ck_hp_fifo: Fix race condition on dequeue.

I accidentally swapped head/tail load in ck_hp_fifo (not in
ck_fifo, however). We must acquire head snapshot before tail snapshot.
An example execution history which could cause an incorrect update to occur
is below.

 - tail <- fifo.tail / fifo.head != fifo.tail
 - dequeue to empty (until final CAS which renders fifo.head = fifo.tail)
 - head <- fifo.head / (head != tail)
 - next <- fifo.head->next / next = NULL
 - As head != tail, update to next pointer (where next is NULL).

However, if

 - head <- fifo.head / (fifo.head != fifo.tail)
 - dequeue to empty (until final CAS which renders fifo.head = fifo.tail)
 - tail <- fifo.tail / fifo.head != fifo.tail
 - next <- fifo.head->next / next = NULL

If we caught tail in final transition, the by the time we read next pointer,
head would have also changed forcing us to re-read. Thanks to Hendrik Donner
for reporting this.
ck_pring
Samy Al Bahra 13 years ago
parent f02b87cd65
commit 319872ca8c

@ -134,9 +134,8 @@ ck_hp_fifo_dequeue_mpmc(ck_hp_record_t *record,
struct ck_hp_fifo_entry *head, *tail, *next; struct ck_hp_fifo_entry *head, *tail, *next;
for (;;) { for (;;) {
tail = ck_pr_load_ptr(&fifo->tail);
head = ck_pr_load_ptr(&fifo->head); head = ck_pr_load_ptr(&fifo->head);
tail = ck_pr_load_ptr(&fifo->tail);
ck_hp_set(record, 0, head); ck_hp_set(record, 0, head);
ck_pr_fence_memory(); ck_pr_fence_memory();
if (head != ck_pr_load_ptr(&fifo->head)) if (head != ck_pr_load_ptr(&fifo->head))
@ -169,8 +168,8 @@ ck_hp_fifo_trydequeue_mpmc(ck_hp_record_t *record,
{ {
struct ck_hp_fifo_entry *head, *tail, *next; struct ck_hp_fifo_entry *head, *tail, *next;
tail = ck_pr_load_ptr(&fifo->tail);
head = ck_pr_load_ptr(&fifo->head); head = ck_pr_load_ptr(&fifo->head);
tail = ck_pr_load_ptr(&fifo->tail);
ck_hp_set(record, 0, head); ck_hp_set(record, 0, head);
ck_pr_fence_memory(); ck_pr_fence_memory();
if (head != ck_pr_load_ptr(&fifo->head)) if (head != ck_pr_load_ptr(&fifo->head))

Loading…
Cancel
Save