I've got a pthread-based multithreaded program that has four threads indefinitely executing this run-loop (pseudocode):
while(keepRunning)
{
pthread_barrier_wait(&g_stage_one_barrier);
UpdateThisThreadsStateVariables();
pthread_barrier_wait(&g_stage_two_barrier);
DoComputationsThatReadFromAllThreadsStateVariables();
}
This works pretty well, in that during stage one each thread updates its own state variables, and that's okay because no other thread is reading any other thread's state variables during stage one. Then during stage two it's a free-for-all as far as threads reading each others' state is concerned, but that's okay because during stage two no thread is modifying its local state variables, so they are effectively read-only.
My only remaining problem is, how do I cleanly and reliably shut down these threads when it's time for my application to quit? (By "cleanly and reliably", I mean without introducing potential deadlocks or race conditions, and ideally without having to send any UNIX-signals to force threads out of a pthread_barrier_wait() call)
My main() thread can of course set keepRunning to false for each thread, but then how does it get pthread_barrier_wait() to return for each thread? AFAICT the only way to get pthread_barrier_wait() to return is to have all four threads' execution-location inside pthread_barrier_wait() simultaneously, but that's difficult to do when some threads may have exited already.
Calling pthread_barrier_destroy() seems like what I'd want to do, but it's undefined behavior to do that while any threads might be waiting on the barrier.
Is there a good known solution to this problem?
Having two flags and using something like the following should work:
for (;;)
{
pthread_barrier_wait(&g_stage_one_barrier); +
|
UpdateThisThreadsStateVariables(); |
|
pthread_mutex_lock(&shutdownMtx); | Zone 1
pendingShutdown = !keepRunning; |
pthread_mutex_unlock(&shutdownMtx); |
|
pthread_barrier_wait(&g_stage_two_barrier); +
|
if (pendingShutdown) |
break; | Zone 2
|
DoComputationsThatReadFromAllThreadsStateVariables(); |
}
shutdownMtx
should protect the setting of keepRunning
too, though it's not shown.
The logic is that by the time pendingShutdown
gets set to true
, all the threads must be within Zone 1. (This is true even if only some of the threads saw keepRunning
being false
, so races on keepRunning
should be okay.) It follows that they will all reach pthread_barrier_wait(&g_stage_two_barrier)
, and then all break out when they enter Zone 2.
It would also be possible to check for PTHREAD_BARRIER_SERIAL_THREAD
-- which is returned by pthread_barrier_wait()
for exactly one of the threads -- and only do the locking and updating of pendingShutdown
in that thread, which could improve performance.