[libcxx-commits] [PATCH] D114119: [libcxx] Fix potential lost wake-up in counting semaphore

Fabian Wolff via Phabricator via libcxx-commits libcxx-commits at lists.llvm.org
Wed Apr 13 09:11:20 PDT 2022

fwolff added a comment.
Herald added a project: All.

Thanks a lot for your analysis @jiixyj! Your analysis of the ABA problem makes sense to me.

To summarize, the current `acquire()` implementation does this:

  auto const __test_fn = [this]() -> bool {
      auto __old = __a.load(memory_order_relaxed);
      return (__old != 0) && __a.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed);
  __cxx_atomic_wait(&__a.__a_, __test_fn);

where `__cxx_atomic_wait` does this:

  auto const __monitor = __libcpp_atomic_monitor(__a);
      return true;
  __libcpp_atomic_wait(__a, __monitor);

i.e. `__cxx_atomic_wait` loads the old value, and then the test function //also// loads the old value, which might have changed in between, so the test function tests a different value than `__cxx_atomic_wait` then waits on. In particular, `__cxx_atomic_wait` might read 1, `__test_fn` might read zero and therefore return `false`, and then the value of `__a.__a_` might concurrently be updated to 1 before `__cxx_atomic_wait` enters the wait with an old value of 1, hence causing a deadlock/lost wakeup.

So I think the main problem here is that `__cxx_atomic_wait` loads the value, and then `__test_fn` has to load it again. The fix proposed by @jiixyj, namely to have `__cxx_atomic_wait` pass the value of `__monitor` as a reference into `__test_fn`, appears sensible to me; what do you think, @ldionne?



More information about the libcxx-commits mailing list