LibC: Make pthread_barrier safe
It used to deadlock and it was not safe if more threads than the target were attempting to wait on it.
This commit is contained in:
parent
57c9f5a8a8
commit
658a001d91
|
@ -28,17 +28,6 @@ typedef struct
|
||||||
unsigned lock_depth;
|
unsigned lock_depth;
|
||||||
} pthread_mutex_t;
|
} pthread_mutex_t;
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int shared;
|
|
||||||
} pthread_barrierattr_t;
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
pthread_barrierattr_t attr;
|
|
||||||
unsigned target;
|
|
||||||
unsigned waiting;
|
|
||||||
} pthread_barrier_t;
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int clock;
|
int clock;
|
||||||
|
@ -56,6 +45,20 @@ typedef struct
|
||||||
struct _pthread_cond_block* block_list;
|
struct _pthread_cond_block* block_list;
|
||||||
} pthread_cond_t;
|
} pthread_cond_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int shared;
|
||||||
|
} pthread_barrierattr_t;
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
pthread_barrierattr_t attr;
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
pthread_cond_t cond;
|
||||||
|
unsigned target;
|
||||||
|
unsigned waiting;
|
||||||
|
unsigned generation;
|
||||||
|
} pthread_barrier_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int shared;
|
int shared;
|
||||||
|
|
|
@ -1238,28 +1238,34 @@ int pthread_barrier_init(pthread_barrier_t* __restrict barrier, const pthread_ba
|
||||||
attr = &default_attr;
|
attr = &default_attr;
|
||||||
*barrier = {
|
*barrier = {
|
||||||
.attr = *attr,
|
.attr = *attr,
|
||||||
|
.lock = PTHREAD_MUTEX_INITIALIZER,
|
||||||
|
.cond = PTHREAD_COND_INITIALIZER,
|
||||||
.target = count,
|
.target = count,
|
||||||
.waiting = 0,
|
.waiting = 0,
|
||||||
|
.generation = 0,
|
||||||
};
|
};
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_barrier_wait(pthread_barrier_t* barrier)
|
int pthread_barrier_wait(pthread_barrier_t* barrier)
|
||||||
{
|
{
|
||||||
const unsigned index = BAN::atomic_add_fetch(barrier->waiting, 1);
|
pthread_mutex_lock(&barrier->lock);
|
||||||
|
|
||||||
// FIXME: this case should be handled, but should be relatively uncommon
|
const auto gen = barrier->generation;
|
||||||
// so i'll just roll with the easy implementation
|
barrier->waiting++;
|
||||||
ASSERT(index <= barrier->target);
|
|
||||||
|
|
||||||
if (index == barrier->target)
|
if (barrier->waiting == barrier->target)
|
||||||
{
|
{
|
||||||
BAN::atomic_store(barrier->waiting, 0);
|
barrier->waiting = 0;
|
||||||
|
barrier->generation++;
|
||||||
|
pthread_cond_broadcast(&barrier->cond);
|
||||||
|
pthread_mutex_unlock(&barrier->lock);
|
||||||
return PTHREAD_BARRIER_SERIAL_THREAD;
|
return PTHREAD_BARRIER_SERIAL_THREAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (BAN::atomic_load(barrier->waiting))
|
while (barrier->generation == gen)
|
||||||
sched_yield();
|
pthread_cond_wait(&barrier->cond, &barrier->lock);
|
||||||
|
pthread_mutex_unlock(&barrier->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue