iplib: Make netmkaddr() threadsafe
[akaros.git] / user / pthread / semaphore.c
1 #include <parlib/uthread.h>
2 #include <semaphore.h>
3 #include <parlib/mcs.h>
4 #include <stdio.h>
5 #include <benchutil/alarm.h>
6 #include <errno.h>
7
8 int sem_init (sem_t *__sem, int __pshared, unsigned int __value)
9 {
10         if(__pshared == TRUE) {
11                 printf("__pshared functionality of sem_init is not yet implemented!");
12                 return -1;
13         }
14         __sem->count = __value;
15         TAILQ_INIT(&__sem->queue);
16         spin_pdr_init(&__sem->lock);
17         return 0;
18 }
19
20 int sem_destroy (sem_t *__sem)
21 {
22         return 0;
23 }
24
25 sem_t *sem_open (__const char *__name, int __oflag, ...)
26 {
27         printf("sem_open is not yet implemented!");
28         return NULL;
29 }
30
31 int sem_close (sem_t *__sem)
32 {
33         printf("sem_close is not yet implemented!");
34         return -1;
35 }
36
37 int sem_unlink (__const char *__name)
38 {
39         printf("sem_unlink is not yet implemented!");
40         return -1;
41 }
42
43 static void __sem_timeout(struct alarm_waiter *awaiter)
44 {
45         struct sem_queue_element *e = awaiter->data;
46         struct sem_queue_element *__e = NULL;
47
48         /* Try and yank out the thread. */
49         spin_pdr_lock(&e->sem->lock);
50         TAILQ_FOREACH(__e, &e->sem->queue, next)
51                 if (__e == e) break;
52         if (__e) {
53                 TAILQ_REMOVE(&e->sem->queue, e, next);
54                 e->timedout = true;
55         }
56         spin_pdr_unlock(&e->sem->lock);
57
58         /* If we were able to yank it out, wake it up. */
59         if (__e)
60                 uthread_runnable((struct uthread*)e->pthread);
61
62         /* Set this as the very last thing we do whether we
63          * successfully woke the thread blocked on the futex or not.
64          * Either we set this or post() sets this, not both.  Spin on
65          * this in the bottom-half of the wait() code to ensure there
66          * are no more references to awaiter before freeing the
67          * memory for it. */
68         e->awaiter.data = NULL;
69 }
70
71 static void __sem_block(struct uthread *uthread, void *arg)
72 {
73         struct sem_queue_element *e = (struct sem_queue_element *)arg;
74         pthread_t pthread = (pthread_t)uthread;
75         __pthread_generic_yield(pthread);
76         pthread->state = PTH_BLK_MUTEX;
77         TAILQ_INSERT_TAIL(&e->sem->queue, e, next);
78         spin_pdr_unlock(&e->sem->lock);
79 }
80
81 static void __sem_timedblock(struct uthread *uthread, void *arg)
82 {
83         struct sem_queue_element *e = (struct sem_queue_element *)arg;
84         e->awaiter.data = e;
85         init_awaiter(&e->awaiter, __sem_timeout);
86         set_awaiter_abs_unix(&e->awaiter, e->us_timeout);
87         set_alarm(&e->awaiter);
88         __sem_block(uthread, e);
89 }
90
91 int sem_wait (sem_t *__sem)
92 {
93         pthread_t pthread = (pthread_t)current_uthread;
94         struct sem_queue_element e = {{0}, __sem, pthread, -1, {0}, false};
95
96         spin_pdr_lock(&__sem->lock);
97         if(__sem->count > 0) {
98                 __sem->count--;
99                 spin_pdr_unlock(&__sem->lock);
100         }
101         else {
102                 /* We unlock in the body of __sem_block */
103                 uthread_yield(TRUE, __sem_block, &e);
104         }
105         return 0;
106 }
107
108 int sem_timedwait(sem_t *__sem, const struct timespec *abs_timeout)
109 {
110         int ret = 0;
111         uint64_t us = abs_timeout->tv_nsec/1000 + (abs_timeout->tv_sec)*1000000L;
112         pthread_t pthread = (pthread_t)current_uthread;
113         struct sem_queue_element e = {{0}, __sem, pthread, us, {0}, false};
114
115         spin_pdr_lock(&__sem->lock);
116         if(__sem->count > 0) {
117                 __sem->count--;
118                 spin_pdr_unlock(&__sem->lock);
119         }
120         else {
121                 /* We unlock in the body of __sem_block */
122                 uthread_yield(TRUE, __sem_timedblock, &e);
123
124                 /* Spin briefly to make sure that all references to e are
125                  * gone between the post() and the timeout() code. We use
126                  * e.awaiter.data to do this. */
127                 while (e.awaiter.data != NULL)
128                         cpu_relax();
129
130                 if (e.timedout) {
131                         errno = ETIMEDOUT;
132                         ret = -1;
133                 }
134         }
135         return ret;
136 }
137
138 int sem_trywait (sem_t *__sem)
139 {
140         int ret = -1;
141         spin_pdr_lock(&__sem->lock);
142         if(__sem->count > 0) {
143                 __sem->count--;
144                 ret = 0;
145         }
146         spin_pdr_unlock(&__sem->lock);
147         return ret;
148 }
149
150 int sem_post (sem_t *__sem)
151 {
152         spin_pdr_lock(&__sem->lock);
153         struct sem_queue_element *e = TAILQ_FIRST(&__sem->queue);
154         if (e)
155                 TAILQ_REMOVE(&__sem->queue, e, next);
156         else
157                 __sem->count++; 
158         spin_pdr_unlock(&__sem->lock);
159
160         if (e) {
161                 if(e->us_timeout != (uint64_t)-1) {
162                         /* Try and unset the alarm.  If this fails, then we
163                          * have already started running the alarm callback.  If
164                          * it succeeds, then we can set awaiter->data to NULL
165                          * so that the bottom half of wake can proceed. Either
166                          * we set awaiter->data to NULL or __sem_timeout
167                          * does. The fact that we made it here though, means
168                          * that WE are the one who removed e from the queue, so
169                          * we are basically just deciding who should set
170                          * awaiter->data to NULL to indicate that there are no
171                          * more references to it. */
172                         if(unset_alarm(&e->awaiter))
173                                 e->awaiter.data = NULL;
174                 }
175                 uthread_runnable((struct uthread*)e->pthread);
176         }
177         return 0;
178 }
179
180 int sem_getvalue (sem_t *__restrict __sem, int *__restrict __sval)
181 {
182         spin_pdr_lock(&__sem->lock);
183         *__sval = __sem->count;
184         spin_pdr_unlock(&__sem->lock);
185         return 0;
186 }
187