tclThreadJoin.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:10k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclThreadJoin.c --
  3.  *
  4.  * This file implements a platform independent emulation layer for
  5.  * the handling of joinable threads. The Mac and Windows platforms
  6.  * use this code to provide the functionality of joining threads.
  7.  * This code is currently not necessary on Unix.
  8.  *
  9.  * Copyright (c) 2000 by Scriptics Corporation
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * RCS: @(#) $Id: tclThreadJoin.c,v 1.4 2002/04/24 20:35:40 hobbs Exp $
  15.  */
  16. #include "tclInt.h"
  17. #if defined(WIN32) || defined(MAC_TCL)
  18. /* The information about each joinable thread is remembered in a
  19.  * structure as defined below.
  20.  */
  21. typedef struct JoinableThread {
  22.   Tcl_ThreadId  id;                     /* The id of the joinable thread */
  23.   int           result;                 /* A place for the result after the
  24.  * demise of the thread */
  25.   int           done;                   /* Boolean flag. Initialized to 0
  26.  * and set to 1 after the exit of
  27.  * the thread. This allows a thread
  28.  * requesting a join to detect when
  29.  * waiting is not necessary. */
  30.   int           waitedUpon;             /* Boolean flag. Initialized to 0
  31.  * and set to 1 by the thread waiting
  32.  * for this one via Tcl_JoinThread.
  33.  * Used to lock any other thread
  34.  * trying to wait on this one.
  35.  */
  36.   Tcl_Mutex     threadMutex;            /* The mutex used to serialize access
  37.  * to this structure. */
  38.   Tcl_Condition cond;                   /* This is the condition a thread has
  39.  * to wait upon to get notified of the
  40.  * end of the described thread. It is
  41.  * signaled indirectly by
  42.  * Tcl_ExitThread. */
  43.   struct JoinableThread* nextThreadPtr; /* Reference to the next thread in the
  44.  * list of joinable threads */
  45. } JoinableThread;
  46. /* The following variable is used to maintain the global list of all
  47.  * joinable threads. Usage by a thread is allowed only if the
  48.  * thread acquired the 'joinMutex'.
  49.  */
  50. TCL_DECLARE_MUTEX(joinMutex)
  51. static JoinableThread* firstThreadPtr;
  52. /*
  53.  *----------------------------------------------------------------------
  54.  *
  55.  * TclJoinThread --
  56.  *
  57.  * This procedure waits for the exit of the thread with the specified
  58.  * id and returns its result.
  59.  *
  60.  * Results:
  61.  * A standard tcl result signaling the overall success/failure of the
  62.  * operation and an integer result delivered by the thread which was
  63.  * waited upon.
  64.  *
  65.  * Side effects:
  66.  * Deallocates the memory allocated by TclRememberJoinableThread.
  67.  * Removes the data associated to the thread waited upon from the
  68.  * list of joinable threads.
  69.  *
  70.  *----------------------------------------------------------------------
  71.  */
  72. int
  73. TclJoinThread(id, result)
  74.     Tcl_ThreadId id;     /* The id of the thread to wait upon. */
  75.     int*         result; /* Reference to a location for the result
  76.   * of the thread we are waiting upon. */
  77. {
  78.     /* Steps done here:
  79.      * i.    Acquire the joinMutex and search for the thread.
  80.      * ii.   Error out if it could not be found.
  81.      * iii.  If found, switch from exclusive access to the list to exclusive
  82.      *       access to the thread structure.
  83.      * iv.   Error out if some other is already waiting.
  84.      * v.    Skip the waiting part of the thread is already done.
  85.      * vi.   Wait for the thread to exit, mark it as waited upon too.
  86.      * vii.  Get the result form the structure, 
  87.      * viii. switch to exclusive access of the list,
  88.      * ix.   remove the structure from the list,
  89.      * x.    then switch back to exclusive access to the structure
  90.      * xi.   and delete it.
  91.      */
  92.     JoinableThread* threadPtr;
  93.     Tcl_MutexLock (&joinMutex);
  94.     for (threadPtr = firstThreadPtr;
  95.  (threadPtr != (JoinableThread*) NULL) && (threadPtr->id != id);
  96.  threadPtr = threadPtr->nextThreadPtr)
  97.         /* empty body */
  98.       ;
  99.     if (threadPtr == (JoinableThread*) NULL) {
  100.         /* Thread not found. Either not joinable, or already waited
  101.  * upon and exited. Whatever, an error is in order.
  102.  */
  103.       Tcl_MutexUnlock (&joinMutex);
  104.       return TCL_ERROR;
  105.     }
  106.     /* [1] If we don't lock the structure before giving up exclusive access
  107.      * to the list some other thread just completing its wait on the same
  108.      * thread can delete the structure from under us, leaving us with a
  109.      * dangling pointer.
  110.      */
  111.     Tcl_MutexLock   (&threadPtr->threadMutex);
  112.     Tcl_MutexUnlock (&joinMutex);
  113.     /* [2] Now that we have the structure mutex any other thread that just
  114.      * tries to delete structure will wait at location [3] until we are
  115.      * done with the structure. And in that case we are done with it
  116.      * rather quickly as 'waitedUpon' will be set and we will have to
  117.      * error out.
  118.      */
  119.     if (threadPtr->waitedUpon) {
  120.         Tcl_MutexUnlock (&threadPtr->threadMutex);
  121. return TCL_ERROR;
  122.     }
  123.     /* We are waiting now, let other threads recognize this
  124.      */
  125.     threadPtr->waitedUpon = 1;
  126.     while (!threadPtr->done) {
  127.       Tcl_ConditionWait (&threadPtr->cond, &threadPtr->threadMutex, NULL);
  128.     }
  129.     /* We have to release the structure before trying to access the list
  130.      * again or we can run into deadlock with a thread at [1] (see above)
  131.      * because of us holding the structure and the other holding the list.
  132.      * There is no problem with dangling pointers here as 'waitedUpon == 1'
  133.      * is still valid and any other thread will error out and not come to
  134.      * this place. IOW, the fact that we are here also means that no other
  135.      * thread came here before us and is able to delete the structure.
  136.      */
  137.     Tcl_MutexUnlock (&threadPtr->threadMutex);
  138.     Tcl_MutexLock   (&joinMutex);
  139.     /* We have to search the list again as its structure may (may, almost
  140.      * certainly) have changed while we were waiting. Especially now is the
  141.      * time to compute the predecessor in the list. Any earlier result can
  142.      * be dangling by now.
  143.      */
  144.     if (firstThreadPtr == threadPtr) {
  145.         firstThreadPtr = threadPtr->nextThreadPtr;
  146.     } else {
  147.         JoinableThread* prevThreadPtr;
  148. for (prevThreadPtr = firstThreadPtr;
  149.      prevThreadPtr->nextThreadPtr != threadPtr;
  150.      prevThreadPtr = prevThreadPtr->nextThreadPtr)
  151.     /* empty body */
  152.   ;
  153. prevThreadPtr->nextThreadPtr = threadPtr->nextThreadPtr;
  154.     }
  155.     Tcl_MutexUnlock (&joinMutex);
  156.     /* [3] Now that the structure is not part of the list anymore no other
  157.      * thread can acquire its mutex from now on. But it is possible that
  158.      * another thread is still holding the mutex though, see location [2].
  159.      * So we have to acquire the mutex one more time to wait for that thread
  160.      * to finish. We can (and have to) release the mutex immediately.
  161.      */
  162.     Tcl_MutexLock   (&threadPtr->threadMutex);
  163.     Tcl_MutexUnlock (&threadPtr->threadMutex);
  164.     /* Copy the result to us, finalize the synchronisation objects, then
  165.      * free the structure and return.
  166.      */
  167.     *result = threadPtr->result;
  168.     Tcl_ConditionFinalize (&threadPtr->cond);
  169.     Tcl_MutexFinalize (&threadPtr->threadMutex);
  170.     ckfree ((VOID*) threadPtr);
  171.     return TCL_OK;
  172. }
  173. /*
  174.  *----------------------------------------------------------------------
  175.  *
  176.  * TclRememberJoinableThread --
  177.  *
  178.  * This procedure remebers a thread as joinable. Only a call to
  179.  * TclJoinThread will remove the structre created (and initialized)
  180.  * here. IOW, not waiting upon a joinable thread will cause memory
  181.  * leaks.
  182.  *
  183.  * Results:
  184.  * None.
  185.  *
  186.  * Side effects:
  187.  * Allocates memory, adds it to the global list of all joinable
  188.  * threads.
  189.  *
  190.  *----------------------------------------------------------------------
  191.  */
  192. VOID
  193. TclRememberJoinableThread(id)
  194.     Tcl_ThreadId id; /* The thread to remember as joinable */
  195. {
  196.     JoinableThread* threadPtr;
  197.     threadPtr = (JoinableThread*) ckalloc (sizeof (JoinableThread));
  198.     threadPtr->id          = id;
  199.     threadPtr->done        = 0;
  200.     threadPtr->waitedUpon  = 0;
  201.     threadPtr->threadMutex = (Tcl_Mutex) NULL;
  202.     threadPtr->cond        = (Tcl_Condition) NULL;
  203.     Tcl_MutexLock (&joinMutex);
  204.     threadPtr->nextThreadPtr = firstThreadPtr;
  205.     firstThreadPtr           = threadPtr;
  206.     Tcl_MutexUnlock (&joinMutex);
  207. }
  208. /*
  209.  *----------------------------------------------------------------------
  210.  *
  211.  * TclSignalExitThread --
  212.  *
  213.  * This procedure signals that the specified thread is done with
  214.  * its work. If the thread is joinable this signal is propagated
  215.  * to the thread waiting upon it.
  216.  *
  217.  * Results:
  218.  * None.
  219.  *
  220.  * Side effects:
  221.  * Modifies the associated structure to hold the result.
  222.  *
  223.  *----------------------------------------------------------------------
  224.  */
  225. VOID
  226. TclSignalExitThread(id,result)
  227.     Tcl_ThreadId id;     /* Id of the thread signaling its exit */
  228.     int          result; /* The result from the thread */
  229. {
  230.     JoinableThread* threadPtr;
  231.     Tcl_MutexLock (&joinMutex);
  232.     for (threadPtr = firstThreadPtr;
  233.  (threadPtr != (JoinableThread*) NULL) && (threadPtr->id != id);
  234.  threadPtr = threadPtr->nextThreadPtr)
  235.         /* empty body */
  236.       ;
  237.     if (threadPtr == (JoinableThread*) NULL) {
  238.         /* Thread not found. Not joinable. No problem, nothing to do.
  239.  */
  240.         Tcl_MutexUnlock (&joinMutex);
  241. return;
  242.     }
  243.     /* Switch over the exclusive access from the list to the structure,
  244.      * then store the result, set the flag and notify the waiting thread,
  245.      * provided that it exists. The order of lock/unlock ensures that a
  246.      * thread entering 'TclJoinThread' will not interfere with us.
  247.      */
  248.     Tcl_MutexLock   (&threadPtr->threadMutex);
  249.     Tcl_MutexUnlock (&joinMutex);
  250.     threadPtr->done   = 1;
  251.     threadPtr->result = result;
  252.     if (threadPtr->waitedUpon) {
  253.       Tcl_ConditionNotify (&threadPtr->cond);
  254.     }
  255.     Tcl_MutexUnlock (&threadPtr->threadMutex);
  256. }
  257. #endif /* WIN32 || MAC_TCL */