25
25
#include <time.h>
26
26
#include <sys/time.h>
27
27
#ifdef __linux__
28
+ #include <features.h>
28
29
#include <unistd.h>
30
+ #include <sys/syscall.h>
31
+ #include <linux/futex.h>
32
+ #include <limits.h>
29
33
#endif
30
34
31
35
typedef int st_retcode ;
@@ -109,6 +113,78 @@ Caml_inline void st_thread_set_id(intnat id)
109
113
return ;
110
114
}
111
115
116
+ /* If we're using glibc, use a custom condition variable implementation to
117
+ avoid this bug: https://sourceware.org/bugzilla/show_bug.cgi?id=25847
118
+
119
+ For now we only have this on linux because it directly uses the linux futex
120
+ syscalls. */
121
+ #if defined(__linux__ ) && defined(__GNU_LIBRARY__ ) && defined(__GLIBC__ ) && defined(__GLIBC_MINOR__ )
122
+ typedef struct {
123
+ volatile unsigned counter ;
124
+ } custom_condvar ;
125
+
126
+ static int custom_condvar_init (custom_condvar * cv )
127
+ {
128
+ cv -> counter = 0 ;
129
+ return 0 ;
130
+ }
131
+
132
+ static int custom_condvar_destroy (custom_condvar * cv )
133
+ {
134
+ return 0 ;
135
+ }
136
+
137
+ static int custom_condvar_wait (custom_condvar * cv , pthread_mutex_t * mutex )
138
+ {
139
+ unsigned old_count = cv -> counter ;
140
+ pthread_mutex_unlock (mutex );
141
+ syscall (SYS_futex , & cv -> counter , FUTEX_WAIT_PRIVATE , old_count , NULL , NULL , 0 );
142
+ pthread_mutex_lock (mutex );
143
+ return 0 ;
144
+ }
145
+
146
+ static int custom_condvar_signal (custom_condvar * cv )
147
+ {
148
+ __sync_add_and_fetch (& cv -> counter , 1 );
149
+ syscall (SYS_futex , & cv -> counter , FUTEX_WAKE_PRIVATE , 1 , NULL , NULL , 0 );
150
+ return 0 ;
151
+ }
152
+
153
+ static int custom_condvar_broadcast (custom_condvar * cv )
154
+ {
155
+ __sync_add_and_fetch (& cv -> counter , 1 );
156
+ syscall (SYS_futex , & cv -> counter , FUTEX_WAKE_PRIVATE , INT_MAX , NULL , NULL , 0 );
157
+ return 0 ;
158
+ }
159
+ #else
160
+ typedef pthread_cond_t custom_condvar ;
161
+
162
+ static int custom_condvar_init (custom_condvar * cv )
163
+ {
164
+ return pthread_cond_init (cv , NULL );
165
+ }
166
+
167
+ static int custom_condvar_destroy (custom_condvar * cv )
168
+ {
169
+ return pthread_cond_destroy (cv );
170
+ }
171
+
172
+ static int custom_condvar_wait (custom_condvar * cv , pthread_mutex_t * mutex )
173
+ {
174
+ return pthread_cond_wait (cv , mutex );
175
+ }
176
+
177
+ static int custom_condvar_signal (custom_condvar * cv )
178
+ {
179
+ return pthread_cond_signal (cv );
180
+ }
181
+
182
+ static int custom_condvar_broadcast (custom_condvar * cv )
183
+ {
184
+ return pthread_cond_broadcast (cv );
185
+ }
186
+ #endif
187
+
112
188
/* The master lock. This is a mutex that is held most of the time,
113
189
so we implement it in a slightly convoluted way to avoid
114
190
all risks of busy-waiting. Also, we count the number of waiting
@@ -118,13 +194,13 @@ typedef struct {
118
194
pthread_mutex_t lock ; /* to protect contents */
119
195
int busy ; /* 0 = free, 1 = taken */
120
196
volatile int waiters ; /* number of threads waiting on master lock */
121
- pthread_cond_t is_free ; /* signaled when free */
197
+ custom_condvar is_free ; /* signaled when free */
122
198
} st_masterlock ;
123
199
124
200
static void st_masterlock_init (st_masterlock * m )
125
201
{
126
202
pthread_mutex_init (& m -> lock , NULL );
127
- pthread_cond_init (& m -> is_free , NULL );
203
+ custom_condvar_init (& m -> is_free );
128
204
m -> busy = 1 ;
129
205
m -> waiters = 0 ;
130
206
}
@@ -134,7 +210,7 @@ static void st_masterlock_acquire(st_masterlock * m)
134
210
pthread_mutex_lock (& m -> lock );
135
211
while (m -> busy ) {
136
212
m -> waiters ++ ;
137
- pthread_cond_wait (& m -> is_free , & m -> lock );
213
+ custom_condvar_wait (& m -> is_free , & m -> lock );
138
214
m -> waiters -- ;
139
215
}
140
216
m -> busy = 1 ;
@@ -146,7 +222,7 @@ static void st_masterlock_release(st_masterlock * m)
146
222
pthread_mutex_lock (& m -> lock );
147
223
m -> busy = 0 ;
148
224
pthread_mutex_unlock (& m -> lock );
149
- pthread_cond_signal (& m -> is_free );
225
+ custom_condvar_signal (& m -> is_free );
150
226
}
151
227
152
228
CAMLno_tsan /* This can be called for reading [waiters] without locking. */
@@ -179,14 +255,14 @@ Caml_inline void st_thread_yield(st_masterlock * m)
179
255
}
180
256
181
257
m -> busy = 0 ;
182
- pthread_cond_signal (& m -> is_free );
258
+ custom_condvar_signal (& m -> is_free );
183
259
m -> waiters ++ ;
184
260
do {
185
261
/* Note: the POSIX spec prevents the above signal from pairing with this
186
262
wait, which is good: we'll reliably continue waiting until the next
187
263
yield() or enter_blocking_section() call (or we see a spurious condvar
188
264
wakeup, which are rare at best.) */
189
- pthread_cond_wait (& m -> is_free , & m -> lock );
265
+ custom_condvar_wait (& m -> is_free , & m -> lock );
190
266
} while (m -> busy );
191
267
m -> busy = 1 ;
192
268
m -> waiters -- ;
@@ -254,14 +330,14 @@ Caml_inline int st_mutex_unlock(st_mutex m)
254
330
255
331
/* Condition variables */
256
332
257
- typedef pthread_cond_t * st_condvar ;
333
+ typedef custom_condvar * st_condvar ;
258
334
259
335
static int st_condvar_create (st_condvar * res )
260
336
{
261
337
int rc ;
262
- st_condvar c = caml_stat_alloc_noexc (sizeof (pthread_cond_t ));
338
+ st_condvar c = caml_stat_alloc_noexc (sizeof (custom_condvar ));
263
339
if (c == NULL ) return ENOMEM ;
264
- rc = pthread_cond_init ( c , NULL );
340
+ rc = custom_condvar_init ( c );
265
341
if (rc != 0 ) { caml_stat_free (c ); return rc ; }
266
342
* res = c ;
267
343
return 0 ;
@@ -270,32 +346,32 @@ static int st_condvar_create(st_condvar * res)
270
346
static int st_condvar_destroy (st_condvar c )
271
347
{
272
348
int rc ;
273
- rc = pthread_cond_destroy (c );
349
+ rc = custom_condvar_destroy (c );
274
350
caml_stat_free (c );
275
351
return rc ;
276
352
}
277
353
278
354
Caml_inline int st_condvar_signal (st_condvar c )
279
355
{
280
- return pthread_cond_signal (c );
356
+ return custom_condvar_signal (c );
281
357
}
282
358
283
359
Caml_inline int st_condvar_broadcast (st_condvar c )
284
360
{
285
- return pthread_cond_broadcast (c );
361
+ return custom_condvar_broadcast (c );
286
362
}
287
363
288
364
Caml_inline int st_condvar_wait (st_condvar c , st_mutex m )
289
365
{
290
- return pthread_cond_wait (c , m );
366
+ return custom_condvar_wait (c , m );
291
367
}
292
368
293
369
/* Triggered events */
294
370
295
371
typedef struct st_event_struct {
296
372
pthread_mutex_t lock ; /* to protect contents */
297
373
int status ; /* 0 = not triggered, 1 = triggered */
298
- pthread_cond_t triggered ; /* signaled when triggered */
374
+ custom_condvar triggered ; /* signaled when triggered */
299
375
} * st_event ;
300
376
301
377
static int st_event_create (st_event * res )
@@ -305,7 +381,7 @@ static int st_event_create(st_event * res)
305
381
if (e == NULL ) return ENOMEM ;
306
382
rc = pthread_mutex_init (& e -> lock , NULL );
307
383
if (rc != 0 ) { caml_stat_free (e ); return rc ; }
308
- rc = pthread_cond_init (& e -> triggered , NULL );
384
+ rc = custom_condvar_init (& e -> triggered );
309
385
if (rc != 0 )
310
386
{ pthread_mutex_destroy (& e -> lock ); caml_stat_free (e ); return rc ; }
311
387
e -> status = 0 ;
@@ -317,7 +393,7 @@ static int st_event_destroy(st_event e)
317
393
{
318
394
int rc1 , rc2 ;
319
395
rc1 = pthread_mutex_destroy (& e -> lock );
320
- rc2 = pthread_cond_destroy (& e -> triggered );
396
+ rc2 = custom_condvar_destroy (& e -> triggered );
321
397
caml_stat_free (e );
322
398
return rc1 != 0 ? rc1 : rc2 ;
323
399
}
@@ -330,7 +406,7 @@ static int st_event_trigger(st_event e)
330
406
e -> status = 1 ;
331
407
rc = pthread_mutex_unlock (& e -> lock );
332
408
if (rc != 0 ) return rc ;
333
- rc = pthread_cond_broadcast (& e -> triggered );
409
+ rc = custom_condvar_broadcast (& e -> triggered );
334
410
return rc ;
335
411
}
336
412
@@ -340,7 +416,7 @@ static int st_event_wait(st_event e)
340
416
rc = pthread_mutex_lock (& e -> lock );
341
417
if (rc != 0 ) return rc ;
342
418
while (e -> status == 0 ) {
343
- rc = pthread_cond_wait (& e -> triggered , & e -> lock );
419
+ rc = custom_condvar_wait (& e -> triggered , & e -> lock );
344
420
if (rc != 0 ) return rc ;
345
421
}
346
422
rc = pthread_mutex_unlock (& e -> lock );
0 commit comments