Preliminary work towards threads on win32
[sbcl.git] / src / runtime / pthread-futex.c
blobcddcda9ec9e866617133a7870f8c3e822c039776
1 /* An approximation of Linux futexes implemented using pthread mutexes
2 * and pthread condition variables.
3 */
5 /*
6 * This software is part of the SBCL system. See the README file for
7 * more information.
9 * The software is in the public domain and is provided with
10 * absolutely no warranty. See the COPYING and CREDITS files for more
11 * information.
14 #include "sbcl.h"
16 #if defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_SB_PTHREAD_FUTEX)
18 #include <errno.h>
19 #include <pthread.h>
20 #include <stdlib.h>
22 #include "runtime.h"
23 #include "arch.h"
24 #include "target-arch-os.h"
25 #include "os.h"
27 #define FUTEX_WAIT_NSEC (10000000) /* 10 msec */
29 #if 1
30 # define futex_assert(ex) \
31 do { \
32 if (!(ex)) futex_abort(); \
33 } while (0)
34 # define futex_assert_verbose(ex, fmt, ...) \
35 do { \
36 if (!(ex)) { \
37 fprintf(stderr, fmt, ## __VA_ARGS__); \
38 futex_abort(); \
39 } \
40 } while (0)
41 #else
42 # define futex_assert(ex)
43 # define futex_assert_verbose(ex, fmt, ...)
44 #endif
46 #define futex_abort() \
47 lose("Futex assertion failure, file \"%s\", line %d\n", __FILE__, __LINE__)
49 struct futex {
50 struct futex *prev;
51 struct futex *next;
52 int *lock_word;
53 pthread_mutex_t mutex;
54 pthread_cond_t cond;
55 int count;
58 static pthread_mutex_t futex_lock = PTHREAD_MUTEX_INITIALIZER;
60 static struct futex *futex_head = NULL;
61 static struct futex *futex_free_head = NULL;
63 static struct futex *
64 futex_add(struct futex *head, struct futex *futex)
66 futex->prev = NULL;
67 futex->next = head;
68 if (head != NULL)
69 head->prev = futex;
70 head = futex;
72 return head;
75 static struct futex *
76 futex_delete(struct futex *head, struct futex *futex)
78 if (head == futex)
79 head = futex->next;
80 if (futex->prev != NULL)
81 futex->prev->next = futex->next;
82 if (futex->next != NULL)
83 futex->next->prev = futex->prev;
85 return head;
88 static struct futex *
89 futex_find(struct futex *head, int *lock_word)
91 struct futex *futex;
93 for (futex = head; futex != NULL; futex = futex->next) {
94 if (futex->lock_word == lock_word)
95 break;
98 return futex;
101 static struct futex *
102 futex_get(int *lock_word)
104 int ret;
105 struct futex *futex;
107 ret = pthread_mutex_lock(&futex_lock);
108 futex_assert(ret == 0);
110 futex = futex_find(futex_head, lock_word);
112 if (futex != NULL)
113 futex->count++;
115 ret = pthread_mutex_unlock(&futex_lock);
116 futex_assert(ret == 0);
118 if (futex != NULL) {
119 ret = pthread_mutex_lock(&futex->mutex);
120 futex_assert(ret == 0);
123 return futex;
126 static struct futex *
127 futex_allocate(int *lock_word)
129 int ret;
130 struct futex *futex;
132 ret = pthread_mutex_lock(&futex_lock);
133 futex_assert(ret == 0);
135 futex = futex_free_head;
137 if (futex != NULL)
138 futex_free_head = futex_delete(futex_free_head, futex);
140 ret = pthread_mutex_unlock(&futex_lock);
141 futex_assert(ret == 0);
143 if (futex == NULL) {
144 futex = malloc(sizeof(struct futex));
145 futex_assert(futex != NULL);
147 ret = pthread_mutex_init(&futex->mutex, NULL);
148 futex_assert(ret == 0);
150 ret = pthread_cond_init(&futex->cond, NULL);
151 futex_assert(ret == 0);
154 futex->lock_word = lock_word;
155 futex->count = 1;
157 /* Lock mutex before register to avoid race conditions. */
158 ret = pthread_mutex_lock(&futex->mutex);
159 futex_assert(ret == 0);
161 ret = pthread_mutex_lock(&futex_lock);
162 futex_assert(ret == 0);
164 futex_head = futex_add(futex_head, futex);
166 ret = pthread_mutex_unlock(&futex_lock);
167 futex_assert(ret == 0);
169 return futex;
172 static void
173 futex_cleanup(void *p)
175 struct futex *futex = (struct futex *)p;
176 int ret, count;
178 ret = pthread_mutex_lock(&futex_lock);
179 futex_assert(ret == 0);
181 count = --futex->count;
182 if (count <= 0) {
183 futex_head = futex_delete(futex_head, futex);
184 futex_free_head = futex_add(futex_free_head, futex);
187 ret = pthread_mutex_unlock(&futex_lock);
188 futex_assert(ret == 0);
190 ret = pthread_mutex_unlock(&futex->mutex);
191 futex_assert(ret == 0);
194 static int
195 futex_relative_to_abs(struct timespec *tp, int relative)
197 int ret;
198 struct timeval tv;
200 ret = gettimeofday(&tv, NULL);
201 if (ret != 0)
202 return ret;
203 tp->tv_sec = tv.tv_sec + (tv.tv_usec * 1000 + relative) / 1000000000;
204 tp->tv_nsec = (tv.tv_usec * 1000 + relative) % 1000000000;
205 return 0;
208 static int
209 futex_istimeout(struct timeval *timeout)
211 int ret;
212 struct timeval tv;
214 if (timeout == NULL)
215 return 0;
217 ret = gettimeofday(&tv, NULL);
218 if (ret != 0)
219 return ret;
221 return (tv.tv_sec > timeout->tv_sec) ||
222 ((tv.tv_sec == timeout->tv_sec) && tv.tv_usec > timeout->tv_usec);
226 futex_wait(int *lock_word, int oldval, long sec, unsigned long usec)
228 int ret, result;
229 struct futex *futex;
230 sigset_t oldset;
231 struct timeval tv, *timeout;
233 again:
234 if (sec < 0)
235 timeout = NULL;
236 else {
237 ret = gettimeofday(&tv, NULL);
238 if (ret != 0)
239 return ret;
240 tv.tv_sec = tv.tv_sec + sec + (tv.tv_usec + usec) / 1000000;
241 tv.tv_usec = (tv.tv_usec + usec) % 1000000;
242 timeout = &tv;
245 block_deferrable_signals(0, &oldset);
247 futex = futex_get(lock_word);
249 if (futex == NULL)
250 futex = futex_allocate(lock_word);
252 pthread_cleanup_push(futex_cleanup, futex);
254 /* Compare lock_word after the lock is aquired to avoid race
255 * conditions. */
256 if (*(volatile int *)lock_word != oldval) {
257 result = EWOULDBLOCK;
258 goto done;
261 /* It's not possible to unwind frames across pthread_cond_wait(3). */
262 for (;;) {
263 int i;
264 sigset_t pendset;
265 struct timespec abstime;
267 ret = futex_relative_to_abs(&abstime, FUTEX_WAIT_NSEC);
268 futex_assert(ret == 0);
270 result = pthread_cond_timedwait(&futex->cond, &futex->mutex,
271 &abstime);
272 futex_assert(result == 0 || result == ETIMEDOUT);
274 if (result != ETIMEDOUT || futex_istimeout(timeout))
275 break;
277 /* futex system call of Linux returns with EINTR errno when
278 * it's interrupted by signals. Check pending signals here to
279 * emulate this behaviour. */
280 sigpending(&pendset);
281 for (i = 1; i < NSIG; i++) {
282 if (sigismember(&pendset, i) && sigismember(&newset, i)) {
283 result = EINTR;
284 goto done;
288 done:
289 ; /* Null statement is required between label and pthread_cleanup_pop. */
290 pthread_cleanup_pop(1);
291 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
293 /* futex_wake() in linux-os.c loops when futex system call returns
294 * EINTR. */
295 if (result == EINTR) {
296 sched_yield();
297 goto again;
300 if (result == ETIMEDOUT)
301 return 1;
303 return result;
307 futex_wake(int *lock_word, int n)
309 int ret;
310 struct futex *futex;
311 sigset_t oldset;
313 block_deferrable_signals(0, &oldset);
315 futex = futex_get(lock_word);
317 if (futex != NULL) {
318 pthread_cleanup_push(futex_cleanup, futex);
320 /* The lisp-side code passes N=2**29-1 for a broadcast. */
321 if (n >= ((1 << 29) - 1)) {
322 /* CONDITION-BROADCAST */
323 ret = pthread_cond_broadcast(&futex->cond);
324 futex_assert(ret == 0);
325 } else {
326 while (n-- > 0) {
327 ret = pthread_cond_signal(&futex->cond);
328 futex_assert(ret == 0);
332 pthread_cleanup_pop(1);
335 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
337 return 0;
339 #endif