libroot: Implemented pthread barriers
This is an implementation of pthread barriers pursuant to the relevant specification. Barriers are essentially a special case of conditional variables, such that all threads waiting on one are woken up when the number of waiters reaches a number provided at the initialization of the barrier. In view of that, this implementation mimics the implementation of pthread_cond, except it is more specialized and self-contained. Signed-off-by: Jérôme Duval <jerome.duval@gmail.com>
This commit is contained in:
parent
efd1e1eee8
commit
0e0f49e799
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
157
src/system/libroot/posix/pthread/pthread_barrier.cpp
Normal file
157
src/system/libroot/posix/pthread/pthread_barrier.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2016, Dmytro Shynkevych, dm.shynk@gmail.com
|
||||
* Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
|
||||
#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 = {
|
||||
/* .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;
|
||||
}
|
@ -37,6 +37,7 @@ SimpleTest tst-mktime : tst-mktime.c ;
|
||||
SimpleTest <test>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 ;
|
||||
|
51
src/tests/system/libroot/posix/pthread_barrier_test.cpp
Normal file
51
src/tests/system/libroot/posix/pthread_barrier_test.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user