• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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