diff --git a/headers/posix/pthread.h b/headers/posix/pthread.h index 76d3cce2d7..ef7e14596e 100644 --- a/headers/posix/pthread.h +++ b/headers/posix/pthread.h @@ -114,6 +114,20 @@ extern int pthread_mutexattr_setpshared(pthread_mutexattr_t *mutexAttr, int processShared); extern int pthread_mutexattr_settype(pthread_mutexattr_t *mutexAttr, int type); +/* barrier functions */ +extern int pthread_barrier_init(pthread_barrier_t *barrier, + const pthread_barrierattr_t *attr, unsigned count); +extern int pthread_barrier_destroy(pthread_barrier_t *barrier); +extern int pthread_barrier_wait(pthread_barrier_t *barrier); + +/* barrier attribute functions */ +extern int pthread_barrierattr_destroy(pthread_barrierattr_t *attr); +extern int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, + int *shared); +extern int pthread_barrierattr_init(pthread_barrierattr_t *attr); +extern int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, + int shared); + /* condition variable functions */ extern int pthread_cond_destroy(pthread_cond_t *cond); extern int pthread_cond_init(pthread_cond_t *cond, diff --git a/headers/posix/sys/types.h b/headers/posix/sys/types.h index 7ae5450630..b09aba569a 100644 --- a/headers/posix/sys/types.h +++ b/headers/posix/sys/types.h @@ -60,6 +60,8 @@ typedef struct __timer_t* timer_t; typedef struct _pthread_thread *pthread_t; typedef struct _pthread_attr *pthread_attr_t; +typedef struct _pthread_barrier pthread_barrier_t; +typedef struct _pthread_barrierattr *pthread_barrierattr_t; typedef struct _pthread_mutex pthread_mutex_t; typedef struct _pthread_mutexattr *pthread_mutexattr_t; typedef struct _pthread_cond pthread_cond_t; @@ -69,10 +71,6 @@ typedef struct _pthread_once pthread_once_t; typedef struct _pthread_rwlock pthread_rwlock_t; typedef struct _pthread_rwlockattr *pthread_rwlockattr_t; typedef struct _pthread_spinlock pthread_spinlock_t; -/* -typedef struct _pthread_barrier *pthread_barrier_t; -typedef struct _pthread_barrierattr *pthread_barrierattr_t; -*/ struct _pthread_mutex { __haiku_std_uint32 flags; @@ -82,6 +80,14 @@ struct _pthread_mutex { __haiku_std_int32 owner_count; }; +struct _pthread_barrier { + __haiku_std_uint32 flags; + __haiku_std_int32 lock; + __haiku_std_int32 mutex; + __haiku_std_int32 waiter_count; + __haiku_std_int32 waiter_max; +}; + struct _pthread_cond { __haiku_std_uint32 flags; __haiku_std_int32 unused; diff --git a/headers/private/libroot/pthread_private.h b/headers/private/libroot/pthread_private.h index 169bc8d9bf..953315b60c 100644 --- a/headers/private/libroot/pthread_private.h +++ b/headers/private/libroot/pthread_private.h @@ -37,6 +37,10 @@ typedef struct _pthread_mutexattr { bool process_shared; } pthread_mutexattr; +typedef struct _pthread_barrierattr { + bool process_shared; +} pthread_barrierattr; + typedef struct _pthread_attr { int32 detach_state; int32 sched_priority; diff --git a/src/system/libroot/posix/pthread/Jamfile b/src/system/libroot/posix/pthread/Jamfile index 1968a8e8ee..9a37630f10 100644 --- a/src/system/libroot/posix/pthread/Jamfile +++ b/src/system/libroot/posix/pthread/Jamfile @@ -15,6 +15,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { pthread.cpp pthread_atfork.c pthread_attr.c + pthread_barrier.cpp pthread_cancel.cpp pthread_cleanup.cpp pthread_cond.cpp diff --git a/src/system/libroot/posix/pthread/pthread_barrier.cpp b/src/system/libroot/posix/pthread/pthread_barrier.cpp new file mode 100644 index 0000000000..7421cb7e25 --- /dev/null +++ b/src/system/libroot/posix/pthread/pthread_barrier.cpp @@ -0,0 +1,157 @@ +/* + * Copyright 2016, Dmytro Shynkevych, dm.shynk@gmail.com + * Distributed under the terms of the MIT license. + */ + + +#include +#include "pthread_private.h" + +#include +#include +#include + +#include +#include +#include + + +#define BARRIER_FLAG_SHARED 0x80000000 + + +static const pthread_barrierattr pthread_barrierattr_default = { + /* .process_shared = */ 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 = 0; + barrier->mutex = 0; + barrier->waiter_count = 0; + barrier->waiter_max = count; + + return B_OK; +} + + +int +pthread_barrier_wait(pthread_barrier_t* barrier) +{ + if (barrier == NULL) + return B_BAD_VALUE; + + // Enter critical region: lock the mutex + int32 status = atomic_or((int32*)&barrier->mutex, B_USER_MUTEX_LOCKED); + + // If already locked, call the kernel + if (status & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) { + do { + status = _kern_mutex_lock((int32*)&barrier->mutex, NULL, 0, 0); + } while (status == B_INTERRUPTED); + + if (status != B_OK) + return status; + } + + barrier->waiter_count++; + + // If this thread is the last to arrive + if (barrier->waiter_count == barrier->waiter_max) { + // Let other threads exit the do...while loop + barrier->waiter_count = 0; + + // Wake up everyone trying to acquire the barrier lock + _kern_mutex_unlock((int32*)&barrier->lock, B_USER_MUTEX_UNBLOCK_ALL); + + // Exit critical region: unlock the mutex + int32 status = atomic_and((int32*)&barrier->mutex, + ~(int32)B_USER_MUTEX_LOCKED); + + if (status & B_USER_MUTEX_WAITING) + _kern_mutex_unlock((int32*)&barrier->mutex, 0); + + // Inform the calling thread that it arrived last + return PTHREAD_BARRIER_SERIAL_THREAD; + } + + do { + // Wait indefinitely trying to acquire the barrier lock. + // Other threads may now enter (mutex is unlocked). + _kern_mutex_switch_lock((int32*)&barrier->mutex, + (int32*)&barrier->lock, "barrier wait", 0, 0); + } while (barrier->waiter_count != 0); + + // This thread did not arrive last + return 0; +} + + +int +pthread_barrier_destroy(pthread_barrier_t* barrier) +{ + // No dynamic resources to free + 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 = *_attr; + + *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 = *_attr; + + attr->process_shared = shared == PTHREAD_PROCESS_SHARED; + + return 0; +} diff --git a/src/tests/system/libroot/posix/Jamfile b/src/tests/system/libroot/posix/Jamfile index 95537a9f38..b62c971448 100644 --- a/src/tests/system/libroot/posix/Jamfile +++ b/src/tests/system/libroot/posix/Jamfile @@ -37,6 +37,7 @@ SimpleTest tst-mktime : tst-mktime.c ; SimpleTest truncate : truncate.cpp ; SimpleTest init_rld_after_fork_test : init_rld_after_fork_test.cpp ; SimpleTest user_thread_fork_test : user_thread_fork_test.cpp ; +SimpleTest pthread_barrier_test : pthread_barrier_test.cpp ; # XSI tests SimpleTest xsi_msg_queue_test1 : xsi_msg_queue_test1.cpp ; diff --git a/src/tests/system/libroot/posix/pthread_barrier_test.cpp b/src/tests/system/libroot/posix/pthread_barrier_test.cpp new file mode 100644 index 0000000000..d9afc156a1 --- /dev/null +++ b/src/tests/system/libroot/posix/pthread_barrier_test.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +#define THREAD_COUNT 10 +#define CYCLES 2 + +pthread_barrier_t mybarrier; + +void* threadFn(void* id_ptr) +{ + int thread_id = *(int*)id_ptr; + + for (int i = 0; i < CYCLES; ++i) { + int wait_sec = 1 + rand() % 10; + printf("thread %d: Wait %d seconds.\n", thread_id, wait_sec); + sleep(wait_sec); + printf("thread %d: Waiting on barrier...\n", thread_id); + + int status = pthread_barrier_wait(&mybarrier); + if (status == PTHREAD_BARRIER_SERIAL_THREAD) + printf("thread %d: serial thread.\n", thread_id); + printf("thread %d: Finished!\n", thread_id); + } + + return NULL; +} + + +int main() +{ + pthread_t ids[THREAD_COUNT]; + int short_ids[THREAD_COUNT]; + + srand(time(NULL)); + pthread_barrier_init(&mybarrier, NULL, THREAD_COUNT); + + for (int i = 0; i < THREAD_COUNT; i++) { + short_ids[i] = i; + pthread_create(&ids[i], NULL, threadFn, &short_ids[i]); + } + + for (int i = 0; i < THREAD_COUNT; i++) + pthread_join(ids[i], NULL); + + pthread_barrier_destroy(&mybarrier); + + return 0; +} +