Last active
February 1, 2016 01:28
-
-
Save wilx/bb77de5d3f554afedd79 to your computer and use it in GitHub Desktop.
possible reimplementation of pthread_barrier* API for Cygwin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <pthread.h> | |
#include <stdlib.h> | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <unistd.h> | |
#include <stdio.h> | |
#include <errno.h> | |
#define faux_LIKELY(X) __builtin_expect (!!(X), 1) | |
#define faux_UNLIKELY(X) __builtin_expect (!!(X), 0) | |
struct faux_pthread_barrierattr_t_struct | |
{ | |
unsigned magic; | |
int shared; | |
}; | |
typedef struct faux_pthread_barrierattr_t_struct faux_pthread_barrierattr_t; | |
#define faux_PTHREAD_BARRIERATTR_MAGIC \ | |
((0xBAu << 24) \ | |
| (((unsigned)(unsigned char)'R') << 16) \ | |
| (((unsigned)(unsigned char)'R') << 8) \ | |
| 0x1Au) | |
#define faux_PTHREAD_PROCESS_SHARED (1) | |
#define faux_PTHREAD_PROCESS_PRIVATE (0) | |
int | |
faux_pthread_barrierattr_init (faux_pthread_barrierattr_t * battr) | |
{ | |
if (faux_UNLIKELY(battr == NULL)) | |
return EINVAL; | |
battr->shared = faux_PTHREAD_PROCESS_PRIVATE; | |
battr->magic = faux_PTHREAD_BARRIERATTR_MAGIC; | |
return 0; | |
} | |
int | |
faux_pthread_barrierattr_setpshared (faux_pthread_barrierattr_t * battr, int shared) | |
{ | |
if (faux_UNLIKELY(battr == NULL | |
|| battr->magic != faux_PTHREAD_BARRIERATTR_MAGIC)) | |
return EINVAL; | |
if (faux_UNLIKELY(shared != faux_PTHREAD_PROCESS_SHARED | |
&& shared != faux_PTHREAD_PROCESS_PRIVATE)) | |
return EINVAL; | |
battr->shared = shared; | |
return 0; | |
} | |
int | |
faux_pthread_barrierattr_getpshared (const faux_pthread_barrierattr_t * restrict battr, | |
int * restrict shared) | |
{ | |
if (faux_UNLIKELY(battr == NULL | |
|| battr->magic != faux_PTHREAD_BARRIERATTR_MAGIC | |
|| shared == NULL)) | |
return EINVAL; | |
*shared = battr->shared; | |
return 0; | |
} | |
int | |
faux_pthread_barrierattr_destroy (faux_pthread_barrierattr_t * battr) | |
{ | |
if (faux_UNLIKELY(battr == NULL | |
|| battr->magic != faux_PTHREAD_BARRIERATTR_MAGIC)) | |
return EINVAL; | |
battr->magic = 0; | |
return 0; | |
} | |
#define faux_PTHREAD_BARRIER_MAGIC \ | |
((0xBAu << 24) \ | |
| (((unsigned)(unsigned char)'R') << 16) \ | |
| (((unsigned)(unsigned char)'R') << 8) \ | |
| 0x1Eu) | |
struct faux_pthread_barrier_t_struct | |
{ | |
unsigned magic; | |
pthread_mutex_t mtx; /* Mutex protecting everything below. */ | |
pthread_cond_t cond; /* Conditional variable to wait on. */ | |
unsigned cnt; /* Barrier count. Threads to wait for. */ | |
uint64_t cyc; /* Cycle count. */ | |
unsigned wt; /* Already waiting threads count. */ | |
}; | |
typedef struct faux_pthread_barrier_t_struct faux_pthread_barrier_t; | |
#define faux_PTHREAD_BARRIER_SERIAL_THREAD (-1) | |
int | |
faux_pthread_barrier_init (faux_pthread_barrier_t * bar, | |
const faux_pthread_barrierattr_t * attr, | |
unsigned count) | |
{ | |
(void)attr; | |
pthread_mutex_t * mutex = NULL; | |
if (faux_UNLIKELY(bar == NULL | |
|| (attr != NULL | |
&& (attr->magic != faux_PTHREAD_BARRIERATTR_MAGIC | |
|| attr->shared == faux_PTHREAD_PROCESS_SHARED)) | |
|| count == 0)) | |
return EINVAL; | |
int retval = pthread_mutex_init (&bar->mtx, NULL); | |
if (faux_UNLIKELY(retval != 0)) | |
return retval; | |
retval = pthread_cond_init (&bar->cond, NULL); | |
if (faux_UNLIKELY(retval != 0)) | |
{ | |
(void)pthread_mutex_destroy (mutex); | |
return retval; | |
} | |
bar->cnt = count; | |
bar->cyc = 0; | |
bar->wt = 0; | |
bar->magic = faux_PTHREAD_BARRIER_MAGIC; | |
return 0; | |
} | |
int | |
faux_pthread_barrier_destroy (faux_pthread_barrier_t * bar) | |
{ | |
if (faux_UNLIKELY(bar == NULL | |
|| bar->magic != faux_PTHREAD_BARRIER_MAGIC)) | |
return EINVAL; | |
if (faux_UNLIKELY(bar->wt != 0)) | |
return EBUSY; | |
int retval = pthread_cond_destroy (&bar->cond); | |
if (faux_UNLIKELY(retval != 0)) | |
return retval; | |
retval = pthread_mutex_destroy (&bar->mtx); | |
if (faux_UNLIKELY(retval != 0)) | |
return retval; | |
bar->cnt = 0; | |
bar->cyc = 0; | |
bar->wt = 0; | |
bar->magic = 0; | |
return 0; | |
} | |
int | |
faux_pthread_barrier_wait (faux_pthread_barrier_t * bar) | |
{ | |
if (faux_UNLIKELY(bar == NULL | |
|| bar->magic != faux_PTHREAD_BARRIER_MAGIC)) | |
return EINVAL; | |
int retval = pthread_mutex_lock (&bar->mtx); | |
if (faux_UNLIKELY(retval != 0)) | |
return retval; | |
if (faux_UNLIKELY(bar->wt >= bar->cnt)) | |
abort (); | |
if (faux_UNLIKELY(++bar->wt == bar->cnt)) | |
{ | |
++bar->cyc; | |
/* This is the last thread to reach the barrier. Signal the waiting | |
threads to wake up and continue. */ | |
retval = pthread_cond_broadcast (&bar->cond); | |
if (faux_UNLIKELY(retval != 0)) | |
goto cond_error; | |
bar->wt = 0; | |
retval = pthread_mutex_unlock (&bar->mtx); | |
if (faux_UNLIKELY(retval != 0)) | |
abort (); | |
return faux_PTHREAD_BARRIER_SERIAL_THREAD; | |
} | |
else | |
{ | |
uint64_t cycle = bar->cyc; | |
do | |
{ | |
retval = pthread_cond_wait (&bar->cond, &bar->mtx); | |
if (faux_UNLIKELY(retval != 0)) | |
goto cond_error; | |
} | |
while (faux_UNLIKELY(cycle == bar->cyc)); | |
retval = pthread_mutex_unlock (&bar->mtx); | |
if (faux_UNLIKELY(retval != 0)) | |
abort (); | |
return 0; | |
} | |
cond_error: | |
{ | |
--bar->wt; | |
int ret = pthread_mutex_unlock (&bar->mtx); | |
if (faux_UNLIKELY(ret != 0)) | |
abort (); | |
return retval; | |
} | |
} | |
faux_pthread_barrier_t barrier; | |
#define ROUNDS 5 | |
void * | |
thread_routine (void * arg) | |
{ | |
int myid = (ptrdiff_t)arg; | |
for (int round = 0; round != ROUNDS; ++round) | |
{ | |
printf ("Thread %d reporting for duty. Going to wait on barrier. Round %d.\n", | |
myid, round); | |
int retval = faux_pthread_barrier_wait (&barrier); | |
if (retval == faux_PTHREAD_BARRIER_SERIAL_THREAD) | |
printf ("I, number %d, was the last thread to make it to the barrier. Round %d.\n", | |
myid, round); | |
else if (retval != 0) | |
printf ("I, number %d, got error %d on barrier. Round %d.\n", | |
myid, retval, round); | |
else | |
printf ("I, number %d, got released by the barrier. Round %d.\n", | |
myid, round); | |
} | |
return NULL; | |
} | |
#define COUNT 5 | |
pthread_t threads[COUNT]; | |
int | |
main () | |
{ | |
faux_pthread_barrierattr_t battr; | |
int retval = faux_pthread_barrierattr_init (&battr); | |
if (retval != 0) | |
{ | |
printf ("Failed to initialize barrier attribute: %d", retval); | |
return retval; | |
} | |
retval = faux_pthread_barrierattr_setpshared (&battr, faux_PTHREAD_PROCESS_SHARED); | |
if (retval != 0) | |
{ | |
printf ("Failed to set faux_PTHREAD_PROCESS_SHARED on barrier attribute: %d\n", | |
retval); | |
return retval; | |
} | |
retval = faux_pthread_barrierattr_setpshared (&battr, faux_PTHREAD_PROCESS_PRIVATE); | |
if (retval != 0) | |
{ | |
printf ("Failed to set faux_PTHREAD_PROCESS_PRIVATE on barrier attribute: %d\n", | |
retval); | |
return retval; | |
} | |
retval = faux_pthread_barrier_init (&barrier, &battr, COUNT); | |
if (retval != 0) | |
{ | |
printf ("Failed to init barrier: %d\n", retval); | |
return retval; | |
} | |
retval = faux_pthread_barrierattr_destroy (&battr); | |
if (retval != 0) | |
{ | |
printf ("Failed to destroy barrier attribute: %d\n", retval); | |
return retval; | |
} | |
for (int i = 0; i != COUNT; ++i) | |
{ | |
retval = pthread_create(&threads[i], NULL, thread_routine, | |
(void*)(ptrdiff_t)i); | |
if (retval != 0) | |
{ | |
printf ("Failed to create thread %d, error %d.\n", i, retval); | |
return retval; | |
} | |
} | |
for (int i = 0; i != COUNT; ++i) | |
{ | |
retval = pthread_join (threads[i], NULL); | |
if (retval != 0) | |
{ | |
printf ("Failed to join thread %d, error %d.\n", i, retval); | |
return retval; | |
} | |
} | |
retval = faux_pthread_barrier_destroy (&barrier); | |
if (retval != 0) | |
{ | |
printf ("Failed to destroy barrier: %d\n", retval); | |
return retval; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment