Skip to content

Commit a2d1dba

Browse files
Merge pull request #186 from insertinterestingnamehere/tsan
Resolve Race Condition in Nemesis Threadqueue (#187)
2 parents b1171cb + 85c5389 commit a2d1dba

File tree

2 files changed

+68
-68
lines changed

2 files changed

+68
-68
lines changed

Diff for: include/qt_atomics.h

+2-11
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,14 @@
11
#ifndef QT_ATOMICS_H
22
#define QT_ATOMICS_H
33

4+
#include <stdatomic.h>
45
#include <sys/time.h>
56

67
#include <qthread/common.h>
78
#include <qthread/qthread.h>
89

9-
#if (__STDC_VERSION__ >= 201112L) && (!defined(__STDC_NO_ATOMICS__))
10-
#define USE_C11_MEMORY_FENCE
11-
#include <stdatomic.h>
12-
#endif
13-
14-
#ifdef USE_C11_MEMORY_FENCE
1510
#define THREAD_FENCE_MEM_ACQUIRE_IMPL atomic_thread_fence(memory_order_acquire)
1611
#define THREAD_FENCE_MEM_RELEASE_IMPL atomic_thread_fence(memory_order_release)
17-
#else
18-
#define THREAD_FENCE_MEM_ACQUIRE_IMPL MACHINE_FENCE
19-
#define THREAD_FENCE_MEM_RELEASE_IMPL MACHINE_FENCE
20-
#endif
2112

2213
#if (QTHREAD_ASSEMBLY_ARCH == QTHREAD_IA32) || \
2314
(QTHREAD_ASSEMBLY_ARCH == QTHREAD_AMD64)
@@ -750,7 +741,7 @@ static QINLINE aligned_t qthread_internal_incr_mod_(aligned_t *opera
750741
static QINLINE void *qt_internal_atomic_swap_ptr(void **addr,
751742
void *newval)
752743
{ /*{{{*/
753-
void *oldval = *addr;
744+
void *oldval = atomic_load_explicit((void *_Atomic *)addr, memory_order_relaxed);
754745
void *tmp;
755746

756747
while ((tmp = qthread_cas_ptr(addr, oldval, newval)) != oldval) {

Diff for: src/threadqueues/nemesis_threadqueues.c

+66-57
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/* System Headers */
66
#include <pthread.h>
77
#include <sys/types.h>
8+
#include <stdatomic.h>
89
#include <stdio.h>
910
#include <stdlib.h>
1011

@@ -42,14 +43,14 @@ int num_spins_before_condwait;
4243

4344
/* Data Structures */
4445
struct _qt_threadqueue_node {
45-
struct _qt_threadqueue_node *next;
46+
struct _qt_threadqueue_node *_Atomic next;
4647
qthread_t *thread;
4748
};
4849

4950
typedef struct {
5051
/* The First Cacheline */
51-
void *head;
52-
void *tail;
52+
void *_Atomic head;
53+
void *_Atomic tail;
5354
uint8_t pad1[CACHELINE_WIDTH - (2 * sizeof(void *))];
5455
/* The Second Cacheline */
5556
void *shadow_head;
@@ -110,7 +111,9 @@ qt_threadqueue_t INTERNAL *qt_threadqueue_new(void)
110111

111112
qassert_ret(q != NULL, NULL);
112113

113-
q->q.shadow_head = q->q.head = q->q.tail = NULL;
114+
atomic_init(&q->q.head, NULL);
115+
atomic_init(&q->q.tail, NULL);
116+
q->q.shadow_head = NULL;
114117
q->advisory_queuelen = 0;
115118
q->q.nemesis_advisory_queuelen = 0; // redundant
116119
#ifdef QTHREAD_CONDWAIT_BLOCKING_QUEUE
@@ -124,27 +127,31 @@ qt_threadqueue_t INTERNAL *qt_threadqueue_new(void)
124127
static inline qt_threadqueue_node_t *qt_internal_NEMESIS_dequeue(NEMESIS_queue *q)
125128
{ /*{{{ */
126129
if (!q->shadow_head) {
127-
if (!q->head) {
130+
void *head = atomic_load_explicit(&q->head, memory_order_relaxed);
131+
if (!head) {
128132
return NULL;
129133
}
130-
q->shadow_head = q->head;
131-
q->head = NULL;
134+
q->shadow_head = head;
135+
136+
atomic_store_explicit(&q->head, NULL, memory_order_relaxed);
132137
}
133138

134139
qt_threadqueue_node_t *const retval = (void *volatile)(q->shadow_head);
135140

136141
if ((retval != NULL) && (retval != (void *)1)) {
137-
if (retval->next != NULL) {
138-
q->shadow_head = retval->next;
139-
retval->next = NULL;
142+
struct _qt_threadqueue_node *next_loc = atomic_load_explicit(&retval->next, memory_order_acquire);
143+
if (next_loc != NULL) {
144+
q->shadow_head = next_loc;
145+
atomic_store_explicit(&retval->next, NULL, memory_order_relaxed);
140146
} else {
141147
qt_threadqueue_node_t *old;
142148
q->shadow_head = NULL;
143-
old = qthread_cas_ptr(&(q->tail), retval, NULL);
149+
old = qthread_cas_ptr((void**)&(q->tail), retval, NULL);
144150
if (old != retval) {
145-
while (retval->next == NULL) SPINLOCK_BODY();
146-
q->shadow_head = retval->next;
147-
retval->next = NULL;
151+
void *retval_next_tmp;
152+
while ((retval_next_tmp = atomic_load_explicit(&retval->next, memory_order_relaxed)) == NULL) SPINLOCK_BODY();
153+
q->shadow_head = retval_next_tmp;
154+
atomic_store_explicit(&retval->next, NULL, memory_order_relaxed);
148155
}
149156
}
150157
}
@@ -154,27 +161,29 @@ static inline qt_threadqueue_node_t *qt_internal_NEMESIS_dequeue(NEMESIS_queue *
154161
static inline qt_threadqueue_node_t *qt_internal_NEMESIS_dequeue_st(NEMESIS_queue *q)
155162
{ /*{{{ */
156163
if (!q->shadow_head) {
157-
if (!q->head) {
164+
void *head = atomic_load_explicit(&q->head, memory_order_relaxed);
165+
if (!head) {
158166
return NULL;
159167
}
160-
q->shadow_head = q->head;
161-
q->head = NULL;
168+
q->shadow_head = head;
169+
atomic_store_explicit(&q->head, NULL, memory_order_relaxed);
162170
}
163171

164172
qt_threadqueue_node_t *const retval = (void *volatile)(q->shadow_head);
165173

166174
if ((retval != NULL) && (retval != (void *)1)) {
167-
if (retval->next != NULL) {
168-
q->shadow_head = retval->next;
169-
retval->next = NULL;
175+
void *retval_next_tmp = atomic_load_explicit(&retval->next, memory_order_relaxed);
176+
if (retval_next_tmp != NULL) {
177+
q->shadow_head = retval_next_tmp;
178+
atomic_store_explicit(&retval->next, NULL, memory_order_relaxed);
170179
} else {
171180
q->shadow_head = NULL;
172-
if (q->tail == retval) {
173-
q->tail = NULL;
181+
if (atomic_load_explicit(&q->tail, memory_order_relaxed) == retval) {
182+
atomic_store_explicit(&q->tail, NULL, memory_order_relaxed);
174183
}
175184
}
176185
}
177-
qthread_debug(THREADQUEUE_DETAILS, "nemesis q:%p head:%p tail:%p shadow_head:%p\n", q, q->head, q->tail, q->shadow_head);
186+
qthread_debug(THREADQUEUE_DETAILS, "nemesis q:%p head:%p tail:%p shadow_head:%p\n", q, atomic_load_explicit(&q->head, memory_order_relaxed), atomic_load_explicit(&q->tail, memory_order_relaxed), q->shadow_head);
178187
return retval;
179188
} /*}}} */
180189

@@ -185,7 +194,7 @@ void INTERNAL qt_threadqueue_free(qt_threadqueue_t *q)
185194
qt_threadqueue_node_t *node = qt_internal_NEMESIS_dequeue_st(&q->q);
186195
if (node) {
187196
qthread_t *retval = node->thread;
188-
assert(node->next == NULL);
197+
assert(atomic_load_explicit(&node->next, memory_order_relaxed) == NULL);
189198
(void)qthread_incr(&(q->advisory_queuelen), -1);
190199
FREE_TQNODE(node);
191200
qthread_thread_free(retval);
@@ -242,18 +251,18 @@ static void sanity_check_tq(NEMESIS_queue *q)
242251
if (q->shadow_head) {
243252
assert(q->head != q->shadow_head);
244253
}
245-
if (q->tail != NULL) {
246-
if (q->head == NULL) {
254+
if (atomic_load_explicit(&q->tail, memory_order_relaxed) != NULL) {
255+
if (atomic_load_explicit(&q->head, memory_order_relaxed) == NULL) {
247256
assert(q->shadow_head != NULL);
248257
}
249258
}
250-
if ((q->head != NULL) || (q->tail != NULL)) {
259+
if ((atomic_load_explicit(&q->head, memory_order_relaxed) != NULL) || (atomic_load_explicit(&q->tail, memory_order_relaxed) != NULL)) {
251260
if (q->shadow_head) {
252261
curs = q->shadow_head;
253262
assert(curs->thread);
254263
assert(curs->thread != (void *)0x7777777777777777);
255-
while (curs->next) {
256-
curs = curs->next;
264+
while (atomic_load_explicit(&curs->next, memory_order_relaxed)) {
265+
curs = atomic_load_explicit(&curs->next, memory_order_relaxed);
257266
assert(curs->thread);
258267
assert(curs->thread != (void *)0x7777777777777777);
259268
}
@@ -262,8 +271,8 @@ static void sanity_check_tq(NEMESIS_queue *q)
262271
curs = q->head;
263272
assert(curs->thread);
264273
assert(curs->thread != (void *)0x7777777777777777);
265-
while (curs->next) {
266-
curs = curs->next;
274+
while (atomic_load_explicit(&curs->next, memory_order_relaxed)) {
275+
curs = atomic_load_explicit(&curs->next, memory_order_relaxed);
267276
assert(curs->thread);
268277
assert(curs->thread != (void *)0x7777777777777777);
269278
}
@@ -305,14 +314,14 @@ void INTERNAL qt_threadqueue_enqueue(qt_threadqueue_t *restrict q,
305314
node = ALLOC_TQNODE();
306315
assert(node != NULL);
307316
node->thread = t;
308-
node->next = NULL;
317+
atomic_store_explicit(&node->next, NULL, memory_order_release);
309318

310319
prev = qt_internal_atomic_swap_ptr((void **)&(q->q.tail), node);
311320

312321
if (prev == NULL) {
313-
q->q.head = node;
322+
atomic_store_explicit(&q->q.head, node, memory_order_relaxed);
314323
} else {
315-
prev->next = node;
324+
atomic_store_explicit(&prev->next, node, memory_order_relaxed);
316325
}
317326
PARANOIA(sanity_check_tq(&q->q));
318327
(void)qthread_incr(&(q->advisory_queuelen), 1);
@@ -354,12 +363,12 @@ qthread_t INTERNAL *qt_scheduler_get_thread(qt_threadqueue_t *q,
354363
#ifdef QTHREAD_USE_EUREKAS
355364
qt_eureka_disable();
356365
#endif /* QTHREAD_USE_EUREKAS */
357-
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p sh:%p} q->advisory_queuelen:%u\n", q, q->q.head, q->q.tail, q->q.shadow_head, q->advisory_queuelen);
366+
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p sh:%p} q->advisory_queuelen:%u\n", q, atomic_load_explicit(&q->q.head, memory_order_relaxed), atomic_load_explicit(&q->q.tail, memory_order_relaxed), q->q.shadow_head, q->advisory_queuelen);
358367
PARANOIA(sanity_check_tq(&q->q));
359368
qt_threadqueue_node_t *node = qt_internal_NEMESIS_dequeue(&q->q);
360369
qthread_t *retval;
361370

362-
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p sh:%p} q->advisory_queuelen:%u\n", q, q->q.head, q->q.tail, q->q.shadow_head, q->advisory_queuelen);
371+
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p sh:%p} q->advisory_queuelen:%u\n", q, atomic_load_explicit(&q->q.head, memory_order_relaxed), atomic_load_explicit(&q->q.tail), q->q.shadow_head, q->advisory_queuelen);
363372
PARANOIA(sanity_check_tq(&q->q));
364373
if (node == NULL) {
365374
#ifdef QTHREAD_USE_EUREKAS
@@ -368,13 +377,13 @@ qthread_t INTERNAL *qt_scheduler_get_thread(qt_threadqueue_t *q,
368377

369378
#ifdef QTHREAD_CONDWAIT_BLOCKING_QUEUE
370379
i = num_spins_before_condwait;
371-
while (q->q.shadow_head == NULL && q->q.head == NULL && i > 0) {
380+
while (q->q.shadow_head == NULL && atomic_load_explicit(&q->q.head, memory_order_relaxed) == NULL && i > 0) {
372381
SPINLOCK_BODY();
373382
i--;
374383
}
375384
#endif /* QTHREAD_CONDWAIT_BLOCKING_QUEUE */
376385

377-
while (q->q.shadow_head == NULL && q->q.head == NULL) {
386+
while (q->q.shadow_head == NULL && atomic_load_explicit(&q->q.head, memory_order_relaxed) == NULL) {
378387
#ifndef QTHREAD_CONDWAIT_BLOCKING_QUEUE
379388
SPINLOCK_BODY();
380389
#else
@@ -393,11 +402,11 @@ qthread_t INTERNAL *qt_scheduler_get_thread(qt_threadqueue_t *q,
393402
node = qt_internal_NEMESIS_dequeue(&q->q);
394403
}
395404
assert(node);
396-
assert(node->next == NULL);
405+
assert(atomic_load_explicit(&node->next, memory_order_relaxed) == NULL);
397406
(void)qthread_incr(&(q->advisory_queuelen), -1);
398407
retval = node->thread;
399408
FREE_TQNODE(node);
400-
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p sh:%p} q->advisory_queuelen:%u\n", q, q->q.head, q->q.tail, q->q.shadow_head, q->advisory_queuelen);
409+
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p sh:%p} q->advisory_queuelen:%u\n", q, atomic_load_explicit(&q->q.head, memory_order_relaxed), atomic_load_explicit(&q->q.tail), q->q.shadow_head, q->advisory_queuelen);
401410
PARANOIA(sanity_check_tq(&q->q));
402411
return retval;
403412
} /*}}} */
@@ -412,33 +421,33 @@ void INTERNAL qt_threadqueue_filter(qt_threadqueue_t *q,
412421
assert(q != NULL);
413422
qthread_debug(THREADQUEUE_FUNCTIONS, "begin q:%p f:%p\n", q, f);
414423

415-
tmp.head = NULL;
416-
tmp.tail = NULL;
424+
atomic_init(&tmp.head, NULL);
425+
atomic_init(&tmp.tail, NULL);
417426
tmp.shadow_head = NULL;
418427
tmp.nemesis_advisory_queuelen = 0;
419-
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p} q->advisory_queuelen:%u\n", q, q->q.head, q->q.tail, q->advisory_queuelen);
428+
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p} q->advisory_queuelen:%u\n", q, atomic_load_explicit(&q->q.head, memory_order_relaxed), atomic_load_explicit(&q->q.tail, memory_order_relaxed), q->advisory_queuelen);
420429
PARANOIA(sanity_check_tq(&q->q));
421430
while ((curs = qt_internal_NEMESIS_dequeue_st(&q->q))) {
422431
qthread_t *t = curs->thread;
423-
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p} q->advisory_queuelen:%u\n", q, q->q.head, q->q.tail, q->advisory_queuelen);
432+
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p} q->advisory_queuelen:%u\n", q, atomic_load_explicit(&q->q.head, memory_order_relaxed), atomic_load_explicit(&q->q.tail, memory_order_relaxed), q->advisory_queuelen);
424433
PARANOIA(sanity_check_tq(&tmp));
425434
PARANOIA(sanity_check_tq(&q->q));
426435
switch (f(t)) {
427436
case IGNORE_AND_CONTINUE: // ignore, move on
428437
prev = qt_internal_atomic_swap_ptr((void **)&(tmp.tail), curs);
429438
if (prev == NULL) {
430-
tmp.head = curs;
439+
atomic_store_explicit(&tmp.head, curs, memory_order_relaxed);
431440
} else {
432-
prev->next = curs;
441+
atomic_store_explicit(&prev->next, curs, memory_order_relaxed);
433442
}
434443
tmp.nemesis_advisory_queuelen++;
435444
break;
436445
case IGNORE_AND_STOP: // ignore, stop looking
437446
prev = qt_internal_atomic_swap_ptr((void **)&(tmp.tail), curs);
438447
if (prev == NULL) {
439-
tmp.head = curs;
448+
atomic_store_explicit(&tmp.head, curs, memory_order_relaxed);
440449
} else {
441-
prev->next = curs;
450+
atomic_store_explicit(&prev->next, curs, memory_order_relaxed);
442451
}
443452
tmp.nemesis_advisory_queuelen++;
444453
goto pushback;
@@ -458,24 +467,24 @@ void INTERNAL qt_threadqueue_filter(qt_threadqueue_t *q,
458467
}
459468
pushback:
460469
/* dequeue the rest of the queue */
461-
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p} q->advisory_queuelen:%u\n", q, q->q.head, q->q.tail, q->advisory_queuelen);
462-
qthread_debug(THREADQUEUE_DETAILS, "tmp {head:%p tail:%p} tmp->advisory_queuelen:%u\n", tmp.head, tmp.tail, tmp.nemesis_advisory_queuelen);
470+
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p} q->advisory_queuelen:%u\n", q, atomic_load_explicit(&q->q.head, memory_order_relaxed), atomic_load_explicit(&q->q.tail, memory_order_relaxed), q->advisory_queuelen);
471+
qthread_debug(THREADQUEUE_DETAILS, "tmp {head:%p tail:%p} tmp->advisory_queuelen:%u\n", atomic_load_explicit(&tmp.head, memory_order_relaxed), atomic_load_explicit(&tmp.tail, memory_order_relaxed), tmp.nemesis_advisory_queuelen);
463472
PARANOIA(sanity_check_tq(&tmp));
464-
if (q->q.head) {
473+
if (atomic_load_explicit(&q->q.head, memory_order_relaxed)) {
465474
prev = qt_internal_atomic_swap_ptr((void **)&(tmp.tail), q->q.head);
466475
if (prev == NULL) {
467-
tmp.head = q->q.head;
476+
atomic_store_explicit(&tmp.head, atomic_load_explicit(&q->q.head, memory_order_relaxed), memory_order_relaxed);
468477
} else {
469-
prev->next = q->q.head;
478+
atomic_store_explicit(&prev->next, atomic_load_explicit(&q->q.head, memory_order_relaxed), memory_order_relaxed);
470479
}
471480
tmp.nemesis_advisory_queuelen += q->advisory_queuelen;
472-
tmp.tail = q->q.tail;
481+
atomic_store_explicit(&tmp.tail, atomic_load_explicit(&q->q.tail, memory_order_relaxed), memory_order_relaxed);
473482
}
474-
q->q.head = tmp.head;
475-
q->q.tail = tmp.tail;
483+
atomic_store_explicit(&q->q.head, atomic_load_explicit(&tmp.head, memory_order_relaxed), memory_order_relaxed);
484+
atomic_store_explicit(&q->q.tail, atomic_load_explicit(&tmp.tail, memory_order_relaxed), memory_order_relaxed);
476485
q->q.shadow_head = NULL;
477486
q->advisory_queuelen = tmp.nemesis_advisory_queuelen;
478-
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p} q->advisory_queuelen:%u\n", q, q->q.head, q->q.tail, q->advisory_queuelen);
487+
qthread_debug(THREADQUEUE_DETAILS, "q(%p)->q {head:%p tail:%p} q->advisory_queuelen:%u\n", q, atomic_load_explicit(&q->q.head, memory_order_relaxed), atomic_load_explicit(&q->q.tail, memory_order_relaxed), q->advisory_queuelen);
479488
PARANOIA(sanity_check_tq(&q->q));
480489
} /*}}}*/
481490

0 commit comments

Comments
 (0)