/[dtapublic]/projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclwinthrd.c
ViewVC logotype

Diff of /projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclwinthrd.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.25  
changed lines
  Added in v.71

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25