|
Szabolcs Nagy |
195a19 |
// by Jens Gustedt from http://www.openwall.com/lists/musl/2014/08/11/1
|
|
Szabolcs Nagy |
195a19 |
// c11 threads test was removed and t_error messages were added
|
|
Szabolcs Nagy |
195a19 |
// the test deadlocks with a broken cond var implementation so
|
|
Szabolcs Nagy |
195a19 |
// cond_waits were changed to cond_timedwaits with short timeout
|
|
Szabolcs Nagy |
195a19 |
#include <stdio.h>
|
|
Szabolcs Nagy |
195a19 |
#include <string.h>
|
|
Szabolcs Nagy |
195a19 |
#include <time.h>
|
|
Szabolcs Nagy |
195a19 |
#include <stdlib.h>
|
|
Szabolcs Nagy |
195a19 |
#include <errno.h>
|
|
Szabolcs Nagy |
195a19 |
#include "test.h"
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
# include <pthread.h>
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
# define VERSION "POSIX threads"
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
typedef pthread_mutex_t mutex;
|
|
Szabolcs Nagy |
195a19 |
typedef pthread_cond_t condition;
|
|
Szabolcs Nagy |
195a19 |
typedef pthread_t thread;
|
|
Szabolcs Nagy |
195a19 |
typedef void* thread_ret;
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
# define mutex_init(M) pthread_mutex_init((M), 0)
|
|
Szabolcs Nagy |
195a19 |
# define mutex_destroy pthread_mutex_destroy
|
|
Szabolcs Nagy |
195a19 |
# define mutex_lock pthread_mutex_lock
|
|
Szabolcs Nagy |
195a19 |
# define mutex_unlock pthread_mutex_unlock
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
# define condition_init(C) pthread_cond_init((C), 0)
|
|
Szabolcs Nagy |
195a19 |
# define condition_destroy pthread_cond_destroy
|
|
Szabolcs Nagy |
195a19 |
# define condition_wait pthread_cond_wait
|
|
Szabolcs Nagy |
195a19 |
# define condition_timedwait pthread_cond_timedwait
|
|
Szabolcs Nagy |
195a19 |
# define condition_signal pthread_cond_signal
|
|
Szabolcs Nagy |
195a19 |
# define condition_broadcast pthread_cond_broadcast
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
# define thread_create(ID, START, ARG) pthread_create(ID, 0, START, ARG)
|
|
Szabolcs Nagy |
195a19 |
# define thread_join pthread_join
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
# define gettime(TS) clock_gettime(CLOCK_REALTIME, (TS))
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
# define errorstring strerror
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
#ifdef __GLIBC__
|
|
Szabolcs Nagy |
195a19 |
# define LIBRARY "glibc"
|
|
Szabolcs Nagy |
195a19 |
#else
|
|
Szabolcs Nagy |
195a19 |
# define LIBRARY "unidentified"
|
|
Szabolcs Nagy |
195a19 |
#endif
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
#define trace2(L, ...) fprintf(stderr, __FILE__ ":" #L ": " __VA_ARGS__)
|
|
Szabolcs Nagy |
195a19 |
#define trace1(L, ...) trace2(L, __VA_ARGS__)
|
|
Szabolcs Nagy |
195a19 |
#ifdef DEBUG
|
|
Szabolcs Nagy |
195a19 |
# define trace(...) trace1(__LINE__, __VA_ARGS__)
|
|
Szabolcs Nagy |
195a19 |
#else
|
|
Szabolcs Nagy |
195a19 |
# define trace(...) do { if (0) trace1(__LINE__, __VA_ARGS__); } while (0)
|
|
Szabolcs Nagy |
195a19 |
#endif
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
//#define tell(...) trace1(__LINE__, __VA_ARGS__)
|
|
Szabolcs Nagy |
195a19 |
#define tell(...) trace(__VA_ARGS__)
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
enum {
|
|
Szabolcs Nagy |
195a19 |
phases = 10,
|
|
Szabolcs Nagy |
195a19 |
threads = 10,
|
|
Szabolcs Nagy |
195a19 |
};
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
static thread id[threads];
|
|
Szabolcs Nagy |
195a19 |
static unsigned args[threads];
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
static mutex mut[phases];
|
|
Szabolcs Nagy |
195a19 |
static unsigned inside[phases];
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
static condition cond_client;
|
|
Szabolcs Nagy |
195a19 |
static condition cond_main;
|
|
Szabolcs Nagy |
195a19 |
static unsigned volatile phase;
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
static void settimeout(struct timespec *ts)
|
|
Szabolcs Nagy |
195a19 |
{
|
|
Szabolcs Nagy |
195a19 |
if (clock_gettime(CLOCK_REALTIME, ts))
|
|
Szabolcs Nagy |
ac9368 |
t_error("clock_gettime failed: %s\n", strerror(errno));
|
|
Szabolcs Nagy |
195a19 |
ts->tv_nsec += 500*1000*1000;
|
|
Szabolcs Nagy |
724826 |
if (ts->tv_nsec >= 1000*1000*1000) {
|
|
Szabolcs Nagy |
724826 |
ts->tv_nsec -= 1000*1000*1000;
|
|
Szabolcs Nagy |
724826 |
ts->tv_sec++;
|
|
Szabolcs Nagy |
724826 |
}
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
static thread_ret client(void *arg) {
|
|
Szabolcs Nagy |
195a19 |
struct timespec ts;
|
|
Szabolcs Nagy |
195a19 |
unsigned * number = arg;
|
|
Szabolcs Nagy |
195a19 |
for (unsigned i = 0; i < phases; ++i) {
|
|
Szabolcs Nagy |
195a19 |
trace("thread %u in phase %u\n", *number, i);
|
|
Szabolcs Nagy |
195a19 |
mutex_lock(&mut[i]);
|
|
Szabolcs Nagy |
195a19 |
++inside[i];
|
|
Szabolcs Nagy |
195a19 |
if (inside[i] == threads) {
|
|
Szabolcs Nagy |
195a19 |
trace("thread %u is last, signalling main\n", *number);
|
|
Szabolcs Nagy |
195a19 |
int ret = condition_signal(&cond_main);
|
|
Szabolcs Nagy |
195a19 |
trace("thread %u is last, signalling main, %s\n", *number, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
if (ret)
|
|
Szabolcs Nagy |
195a19 |
t_error("thread %u is last in phase %u, signalling main failed: %s\n", *number, i, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
while (i == phase) {
|
|
Szabolcs Nagy |
195a19 |
tell("thread %u in phase %u (%u), waiting\n", *number, i, phase);
|
|
Szabolcs Nagy |
195a19 |
settimeout(&ts);
|
|
Szabolcs Nagy |
195a19 |
int ret = condition_timedwait(&cond_client, &mut[i], &ts);
|
|
Szabolcs Nagy |
195a19 |
trace("thread %u in phase %u (%u), finished, %s\n", *number, i, phase, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
if (ret) {
|
|
Szabolcs Nagy |
195a19 |
t_error("thread %u in phase %u (%u) finished waiting: %s\n", *number, i, phase, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
exit(t_status);
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
int ret = mutex_unlock(&mut[i]);
|
|
Szabolcs Nagy |
195a19 |
trace("thread %u in phase %u (%u), has unlocked mutex: %s\n", *number, i, phase, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
if (ret)
|
|
Szabolcs Nagy |
195a19 |
t_error("thread %u in phase %u (%u), failed to unlock: %s\n", *number, i, phase, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
return 0;
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
int main(void) {
|
|
Szabolcs Nagy |
195a19 |
struct timespec ts;
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
tell("start up of main, using %s, library %s\n", VERSION, LIBRARY);
|
|
Szabolcs Nagy |
195a19 |
condition_init(&cond_client);
|
|
Szabolcs Nagy |
195a19 |
condition_init(&cond_main);
|
|
Szabolcs Nagy |
195a19 |
for (unsigned i = 0; i < phases; ++i) {
|
|
Szabolcs Nagy |
195a19 |
mutex_init(&mut[i]);
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
mutex_lock(&mut[0]);
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
for (unsigned i = 0; i < threads; ++i) {
|
|
Szabolcs Nagy |
195a19 |
args[i] = i;
|
|
Szabolcs Nagy |
195a19 |
thread_create(&id[i], client, &args[i]);
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
while (phase < phases) {
|
|
Szabolcs Nagy |
195a19 |
while (inside[phase] < threads) {
|
|
Szabolcs Nagy |
195a19 |
trace("main seeing %u threads in phase %u, waiting\n", inside[phase], phase);
|
|
Szabolcs Nagy |
195a19 |
settimeout(&ts);
|
|
Szabolcs Nagy |
195a19 |
int ret = condition_timedwait(&cond_main, &mut[phase], &ts);
|
|
Szabolcs Nagy |
195a19 |
tell("main seeing %u threads in phase %u, %s\n", inside[phase], phase, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
if (ret) {
|
|
Szabolcs Nagy |
195a19 |
t_error("main thread in phase %u (%u threads inside), finished waiting: %s\n", phase, inside[phase], errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
return t_status;
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
/* now we know that everybody is waiting inside, lock the next
|
|
Szabolcs Nagy |
195a19 |
mutex, if any, such that nobody can enter the next phase
|
|
Szabolcs Nagy |
195a19 |
without our permission. */
|
|
Szabolcs Nagy |
195a19 |
if (phase < phases-1)
|
|
Szabolcs Nagy |
195a19 |
mutex_lock(&mut[phase+1]);
|
|
Szabolcs Nagy |
195a19 |
/* Now signal all clients, update the phase count and release the
|
|
Szabolcs Nagy |
195a19 |
mutex they are waiting for. */
|
|
Szabolcs Nagy |
195a19 |
int ret = condition_broadcast(&cond_client);
|
|
Szabolcs Nagy |
195a19 |
trace("main has broadcast to %u: %s\n", phase, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
if (ret)
|
|
Szabolcs Nagy |
195a19 |
t_error("main broadcast in phase %u failed: %s\n", phase, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
++phase;
|
|
Szabolcs Nagy |
195a19 |
ret = mutex_unlock(&mut[phase-1]);
|
|
Szabolcs Nagy |
195a19 |
trace("main has unlocked mutex %u: %s\n", phase-1, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
if (ret)
|
|
Szabolcs Nagy |
195a19 |
t_error("main failed to unlock mutex %u: %s\n", phase-1, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
trace("main finished loop\n");
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
for (unsigned i = 0; i < threads; ++i) {
|
|
Szabolcs Nagy |
195a19 |
trace("main joining thread %u\n", i);
|
|
Szabolcs Nagy |
195a19 |
int ret = thread_join(id[i], &(thread_ret){0});
|
|
Szabolcs Nagy |
195a19 |
trace("main joining thread %u: %s\n", i, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
if (ret)
|
|
Szabolcs Nagy |
195a19 |
t_error("main failed join thread %u: %s\n", i, errorstring(ret));
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
/* C functions to destroy the control structures don't return error
|
|
Szabolcs Nagy |
195a19 |
information, so we can't check for errors, here. */
|
|
Szabolcs Nagy |
195a19 |
for (unsigned i = 0; i < phases; ++i) {
|
|
Szabolcs Nagy |
195a19 |
mutex_destroy(&mut[i]);
|
|
Szabolcs Nagy |
195a19 |
}
|
|
Szabolcs Nagy |
195a19 |
condition_destroy(&cond_main);
|
|
Szabolcs Nagy |
195a19 |
condition_destroy(&cond_client);
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
tell("shut down of main, using %s, library %s\n", VERSION, LIBRARY);
|
|
Szabolcs Nagy |
195a19 |
|
|
Szabolcs Nagy |
195a19 |
return t_status;
|
|
Szabolcs Nagy |
195a19 |
}
|