As @nyh noted in #823 gcc 4.8 doesn't like both using and redefining pthread_mutex_lock in the same file. This is a workaround for what seems to be a compiler bug. The code using pthread_mutex_lock/unlock (pthread_barrier_* functions) is moved to a new .cc file.
Fixes #823 Signed-off-by: Justin Cinkelj <[email protected]> --- Makefile | 1 + libc/pthread.cc | 116 +------------------------------------------- libc/pthread_barrier.cc | 126 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 115 deletions(-) create mode 100644 libc/pthread_barrier.cc diff --git a/Makefile b/Makefile index cf58c0a..bca37eb 100644 --- a/Makefile +++ b/Makefile @@ -1652,6 +1652,7 @@ musl += regex/tre-mem.o $(out)/musl/src/regex/tre-mem.o: CFLAGS += -UNDEBUG libc += pthread.o +libc += pthread_barrier.o libc += libc.o libc += dlfcn.o libc += time.o diff --git a/libc/pthread.cc b/libc/pthread.cc index 62de17d..a2b9292 100644 --- a/libc/pthread.cc +++ b/libc/pthread.cc @@ -29,7 +29,7 @@ #include <api/time.h> #include <osv/rwlock.h> -#include <osv/latch.hh> + #include "pthread.hh" namespace pthread_private { @@ -1128,117 +1128,3 @@ int pthread_attr_getaffinity_np(const pthread_attr_t *attr, size_t cpusetsize, return 0; } - -// Private definitions of the internal structs backing pthread_barrier_t and -// pthread_barrierattr_t -typedef struct -{ - unsigned int out; - unsigned int count; - latch *ltch; - pthread_mutex_t *mtx; -} pthread_barrier_t_int; - -typedef struct -{ - unsigned pshared; -} pthread_barrierattr_t_int; - -int pthread_barrier_init(pthread_barrier_t *barrier_opq, - const pthread_barrierattr_t *attr_opq, - unsigned count) -{ - pthread_barrier_t_int *barrier = (pthread_barrier_t_int*) barrier_opq; - static_assert(sizeof(pthread_barrier_t_int) <= sizeof(pthread_barrier_t), - "pthread_barrier_t_int is larger than pthread_barrier_t"); - - // Linux returns EINVAL if count == 0 or INT_MAX so we do too. - // In theory, we could go up to UINT_MAX since count is unsigned. - if (!barrier || count == 0 || count >= INT_MAX) { - return EINVAL; - } - - // Always ignore attr, it has no meaning in the context of a unikernel. - // pthread_barrierattr_t has a single member variable pshared that can be set - // to PTHREAD_PROCESS_PRIVATE or PTHREAD_PROCESS_SHARED. These have the - // same effect in a unikernel - there is only a single process and all - // threads can manipulate the memory area associated with the - // pthread_barrier_t so it doesn't matter what the value of pshared is set to - barrier->count = count; - barrier->out = 0; - barrier->ltch = new latch(count); - barrier->mtx = new pthread_mutex_t; - pthread_mutex_init(barrier->mtx, NULL); - return 0; -} - -int pthread_barrier_wait(pthread_barrier_t *barrier_opq) -{ - pthread_barrier_t_int *barrier = (pthread_barrier_t_int*) barrier_opq; - static_assert(sizeof(pthread_barrier_t_int) <= sizeof(pthread_barrier_t), - "pthread_barrier_t_int is larger than pthread_barrier_t"); - - if (!barrier || !barrier->ltch || !barrier->mtx) { - return EINVAL; - } - - int retval = 0; - pthread_mutex_t *mtx = barrier->mtx; - - pthread_mutex_lock(mtx); - pthread_mutex_unlock(mtx); - - latch *l = barrier->ltch; - l->count_down(); - // All threads stuck here until we get at least 'count' waiters - l->await(); - - // If the last thread (thread x) to wait on the barrier is descheduled here - // (immediately after being the count'th thread crossing the barrier) - // the barrier remains open (a new waiting thread will cross) until - // the barrier is reset below (when thread x is rescheduled), which doesn't - // seem technically incorrect. Only one of the crossing threads will get a - // retval of PTHREAD_BARRIER_SERIAL_THREAD, when - // barrier->out == barrier->count. - // All other crossing threads will get a retval of 0. - - pthread_mutex_lock(mtx); - barrier->out++; - // Make the last thread out responsible for resetting the barrier's latch. - // The last thread also gets the special return value - // PTHREAD_BARRIER_SERIAL_THREAD. Every other thread gets a retval of 0 - if (barrier->out == barrier->count) { - retval = PTHREAD_BARRIER_SERIAL_THREAD; - // Reset the latch for the next round of waiters. We're using an - // external lock (mtx) to ensure that no other thread is calling - // count_down or in await when we're resetting it. Without the external - // lock, resetting the latch isn't safe. - l->unsafe_reset(barrier->count); - // Reset the 'out' counter so that the equality check above works across - // multiple rounds of threads waiting on the barrier - barrier->out = 0; - } - pthread_mutex_unlock(mtx); - return retval; -} - -int pthread_barrier_destroy(pthread_barrier_t *barrier_opq) -{ - pthread_barrier_t_int *barrier = (pthread_barrier_t_int*) barrier_opq; - - static_assert(sizeof(pthread_barrier_t_int) <= sizeof(pthread_barrier_t), - "pthread_barrier_t_int is larger than pthread_barrier_t"); - - if (!barrier || !barrier->ltch || !barrier->mtx) { - return EINVAL; - } - - delete barrier->ltch; - barrier->ltch = nullptr; - - pthread_mutex_destroy(barrier->mtx); - delete barrier->mtx; - barrier->mtx = nullptr; - - return 0; -} diff --git a/libc/pthread_barrier.cc b/libc/pthread_barrier.cc new file mode 100644 index 0000000..2437d73 --- /dev/null +++ b/libc/pthread_barrier.cc @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2013 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include <pthread.h> +#include <osv/debug.hh> +#include <osv/rwlock.h> +#include <osv/latch.hh> +#include "pthread.hh" + +// Private definitions of the internal structs backing pthread_barrier_t and +// pthread_barrierattr_t +typedef struct +{ + unsigned int out; + unsigned int count; + latch *ltch; + pthread_mutex_t *mtx; +} pthread_barrier_t_int; + +typedef struct +{ + unsigned pshared; +} pthread_barrierattr_t_int; + +int pthread_barrier_init(pthread_barrier_t *barrier_opq, + const pthread_barrierattr_t *attr_opq, + unsigned count) +{ + pthread_barrier_t_int *barrier = (pthread_barrier_t_int*) barrier_opq; + static_assert(sizeof(pthread_barrier_t_int) <= sizeof(pthread_barrier_t), + "pthread_barrier_t_int is larger than pthread_barrier_t"); + + // Linux returns EINVAL if count == 0 or INT_MAX so we do too. + // In theory, we could go up to UINT_MAX since count is unsigned. + if (!barrier || count == 0 || count >= INT_MAX) { + return EINVAL; + } + + // Always ignore attr, it has no meaning in the context of a unikernel. + // pthread_barrierattr_t has a single member variable pshared that can be set + // to PTHREAD_PROCESS_PRIVATE or PTHREAD_PROCESS_SHARED. These have the + // same effect in a unikernel - there is only a single process and all + // threads can manipulate the memory area associated with the + // pthread_barrier_t so it doesn't matter what the value of pshared is set to + barrier->count = count; + barrier->out = 0; + barrier->ltch = new latch(count); + barrier->mtx = new pthread_mutex_t; + pthread_mutex_init(barrier->mtx, NULL); + return 0; +} + +int pthread_barrier_wait(pthread_barrier_t *barrier_opq) +{ + pthread_barrier_t_int *barrier = (pthread_barrier_t_int*) barrier_opq; + static_assert(sizeof(pthread_barrier_t_int) <= sizeof(pthread_barrier_t), + "pthread_barrier_t_int is larger than pthread_barrier_t"); + + if (!barrier || !barrier->ltch || !barrier->mtx) { + return EINVAL; + } + + int retval = 0; + pthread_mutex_t *mtx = barrier->mtx; + + pthread_mutex_lock(mtx); + pthread_mutex_unlock(mtx); + + latch *l = barrier->ltch; + l->count_down(); + // All threads stuck here until we get at least 'count' waiters + l->await(); + + // If the last thread (thread x) to wait on the barrier is descheduled here + // (immediately after being the count'th thread crossing the barrier) + // the barrier remains open (a new waiting thread will cross) until + // the barrier is reset below (when thread x is rescheduled), which doesn't + // seem technically incorrect. Only one of the crossing threads will get a + // retval of PTHREAD_BARRIER_SERIAL_THREAD, when + // barrier->out == barrier->count. + // All other crossing threads will get a retval of 0. + + pthread_mutex_lock(mtx); + barrier->out++; + // Make the last thread out responsible for resetting the barrier's latch. + // The last thread also gets the special return value + // PTHREAD_BARRIER_SERIAL_THREAD. Every other thread gets a retval of 0 + if (barrier->out == barrier->count) { + retval = PTHREAD_BARRIER_SERIAL_THREAD; + // Reset the latch for the next round of waiters. We're using an + // external lock (mtx) to ensure that no other thread is calling + // count_down or in await when we're resetting it. Without the external + // lock, resetting the latch isn't safe. + l->unsafe_reset(barrier->count); + // Reset the 'out' counter so that the equality check above works across + // multiple rounds of threads waiting on the barrier + barrier->out = 0; + } + pthread_mutex_unlock(mtx); + return retval; +} + +int pthread_barrier_destroy(pthread_barrier_t *barrier_opq) +{ + pthread_barrier_t_int *barrier = (pthread_barrier_t_int*) barrier_opq; + + static_assert(sizeof(pthread_barrier_t_int) <= sizeof(pthread_barrier_t), + "pthread_barrier_t_int is larger than pthread_barrier_t"); + + if (!barrier || !barrier->ltch || !barrier->mtx) { + return EINVAL; + } + + delete barrier->ltch; + barrier->ltch = nullptr; + + pthread_mutex_destroy(barrier->mtx); + delete barrier->mtx; + barrier->mtx = nullptr; + + return 0; +} -- 1.8.3.1 -- You received this message because you are subscribed to the Google Groups "OSv Development" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
