https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98632

--- Comment #2 from tilps at hotmail dot com ---
*rough sketch*

class TaskConsumer {

  void run() {
    if (taken_count_.load(std::memory_order_acquire) <
task_count_.load(std::memory_order_acquire)) {
      taken_count_.fetch_add(1, std::memory_order_acq_rel);
      // ... handle the new task...
    }
  }

  void addTask() {
    task_count_.fetch_add(1, std::memory_order_acq_rel);
  }

  void reset() {
    task_count_.store(0, std::memory_order_release);
    taken_count_.store(0, std::memory_order_release);
  }

  std::atomic<int> task_count_;
  std::atomic<int> taken_count_;
};

The above is not a 'complete' code sample, just illustrative.
One thread is calling 'run' in a loop.
Other thread calls reset, then add task some number of times.  Waits until it
knows the first thread has done all tasks (not covered in the code above, but
assume that it is thread-safe and establishes acquire/release ordering as
appropriate), then calls reset again and repeats the process.

In order for the 'if' statement to behave correctly taken count must be read
before task count. If task count is read first it can read the value in
task_count_ before reset, but the taken_count_ value after reset.

If an optimizer (not necessarily an existing gcc one) decides to reorder the if
statement because the standard says order is unspecified and it notices that
task_count_ is an earlier memory address to taken_count_ and presumes
reordering might give a small performance increase due to sequential memory
access and cpu prefetching assumptions... then the code breaks.

Thus I would like a warning pointing at that if statement with wording along
the lines of:
C++ standard does not guarantee ordering of evaluation in this expression, thus
the order of atomic operations with non-relaxed memory ordering in this
expression is unspecified. Extract them into separate expressions to guarantee
that the ordering is consistent with the memory ordering annotations.

Reply via email to