@@ -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
@@ -124,8 +123,6 @@ private val LOCK_FAIL = Symbol("LOCK_FAIL")
124
123
@SharedImmutable
125
124
private val UNLOCK_FAIL = Symbol (" UNLOCK_FAIL" )
126
125
@SharedImmutable
127
- private val SELECT_SUCCESS = Symbol (" SELECT_SUCCESS" )
128
- @SharedImmutable
129
126
private val LOCKED = Symbol (" LOCKED" )
130
127
@SharedImmutable
131
128
private val UNLOCKED = Symbol (" UNLOCKED" )
@@ -191,7 +188,7 @@ internal class MutexImpl(locked: Boolean) : Mutex, SelectClause2<Any?, Mutex> {
191
188
}
192
189
193
190
private suspend fun lockSuspend (owner : Any? ) = suspendCancellableCoroutineReusable<Unit > sc@ { cont ->
194
- val waiter = LockCont (owner, cont)
191
+ var waiter = LockCont (owner, cont)
195
192
_state .loop { state ->
196
193
when (state) {
197
194
is Empty -> {
@@ -210,11 +207,24 @@ internal class MutexImpl(locked: Boolean) : Mutex, SelectClause2<Any?, Mutex> {
210
207
is LockedQueue -> {
211
208
val curOwner = state.owner
212
209
check(curOwner != = owner) { " Already locked by $owner " }
213
- if (state.addLastIf(waiter) { _state .value == = state }) {
214
- // added to waiter list!
210
+
211
+ state.addLast(waiter)
212
+ /*
213
+ * If the state has been changed while we were adding the waiter,
214
+ * it means that 'unlock' has taken it and _either_ resumed it successfully or just overwritten.
215
+ * To rendezvous that, we try to "invalidate" our node and go for retry.
216
+ *
217
+ * Node has to be re-instantiated as we do not support node re-adding, even to
218
+ * another list
219
+ */
220
+ if (_state .value == = state || ! waiter.take()) {
221
+ // added to waiter list
215
222
cont.removeOnCancellation(waiter)
216
223
return @sc
217
224
}
225
+
226
+ waiter = LockCont (owner, cont)
227
+ return @loop
218
228
}
219
229
is OpDescriptor -> state.perform(this ) // help
220
230
else -> error(" Illegal state $state " )
@@ -252,8 +262,17 @@ internal class MutexImpl(locked: Boolean) : Mutex, SelectClause2<Any?, Mutex> {
252
262
is LockedQueue -> {
253
263
check(state.owner != = owner) { " Already locked by $owner " }
254
264
val node = LockSelect (owner, select, block)
255
- if (state.addLastIf(node) { _state .value == = state }) {
256
- // successfully enqueued
265
+ /*
266
+ * If the state has been changed while we were adding the waiter,
267
+ * it means that 'unlock' has taken it and _either_ resumed it successfully or just overwritten.
268
+ * To rendezvous that, we try to "invalidate" our node and go for retry.
269
+ *
270
+ * Node has to be re-instantiated as we do not support node re-adding, even to
271
+ * another list
272
+ */
273
+ state.addLast(node)
274
+ if (_state .value == = state || ! node.take()) {
275
+ // added to waiter list
257
276
select.disposeOnSelect(node)
258
277
return
259
278
}
@@ -300,7 +319,7 @@ internal class MutexImpl(locked: Boolean) : Mutex, SelectClause2<Any?, Mutex> {
300
319
}
301
320
}
302
321
303
- public override fun unlock (owner : Any? ) {
322
+ override fun unlock (owner : Any? ) {
304
323
_state .loop { state ->
305
324
when (state) {
306
325
is Empty -> {
@@ -319,10 +338,9 @@ internal class MutexImpl(locked: Boolean) : Mutex, SelectClause2<Any?, Mutex> {
319
338
val op = UnlockOp (state)
320
339
if (_state .compareAndSet(state, op) && op.perform(this ) == null ) return
321
340
} else {
322
- val token = (waiter as LockWaiter ).tryResumeLockWaiter()
323
- if (token != null ) {
341
+ if ((waiter as LockWaiter ).tryResumeLockWaiter()) {
324
342
state.owner = waiter.owner ? : LOCKED
325
- waiter.completeResumeLockWaiter(token )
343
+ waiter.completeResumeLockWaiter()
326
344
return
327
345
}
328
346
}
@@ -352,31 +370,37 @@ internal class MutexImpl(locked: Boolean) : Mutex, SelectClause2<Any?, Mutex> {
352
370
private abstract inner class LockWaiter (
353
371
@JvmField val owner : Any?
354
372
) : LockFreeLinkedListNode(), DisposableHandle {
373
+ private val isTaken = atomic<Boolean >(false )
374
+ fun take (): Boolean = isTaken.compareAndSet(false , true )
355
375
final override fun dispose () { remove() }
356
- abstract fun tryResumeLockWaiter (): Any?
357
- abstract fun completeResumeLockWaiter (token : Any )
376
+ abstract fun tryResumeLockWaiter (): Boolean
377
+ abstract fun completeResumeLockWaiter ()
358
378
}
359
379
360
380
private inner class LockCont (
361
381
owner : Any? ,
362
- @JvmField val cont : CancellableContinuation <Unit >
382
+ private val cont : CancellableContinuation <Unit >
363
383
) : LockWaiter(owner) {
364
- override fun tryResumeLockWaiter () = cont.tryResume(Unit , idempotent = null ) {
365
- // if this continuation gets cancelled during dispatch to the caller, then release the lock
366
- unlock(owner)
384
+
385
+ override fun tryResumeLockWaiter (): Boolean {
386
+ if (! take()) return false
387
+ return cont.tryResume(Unit , idempotent = null ) {
388
+ // if this continuation gets cancelled during dispatch to the caller, then release the lock
389
+ unlock(owner)
390
+ } != null
367
391
}
368
- override fun completeResumeLockWaiter (token : Any ) = cont.completeResume(token)
369
- override fun toString (): String = " LockCont[$owner , $cont ] for ${this @MutexImpl} "
392
+
393
+ override fun completeResumeLockWaiter () = cont.completeResume(RESUME_TOKEN )
394
+ override fun toString (): String = " LockCont[$owner , ${cont} ] for ${this @MutexImpl} "
370
395
}
371
396
372
397
private inner class LockSelect <R >(
373
398
owner : Any? ,
374
399
@JvmField val select : SelectInstance <R >,
375
400
@JvmField val block : suspend (Mutex ) -> R
376
401
) : LockWaiter(owner) {
377
- override fun tryResumeLockWaiter (): Any? = if (select.trySelect()) SELECT_SUCCESS else null
378
- override fun completeResumeLockWaiter (token : Any ) {
379
- assert { token == = SELECT_SUCCESS }
402
+ override fun tryResumeLockWaiter (): Boolean = take() && select.trySelect()
403
+ override fun completeResumeLockWaiter () {
380
404
block.startCoroutineCancellable(receiver = this @MutexImpl, completion = select.completion) {
381
405
// if this continuation gets cancelled during dispatch to the caller, then release the lock
382
406
unlock(owner)
0 commit comments