pthread: Implement POSIX semaphores with uth sems (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 7 Apr 2017 17:47:36 +0000 (13:47 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 3 May 2017 16:13:02 +0000 (12:13 -0400)
We had these as part of pthreads, but now the POSIX sem interfaces work for
any 2LS.  We'll keep it in pthread.c for now, since it's basically the
POSIX interface to threads and synchronization.  Eventually everyone will
be able to use pthread-like functions (including these sems) regardless of
their 2LS.

Fully rebuild the toolchain, including reconfiguring.  (make clean will do
the trick).  The issue is libgomp determines the size of omp_locks during
config time.  Any changes to the size of a semaphore (which is a uthread
sem) triggers the need for a reconfig.  If you ever have builds dying
during libgomp (late in the toolchain build), this is probably why.  Scroll
back a dozen pages and you'll see the error.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
user/pthread/semaphore.c
user/pthread/semaphore.h

index 61a8e22..96ee4cf 100644 (file)
 #include <parlib/uthread.h>
+#include <parlib/assert.h>
 #include <semaphore.h>
-#include <parlib/mcs.h>
 #include <stdio.h>
-#include <parlib/alarm.h>
 #include <errno.h>
 
-int sem_init (sem_t *__sem, int __pshared, unsigned int __value)
+int sem_init(sem_t *__sem, int __pshared, unsigned int __value)
 {
-       if(__pshared == TRUE) {
+       if (__pshared == TRUE) {
                printf("__pshared functionality of sem_init is not yet implemented!");
                return -1;
        }
-       __sem->count = __value;
-       TAILQ_INIT(&__sem->queue);
-       spin_pdr_init(&__sem->lock);
+       uth_semaphore_init(&__sem->real_sem, __value);
        return 0;
 }
 
-int sem_destroy (sem_t *__sem)
+int sem_destroy(sem_t *__sem)
 {
+       uth_semaphore_destroy(&__sem->real_sem);
        return 0;
 }
 
-sem_t *sem_open (__const char *__name, int __oflag, ...)
+sem_t *sem_open(__const char *__name, int __oflag, ...)
 {
        printf("sem_open is not yet implemented!");
        return NULL;
 }
 
-int sem_close (sem_t *__sem)
+int sem_close(sem_t *__sem)
 {
        printf("sem_close is not yet implemented!");
        return -1;
 }
 
-int sem_unlink (__const char *__name)
+int sem_unlink(__const char *__name)
 {
        printf("sem_unlink is not yet implemented!");
        return -1;
 }
 
-static void __sem_timeout(struct alarm_waiter *awaiter)
+int sem_wait(sem_t *__sem)
 {
-       struct sem_queue_element *e = awaiter->data;
-       struct sem_queue_element *__e = NULL;
-
-       /* Try and yank out the thread. */
-       spin_pdr_lock(&e->sem->lock);
-       TAILQ_FOREACH(__e, &e->sem->queue, next)
-               if (__e == e) break;
-       if (__e) {
-               TAILQ_REMOVE(&e->sem->queue, e, next);
-               e->timedout = true;
-       }
-       spin_pdr_unlock(&e->sem->lock);
-
-       /* If we were able to yank it out, wake it up. */
-       if (__e)
-               uthread_runnable((struct uthread*)e->pthread);
-
-       /* Set this as the very last thing we do whether we
-        * successfully woke the thread blocked on the futex or not.
-        * Either we set this or post() sets this, not both.  Spin on
-        * this in the bottom-half of the wait() code to ensure there
-        * are no more references to awaiter before freeing the
-        * memory for it. */
-       e->awaiter.data = NULL;
-}
-
-static void __sem_block(struct uthread *uthread, void *arg)
-{
-       struct sem_queue_element *e = (struct sem_queue_element *)arg;
-       pthread_t pthread = (pthread_t)uthread;
-       __pthread_generic_yield(pthread);
-       pthread->state = PTH_BLK_MUTEX;
-       TAILQ_INSERT_TAIL(&e->sem->queue, e, next);
-       spin_pdr_unlock(&e->sem->lock);
-}
-
-static void __sem_timedblock(struct uthread *uthread, void *arg)
-{
-       struct sem_queue_element *e = (struct sem_queue_element *)arg;
-       e->awaiter.data = e;
-       init_awaiter(&e->awaiter, __sem_timeout);
-       set_awaiter_abs_unix(&e->awaiter, e->us_timeout);
-       set_alarm(&e->awaiter);
-       __sem_block(uthread, e);
-}
-
-int sem_wait (sem_t *__sem)
-{
-       pthread_t pthread = (pthread_t)current_uthread;
-       struct sem_queue_element e = {{0}, __sem, pthread, -1, {0}, false};
-
-       spin_pdr_lock(&__sem->lock);
-       if(__sem->count > 0) {
-               __sem->count--;
-               spin_pdr_unlock(&__sem->lock);
-       }
-       else {
-               /* We unlock in the body of __sem_block */
-               uthread_yield(TRUE, __sem_block, &e);
-       }
+       uth_semaphore_down(&__sem->real_sem);
        return 0;
 }
 
 int sem_timedwait(sem_t *__sem, const struct timespec *abs_timeout)
 {
-       int ret = 0;
-       uint64_t us = abs_timeout->tv_nsec/1000 + (abs_timeout->tv_sec)*1000000L;
-       pthread_t pthread = (pthread_t)current_uthread;
-       struct sem_queue_element e = {{0}, __sem, pthread, us, {0}, false};
-
-       spin_pdr_lock(&__sem->lock);
-       if(__sem->count > 0) {
-               __sem->count--;
-               spin_pdr_unlock(&__sem->lock);
-       }
-       else {
-               /* We unlock in the body of __sem_block */
-               uthread_yield(TRUE, __sem_timedblock, &e);
-
-               /* Spin briefly to make sure that all references to e are
-                * gone between the post() and the timeout() code. We use
-                * e.awaiter.data to do this. */
-               while (e.awaiter.data != NULL)
-                       cpu_relax();
-
-               if (e.timedout) {
-                       errno = ETIMEDOUT;
-                       ret = -1;
-               }
+       if (!uth_semaphore_timed_down(&__sem->real_sem, abs_timeout)) {
+               errno = ETIMEDOUT;
+               return -1;
        }
-       return ret;
+       return 0;
 }
 
-int sem_trywait (sem_t *__sem)
+int sem_trywait(sem_t *__sem)
 {
-       int ret = -1;
-       spin_pdr_lock(&__sem->lock);
-       if(__sem->count > 0) {
-               __sem->count--;
-               ret = 0;
-       }
-       spin_pdr_unlock(&__sem->lock);
-       return ret;
+       if (!uth_semaphore_trydown(&__sem->real_sem))
+               return -1;
+       return 0;
 }
 
-int sem_post (sem_t *__sem)
+int sem_post(sem_t *__sem)
 {
-       spin_pdr_lock(&__sem->lock);
-       struct sem_queue_element *e = TAILQ_FIRST(&__sem->queue);
-       if (e)
-               TAILQ_REMOVE(&__sem->queue, e, next);
-       else
-               __sem->count++; 
-       spin_pdr_unlock(&__sem->lock);
-
-       if (e) {
-               if(e->us_timeout != (uint64_t)-1) {
-                       /* Try and unset the alarm.  If this fails, then we
-                        * have already started running the alarm callback.  If
-                        * it succeeds, then we can set awaiter->data to NULL
-                        * so that the bottom half of wake can proceed. Either
-                        * we set awaiter->data to NULL or __sem_timeout
-                        * does. The fact that we made it here though, means
-                        * that WE are the one who removed e from the queue, so
-                        * we are basically just deciding who should set
-                        * awaiter->data to NULL to indicate that there are no
-                        * more references to it. */
-                       if(unset_alarm(&e->awaiter))
-                               e->awaiter.data = NULL;
-               }
-               uthread_runnable((struct uthread*)e->pthread);
-       }
+       uth_semaphore_up(&__sem->real_sem);
        return 0;
 }
 
-int sem_getvalue (sem_t *__restrict __sem, int *__restrict __sval)
+int sem_getvalue(sem_t *__restrict __sem, int *__restrict __sval)
 {
-       spin_pdr_lock(&__sem->lock);
-       *__sval = __sem->count;
-       spin_pdr_unlock(&__sem->lock);
+       spin_pdr_lock(&__sem->real_sem.lock);
+       *__sval = __sem->real_sem.count;
+       spin_pdr_unlock(&__sem->real_sem.lock);
        return 0;
 }
-
index b364ad1..6ac6177 100644 (file)
 
 #pragma once
 
-#include <sys/queue.h>
-#include <pthread.h>
-#include <parlib/mcs.h>
-#include <parlib/alarm.h>
+#include <parlib/uthread.h>
 
 __BEGIN_DECLS
 
 /* Value returned if `sem_open' failed.  */
 #define SEM_FAILED      ((sem_t *) 0)
 
-struct sem_queue_element {
-       TAILQ_ENTRY(sem_queue_element) next;
-       struct sem *sem;
-       pthread_t pthread;
-       uint64_t us_timeout;
-       struct alarm_waiter awaiter;
-       bool timedout;
-};
-TAILQ_HEAD(sem_qe_queue, sem_queue_element);
-
-typedef struct sem
-{
-       unsigned int count;
-       struct sem_qe_queue queue;
-       struct spin_pdr_lock lock;
+typedef struct {
+       uth_semaphore_t                         real_sem;
 } sem_t;
 
-extern int sem_init (sem_t *__sem, int __pshared, unsigned int __value);
-extern int sem_destroy (sem_t *__sem);
-extern sem_t *sem_open (__const char *__name, int __oflag, ...);
-extern int sem_close (sem_t *__sem);
-extern int sem_unlink (__const char *__name);
-extern int sem_wait (sem_t *__sem);
-extern int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
-extern int sem_trywait (sem_t *__sem);
-extern int sem_post (sem_t *__sem);
-extern int sem_getvalue (sem_t *__restrict __sem, int *__restrict __sval);
+int sem_init(sem_t *__sem, int __pshared, unsigned int __value);
+int sem_destroy(sem_t *__sem);
+sem_t *sem_open(__const char *__name, int __oflag, ...);
+int sem_close(sem_t *__sem);
+int sem_unlink(__const char *__name);
+int sem_wait(sem_t *__sem);
+int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
+int sem_trywait(sem_t *__sem);
+int sem_post(sem_t *__sem);
+int sem_getvalue(sem_t *__restrict __sem, int *__restrict __sval);
 
 __END_DECLS