Handle threading after a fork() (XCC)
[akaros.git] / user / pthread / semaphore.c
index ad238c2..96ee4cf 100644 (file)
 #include <parlib/uthread.h>
+#include <parlib/assert.h>
 #include <semaphore.h>
-#include <parlib/mcs.h>
 #include <stdio.h>
-#include <benchutil/alarm.h>
 #include <errno.h>
 
-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;
-};
-
-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)
-{
-       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)
+int sem_wait(sem_t *__sem)
 {
-       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(&e->awaiter, e->us_timeout);
-       set_alarm(&e->awaiter);
-       __sem_block(uthread, e->sem);
-}
-
-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, __sem);
-       }
+       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;
 }
-