1 @file:OptIn(BetaInteropApi::class)
2
3 package kotlinx.coroutines
4
5 import kotlinx.cinterop.*
6 import platform.CoreFoundation.*
7 import platform.darwin.*
8 import kotlin.coroutines.*
9 import kotlin.concurrent.*
10 import kotlin.native.internal.NativePtr
11
isMainThreadnull12 internal fun isMainThread(): Boolean = CFRunLoopGetCurrent() == CFRunLoopGetMain()
13
14 internal actual fun createMainDispatcher(default: CoroutineDispatcher): MainCoroutineDispatcher = DarwinMainDispatcher(false)
15
16 internal actual fun createDefaultDispatcher(): CoroutineDispatcher = DarwinGlobalQueueDispatcher
17
18 private object DarwinGlobalQueueDispatcher : CoroutineDispatcher() {
19 override fun dispatch(context: CoroutineContext, block: Runnable) {
20 autoreleasepool {
21 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.convert(), 0u)) {
22 block.run()
23 }
24 }
25 }
26 }
27
28 private class DarwinMainDispatcher(
29 private val invokeImmediately: Boolean
30 ) : MainCoroutineDispatcher(), Delay {
31
32 override val immediate: MainCoroutineDispatcher =
33 if (invokeImmediately) this else DarwinMainDispatcher(true)
34
isDispatchNeedednull35 override fun isDispatchNeeded(context: CoroutineContext): Boolean = !(invokeImmediately && isMainThread())
36
37 override fun dispatch(context: CoroutineContext, block: Runnable) {
38 autoreleasepool {
39 dispatch_async(dispatch_get_main_queue()) {
40 block.run()
41 }
42 }
43 }
44
scheduleResumeAfterDelaynull45 override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
46 val timer = Timer()
47 val timerBlock: TimerBlock = {
48 timer.dispose()
49 continuation.resume(Unit)
50 }
51 timer.start(timeMillis, timerBlock)
52 continuation.disposeOnCancellation(timer)
53 }
54
invokeOnTimeoutnull55 override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle {
56 val timer = Timer()
57 val timerBlock: TimerBlock = {
58 timer.dispose()
59 block.run()
60 }
61 timer.start(timeMillis, timerBlock)
62 return timer
63 }
64
toStringnull65 override fun toString(): String =
66 if (invokeImmediately) "Dispatchers.Main.immediate" else "Dispatchers.Main"
67 }
68
69 private typealias TimerBlock = (CFRunLoopTimerRef?) -> Unit
70
71 private val TIMER_NEW = NativePtr.NULL
72 private val TIMER_DISPOSED = NativePtr.NULL.plus(1)
73
74 private class Timer : DisposableHandle {
75 private val ref = AtomicNativePtr(TIMER_NEW)
76
77 fun start(timeMillis: Long, timerBlock: TimerBlock) {
78 val fireDate = CFAbsoluteTimeGetCurrent() + timeMillis / 1000.0
79 val timer = CFRunLoopTimerCreateWithHandler(null, fireDate, 0.0, 0u, 0, timerBlock)
80 CFRunLoopAddTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes)
81 if (!ref.compareAndSet(TIMER_NEW, timer.rawValue)) {
82 // dispose was already called concurrently
83 release(timer)
84 }
85 }
86
87 override fun dispose() {
88 while (true) {
89 val ptr = ref.value
90 if (ptr == TIMER_DISPOSED) return
91 if (ref.compareAndSet(ptr, TIMER_DISPOSED)) {
92 if (ptr != TIMER_NEW) release(interpretCPointer(ptr))
93 return
94 }
95 }
96 }
97
98 private fun release(timer: CFRunLoopTimerRef?) {
99 CFRunLoopRemoveTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes)
100 CFRelease(timer)
101 }
102 }
103
<lambda>null104 internal actual inline fun platformAutoreleasePool(crossinline block: () -> Unit): Unit = autoreleasepool { block() }
105