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
);