nativeDarwinMain.Dispatchers.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlinx-coroutines-core
Show all versions of kotlinx-coroutines-core
Coroutines support libraries for Kotlin
/*
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@file:OptIn(BetaInteropApi::class)
package kotlinx.coroutines
import kotlinx.cinterop.*
import platform.CoreFoundation.*
import platform.darwin.*
import kotlin.coroutines.*
import kotlin.concurrent.*
import kotlin.native.internal.NativePtr
internal fun isMainThread(): Boolean = CFRunLoopGetCurrent() == CFRunLoopGetMain()
internal actual fun createMainDispatcher(default: CoroutineDispatcher): MainCoroutineDispatcher = DarwinMainDispatcher(false)
internal actual fun createDefaultDispatcher(): CoroutineDispatcher = DarwinGlobalQueueDispatcher
private object DarwinGlobalQueueDispatcher : CoroutineDispatcher() {
@OptIn(UnsafeNumber::class)
override fun dispatch(context: CoroutineContext, block: Runnable) {
autoreleasepool {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.convert(), 0u)) {
block.run()
}
}
}
}
private class DarwinMainDispatcher(
private val invokeImmediately: Boolean
) : MainCoroutineDispatcher(), Delay {
override val immediate: MainCoroutineDispatcher =
if (invokeImmediately) this else DarwinMainDispatcher(true)
override fun isDispatchNeeded(context: CoroutineContext): Boolean = !(invokeImmediately && isMainThread())
override fun dispatch(context: CoroutineContext, block: Runnable) {
autoreleasepool {
dispatch_async(dispatch_get_main_queue()) {
block.run()
}
}
}
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) {
val timer = Timer()
val timerBlock: TimerBlock = {
timer.dispose()
continuation.resume(Unit)
}
timer.start(timeMillis, timerBlock)
continuation.disposeOnCancellation(timer)
}
override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle {
val timer = Timer()
val timerBlock: TimerBlock = {
timer.dispose()
block.run()
}
timer.start(timeMillis, timerBlock)
return timer
}
override fun toString(): String =
if (invokeImmediately) "Dispatchers.Main.immediate" else "Dispatchers.Main"
}
private typealias TimerBlock = (CFRunLoopTimerRef?) -> Unit
private val TIMER_NEW = NativePtr.NULL
private val TIMER_DISPOSED = NativePtr.NULL.plus(1)
private class Timer : DisposableHandle {
private val ref = AtomicNativePtr(TIMER_NEW)
@OptIn(UnsafeNumber::class)
fun start(timeMillis: Long, timerBlock: TimerBlock) {
val fireDate = CFAbsoluteTimeGetCurrent() + timeMillis / 1000.0
val timer = CFRunLoopTimerCreateWithHandler(null, fireDate, 0.0, 0u, 0, timerBlock)
CFRunLoopAddTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes)
if (!ref.compareAndSet(TIMER_NEW, timer.rawValue)) {
// dispose was already called concurrently
release(timer)
}
}
override fun dispose() {
while (true) {
val ptr = ref.value
if (ptr == TIMER_DISPOSED) return
if (ref.compareAndSet(ptr, TIMER_DISPOSED)) {
if (ptr != TIMER_NEW) release(interpretCPointer(ptr))
return
}
}
}
private fun release(timer: CFRunLoopTimerRef?) {
CFRunLoopRemoveTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes)
CFRelease(timer)
}
}
internal actual inline fun platformAutoreleasePool(crossinline block: () -> Unit): Unit = autoreleasepool { block() }