MCS-PDR locks take a *qnode
[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   struct mcs_pdr_qnode *mcs_qnode;
16 };
17 TAILQ_HEAD(futex_queue, futex_element);
18
19 struct futex_data {
20   struct mcs_pdr_lock lock;
21   struct futex_queue queue;
22   struct kmem_cache *element_cache;
23 };
24 static struct futex_data __futex;
25
26 static inline void futex_init()
27 {
28   mcs_pdr_init(&__futex.lock);
29   TAILQ_INIT(&__futex.queue);
30   __futex.element_cache = kmem_cache_create("futex_element_cache", 
31     sizeof(struct futex_element), __alignof__(struct futex_element),
32     0, NULL, NULL);
33 }
34
35 static void __futex_block(struct uthread *uthread, void *arg) {
36   struct futex_element *e = (struct futex_element*)arg;
37   e->pthread = (pthread_t)uthread;
38         __pthread_generic_yield(e->pthread);
39   e->pthread->state = PTH_BLK_MUTEX;
40   TAILQ_INSERT_TAIL(&__futex.queue, e, link);
41   mcs_pdr_unlock(&__futex.lock, e->mcs_qnode);
42 }
43
44 static inline int futex_wait(int *uaddr, int val)
45 {
46   struct mcs_pdr_qnode qnode;
47   mcs_pdr_lock(&__futex.lock, &qnode);
48   if(*uaddr == val) {
49     // We unlock in the body of __futex_block
50     struct futex_element *e = kmem_cache_alloc(__futex.element_cache, 0); 
51     e->uaddr = uaddr;
52     e->mcs_qnode = &qnode;
53     uthread_yield(TRUE, __futex_block, e);
54   }
55   else {
56     mcs_pdr_unlock(&__futex.lock, &qnode);
57   }
58   return 0;
59 }
60
61 static inline int futex_wake(int *uaddr, int count)
62 {
63   struct futex_element *e,*n = NULL;
64   struct mcs_pdr_qnode qnode;
65   mcs_pdr_lock(&__futex.lock, &qnode);
66   e = TAILQ_FIRST(&__futex.queue);
67   while(e != NULL) {
68     if(count > 0) {
69       n = TAILQ_NEXT(e, link);
70       if(e->uaddr == uaddr) {
71         TAILQ_REMOVE(&__futex.queue, e, link);
72         uthread_runnable((struct uthread*)e->pthread);
73         kmem_cache_free(__futex.element_cache, e); 
74         count--;
75       }
76       e = n;
77     }
78     else break;
79   }
80   mcs_pdr_unlock(&__futex.lock, &qnode);
81   return 0;
82 }
83
84 int futex(int *uaddr, int op, int val, const struct timespec *timeout,
85                  int *uaddr2, int val3)
86 {
87   assert(timeout == NULL);
88   assert(uaddr2 == NULL);
89   assert(val3 == 0);
90
91   run_once(futex_init());
92   switch(op) {
93     case FUTEX_WAIT:
94       return futex_wait(uaddr, val);
95     case FUTEX_WAKE:
96       return futex_wake(uaddr, val);
97     default:
98       errno = ENOSYS;
99       return -1;
100   }
101   return -1;
102 }
103