4

I am implementing the Chase-lev deque based on the paper: "Correct and Efficient Work-Stealing for Weak Memory Models". In the paper, it requires the deque to have a buffer with atomic elements:

struct Deque {
  std::atomic<size_t> size;
  std::atomic<int> buffer[];
}

Why is the element in the buffer with type std::atomic<int> instead of plain int?

Tes
  • 349
  • 3
  • 12

1 Answers1

0

Well, because the buffer elements are read/written by different threads, and you don't always have a happens-before relation between a write and a subsequent read. So if the buffer elements were not atomic, you would have a data race.

In case you are interested, you can take a look at my implementation of the chase-lev-deque: https://github.com/mpoeter/xenium/blob/master/xenium/chase_work_stealing_deque.hpp

Update

The problem is that the indexes can wrap around. Suppose a thread calling steal might get suspended after reading top and bottom, but before it can read the item from the buffer. If in the meantime the indexes wrap around, the item could be overwritten by some push operation. Therefore the load operation in steal would not have a happens-before relation with that store.

The standard defines a data race as follows:

The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens-before the other.

Since the described example does not provide happens-before relation between the read and write operations on the buffer, this would be a data race if the buffer is not atomic. However, atomics can never participate in a data race, so by making buffer atomic, we completely prevent any data races caused by unsynchronized access (even if such operations are relaxed).

Note that this is only necessary to prevent data races for operations on buffer. The actual synchronization between a push and a steal operation happens via the operations on bottom.

mpoeter
  • 2,574
  • 1
  • 5
  • 12
  • Wouldn't the read/written to buffer elements be synchronized by the update on the `top` and `bottom` indices? – Tes Feb 03 '20 at 01:10
  • Not necessarily... The problem is that the indexes can wrap around. Suppose a thread calling steal might get suspended after reading top and bottom, but before it can read the item from the buffer. If in the meantime the indexes wrap around, the item could be overwritten by some push operation. Therefore the load operation in steal would not have a happens-before relation with that store, which would be a data race if the buffer is not atomic. – mpoeter Feb 03 '20 at 09:39
  • But why using `std::memory_order_relaxed` to read/write the element can solve the data race? Relaxed memory order does not synchronize with other operations. Could you please elaborate more on the details? Thanks! – Tes Feb 04 '20 at 22:36
  • @Tes I have updated my answer to provide more details. Please let me know if anything is still unclear. – mpoeter Feb 05 '20 at 08:12