xref: /aosp_15_r20/external/kotlinx.coroutines/kotlinx-coroutines-core/common/src/internal/Atomic.kt (revision 7a7160fed73afa6648ef8aa100d4a336fe921d9a)
1 @file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE")
2 
3 package kotlinx.coroutines.internal
4 
5 import kotlinx.atomicfu.atomic
6 import kotlinx.coroutines.*
7 import kotlin.jvm.*
8 
9 /**
10  * The most abstract operation that can be in process. Other threads observing an instance of this
11  * class in the fields of their object shall invoke [perform] to help.
12  *
13  * @suppress **This is unstable API and it is subject to change.**
14  */
15 public abstract class OpDescriptor {
16     /**
17      * Returns `null` is operation was performed successfully or some other
18      * object that indicates the failure reason.
19      */
performnull20     abstract fun perform(affected: Any?): Any?
21 
22     /**
23      * Returns reference to atomic operation that this descriptor is a part of or `null`
24      * if not a part of any [AtomicOp].
25      */
26     abstract val atomicOp: AtomicOp<*>?
27 
28     override fun toString(): String = "$classSimpleName@$hexAddress" // debug
29 }
30 
31 @JvmField
32 internal val NO_DECISION: Any = Symbol("NO_DECISION")
33 
34 /**
35  * Descriptor for multi-word atomic operation.
36  * Based on paper
37  * ["A Practical Multi-Word Compare-and-Swap Operation"](https://www.cl.cam.ac.uk/research/srg/netos/papers/2002-casn.pdf)
38  * by Timothy L. Harris, Keir Fraser and Ian A. Pratt.
39  *
40  * Note: parts of atomic operation must be globally ordered. Otherwise, this implementation will produce
41  * `StackOverflowError`.
42  *
43  * @suppress **This is unstable API and it is subject to change.**
44  */
45 @InternalCoroutinesApi
46 public abstract class AtomicOp<in T> : OpDescriptor() {
47     private val _consensus = atomic<Any?>(NO_DECISION)
48 
49     override val atomicOp: AtomicOp<*> get() = this
50 
51     private fun decide(decision: Any?): Any? {
52         assert { decision !== NO_DECISION }
53         val current = _consensus.value
54         if (current !== NO_DECISION) return current
55         if (_consensus.compareAndSet(NO_DECISION, decision)) return decision
56         return _consensus.value
57     }
58 
59     abstract fun prepare(affected: T): Any? // `null` if Ok, or failure reason
60 
61     abstract fun complete(affected: T, failure: Any?) // failure != null if failed to prepare op
62 
63     // returns `null` on success
64     @Suppress("UNCHECKED_CAST")
65     final override fun perform(affected: Any?): Any? {
66         // make decision on status
67         var decision = this._consensus.value
68         if (decision === NO_DECISION) {
69             decision = decide(prepare(affected as T))
70         }
71         // complete operation
72         complete(affected as T, decision)
73         return decision
74     }
75 }
76