tclThreadJoin.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:10k
- /*
- * 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.
- *
- *----------------------------------------------------------------------
- */
- int
- TclJoinThread(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.
- *
- *----------------------------------------------------------------------
- */
- VOID
- TclRememberJoinableThread(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.
- *
- *----------------------------------------------------------------------
- */
- VOID
- TclSignalExitThread(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 */