#include <pthread.h>
#include "pthread_private.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syscall_utils.h>
#include <syscalls.h>
#include <user_mutex_defs.h>
#define BARRIER_FLAG_SHARED 0x80000000
static const pthread_barrierattr pthread_barrierattr_default = {
false
};
int
pthread_barrier_init(pthread_barrier_t* barrier,
const pthread_barrierattr_t* _attr, unsigned count)
{
const pthread_barrierattr* attr = _attr != NULL
? *_attr : &pthread_barrierattr_default;
if (barrier == NULL || attr == NULL || count < 1)
return B_BAD_VALUE;
barrier->flags = attr->process_shared ? BARRIER_FLAG_SHARED : 0;
barrier->lock = B_USER_MUTEX_LOCKED;
barrier->mutex = B_USER_MUTEX_LOCKED | B_USER_MUTEX_DISABLED;
barrier->waiter_count = 0;
barrier->waiter_max = count;
return B_OK;
}
static void
barrier_disable_and_unblock(__haiku_std_int32* mutex, uint32 flags)
{
int32 oldValue = atomic_or((int32*)mutex, B_USER_MUTEX_DISABLED);
if ((oldValue & B_USER_MUTEX_WAITING) != 0)
_kern_mutex_unblock((int32*)mutex, flags | B_USER_MUTEX_UNBLOCK_ALL);
}
static void
barrier_ensure_none_exiting(pthread_barrier_t* barrier)
{
const uint32 flags = (barrier->flags & BARRIER_FLAG_SHARED) ? B_USER_MUTEX_SHARED : 0;
while (atomic_get((int32*)&barrier->waiter_count) < 0) {
status_t status = _kern_mutex_lock((int32*)&barrier->mutex, NULL, flags, 0);
if (status != B_INTERRUPTED)
return;
}
}
int
pthread_barrier_wait(pthread_barrier_t* barrier)
{
if (barrier == NULL)
return B_BAD_VALUE;
if (barrier->waiter_max == 1)
return PTHREAD_BARRIER_SERIAL_THREAD;
const uint32 mutexFlags = (barrier->flags & BARRIER_FLAG_SHARED) ? B_USER_MUTEX_SHARED : 0;
barrier_ensure_none_exiting(barrier);
if (atomic_add((int32*)&barrier->waiter_count, 1) == (barrier->waiter_max - 1)) {
barrier->waiter_count = (-barrier->waiter_max) + 1;
barrier->mutex = B_USER_MUTEX_LOCKED;
barrier_disable_and_unblock(&barrier->lock, mutexFlags);
return PTHREAD_BARRIER_SERIAL_THREAD;
}
do {
_kern_mutex_lock((int32*)&barrier->lock, "barrier wait", mutexFlags, 0);
} while (barrier->waiter_count > 0);
if (atomic_add((int32*)&barrier->waiter_count, 1) == -1) {
atomic_and((int32*)&barrier->lock, ~(int32)B_USER_MUTEX_DISABLED);
barrier_disable_and_unblock(&barrier->mutex, mutexFlags);
}
return 0;
}
int
pthread_barrier_destroy(pthread_barrier_t* barrier)
{
barrier_ensure_none_exiting(barrier);
while (atomic_get((int32*)&barrier->mutex) != (B_USER_MUTEX_LOCKED | B_USER_MUTEX_DISABLED))
sched_yield();
return B_OK;
}
int
pthread_barrierattr_init(pthread_barrierattr_t* _attr)
{
pthread_barrierattr* attr = (pthread_barrierattr*)malloc(
sizeof(pthread_barrierattr));
if (attr == NULL)
return B_NO_MEMORY;
*attr = pthread_barrierattr_default;
*_attr = attr;
return B_OK;
}
int
pthread_barrierattr_destroy(pthread_barrierattr_t* _attr)
{
pthread_barrierattr* attr = _attr != NULL ? *_attr : NULL;
if (attr == NULL)
return B_BAD_VALUE;
free(attr);
return B_OK;
}
int
pthread_barrierattr_getpshared(const pthread_barrierattr_t* _attr, int* shared)
{
pthread_barrierattr* attr;
if (_attr == NULL || (attr = *_attr) == NULL || shared == NULL)
return B_BAD_VALUE;
*shared = attr->process_shared
? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE;
return B_OK;
}
int
pthread_barrierattr_setpshared(pthread_barrierattr_t* _attr, int shared)
{
pthread_barrierattr* attr;
if (_attr == NULL || (attr = *_attr) == NULL
|| shared < PTHREAD_PROCESS_PRIVATE
|| shared > PTHREAD_PROCESS_SHARED) {
return B_BAD_VALUE;
}
attr->process_shared = shared == PTHREAD_PROCESS_SHARED;
return 0;
}