Skip to content

Commit fa7e3e2

Browse files
authored
flambda-backend: Port "condvar" bugfix to runtime 5 (#2076)
1 parent c69bd92 commit fa7e3e2

File tree

5 files changed

+123
-31
lines changed

5 files changed

+123
-31
lines changed

otherlibs/systhreads/st_pthreads.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,15 @@ typedef struct {
100100
pthread_mutex_t lock; /* to protect contents */
101101
uintnat busy; /* 0 = free, 1 = taken */
102102
atomic_uintnat waiters; /* number of threads waiting on master lock */
103-
pthread_cond_t is_free; /* signaled when free */
103+
custom_condvar is_free; /* signaled when free */
104104
} st_masterlock;
105105

106106
static void st_masterlock_init(st_masterlock * m)
107107
{
108108
if (!m->init) {
109109
// FIXME: check errors
110110
pthread_mutex_init(&m->lock, NULL);
111-
pthread_cond_init(&m->is_free, NULL);
111+
custom_condvar_init(&m->is_free);
112112
m->init = 1;
113113
}
114114
m->busy = 1;
@@ -156,7 +156,7 @@ static void st_masterlock_acquire(st_masterlock *m)
156156
pthread_mutex_lock(&m->lock);
157157
while (m->busy) {
158158
atomic_fetch_add(&m->waiters, +1);
159-
pthread_cond_wait(&m->is_free, &m->lock);
159+
custom_condvar_wait(&m->is_free, &m->lock);
160160
atomic_fetch_add(&m->waiters, -1);
161161
}
162162
m->busy = 1;
@@ -171,7 +171,7 @@ static void st_masterlock_release(st_masterlock * m)
171171
pthread_mutex_lock(&m->lock);
172172
m->busy = 0;
173173
st_bt_lock_release(m);
174-
pthread_cond_signal(&m->is_free);
174+
custom_condvar_signal(&m->is_free);
175175
pthread_mutex_unlock(&m->lock);
176176

177177
return;
@@ -203,7 +203,7 @@ Caml_inline void st_thread_yield(st_masterlock * m)
203203

204204
m->busy = 0;
205205
atomic_fetch_add(&m->waiters, +1);
206-
pthread_cond_signal(&m->is_free);
206+
custom_condvar_signal(&m->is_free);
207207
/* releasing the domain lock but not triggering bt messaging
208208
messaging the bt should not be required because yield assumes
209209
that a thread will resume execution (be it the yielding thread
@@ -215,7 +215,7 @@ Caml_inline void st_thread_yield(st_masterlock * m)
215215
wait, which is good: we'll reliably continue waiting until the next
216216
yield() or enter_blocking_section() call (or we see a spurious condvar
217217
wakeup, which are rare at best.) */
218-
pthread_cond_wait(&m->is_free, &m->lock);
218+
custom_condvar_wait(&m->is_free, &m->lock);
219219
} while (m->busy);
220220

221221
m->busy = 1;
@@ -233,7 +233,7 @@ Caml_inline void st_thread_yield(st_masterlock * m)
233233
typedef struct st_event_struct {
234234
pthread_mutex_t lock; /* to protect contents */
235235
int status; /* 0 = not triggered, 1 = triggered */
236-
pthread_cond_t triggered; /* signaled when triggered */
236+
custom_condvar triggered; /* signaled when triggered */
237237
} * st_event;
238238

239239

@@ -244,7 +244,7 @@ static int st_event_create(st_event * res)
244244
if (e == NULL) return ENOMEM;
245245
rc = pthread_mutex_init(&e->lock, NULL);
246246
if (rc != 0) { caml_stat_free(e); return rc; }
247-
rc = pthread_cond_init(&e->triggered, NULL);
247+
rc = custom_condvar_init(&e->triggered);
248248
if (rc != 0)
249249
{ pthread_mutex_destroy(&e->lock); caml_stat_free(e); return rc; }
250250
e->status = 0;
@@ -256,7 +256,7 @@ static int st_event_destroy(st_event e)
256256
{
257257
int rc1, rc2;
258258
rc1 = pthread_mutex_destroy(&e->lock);
259-
rc2 = pthread_cond_destroy(&e->triggered);
259+
rc2 = custom_condvar_destroy(&e->triggered);
260260
caml_stat_free(e);
261261
return rc1 != 0 ? rc1 : rc2;
262262
}
@@ -269,7 +269,7 @@ static int st_event_trigger(st_event e)
269269
e->status = 1;
270270
rc = pthread_mutex_unlock(&e->lock);
271271
if (rc != 0) return rc;
272-
rc = pthread_cond_broadcast(&e->triggered);
272+
rc = custom_condvar_broadcast(&e->triggered);
273273
return rc;
274274
}
275275

@@ -279,7 +279,7 @@ static int st_event_wait(st_event e)
279279
rc = pthread_mutex_lock(&e->lock);
280280
if (rc != 0) return rc;
281281
while(e->status == 0) {
282-
rc = pthread_cond_wait(&e->triggered, &e->lock);
282+
rc = custom_condvar_wait(&e->triggered, &e->lock);
283283
if (rc != 0) return rc;
284284
}
285285
rc = pthread_mutex_unlock(&e->lock);

runtime/caml/platform.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <string.h>
2626
#include "config.h"
2727
#include "mlvalues.h"
28+
#include "sync.h"
2829
#include "sys.h"
2930

3031
#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
@@ -106,7 +107,7 @@ void caml_plat_assert_locked(caml_plat_mutex*);
106107
void caml_plat_assert_all_locks_unlocked(void);
107108
Caml_inline void caml_plat_unlock(caml_plat_mutex*);
108109
void caml_plat_mutex_free(caml_plat_mutex*);
109-
typedef struct { pthread_cond_t cond; caml_plat_mutex* mutex; } caml_plat_cond;
110+
typedef struct { custom_condvar cond; caml_plat_mutex* mutex; } caml_plat_cond;
110111
#define CAML_PLAT_COND_INITIALIZER(m) { PTHREAD_COND_INITIALIZER, m }
111112
void caml_plat_cond_init(caml_plat_cond*, caml_plat_mutex*);
112113
void caml_plat_wait(caml_plat_cond*);

runtime/caml/sync.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ typedef pthread_mutex_t * sync_mutex;
2929
CAMLextern int caml_mutex_lock(sync_mutex mut);
3030
CAMLextern int caml_mutex_unlock(sync_mutex mut);
3131

32+
/* If we're using glibc, use a custom condition variable implementation to
33+
avoid this bug: https://sourceware.org/bugzilla/show_bug.cgi?id=25847
34+
35+
For now we only have this on linux because it directly uses the linux futex
36+
syscalls. */
37+
#if defined(__linux__) && defined(__GNU_LIBRARY__) && defined(__GLIBC__) && defined(__GLIBC_MINOR__)
38+
typedef struct {
39+
volatile unsigned counter;
40+
} custom_condvar;
41+
#else
42+
typedef pthread_cond_t custom_condvar;
43+
#endif
44+
3245
#endif /* CAML_INTERNALS */
3346

3447
#endif /* CAML_SYNC_H */

runtime/platform.c

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
#include "caml/domain.h"
3434
#endif
3535

36+
#include "caml/alloc.h"
37+
#include "sync_posix.h"
38+
3639
/* Error reporting */
3740

3841
void caml_plat_fatal_error(const char * action, int err)
@@ -89,14 +92,7 @@ void caml_plat_mutex_free(caml_plat_mutex* m)
8992

9093
static void caml_plat_cond_init_aux(caml_plat_cond *cond)
9194
{
92-
pthread_condattr_t attr;
93-
pthread_condattr_init(&attr);
94-
#if defined(_POSIX_TIMERS) && \
95-
defined(_POSIX_MONOTONIC_CLOCK) && \
96-
_POSIX_MONOTONIC_CLOCK != (-1)
97-
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
98-
#endif
99-
pthread_cond_init(&cond->cond, &attr);
95+
custom_condvar_init(&cond->cond);
10096
}
10197

10298
/* Condition variables */
@@ -109,24 +105,24 @@ void caml_plat_cond_init(caml_plat_cond* cond, caml_plat_mutex* m)
109105
void caml_plat_wait(caml_plat_cond* cond)
110106
{
111107
caml_plat_assert_locked(cond->mutex);
112-
check_err("wait", pthread_cond_wait(&cond->cond, cond->mutex));
108+
check_err("wait", custom_condvar_wait(&cond->cond, cond->mutex));
113109
}
114110

115111
void caml_plat_broadcast(caml_plat_cond* cond)
116112
{
117113
caml_plat_assert_locked(cond->mutex);
118-
check_err("cond_broadcast", pthread_cond_broadcast(&cond->cond));
114+
check_err("cond_broadcast", custom_condvar_broadcast(&cond->cond));
119115
}
120116

121117
void caml_plat_signal(caml_plat_cond* cond)
122118
{
123119
caml_plat_assert_locked(cond->mutex);
124-
check_err("cond_signal", pthread_cond_signal(&cond->cond));
120+
check_err("cond_signal", custom_condvar_signal(&cond->cond));
125121
}
126122

127123
void caml_plat_cond_free(caml_plat_cond* cond)
128124
{
129-
check_err("cond_free", pthread_cond_destroy(&cond->cond));
125+
check_err("cond_free", custom_condvar_destroy(&cond->cond));
130126
cond->mutex=0;
131127
}
132128

runtime/sync_posix.h

Lines changed: 89 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323
#include <pthread.h>
2424
#include <string.h>
2525

26+
#ifdef __linux__
27+
#include <features.h>
28+
#include <unistd.h>
29+
#include <sys/syscall.h>
30+
#include <linux/futex.h>
31+
#include <limits.h>
32+
#endif
33+
2634
typedef int sync_retcode;
2735

2836
/* Mutexes */
@@ -82,18 +90,92 @@ Caml_inline int sync_mutex_unlock(sync_mutex m)
8290
return pthread_mutex_unlock(m);
8391
}
8492

93+
/* If we're using glibc, use a custom condition variable implementation to
94+
avoid this bug: https://sourceware.org/bugzilla/show_bug.cgi?id=25847
95+
96+
For now we only have this on linux because it directly uses the linux futex
97+
syscalls. */
98+
#if defined(__linux__) && defined(__GNU_LIBRARY__) && defined(__GLIBC__) && defined(__GLIBC_MINOR__)
99+
static int custom_condvar_init(custom_condvar * cv)
100+
{
101+
cv->counter = 0;
102+
return 0;
103+
}
104+
105+
static int custom_condvar_destroy(custom_condvar * cv)
106+
{
107+
return 0;
108+
}
109+
110+
static int custom_condvar_wait(custom_condvar * cv, pthread_mutex_t * mutex)
111+
{
112+
unsigned old_count = cv->counter;
113+
pthread_mutex_unlock(mutex);
114+
syscall(SYS_futex, &cv->counter, FUTEX_WAIT_PRIVATE, old_count, NULL, NULL, 0);
115+
pthread_mutex_lock(mutex);
116+
return 0;
117+
}
118+
119+
static int custom_condvar_signal(custom_condvar * cv)
120+
{
121+
__sync_add_and_fetch(&cv->counter, 1);
122+
syscall(SYS_futex, &cv->counter, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
123+
return 0;
124+
}
125+
126+
static int custom_condvar_broadcast(custom_condvar * cv)
127+
{
128+
__sync_add_and_fetch(&cv->counter, 1);
129+
syscall(SYS_futex, &cv->counter, FUTEX_WAKE_PRIVATE, INT_MAX, NULL, NULL, 0);
130+
return 0;
131+
}
132+
#else
133+
static int custom_condvar_init(custom_condvar * cv)
134+
{
135+
pthread_condattr_t attr;
136+
pthread_condattr_init(&attr);
137+
#if defined(_POSIX_TIMERS) && \
138+
defined(_POSIX_MONOTONIC_CLOCK) && \
139+
_POSIX_MONOTONIC_CLOCK != (-1)
140+
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
141+
#endif
142+
return pthread_cond_init(cv, &attr);
143+
}
144+
145+
static int custom_condvar_destroy(custom_condvar * cv)
146+
{
147+
return pthread_cond_destroy(cv);
148+
}
149+
150+
static int custom_condvar_wait(custom_condvar * cv, pthread_mutex_t * mutex)
151+
{
152+
return pthread_cond_wait(cv, mutex);
153+
}
154+
155+
static int custom_condvar_signal(custom_condvar * cv)
156+
{
157+
return pthread_cond_signal(cv);
158+
}
159+
160+
static int custom_condvar_broadcast(custom_condvar * cv)
161+
{
162+
return pthread_cond_broadcast(cv);
163+
}
164+
#endif
165+
166+
85167
/* Condition variables */
86168

87-
typedef pthread_cond_t * sync_condvar;
169+
typedef custom_condvar * sync_condvar;
88170

89171
#define Condition_val(v) (* (sync_condvar *) Data_custom_val(v))
90172

91173
Caml_inline int sync_condvar_create(sync_condvar * res)
92174
{
93175
int rc;
94-
sync_condvar c = caml_stat_alloc_noexc(sizeof(pthread_cond_t));
176+
sync_condvar c = caml_stat_alloc_noexc(sizeof(custom_condvar));
95177
if (c == NULL) return ENOMEM;
96-
rc = pthread_cond_init(c, NULL);
178+
rc = custom_condvar_init(c);
97179
if (rc != 0) { caml_stat_free(c); return rc; }
98180
*res = c;
99181
return 0;
@@ -102,24 +184,24 @@ Caml_inline int sync_condvar_create(sync_condvar * res)
102184
Caml_inline int sync_condvar_destroy(sync_condvar c)
103185
{
104186
int rc;
105-
rc = pthread_cond_destroy(c);
187+
rc = custom_condvar_destroy(c);
106188
caml_stat_free(c);
107189
return rc;
108190
}
109191

110192
Caml_inline int sync_condvar_signal(sync_condvar c)
111193
{
112-
return pthread_cond_signal(c);
194+
return custom_condvar_signal(c);
113195
}
114196

115197
Caml_inline int sync_condvar_broadcast(sync_condvar c)
116198
{
117-
return pthread_cond_broadcast(c);
199+
return custom_condvar_broadcast(c);
118200
}
119201

120202
Caml_inline int sync_condvar_wait(sync_condvar c, sync_mutex m)
121203
{
122-
return pthread_cond_wait(c, m);
204+
return custom_condvar_wait(c, m);
123205
}
124206

125207
/* Reporting errors */

0 commit comments

Comments
 (0)