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