From 0ad0e0f092b9c42ece80d172af7ec001378912a5 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Thu, 11 Sep 2025 11:55:19 +0200 Subject: [PATCH] Deprecate abstract coroutine context keys Additionally, removed the outdated opt-ins into experimental standard library APIs, as those are all stable now. Fixes #4333 --- kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt | 4 ++++ kotlinx-coroutines-core/common/src/flow/SharingStarted.kt | 1 - kotlinx-coroutines-core/common/src/selects/SelectOld.kt | 6 ++---- .../common/test/flow/sharing/ShareInTest.kt | 1 - .../common/test/flow/sharing/SharedFlowScenarioTest.kt | 1 - kotlinx-coroutines-core/jvm/src/Executors.kt | 4 ++++ .../jvm/src/debug/internal/DebugProbesImpl.kt | 3 +-- kotlinx-coroutines-core/jvm/test/DispatchersToStringTest.kt | 5 ++--- .../native/src/internal/CoroutineExceptionHandlerImpl.kt | 1 - kotlinx-coroutines-core/nativeOther/src/Dispatchers.kt | 1 - .../test/DumpCoroutineInfoAsJsonAndReferencesTest.kt | 4 +--- 11 files changed, 14 insertions(+), 17 deletions(-) diff --git a/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt b/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt index a1bbdae7d4..af88f9e82c 100644 --- a/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt +++ b/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt @@ -61,6 +61,10 @@ public abstract class CoroutineDispatcher : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor { /** @suppress */ + @Deprecated("Use ContinuationInterceptor.Key and attempt " + + "casting the context element to CoroutineDispatcher instead", + level = DeprecationLevel.WARNING) + // WARNING since 1.11, ERROR since 1.12, remove in 1.13 @ExperimentalStdlibApi public companion object Key : AbstractCoroutineContextKey( ContinuationInterceptor, diff --git a/kotlinx-coroutines-core/common/src/flow/SharingStarted.kt b/kotlinx-coroutines-core/common/src/flow/SharingStarted.kt index e1f2051e60..e891a09ef5 100644 --- a/kotlinx-coroutines-core/common/src/flow/SharingStarted.kt +++ b/kotlinx-coroutines-core/common/src/flow/SharingStarted.kt @@ -185,7 +185,6 @@ private class StartedWhileSubscribed( .dropWhile { it != SharingCommand.START } // don't emit any STOP/RESET_BUFFER to start with, only START .distinctUntilChanged() // just in case somebody forgets it, don't leak our multiple sending of START - @OptIn(ExperimentalStdlibApi::class) override fun toString(): String { val params = buildList(2) { if (stopTimeout > 0) add("stopTimeout=${stopTimeout}ms") diff --git a/kotlinx-coroutines-core/common/src/selects/SelectOld.kt b/kotlinx-coroutines-core/common/src/selects/SelectOld.kt index e636a63233..ae1222ce72 100644 --- a/kotlinx-coroutines-core/common/src/selects/SelectOld.kt +++ b/kotlinx-coroutines-core/common/src/selects/SelectOld.kt @@ -122,9 +122,8 @@ internal suspend inline fun selectUnbiasedOld(crossinline builder: SelectBui scope.initSelectResult() } -@OptIn(ExperimentalStdlibApi::class) private fun CancellableContinuation.resumeUndispatched(result: T) { - val dispatcher = context[CoroutineDispatcher] + val dispatcher = context[ContinuationInterceptor] as? CoroutineDispatcher if (dispatcher != null) { dispatcher.resumeUndispatched(result) } else { @@ -132,9 +131,8 @@ private fun CancellableContinuation.resumeUndispatched(result: T) { } } -@OptIn(ExperimentalStdlibApi::class) private fun CancellableContinuation<*>.resumeUndispatchedWithException(exception: Throwable) { - val dispatcher = context[CoroutineDispatcher] + val dispatcher = context[ContinuationInterceptor] as? CoroutineDispatcher if (dispatcher != null) { dispatcher.resumeUndispatchedWithException(exception) } else { diff --git a/kotlinx-coroutines-core/common/test/flow/sharing/ShareInTest.kt b/kotlinx-coroutines-core/common/test/flow/sharing/ShareInTest.kt index 1df59b91fe..287fad6ae6 100644 --- a/kotlinx-coroutines-core/common/test/flow/sharing/ShareInTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/sharing/ShareInTest.kt @@ -119,7 +119,6 @@ class ShareInTest : TestBase() { fun testWhileSubscribedCustomAtLeast2() = testWhileSubscribed(2, SharingStarted.WhileSubscribedAtLeast(2)) - @OptIn(ExperimentalStdlibApi::class) private fun testWhileSubscribed(threshold: Int, started: SharingStarted) = runTest { expect(1) val flowState = FlowState() diff --git a/kotlinx-coroutines-core/common/test/flow/sharing/SharedFlowScenarioTest.kt b/kotlinx-coroutines-core/common/test/flow/sharing/SharedFlowScenarioTest.kt index f4417e109a..bcb2543502 100644 --- a/kotlinx-coroutines-core/common/test/flow/sharing/SharedFlowScenarioTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/sharing/SharedFlowScenarioTest.kt @@ -267,7 +267,6 @@ class SharedFlowScenarioTest : TestBase() { private data class ResumeCollecting(val job: TestJob) : Action() private data class Cancelled(val job: TestJob) : Action() - @OptIn(ExperimentalStdlibApi::class) private class ScenarioDsl( val sharedFlow: MutableSharedFlow, coroutineContext: CoroutineContext diff --git a/kotlinx-coroutines-core/jvm/src/Executors.kt b/kotlinx-coroutines-core/jvm/src/Executors.kt index 9bd48b1537..c5976a6d49 100644 --- a/kotlinx-coroutines-core/jvm/src/Executors.kt +++ b/kotlinx-coroutines-core/jvm/src/Executors.kt @@ -15,7 +15,11 @@ import kotlin.AutoCloseable */ public abstract class ExecutorCoroutineDispatcher : CoroutineDispatcher(), Closeable, AutoCloseable { /** @suppress */ + @Deprecated("Use ContinuationInterceptor.Key and attempt " + + "casting the context element to ExecutorCoroutineDispatcher instead", + level = DeprecationLevel.WARNING) @ExperimentalStdlibApi + @Suppress("DEPRECATION") public companion object Key : AbstractCoroutineContextKey( CoroutineDispatcher, { it as? ExecutorCoroutineDispatcher }) diff --git a/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt b/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt index 25594ad01a..9583d7e766 100644 --- a/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt +++ b/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt @@ -176,7 +176,6 @@ internal object DebugProbesImpl { * Internal (JVM-public) method used by IDEA debugger as of 1.6.0-RC. * See KTIJ-24102. */ - @OptIn(ExperimentalStdlibApi::class) fun dumpCoroutinesInfoAsJsonAndReferences(): Array { val coroutinesInfo = dumpCoroutinesInfo() val size = coroutinesInfo.size @@ -186,7 +185,7 @@ internal object DebugProbesImpl { for (info in coroutinesInfo) { val context = info.context val name = context[CoroutineName.Key]?.name?.toStringRepr() - val dispatcher = context[CoroutineDispatcher.Key]?.toStringRepr() + val dispatcher = context[ContinuationInterceptor.Key]?.toStringRepr() coroutinesInfoAsJson.add( """ { diff --git a/kotlinx-coroutines-core/jvm/test/DispatchersToStringTest.kt b/kotlinx-coroutines-core/jvm/test/DispatchersToStringTest.kt index 32573ca1f6..8d4d030b80 100644 --- a/kotlinx-coroutines-core/jvm/test/DispatchersToStringTest.kt +++ b/kotlinx-coroutines-core/jvm/test/DispatchersToStringTest.kt @@ -1,9 +1,8 @@ -@file:OptIn(ExperimentalStdlibApi::class) - package kotlinx.coroutines import kotlinx.coroutines.scheduling.CORE_POOL_SIZE import kotlinx.coroutines.scheduling.MAX_POOL_SIZE +import kotlin.coroutines.ContinuationInterceptor import kotlin.test.* class DispatchersToStringTest { @@ -44,7 +43,7 @@ class DispatchersToStringTest { assertEquals("12", limitedNamed.limitedParallelism(12, "12").toString()) runBlocking { - val d = coroutineContext[CoroutineDispatcher]!! + val d = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher assertContains(d.toString(), "BlockingEventLoop") val limited = d.limitedParallelism(2) assertContains(limited.toString(), "BlockingEventLoop") diff --git a/kotlinx-coroutines-core/native/src/internal/CoroutineExceptionHandlerImpl.kt b/kotlinx-coroutines-core/native/src/internal/CoroutineExceptionHandlerImpl.kt index 621b8a9c08..a5509a500d 100644 --- a/kotlinx-coroutines-core/native/src/internal/CoroutineExceptionHandlerImpl.kt +++ b/kotlinx-coroutines-core/native/src/internal/CoroutineExceptionHandlerImpl.kt @@ -17,7 +17,6 @@ internal actual fun ensurePlatformExceptionHandlerLoaded(callback: CoroutineExce } } -@OptIn(ExperimentalStdlibApi::class) internal actual fun propagateExceptionFinalResort(exception: Throwable) { // log exception processUnhandledException(exception) diff --git a/kotlinx-coroutines-core/nativeOther/src/Dispatchers.kt b/kotlinx-coroutines-core/nativeOther/src/Dispatchers.kt index 5d200d328a..b8d4ce9407 100644 --- a/kotlinx-coroutines-core/nativeOther/src/Dispatchers.kt +++ b/kotlinx-coroutines-core/nativeOther/src/Dispatchers.kt @@ -10,7 +10,6 @@ internal actual fun createDefaultDispatcher(): CoroutineDispatcher = DefaultDisp private object DefaultDispatcher : CoroutineDispatcher() { // Be consistent with JVM -- at least 2 threads to provide some liveness guarantees in case of improper uses - @OptIn(ExperimentalStdlibApi::class) private val ctx = newFixedThreadPoolContext(Platform.getAvailableProcessors().coerceAtLeast(2), "Dispatchers.Default") override fun dispatch(context: CoroutineContext, block: Runnable) { diff --git a/kotlinx-coroutines-debug/test/DumpCoroutineInfoAsJsonAndReferencesTest.kt b/kotlinx-coroutines-debug/test/DumpCoroutineInfoAsJsonAndReferencesTest.kt index afd7a50227..610a62e6bb 100644 --- a/kotlinx-coroutines-debug/test/DumpCoroutineInfoAsJsonAndReferencesTest.kt +++ b/kotlinx-coroutines-debug/test/DumpCoroutineInfoAsJsonAndReferencesTest.kt @@ -1,7 +1,6 @@ @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") package kotlinx.coroutines.debug -import kotlinx.coroutines.testing.* import com.google.gson.* import kotlinx.coroutines.* import kotlinx.coroutines.debug.internal.* @@ -9,7 +8,6 @@ import org.junit.Test import kotlin.coroutines.* import kotlin.test.* -@ExperimentalStdlibApi class DumpCoroutineInfoAsJsonAndReferencesTest : DebugTestBase() { private data class CoroutineInfoFromJson( val name: String?, @@ -93,7 +91,7 @@ class DumpCoroutineInfoAsJsonAndReferencesTest : DebugTestBase() { val context = info.context assertEquals(context[CoroutineName.Key]?.name, infoFromJson.name) assertEquals(context[CoroutineId.Key]?.id, infoFromJson.id) - assertEquals(context[CoroutineDispatcher.Key]?.toString(), infoFromJson.dispatcher) + assertEquals(context[ContinuationInterceptor.Key]?.toString(), infoFromJson.dispatcher) } } }