Skip to content

Commit 3b558d4

Browse files
committed
Fixed linearizability of Mutex.unlock
1 parent ea6b1ac commit 3b558d4

File tree

1 file changed

+14
-2
lines changed
  • kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental

1 file changed

+14
-2
lines changed

kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Mutex.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,22 @@ public class Mutex(locked: Boolean = false) {
158158
// atomic unlock operation that checks that waiters queue is empty
159159
private inner class UnlockOp(val queue: LockFreeLinkedListHead) {
160160
fun helpComplete(): Boolean {
161-
val success = queue.isEmpty // Note: queue cannot change anymore (so decision is consistent)
161+
/*
162+
Note: queue cannot change while this UnlockOp is in progress, so all concurrent attempts to
163+
make a decision will reach it consistently. It does not matter what is a proposed
164+
decision when this UnlockOp is not longer active, because in this case the following CAS
165+
will fail anyway.
166+
*/
167+
val success = queue.isEmpty
162168
val update: Any = if (success) EmptyUnlocked else queue
163169
STATE.compareAndSet(this@Mutex, this@UnlockOp, update)
164-
return success
170+
/*
171+
`helpComplete` invocation from the original `unlock` invocation may be coming too late, when
172+
some other thread had already helped to complete it (either successfully or not).
173+
That operation was unsuccessful if `state` was restored to this `queue` reference and
174+
that is what is being checked below.
175+
*/
176+
return state !== queue
165177
}
166178
}
167179
}

0 commit comments

Comments
 (0)