Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/win/tclWinThrd.c @ 47

Last change on this file since 47 was 25, checked in by landauf, 17 years ago

added tcl to libs

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