rem: Ns_MasterUnlock;
add: Ns_MasterUnlock();
context: static Ns_TlsCleanup FreeDataBlock;/* *---------------------------------------------------------------------- * * Tcl_CreateThread -- * *	This procedure creates a new thread. * * Results: *	TCL_OK if the thread could be created.  The thread ID is *	returned in a parameter. * * Side effects: *	A new thread is created. * *---------------------------------------------------------------------- */NS_EXPORT intTcl_CreateThread(idPtr, proc, clientData, stackSize, flags)    Tcl_ThreadId *idPtr;		/* Return, the ID of the thread */    Tcl_ThreadCreateProc proc;		/* Main() function of the thread */    ClientData clientData;		/* The one argument to Main() */    int stackSize;			/* New thread stack size. */    int flags;				/* Flags for new thread (ignored). */{    Ns_Thread tid;    ThreadArg *argPtr;    if (stackSize == TCL_THREAD_STACK_DEFAULT) {	stackSize = 0;    }    if (flags & TCL_THREAD_JOINABLE) {	flags = 0;    } else {	flags = NS_THREAD_DETACHED;    }    argPtr = NsAlloc(sizeof(ThreadArg));    argPtr->proc = proc;    argPtr->clientData = clientData;    Ns_ThreadCreate2(TclNsThread, argPtr, (long) stackSize, flags, &tid);    *idPtr = (Tcl_ThreadId) tid;    return TCL_OK;}/* *---------------------------------------------------------------------- * * TclNsThread -- * *	Tcl specific thread startup routine.  This is only required *	to handle the different proc types between Tcl and Ns threads. * * Results: *	None. * * Side effects: *	Depends on given thread start proc. * *---------------------------------------------------------------------- */static voidTclNsThread(void *arg){    ThreadArg *argPtr = arg;    Tcl_ThreadCreateProc *proc;    ClientData clientData;    proc = argPtr->proc;    clientData = argPtr->clientData;    NsFree(argPtr);    (*proc)(clientData);}/* *---------------------------------------------------------------------- * * Tcl_JoinThread -- * *	This procedure waits for a thread to exit. * * Results: *	TCL_OK * * Side effects: *	Thread exit status is returned in given resultPtr. * *---------------------------------------------------------------------- */NS_EXPORT intTcl_JoinThread(id, resultPtr)    Tcl_ThreadId id;		/* ID of the thread */    int *resultPtr;		/* Thread exit status. */{    void *status;    Ns_ThreadJoin((Ns_Thread *) &id, &status);    *resultPtr = (int) status;    return TCL_OK;}/* *---------------------------------------------------------------------- * * TclpThreadExit -- * *	This procedure terminates the current thread. * * Results: *	None. * * Side effects: *	This procedure terminates the current thread. * *---------------------------------------------------------------------- */NS_EXPORT voidTclpThreadExit(status)    int status;{    Ns_ThreadExit((void *) status);}/* *---------------------------------------------------------------------- * * Tcl_GetCurrentThread -- * *	This procedure returns the ID of the currently running thread. * * Results: *	A thread ID. * * Side effects: *	None. * *---------------------------------------------------------------------- */NS_EXPORT Tcl_ThreadIdTcl_GetCurrentThread(){    Ns_Thread tid;    Ns_ThreadSelf(&tid);    return (Tcl_ThreadId) tid;}/* *---------------------------------------------------------------------- * * TclpInitLock * *	This procedure is used to grab a lock that serializes *	initialization and finalization of Tcl.  With nsthreads, *	the single recursive Ns_MasterLock is used.  * * Results: *	None. * * Side effects: *	Acquire the initialization mutex. * *---------------------------------------------------------------------- */NS_EXPORT voidTclpInitLock(){    Ns_MasterLock;}/* *---------------------------------------------------------------------- * * TclpInitUnlock * *	This procedure is used to release a lock that serializes initialization *	and finalization of Tcl. * * Results: *	None. * * Side effects: *	Release the initialization mutex. * *---------------------------------------------------------------------- */NS_EXPORT voidTclpInitUnlock(){    Ns_MasterUnlock;}/* *---------------------------------------------------------------------- * * TclpMasterLock * *	This procedure is used to grab a lock that serializes creation *	and finalization of serialization objects. * * Results: *	None. * * Side effects: *	Acquire the master mutex. * *---------------------------------------------------------------------- */NS_EXPORT voidTclpMasterLock(){    Ns_MasterLock;}/* *---------------------------------------------------------------------- * * TclpMasterUnlock * *	This procedure is used to release a lock that serializes creation *	and finalization of synchronization objects. * * Results: *	None. * * Side effects: *	Release the master mutex. * *---------------------------------------------------------------------- */NS_EXPORT voidTclpMasterUnlock(){    Ns_MasterUnlock;}/* *---------------------------------------------------------------------- * * Tcl_GetAllocMutex * *	This procedure returns a pointer to a statically initialized *	mutex for use by the memory allocator.  The alloctor must *	use this lock, because all other locks are allocated. * * Results: *	A pointer to a mutex that is suitable for passing to *	Tcl_MutexLock and Tcl_MutexUnlock. * * Side effects: *	None. * *---------------------------------------------------------------------- */NS_EXPORT Tcl_Mutex *Tcl_GetAllocMutex(){    static Ns_Mutex allocLock;    return (Tcl_Mutex *) &allocLock;}/* *---------------------------------------------------------------------- * * Tcl_MutexLock -- * *	This procedure is invoked to lock a mutex.  This procedure *	handles initializing the mutex, if necessary.  The caller *	can rely on the fact that Tcl_Mutex is an opaque pointer. *	This routine will change that pointer from NULL after first use. * * Results: *	None. * * Side effects: *	May block the current thread.  The mutex is aquired when *	this returns.  Will allocate memory for a Ns_Mutex *	and initialize this the first time this Tcl_Mutex is used. * *---------------------------------------------------------------------- */NS_EXPORT voidTcl_MutexLock(mutexPtr)    Tcl_Mutex *mutexPtr;	/* Really (Ns_Mutex **) */{    Ns_Mutex *nsmutexPtr = (Ns_Mutex *) mutexPtr;    if (*nsmutexPtr == NULL) {	Ns_MasterLock;	if (*nsmutexPtr == NULL) {	    static int next;	    char buf[20];	    sprintf(buf, "tcl:%d", next++);	    Ns_MutexInit(nsmutexPtr);	    Ns_MutexSetName(nsmutexPtr, buf);	}	Ns_MasterUnlock;    }    Ns_MutexLock(nsmutexPtr);}/* *---------------------------------------------------------------------- * * TclpMutexUnlock -- * *	This procedure is invoked to unlock a mutex.  The mutex must *	have been locked by Tcl_MutexLock. * * Results: *	None. * * Side effects: *	The mutex is released when this returns. * *---------------------------------------------------------------------- */NS_EXPORT voidTcl_MutexUnlock(mutexPtr)    Tcl_Mutex *mutexPtr;	/* Really (Ns_Mutex **) */{    Ns_MutexUnlock((Ns_Mutex *) mutexPtr);}/* *---------------------------------------------------------------------- * * TclpFinalizeMutex -- * *	This procedure is invoked to clean up one mutex.  This is only *	safe to call at the end of time. * *	This assumes the Master Lock is held. * * Results: *	None. * * Side effects: *	The mutex list is deallocated. * *---------------------------------------------------------------------- */NS_EXPORT voidTclpFinalizeMutex(mutexPtr)    Tcl_Mutex *mutexPtr;{    Ns_MutexDestroy((Ns_Mutex *) mutexPtr);}/* *---------------------------------------------------------------------- * * TclpThreadDataKeyInit -- * *	This procedure initializes a thread specific data block key. *	In Tcl, a thread data block is thread local storage allocated *	with TclpAlloc. * * Results: *	None. * * Side effects: *	Per-thread data blocks will be deallocated at thread exit with *	FreeDataBlock. * *---------------------------------------------------------------------- */NS_EXPORT voidTclpThreadDataKeyInit(keyPtr)    Tcl_ThreadDataKey *keyPtr;	/* Identifier for the data chunk */{    Ns_Tls *tlsPtr = (Ns_Tls *) keyPtr;    Ns_MasterLock;    if (*tlsPtr == NULL) {	Ns_TlsAlloc(tlsPtr, FreeDataBlock);    }    Ns_MasterUnlock;}/* *---------------------------------------------------------------------- * * TclpThreadDataKeyGet -- * *	This procedure returns a pointer to a thread data block. * * Results: *	A thread-specific pointer to the data structure, or NULL *	if the memory has not been assigned to this key for this thread. * * Side effects: *	None. * *---------------------------------------------------------------------- */NS_EXPORT VOID *TclpThreadDataKeyGet(keyPtr)    Tcl_ThreadDataKey *keyPtr;	/* Identifier for the data chunk */{    if (*keyPtr == NULL) {	return NULL;    }    return (VOID *) Ns_TlsGet((Ns_Tls *) keyPtr);}/* *---------------------------------------------------------------------- * * TclpThreadDataKeySet -- * *	This procedure sets the pointer to a thread data block. * * Results: *	None. * * Side effects: *	Sets up the thread so future calls to TclpThreadDataKeyGet with *	this key will return the data pointer. * *---------------------------------------------------------------------- */NS_EXPORT voidTclpThreadDataKeySet(keyPtr, data)    Tcl_ThreadDataKey *keyPtr;	/* Identifier for the data chunk */    VOID *data;			/* Thread local storage */{    Ns_TlsSet((Ns_Tls *) keyPtr, data);}/* *---------------------------------------------------------------------- * * TclpFinalizeThreadData -- * *	In nsthread's, this routine is not called because there is no *	call to TclRememberThreadDataKey in TclpThreadDataKeyInit. *	Instead, the blocks are deallocated by the FreeDataBlock *	callback in the Ns_Tls interface. * * Results: *	None. * * Side effects: *	None. * *---------------------------------------------------------------------- */NS_EXPORT voidTclpFinalizeThreadData(keyPtr)    Tcl_ThreadDataKey *keyPtr;{    return;}/* *---------------------------------------------------------------------- * * TclpFinalizeThreadDataKey -- * *	In nsthreads, this procedure is never called because there *	is no call to TclRememberThreadDataKey in TclpThreadDataKeyInit. * * Results: *	None. * * Side effects: *	None. * *---------------------------------------------------------------------- */NS_EXPORT voidTclpFinalizeThreadDataKey(keyPtr)    Tcl_ThreadDataKey *keyPtr;{    return;}/* *---------------------------------------------------------------------- * * Tcl_ConditionWait -- * *	This procedure is invoked to wait on a condition variable. *	The mutex is automically released as part of the wait, and *	automatically grabbed when the condition is signaled. * *	The mutex must be held when this procedure is called. * * Results: *	None. * * Side effects: *	May block the current thread.  The mutex is aquired when *	this returns.  Will allocate memory for a Ns_Mutex *	and initialize this the first time this Tcl_Mutex is used. * *---------------------------------------------------------------------- */NS_EXPORT voidTcl_ConditionWait(condPtr, mutexPtr, timePtr)    Tcl_Condition *condPtr;	/* Really (Ns_Cond *) */    Tcl_Mutex *mutexPtr;	/* Really (Ns_Mutex *) */    Tcl_Time *timePtr;		/* Timeout on waiting period */{    Ns_Time timeout;        if (timePtr != NULL) {	/*	 * Convert from the Tcl API relative timeout to the	 * NS API absolute timeout.	 */	Ns_GetTime(&timeout);	Ns_IncrTime(&timeout, timePtr->sec, timePtr->usec);	timePtr = (Tcl_Time *) &timeout;    }    Ns_CondTimedWait((Ns_Cond *) condPtr, (Ns_Mutex *) mutexPtr,	(Ns_Time *) timePtr);}/* *---------------------------------------------------------------------- * * Tcl_ConditionNotify -- * *	This procedure is invoked to signal a condition variable. * *	The mutex must be held during this call to avoid races, *	but this interface does not enforce that. * * Results: *	None. * * Side effects: *	May unblock another thread. * *---------------------------------------------------------------------- */NS_EXPORT voidTcl_ConditionNotify(condPtr)    Tcl_Condition *condPtr;{    Ns_CondBroadcast((Ns_Cond *) condPtr);}/* *---------------------------------------------------------------------- * * TclpFinalizeCondition -- * *	This procedure is invoked to clean up a condition variable. *	This is only safe to call at the end of time. * *	This assumes the Master Lock is held. * * Results: *	None. * * Side effects: *	The condition variable is deallocated. * *---------------------------------------------------------------------- */NS_EXPORT voidTclpFinalizeCondition(condPtr)    Tcl_Condition *condPtr;{    Ns_CondDestroy((Ns_Cond *) condPtr);}/* *---------------------------------------------------------------------- * * TclpAlloc -- * *	Wraps ns_malloc.  * * Results: *	See ns_malloc  * * Side effects: *	See ns_malloc  * *---------------------------------------------------------------------- */NS_EXPORT char *TclpAlloc(unsigned int nbytes){    return (char *) ns_malloc(nbytes);}/* *---------------------------------------------------------------------- * * TclpFree -- * *	Wraps ns_free.  * * Results: *	See ns_free  * * Side effects: *	See ns_free  * *---------------------------------------------------------------------- */NS_EXPORT voidTclpFree(char *cp){    ns_free(cp);}/* *---------------------------------------------------------------------- * * TclpRealloc -- * *	Wraps ns_realloc.  * * Results: *	See ns_realloc  * * Side effects: *	See ns_realloc  * *---------------------------------------------------------------------- */NS_EXPORT char *TclpRealloc(char *cp, unsigned int nbytes){    return ns_realloc(cp, nbytes);}/* *---------------------------------------------------------------------- * * FreeDataBlock -- * *	Ns_Tls cleanup callback to free thread data blocks. * * Results: *	None. * * Side effects: *	None. * *---------------------------------------------------------------------- */static voidFreeDataBlock(void *arg){    TclpFree((char *) arg);}