@@ -10,7 +10,6 @@ import kotlinx.coroutines.internal.*
10
10
import kotlinx.coroutines.intrinsics.*
11
11
import kotlinx.coroutines.selects.*
12
12
import kotlin.contracts.*
13
- import kotlin.coroutines.*
14
13
import kotlin.jvm.*
15
14
import kotlin.native.concurrent.*
16
15
@@ -20,11 +19,6 @@ import kotlin.native.concurrent.*
20
19
* Mutex has two states: _locked_ and _unlocked_.
21
20
* It is **non-reentrant**, that is invoking [lock] even from the same thread/coroutine that currently holds
22
21
* the lock still suspends the invoker.
23
- *
24
- * JVM API note:
25
- * Memory semantic of the [Mutex] is similar to `synchronized` block on JVM:
26
- * An unlock on a [Mutex] happens-before every subsequent successful lock on that [Mutex].
27
- * Unsuccessful call to [tryLock] do not have any memory effects.
28
22
*/
29
23
public interface Mutex {
30
24
/* *
@@ -81,8 +75,8 @@ public interface Mutex {
81
75
* Unlocks this mutex. Throws [IllegalStateException] if invoked on a mutex that is not locked or
82
76
* was locked with a different owner token (by identity).
83
77
*
84
- * @param owner Optional owner token for debugging. When `owner` is specified (non-null value) and this mutex
85
- * was locked with the different token (by identity) , this function throws [IllegalStateException].
78
+ * @param owner Optional owner token for debugging. When `owner` does not match the owner (or its absence)
79
+ * that locked this mutex , this function throws [IllegalStateException].
86
80
*/
87
81
public fun unlock (owner : Any? = null)
88
82
}
@@ -136,6 +130,12 @@ private val EMPTY_LOCKED = Empty(LOCKED)
136
130
private val EMPTY_UNLOCKED = Empty (UNLOCKED )
137
131
138
132
private class Empty (
133
+ /*
134
+ * State of the lock:
135
+ * - LOCKED
136
+ * - UNLOCKED
137
+ * - owner (of the lock, passed via 'lock(owner)')
138
+ */
139
139
@JvmField val locked : Any
140
140
) {
141
141
override fun toString (): String = " Empty[$locked ]"
@@ -163,7 +163,7 @@ internal class MutexImpl(locked: Boolean) : Mutex, SelectClause2<Any?, Mutex> {
163
163
return state is LockedQueue && state.isEmpty
164
164
}
165
165
166
- public override fun tryLock (owner : Any? ): Boolean {
166
+ override fun tryLock (owner : Any? ): Boolean {
167
167
_state .loop { state ->
168
168
when (state) {
169
169
is Empty -> {
@@ -183,7 +183,7 @@ internal class MutexImpl(locked: Boolean) : Mutex, SelectClause2<Any?, Mutex> {
183
183
}
184
184
}
185
185
186
- public override suspend fun lock (owner : Any? ) {
186
+ override suspend fun lock (owner : Any? ) {
187
187
// fast-path -- try lock
188
188
if (tryLock(owner)) return
189
189
// slow-path -- suspend
@@ -304,16 +304,23 @@ internal class MutexImpl(locked: Boolean) : Mutex, SelectClause2<Any?, Mutex> {
304
304
_state .loop { state ->
305
305
when (state) {
306
306
is Empty -> {
307
- if (owner == null )
307
+ if (owner == null ) {
308
308
check(state.locked != = UNLOCKED ) { " Mutex is not locked" }
309
- else
310
- check(state.locked == = owner) { " Mutex is locked by ${state.locked} but expected $owner " }
309
+ expectNoOwner(state.locked)
310
+ }
311
+ else {
312
+ val actualOwner = state.locked
313
+ expectOwner(actualOwner, owner)
314
+ }
311
315
if (_state .compareAndSet(state, EMPTY_UNLOCKED )) return
312
316
}
313
317
is OpDescriptor -> state.perform(this )
314
318
is LockedQueue -> {
315
- if (owner != null )
316
- check(state.owner == = owner) { " Mutex is locked by ${state.owner} but expected $owner " }
319
+ if (owner != null ) {
320
+ expectOwner(state.owner, owner)
321
+ } else {
322
+ expectNoOwner(state.owner)
323
+ }
317
324
val waiter = state.removeFirstOrNull()
318
325
if (waiter == null ) {
319
326
val op = UnlockOp (state)
@@ -332,6 +339,14 @@ internal class MutexImpl(locked: Boolean) : Mutex, SelectClause2<Any?, Mutex> {
332
339
}
333
340
}
334
341
342
+ private fun expectNoOwner (l : Any ) {
343
+ check(l == LOCKED ) { " Mutex is locked by $l but expected no owner" }
344
+ }
345
+
346
+ private fun expectOwner (actualOwner : Any , owner : Any? ) {
347
+ check(actualOwner == = owner) { " Mutex is locked by $actualOwner but expected $owner " }
348
+ }
349
+
335
350
override fun toString (): String {
336
351
_state .loop { state ->
337
352
when (state) {
0 commit comments