Ensure seq_cst memory ordering for num_contenders
Problem.
Mutexes and ReaderWriterMutexes can lose wakeups due to
weak memory ordering. An unlocking thread may overlook waiters.
Thread A
0. ExclusiveLock
1. increase num_contenders as default ordering.
(fetch_add, std::memory_order_seq_cst)
2. futex waiting
...permently waiting
3. wakeup
4. decrease num_contenders
5. running
Thread B
0. Reset lock state to unlocked using seq_cst CAS.
1. load num_contenders with LoadRelaxed
(std::memory_order_relaxed)
2. if num_contenders is bigger than 0, wakeup waiters.
Thread B's load of num_contenders may be reordered with the store
in the preceding CAS (step 0).
We can then get the following interleaving:
A.0 (fails: lock held.)
B.0a (CAS load acquire sees lock as held)
B.1 (sees num_contenders = 0)
A.1 num_contenders++;
A.2 futex starts waiting (state unchanged)
B.0b (CAS store release sets state to unlocked)
B.2 (does nothing since num_contenders was 0)
We observed this hang with state_ = 0,
exclusive_owner_ = 0, num_contenders_ = 1
Indeed, the preceding comment strongly suggests that the num_contenders
load should not be relaxed.
Test: test-art-host, test-art-target
Change-Id: I912bcd3a186d9c36fb3da8a41c1f9aa1f7b39be5
Signed-off-by: Hyangseok Chae <neo.chae@lge.com>
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index 51ca274..74db312 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -193,8 +193,8 @@
// a status bit into the state on contention.
done = state_.CompareAndSetWeakSequentiallyConsistent(cur_state, cur_state - 1);
if (done && (cur_state - 1) == 0) { // Weak CAS may fail spuriously.
- if (num_pending_writers_.load(std::memory_order_relaxed) > 0 ||
- num_pending_readers_.load(std::memory_order_relaxed) > 0) {
+ if (num_pending_writers_.load(std::memory_order_seq_cst) > 0 ||
+ num_pending_readers_.load(std::memory_order_seq_cst) > 0) {
// Wake any exclusive waiters as there are now no readers.
futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0);
}
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index dd58d75..7b888b1 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -553,7 +553,7 @@
done = state_.CompareAndSetWeakSequentiallyConsistent(cur_state, 0 /* new state */);
if (LIKELY(done)) { // Spurious fail?
// Wake a contender.
- if (UNLIKELY(num_contenders_.load(std::memory_order_relaxed) > 0)) {
+ if (UNLIKELY(num_contenders_.load(std::memory_order_seq_cst) > 0)) {
futex(state_.Address(), FUTEX_WAKE, 1, nullptr, nullptr, 0);
}
}
@@ -690,8 +690,8 @@
done = state_.CompareAndSetWeakSequentiallyConsistent(-1 /* cur_state*/, 0 /* new state */);
if (LIKELY(done)) { // Weak CAS may fail spuriously.
// Wake any waiters.
- if (UNLIKELY(num_pending_readers_.load(std::memory_order_relaxed) > 0 ||
- num_pending_writers_.load(std::memory_order_relaxed) > 0)) {
+ if (UNLIKELY(num_pending_readers_.load(std::memory_order_seq_cst) > 0 ||
+ num_pending_writers_.load(std::memory_order_seq_cst) > 0)) {
futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0);
}
}