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:
Dmytro Shynkevych 2016-12-27 13:54:32 -05:00 committed by Jérôme Duval
parent efd1e1eee8
commit 0e0f49e799
7 changed files with 238 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

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

View File

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

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