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