⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tclthreadjoin.c

📁 tcl是工具命令语言
💻 C
字号:
/*  * tclThreadJoin.c -- * *	This file implements a platform independent emulation layer for *	the handling of joinable threads. The Mac and Windows platforms *	use this code to provide the functionality of joining threads. *	This code is currently not necessary on Unix. * * Copyright (c) 2000 by Scriptics Corporation * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclThreadJoin.c,v 1.4 2002/04/24 20:35:40 hobbs Exp $ */#include "tclInt.h"#if defined(WIN32) || defined(MAC_TCL)/* The information about each joinable thread is remembered in a * structure as defined below. */typedef struct JoinableThread {  Tcl_ThreadId  id;                     /* The id of the joinable thread */  int           result;                 /* A place for the result after the					 * demise of the thread */  int           done;                   /* Boolean flag. Initialized to 0					 * and set to 1 after the exit of					 * the thread. This allows a thread					 * requesting a join to detect when					 * waiting is not necessary. */  int           waitedUpon;             /* Boolean flag. Initialized to 0					 * and set to 1 by the thread waiting					 * for this one via Tcl_JoinThread.					 * Used to lock any other thread					 * trying to wait on this one.					 */  Tcl_Mutex     threadMutex;            /* The mutex used to serialize access					 * to this structure. */  Tcl_Condition cond;                   /* This is the condition a thread has					 * to wait upon to get notified of the					 * end of the described thread. It is					 * signaled indirectly by					 * Tcl_ExitThread. */  struct JoinableThread* nextThreadPtr; /* Reference to the next thread in the					 * list of joinable threads */} JoinableThread;/* The following variable is used to maintain the global list of all * joinable threads. Usage by a thread is allowed only if the * thread acquired the 'joinMutex'. */TCL_DECLARE_MUTEX(joinMutex)static JoinableThread* firstThreadPtr;/* *---------------------------------------------------------------------- * * TclJoinThread -- * *	This procedure waits for the exit of the thread with the specified *	id and returns its result. * * Results: *	A standard tcl result signaling the overall success/failure of the *	operation and an integer result delivered by the thread which was *	waited upon. * * Side effects: *	Deallocates the memory allocated by TclRememberJoinableThread. *	Removes the data associated to the thread waited upon from the *	list of joinable threads. * *---------------------------------------------------------------------- */intTclJoinThread(id, result)    Tcl_ThreadId id;     /* The id of the thread to wait upon. */    int*         result; /* Reference to a location for the result			  * of the thread we are waiting upon. */{    /* Steps done here:     * i.    Acquire the joinMutex and search for the thread.     * ii.   Error out if it could not be found.     * iii.  If found, switch from exclusive access to the list to exclusive     *       access to the thread structure.     * iv.   Error out if some other is already waiting.     * v.    Skip the waiting part of the thread is already done.     * vi.   Wait for the thread to exit, mark it as waited upon too.     * vii.  Get the result form the structure,      * viii. switch to exclusive access of the list,     * ix.   remove the structure from the list,     * x.    then switch back to exclusive access to the structure     * xi.   and delete it.     */    JoinableThread* threadPtr;    Tcl_MutexLock (&joinMutex);    for (threadPtr = firstThreadPtr;	 (threadPtr != (JoinableThread*) NULL) && (threadPtr->id != id);	 threadPtr = threadPtr->nextThreadPtr)        /* empty body */      ;    if (threadPtr == (JoinableThread*) NULL) {        /* Thread not found. Either not joinable, or already waited	 * upon and exited. Whatever, an error is in order.	 */      Tcl_MutexUnlock (&joinMutex);      return TCL_ERROR;    }    /* [1] If we don't lock the structure before giving up exclusive access     * to the list some other thread just completing its wait on the same     * thread can delete the structure from under us, leaving us with a     * dangling pointer.     */    Tcl_MutexLock   (&threadPtr->threadMutex);    Tcl_MutexUnlock (&joinMutex);    /* [2] Now that we have the structure mutex any other thread that just     * tries to delete structure will wait at location [3] until we are     * done with the structure. And in that case we are done with it     * rather quickly as 'waitedUpon' will be set and we will have to     * error out.     */    if (threadPtr->waitedUpon) {        Tcl_MutexUnlock (&threadPtr->threadMutex);	return TCL_ERROR;    }    /* We are waiting now, let other threads recognize this     */    threadPtr->waitedUpon = 1;    while (!threadPtr->done) {      Tcl_ConditionWait (&threadPtr->cond, &threadPtr->threadMutex, NULL);    }    /* We have to release the structure before trying to access the list     * again or we can run into deadlock with a thread at [1] (see above)     * because of us holding the structure and the other holding the list.     * There is no problem with dangling pointers here as 'waitedUpon == 1'     * is still valid and any other thread will error out and not come to     * this place. IOW, the fact that we are here also means that no other     * thread came here before us and is able to delete the structure.     */    Tcl_MutexUnlock (&threadPtr->threadMutex);    Tcl_MutexLock   (&joinMutex);    /* We have to search the list again as its structure may (may, almost     * certainly) have changed while we were waiting. Especially now is the     * time to compute the predecessor in the list. Any earlier result can     * be dangling by now.     */    if (firstThreadPtr == threadPtr) {        firstThreadPtr = threadPtr->nextThreadPtr;    } else {        JoinableThread* prevThreadPtr;	for (prevThreadPtr = firstThreadPtr;	     prevThreadPtr->nextThreadPtr != threadPtr;	     prevThreadPtr = prevThreadPtr->nextThreadPtr)	    /* empty body */	  ;	prevThreadPtr->nextThreadPtr = threadPtr->nextThreadPtr;    }    Tcl_MutexUnlock (&joinMutex);    /* [3] Now that the structure is not part of the list anymore no other     * thread can acquire its mutex from now on. But it is possible that     * another thread is still holding the mutex though, see location [2].     * So we have to acquire the mutex one more time to wait for that thread     * to finish. We can (and have to) release the mutex immediately.     */    Tcl_MutexLock   (&threadPtr->threadMutex);    Tcl_MutexUnlock (&threadPtr->threadMutex);    /* Copy the result to us, finalize the synchronisation objects, then     * free the structure and return.     */    *result = threadPtr->result;    Tcl_ConditionFinalize (&threadPtr->cond);    Tcl_MutexFinalize (&threadPtr->threadMutex);    ckfree ((VOID*) threadPtr);    return TCL_OK;}/* *---------------------------------------------------------------------- * * TclRememberJoinableThread -- * *	This procedure remebers a thread as joinable. Only a call to *	TclJoinThread will remove the structre created (and initialized) *	here. IOW, not waiting upon a joinable thread will cause memory *	leaks. * * Results: *	None. * * Side effects: *	Allocates memory, adds it to the global list of all joinable *	threads. * *---------------------------------------------------------------------- */VOIDTclRememberJoinableThread(id)    Tcl_ThreadId id; /* The thread to remember as joinable */{    JoinableThread* threadPtr;    threadPtr = (JoinableThread*) ckalloc (sizeof (JoinableThread));    threadPtr->id          = id;    threadPtr->done        = 0;    threadPtr->waitedUpon  = 0;    threadPtr->threadMutex = (Tcl_Mutex) NULL;    threadPtr->cond        = (Tcl_Condition) NULL;    Tcl_MutexLock (&joinMutex);    threadPtr->nextThreadPtr = firstThreadPtr;    firstThreadPtr           = threadPtr;    Tcl_MutexUnlock (&joinMutex);}/* *---------------------------------------------------------------------- * * TclSignalExitThread -- * *	This procedure signals that the specified thread is done with *	its work. If the thread is joinable this signal is propagated *	to the thread waiting upon it. * * Results: *	None. * * Side effects: *	Modifies the associated structure to hold the result. * *---------------------------------------------------------------------- */VOIDTclSignalExitThread(id,result)    Tcl_ThreadId id;     /* Id of the thread signaling its exit */    int          result; /* The result from the thread */{    JoinableThread* threadPtr;    Tcl_MutexLock (&joinMutex);    for (threadPtr = firstThreadPtr;	 (threadPtr != (JoinableThread*) NULL) && (threadPtr->id != id);	 threadPtr = threadPtr->nextThreadPtr)        /* empty body */      ;    if (threadPtr == (JoinableThread*) NULL) {        /* Thread not found. Not joinable. No problem, nothing to do.	 */        Tcl_MutexUnlock (&joinMutex);	return;    }    /* Switch over the exclusive access from the list to the structure,     * then store the result, set the flag and notify the waiting thread,     * provided that it exists. The order of lock/unlock ensures that a     * thread entering 'TclJoinThread' will not interfere with us.     */    Tcl_MutexLock   (&threadPtr->threadMutex);    Tcl_MutexUnlock (&joinMutex);    threadPtr->done   = 1;    threadPtr->result = result;    if (threadPtr->waitedUpon) {      Tcl_ConditionNotify (&threadPtr->cond);    }    Tcl_MutexUnlock (&threadPtr->threadMutex);}#endif /* WIN32 || MAC_TCL */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -