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;