All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.dev.datlag.tooling.decompose.CloseableCoroutineScope.kt Maven / Gradle / Ivy

There is a newer version: 1.7.0
Show newest version
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