xref: /aosp_15_r20/external/kotlinx.coroutines/kotlinx-coroutines-core/nativeDarwin/src/Dispatchers.kt (revision 7a7160fed73afa6648ef8aa100d4a336fe921d9a)
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