Moves futex.h to user/pthread (XCC)
[akaros.git] / user / pthread / futex.c
1 #include <ros/common.h>
2 #include <futex.h>
3 #include <sys/queue.h>
4 #include <pthread.h>
5 #include <assert.h>
6 #include <stdio.h>
7 #include <errno.h>
8 #include <slab.h>
9 #include <mcs.h>
10
11 struct futex_element {
12   TAILQ_ENTRY(futex_element) link;
13   pthread_t pthread;
14   int *uaddr;
15 };
16 TAILQ_HEAD(futex_queue, futex_element);
17
18 struct futex_data {
19   struct mcs_pdr_lock lock;
20   struct futex_queue queue;
21   struct kmem_cache *element_cache;
22 };
23 static struct futex_data __futex;
24
25 static inline void futex_init()
26 {
27   mcs_pdr_init(&__futex.lock);
28   TAILQ_INIT(&__futex.queue);
29   __futex.element_cache = kmem_cache_create("futex_element_cache", 
30     sizeof(struct futex_element), __alignof__(struct futex_element),
31     0, NULL, NULL);
32 }
33
34 static void __futex_block(struct uthread *uthread, void *arg) {
35   struct futex_element *e = (struct futex_element*)arg;
36   e->pthread = (pthread_t)uthread;
37   e->pthread->state = PTH_BLK_MUTEX;
38   TAILQ_INSERT_TAIL(&__futex.queue, e, link);
39   mcs_pdr_unlock(&__futex.lock);
40 }
41
42 static inline int futex_wait(int *uaddr, int val)
43 {
44   mcs_pdr_lock(&__futex.lock);
45   if(*uaddr == val) {
46     // We unlock in the body of __futex_block
47     struct futex_element *e = kmem_cache_alloc(__futex.element_cache, 0); 
48     e->uaddr = uaddr;
49     uthread_yield(TRUE, __futex_block, e);
50   }
51   else {
52     mcs_pdr_unlock(&__futex.lock);
53   }
54   return 0;
55 }
56
57 static inline int futex_wake(int *uaddr, int count)
58 {
59   struct futex_element *e,*n = NULL;
60   mcs_pdr_lock(&__futex.lock);
61   e = TAILQ_FIRST(&__futex.queue);
62   while(e != NULL) {
63     if(count > 0) {
64       n = TAILQ_NEXT(e, link);
65       if(e->uaddr == uaddr) {
66         TAILQ_REMOVE(&__futex.queue, e, link);
67         uthread_runnable((struct uthread*)e->pthread);
68         kmem_cache_free(__futex.element_cache, e); 
69         count--;
70       }
71       e = n;
72     }
73     else break;
74   }
75   mcs_pdr_unlock(&__futex.lock);
76   return 0;
77 }
78
79 int futex(int *uaddr, int op, int val, const struct timespec *timeout,
80                  int *uaddr2, int val3)
81 {
82   assert(timeout == NULL);
83   assert(uaddr2 == NULL);
84   assert(val3 == 0);
85
86   run_once(futex_init());
87   switch(op) {
88     case FUTEX_WAIT:
89       return futex_wait(uaddr, val);
90     case FUTEX_WAKE:
91       return futex_wake(uaddr, val);
92     default:
93       errno = ENOSYS;
94       return -1;
95   }
96   return -1;
97 }
98