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:
Bananymous 2025-08-04 19:08:50 +03:00
parent 57c9f5a8a8
commit 658a001d91
2 changed files with 28 additions and 19 deletions

View File

@ -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;

View File

@ -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;
} }