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

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclWinThread.c --
  3.  *
  4.  * This file implements the Windows-specific thread operations.
  5.  *
  6.  * Copyright (c) 1998 by Sun Microsystems, Inc.
  7.  * Copyright (c) 1999 by Scriptics Corporation
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * RCS: @(#) $Id: tclWinThrd.c,v 1.24.2.12 2007/03/24 09:31:11 vasiljevic Exp $
  13.  */
  14. #include "tclWinInt.h"
  15. #include <fcntl.h>
  16. #include <io.h>
  17. #include <sys/stat.h>
  18. /*
  19.  * This is the master lock used to serialize access to other
  20.  * serialization data structures.
  21.  */
  22. static CRITICAL_SECTION masterLock;
  23. static int init = 0;
  24. #define MASTER_LOCK TclpMasterLock() 
  25. #define MASTER_UNLOCK TclpMasterUnlock() 
  26. /*
  27.  * This is the master lock used to serialize initialization and finalization
  28.  * of Tcl as a whole.
  29.  */
  30. static CRITICAL_SECTION initLock;
  31. /*
  32.  * allocLock is used by Tcl's version of malloc for synchronization.
  33.  * For obvious reasons, cannot use any dyamically allocated storage.
  34.  */
  35. #ifdef TCL_THREADS
  36. static CRITICAL_SECTION allocLock;
  37. static Tcl_Mutex allocLockPtr = (Tcl_Mutex) &allocLock;
  38. static int allocOnce = 0;
  39. #endif /* TCL_THREADS */
  40. /*
  41.  * The joinLock serializes Create- and ExitThread. This is necessary to
  42.  * prevent a race where a new joinable thread exits before the creating
  43.  * thread had the time to create the necessary data structures in the
  44.  * emulation layer.
  45.  */
  46. static CRITICAL_SECTION joinLock;
  47. /*
  48.  * Condition variables are implemented with a combination of a 
  49.  * per-thread Windows Event and a per-condition waiting queue.
  50.  * The idea is that each thread has its own Event that it waits
  51.  * on when it is doing a ConditionWait; it uses the same event for
  52.  * all condition variables because it only waits on one at a time.
  53.  * Each condition variable has a queue of waiting threads, and a 
  54.  * mutex used to serialize access to this queue.
  55.  *
  56.  * Special thanks to David Nichols and
  57.  * Jim Davidson for advice on the Condition Variable implementation.
  58.  */
  59. /*
  60.  * The per-thread event and queue pointers.
  61.  */
  62. #ifdef TCL_THREADS
  63. typedef struct ThreadSpecificData {
  64.     HANDLE condEvent; /* Per-thread condition event */
  65.     struct ThreadSpecificData *nextPtr; /* Queue pointers */
  66.     struct ThreadSpecificData *prevPtr;
  67.     int flags; /* See flags below */
  68. } ThreadSpecificData;
  69. static Tcl_ThreadDataKey dataKey;
  70. #endif /* TCL_THREADS */
  71. /*
  72.  * Additions by AOL for specialized thread memory allocator.
  73.  */
  74. #if defined(USE_THREAD_ALLOC) && !defined(TCL_MEM_DEBUG)
  75. static int   once;
  76. static DWORD tlsKey;
  77. typedef struct allocMutex {
  78.     Tcl_Mutex        tlock;
  79.     CRITICAL_SECTION wlock;
  80. } allocMutex;
  81. #endif
  82. /*
  83.  * State bits for the thread.
  84.  * WIN_THREAD_UNINIT Uninitialized.  Must be zero because
  85.  * of the way ThreadSpecificData is created.
  86.  * WIN_THREAD_RUNNING Running, not waiting.
  87.  * WIN_THREAD_BLOCKED Waiting, or trying to wait.
  88.  */ 
  89. #define WIN_THREAD_UNINIT 0x0
  90. #define WIN_THREAD_RUNNING 0x1
  91. #define WIN_THREAD_BLOCKED 0x2
  92. /*
  93.  * The per condition queue pointers and the
  94.  * Mutex used to serialize access to the queue.
  95.  */
  96. typedef struct WinCondition {
  97.     CRITICAL_SECTION condLock; /* Lock to serialize queuing on the condition */
  98.     struct ThreadSpecificData *firstPtr; /* Queue pointers */
  99.     struct ThreadSpecificData *lastPtr;
  100. } WinCondition;
  101. /*
  102.  *----------------------------------------------------------------------
  103.  *
  104.  * TclpThreadCreate --
  105.  *
  106.  * This procedure creates a new thread.
  107.  *
  108.  * Results:
  109.  * TCL_OK if the thread could be created.  The thread ID is
  110.  * returned in a parameter.
  111.  *
  112.  * Side effects:
  113.  * A new thread is created.
  114.  *
  115.  *----------------------------------------------------------------------
  116.  */
  117. int
  118. TclpThreadCreate(idPtr, proc, clientData, stackSize, flags)
  119.     Tcl_ThreadId *idPtr; /* Return, the ID of the thread */
  120.     Tcl_ThreadCreateProc proc; /* Main() function of the thread */
  121.     ClientData clientData; /* The one argument to Main() */
  122.     int stackSize; /* Size of stack for the new thread */
  123.     int flags; /* Flags controlling behaviour of
  124.  * the new thread */
  125. {
  126.     HANDLE tHandle;
  127.     EnterCriticalSection(&joinLock);
  128. #if defined(_MSC_VER) || defined(__MSVCRT__) || defined(__BORLANDC__)
  129.     tHandle = (HANDLE) _beginthreadex(NULL, (unsigned) stackSize, proc,
  130. clientData, 0, (unsigned *)idPtr);
  131. #else
  132.     tHandle = CreateThread(NULL, (DWORD) stackSize,
  133.     (LPTHREAD_START_ROUTINE) proc, (LPVOID) clientData,
  134.     (DWORD) 0, (LPDWORD)idPtr);
  135. #endif
  136.     if (tHandle == NULL) {
  137.         LeaveCriticalSection(&joinLock);
  138. return TCL_ERROR;
  139.     } else {
  140.         if (flags & TCL_THREAD_JOINABLE) {
  141.     TclRememberJoinableThread (*idPtr);
  142. }
  143. /*
  144.  * The only purpose of this is to decrement the reference count so the
  145.  * OS resources will be reaquired when the thread closes.
  146.  */
  147. CloseHandle(tHandle);
  148. LeaveCriticalSection(&joinLock);
  149. return TCL_OK;
  150.     }
  151. }
  152. /*
  153.  *----------------------------------------------------------------------
  154.  *
  155.  * Tcl_JoinThread --
  156.  *
  157.  * This procedure waits upon the exit of the specified thread.
  158.  *
  159.  * Results:
  160.  * TCL_OK if the wait was successful, TCL_ERROR else.
  161.  *
  162.  * Side effects:
  163.  * The result area is set to the exit code of the thread we
  164.  * waited upon.
  165.  *
  166.  *----------------------------------------------------------------------
  167.  */
  168. int
  169. Tcl_JoinThread(threadId, result)
  170.     Tcl_ThreadId threadId;  /* Id of the thread to wait upon */
  171.     int*     result;     /* Reference to the storage the result
  172.      * of the thread we wait upon will be
  173.      * written into. */
  174. {
  175.     return TclJoinThread (threadId, result);
  176. }
  177. /*
  178.  *----------------------------------------------------------------------
  179.  *
  180.  * TclpThreadExit --
  181.  *
  182.  * This procedure terminates the current thread.
  183.  *
  184.  * Results:
  185.  * None.
  186.  *
  187.  * Side effects:
  188.  * This procedure terminates the current thread.
  189.  *
  190.  *----------------------------------------------------------------------
  191.  */
  192. void
  193. TclpThreadExit(status)
  194.     int status;
  195. {
  196.     EnterCriticalSection(&joinLock);
  197.     TclSignalExitThread (Tcl_GetCurrentThread (), status);
  198.     LeaveCriticalSection(&joinLock);
  199. #if defined(_MSC_VER) || defined(__MSVCRT__) || defined(__BORLANDC__)
  200.     _endthreadex((unsigned) status);
  201. #else
  202.     ExitThread((DWORD) status);
  203. #endif
  204. }
  205. /*
  206.  *----------------------------------------------------------------------
  207.  *
  208.  * Tcl_GetCurrentThread --
  209.  *
  210.  * This procedure returns the ID of the currently running thread.
  211.  *
  212.  * Results:
  213.  * A thread ID.
  214.  *
  215.  * Side effects:
  216.  * None.
  217.  *
  218.  *----------------------------------------------------------------------
  219.  */
  220. Tcl_ThreadId
  221. Tcl_GetCurrentThread()
  222. {
  223.     return (Tcl_ThreadId)GetCurrentThreadId();
  224. }
  225. /*
  226.  *----------------------------------------------------------------------
  227.  *
  228.  * TclpInitLock
  229.  *
  230.  * This procedure is used to grab a lock that serializes initialization
  231.  * and finalization of Tcl.  On some platforms this may also initialize
  232.  * the mutex used to serialize creation of more mutexes and thread
  233.  * local storage keys.
  234.  *
  235.  * Results:
  236.  * None.
  237.  *
  238.  * Side effects:
  239.  * Acquire the initialization mutex.
  240.  *
  241.  *----------------------------------------------------------------------
  242.  */
  243. void
  244. TclpInitLock()
  245. {
  246.     if (!init) {
  247. /*
  248.  * There is a fundamental race here that is solved by creating
  249.  * the first Tcl interpreter in a single threaded environment.
  250.  * Once the interpreter has been created, it is safe to create
  251.  * more threads that create interpreters in parallel.
  252.  */
  253. init = 1;
  254. InitializeCriticalSection(&joinLock);
  255. InitializeCriticalSection(&initLock);
  256. InitializeCriticalSection(&masterLock);
  257.     }
  258.     EnterCriticalSection(&initLock);
  259. }
  260. /*
  261.  *----------------------------------------------------------------------
  262.  *
  263.  * TclpInitUnlock
  264.  *
  265.  * This procedure is used to release a lock that serializes initialization
  266.  * and finalization of Tcl.
  267.  *
  268.  * Results:
  269.  * None.
  270.  *
  271.  * Side effects:
  272.  * Release the initialization mutex.
  273.  *
  274.  *----------------------------------------------------------------------
  275.  */
  276. void
  277. TclpInitUnlock()
  278. {
  279.     LeaveCriticalSection(&initLock);
  280. }
  281. /*
  282.  *----------------------------------------------------------------------
  283.  *
  284.  * TclpMasterLock
  285.  *
  286.  * This procedure is used to grab a lock that serializes creation
  287.  * of mutexes, condition variables, and thread local storage keys.
  288.  *
  289.  * This lock must be different than the initLock because the
  290.  * initLock is held during creation of syncronization objects.
  291.  *
  292.  * Results:
  293.  * None.
  294.  *
  295.  * Side effects:
  296.  * Acquire the master mutex.
  297.  *
  298.  *----------------------------------------------------------------------
  299.  */
  300. void
  301. TclpMasterLock()
  302. {
  303.     if (!init) {
  304. /*
  305.  * There is a fundamental race here that is solved by creating
  306.  * the first Tcl interpreter in a single threaded environment.
  307.  * Once the interpreter has been created, it is safe to create
  308.  * more threads that create interpreters in parallel.
  309.  */
  310. init = 1;
  311. InitializeCriticalSection(&joinLock);
  312. InitializeCriticalSection(&initLock);
  313. InitializeCriticalSection(&masterLock);
  314.     }
  315.     EnterCriticalSection(&masterLock);
  316. }
  317. /*
  318.  *----------------------------------------------------------------------
  319.  *
  320.  * TclpMasterUnlock
  321.  *
  322.  * This procedure is used to release a lock that serializes creation
  323.  * and deletion of synchronization objects.
  324.  *
  325.  * Results:
  326.  * None.
  327.  *
  328.  * Side effects:
  329.  * Release the master mutex.
  330.  *
  331.  *----------------------------------------------------------------------
  332.  */
  333. void
  334. TclpMasterUnlock()
  335. {
  336.     LeaveCriticalSection(&masterLock);
  337. }
  338. /*
  339.  *----------------------------------------------------------------------
  340.  *
  341.  * Tcl_GetAllocMutex
  342.  *
  343.  * This procedure returns a pointer to a statically initialized
  344.  * mutex for use by the memory allocator.  The alloctor must
  345.  * use this lock, because all other locks are allocated...
  346.  *
  347.  * Results:
  348.  * A pointer to a mutex that is suitable for passing to
  349.  * Tcl_MutexLock and Tcl_MutexUnlock.
  350.  *
  351.  * Side effects:
  352.  * None.
  353.  *
  354.  *----------------------------------------------------------------------
  355.  */
  356. Tcl_Mutex *
  357. Tcl_GetAllocMutex()
  358. {
  359. #ifdef TCL_THREADS
  360.     if (!allocOnce) {
  361. InitializeCriticalSection(&allocLock);
  362. allocOnce = 1;
  363.     }
  364.     return &allocLockPtr;
  365. #else
  366.     return NULL;
  367. #endif
  368. }
  369. /*
  370.  *----------------------------------------------------------------------
  371.  *
  372.  * TclpFinalizeLock
  373.  *
  374.  * This procedure is used to destroy all private resources used in
  375.  * this file.
  376.  *
  377.  * Results:
  378.  * None.
  379.  *
  380.  * Side effects:
  381.  * Destroys everything private.  TclpInitLock must be held
  382.  * entering this function.
  383.  *
  384.  *----------------------------------------------------------------------
  385.  */
  386. void
  387. TclFinalizeLock ()
  388. {
  389.     MASTER_LOCK;
  390.     DeleteCriticalSection(&joinLock);
  391.     /* Destroy the critical section that we are holding! */
  392.     DeleteCriticalSection(&masterLock);
  393.     init = 0;
  394. #ifdef TCL_THREADS
  395.     DeleteCriticalSection(&allocLock);
  396.     allocOnce = 0;
  397. #endif
  398.     /* Destroy the critical section that we are holding! */
  399.     DeleteCriticalSection(&initLock);
  400. }
  401. #ifdef TCL_THREADS
  402. /* locally used prototype */
  403. static void FinalizeConditionEvent(ClientData data);
  404. /*
  405.  *----------------------------------------------------------------------
  406.  *
  407.  * Tcl_MutexLock --
  408.  *
  409.  * This procedure is invoked to lock a mutex.  This is a self 
  410.  * initializing mutex that is automatically finalized during
  411.  * Tcl_Finalize.
  412.  *
  413.  * Results:
  414.  * None.
  415.  *
  416.  * Side effects:
  417.  * May block the current thread.  The mutex is aquired when
  418.  * this returns.
  419.  *
  420.  *----------------------------------------------------------------------
  421.  */
  422. void
  423. Tcl_MutexLock(mutexPtr)
  424.     Tcl_Mutex *mutexPtr; /* The lock */
  425. {
  426.     CRITICAL_SECTION *csPtr;
  427.     if (*mutexPtr == NULL) {
  428. MASTER_LOCK;
  429. /* 
  430.  * Double inside master lock check to avoid a race.
  431.  */
  432. if (*mutexPtr == NULL) {
  433.     csPtr = (CRITICAL_SECTION *)ckalloc(sizeof(CRITICAL_SECTION));
  434.     InitializeCriticalSection(csPtr);
  435.     *mutexPtr = (Tcl_Mutex)csPtr;
  436.     TclRememberMutex(mutexPtr);
  437. }
  438. MASTER_UNLOCK;
  439.     }
  440.     csPtr = *((CRITICAL_SECTION **)mutexPtr);
  441.     EnterCriticalSection(csPtr);
  442. }
  443. /*
  444.  *----------------------------------------------------------------------
  445.  *
  446.  * Tcl_MutexUnlock --
  447.  *
  448.  * This procedure is invoked to unlock a mutex.
  449.  *
  450.  * Results:
  451.  * None.
  452.  *
  453.  * Side effects:
  454.  * The mutex is released when this returns.
  455.  *
  456.  *----------------------------------------------------------------------
  457.  */
  458. void
  459. Tcl_MutexUnlock(mutexPtr)
  460.     Tcl_Mutex *mutexPtr; /* The lock */
  461. {
  462.     CRITICAL_SECTION *csPtr = *((CRITICAL_SECTION **)mutexPtr);
  463.     LeaveCriticalSection(csPtr);
  464. }
  465. /*
  466.  *----------------------------------------------------------------------
  467.  *
  468.  * TclpFinalizeMutex --
  469.  *
  470.  * This procedure is invoked to clean up one mutex.  This is only
  471.  * safe to call at the end of time.
  472.  *
  473.  * Results:
  474.  * None.
  475.  *
  476.  * Side effects:
  477.  * The mutex list is deallocated.
  478.  *
  479.  *----------------------------------------------------------------------
  480.  */
  481. void
  482. TclpFinalizeMutex(mutexPtr)
  483.     Tcl_Mutex *mutexPtr;
  484. {
  485.     CRITICAL_SECTION *csPtr = *(CRITICAL_SECTION **)mutexPtr;
  486.     if (csPtr != NULL) {
  487. DeleteCriticalSection(csPtr);
  488. ckfree((char *)csPtr);
  489. *mutexPtr = NULL;
  490.     }
  491. }
  492. /*
  493.  *----------------------------------------------------------------------
  494.  *
  495.  * TclpThreadDataKeyInit --
  496.  *
  497.  * This procedure initializes a thread specific data block key.
  498.  * Each thread has table of pointers to thread specific data.
  499.  * all threads agree on which table entry is used by each module.
  500.  * this is remembered in a "data key", that is just an index into
  501.  * this table.  To allow self initialization, the interface
  502.  * passes a pointer to this key and the first thread to use
  503.  * the key fills in the pointer to the key.  The key should be
  504.  * a process-wide static.
  505.  *
  506.  * Results:
  507.  * None.
  508.  *
  509.  * Side effects:
  510.  * Will allocate memory the first time this process calls for
  511.  * this key.  In this case it modifies its argument
  512.  * to hold the pointer to information about the key.
  513.  *
  514.  *----------------------------------------------------------------------
  515.  */
  516. void
  517. TclpThreadDataKeyInit(keyPtr)
  518.     Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk,
  519.  * really (DWORD **) */
  520. {
  521.     DWORD *indexPtr;
  522.     DWORD newKey;
  523.     MASTER_LOCK;
  524.     if (*keyPtr == NULL) {
  525. indexPtr = (DWORD *)ckalloc(sizeof(DWORD));
  526. newKey = TlsAlloc();
  527.         if (newKey != TLS_OUT_OF_INDEXES) {
  528.             *indexPtr = newKey;
  529.         } else {
  530.             panic("TlsAlloc failed from TclpThreadDataKeyInit!"); /* this should be a fatal error */
  531.         }
  532. *keyPtr = (Tcl_ThreadDataKey)indexPtr;
  533. TclRememberDataKey(keyPtr);
  534.     }
  535.     MASTER_UNLOCK;
  536. }
  537. /*
  538.  *----------------------------------------------------------------------
  539.  *
  540.  * TclpThreadDataKeyGet --
  541.  *
  542.  * This procedure returns a pointer to a block of thread local storage.
  543.  *
  544.  * Results:
  545.  * A thread-specific pointer to the data structure, or NULL
  546.  * if the memory has not been assigned to this key for this thread.
  547.  *
  548.  * Side effects:
  549.  * None.
  550.  *
  551.  *----------------------------------------------------------------------
  552.  */
  553. VOID *
  554. TclpThreadDataKeyGet(keyPtr)
  555.     Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk,
  556.  * really (DWORD **) */
  557. {
  558.     DWORD *indexPtr = *(DWORD **)keyPtr;
  559.     LPVOID result;
  560.     if (indexPtr == NULL) {
  561. return NULL;
  562.     } else {
  563.         result = TlsGetValue(*indexPtr);
  564.         if ((result == NULL) && (GetLastError() != NO_ERROR)) {
  565.             panic("TlsGetValue failed from TclpThreadDataKeyGet!");
  566.         }
  567. return result;
  568.     }
  569. }
  570. /*
  571.  *----------------------------------------------------------------------
  572.  *
  573.  * TclpThreadDataKeySet --
  574.  *
  575.  * This procedure sets the pointer to a block of thread local storage.
  576.  *
  577.  * Results:
  578.  * None.
  579.  *
  580.  * Side effects:
  581.  * Sets up the thread so future calls to TclpThreadDataKeyGet with
  582.  * this key will return the data pointer.
  583.  *
  584.  *----------------------------------------------------------------------
  585.  */
  586. void
  587. TclpThreadDataKeySet(keyPtr, data)
  588.     Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk,
  589.  * really (pthread_key_t **) */
  590.     VOID *data; /* Thread local storage */
  591. {
  592.     DWORD *indexPtr = *(DWORD **)keyPtr;
  593.     BOOL success;
  594.     success = TlsSetValue(*indexPtr, (void *)data);
  595.     if (!success) {
  596.         panic("TlsSetValue failed from TclpThreadDataKeySet!");
  597.     }
  598. }
  599. /*
  600.  *----------------------------------------------------------------------
  601.  *
  602.  * TclpFinalizeThreadData --
  603.  *
  604.  * This procedure cleans up the thread-local storage.  This is
  605.  * called once for each thread.
  606.  *
  607.  * Results:
  608.  * None.
  609.  *
  610.  * Side effects:
  611.  * Frees up the memory.
  612.  *
  613.  *----------------------------------------------------------------------
  614.  */
  615. void
  616. TclpFinalizeThreadData(keyPtr)
  617.     Tcl_ThreadDataKey *keyPtr;
  618. {
  619.     VOID *result;
  620.     DWORD *indexPtr;
  621.     BOOL success;
  622.     if (*keyPtr != NULL) {
  623. indexPtr = *(DWORD **)keyPtr;
  624. result = (VOID *)TlsGetValue(*indexPtr);
  625. if (result != NULL) {
  626. #if defined(USE_THREAD_ALLOC) && !defined(TCL_MEM_DEBUG)
  627.     if (indexPtr == &tlsKey) {
  628. TclpFreeAllocCache(result);
  629. return;
  630.     }
  631. #endif
  632.     ckfree((char *)result);
  633.     success = TlsSetValue(*indexPtr, (void *)NULL);
  634.             if (!success) {
  635.                 panic("TlsSetValue failed from TclpFinalizeThreadData!");
  636.             }
  637. } else {
  638.             if (GetLastError() != NO_ERROR) {
  639.                 panic("TlsGetValue failed from TclpFinalizeThreadData!");
  640.             }
  641. }
  642.     }
  643. }
  644. /*
  645.  *----------------------------------------------------------------------
  646.  *
  647.  * TclpFinalizeThreadDataKey --
  648.  *
  649.  * This procedure is invoked to clean up one key.  This is a
  650.  * process-wide storage identifier.  The thread finalization code
  651.  * cleans up the thread local storage itself.
  652.  *
  653.  * This assumes the master lock is held.
  654.  *
  655.  * Results:
  656.  * None.
  657.  *
  658.  * Side effects:
  659.  * The key is deallocated.
  660.  *
  661.  *----------------------------------------------------------------------
  662.  */
  663. void
  664. TclpFinalizeThreadDataKey(keyPtr)
  665.     Tcl_ThreadDataKey *keyPtr;
  666. {
  667.     DWORD *indexPtr;
  668.     BOOL success;
  669.     if (*keyPtr != NULL) {
  670. indexPtr = *(DWORD **)keyPtr;
  671. success = TlsFree(*indexPtr);
  672.         if (!success) {
  673.             panic("TlsFree failed from TclpFinalizeThreadDataKey!");
  674.         }
  675. ckfree((char *)indexPtr);
  676. *keyPtr = NULL;
  677.     }
  678. }
  679. /*
  680.  *----------------------------------------------------------------------
  681.  *
  682.  * Tcl_ConditionWait --
  683.  *
  684.  * This procedure is invoked to wait on a condition variable.
  685.  * The mutex is atomically released as part of the wait, and
  686.  * automatically grabbed when the condition is signaled.
  687.  *
  688.  * The mutex must be held when this procedure is called.
  689.  *
  690.  * Results:
  691.  * None.
  692.  *
  693.  * Side effects:
  694.  * May block the current thread.  The mutex is aquired when
  695.  * this returns.  Will allocate memory for a HANDLE
  696.  * and initialize this the first time this Tcl_Condition is used.
  697.  *
  698.  *----------------------------------------------------------------------
  699.  */
  700. void
  701. Tcl_ConditionWait(condPtr, mutexPtr, timePtr)
  702.     Tcl_Condition *condPtr; /* Really (WinCondition **) */
  703.     Tcl_Mutex *mutexPtr; /* Really (CRITICAL_SECTION **) */
  704.     Tcl_Time *timePtr; /* Timeout on waiting period */
  705. {
  706.     WinCondition *winCondPtr; /* Per-condition queue head */
  707.     CRITICAL_SECTION *csPtr; /* Caller's Mutex, after casting */
  708.     DWORD wtime; /* Windows time value */
  709.     int timeout; /* True if we got a timeout */
  710.     int doExit = 0; /* True if we need to do exit setup */
  711.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  712.     /*
  713.      * Self initialize the two parts of the condition.
  714.      * The per-condition and per-thread parts need to be
  715.      * handled independently.
  716.      */
  717.     if (tsdPtr->flags == WIN_THREAD_UNINIT) {
  718. MASTER_LOCK;
  719. /* 
  720.  * Create the per-thread event and queue pointers.
  721.  */
  722. if (tsdPtr->flags == WIN_THREAD_UNINIT) {
  723.     tsdPtr->condEvent = CreateEvent(NULL, TRUE /* manual reset */,
  724. FALSE /* non signaled */, NULL);
  725.     tsdPtr->nextPtr = NULL;
  726.     tsdPtr->prevPtr = NULL;
  727.     tsdPtr->flags = WIN_THREAD_RUNNING;
  728.     doExit = 1;
  729. }
  730. MASTER_UNLOCK;
  731. if (doExit) {
  732.     /*
  733.      * Create a per-thread exit handler to clean up the condEvent.
  734.      * We must be careful to do this outside the Master Lock
  735.      * because Tcl_CreateThreadExitHandler uses its own
  736.      * ThreadSpecificData, and initializing that may drop
  737.      * back into the Master Lock.
  738.      */
  739.     
  740.     Tcl_CreateThreadExitHandler(FinalizeConditionEvent,
  741.     (ClientData) tsdPtr);
  742. }
  743.     }
  744.     if (*condPtr == NULL) {
  745. MASTER_LOCK;
  746. /*
  747.  * Initialize the per-condition queue pointers and Mutex.
  748.  */
  749. if (*condPtr == NULL) {
  750.     winCondPtr = (WinCondition *)ckalloc(sizeof(WinCondition));
  751.     InitializeCriticalSection(&winCondPtr->condLock);
  752.     winCondPtr->firstPtr = NULL;
  753.     winCondPtr->lastPtr = NULL;
  754.     *condPtr = (Tcl_Condition)winCondPtr;
  755.     TclRememberCondition(condPtr);
  756. }
  757. MASTER_UNLOCK;
  758.     }
  759.     csPtr = *((CRITICAL_SECTION **)mutexPtr);
  760.     winCondPtr = *((WinCondition **)condPtr);
  761.     if (timePtr == NULL) {
  762. wtime = INFINITE;
  763.     } else {
  764. wtime = timePtr->sec * 1000 + timePtr->usec / 1000;
  765.     }
  766.     /*
  767.      * Queue the thread on the condition, using
  768.      * the per-condition lock for serialization.
  769.      */
  770.     tsdPtr->flags = WIN_THREAD_BLOCKED;
  771.     tsdPtr->nextPtr = NULL;
  772.     EnterCriticalSection(&winCondPtr->condLock);
  773.     tsdPtr->prevPtr = winCondPtr->lastPtr; /* A: */
  774.     winCondPtr->lastPtr = tsdPtr;
  775.     if (tsdPtr->prevPtr != NULL) {
  776.         tsdPtr->prevPtr->nextPtr = tsdPtr;
  777.     }
  778.     if (winCondPtr->firstPtr == NULL) {
  779.         winCondPtr->firstPtr = tsdPtr;
  780.     }
  781.     /*
  782.      * Unlock the caller's mutex and wait for the condition, or a timeout.
  783.      * There is a minor issue here in that we don't count down the
  784.      * timeout if we get notified, but another thread grabs the condition
  785.      * before we do.  In that race condition we'll wait again for the
  786.      * full timeout.  Timed waits are dubious anyway.  Either you have
  787.      * the locking protocol wrong and are masking a deadlock,
  788.      * or you are using conditions to pause your thread.
  789.      */
  790.     
  791.     LeaveCriticalSection(csPtr);
  792.     timeout = 0;
  793.     while (!timeout && (tsdPtr->flags & WIN_THREAD_BLOCKED)) {
  794. ResetEvent(tsdPtr->condEvent);
  795. LeaveCriticalSection(&winCondPtr->condLock);
  796. if (WaitForSingleObject(tsdPtr->condEvent, wtime) == WAIT_TIMEOUT) {
  797.     timeout = 1;
  798. }
  799. EnterCriticalSection(&winCondPtr->condLock);
  800.     }
  801.     /*
  802.      * Be careful on timeouts because the signal might arrive right around
  803.      * the time limit and someone else could have taken us off the queue.
  804.      */
  805.     
  806.     if (timeout) {
  807. if (tsdPtr->flags & WIN_THREAD_RUNNING) {
  808.     timeout = 0;
  809. } else {
  810.     /*
  811.      * When dequeuing, we can leave the tsdPtr->nextPtr
  812.      * and tsdPtr->prevPtr with dangling pointers because
  813.      * they are reinitialilzed w/out reading them when the
  814.      * thread is enqueued later.
  815.      */
  816.             if (winCondPtr->firstPtr == tsdPtr) {
  817.                 winCondPtr->firstPtr = tsdPtr->nextPtr;
  818.             } else {
  819.                 tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
  820.             }
  821.             if (winCondPtr->lastPtr == tsdPtr) {
  822.                 winCondPtr->lastPtr = tsdPtr->prevPtr;
  823.             } else {
  824.                 tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
  825.             }
  826.             tsdPtr->flags = WIN_THREAD_RUNNING;
  827. }
  828.     }
  829.     LeaveCriticalSection(&winCondPtr->condLock);
  830.     EnterCriticalSection(csPtr);
  831. }
  832. /*
  833.  *----------------------------------------------------------------------
  834.  *
  835.  * Tcl_ConditionNotify --
  836.  *
  837.  * This procedure is invoked to signal a condition variable.
  838.  *
  839.  * The mutex must be held during this call to avoid races,
  840.  * but this interface does not enforce that.
  841.  *
  842.  * Results:
  843.  * None.
  844.  *
  845.  * Side effects:
  846.  * May unblock another thread.
  847.  *
  848.  *----------------------------------------------------------------------
  849.  */
  850. void
  851. Tcl_ConditionNotify(condPtr)
  852.     Tcl_Condition *condPtr;
  853. {
  854.     WinCondition *winCondPtr;
  855.     ThreadSpecificData *tsdPtr;
  856.     if (condPtr != NULL) {
  857. winCondPtr = *((WinCondition **)condPtr);
  858. if (winCondPtr == NULL) {
  859.     return;
  860. }
  861. /*
  862.  * Loop through all the threads waiting on the condition
  863.  * and notify them (i.e., broadcast semantics).  The queue
  864.  * manipulation is guarded by the per-condition coordinating mutex.
  865.  */
  866. EnterCriticalSection(&winCondPtr->condLock);
  867. while (winCondPtr->firstPtr != NULL) {
  868.     tsdPtr = winCondPtr->firstPtr;
  869.     winCondPtr->firstPtr = tsdPtr->nextPtr;
  870.     if (winCondPtr->lastPtr == tsdPtr) {
  871. winCondPtr->lastPtr = NULL;
  872.     }
  873.     tsdPtr->flags = WIN_THREAD_RUNNING;
  874.     tsdPtr->nextPtr = NULL;
  875.     tsdPtr->prevPtr = NULL; /* Not strictly necessary, see A: */
  876.     SetEvent(tsdPtr->condEvent);
  877. }
  878. LeaveCriticalSection(&winCondPtr->condLock);
  879.     } else {
  880. /*
  881.  * Noone has used the condition variable, so there are no waiters.
  882.  */
  883.     }
  884. }
  885. /*
  886.  *----------------------------------------------------------------------
  887.  *
  888.  * FinalizeConditionEvent --
  889.  *
  890.  * This procedure is invoked to clean up the per-thread
  891.  * event used to implement condition waiting.
  892.  * This is only safe to call at the end of time.
  893.  *
  894.  * Results:
  895.  * None.
  896.  *
  897.  * Side effects:
  898.  * The per-thread event is closed.
  899.  *
  900.  *----------------------------------------------------------------------
  901.  */
  902. static void
  903. FinalizeConditionEvent(data)
  904.     ClientData data;
  905. {
  906.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)data;
  907.     tsdPtr->flags = WIN_THREAD_UNINIT;
  908.     CloseHandle(tsdPtr->condEvent);
  909. }
  910. /*
  911.  *----------------------------------------------------------------------
  912.  *
  913.  * TclpFinalizeCondition --
  914.  *
  915.  * This procedure is invoked to clean up a condition variable.
  916.  * This is only safe to call at the end of time.
  917.  *
  918.  * This assumes the Master Lock is held.
  919.  *
  920.  * Results:
  921.  * None.
  922.  *
  923.  * Side effects:
  924.  * The condition variable is deallocated.
  925.  *
  926.  *----------------------------------------------------------------------
  927.  */
  928. void
  929. TclpFinalizeCondition(condPtr)
  930.     Tcl_Condition *condPtr;
  931. {
  932.     WinCondition *winCondPtr = *(WinCondition **)condPtr;
  933.     /*
  934.      * Note - this is called long after the thread-local storage is
  935.      * reclaimed.  The per-thread condition waiting event is
  936.      * reclaimed earlier in a per-thread exit handler, which is
  937.      * called before thread local storage is reclaimed.
  938.      */
  939.     if (winCondPtr != NULL) {
  940. DeleteCriticalSection(&winCondPtr->condLock);
  941. ckfree((char *)winCondPtr);
  942. *condPtr = NULL;
  943.     }
  944. }
  945. /*
  946.  * Additions by AOL for specialized thread memory allocator.
  947.  */
  948. #if defined(USE_THREAD_ALLOC) && !defined(TCL_MEM_DEBUG)
  949. Tcl_Mutex *
  950. TclpNewAllocMutex(void)
  951. {
  952.     struct allocMutex *lockPtr;
  953.     lockPtr = malloc(sizeof(struct allocMutex));
  954.     if (lockPtr == NULL) {
  955. panic("could not allocate lock");
  956.     }
  957.     lockPtr->tlock = (Tcl_Mutex) &lockPtr->wlock;
  958.     InitializeCriticalSection(&lockPtr->wlock);
  959.     return &lockPtr->tlock;
  960. }
  961. void
  962. TclpFreeAllocMutex(mutex)
  963.     Tcl_Mutex *mutex; /* The alloc mutex to free. */
  964. {
  965.     allocMutex* lockPtr = (allocMutex*) mutex;
  966.     if (!lockPtr) return;
  967.     DeleteCriticalSection(&lockPtr->wlock);
  968.     free(lockPtr);
  969. }
  970. void *
  971. TclpGetAllocCache(void)
  972. {
  973.     VOID *result;
  974.     if (!once) {
  975. /*
  976.  * We need to make sure that TclpFreeAllocCache is called
  977.  * on each thread that calls this, but only on threads that
  978.  * call this.
  979.  */
  980.      tlsKey = TlsAlloc();
  981. once = 1;
  982. if (tlsKey == TLS_OUT_OF_INDEXES) {
  983.     panic("could not allocate thread local storage");
  984. }
  985.     }
  986.     result = TlsGetValue(tlsKey);
  987.     if ((result == NULL) && (GetLastError() != NO_ERROR)) {
  988.         panic("TlsGetValue failed from TclpGetAllocCache!");
  989.     }
  990.     return result;
  991. }
  992. void
  993. TclpSetAllocCache(void *ptr)
  994. {
  995.     BOOL success;
  996.     success = TlsSetValue(tlsKey, ptr);
  997.     if (!success) {
  998.         panic("TlsSetValue failed from TclpSetAllocCache!");
  999.     }
  1000. }
  1001. void
  1002. TclpFreeAllocCache(void *ptr)
  1003. {
  1004.     BOOL success;
  1005.     if (ptr != NULL) {
  1006.         /*
  1007.          * Called by the pthread lib when a thread exits
  1008.          */
  1009.         TclFreeAllocCache(ptr);
  1010.         success = TlsSetValue(tlsKey, NULL);
  1011.         if (!success) {
  1012.             panic("TlsSetValue failed from TclpFreeAllocCache!");
  1013.         }
  1014.     } else if (once) { 
  1015.         /*
  1016.          * Called by us in TclFinalizeThreadAlloc() during
  1017.          * the library finalization initiated from Tcl_Finalize()
  1018.          */
  1019.         success = TlsFree(tlsKey);
  1020.         if (!success) {
  1021.             Tcl_Panic("TlsFree failed from TclpFreeAllocCache!");
  1022.         }
  1023.         once = 0; /* reset for next time. */
  1024.     }
  1025. }
  1026. #endif /* USE_THREAD_ALLOC */
  1027. #endif /* TCL_THREADS */