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_wait(int *lock_word, int oldval, long sec, unsigned long usec)
213 sigset_t oldset, newset;
215 sigemptyset(&newset);
216 sigaddset_deferrable(&newset);
219 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
221 futex = futex_get(lock_word);
224 futex = futex_allocate(lock_word);
226 pthread_cleanup_push(futex_cleanup, futex);
228 /* Compare lock_word after the lock is aquired to avoid race
230 if (*(volatile int *)lock_word != oldval) {
231 result = EWOULDBLOCK;
235 /* It's not possible to unwind frames across pthread_cond_wait(3). */
239 struct timespec abstime;
241 ret = futex_relative_to_abs(&abstime, FUTEX_WAIT_NSEC);
242 futex_assert(ret == 0);
244 result = pthread_cond_timedwait(&futex->cond, &futex->mutex,
246 futex_assert(result == 0 || result == ETIMEDOUT);
248 if (result != ETIMEDOUT)
251 /* futex system call of Linux returns with EINTR errno when
252 * it's interrupted by signals. Check pending signals here to
253 * emulate this behaviour. */
254 sigpending(&pendset);
255 for (i = 1; i < NSIG; i++) {
256 if (sigismember(&pendset, i) && sigismember(&newset, i)) {
263 ; /* Null statement is required between label and pthread_cleanup_pop. */
264 pthread_cleanup_pop(1);
265 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
267 /* futex_wake() in linux-os.c loops when futex system call returns
269 if (result == EINTR) {
278 futex_wake(int *lock_word, int n)
282 sigset_t newset, oldset;
284 sigemptyset(&newset);
285 sigaddset_deferrable(&newset);
287 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
289 futex = futex_get(lock_word);
292 pthread_cleanup_push(futex_cleanup, futex);
294 /* The lisp-side code passes N=2**29-1 for a broadcast. */
295 if (n >= ((1 << 29) - 1)) {
296 /* CONDITION-BROADCAST */
297 ret = pthread_cond_broadcast(&futex->cond);
298 futex_assert(ret == 0);
301 ret = pthread_cond_signal(&futex->cond);
302 futex_assert(ret == 0);
306 pthread_cleanup_pop(1);
309 pthread_sigmask(SIG_SETMASK, &oldset, NULL);