1 /* An approximation of Linux futexes implemented using pthread mutexes
2 * and pthread condition variables.
6 * This software is part of the SBCL system. See the README file for
9 * The software is in the public domain and is provided with
10 * absolutely no warranty. See the COPYING and CREDITS files for more
16 #if defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_SB_PTHREAD_FUTEX)
24 #include "target-arch-os.h"
27 #define FUTEX_WAIT_NSEC (10000000) /* 10 msec */
30 # define futex_assert(ex) \
32 if (!(ex)) futex_abort(); \
34 # define futex_assert_verbose(ex, fmt, ...) \
37 fprintf(stderr, fmt, ## __VA_ARGS__); \
42 # define futex_assert(ex)
43 # define futex_assert_verbose(ex, fmt, ...)
46 #define futex_abort() \
47 lose("Futex assertion failure, file \"%s\", line %d\n", __FILE__, __LINE__)
53 pthread_mutex_t mutex;
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;
64 futex_add(struct futex *head, struct futex *futex)
76 futex_delete(struct futex *head, struct futex *futex)
80 if (futex->prev != NULL)
81 futex->prev->next = futex->next;
82 if (futex->next != NULL)
83 futex->next->prev = futex->prev;
89 futex_find(struct futex *head, int *lock_word)
93 for (futex = head; futex != NULL; futex = futex->next) {
94 if (futex->lock_word == lock_word)
101 static struct futex *
102 futex_get(int *lock_word)
107 ret = pthread_mutex_lock(&futex_lock);
108 futex_assert(ret == 0);
110 futex = futex_find(futex_head, lock_word);
115 ret = pthread_mutex_unlock(&futex_lock);
116 futex_assert(ret == 0);
119 ret = pthread_mutex_lock(&futex->mutex);
120 futex_assert(ret == 0);
126 static struct futex *
127 futex_allocate(int *lock_word)
132 ret = pthread_mutex_lock(&futex_lock);
133 futex_assert(ret == 0);
135 futex = futex_free_head;
138 futex_free_head = futex_delete(futex_free_head, futex);
140 ret = pthread_mutex_unlock(&futex_lock);
141 futex_assert(ret == 0);
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;
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);
173 futex_cleanup(void *p)
175 struct futex *futex = (struct futex *)p;
178 ret = pthread_mutex_lock(&futex_lock);
179 futex_assert(ret == 0);
181 count = --futex->count;
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);
195 futex_relative_to_abs(struct timespec *tp, int relative)
200 ret = gettimeofday(&tv, NULL);
203 tp->tv_sec = tv.tv_sec + (tv.tv_usec * 1000 + relative) / 1000000000;
204 tp->tv_nsec = (tv.tv_usec * 1000 + relative) % 1000000000;
209 futex_istimeout(struct timeval *timeout)
217 ret = gettimeofday(&tv, NULL);
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)
230 sigset_t oldset, newset;
231 struct timeval tv, *timeout;
233 sigemptyset(&newset);
234 sigaddset_deferrable(&newset);
240 ret = gettimeofday(&tv, NULL);
243 tv.tv_sec = tv.tv_sec + sec + (tv.tv_usec + usec) / 1000000;
244 tv.tv_usec = (tv.tv_usec + usec) % 1000000;
248 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
250 futex = futex_get(lock_word);
253 futex = futex_allocate(lock_word);
255 pthread_cleanup_push(futex_cleanup, futex);
257 /* Compare lock_word after the lock is aquired to avoid race
259 if (*(volatile int *)lock_word != oldval) {
260 result = EWOULDBLOCK;
264 /* It's not possible to unwind frames across pthread_cond_wait(3). */
268 struct timespec abstime;
270 ret = futex_relative_to_abs(&abstime, FUTEX_WAIT_NSEC);
271 futex_assert(ret == 0);
273 result = pthread_cond_timedwait(&futex->cond, &futex->mutex,
275 futex_assert(result == 0 || result == ETIMEDOUT);
277 if (result != ETIMEDOUT || futex_istimeout(timeout))
280 /* futex system call of Linux returns with EINTR errno when
281 * it's interrupted by signals. Check pending signals here to
282 * emulate this behaviour. */
283 sigpending(&pendset);
284 for (i = 1; i < NSIG; i++) {
285 if (sigismember(&pendset, i) && sigismember(&newset, i)) {
292 ; /* Null statement is required between label and pthread_cleanup_pop. */
293 pthread_cleanup_pop(1);
294 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
296 /* futex_wake() in linux-os.c loops when futex system call returns
298 if (result == EINTR) {
303 if (result == ETIMEDOUT)
310 futex_wake(int *lock_word, int n)
314 sigset_t newset, oldset;
316 sigemptyset(&newset);
317 sigaddset_deferrable(&newset);
319 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
321 futex = futex_get(lock_word);
324 pthread_cleanup_push(futex_cleanup, futex);
326 /* The lisp-side code passes N=2**29-1 for a broadcast. */
327 if (n >= ((1 << 29) - 1)) {
328 /* CONDITION-BROADCAST */
329 ret = pthread_cond_broadcast(&futex->cond);
330 futex_assert(ret == 0);
333 ret = pthread_cond_signal(&futex->cond);
334 futex_assert(ret == 0);
338 pthread_cleanup_pop(1);
341 pthread_sigmask(SIG_SETMASK, &oldset, NULL);