/*
 * The contents of this file are subject to the AOLserver Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://aolserver.com/.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is AOLserver Code and related documentation
 * distributed by AOL.
 * 
 * The Initial Developer of the Original Code is America Online,
 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
 * Inc. All Rights Reserved.
 *
 * Alternatively, the contents of this file may be used under the terms
 * of the GNU General Public License (the "GPL"), in which case the
 * provisions of GPL are applicable instead of those above.  If you wish
 * to allow use of your version of this file only under the terms of the
 * GPL and not to allow others to use your version of this file under the
 * License, indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by the GPL.
 * If you do not delete the provisions above, a recipient may use your
 * version of this file under either the License or the GPL.
 */


/* 
 * cslock.c --
 *
 *	Support for critical sections.  Critical sections differ
 *	from mutexes in that a critical section can be repeatedly
 *	locked by the same thread as long as each lock is matched with
 * 	a cooresponding unlock.  Critical sections are used in cases
 *	where the lock could be called recursively, e.g., for the
 *	Ns_MasterLock.
 *
 *	Note:  Critical sections are almost always a bad idea.  You'll
 *	see below that the number of actual lock and unlock operations are
 *	doubled and threads can end up in condition waits instead of spin
 *	locks.
 */

static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsthread/cslock.c,v 1.2 2002/06/12 11:30:44 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__;

#include "thread.h"

/*
 * The following structure defines a critcal section including a mutex,
 * thread id of the owner, and a condition variable for waiting threads.
 */

typedef struct CsLock {
    Ns_Mutex        mutex;
    Ns_Cond         cond;
    int		    tid;
    int             count;
} CsLock;


/*
 *----------------------------------------------------------------------
 *
 * Ns_CsInit --
 *
 *	Initialize a critical section object.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A critical section object is allocated from the heap.
 *
 *----------------------------------------------------------------------
 */

void
Ns_CsInit(Ns_Cs *csPtr)
{
    CsLock     *lockPtr;
    static unsigned int nextid = 0;

    lockPtr = ns_malloc(sizeof(CsLock));
    NsMutexInitNext(&lockPtr->mutex, "cs", &nextid);
    Ns_CondInit(&lockPtr->cond);
    lockPtr->count = 0;
    *csPtr = (Ns_Cs) lockPtr;
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_CsDestroy --
 *
 *	Destroy a critical section object.  Note that you would almost
 *	never need to call this function as synchronization objects are
 *	typically created at startup and exist until the server exits.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The underly objects in the critical section are destroy and
 *	the critical section memory returned to the heap.
 *
 *----------------------------------------------------------------------
 */

void
Ns_CsDestroy(Ns_Cs *csPtr)
{
    CsLock *lockPtr = (CsLock *) *csPtr;

    /*
     * Destroy the condition only if it's not null, i.e., initialized
     * by the first use.
     */

    if (lockPtr != NULL) {
    	Ns_MutexDestroy(&lockPtr->mutex);
    	Ns_CondDestroy(&lockPtr->cond);
    	lockPtr->count = 0;
    	ns_free(lockPtr);
    	*csPtr = NULL;
    }
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_CsEnter --
 *
 *	Lock a critical section object, initializing it first if needed.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Thread may wait on the critical section condition variable if
 *	the critical section is already owned by another thread.
 *
 *----------------------------------------------------------------------
 */

void
Ns_CsEnter(Ns_Cs *csPtr)
{
    CsLock	*lockPtr;
    int		 tid = Ns_ThreadId();

    /*
     * Initialize the critical section if it has never been used before.
     */

    if (*csPtr == NULL) {
    	Ns_MasterLock();
	if (*csPtr == NULL) {
	    Ns_CsInit(csPtr);
	}
	Ns_MasterUnlock();
    }
    lockPtr = (CsLock *) *csPtr;

    /*
     * Wait on the condition if the critical section is owned by another
     * thread.
     */
    Ns_MutexLock(&lockPtr->mutex);
    while (lockPtr->count > 0 && lockPtr->tid != tid) {
        Ns_CondWait(&lockPtr->cond, &lockPtr->mutex);
    }
    lockPtr->tid = tid;
    lockPtr->count++;
    Ns_MutexUnlock(&lockPtr->mutex);
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_CsLeave --
 *
 *	Unlock a critical section once.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Condition is signaled if this is the final unlock of the critical
 *	section.
 *
 *----------------------------------------------------------------------
 */

void
Ns_CsLeave(Ns_Cs *csPtr)
{
    CsLock *lockPtr = (CsLock *) *csPtr;

    Ns_MutexLock(&lockPtr->mutex);
    if (--lockPtr->count == 0) {
        Ns_CondSignal(&lockPtr->cond);
    }
    Ns_MutexUnlock(&lockPtr->mutex);
}
