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