commonMain.dev.datlag.tooling.decompose.CloseableCoroutineScope.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tooling-decompose-jvm Show documentation
Show all versions of tooling-decompose-jvm Show documentation
Kotlin multiplatform tooling library.
package dev.datlag.tooling.decompose
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
internal const val COMPONENT_SCOPE_KEY = "dev.datlag.decompose.ComponentCoroutineScope.JOB_KEY"
/**
* Creates a [CloseableCoroutineScope] intended for Component use.
*
* The [CoroutineScope.coroutineContext] is configured with:
* - [SupervisorJob]: ensures children jobs can fail independently of each other.
* - [MainCoroutineDispatcher.immediate]: executes jobs immediately on the main (UI) thread. If
* the [Dispatchers.Main] is not available on the current platform (e.g., Linux), we fallback to
* an [EmptyCoroutineContext].
*
* For background execution, use [kotlinx.coroutines.withContext] to switch to appropriate
* dispatchers (e.g., [kotlinx.coroutines.IO]).
*/
internal fun createComponentScope(): CloseableCoroutineScope {
val dispatcher = try {
// In platforms where `Dispatchers.Main` is not available, Kotlin Multiplatform will throw
// an exception (the specific exception type may depend on the platform). Since there's no
// direct functional alternative, we use `EmptyCoroutineContext` to ensure that a coroutine
// launched within this scope will run in the same context as the caller.
Dispatchers.Main.immediate
} catch (_: NotImplementedError) {
// In Native environments where `Dispatchers.Main` might not exist (e.g., Linux):
EmptyCoroutineContext
} catch (_: IllegalStateException) {
// In JVM Desktop environments where `Dispatchers.Main` might not exist (e.g., Swing):
EmptyCoroutineContext
}
return CloseableCoroutineScope(coroutineContext = dispatcher + SupervisorJob())
}
/** Represents this [CoroutineScope] as a [AutoCloseable]. */
internal fun CoroutineScope.asCloseable() = CloseableCoroutineScope(coroutineScope = this)
/**
* [CoroutineScope] that provides a method to [close] it, causing the rejection of any new tasks and
* cleanup of all underlying resources associated with the scope.
*/
@OptIn(ExperimentalStdlibApi::class)
internal class CloseableCoroutineScope(
override val coroutineContext: CoroutineContext,
) : AutoCloseable, CoroutineScope {
constructor(coroutineScope: CoroutineScope) : this(coroutineScope.coroutineContext)
override fun close() = coroutineContext.cancel()
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy