/[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

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

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

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25