blob: 4014030fe8ebbdf86edd30635a8218dfd06795b3 [file] [log] [blame]
Roman Elizarovfadf8c82017-03-02 23:36:06 +03001/*
Roman Elizarov1f74a2d2018-06-29 19:19:45 +03002 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
Roman Elizarovfadf8c82017-03-02 23:36:06 +03003 */
4
Roman Elizarovee7c0eb2017-02-16 15:29:28 +03005package kotlinx.coroutines.experimental.internal
6
Roman Elizarov7753f8e2017-08-15 11:16:33 +03007import kotlinx.atomicfu.atomic
Roman Elizarovee7c0eb2017-02-16 15:29:28 +03008
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 */
15public abstract class OpDescriptor {
16 /**
17 * Returns `null` is operation was performed successfully or some other
18 * object that indicates the failure reason.
19 */
20 abstract fun perform(affected: Any?): Any?
21}
22
Roman Elizarov7753f8e2017-08-15 11:16:33 +030023private val NO_DECISION: Any = Symbol("NO_DECISION")
24
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030025/**
26 * Descriptor for multi-word atomic operation.
27 * Based on paper
Roman Elizarov7753f8e2017-08-15 11:16:33 +030028 * ["A Practical Multi-Word Compare-and-Swap Operation"](http://www.cl.cam.ac.uk/research/srgnetos/papers/2002-casn.pdf)
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030029 * by Timothy L. Harris, Keir Fraser and Ian A. Pratt.
30 *
31 * Note: parts of atomic operation must be globally ordered. Otherwise, this implementation will produce
Roman Elizarovaa461cf2018-04-11 13:20:29 +030032 * `StackOverflowError`.
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030033 *
34 * @suppress **This is unstable API and it is subject to change.**
35 */
Roman Elizarovd82b3a92017-06-23 21:52:08 +030036public abstract class AtomicOp<in T> : OpDescriptor() {
Roman Elizarov7753f8e2017-08-15 11:16:33 +030037 private val _consensus = atomic<Any?>(NO_DECISION)
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030038
Roman Elizarov7753f8e2017-08-15 11:16:33 +030039 val isDecided: Boolean get() = _consensus.value !== NO_DECISION
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030040
Roman Elizarov1216e912017-02-22 09:57:06 +030041 fun tryDecide(decision: Any?): Boolean {
Roman Elizarov7753f8e2017-08-15 11:16:33 +030042 check(decision !== NO_DECISION)
43 return _consensus.compareAndSet(NO_DECISION, decision)
Roman Elizarov1216e912017-02-22 09:57:06 +030044 }
45
Roman Elizarov7753f8e2017-08-15 11:16:33 +030046 private fun decide(decision: Any?): Any? = if (tryDecide(decision)) decision else _consensus.value
Roman Elizarov1216e912017-02-22 09:57:06 +030047
Roman Elizarovd82b3a92017-06-23 21:52:08 +030048 abstract fun prepare(affected: T): Any? // `null` if Ok, or failure reason
Roman Elizarov1216e912017-02-22 09:57:06 +030049
Roman Elizarovd82b3a92017-06-23 21:52:08 +030050 abstract fun complete(affected: T, failure: Any?) // failure != null if failed to prepare op
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030051
52 // returns `null` on success
Roman Elizarovd82b3a92017-06-23 21:52:08 +030053 @Suppress("UNCHECKED_CAST")
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030054 final override fun perform(affected: Any?): Any? {
55 // make decision on status
Roman Elizarov7753f8e2017-08-15 11:16:33 +030056 var decision = this._consensus.value
Vsevolod Tolstopyatov2cdbfd72018-04-22 18:26:14 +030057 if (decision === NO_DECISION) {
Roman Elizarovd82b3a92017-06-23 21:52:08 +030058 decision = decide(prepare(affected as T))
Vsevolod Tolstopyatov2cdbfd72018-04-22 18:26:14 +030059 }
60
Roman Elizarovd82b3a92017-06-23 21:52:08 +030061 complete(affected as T, decision)
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030062 return decision
63 }
64}
65
66/**
67 * A part of multi-step atomic operation [AtomicOp].
68 *
69 * @suppress **This is unstable API and it is subject to change.**
70 */
71public abstract class AtomicDesc {
Roman Elizarovd82b3a92017-06-23 21:52:08 +030072 abstract fun prepare(op: AtomicOp<*>): Any? // returns `null` if prepared successfully
73 abstract fun complete(op: AtomicOp<*>, failure: Any?) // decision == null if success
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030074}