#include #include #include #include #include #include #include const int maxWaiterThreadsCount = 30; const bool releaseMutexBetweenSignals = false; const int waitTimeSec = 15; bool exitProgram; int threadsCount; int beforeWaitersCount; int afterWaitersCount; int signalsSentCount; int beforeWaitersWokenCount; int afterWaitersWokenCount; int iterationCount; pthread_mutex_t mutex; pthread_cond_t testCond; void* ThreadMain(void* data) { // Wait for the main thread to create all waiter threads pthread_mutex_lock(&mutex); while (!exitProgram) { // 1. Remember which is the current test iteration int currIterationCount = iterationCount; // 2. Check if we are starting the wait before all signals have been sent. bool beforeAllSignalsSent = signalsSentCount < threadsCount; // 3. Count the thread towards the appropriate group of waiters (before/after // all signals are sent) if (beforeAllSignalsSent) { ++beforeWaitersCount; } else { ++afterWaitersCount; } // 4. Wait on the condition variable. pthread_cond_wait(&testCond, &mutex); // 5. If the iteration count has changed, start over if (currIterationCount != iterationCount) { continue; } // 6. Count the woken thread towards the appropriate group of woken threads (the // same group as step 2). if (beforeAllSignalsSent) { ++beforeWaitersWokenCount; } else { ++afterWaitersWokenCount; } } pthread_mutex_unlock(&mutex); pthread_exit(NULL); } int main() { // 1. Test setup: // 1.1. Initialize the mutex and the condition variable pthread_mutex_init(&mutex, NULL); pthread_cond_init(&testCond, NULL); // 1.2. exitProgram = false; // 1.3. Create some waiter threads. All waiters will imediately block on the mutex pthread_mutex_lock(&mutex); for (threadsCount = 0; threadsCount < maxWaiterThreadsCount; ++threadsCount) { pthread_t thread; if (pthread_create(&thread, NULL, ThreadMain, NULL) != 0) { break; } pthread_detach(thread); } printf("Created %d threads.\n", threadsCount); // cout << "Created " << threadsCount << " threads." << endl; // 2. Test body. Count the test body iterations to help waiters detect when a // new iteration has started for (iterationCount = 0; !exitProgram; ++iterationCount) { // 2.1. Reset the counters beforeWaitersCount = 0; afterWaitersCount = 0; beforeWaitersWokenCount = 0; afterWaitersWokenCount = 0; signalsSentCount = 0; // 2.2. Wait for all waiters to block on the condition variable while (beforeWaitersCount < threadsCount) { pthread_mutex_unlock(&mutex); sched_yield(); pthread_mutex_lock(&mutex); } // 2.3. Send as many inidividual signals as there are waiter threads for (; signalsSentCount < threadsCount; ++signalsSentCount) { pthread_cond_signal(&testCond); if (releaseMutexBetweenSignals) { pthread_mutex_unlock(&mutex); sched_yield(); pthread_mutex_lock(&mutex); } } // 2.4. Wait for at least two threads to block again after the signals were // sent. But if we were releasing the mutex between signals it is possible // that too much threads woke up and reentered the wait so we need to make // sure that there are enough threads that havent woken yet. if (signalsSentCount - beforeWaitersWokenCount > 2) { while (afterWaitersCount < 2) { pthread_mutex_unlock(&mutex); sched_yield(); pthread_mutex_lock(&mutex); } } // 2.5. Send a single signal pthread_cond_signal(&testCond); // 2.6. Wait for a predefined time for at least signalsSentCount 'before' // waiters to wake up. time_t endTime; time_t startTime = time(&endTime); while (beforeWaitersWokenCount < signalsSentCount) { pthread_mutex_unlock(&mutex); sched_yield(); pthread_mutex_lock(&mutex); if (time(&endTime) - startTime > waitTimeSec) { break; } } // 2.7. If some threads that should have been unblocked by the first // signalsSentCount signals have remained blocked, report that the // race was hit if (beforeWaitersWokenCount < signalsSentCount) { printf("Race hit.\n\tWaited:\t\t%ds\n\tFailed to wake:\t%d\n\tExtra woken:\t%d\n", endTime-startTime, signalsSentCount-beforeWaitersWokenCount, afterWaitersWokenCount-1); // cout << "Race hit." << endl // << "\tWaited:\t\t" << endTime - startTime << "s" << endl // << "\tFailed to wake\t: " << signalsSentCount - beforeWaitersWokenCount << endl // << "\tExtra woken:\t" << afterWaitersWokenCount - 1 << endl; // Notify all waiters that they should exit exitProgram = true; } else { printf("Race not hit.\n"); // cout << "Race not hit." << endl; } // 2.8. Send a broadcast to let all waiters move to the start of the next // test iteration or exit pthread_cond_broadcast(&testCond); } pthread_mutex_unlock(&mutex); pthread_exit(NULL); }