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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25