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

Annotation of /projs/ets/trunk/src/c_tcl_base_7_5_w_mods/tclwinthrd.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 220 - (hide annotations) (download)
Sun Jul 22 15:58:07 2018 UTC (5 years, 11 months ago) by dashley
File MIME type: text/plain
File size: 22413 byte(s)
Reorganize.
1 dashley 71 /* $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:eol-style native
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25