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 <[email protected]>
This commit is contained in:
committed by
Jérôme Duval
parent
efd1e1eee8
commit
0e0f49e799
@@ -114,6 +114,20 @@ extern int pthread_mutexattr_setpshared(pthread_mutexattr_t *mutexAttr,
|
|||||||
int processShared);
|
int processShared);
|
||||||
extern int pthread_mutexattr_settype(pthread_mutexattr_t *mutexAttr, int type);
|
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 */
|
/* condition variable functions */
|
||||||
extern int pthread_cond_destroy(pthread_cond_t *cond);
|
extern int pthread_cond_destroy(pthread_cond_t *cond);
|
||||||
extern int pthread_cond_init(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_thread *pthread_t;
|
||||||
typedef struct _pthread_attr *pthread_attr_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_mutex pthread_mutex_t;
|
||||||
typedef struct _pthread_mutexattr *pthread_mutexattr_t;
|
typedef struct _pthread_mutexattr *pthread_mutexattr_t;
|
||||||
typedef struct _pthread_cond pthread_cond_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_rwlock pthread_rwlock_t;
|
||||||
typedef struct _pthread_rwlockattr *pthread_rwlockattr_t;
|
typedef struct _pthread_rwlockattr *pthread_rwlockattr_t;
|
||||||
typedef struct _pthread_spinlock pthread_spinlock_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 {
|
struct _pthread_mutex {
|
||||||
__haiku_std_uint32 flags;
|
__haiku_std_uint32 flags;
|
||||||
@@ -82,6 +80,14 @@ struct _pthread_mutex {
|
|||||||
__haiku_std_int32 owner_count;
|
__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 {
|
struct _pthread_cond {
|
||||||
__haiku_std_uint32 flags;
|
__haiku_std_uint32 flags;
|
||||||
__haiku_std_int32 unused;
|
__haiku_std_int32 unused;
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ typedef struct _pthread_mutexattr {
|
|||||||
bool process_shared;
|
bool process_shared;
|
||||||
} pthread_mutexattr;
|
} pthread_mutexattr;
|
||||||
|
|
||||||
|
typedef struct _pthread_barrierattr {
|
||||||
|
bool process_shared;
|
||||||
|
} pthread_barrierattr;
|
||||||
|
|
||||||
typedef struct _pthread_attr {
|
typedef struct _pthread_attr {
|
||||||
int32 detach_state;
|
int32 detach_state;
|
||||||
int32 sched_priority;
|
int32 sched_priority;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
|
|||||||
pthread.cpp
|
pthread.cpp
|
||||||
pthread_atfork.c
|
pthread_atfork.c
|
||||||
pthread_attr.c
|
pthread_attr.c
|
||||||
|
pthread_barrier.cpp
|
||||||
pthread_cancel.cpp
|
pthread_cancel.cpp
|
||||||
pthread_cleanup.cpp
|
pthread_cleanup.cpp
|
||||||
pthread_cond.cpp
|
pthread_cond.cpp
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Dmytro Shynkevych, [email protected]
|
||||||
|
* 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 <test>truncate : truncate.cpp ;
|
||||||
SimpleTest init_rld_after_fork_test : init_rld_after_fork_test.cpp ;
|
SimpleTest init_rld_after_fork_test : init_rld_after_fork_test.cpp ;
|
||||||
SimpleTest user_thread_fork_test : user_thread_fork_test.cpp ;
|
SimpleTest user_thread_fork_test : user_thread_fork_test.cpp ;
|
||||||
|
SimpleTest pthread_barrier_test : pthread_barrier_test.cpp ;
|
||||||
|
|
||||||
# XSI tests
|
# XSI tests
|
||||||
SimpleTest xsi_msg_queue_test1 : xsi_msg_queue_test1.cpp ;
|
SimpleTest xsi_msg_queue_test1 : xsi_msg_queue_test1.cpp ;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user