Artifact [fd3489f7eb]
Not logged in

Artifact fd3489f7eb4a4859702ac7a2d7a02e82d7b2758ec226bad30eae96e26128f509:

Attachment "tclMutexTest.diff3" to ticket [893f8cc5db] added by chw 2025-10-06 18:38:40.
Index: generic/tclMutexTest.c
==================================================================
--- generic/tclMutexTest.c
+++ generic/tclMutexTest.c
@@ -27,10 +27,12 @@
 /*
  * Types related to Tcl_Mutex tests.
  */
 
 TCL_DECLARE_MUTEX(testContextMutex)
+TCL_DECLARE_MUTEX(threadStartMutex)
+static Tcl_Condition threadStartCond;
 
 static inline void
 LockTestContext(
     int numRecursions)
 {
@@ -70,10 +72,11 @@
 typedef struct {
     int numThreads;		/* Number of threads in test run */
     int numRecursions;		/* Number of mutex lock recursions */
     int numIterations;		/* Number of times each thread should loop */
     int yield;			/* Whether threads should yield when looping */
+    int run;			/* Whether threads should start */
     union {
 	Tcl_WideUInt counter;		/* Used in lock tests */
 	ProducerConsumerQueue queue;	/* Used in condition variable tests */
     } u;
 } MutexSharedContext;
@@ -87,13 +90,10 @@
     Tcl_ThreadId threadId;		  /* Only access in creator */
     Tcl_WideUInt numOperations;		  /* Use is dependent on the test */
     Tcl_WideUInt timeouts;		  /* Timeouts on condition variables */
 } MutexThreadContext;
 
-/* Used to track how many test threads running. Also used as trigger */
-static volatile int mutexThreadCount;
-
 static Tcl_ThreadCreateType	CounterThreadProc(void *clientData);
 static int			TestMutexLock(Tcl_Interp *interp,
 				    MutexSharedContext *contextPtr);
 static int			TestConditionVariable(Tcl_Interp *interp,
 				    MutexSharedContext *contextPtr);
@@ -231,12 +231,17 @@
     MutexSharedContext *contextPtr)
 {
     MutexThreadContext *threadContextsPtr = (MutexThreadContext *)
 	    Tcl_Alloc(sizeof(*threadContextsPtr) * contextPtr->numThreads);
 
+    Tcl_MutexLock(&threadStartMutex);
+    /* Inflate threadStartCond for later */
+    Tcl_Time nullTime = { 0, 0 };
+    Tcl_ConditionWait(&threadStartCond, &threadStartMutex, &nullTime);
+
+    contextPtr->run = 0;
     contextPtr->u.counter = 0;
-    mutexThreadCount = 0;
     for (int i = 0; i < contextPtr->numThreads; i++) {
 	threadContextsPtr[i].sharedContextPtr = contextPtr;
 	threadContextsPtr[i].numOperations = 0; /* Init though not used */
 
 	if (Tcl_CreateThread(&threadContextsPtr[i].threadId,
@@ -243,11 +248,15 @@
 		CounterThreadProc, &threadContextsPtr[i],
 		TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
 	    Tcl_Panic("Failed to create %d'th thread\n", i);
 	}
     }
-    mutexThreadCount = contextPtr->numThreads; /* Will fire off all test threads */
+
+    /* Trigger start signal */
+    contextPtr->run = 1;
+    Tcl_ConditionNotify(&threadStartCond);
+    Tcl_MutexUnlock(&threadStartMutex);
 
     /* Wait for all threads */
     for (int i = 0; i < contextPtr->numThreads; i++) {
 	int threadResult;
 	Tcl_JoinThread(threadContextsPtr[i].threadId, &threadResult);
@@ -279,14 +288,16 @@
     void *clientData)
 {
     MutexThreadContext *threadContextPtr = (MutexThreadContext *)clientData;
     MutexSharedContext *contextPtr = threadContextPtr->sharedContextPtr;
 
-    /* Spin wait until given the run signal */
-    while (mutexThreadCount < contextPtr->numThreads) {
-	YieldToOtherThreads();
+    /* Wait for start signal */
+    Tcl_MutexLock(&threadStartMutex);
+    while (!contextPtr->run) {
+	Tcl_ConditionWait(&threadStartCond, &threadStartMutex, NULL);
     }
+    Tcl_MutexUnlock(&threadStartMutex);
 
     for (int i = 0; i < contextPtr->numIterations; i++) {
 	LockTestContext(contextPtr->numRecursions);
 	Tcl_WideUInt temp = contextPtr->u.counter;
 	if (contextPtr->yield) {
@@ -327,10 +338,11 @@
 	return TCL_ERROR;
     }
     int numProducers = contextPtr->numThreads / 2;
     int numConsumers = contextPtr->numThreads - numProducers;
 
+    contextPtr->run = 0;
     contextPtr->u.queue.canDequeue = NULL;
     contextPtr->u.queue.canEnqueue = NULL;
 
     /*
      * available tracks how many elements in the virtual queue
@@ -344,11 +356,14 @@
     MutexThreadContext *consumerContextsPtr = (MutexThreadContext *)Tcl_Alloc(
 	    sizeof(*consumerContextsPtr) * numConsumers);
     MutexThreadContext *producerContextsPtr = (MutexThreadContext *)Tcl_Alloc(
 	    sizeof(*producerContextsPtr) * numProducers);
 
-    mutexThreadCount = 0;
+    Tcl_MutexLock(&threadStartMutex);
+    /* Inflate threadStartCond for later */
+    Tcl_Time nullTime = { 0, 0 };
+    Tcl_ConditionWait(&threadStartCond, &threadStartMutex, &nullTime);
 
     for (int i = 0; i < numConsumers; i++) {
 	consumerContextsPtr[i].sharedContextPtr = contextPtr;
 	consumerContextsPtr[i].numOperations = 0;
 	consumerContextsPtr[i].timeouts = 0;
@@ -370,11 +385,14 @@
 		TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
 	    Tcl_Panic("Failed to create %d'th thread\n", (int) i);
 	}
     }
 
-    mutexThreadCount = contextPtr->numThreads; /* Will trigger all threads */
+    /* Trigger start signal */
+    contextPtr->run = 1;
+    Tcl_ConditionNotify(&threadStartCond);
+    Tcl_MutexUnlock(&threadStartMutex);
 
     /* Producer total, thread, timeouts, Consumer total, thread, timeouts */
     Tcl_Obj *results[6];
     results[1] = Tcl_NewListObj(numProducers, NULL);
     results[4] = Tcl_NewListObj(numConsumers, NULL);
@@ -439,14 +457,16 @@
 
     /* Limit on total number of operations across all threads */
     Tcl_WideUInt limit;
     limit = contextPtr->numThreads * (Tcl_WideUInt) contextPtr->numIterations;
 
-    /* Spin wait until given the run signal */
-    while (mutexThreadCount < contextPtr->numThreads) {
-	YieldToOtherThreads();
+    /* Wait for start signal */
+    Tcl_MutexLock(&threadStartMutex);
+    while (!contextPtr->run) {
+	Tcl_ConditionWait(&threadStartCond, &threadStartMutex, NULL);
     }
+    Tcl_MutexUnlock(&threadStartMutex);
 
     LockTestContext(contextPtr->numRecursions);
     while (contextPtr->u.queue.totalEnqueued < limit) {
 	if (contextPtr->u.queue.available == contextPtr->u.queue.capacity) {
 	    Tcl_Time before, after;
@@ -501,14 +521,16 @@
 
     /* Limit on total number of operations across all threads */
     Tcl_WideUInt limit;
     limit = contextPtr->numThreads * (Tcl_WideUInt) contextPtr->numIterations;
 
-    /* Spin wait until given the run signal */
-    while (mutexThreadCount < contextPtr->numThreads) {
-	YieldToOtherThreads();
+    /* Wait for start signal */
+    Tcl_MutexLock(&threadStartMutex);
+    while (!contextPtr->run) {
+	Tcl_ConditionWait(&threadStartCond, &threadStartMutex, NULL);
     }
+    Tcl_MutexUnlock(&threadStartMutex);
 
     LockTestContext(contextPtr->numRecursions);
     while (contextPtr->u.queue.totalDequeued < limit) {
 	if (contextPtr->u.queue.available == 0) {
 	    Tcl_Time before, after;