@@ -69,56 +69,6 @@ static int allocOnce = 0;
6969
7070static CRITICAL_SECTION joinLock ;
7171
72- /*
73- * Condition variables are implemented with a combination of a per-thread
74- * Windows Event and a per-condition waiting queue. The idea is that each
75- * thread has its own Event that it waits on when it is doing a ConditionWait;
76- * it uses the same event for all condition variables because it only waits on
77- * one at a time. Each condition variable has a queue of waiting threads, and
78- * a mutex used to serialize access to this queue.
79- *
80- * Special thanks to David Nichols and Jim Davidson for advice on the
81- * Condition Variable implementation.
82- */
83-
84- /*
85- * The per-thread event and queue pointers.
86- */
87-
88- #if TCL_THREADS
89-
90- typedef struct ThreadSpecificData {
91- HANDLE condEvent ; /* Per-thread condition event */
92- struct ThreadSpecificData * nextPtr ; /* Queue pointers */
93- struct ThreadSpecificData * prevPtr ;
94- int flags ; /* See ThreadStateFlags below */
95- } ThreadSpecificData ;
96- static Tcl_ThreadDataKey dataKey ;
97-
98- #endif /* TCL_THREADS */
99-
100- /*
101- * State bits for the thread.
102- */
103- enum ThreadStateFlags {
104- WIN_THREAD_UNINIT = 0x0 , /* Uninitialized. Must be zero because of the
105- * way ThreadSpecificData is created. */
106- WIN_THREAD_RUNNING = 0x1 , /* Running, not waiting. */
107- WIN_THREAD_BLOCKED = 0x2 /* Waiting, or trying to wait. */
108- };
109-
110- /*
111- * The per condition queue pointers and the Mutex used to serialize access to
112- * the queue.
113- */
114-
115- typedef struct {
116- CRITICAL_SECTION condLock ; /* Lock to serialize queuing on the
117- * condition. */
118- ThreadSpecificData * firstPtr ; /* Queue pointers */
119- ThreadSpecificData * lastPtr ;
120- } WinCondition ;
121-
12272/*
12373 * Additions by AOL for specialized thread memory allocator.
12474 */
@@ -705,8 +655,8 @@ TclpFinalizeMutex(
705655 *
706656 * Side effects:
707657 * May block the current thread. The mutex is acquired when this returns.
708- * Will allocate memory for a HANDLE and initialize this the first time
709- * this Tcl_Condition is used.
658+ * Will allocate memory for a CONDITION_VARIABLE and initialize the first
659+ * time this Tcl_Condition is used.
710660 *
711661 *----------------------------------------------------------------------
712662 */
@@ -717,149 +667,44 @@ Tcl_ConditionWait(
717667 Tcl_Mutex * mutexPtr , /* Really (CRITICAL_SECTION **) */
718668 const Tcl_Time * timePtr ) /* Timeout on waiting period */
719669{
720- WinCondition * winCondPtr ; /* Per-condition queue head */
670+ CONDITION_VARIABLE * cvPtr ; /* Per-condition queue head */
721671 WMutex * wmPtr ; /* Caller's Mutex, after casting */
722672 DWORD wtime ; /* Windows time value */
723- int timeout ; /* True if we got a timeout */
724- int counter ; /* Caller's Mutex counter */
725- int doExit = 0 ; /* True if we need to do exit setup */
726- ThreadSpecificData * tsdPtr = TCL_TSD_INIT (& dataKey );
727673
728- /*
729- * Self initialize the two parts of the condition. The per-condition and
730- * per-thread parts need to be handled independently.
731- */
732-
733- if (tsdPtr -> flags == WIN_THREAD_UNINIT ) {
734- TclpGlobalLock ();
735-
736- /*
737- * Create the per-thread event and queue pointers.
738- */
739-
740- if (tsdPtr -> flags == WIN_THREAD_UNINIT ) {
741- tsdPtr -> condEvent = CreateEventW (NULL , TRUE /* manual reset */ ,
742- FALSE /* non signaled */ , NULL );
743- tsdPtr -> nextPtr = NULL ;
744- tsdPtr -> prevPtr = NULL ;
745- tsdPtr -> flags = WIN_THREAD_RUNNING ;
746- doExit = 1 ;
747- }
748- TclpGlobalUnlock ();
749-
750- if (doExit ) {
751- /*
752- * Create a per-thread exit handler to clean up the condEvent. We
753- * must be careful to do this outside the Global Lock because
754- * Tcl_CreateThreadExitHandler uses its own ThreadSpecificData,
755- * and initializing that may drop back into the Global Lock.
756- */
757-
758- Tcl_CreateThreadExitHandler (FinalizeConditionEvent , tsdPtr );
759- }
674+ if (timePtr == NULL ) {
675+ wtime = INFINITE ;
676+ } else {
677+ wtime = (DWORD )timePtr -> sec * 1000 + (DWORD )timePtr -> usec / 1000 ;
760678 }
761679
762680 if (* condPtr == NULL ) {
763681 TclpGlobalLock ();
764-
765- /*
766- * Initialize the per-condition queue pointers and Mutex.
767- */
768-
769682 if (* condPtr == NULL ) {
770- winCondPtr = (WinCondition * )Tcl_Alloc (sizeof (WinCondition ));
771- InitializeCriticalSection (& winCondPtr -> condLock );
772- winCondPtr -> firstPtr = NULL ;
773- winCondPtr -> lastPtr = NULL ;
774- * condPtr = (Tcl_Condition ) winCondPtr ;
683+ cvPtr = (CONDITION_VARIABLE * )Tcl_Alloc (sizeof (* cvPtr ));
684+ InitializeConditionVariable (cvPtr );
685+ * condPtr = (Tcl_Condition ) cvPtr ;
775686 TclRememberCondition (condPtr );
776687 }
777688 TclpGlobalUnlock ();
778689 }
779690 wmPtr = * ((WMutex * * )mutexPtr );
780- winCondPtr = * ((WinCondition * * )condPtr );
781- if (timePtr == NULL ) {
782- wtime = INFINITE ;
783- } else {
784- wtime = (DWORD )timePtr -> sec * 1000 + (DWORD )timePtr -> usec / 1000 ;
785- }
691+ cvPtr = * ((CONDITION_VARIABLE * * )condPtr );
786692
787- /*
788- * Queue the thread on the condition, using the per-condition lock for
789- * serialization.
790- */
791-
792- tsdPtr -> flags = WIN_THREAD_BLOCKED ;
793- tsdPtr -> nextPtr = NULL ;
794- EnterCriticalSection (& winCondPtr -> condLock );
795- tsdPtr -> prevPtr = winCondPtr -> lastPtr ; /* A: */
796- winCondPtr -> lastPtr = tsdPtr ;
797- if (tsdPtr -> prevPtr != NULL ) {
798- tsdPtr -> prevPtr -> nextPtr = tsdPtr ;
799- }
800- if (winCondPtr -> firstPtr == NULL ) {
801- winCondPtr -> firstPtr = tsdPtr ;
802- }
803-
804- /*
805- * Unlock the caller's mutex and wait for the condition, or a timeout.
806- * There is a minor issue here in that we don't count down the timeout if
807- * we get notified, but another thread grabs the condition before we do.
808- * In that race condition we'll wait again for the full timeout. Timed
809- * waits are dubious anyway. Either you have the locking protocol wrong
810- * and are masking a deadlock, or you are using conditions to pause your
811- * thread.
812- */
813-
814- counter = wmPtr -> counter ;
693+ int counter = wmPtr -> counter ;
815694 wmPtr -> counter = 0 ;
816695 LONG mythread = GetCurrentThreadId ();
817696 assert (wmPtr -> thread == mythread );
818697 wmPtr -> thread = 0 ;
819- LeaveCriticalSection (& wmPtr -> crit );
820- timeout = 0 ;
821- while (!timeout && (tsdPtr -> flags & WIN_THREAD_BLOCKED )) {
822- ResetEvent (tsdPtr -> condEvent );
823- LeaveCriticalSection (& winCondPtr -> condLock );
824- if (WaitForSingleObjectEx (tsdPtr -> condEvent , wtime ,
825- TRUE) == WAIT_TIMEOUT ) {
826- timeout = 1 ;
827- }
828- EnterCriticalSection (& winCondPtr -> condLock );
829- }
830-
831- /*
832- * Be careful on timeouts because the signal might arrive right around the
833- * time limit and someone else could have taken us off the queue.
834- */
835-
836- if (timeout ) {
837- if (tsdPtr -> flags & WIN_THREAD_RUNNING ) {
838- timeout = 0 ;
839- } else {
840- /*
841- * When dequeueing, we can leave the tsdPtr->nextPtr and
842- * tsdPtr->prevPtr with dangling pointers because they are
843- * reinitialized w/out reading them when the thread is enqueued
844- * later.
845- */
846-
847- if (winCondPtr -> firstPtr == tsdPtr ) {
848- winCondPtr -> firstPtr = tsdPtr -> nextPtr ;
849- } else {
850- tsdPtr -> prevPtr -> nextPtr = tsdPtr -> nextPtr ;
851- }
852- if (winCondPtr -> lastPtr == tsdPtr ) {
853- winCondPtr -> lastPtr = tsdPtr -> prevPtr ;
854- } else {
855- tsdPtr -> nextPtr -> prevPtr = tsdPtr -> prevPtr ;
856- }
857- tsdPtr -> flags = WIN_THREAD_RUNNING ;
698+ if (SleepConditionVariableCS (cvPtr ,
699+ & wmPtr -> crit , wtime ) == 0 ) {
700+ DWORD err = GetLastError ();
701+ if (err != ERROR_TIMEOUT ) {
702+ Tcl_Panic (
703+ "Tcl_ConditionWait: SleepConditionVariableCS error %d" ,
704+ err );
858705 }
859706 }
860707
861- LeaveCriticalSection (& winCondPtr -> condLock );
862- EnterCriticalSection (& wmPtr -> crit );
863708 wmPtr -> counter = counter ;
864709 wmPtr -> thread = mythread ;
865710}
@@ -887,70 +732,18 @@ void
887732Tcl_ConditionNotify (
888733 Tcl_Condition * condPtr )
889734{
890- WinCondition * winCondPtr ;
891- ThreadSpecificData * tsdPtr ;
735+ CONDITION_VARIABLE * cvPtr ;
892736
737+ /* If uninitialized, no could be waiting on the condition variable */
893738 if (* condPtr != NULL ) {
894- winCondPtr = * ((WinCondition * * )condPtr );
739+ cvPtr = * ((CONDITION_VARIABLE * * )condPtr );
895740
896- if (winCondPtr == NULL ) {
897- return ;
741+ if (cvPtr ) {
742+ WakeAllConditionVariable ( cvPtr ) ;
898743 }
899-
900- /*
901- * Loop through all the threads waiting on the condition and notify
902- * them (i.e., broadcast semantics). The queue manipulation is guarded
903- * by the per-condition coordinating mutex.
904- */
905-
906- EnterCriticalSection (& winCondPtr -> condLock );
907- while (winCondPtr -> firstPtr != NULL ) {
908- tsdPtr = winCondPtr -> firstPtr ;
909- winCondPtr -> firstPtr = tsdPtr -> nextPtr ;
910- if (winCondPtr -> lastPtr == tsdPtr ) {
911- winCondPtr -> lastPtr = NULL ;
912- }
913- tsdPtr -> flags = WIN_THREAD_RUNNING ;
914- tsdPtr -> nextPtr = NULL ;
915- tsdPtr -> prevPtr = NULL ; /* Not strictly necessary, see A: */
916- SetEvent (tsdPtr -> condEvent );
917- }
918- LeaveCriticalSection (& winCondPtr -> condLock );
919- } else {
920- /*
921- * No-one has used the condition variable, so there are no waiters.
922- */
923744 }
924745}
925746
926- /*
927- *----------------------------------------------------------------------
928- *
929- * FinalizeConditionEvent --
930- *
931- * This procedure is invoked to clean up the per-thread event used to
932- * implement condition waiting. This is only safe to call at the end of
933- * time.
934- *
935- * Results:
936- * None.
937- *
938- * Side effects:
939- * The per-thread event is closed.
940- *
941- *----------------------------------------------------------------------
942- */
943-
944- static void
945- FinalizeConditionEvent (
946- void * data )
947- {
948- ThreadSpecificData * tsdPtr = (ThreadSpecificData * ) data ;
949-
950- tsdPtr -> flags = WIN_THREAD_UNINIT ;
951- CloseHandle (tsdPtr -> condEvent );
952- }
953-
954747/*
955748 *----------------------------------------------------------------------
956749 *
@@ -974,18 +767,10 @@ void
974767TclpFinalizeCondition (
975768 Tcl_Condition * condPtr )
976769{
977- WinCondition * winCondPtr = * (WinCondition * * )condPtr ;
978-
979- /*
980- * Note - this is called long after the thread-local storage is reclaimed.
981- * The per-thread condition waiting event is reclaimed earlier in a
982- * per-thread exit handler, which is called before thread local storage is
983- * reclaimed.
984- */
770+ CONDITION_VARIABLE * cvPtr = * (CONDITION_VARIABLE * * )condPtr ;
985771
986- if (winCondPtr != NULL ) {
987- DeleteCriticalSection (& winCondPtr -> condLock );
988- Tcl_Free (winCondPtr );
772+ if (cvPtr ) {
773+ Tcl_Free (cvPtr );
989774 * condPtr = NULL ;
990775 }
991776}
0 commit comments