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