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