Skip to content

Commit 54b2ecd

Browse files
committed
Merge branch 'develop' into stacktrace-recovery
# Conflicts: # common/kotlinx-coroutines-core-common/src/JobSupport.kt # core/kotlinx-coroutines-core/test/exceptions/SuppressionTests.kt
2 parents 515ccb1 + 3c4168b commit 54b2ecd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+379
-102
lines changed

binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt

-1
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,6 @@ public final class kotlinx/coroutines/channels/ClosedSendChannelException : java
630630
}
631631

632632
public final class kotlinx/coroutines/channels/ConflatedBroadcastChannel : kotlinx/coroutines/channels/BroadcastChannel {
633-
public static final field Companion Lkotlinx/coroutines/channels/ConflatedBroadcastChannel$Companion;
634633
public fun <init> ()V
635634
public fun <init> (Ljava/lang/Object;)V
636635
public fun cancel (Ljava/lang/Throwable;)Z

build.gradle

+3-2
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,11 @@ configure(subprojects.findAll { !unpublished.contains(it.name) }) {
193193
tasks.withType(org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile).all {
194194
kotlinOptions.freeCompilerArgs += ["-Xuse-experimental=kotlin.Experimental",
195195
"-Xuse-experimental=kotlin.experimental.ExperimentalTypeInference",
196+
"-Xuse-experimental=kotlin.ExperimentalMultiplatform",
196197
"-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi",
197198
"-Xuse-experimental=kotlinx.coroutines.ObsoleteCoroutinesApi",
198-
"-Xuse-experimental=kotlinx.coroutines.InternalCoroutinesApi"]
199-
199+
"-Xuse-experimental=kotlinx.coroutines.InternalCoroutinesApi",
200+
"-progressive"]
200201
}
201202
}
202203

common/kotlinx-coroutines-core-common/src/AbstractContinuation.kt

+1
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ internal abstract class AbstractContinuation<in T>(
277277
internal interface NotCompleted
278278

279279
private class Active : NotCompleted
280+
@SharedImmutable
280281
private val ACTIVE: Active = Active()
281282

282283
internal abstract class CancelHandler : CancelHandlerBase(), NotCompleted

common/kotlinx-coroutines-core-common/src/Builders.common.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ private class DispatchedCoroutine<in T>(
228228
fun getResult(): Any? {
229229
if (trySuspend()) return COROUTINE_SUSPENDED
230230
// otherwise, onCompletionInternal was already invoked & invoked tryResume, and the result is in the state
231-
val state = this.state
231+
val state = this.state.unboxState()
232232
if (state is CompletedExceptionally) throw state.cause
233233
@Suppress("UNCHECKED_CAST")
234234
return state as T

common/kotlinx-coroutines-core-common/src/CoroutineExceptionHandler.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ internal fun handlerException(originalException: Throwable, thrownException: Thr
6565
*/
6666
@Suppress("FunctionName")
6767
public inline fun CoroutineExceptionHandler(crossinline handler: (CoroutineContext, Throwable) -> Unit): CoroutineExceptionHandler =
68-
object: AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler {
68+
object : AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler {
6969
override fun handleException(context: CoroutineContext, exception: Throwable) =
7070
handler.invoke(context, exception)
7171
}

common/kotlinx-coroutines-core-common/src/Dispatched.kt

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import kotlin.coroutines.*
99
import kotlin.jvm.*
1010

1111
@Suppress("PrivatePropertyName")
12+
@SharedImmutable
1213
private val UNDEFINED = Symbol("UNDEFINED")
1314

1415
@NativeThreadLocal

common/kotlinx-coroutines-core-common/src/JobSupport.kt

+29-13
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,10 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
154154
}
155155

156156
// ------------ state query ------------
157-
158157
/**
159158
* Returns current state of this job.
160-
* @suppress **This is unstable API and it is subject to change.**
159+
* If final state of the job is [Incomplete], then it is boxed into [IncompleteStateBox]
160+
* and should be [unboxed][unboxState] before returning to user code.
161161
*/
162162
internal val state: Any? get() {
163163
_state.loop { state -> // helper loop on state (complete in-progress atomic operations)
@@ -192,7 +192,12 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
192192
// Finalizes Finishing -> Completed (terminal state) transition.
193193
// ## IMPORTANT INVARIANT: Only one thread can be concurrently invoking this method.
194194
private fun tryFinalizeFinishingState(state: Finishing, proposedUpdate: Any?, mode: Int): Boolean {
195-
require(proposedUpdate !is Incomplete) // only incomplete -> completed transition is allowed
195+
/*
196+
* Note: proposed state can be Incompleted, e.g.
197+
* async {
198+
* smth.invokeOnCompletion {} // <- returns handle which implements Incomplete under the hood
199+
* }
200+
*/
196201
require(this.state === state) // consistency check -- it cannot change
197202
require(!state.isSealed) // consistency check -- cannot be sealed yet
198203
require(state.isCompleting) // consistency check -- must be marked as completing
@@ -220,7 +225,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
220225
handleJobException(finalException)
221226
}
222227
// Then CAS to completed state -> it must succeed
223-
require(_state.compareAndSet(state, finalState)) { "Unexpected state: ${_state.value}, expected: $state, update: $finalState" }
228+
require(_state.compareAndSet(state, finalState.boxIncomplete())) { "Unexpected state: ${_state.value}, expected: $state, update: $finalState" }
224229
// And process all post-completion actions
225230
completeStateFinalization(state, finalState, mode, suppressed)
226231
return true
@@ -255,7 +260,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
255260
private fun tryFinalizeSimpleState(state: Incomplete, update: Any?, mode: Int): Boolean {
256261
check(state is Empty || state is JobNode<*>) // only simple state without lists where children can concurrently add
257262
check(update !is CompletedExceptionally) // only for normal completion
258-
if (!_state.compareAndSet(state, update)) return false
263+
if (!_state.compareAndSet(state, update.boxIncomplete())) return false
259264
completeStateFinalization(state, update, mode, false)
260265
return true
261266
}
@@ -1067,7 +1072,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
10671072
val state = this.state
10681073
check(state !is Incomplete) { "This job has not completed yet" }
10691074
if (state is CompletedExceptionally) throw state.cause
1070-
return state
1075+
return state.unboxState()
10711076
}
10721077

10731078
/**
@@ -1082,7 +1087,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
10821087
if (state is CompletedExceptionally) { // Slow path to recover stacktrace
10831088
recoverAndThrow(state.cause)
10841089
}
1085-
return state
1090+
return state.unboxState()
10861091

10871092
}
10881093
if (startInternal(state) >= 0) break // break unless needs to retry
@@ -1114,10 +1119,12 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
11141119
if (state !is Incomplete) {
11151120
// already complete -- select result
11161121
if (select.trySelect(null)) {
1117-
if (state is CompletedExceptionally)
1122+
if (state is CompletedExceptionally) {
11181123
select.resumeSelectCancellableWithException(state.cause)
1119-
else
1120-
block.startCoroutineUnintercepted(state as T, select.completion)
1124+
}
1125+
else {
1126+
block.startCoroutineUnintercepted(state.unboxState() as T, select.completion)
1127+
}
11211128
}
11221129
return
11231130
}
@@ -1139,10 +1146,17 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
11391146
if (state is CompletedExceptionally)
11401147
select.resumeSelectCancellableWithException(state.cause)
11411148
else
1142-
block.startCoroutineCancellable(state as T, select.completion)
1149+
block.startCoroutineCancellable(state.unboxState() as T, select.completion)
11431150
}
11441151
}
11451152

1153+
/*
1154+
* Class to represent object as the final state of the Job
1155+
*/
1156+
private class IncompleteStateBox(@JvmField val state: Incomplete)
1157+
private fun Any?.boxIncomplete(): Any? = if (this is Incomplete) IncompleteStateBox(this) else this
1158+
internal fun Any?.unboxState(): Any? = (this as? IncompleteStateBox)?.state ?: this
1159+
11461160
// --------------- helper classes & constants for job implementation
11471161

11481162
private const val COMPLETING_ALREADY_COMPLETING = 0
@@ -1154,9 +1168,11 @@ private const val RETRY = -1
11541168
private const val FALSE = 0
11551169
private const val TRUE = 1
11561170

1171+
@SharedImmutable
11571172
private val SEALED = Symbol("SEALED")
1158-
1173+
@SharedImmutable
11591174
private val EMPTY_NEW = Empty(false)
1175+
@SharedImmutable
11601176
private val EMPTY_ACTIVE = Empty(true)
11611177

11621178
private class Empty(override val isActive: Boolean) : Incomplete {
@@ -1241,7 +1257,7 @@ private class ResumeAwaitOnCompletion<T>(
12411257
} else {
12421258
// Resuming with value in a cancellable way (AwaitContinuation is configured for this mode).
12431259
@Suppress("UNCHECKED_CAST")
1244-
continuation.resume(state as T)
1260+
continuation.resume(state.unboxState() as T)
12451261
}
12461262
}
12471263
override fun toString() = "ResumeAwaitOnCompletion[$continuation]"

common/kotlinx-coroutines-core-common/src/SchedulerTask.common.kt

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ internal expect abstract class SchedulerTask() : Runnable
88

99
internal expect interface SchedulerTaskContext
1010

11+
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
1112
internal expect val SchedulerTask.taskContext: SchedulerTaskContext
1213

14+
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
1315
internal expect inline fun SchedulerTaskContext.afterTask()

common/kotlinx-coroutines-core-common/src/Timeout.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import kotlin.jvm.*
2626
* @param timeMillis timeout time in milliseconds.
2727
*/
2828
public suspend fun <T> withTimeout(timeMillis: Long, block: suspend CoroutineScope.() -> T): T {
29-
if (timeMillis <= 0L) throw CancellationException("Timed out immediately")
29+
if (timeMillis <= 0L) throw TimeoutCancellationException("Timed out immediately")
3030
return suspendCoroutineUninterceptedOrReturn { uCont ->
3131
setupTimeout(TimeoutCoroutine(timeMillis, uCont), block)
3232
}

common/kotlinx-coroutines-core-common/src/channels/AbstractChannel.kt

+28-17
Original file line numberDiff line numberDiff line change
@@ -1003,32 +1003,43 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
10031003
)
10041004
}
10051005

1006-
/** @suppress **This is unstable API and it is subject to change.** */
1007-
@JvmField internal val OFFER_SUCCESS: Any = Symbol("OFFER_SUCCESS")
1006+
@JvmField
1007+
@SharedImmutable
1008+
internal val OFFER_SUCCESS: Any = Symbol("OFFER_SUCCESS")
10081009

1009-
/** @suppress **This is unstable API and it is subject to change.** */
1010-
@JvmField internal val OFFER_FAILED: Any = Symbol("OFFER_FAILED")
1010+
@JvmField
1011+
@SharedImmutable
1012+
internal val OFFER_FAILED: Any = Symbol("OFFER_FAILED")
10111013

1012-
/** @suppress **This is unstable API and it is subject to change.** */
1013-
@JvmField internal val POLL_FAILED: Any = Symbol("POLL_FAILED")
1014+
@JvmField
1015+
@SharedImmutable
1016+
internal val POLL_FAILED: Any = Symbol("POLL_FAILED")
10141017

1015-
/** @suppress **This is unstable API and it is subject to change.** */
1016-
@JvmField internal val ENQUEUE_FAILED: Any = Symbol("ENQUEUE_FAILED")
1018+
@JvmField
1019+
@SharedImmutable
1020+
internal val ENQUEUE_FAILED: Any = Symbol("ENQUEUE_FAILED")
10171021

1018-
/** @suppress **This is unstable API and it is subject to change.** */
1019-
@JvmField internal val SELECT_STARTED: Any = Symbol("SELECT_STARTED")
1022+
@JvmField
1023+
@SharedImmutable
1024+
internal val SELECT_STARTED: Any = Symbol("SELECT_STARTED")
10201025

1021-
/** @suppress **This is unstable API and it is subject to change.** */
1022-
@JvmField internal val NULL_VALUE: Any = Symbol("NULL_VALUE")
1026+
@JvmField
1027+
@SharedImmutable
1028+
internal val NULL_VALUE: Any = Symbol("NULL_VALUE")
10231029

1024-
/** @suppress **This is unstable API and it is subject to change.** */
1025-
@JvmField internal val CLOSE_RESUMED: Any = Symbol("CLOSE_RESUMED")
1030+
@JvmField
1031+
@SharedImmutable
1032+
internal val CLOSE_RESUMED: Any = Symbol("CLOSE_RESUMED")
10261033

1027-
/** @suppress **This is unstable API and it is subject to change.** */
1028-
@JvmField internal val SEND_RESUMED = Symbol("SEND_RESUMED")
1034+
@JvmField
1035+
@SharedImmutable
1036+
internal val SEND_RESUMED: Any = Symbol("SEND_RESUMED")
1037+
1038+
@JvmField
1039+
@SharedImmutable
1040+
internal val HANDLER_INVOKED: Any = Symbol("ON_CLOSE_HANDLER_INVOKED")
10291041

10301042
internal typealias Handler = (Throwable?) -> Unit
1031-
@JvmField internal val HANDLER_INVOKED = Any()
10321043

10331044
/**
10341045
* Represents sending waiter in the queue.

common/kotlinx-coroutines-core-common/src/channels/Channels.common.kt

+7-3
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,10 @@ public suspend inline fun <E> BroadcastChannel<E>.consumeEach(action: (E) -> Uni
6060
* See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
6161
*/
6262
@ObsoleteCoroutinesApi
63-
public fun ReceiveChannel<*>.consumes(): CompletionHandler =
64-
{ cause: Throwable? -> cancel(cause) }
63+
public fun ReceiveChannel<*>.consumes(): CompletionHandler = { cause: Throwable? ->
64+
@Suppress("DEPRECATION")
65+
cancel(cause)
66+
}
6567

6668
/**
6769
* Returns a [CompletionHandler] that invokes [cancel][ReceiveChannel.cancel] on all the
@@ -77,6 +79,7 @@ public fun consumesAll(vararg channels: ReceiveChannel<*>): CompletionHandler =
7779
var exception: Throwable? = null
7880
for (channel in channels)
7981
try {
82+
@Suppress("DEPRECATION")
8083
channel.cancel(cause)
8184
} catch (e: Throwable) {
8285
if (exception == null) {
@@ -112,6 +115,7 @@ public inline fun <E, R> ReceiveChannel<E>.consume(block: ReceiveChannel<E>.() -
112115
cause = e
113116
throw e
114117
} finally {
118+
@Suppress("DEPRECATION")
115119
cancel(cause)
116120
}
117121
}
@@ -302,7 +306,7 @@ public suspend fun <E> ReceiveChannel<E>.firstOrNull(): E? =
302306
* See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
303307
*/
304308
@ObsoleteCoroutinesApi
305-
public inline suspend fun <E> ReceiveChannel<E>.firstOrNull(predicate: (E) -> Boolean): E? {
309+
public suspend inline fun <E> ReceiveChannel<E>.firstOrNull(predicate: (E) -> Boolean): E? {
306310
consumeEach {
307311
if (predicate(it)) return it
308312
}

common/kotlinx-coroutines-core-common/src/channels/ConflatedBroadcastChannel.kt

+2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ public class ConflatedBroadcastChannel<E>() : BroadcastChannel<E> {
4646
private val onCloseHandler = atomic<Any?>(null)
4747

4848
private companion object {
49+
@SharedImmutable
4950
private val CLOSED = Closed(null)
51+
@SharedImmutable
5052
private val UNDEFINED = Symbol("UNDEFINED")
5153
private val INITIAL_STATE = State<Any?>(UNDEFINED, null)
5254
}

common/kotlinx-coroutines-core-common/src/internal/Atomic.kt

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public abstract class OpDescriptor {
2020
abstract fun perform(affected: Any?): Any?
2121
}
2222

23+
@SharedImmutable
2324
private val NO_DECISION: Any = Symbol("NO_DECISION")
2425

2526
/**

common/kotlinx-coroutines-core-common/src/internal/Concurrent.common.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,8 @@ internal expect class ReentrantLock() {
2121

2222
internal expect inline fun <T> ReentrantLock.withLock(action: () -> T): T
2323

24-
internal expect fun <E> identitySet(expectedSize: Int): MutableSet<E>
24+
internal expect fun <E> identitySet(expectedSize: Int): MutableSet<E>
25+
26+
@ExperimentalMultiplatform
27+
@OptionalExpectation
28+
internal expect annotation class SharedImmutable()

common/kotlinx-coroutines-core-common/src/intrinsics/Undispatched.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ private inline fun <T> AbstractCoroutine<T>.undispatchedResult(
132132
else -> result
133133
}
134134
} else {
135-
state
135+
state.unboxState()
136136
}
137137
}
138138
else -> COROUTINE_SUSPENDED

common/kotlinx-coroutines-core-common/src/selects/Select.kt

+3
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,11 @@ public suspend inline fun <R> select(crossinline builder: SelectBuilder<R>.() ->
186186
}
187187

188188

189+
@SharedImmutable
189190
internal val ALREADY_SELECTED: Any = Symbol("ALREADY_SELECTED")
191+
@SharedImmutable
190192
private val UNDECIDED: Any = Symbol("UNDECIDED")
193+
@SharedImmutable
191194
private val RESUMED: Any = Symbol("RESUMED")
192195

193196
@PublishedApi

0 commit comments

Comments
 (0)