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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25