blob: ace20422f63b7f671dd0b6e34d1f0d8be637cfe3 [file] [log] [blame]
/*
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines
import kotlinx.cinterop.*
import kotlinx.coroutines.internal.*
import platform.CoreFoundation.*
import platform.darwin.*
import kotlin.coroutines.*
import kotlin.native.concurrent.*
import kotlin.native.internal.NativePtr
internal fun isMainThread(): Boolean = CFRunLoopGetCurrent() == CFRunLoopGetMain()
internal actual fun createMainDispatcher(default: CoroutineDispatcher): MainCoroutineDispatcher =
if (multithreadingSupported) DarwinMainDispatcher(false) else OldMainDispatcher(Dispatchers.Default)
internal actual fun createDefaultDispatcher(): CoroutineDispatcher = DarwinGlobalQueueDispatcher
private object DarwinGlobalQueueDispatcher : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
autoreleasepool {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.convert(), 0)) {
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<Unit>) {
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 =
"MainDispatcher${ if(invokeImmediately) "[immediate]" else "" }"
}
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)
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() }