1// by Jens Gustedt from http://www.openwall.com/lists/musl/2014/08/11/1 2// c11 threads test was removed and t_error messages were added 3// the test deadlocks with a broken cond var implementation so 4// cond_waits were changed to cond_timedwaits with short timeout 5#include <stdio.h> 6#include <string.h> 7#include <time.h> 8#include <stdlib.h> 9#include <errno.h> 10#include "test.h" 11 12# include <pthread.h> 13 14# define VERSION "POSIX threads" 15 16typedef pthread_mutex_t mutex; 17typedef pthread_cond_t condition; 18typedef pthread_t thread; 19typedef void* thread_ret; 20 21# define mutex_init(M) pthread_mutex_init((M), 0) 22# define mutex_destroy pthread_mutex_destroy 23# define mutex_lock pthread_mutex_lock 24# define mutex_unlock pthread_mutex_unlock 25 26# define condition_init(C) pthread_cond_init((C), 0) 27# define condition_destroy pthread_cond_destroy 28# define condition_wait pthread_cond_wait 29# define condition_timedwait pthread_cond_timedwait 30# define condition_signal pthread_cond_signal 31# define condition_broadcast pthread_cond_broadcast 32 33 34# define thread_create(ID, START, ARG) pthread_create(ID, 0, START, ARG) 35# define thread_join pthread_join 36 37# define gettime(TS) clock_gettime(CLOCK_REALTIME, (TS)) 38 39# define errorstring strerror 40 41#ifdef __GLIBC__ 42# define LIBRARY "glibc" 43#else 44# define LIBRARY "unidentified" 45#endif 46 47#define trace2(L, ...) fprintf(stderr, __FILE__ ":" #L ": " __VA_ARGS__) 48#define trace1(L, ...) trace2(L, __VA_ARGS__) 49#ifdef DEBUG 50# define trace(...) trace1(__LINE__, __VA_ARGS__) 51#else 52# define trace(...) do { if (0) trace1(__LINE__, __VA_ARGS__); } while (0) 53#endif 54 55//#define tell(...) trace1(__LINE__, __VA_ARGS__) 56#define tell(...) trace(__VA_ARGS__) 57 58enum { 59 phases = 10, 60 threads = 10, 61}; 62 63static thread id[threads]; 64static unsigned args[threads]; 65 66static mutex mut[phases]; 67static unsigned inside[phases]; 68 69static condition cond_client; 70static condition cond_main; 71static unsigned volatile phase; 72 73static void settimeout(struct timespec *ts) 74{ 75 if (clock_gettime(CLOCK_REALTIME, ts)) 76 t_error("clock_gettime failed: %s\n", strerror(errno)); 77 ts->tv_nsec += 500*1000*1000; 78 if (ts->tv_nsec >= 1000*1000*1000) { 79 ts->tv_nsec -= 1000*1000*1000; 80 ts->tv_sec++; 81 } 82} 83 84static thread_ret client(void *arg) { 85 struct timespec ts; 86 unsigned * number = arg; 87 for (unsigned i = 0; i < phases; ++i) { 88 trace("thread %u in phase %u\n", *number, i); 89 mutex_lock(&mut[i]); 90 ++inside[i]; 91 if (inside[i] == threads) { 92 trace("thread %u is last, signalling main\n", *number); 93 int ret = condition_signal(&cond_main); 94 trace("thread %u is last, signalling main, %s\n", *number, errorstring(ret)); 95 if (ret) 96 t_error("thread %u is last in phase %u, signalling main failed: %s\n", *number, i, errorstring(ret)); 97 } 98 while (i == phase) { 99 tell("thread %u in phase %u (%u), waiting\n", *number, i, phase); 100 settimeout(&ts); 101 int ret = condition_timedwait(&cond_client, &mut[i], &ts); 102 trace("thread %u in phase %u (%u), finished, %s\n", *number, i, phase, errorstring(ret)); 103 if (ret) { 104 t_error("thread %u in phase %u (%u) finished waiting: %s\n", *number, i, phase, errorstring(ret)); 105 exit(t_status); 106 } 107 } 108 int ret = mutex_unlock(&mut[i]); 109 trace("thread %u in phase %u (%u), has unlocked mutex: %s\n", *number, i, phase, errorstring(ret)); 110 if (ret) 111 t_error("thread %u in phase %u (%u), failed to unlock: %s\n", *number, i, phase, errorstring(ret)); 112 } 113 return 0; 114} 115 116 117int main(void) { 118 struct timespec ts; 119 120 tell("start up of main, using %s, library %s\n", VERSION, LIBRARY); 121 condition_init(&cond_client); 122 condition_init(&cond_main); 123 for (unsigned i = 0; i < phases; ++i) { 124 mutex_init(&mut[i]); 125 } 126 mutex_lock(&mut[0]); 127 128 for (unsigned i = 0; i < threads; ++i) { 129 args[i] = i; 130 thread_create(&id[i], client, &args[i]); 131 } 132 133 while (phase < phases) { 134 while (inside[phase] < threads) { 135 trace("main seeing %u threads in phase %u, waiting\n", inside[phase], phase); 136 settimeout(&ts); 137 int ret = condition_timedwait(&cond_main, &mut[phase], &ts); 138 tell("main seeing %u threads in phase %u, %s\n", inside[phase], phase, errorstring(ret)); 139 if (ret) { 140 t_error("main thread in phase %u (%u threads inside), finished waiting: %s\n", phase, inside[phase], errorstring(ret)); 141 return t_status; 142 } 143 } 144 /* now we know that everybody is waiting inside, lock the next 145 mutex, if any, such that nobody can enter the next phase 146 without our permission. */ 147 if (phase < phases-1) 148 mutex_lock(&mut[phase+1]); 149 /* Now signal all clients, update the phase count and release the 150 mutex they are waiting for. */ 151 int ret = condition_broadcast(&cond_client); 152 trace("main has broadcast to %u: %s\n", phase, errorstring(ret)); 153 if (ret) 154 t_error("main broadcast in phase %u failed: %s\n", phase, errorstring(ret)); 155 ++phase; 156 ret = mutex_unlock(&mut[phase-1]); 157 trace("main has unlocked mutex %u: %s\n", phase-1, errorstring(ret)); 158 if (ret) 159 t_error("main failed to unlock mutex %u: %s\n", phase-1, errorstring(ret)); 160 } 161 162 163 164 trace("main finished loop\n"); 165 166 for (unsigned i = 0; i < threads; ++i) { 167 trace("main joining thread %u\n", i); 168 int ret = thread_join(id[i], &(thread_ret){0}); 169 trace("main joining thread %u: %s\n", i, errorstring(ret)); 170 if (ret) 171 t_error("main failed join thread %u: %s\n", i, errorstring(ret)); 172 } 173 174 /* C functions to destroy the control structures don't return error 175 information, so we can't check for errors, here. */ 176 for (unsigned i = 0; i < phases; ++i) { 177 mutex_destroy(&mut[i]); 178 } 179 condition_destroy(&cond_main); 180 condition_destroy(&cond_client); 181 182 tell("shut down of main, using %s, library %s\n", VERSION, LIBRARY); 183 184 return t_status; 185} 186