Signed-off-by: rean <[email protected]>
---
include/api/x64/bits/alltypes.h.sh | 4 +-
include/osv/latch.hh | 7 +++
libc/pthread.cc | 80 +++++++++++++++++++++++++++++-
modules/tests/Makefile | 2 +-
tests/tst-pthread-barrier.c | 99 ++++++++++++++++++++++++++++++++++++++
5 files changed, 188 insertions(+), 4 deletions(-)
create mode 100644 tests/tst-pthread-barrier.c
diff --git a/include/api/x64/bits/alltypes.h.sh
b/include/api/x64/bits/alltypes.h.sh
index 5f19458..30d9f33 100755
--- a/include/api/x64/bits/alltypes.h.sh
+++ b/include/api/x64/bits/alltypes.h.sh
@@ -90,13 +90,13 @@ TYPEDEF int pthread_spinlock_t;
TYPEDEF struct { union { int __i[14]; size_t __s[7]; } __u; } pthread_attr_t;
TYPEDEF unsigned pthread_mutexattr_t;
TYPEDEF unsigned pthread_condattr_t;
-TYPEDEF unsigned pthread_barrierattr_t;
+TYPEDEF struct { unsigned pshared; } pthread_barrierattr_t;
TYPEDEF struct { unsigned __attr[2]; } pthread_rwlockattr_t;
TYPEDEF struct { union { int __i[10]; void *__p[5]; } __u; } pthread_mutex_t;
TYPEDEF struct { union { int __i[12]; void *__p[6]; } __u; } pthread_cond_t;
TYPEDEF struct { union { int __i[14]; void *__p[7]; } __u; } pthread_rwlock_t;
-TYPEDEF struct { union { int __i[8]; void *__p[4]; } __u; } pthread_barrier_t;
+TYPEDEF struct { unsigned int in; unsigned int out; unsigned int count; void
*latch; void *mtx; } pthread_barrier_t;
TYPEDEF long off_t;
TYPEDEF long __off_t;
diff --git a/include/osv/latch.hh b/include/osv/latch.hh
index 6ff78a8..09430ea 100644
--- a/include/osv/latch.hh
+++ b/include/osv/latch.hh
@@ -58,6 +58,13 @@ public:
std::unique_lock<std::mutex> l(_mutex);
return _condvar.wait_for(l, duration, [&] () -> bool { return
is_released(); });
}
+ // Useful if latches are being used as a primitive for implementing
+ // pthread_barrier_t so threads can wait on a barrier multiple times
+ // (over multiple rounds)
+ void reset(int count)
+ {
+ _count.fetch_add(count, std::memory_order_release);
+ }
};
class thread_barrier
diff --git a/libc/pthread.cc b/libc/pthread.cc
index 148b79e..251c669 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 {
@@ -1121,3 +1121,81 @@ int pthread_attr_getaffinity_np(const pthread_attr_t
*attr, size_t cpusetsize,
return 0;
}
+
+int pthread_barrier_init(pthread_barrier_t *barrier,
+ const pthread_barrierattr_t *attr,
+ unsigned count)
+{
+ if (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->in = 0;
+ barrier->out = 0;
+ barrier->latch = (void*) (new latch(count));
+ barrier->mtx = (void*) (new pthread_mutex_t);
+ pthread_mutex_init((pthread_mutex_t*) barrier->mtx, NULL);
+ return 0;
+}
+
+int pthread_barrier_wait(pthread_barrier_t *barrier)
+{
+ if (!barrier || !barrier->latch || !barrier->mtx) {
+ return EINVAL;
+ }
+
+ int retval = 0;
+ pthread_mutex_t *mtx = (pthread_mutex_t*) barrier->mtx;
+ // Critical section to increment the number of incoming threads/waiters
+ pthread_mutex_lock(mtx);
+ barrier->in++;
+ pthread_mutex_unlock(mtx);
+
+ latch *l = (latch*) barrier->latch;
+ 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 seems
+ // technically correct. Only one of the crossing threads will get a
+ // retval of PTHREAD_BARRIER_SERIAL_THREAD, when barrier->out % count == 0.
+ // 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 == 0) {
+ retval = PTHREAD_BARRIER_SERIAL_THREAD;
+ // Reset the latch for the next round of waiters
+ l->reset(barrier->count);
+ }
+ pthread_mutex_unlock(mtx);
+ return retval;
+}
+
+int pthread_barrier_destroy(pthread_barrier_t *barrier)
+{
+ if (!barrier || !barrier->latch || !barrier->mtx) {
+ return EINVAL;
+ }
+
+ delete ((latch*) barrier->latch);
+ barrier->latch = nullptr;
+
+ delete ((pthread_mutex_t*) barrier->mtx);
+ barrier->mtx = nullptr;
+
+ return 0;
+}
diff --git a/modules/tests/Makefile b/modules/tests/Makefile
index 3f9cb59..feeba12 100644
--- a/modules/tests/Makefile
+++ b/modules/tests/Makefile
@@ -85,7 +85,7 @@ tests := tst-pthread.so misc-ramdisk.so tst-vblk.so
tst-bsd-evh.so \
payload-merge-env.so misc-execve.so misc-execve-payload.so
misc-mutex2.so \
tst-pthread-setcancelstate.so tst-syscall.so tst-pin.so tst-run.so \
tst-ifaddrs.so tst-pthread-affinity-inherit.so tst-sem-timed-wait.so \
- tst-ttyname.so
+ tst-ttyname.so tst-pthread-barrier.so
# libstatic-thread-variable.so tst-static-thread-variable.so \
diff --git a/tests/tst-pthread-barrier.c b/tests/tst-pthread-barrier.c
new file mode 100644
index 0000000..1d22e19
--- /dev/null
+++ b/tests/tst-pthread-barrier.c
@@ -0,0 +1,99 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <pthread.h>
+#include <limits.h>
+#include <stdlib.h>
+
+unsigned int tests_total = 0, tests_failed = 0;
+
+void report(const char* name, bool passed)
+{
+ static const char* status[] = {"FAIL", "PASS"};
+ printf("%s: %s\n", status[passed], name);
+ tests_total += 1;
+ tests_failed += !passed;
+}
+
+// Opaque type 32 bytes in size
+static pthread_barrier_t barrier;
+// Opaque type 4 bytes in size
+pthread_barrierattr_t attr;
+// Number of crossings across the barrier
+static int numCrossings;
+
+static void* thread_func(void *arg)
+{
+ int threadNum = arg ? *((int*) arg): 0;
+ int retval = 0;
+ printf("[Thread %d] starting...\n", threadNum);
+
+ for (int crossing = 0; crossing < numCrossings; crossing++) {
+ // Force threads to sleep for a random interval so we randomize
+ // which thread might get the special return value
+ // PTHREAD_BARRIER_SERIAL_THREAD
+ int delay = random() % 7 + 1;
+ sleep(delay);
+
+ printf("[Thread %d] waiting on barrier\n", threadNum);
+ retval = pthread_barrier_wait(&barrier);
+ if (retval == PTHREAD_BARRIER_SERIAL_THREAD) {
+ printf("[Thread %d] crossed barrier with %d\n", threadNum,
+ PTHREAD_BARRIER_SERIAL_THREAD);
+ report("pthread_barrier_wait (special)",
+ retval == PTHREAD_BARRIER_SERIAL_THREAD);
+ } else if (retval == 0) {
+ printf("[Thread %d] crossed barrier with %d\n", threadNum, retval);
+ }
+ }
+ return 0;
+}
+
+int main(void)
+{
+ // Number of threads that must call into the barrier before they all unblock
+ const int numThreads = 10;
+ numCrossings = 4; // Pass through the barrier k times
+ pthread_t threads[numThreads];
+ int threadIds[numThreads];
+ int retval = -1;
+ printf("Sizeof pthread_barrier_t : %ld\n", sizeof(barrier));
+ report("sizeof pthread_barrier_t is 32 bytes\n", sizeof(barrier) == 32);
+ printf("Sizeof pthread_barrierattr_t: %ld\n", sizeof(attr));
+ report("sizeof pthread_barrierattr_t is 4 bytes\n", sizeof(attr) == 4);
+
+ // Try an invalid initialization (-1 or 0)
+ retval = pthread_barrier_init(&barrier, NULL, -1);
+ report("pthread_barrier_init (count == -1)", retval == EINVAL);
+ retval = pthread_barrier_init(&barrier, NULL, 0);
+ report("pthread_barrier_init (count == 0)", retval == EINVAL);
+ retval = pthread_barrier_init(&barrier, NULL, INT_MAX);
+ report("pthread_barrier_init (count == INT_MAX)", retval == EINVAL);
+
+ // Initalize a barrier with NULL attributes. In general
+ // it doesn't really matter what we do with pthread_barrierattr_t
+ // PTHREAD_PROCESS_PRIVATE vs PTHREAD_PROCESS_SHARED have the same effect
+ // in a unikernel - there's only a single process and all threads can
+ // manipulate the barrier so we can just ignore pthread_barrierattr_t
+ retval = pthread_barrier_init(&barrier, NULL, numThreads);
+ report("pthread_barrier_init", retval == 0);
+ if (retval != 0) {
+ printf("Early exit, pthread_barrier_init returned %d instead of 0\n",
+ retval);
+ goto exit;
+ }
+
+ for (int t = 0; t < numThreads; t++) {
+ threadIds[t] = t;
+ retval = pthread_create(&threads[t], NULL, thread_func, &threadIds[t]);
+ }
+ exit:
+ for (int t = 0; t < numThreads; t++) {
+ pthread_join(threads[t], NULL);
+ }
+ pthread_barrier_destroy(&barrier);
+ printf("SUMMARY: %u tests / %u failures\n", tests_total, tests_failed);
+ return tests_failed == 0 ? 0 : 1;
+}
--
2.7.4
--
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.