Artifact [d1f3ad866e]
Not logged in

Artifact d1f3ad866ef4a1d67fd96549e0dbdc561a6b156a4fd70b271ecda9148fe06184:

Attachment "POC.diff" to ticket [893f8cc5db] added by chw 2025-08-23 05:01:29.
Index: win/tclWinThrd.c
==================================================================
--- win/tclWinThrd.c
+++ win/tclWinThrd.c
@@ -47,10 +47,21 @@
     CRITICAL_SECTION crit;
 } allocLock;
 static Tcl_Mutex allocLockPtr = &allocLock;
 static int allocOnce = 0;
 
+/*
+ * Although CRITICAL_SECTIONs can be nested, we need to keep track
+ * of their lock counts for condition variables.
+ */
+
+typedef struct WMutex {
+    CRITICAL_SECTION crit;
+    volatile Tcl_ThreadId thread;
+    volatile int counter;
+} WMutex;
+
 #endif /* TCL_THREADS */
 
 /*
  * The joinLock serializes Create- and ExitThread. This is necessary to
  * prevent a race where a new joinable thread exits before the creating thread
@@ -551,31 +562,39 @@
  *----------------------------------------------------------------------
  */
 
 void
 Tcl_MutexLock(
-    Tcl_Mutex *mutexPtr)	/* The lock */
+    Tcl_Mutex *mutexPtr)	/* Really (WMutex **) */
 {
-    CRITICAL_SECTION *csPtr;
+    WMutex *wmPtr;
 
     if (*mutexPtr == NULL) {
 	TclpGlobalLock();
 
 	/*
 	 * Double inside global lock check to avoid a race.
 	 */
 
 	if (*mutexPtr == NULL) {
-	    csPtr = (CRITICAL_SECTION *) Tcl_Alloc(sizeof(CRITICAL_SECTION));
-	    InitializeCriticalSection(csPtr);
-	    *mutexPtr = (Tcl_Mutex) csPtr;
+	    wmPtr = (WMutex *) Tcl_Alloc(sizeof(WMutex));
+	    InitializeCriticalSection(&wmPtr->crit);
+	    wmPtr->thread = 0;
+	    wmPtr->count = 0;
+	    *mutexPtr = (Tcl_Mutex) wmPtr;
 	    TclRememberMutex(mutexPtr);
 	}
 	TclpGlobalUnlock();
+    } else {
+	wmPtr = *((WMutex **)mutexPtr);
     }
-    csPtr = *((CRITICAL_SECTION **)mutexPtr);
-    EnterCriticalSection(csPtr);
+    if (wmPtr->thread != Tcl_GetCurrentThread() || wmPtr->counter == 0) {
+	EnterCriticalSection(&wmPtr->crit);
+	wmPtr->thread = Tcl_GetCurrentThread();
+	wmPtr->counter = 0;
+    }
+    wmPtr->counter++;
 }
 
 /*
  *----------------------------------------------------------------------
  *
@@ -592,15 +611,19 @@
  *----------------------------------------------------------------------
  */
 
 void
 Tcl_MutexUnlock(
-    Tcl_Mutex *mutexPtr)	/* The lock */
+    Tcl_Mutex *mutexPtr)	/* Really (WMutex **) */
 {
-    CRITICAL_SECTION *csPtr = *((CRITICAL_SECTION **)mutexPtr);
+    WMutex *wmPtr = *((WMutex **)mutexPtr);
 
-    LeaveCriticalSection(csPtr);
+    wmPtr->counter--;
+    if (wmPtr->counter == 0) {
+	wmPtr->thread = 0;
+	LeaveCriticalSection(&wmPtr->crit);
+    }
 }
 
 /*
  *----------------------------------------------------------------------
  *
@@ -618,17 +641,18 @@
  *----------------------------------------------------------------------
  */
 
 void
 TclpFinalizeMutex(
-    Tcl_Mutex *mutexPtr)
+    Tcl_Mutex *mutexPtr)	/* Really (WMutex **) */
 {
-    CRITICAL_SECTION *csPtr = *(CRITICAL_SECTION **)mutexPtr;
+    WMutex *wmPtr;
 
-    if (csPtr != NULL) {
-	DeleteCriticalSection(csPtr);
-	Tcl_Free(csPtr);
+    if (mutexPtr != NULL) {
+	wmPtr = *((WMutex **)mutexPtr);
+	DeleteCriticalSection(&wmPtr->crit);
+	Tcl_Free(wmPtr);
 	*mutexPtr = NULL;
     }
 }
 
 /*
@@ -658,13 +682,14 @@
     Tcl_Condition *condPtr,	/* Really (WinCondition **) */
     Tcl_Mutex *mutexPtr,	/* Really (CRITICAL_SECTION **) */
     const Tcl_Time *timePtr)	/* Timeout on waiting period */
 {
     WinCondition *winCondPtr;	/* Per-condition queue head */
-    CRITICAL_SECTION *csPtr;	/* Caller's Mutex, after casting */
+    WMutex *wmPtr;		/* Caller's Mutex, after casting */
     DWORD wtime;		/* Windows time value */
     int timeout;		/* True if we got a timeout */
+    int counter;		/* Caller's Mutex counter */
     int doExit = 0;		/* True if we need to do exit setup */
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
 
     /*
      * Self initialize the two parts of the condition. The per-condition and
@@ -715,11 +740,11 @@
 	    *condPtr = (Tcl_Condition) winCondPtr;
 	    TclRememberCondition(condPtr);
 	}
 	TclpGlobalUnlock();
     }
-    csPtr = *((CRITICAL_SECTION **)mutexPtr);
+    wmPtr = *((WMutex **)mutexPtr);
     winCondPtr = *((WinCondition **)condPtr);
     if (timePtr == NULL) {
 	wtime = INFINITE;
     } else {
 	wtime = (DWORD)timePtr->sec * 1000 + (DWORD)timePtr->usec / 1000;
@@ -750,11 +775,14 @@
      * waits are dubious anyway. Either you have the locking protocol wrong
      * and are masking a deadlock, or you are using conditions to pause your
      * thread.
      */
 
-    LeaveCriticalSection(csPtr);
+    counter = wmPtr->counter;
+    wmPtr->thread = 0;
+    wmPtr->counter = 0;
+    LeaveCriticalSection(&wmPtr->crit);
     timeout = 0;
     while (!timeout && (tsdPtr->flags & WIN_THREAD_BLOCKED)) {
 	ResetEvent(tsdPtr->condEvent);
 	LeaveCriticalSection(&winCondPtr->condLock);
 	if (WaitForSingleObjectEx(tsdPtr->condEvent, wtime,
@@ -793,11 +821,12 @@
 	    tsdPtr->flags = WIN_THREAD_RUNNING;
 	}
     }
 
     LeaveCriticalSection(&winCondPtr->condLock);
-    EnterCriticalSection(csPtr);
+    Tcl_MutexLock(mutexPtr);
+    wmPtr->counter = counter;
 }
 
 /*
  *----------------------------------------------------------------------
  *