Skip to content

Commit 7753f8e

Browse files
committed
Replace remaining xxFieldUpdater classes with AtomicFU atomic vars
1 parent 4ecd46b commit 7753f8e

File tree

17 files changed

+333
-335
lines changed

17 files changed

+333
-335
lines changed

kotlinx-coroutines-core/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@
144144
</execution>
145145
</executions>
146146
</plugin>
147+
<!-- documentation -->
147148
<plugin>
148149
<groupId>org.jetbrains.dokka</groupId>
149150
<artifactId>dokka-maven-plugin</artifactId>

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

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,23 @@
1616

1717
package kotlinx.coroutines.experimental
1818

19-
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater
19+
import kotlinx.atomicfu.atomic
20+
import kotlinx.atomicfu.loop
2021
import kotlin.coroutines.experimental.Continuation
2122

23+
24+
private const val UNDECIDED = 0
25+
private const val SUSPENDED = 1
26+
private const val RESUMED = 2
27+
2228
/**
2329
* @suppress **This is unstable API and it is subject to change.**
2430
*/
2531
internal abstract class AbstractContinuation<in T>(
2632
active: Boolean,
2733
@JvmField protected val resumeMode: Int
2834
) : JobSupport(active), Continuation<T> {
29-
@Volatile
30-
private var decision = UNDECIDED
35+
private val _decision = atomic(UNDECIDED)
3136

3237
/* decision state machine
3338
@@ -44,32 +49,20 @@ internal abstract class AbstractContinuation<in T>(
4449
Note: both tryResume and trySuspend can be invoked at most once, first invocation wins
4550
*/
4651

47-
protected companion object {
48-
@JvmField
49-
val DECISION: AtomicIntegerFieldUpdater<AbstractContinuation<*>> =
50-
AtomicIntegerFieldUpdater.newUpdater(AbstractContinuation::class.java, "decision")
51-
52-
const val UNDECIDED = 0
53-
const val SUSPENDED = 1
54-
const val RESUMED = 2
55-
}
56-
5752
protected fun trySuspend(): Boolean {
58-
while (true) { // lock-free loop on decision
59-
val decision = this.decision // volatile read
53+
_decision.loop { decision ->
6054
when (decision) {
61-
UNDECIDED -> if (DECISION.compareAndSet(this, UNDECIDED, SUSPENDED)) return true
55+
UNDECIDED -> if (this._decision.compareAndSet(UNDECIDED, SUSPENDED)) return true
6256
RESUMED -> return false
6357
else -> error("Already suspended")
6458
}
6559
}
6660
}
6761

6862
protected fun tryResume(): Boolean {
69-
while (true) { // lock-free loop on decision
70-
val decision = this.decision // volatile read
63+
_decision.loop { decision ->
7164
when (decision) {
72-
UNDECIDED -> if (DECISION.compareAndSet(this, UNDECIDED, RESUMED)) return true
65+
UNDECIDED -> if (this._decision.compareAndSet(UNDECIDED, RESUMED)) return true
7366
SUSPENDED -> return false
7467
else -> error("Already resumed")
7568
}
@@ -79,7 +72,7 @@ internal abstract class AbstractContinuation<in T>(
7972
override fun resume(value: T) = resumeImpl(value, resumeMode)
8073

8174
protected fun resumeImpl(value: T, resumeMode: Int) {
82-
lockFreeLoopOnState { state ->
75+
loopOnState { state ->
8376
when (state) {
8477
is Incomplete -> if (updateState(state, value, resumeMode)) return
8578
is Cancelled -> return // ignore resumes on cancelled continuation
@@ -91,7 +84,7 @@ internal abstract class AbstractContinuation<in T>(
9184
override fun resumeWithException(exception: Throwable) = resumeWithExceptionImpl(exception, resumeMode)
9285

9386
protected fun resumeWithExceptionImpl(exception: Throwable, resumeMode: Int) {
94-
lockFreeLoopOnState { state ->
87+
loopOnState { state ->
9588
when (state) {
9689
is Incomplete -> {
9790
if (updateState(state, CompletedExceptionally(exception), resumeMode)) return

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public abstract class AbstractCoroutine<in T>(
3838
final override val hasCancellingState: Boolean get() = true
3939

4040
final override fun resume(value: T) {
41-
lockFreeLoopOnState { state ->
41+
loopOnState { state ->
4242
when (state) {
4343
is Incomplete -> if (updateState(state, value, MODE_ATOMIC_DEFAULT)) return
4444
is Cancelled -> return // ignore resumes on cancelled continuation
@@ -48,14 +48,14 @@ public abstract class AbstractCoroutine<in T>(
4848
}
4949

5050
final override fun resumeWithException(exception: Throwable) {
51-
lockFreeLoopOnState { state ->
51+
loopOnState { state ->
5252
when (state) {
5353
is Incomplete -> {
5454
if (updateState(state, CompletedExceptionally(exception), MODE_ATOMIC_DEFAULT)) return
5555
}
5656
is Cancelled -> {
5757
// ignore resumes on cancelled continuation, but handle exception if a different one is here
58-
if (exception != state.exception) handleCoroutineException(context, exception)
58+
if (exception !== state.exception) handleCoroutineException(context, exception)
5959
return
6060
}
6161
else -> throw IllegalStateException("Already resumed, but got exception $exception", exception)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ private class CompletableDeferredImpl<T> : JobSupport(true), CompletableDeferred
9090
registerSelectAwaitInternal(select, block as (suspend (Any?) -> R))
9191

9292
override fun complete(value: T): Boolean {
93-
lockFreeLoopOnState { state ->
93+
loopOnState { state ->
9494
when (state) {
9595
is Incomplete -> {
9696
// actually, we don't care about the mode here at all, so just use a default
@@ -103,7 +103,7 @@ private class CompletableDeferredImpl<T> : JobSupport(true), CompletableDeferred
103103
}
104104

105105
override fun completeExceptionally(exception: Throwable): Boolean {
106-
lockFreeLoopOnState { state ->
106+
loopOnState { state ->
107107
when (state) {
108108
is Incomplete -> {
109109
// actually, we don't care about the mode here at all, so just use a default

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

Lines changed: 32 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package kotlinx.coroutines.experimental
1818

19+
import kotlinx.atomicfu.atomic
20+
import kotlinx.atomicfu.loop
1921
import kotlinx.coroutines.experimental.internal.LockFreeLinkedListHead
2022
import kotlinx.coroutines.experimental.internal.LockFreeLinkedListNode
2123
import kotlinx.coroutines.experimental.internal.OpDescriptor
@@ -24,8 +26,6 @@ import kotlinx.coroutines.experimental.selects.SelectBuilder
2426
import kotlinx.coroutines.experimental.selects.SelectInstance
2527
import kotlinx.coroutines.experimental.selects.select
2628
import java.util.concurrent.Future
27-
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater
28-
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
2929
import kotlin.coroutines.experimental.AbstractCoroutineContextElement
3030
import kotlin.coroutines.experimental.Continuation
3131
import kotlin.coroutines.experimental.CoroutineContext
@@ -430,22 +430,12 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
430430
FINAL_C (Cancelled) state on cancellation/completion
431431
*/
432432

433-
@Volatile
434-
private var _state: Any? = if (active) EmptyActive else EmptyNew // shared objects while we have no listeners
433+
// Note: use shared objects while we have no listeners
434+
private val _state = atomic<Any?>(if (active) EmptyActive else EmptyNew)
435435

436436
@Volatile
437437
private var parentHandle: DisposableHandle? = null
438438

439-
protected companion object {
440-
private val STATE: AtomicReferenceFieldUpdater<JobSupport, Any?> =
441-
AtomicReferenceFieldUpdater.newUpdater(JobSupport::class.java, Any::class.java, "_state")
442-
443-
fun stateToString(state: Any?): String =
444-
if (state is Incomplete)
445-
if (state.isActive) "Active" else "New"
446-
else "Completed"
447-
}
448-
449439
// ------------ initialization ------------
450440

451441
/**
@@ -488,14 +478,13 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
488478
* Returns current state of this job.
489479
*/
490480
protected val state: Any? get() {
491-
while (true) { // helper loop on state (complete in-progress atomic operations)
492-
val state = _state
481+
_state.loop { state -> // helper loop on state (complete in-progress atomic operations)
493482
if (state !is OpDescriptor) return state
494483
state.perform(this)
495484
}
496485
}
497486

498-
protected inline fun lockFreeLoopOnState(block: (Any?) -> Unit): Nothing {
487+
protected inline fun loopOnState(block: (Any?) -> Unit): Nothing {
499488
while (true) {
500489
block(state)
501490
}
@@ -545,7 +534,7 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
545534
*/
546535
protected fun tryUpdateState(expect: Any, update: Any?): Boolean {
547536
require(expect is Incomplete && update !is Incomplete) // only incomplete -> completed transition is allowed
548-
if (!STATE.compareAndSet(this, expect, update)) return false
537+
if (!_state.compareAndSet(expect, update)) return false
549538
// Unregister from parent job
550539
parentHandle?.dispose() // volatile read parentHandle _after_ state was updated
551540
return true // continues in completeUpdateState
@@ -593,7 +582,7 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
593582
notifyHandlers<JobCancellationNode<*>>(list, cause)
594583

595584
public final override fun start(): Boolean {
596-
lockFreeLoopOnState { state ->
585+
loopOnState { state ->
597586
when (startInternal(state)) {
598587
FALSE -> return false
599588
TRUE -> return true
@@ -609,13 +598,13 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
609598
when (state) {
610599
is Empty -> { // EMPTY_X state -- no completion handlers
611600
if (state.isActive) return FALSE // already active
612-
if (!STATE.compareAndSet(this, state, EmptyActive)) return RETRY
601+
if (!_state.compareAndSet(state, EmptyActive)) return RETRY
613602
onStart()
614603
return TRUE
615604
}
616605
is NodeList -> { // LIST -- a list of completion handlers (either new or active)
617-
if (state.active != 0) return FALSE
618-
if (!NodeList.ACTIVE.compareAndSet(state, 0, 1)) return RETRY
606+
if (state._active.value != 0) return FALSE
607+
if (!state._active.compareAndSet(0, 1)) return RETRY
619608
onStart()
620609
return TRUE
621610
}
@@ -663,13 +652,13 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
663652

664653
private fun installHandler(handler: CompletionHandler, onCancelling: Boolean): DisposableHandle {
665654
var nodeCache: JobNode<*>? = null
666-
lockFreeLoopOnState { state ->
655+
loopOnState { state ->
667656
when (state) {
668657
is Empty -> { // EMPTY_X state -- no completion handlers
669658
if (state.isActive) {
670659
// try move to SINGLE state
671660
val node = nodeCache ?: makeNode(handler, onCancelling).also { nodeCache = it }
672-
if (STATE.compareAndSet(this, state, node)) return node
661+
if (_state.compareAndSet(state, node)) return node
673662
} else
674663
promoteEmptyToNodeList(state) // that way we can add listener for non-active coroutine
675664
}
@@ -710,7 +699,7 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
710699

711700
private fun promoteEmptyToNodeList(state: Empty) {
712701
// try to promote it to list in new state
713-
STATE.compareAndSet(this, state, NodeList(state.isActive))
702+
_state.compareAndSet(state, NodeList(state.isActive))
714703
}
715704

716705
private fun promoteSingleToNodeList(state: JobNode<*>) {
@@ -719,7 +708,7 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
719708
// it must be in SINGLE+ state or state has changed (node could have need removed from state)
720709
val list = state.next // either NodeList or somebody else won the race, updated state
721710
// just attempt converting it to list if state is still the same, then we'll continue lock-free loop
722-
STATE.compareAndSet(this, state, list)
711+
_state.compareAndSet(state, list)
723712
}
724713

725714
final override suspend fun join() {
@@ -728,7 +717,7 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
728717
}
729718

730719
private fun joinInternal(): Boolean {
731-
lockFreeLoopOnState { state ->
720+
loopOnState { state ->
732721
if (state !is Incomplete) return false // not active anymore (complete) -- no need to wait
733722
if (startInternal(state) >= 0) return true // wait unless need to retry
734723
}
@@ -740,7 +729,7 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
740729

741730
override fun <R> registerSelectJoin(select: SelectInstance<R>, block: suspend () -> R) {
742731
// fast-path -- check state and select/return if needed
743-
lockFreeLoopOnState { state ->
732+
loopOnState { state ->
744733
if (select.isSelected) return
745734
if (state !is Incomplete) {
746735
// already complete -- select result
@@ -758,12 +747,12 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
758747

759748
internal fun removeNode(node: JobNode<*>) {
760749
// remove logic depends on the state of the job
761-
lockFreeLoopOnState { state ->
750+
loopOnState { state ->
762751
when (state) {
763752
is JobNode<*> -> { // SINGE/SINGLE+ state -- one completion handler
764753
if (state !== node) return // a different job node --> we were already removed
765754
// try remove and revert back to empty state
766-
if (STATE.compareAndSet(this, state, EmptyActive)) return
755+
if (_state.compareAndSet(state, EmptyActive)) return
767756
}
768757
is NodeList, is Cancelling -> { // LIST or CANCELLING -- a list of completion handlers
769758
// remove node from the list
@@ -789,15 +778,15 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
789778

790779
// transitions to Cancelled state
791780
private fun makeCancelled(cause: Throwable?): Boolean {
792-
lockFreeLoopOnState { state ->
781+
loopOnState { state ->
793782
if (state !is Incomplete) return false // quit if already complete
794783
if (updateStateCancelled(state, cause)) return true
795784
}
796785
}
797786

798787
// transitions to Cancelling state
799788
private fun makeCancelling(cause: Throwable?): Boolean {
800-
lockFreeLoopOnState { state ->
789+
loopOnState { state ->
801790
when (state) {
802791
is Empty -> { // EMPTY_X state -- no completion handlers
803792
if (state.isActive) {
@@ -814,7 +803,7 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
814803
is NodeList -> { // LIST -- a list of completion handlers (either new or active)
815804
if (state.isActive) {
816805
// try make it cancelling on the condition that we're still in this state
817-
if (STATE.compareAndSet(this, state, Cancelling(state, Cancelled(cause)))) {
806+
if (_state.compareAndSet(state, Cancelling(state, Cancelled(cause)))) {
818807
notifyCancellation(state, cause)
819808
onCancellation()
820809
return true
@@ -876,17 +865,9 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
876865
private class NodeList(
877866
active: Boolean
878867
) : LockFreeLinkedListHead(), Incomplete {
879-
@Volatile
880-
@JvmField
881-
var active: Int = if (active) 1 else 0
868+
val _active = atomic(if (active) 1 else 0)
882869

883-
override val isActive: Boolean get() = active != 0
884-
885-
companion object {
886-
@JvmField
887-
val ACTIVE: AtomicIntegerFieldUpdater<NodeList> =
888-
AtomicIntegerFieldUpdater.newUpdater(NodeList::class.java, "active")
889-
}
870+
override val isActive: Boolean get() = _active.value != 0
890871

891872
override fun toString(): String = buildString {
892873
append("List")
@@ -976,7 +957,7 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
976957

977958
protected fun <R> registerSelectAwaitInternal(select: SelectInstance<R>, block: suspend (Any?) -> R) {
978959
// fast-path -- check state and select/return if needed
979-
lockFreeLoopOnState { state ->
960+
loopOnState { state ->
980961
if (select.isSelected) return
981962
if (state !is Incomplete) {
982963
// already complete -- select result
@@ -1006,6 +987,11 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
1006987
}
1007988
}
1008989

990+
internal fun stateToString(state: Any?): String =
991+
if (state is JobSupport.Incomplete)
992+
if (state.isActive) "Active" else "New"
993+
else "Completed"
994+
1009995
private const val RETRY = -1
1010996
private const val FALSE = 0
1011997
private const val TRUE = 1
@@ -1095,16 +1081,10 @@ private class SelectAwaitOnCompletion<R>(
10951081

10961082
internal abstract class JobCancellationNode<out J : Job>(job: J) : JobNode<J>(job) {
10971083
// shall be invoked at most once, so here is an additional flag
1098-
@Volatile
1099-
private var invoked: Int = 0
1100-
1101-
private companion object {
1102-
private val INVOKED: AtomicIntegerFieldUpdater<JobCancellationNode<*>> = AtomicIntegerFieldUpdater
1103-
.newUpdater<JobCancellationNode<*>>(JobCancellationNode::class.java, "invoked")
1104-
}
1084+
private val _invoked = atomic(0)
11051085

11061086
final override fun invoke(reason: Throwable?) {
1107-
if (INVOKED.compareAndSet(this, 0, 1)) invokeOnce(reason)
1087+
if (_invoked.compareAndSet(0, 1)) invokeOnce(reason)
11081088
}
11091089

11101090
abstract fun invokeOnce(reason: Throwable?)

0 commit comments

Comments
 (0)