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;
}
/*
*----------------------------------------------------------------------
*