Skip to content
This repository was archived by the owner on Sep 30, 2022. It is now read-only.

Commit 8eaf6b0

Browse files
committed
opal/sync: fix race condition
This commit fixes a race condition discovered by @artpol84. The race happens when a signalling thread decrements the sync count to 0 then goes to sleep. If the waiting thread runs and detects the count == 0 before going to sleep on the condition variable it will destroy the condition variable while the signalling thread is potentially still processing the completion. The fix is to add a non-atomic member to the sync structure that indicates another process is handling completion. Since the member will only be set to false by the initiating thread and the completing thread the variable does not need to be protected. When destoying a condition variable the waiting thread needs to wait until the singalling thread is finished. Thanks to @artpol84 for tracking this down. Fixes #1813 Signed-off-by: Nathan Hjelm <[email protected]> (cherry picked from open-mpi/ompi@fb455f0) Signed-off-by: Nathan Hjelm <[email protected]>
1 parent 27ffc88 commit 8eaf6b0

File tree

1 file changed

+18
-3
lines changed

1 file changed

+18
-3
lines changed

opal/threads/wait_sync.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,37 @@ typedef struct ompi_wait_sync_t {
2626
pthread_mutex_t lock;
2727
struct ompi_wait_sync_t *next;
2828
struct ompi_wait_sync_t *prev;
29+
volatile bool signaling;
2930
} ompi_wait_sync_t;
3031

3132
#define REQUEST_PENDING (void*)0L
3233
#define REQUEST_COMPLETED (void*)1L
3334

3435
#define SYNC_WAIT(sync) (opal_using_threads() ? sync_wait_mt (sync) : sync_wait_st (sync))
3536

37+
/* The loop in release handles a race condition between the signaling
38+
* thread and the destruction of the condition variable. The signaling
39+
* member will be set to false after the final signaling thread has
40+
* finished opertating on the sync object. This is done to avoid
41+
* extra atomics in the singalling function and keep it as fast
42+
* as possible. Note that the race window is small so spinning here
43+
* is more optimal than sleeping since this macro is called in
44+
* the critical path. */
3645
#define WAIT_SYNC_RELEASE(sync) \
3746
if (opal_using_threads()) { \
38-
pthread_cond_destroy(&(sync)->condition); \
39-
pthread_mutex_destroy(&(sync)->lock); \
47+
while ((sync)->signaling) { \
48+
continue; \
49+
} \
50+
pthread_cond_destroy(&(sync)->condition); \
51+
pthread_mutex_destroy(&(sync)->lock); \
4052
}
4153

4254
#define WAIT_SYNC_SIGNAL(sync) \
4355
if (opal_using_threads()) { \
4456
pthread_mutex_lock(&(sync->lock)); \
4557
pthread_cond_signal(&sync->condition); \
4658
pthread_mutex_unlock(&(sync->lock)); \
59+
sync->signaling = false; \
4760
}
4861

4962
OPAL_DECLSPEC int sync_wait_mt(ompi_wait_sync_t *sync);
@@ -63,6 +76,7 @@ static inline int sync_wait_st (ompi_wait_sync_t *sync)
6376
(sync)->next = NULL; \
6477
(sync)->prev = NULL; \
6578
(sync)->status = 0; \
79+
(sync)->signaling = true; \
6680
if (opal_using_threads()) { \
6781
pthread_cond_init (&(sync)->condition, NULL); \
6882
pthread_mutex_init (&(sync)->lock, NULL); \
@@ -83,8 +97,9 @@ static inline void wait_sync_update(ompi_wait_sync_t *sync, int updates, int sta
8397
}
8498
} else {
8599
/* this is an error path so just use the atomic */
86-
opal_atomic_swap_32 (&sync->count, 0);
87100
sync->status = OPAL_ERROR;
101+
opal_atomic_wmb ();
102+
opal_atomic_swap_32 (&sync->count, 0);
88103
}
89104
WAIT_SYNC_SIGNAL(sync);
90105
}

0 commit comments

Comments
 (0)