16
16
17
17
package kotlinx.coroutines.experimental
18
18
19
- import kotlinx.coroutines.experimental.internal.LockFreeLinkedListHead
20
- import kotlinx.coroutines.experimental.internal.LockFreeLinkedListNode
19
+ import kotlinx.coroutines.experimental.internal.*
21
20
import java.util.concurrent.Future
22
- import java.util.concurrent.atomic.AtomicIntegerFieldUpdater
23
21
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
24
22
import kotlin.coroutines.experimental.AbstractCoroutineContextElement
25
23
import kotlin.coroutines.experimental.Continuation
@@ -260,20 +258,22 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
260
258
261
259
This state machine and its transition matrix are optimized for the common case when job is created in active
262
260
state (EMPTY_A) and at most one completion listener is added to it during its life-time.
261
+
262
+ Note, that the actual `_state` variable can also be a reference to atomic operation descriptor `OpDescriptor`
263
263
*/
264
264
265
265
@Volatile
266
- private var state : Any? = if (active) EmptyActive else EmptyNew // shared objects while we have no listeners
266
+ private var _state : Any? = if (active) EmptyActive else EmptyNew // shared objects while we have no listeners
267
267
268
268
@Volatile
269
269
private var registration: Job .Registration ? = null
270
270
271
271
protected companion object {
272
272
@JvmStatic
273
273
private val STATE : AtomicReferenceFieldUpdater <JobSupport , Any ?> =
274
- AtomicReferenceFieldUpdater .newUpdater(JobSupport ::class .java, Any ::class .java, " state " )
274
+ AtomicReferenceFieldUpdater .newUpdater(JobSupport ::class .java, Any ::class .java, " _state " )
275
275
276
- fun describeState (state : Any? ): String =
276
+ fun stateToString (state : Any? ): String =
277
277
if (state is Incomplete )
278
278
if (state.isActive) " Active" else " New"
279
279
else " Completed"
@@ -299,10 +299,16 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
299
299
/* *
300
300
* Returns current state of this job.
301
301
*/
302
- protected fun getState (): Any? = state
302
+ protected val state: Any? get() {
303
+ while (true ) { // lock-free helping loop
304
+ val state = _state
305
+ if (state !is OpDescriptor ) return state
306
+ state.perform(this )
307
+ }
308
+ }
303
309
304
310
/* *
305
- * Tries to update current [state][getState] of this job.
311
+ * Tries to update current [state] of this job.
306
312
*/
307
313
internal fun updateState (expect : Any , update : Any? ): Boolean {
308
314
if (! tryUpdateState(expect, update)) return false
@@ -347,14 +353,20 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
347
353
afterCompletion(update)
348
354
}
349
355
350
- final override val isActive: Boolean get() {
356
+ public final override val isActive: Boolean get() {
351
357
val state = this .state
352
358
return state is Incomplete && state.isActive
353
359
}
354
360
355
- final override val isCompleted: Boolean get() = state !is Incomplete
361
+ public final override val isCompleted: Boolean get() = state !is Incomplete
362
+
363
+ // this is for `select` operator. `isSelected` state means "not new" (== was started or already completed)
364
+ public val isSelected: Boolean get() {
365
+ val state = this .state
366
+ return state !is Incomplete || state.isActive
367
+ }
356
368
357
- final override fun start (): Boolean {
369
+ public final override fun start (): Boolean {
358
370
while (true ) { // lock-free loop on state
359
371
when (startInternal(state)) {
360
372
0 -> return false
@@ -375,7 +387,7 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
375
387
// LIST -- a list of completion handlers (either new or active)
376
388
state is NodeList -> {
377
389
if (state.isActive) return 0
378
- if (! NodeList .ACTIVE .compareAndSet(state, 0 , 1 )) return - 1
390
+ if (! NodeList .ACTIVE .compareAndSet(state, null , NodeList . ACTIVE_STATE )) return - 1
379
391
onStart()
380
392
return 1
381
393
}
@@ -384,13 +396,53 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
384
396
}
385
397
}
386
398
399
+ internal fun describeStart (failureMarker : Any ): AtomicDesc =
400
+ object : AtomicDesc () {
401
+ override fun prepare (op : AtomicOp ): Any? {
402
+ while (true ) { // lock-free loop on state
403
+ val state = this @JobSupport._state
404
+ when {
405
+ state == = op -> return null // already in progress
406
+ state is OpDescriptor -> state.perform(this @JobSupport) // help
407
+ state == = EmptyNew -> { // EMPTY_NEW state -- no completion handlers, new
408
+ if (STATE .compareAndSet(this @JobSupport, state, op)) return null // success
409
+ }
410
+ state is NodeList -> { // LIST -- a list of completion handlers (either new or active)
411
+ if (state.isActive) return failureMarker
412
+ if (NodeList .ACTIVE .compareAndSet(state, null , op)) return null // success
413
+ }
414
+ else -> return failureMarker // not a new state
415
+ }
416
+ }
417
+ }
418
+
419
+ override fun complete (op : AtomicOp , failure : Any? ) {
420
+ val success = failure == null
421
+ val state = this @JobSupport._state
422
+ when {
423
+ state == = op -> {
424
+ if (STATE .compareAndSet(this @JobSupport, op, if (success) EmptyActive else EmptyNew )) {
425
+ if (success) onStart()
426
+ }
427
+ }
428
+ state is NodeList -> { // LIST -- a list of completion handlers (either new or active)
429
+ if (state._active == = op) {
430
+ if (NodeList .ACTIVE .compareAndSet(state, op, if (success) NodeList .ACTIVE_STATE else null )) {
431
+ if (success) onStart()
432
+ }
433
+ }
434
+ }
435
+ }
436
+ }
437
+ }
438
+
387
439
/* *
388
440
* Override to provide the actual [start] action.
389
441
*/
390
442
protected open fun onStart () {}
391
443
392
444
final override fun getCompletionException (): Throwable {
393
- val state = getState()
445
+ val state = this .state
394
446
return when (state) {
395
447
is Incomplete -> throw IllegalStateException (" Job has not completed yet" )
396
448
is CompletedExceptionally -> state.exception
@@ -414,14 +466,14 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
414
466
// EMPTY_NEW state -- no completion handlers, new
415
467
state == = EmptyNew -> {
416
468
// try to promote it to list in new state
417
- STATE .compareAndSet(this , state, NodeList (active = 0 ))
469
+ STATE .compareAndSet(this , state, NodeList (active = false ))
418
470
}
419
471
// SINGLE/SINGLE+ state -- one completion handler
420
472
state is JobNode <* > -> {
421
473
// try to promote it to list (SINGLE+ state)
422
- state.addFirstIfEmpty (NodeList (active = 1 ))
474
+ state.addOneIfEmpty (NodeList (active = true ))
423
475
// it must be in SINGLE+ state or state has changed (node could have need removed from state)
424
- val list = state.next() // either NodeList or somebody else won the race, updated state
476
+ val list = state.next // either NodeList or somebody else won the race, updated state
425
477
// just attempt converting it to list if state is still the same, then continue lock-free loop
426
478
STATE .compareAndSet(this , state, list)
427
479
}
@@ -498,25 +550,36 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
498
550
? : InvokeOnCompletion (this , handler)
499
551
500
552
// for nicer debugging
501
- override fun toString (): String = " ${this ::class .java.simpleName} {${describeState (state)} }@${Integer .toHexString(System .identityHashCode(this ))} "
553
+ override fun toString (): String = " ${this ::class .java.simpleName} {${stateToString (state)} }@${Integer .toHexString(System .identityHashCode(this ))} "
502
554
503
555
/* *
504
- * Interface for incomplete [state][getState] of a job.
556
+ * Interface for incomplete [state] of a job.
505
557
*/
506
558
public interface Incomplete {
507
559
val isActive: Boolean
508
560
}
509
561
510
562
private class NodeList (
511
- @Volatile
512
- var active : Int
563
+ active : Boolean
513
564
) : LockFreeLinkedListHead(), Incomplete {
514
- override val isActive: Boolean get() = active != 0
565
+ @Volatile
566
+ var _active : Any? = if (active) ACTIVE_STATE else null
567
+
568
+ override val isActive: Boolean get() {
569
+ while (true ) { // helper loop for atomic ops
570
+ val active = this ._active
571
+ if (active !is OpDescriptor ) return active != null
572
+ active.perform(this )
573
+ }
574
+ }
515
575
516
576
companion object {
517
577
@JvmStatic
518
- val ACTIVE : AtomicIntegerFieldUpdater <NodeList > =
519
- AtomicIntegerFieldUpdater .newUpdater(NodeList ::class .java, " active" )
578
+ val ACTIVE : AtomicReferenceFieldUpdater <NodeList , Any ?> =
579
+ AtomicReferenceFieldUpdater .newUpdater(NodeList ::class .java, Any ::class .java, " _active" )
580
+
581
+ @JvmStatic
582
+ val ACTIVE_STATE = Symbol (" ACTIVE_STATE" )
520
583
}
521
584
522
585
override fun toString (): String = buildString {
@@ -533,7 +596,7 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
533
596
}
534
597
535
598
/* *
536
- * Class for a [state][getState] of a job that had completed exceptionally, including cancellation.
599
+ * Class for a [state] of a job that had completed exceptionally, including cancellation.
537
600
*
538
601
* @param cause the exceptional completion cause. If `cause` is null, then a [CancellationException]
539
602
* if created on first get from [exception] property.
0 commit comments