Skip to content

Commit b5b842c

Browse files
committed
Make mutex implementation linearizable (ignoring extra suspensions)
1 parent 5ae3192 commit b5b842c

File tree

1 file changed

+7
-15
lines changed
  • kotlinx-coroutines-core/common/src/sync

1 file changed

+7
-15
lines changed

kotlinx-coroutines-core/common/src/sync/Mutex.kt

+7-15
Original file line numberDiff line numberDiff line change
@@ -383,23 +383,15 @@ internal class MutexImpl(locked: Boolean) : Mutex, SelectClause2<Any?, Mutex> {
383383
) : OpDescriptor() {
384384
override val atomicOp: AtomicOp<*>? get() = null
385385

386+
private val successful = atomic<Boolean?>(null)
387+
386388
override fun perform(affected: Any?): Any? {
387-
/*
388-
Note: queue cannot change while this UnlockOp is in progress, so all concurrent attempts to
389-
make a decision will reach it consistently. It does not matter what is a proposed
390-
decision when this UnlockOp is no longer active, because in this case the following CAS
391-
will fail anyway.
392-
*/
393-
val success = queue.isEmpty
394-
val update: Any = if (success) EMPTY_UNLOCKED else queue
389+
// Note: queue cannot change while this UnlockOp is in progress,
390+
// so all concurrent attempts to make a decision will reach it consistently.
391+
successful.compareAndSet(null, queue.isEmpty)
392+
val update: Any = if (successful.value!!) EMPTY_UNLOCKED else queue
395393
(affected as MutexImpl)._state.compareAndSet(this@UnlockOp, update)
396-
/*
397-
`perform` invocation from the original `unlock` invocation may be coming too late, when
398-
some other thread had already helped to complete it (either successfully or not).
399-
That operation was unsuccessful if `state` was restored to this `queue` reference and
400-
that is what is being checked below.
401-
*/
402-
return if (affected._state.value === queue) UNLOCK_FAIL else null
394+
return if (successful.value!!) null else UNLOCK_FAIL
403395
}
404396
}
405397
}

0 commit comments

Comments
 (0)