1570af302Sopenharmony_ci// by Jens Gustedt from http://www.openwall.com/lists/musl/2014/08/11/1
2570af302Sopenharmony_ci// c11 threads test was removed and t_error messages were added
3570af302Sopenharmony_ci// the test deadlocks with a broken cond var implementation so
4570af302Sopenharmony_ci// cond_waits were changed to cond_timedwaits with short timeout
5570af302Sopenharmony_ci#include <stdio.h>
6570af302Sopenharmony_ci#include <string.h>
7570af302Sopenharmony_ci#include <time.h>
8570af302Sopenharmony_ci#include <stdlib.h>
9570af302Sopenharmony_ci#include <errno.h>
10570af302Sopenharmony_ci#include "test.h"
11570af302Sopenharmony_ci
12570af302Sopenharmony_ci# include <pthread.h>
13570af302Sopenharmony_ci
14570af302Sopenharmony_ci# define VERSION "POSIX threads"
15570af302Sopenharmony_ci
16570af302Sopenharmony_citypedef pthread_mutex_t mutex;
17570af302Sopenharmony_citypedef pthread_cond_t condition;
18570af302Sopenharmony_citypedef pthread_t thread;
19570af302Sopenharmony_citypedef void* thread_ret;
20570af302Sopenharmony_ci
21570af302Sopenharmony_ci# define mutex_init(M) pthread_mutex_init((M), 0)
22570af302Sopenharmony_ci# define mutex_destroy pthread_mutex_destroy
23570af302Sopenharmony_ci# define mutex_lock pthread_mutex_lock
24570af302Sopenharmony_ci# define mutex_unlock pthread_mutex_unlock
25570af302Sopenharmony_ci
26570af302Sopenharmony_ci# define condition_init(C) pthread_cond_init((C), 0)
27570af302Sopenharmony_ci# define condition_destroy pthread_cond_destroy
28570af302Sopenharmony_ci# define condition_wait pthread_cond_wait
29570af302Sopenharmony_ci# define condition_timedwait pthread_cond_timedwait
30570af302Sopenharmony_ci# define condition_signal pthread_cond_signal
31570af302Sopenharmony_ci# define condition_broadcast pthread_cond_broadcast
32570af302Sopenharmony_ci
33570af302Sopenharmony_ci
34570af302Sopenharmony_ci# define thread_create(ID, START, ARG) pthread_create(ID, 0, START, ARG)
35570af302Sopenharmony_ci# define thread_join pthread_join
36570af302Sopenharmony_ci
37570af302Sopenharmony_ci# define gettime(TS) clock_gettime(CLOCK_REALTIME, (TS))
38570af302Sopenharmony_ci
39570af302Sopenharmony_ci# define errorstring strerror
40570af302Sopenharmony_ci
41570af302Sopenharmony_ci#ifdef __GLIBC__
42570af302Sopenharmony_ci# define LIBRARY "glibc"
43570af302Sopenharmony_ci#else
44570af302Sopenharmony_ci# define LIBRARY "unidentified"
45570af302Sopenharmony_ci#endif
46570af302Sopenharmony_ci
47570af302Sopenharmony_ci#define trace2(L, ...) fprintf(stderr, __FILE__ ":" #L ": " __VA_ARGS__)
48570af302Sopenharmony_ci#define trace1(L, ...) trace2(L, __VA_ARGS__)
49570af302Sopenharmony_ci#ifdef DEBUG
50570af302Sopenharmony_ci# define trace(...) trace1(__LINE__, __VA_ARGS__)
51570af302Sopenharmony_ci#else
52570af302Sopenharmony_ci# define trace(...) do { if (0) trace1(__LINE__, __VA_ARGS__); } while (0)
53570af302Sopenharmony_ci#endif
54570af302Sopenharmony_ci
55570af302Sopenharmony_ci//#define tell(...) trace1(__LINE__, __VA_ARGS__)
56570af302Sopenharmony_ci#define tell(...) trace(__VA_ARGS__)
57570af302Sopenharmony_ci
58570af302Sopenharmony_cienum {
59570af302Sopenharmony_ci  phases = 10,
60570af302Sopenharmony_ci  threads = 10,
61570af302Sopenharmony_ci};
62570af302Sopenharmony_ci
63570af302Sopenharmony_cistatic thread id[threads];
64570af302Sopenharmony_cistatic unsigned args[threads];
65570af302Sopenharmony_ci
66570af302Sopenharmony_cistatic mutex mut[phases];
67570af302Sopenharmony_cistatic unsigned inside[phases];
68570af302Sopenharmony_ci
69570af302Sopenharmony_cistatic condition cond_client;
70570af302Sopenharmony_cistatic condition cond_main;
71570af302Sopenharmony_cistatic unsigned volatile phase;
72570af302Sopenharmony_ci
73570af302Sopenharmony_cistatic void settimeout(struct timespec *ts)
74570af302Sopenharmony_ci{
75570af302Sopenharmony_ci  if (clock_gettime(CLOCK_REALTIME, ts))
76570af302Sopenharmony_ci    t_error("clock_gettime failed: %s\n", strerror(errno));
77570af302Sopenharmony_ci  ts->tv_nsec += 500*1000*1000;
78570af302Sopenharmony_ci  if (ts->tv_nsec >= 1000*1000*1000) {
79570af302Sopenharmony_ci    ts->tv_nsec -= 1000*1000*1000;
80570af302Sopenharmony_ci    ts->tv_sec++;
81570af302Sopenharmony_ci  }
82570af302Sopenharmony_ci}
83570af302Sopenharmony_ci
84570af302Sopenharmony_cistatic thread_ret client(void *arg) {
85570af302Sopenharmony_ci  struct timespec ts;
86570af302Sopenharmony_ci  unsigned * number = arg;
87570af302Sopenharmony_ci  for (unsigned i = 0; i < phases; ++i) {
88570af302Sopenharmony_ci    trace("thread %u in phase %u\n", *number, i);
89570af302Sopenharmony_ci    mutex_lock(&mut[i]);
90570af302Sopenharmony_ci    ++inside[i];
91570af302Sopenharmony_ci    if (inside[i] == threads) {
92570af302Sopenharmony_ci      trace("thread %u is last, signalling main\n", *number);
93570af302Sopenharmony_ci      int ret = condition_signal(&cond_main);
94570af302Sopenharmony_ci      trace("thread %u is last, signalling main, %s\n", *number, errorstring(ret));
95570af302Sopenharmony_ci      if (ret)
96570af302Sopenharmony_ci        t_error("thread %u is last in phase %u, signalling main failed: %s\n", *number, i, errorstring(ret));
97570af302Sopenharmony_ci    }
98570af302Sopenharmony_ci    while (i == phase) {
99570af302Sopenharmony_ci      tell("thread %u in phase %u (%u), waiting\n", *number, i, phase);
100570af302Sopenharmony_ci      settimeout(&ts);
101570af302Sopenharmony_ci      int ret = condition_timedwait(&cond_client, &mut[i], &ts);
102570af302Sopenharmony_ci      trace("thread %u in phase %u (%u), finished, %s\n", *number, i, phase, errorstring(ret));
103570af302Sopenharmony_ci      if (ret) {
104570af302Sopenharmony_ci        t_error("thread %u in phase %u (%u) finished waiting: %s\n", *number, i, phase, errorstring(ret));
105570af302Sopenharmony_ci        exit(t_status);
106570af302Sopenharmony_ci      }
107570af302Sopenharmony_ci    }
108570af302Sopenharmony_ci    int ret = mutex_unlock(&mut[i]);
109570af302Sopenharmony_ci    trace("thread %u in phase %u (%u), has unlocked mutex: %s\n", *number, i, phase, errorstring(ret));
110570af302Sopenharmony_ci    if (ret)
111570af302Sopenharmony_ci      t_error("thread %u in phase %u (%u), failed to unlock: %s\n", *number, i, phase, errorstring(ret));
112570af302Sopenharmony_ci  }
113570af302Sopenharmony_ci  return 0;
114570af302Sopenharmony_ci}
115570af302Sopenharmony_ci
116570af302Sopenharmony_ci
117570af302Sopenharmony_ciint main(void) {
118570af302Sopenharmony_ci  struct timespec ts;
119570af302Sopenharmony_ci
120570af302Sopenharmony_ci  tell("start up of main, using %s, library %s\n", VERSION, LIBRARY);
121570af302Sopenharmony_ci  condition_init(&cond_client);
122570af302Sopenharmony_ci  condition_init(&cond_main);
123570af302Sopenharmony_ci  for (unsigned i = 0; i < phases; ++i) {
124570af302Sopenharmony_ci    mutex_init(&mut[i]);
125570af302Sopenharmony_ci  }
126570af302Sopenharmony_ci  mutex_lock(&mut[0]);
127570af302Sopenharmony_ci
128570af302Sopenharmony_ci  for (unsigned i = 0; i < threads; ++i) {
129570af302Sopenharmony_ci    args[i] = i;
130570af302Sopenharmony_ci    thread_create(&id[i], client, &args[i]);
131570af302Sopenharmony_ci  }
132570af302Sopenharmony_ci
133570af302Sopenharmony_ci  while (phase < phases) {
134570af302Sopenharmony_ci    while (inside[phase] < threads) {
135570af302Sopenharmony_ci      trace("main seeing %u threads in phase %u, waiting\n", inside[phase], phase);
136570af302Sopenharmony_ci      settimeout(&ts);
137570af302Sopenharmony_ci      int ret = condition_timedwait(&cond_main, &mut[phase], &ts);
138570af302Sopenharmony_ci      tell("main seeing %u threads in phase %u, %s\n", inside[phase], phase, errorstring(ret));
139570af302Sopenharmony_ci      if (ret) {
140570af302Sopenharmony_ci        t_error("main thread in phase %u (%u threads inside), finished waiting: %s\n", phase, inside[phase], errorstring(ret));
141570af302Sopenharmony_ci        return t_status;
142570af302Sopenharmony_ci      }
143570af302Sopenharmony_ci    }
144570af302Sopenharmony_ci    /* now we know that everybody is waiting inside, lock the next
145570af302Sopenharmony_ci       mutex, if any, such that nobody can enter the next phase
146570af302Sopenharmony_ci       without our permission. */
147570af302Sopenharmony_ci    if (phase < phases-1)
148570af302Sopenharmony_ci      mutex_lock(&mut[phase+1]);
149570af302Sopenharmony_ci    /* Now signal all clients, update the phase count and release the
150570af302Sopenharmony_ci       mutex they are waiting for. */
151570af302Sopenharmony_ci    int ret = condition_broadcast(&cond_client);
152570af302Sopenharmony_ci    trace("main has broadcast to %u: %s\n", phase, errorstring(ret));
153570af302Sopenharmony_ci    if (ret)
154570af302Sopenharmony_ci      t_error("main broadcast in phase %u failed: %s\n", phase, errorstring(ret));
155570af302Sopenharmony_ci    ++phase;
156570af302Sopenharmony_ci    ret = mutex_unlock(&mut[phase-1]);
157570af302Sopenharmony_ci    trace("main has unlocked mutex %u: %s\n", phase-1, errorstring(ret));
158570af302Sopenharmony_ci    if (ret)
159570af302Sopenharmony_ci      t_error("main failed to unlock mutex %u: %s\n", phase-1, errorstring(ret));
160570af302Sopenharmony_ci  }
161570af302Sopenharmony_ci
162570af302Sopenharmony_ci
163570af302Sopenharmony_ci
164570af302Sopenharmony_ci  trace("main finished loop\n");
165570af302Sopenharmony_ci
166570af302Sopenharmony_ci  for (unsigned i = 0; i < threads; ++i) {
167570af302Sopenharmony_ci    trace("main joining thread %u\n", i);
168570af302Sopenharmony_ci    int ret = thread_join(id[i], &(thread_ret){0});
169570af302Sopenharmony_ci    trace("main joining thread %u: %s\n", i, errorstring(ret));
170570af302Sopenharmony_ci    if (ret)
171570af302Sopenharmony_ci      t_error("main failed join thread %u: %s\n", i, errorstring(ret));
172570af302Sopenharmony_ci  }
173570af302Sopenharmony_ci
174570af302Sopenharmony_ci  /* C functions to destroy the control structures don't return error
175570af302Sopenharmony_ci     information, so we can't check for errors, here. */
176570af302Sopenharmony_ci  for (unsigned i = 0; i < phases; ++i) {
177570af302Sopenharmony_ci    mutex_destroy(&mut[i]);
178570af302Sopenharmony_ci  }
179570af302Sopenharmony_ci  condition_destroy(&cond_main);
180570af302Sopenharmony_ci  condition_destroy(&cond_client);
181570af302Sopenharmony_ci
182570af302Sopenharmony_ci  tell("shut down of main, using %s, library %s\n", VERSION, LIBRARY);
183570af302Sopenharmony_ci
184570af302Sopenharmony_ci  return t_status;
185570af302Sopenharmony_ci}
186