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