|  | #include <uthread.h> | 
|  | #include <semaphore.h> | 
|  | #include <mcs.h> | 
|  | #include <stdio.h> | 
|  | #include <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) | 
|  | { | 
|  | 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); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int sem_destroy (sem_t *__sem) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | sem_t *sem_open (__const char *__name, int __oflag, ...) | 
|  | { | 
|  | printf("sem_open is not yet implemented!"); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | int sem_close (sem_t *__sem) | 
|  | { | 
|  | printf("sem_close is not yet implemented!"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | 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) | 
|  | { | 
|  | 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); | 
|  | } | 
|  | 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; | 
|  | } | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int sem_getvalue (sem_t *__restrict __sem, int *__restrict __sval) | 
|  | { | 
|  | spin_pdr_lock(&__sem->lock); | 
|  | *__sval = __sem->count; | 
|  | spin_pdr_unlock(&__sem->lock); | 
|  | return 0; | 
|  | } | 
|  |  |