#include #include #include #if (__STDC_VERSION__ > 201100L) && !defined(__STDC_NO_THREADS__) # include # define VERSION "C threads" typedef mtx_t mutex; typedef cnd_t condition; typedef thrd_t thread; typedef int thread_ret; # define mutex_init(M) mtx_init((M), mtx_plain) # define mutex_destroy mtx_destroy # define mutex_lock mtx_lock # define mutex_unlock mtx_unlock # define condition_init(C) cnd_init((C)) # define condition_destroy cnd_destroy # define condition_wait cnd_wait # define condition_timedwait cnd_timedwait # define condition_signal cnd_signal # define condition_broadcast cnd_broadcast # define thread_create(ID, START, ARG) thrd_create(ID, START, ARG) # define thread_join thrd_join # define gettime(TS) timespec_get((TS), TIME_UTC) char const* errorstring(int err) { switch (err) { case thrd_success: return "success"; case thrd_error: return "generic error"; case thrd_nomem: return "out of memory"; case thrd_timedout: return "timedout"; default: return "unknown error return"; } } #else # include # define VERSION "POSIX threads" typedef pthread_mutex_t mutex; typedef pthread_cond_t condition; typedef pthread_t thread; typedef void* thread_ret; # define mutex_init(M) pthread_mutex_init((M), 0) # define mutex_destroy pthread_mutex_destroy # define mutex_lock pthread_mutex_lock # define mutex_unlock pthread_mutex_unlock # define condition_init(C) pthread_cond_init((C), 0) # define condition_destroy pthread_cond_destroy # define condition_wait pthread_cond_wait # define condition_timedwait pthread_cond_timedwait # define condition_signal pthread_cond_signal # define condition_broadcast pthread_cond_broadcast # define thread_create(ID, START, ARG) pthread_create(ID, 0, START, ARG) # define thread_join pthread_join # define gettime(TS) clock_gettime(CLOCK_REALTIME, (TS)) # define errorstring strerror #endif #ifdef __GLIBC__ # define LIBRARY "glibc" #else # define LIBRARY "unidentified" #endif #define trace2(L, ...) fprintf(stderr, __FILE__ ":" #L ": " __VA_ARGS__) #define trace1(L, ...) trace2(L, __VA_ARGS__) #ifdef DEBUG # define trace(...) trace1(__LINE__, __VA_ARGS__) #else # define trace(...) do { if (0) trace1(__LINE__, __VA_ARGS__); } while (0) #endif #define tell(...) trace1(__LINE__, __VA_ARGS__) enum { phases = 10, threads = 10, }; thread id[threads]; unsigned args[threads]; mutex mut[phases]; unsigned inside[phases]; condition cond_client; condition cond_main; unsigned volatile phase; thread_ret client(void *arg) { unsigned * number = arg; for (unsigned i = 0; i < phases; ++i) { trace("thread %u in phase %u\n", *number, i); mutex_lock(&mut[i]); ++inside[i]; if (inside[i] == threads) { trace("thread %u is last, signalling main\n", *number); int ret = condition_signal(&cond_main); trace("thread %u is last, signalling main, %s\n", *number, errorstring(ret)); } while (i == phase) { tell("thread %u in phase %u (%u), waiting\n", *number, i, phase); int ret = condition_wait(&cond_client, &mut[i]); trace("thread %u in phase %u (%u), finished, %s\n", *number, i, phase, errorstring(ret)); } int ret = mutex_unlock(&mut[i]); trace("thread %u in phase %u (%u), has unlocked mutex: %s\n", *number, i, phase, errorstring(ret)); } return 0; } int main(void) { tell("start up of main, using %s, library %s\n", VERSION, LIBRARY); condition_init(&cond_client); condition_init(&cond_main); for (unsigned i = 0; i < phases; ++i) { mutex_init(&mut[i]); } mutex_lock(&mut[0]); for (unsigned i = 0; i < threads; ++i) { args[i] = i; thread_create(&id[i], client, &args[i]); } while (phase < phases) { while (inside[phase] < threads) { trace("main seeing %u threads in phase %u, waiting\n", inside[phase], phase); int ret = condition_wait(&cond_main, &mut[phase]); tell("main seeing %u threads in phase %u, %s\n", inside[phase], phase, errorstring(ret)); } /* now we know that everybody is waiting inside, lock the next mutex, if any, such that nobody can enter the next phase without our permission. */ if (phase < phases-1) mutex_lock(&mut[phase+1]); /* Now signal all clients, update the phase count and release the mutex they are waiting for. */ int ret = condition_broadcast(&cond_client); trace("main has broadcast to %u: %s\n", phase, errorstring(ret)); ++phase; ret = mutex_unlock(&mut[phase-1]); trace("main has unlocked mutex %u: %s\n", phase-1, errorstring(ret)); } trace("main finished loop\n"); for (unsigned i = 0; i < threads; ++i) { trace("main joining thread %u\n", i); int ret = thread_join(id[i], &(thread_ret){0}); trace("main joining thread %u: %s\n", i, errorstring(ret)); } /* C functions to destroy the control structures don't return error information, so we can't check for errors, here. */ for (unsigned i = 0; i < phases; ++i) { mutex_destroy(&mut[i]); } condition_destroy(&cond_main); condition_destroy(&cond_client); tell("shut down of main, using %s, library %s\n", VERSION, LIBRARY); }