<lambda>null1 @file:Suppress("RedundantVisibilityModifier")
2 
3 package kotlinx.coroutines.tasks
4 
5 import com.google.android.gms.tasks.*
6 import kotlinx.coroutines.*
7 import java.lang.Runnable
8 import java.util.concurrent.Executor
9 import kotlin.coroutines.*
10 
11 /**
12  * Converts this deferred to the instance of [Task].
13  * If deferred is cancelled then resulting task will be cancelled as well.
14  */
15 public fun <T> Deferred<T>.asTask(): Task<T> {
16     val cancellation = CancellationTokenSource()
17     val source = TaskCompletionSource<T>(cancellation.token)
18 
19     invokeOnCompletion callback@{
20         if (it is CancellationException) {
21             cancellation.cancel()
22             return@callback
23         }
24 
25         val t = getCompletionExceptionOrNull()
26         if (t == null) {
27             source.setResult(getCompleted())
28         } else {
29             source.setException(t as? Exception ?: RuntimeExecutionException(t))
30         }
31     }
32 
33     return source.task
34 }
35 
36 /**
37  * Converts this task to an instance of [Deferred].
38  * If task is cancelled then resulting deferred will be cancelled as well.
39  * However, the opposite is not true: if the deferred is cancelled, the [Task] will not be cancelled.
40  * For bi-directional cancellation, an overload that accepts [CancellationTokenSource] can be used.
41  */
asDeferrednull42 public fun <T> Task<T>.asDeferred(): Deferred<T> = asDeferredImpl(null)
43 
44 /**
45  * Converts this task to an instance of [Deferred] with a [CancellationTokenSource] to control cancellation.
46  * The cancellation of this function is bi-directional:
47  * - If the given task is cancelled, the resulting deferred will be cancelled.
48  * - If the resulting deferred is cancelled, the provided [cancellationTokenSource] will be cancelled.
49  *
50  * Providing a [CancellationTokenSource] that is unrelated to the receiving [Task] is not supported and
51  * leads to an unspecified behaviour.
52  */
53 @ExperimentalCoroutinesApi // Since 1.5.1, tentatively until 1.6.0
54 public fun <T> Task<T>.asDeferred(cancellationTokenSource: CancellationTokenSource): Deferred<T> =
55     asDeferredImpl(cancellationTokenSource)
56 
57 private fun <T> Task<T>.asDeferredImpl(cancellationTokenSource: CancellationTokenSource?): Deferred<T> {
58     val deferred = CompletableDeferred<T>()
59     if (isComplete) {
60         val e = exception
61         if (e == null) {
62             if (isCanceled) {
63                 deferred.cancel()
64             } else {
65                 @Suppress("UNCHECKED_CAST")
66                 deferred.complete(result as T)
67             }
68         } else {
69             deferred.completeExceptionally(e)
70         }
71     } else {
72         // Run the callback directly to avoid unnecessarily scheduling on the main thread.
73         addOnCompleteListener(DirectExecutor) {
74             val e = it.exception
75             if (e == null) {
76                 @Suppress("UNCHECKED_CAST")
77                 if (it.isCanceled) deferred.cancel() else deferred.complete(it.result as T)
78             } else {
79                 deferred.completeExceptionally(e)
80             }
81         }
82     }
83 
84     if (cancellationTokenSource != null) {
85         deferred.invokeOnCompletion {
86             cancellationTokenSource.cancel()
87         }
88     }
89     // Prevent casting to CompletableDeferred and manual completion.
90     return object : Deferred<T> by deferred {}
91 }
92 
93 /**
94  * Awaits the completion of the task without blocking a thread.
95  *
96  * This suspending function is cancellable.
97  * If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this function
98  * stops waiting for the completion stage and immediately resumes with [CancellationException].
99  *
100  * For bi-directional cancellation, an overload that accepts [CancellationTokenSource] can be used.
101  */
awaitnull102 public suspend fun <T> Task<T>.await(): T = awaitImpl(null)
103 
104 /**
105  * Awaits the completion of the task that is linked to the given [CancellationTokenSource] to control cancellation.
106  *
107  * This suspending function is cancellable and cancellation is bi-directional:
108  * - If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this function
109  * cancels the [cancellationTokenSource] and throws a [CancellationException].
110  * - If the task is cancelled, then this function will throw a [CancellationException].
111  *
112  * Providing a [CancellationTokenSource] that is unrelated to the receiving [Task] is not supported and
113  * leads to an unspecified behaviour.
114  */
115 @ExperimentalCoroutinesApi // Since 1.5.1, tentatively until 1.6.0
116 public suspend fun <T> Task<T>.await(cancellationTokenSource: CancellationTokenSource): T =
117     awaitImpl(cancellationTokenSource)
118 
119 private suspend fun <T> Task<T>.awaitImpl(cancellationTokenSource: CancellationTokenSource?): T {
120     // fast path
121     if (isComplete) {
122         val e = exception
123         return if (e == null) {
124             if (isCanceled) {
125                 throw CancellationException("Task $this was cancelled normally.")
126             } else {
127                 @Suppress("UNCHECKED_CAST")
128                 result as T
129             }
130         } else {
131             throw e
132         }
133     }
134 
135     return suspendCancellableCoroutine { cont ->
136         // Run the callback directly to avoid unnecessarily scheduling on the main thread.
137         addOnCompleteListener(DirectExecutor) {
138             val e = it.exception
139             if (e == null) {
140                 @Suppress("UNCHECKED_CAST")
141                 if (it.isCanceled) cont.cancel() else cont.resume(it.result as T)
142             } else {
143                 cont.resumeWithException(e)
144             }
145         }
146 
147         if (cancellationTokenSource != null) {
148             cont.invokeOnCancellation {
149                 cancellationTokenSource.cancel()
150             }
151         }
152     }
153 }
154 
155 /**
156  * An [Executor] that just directly executes the [Runnable].
157  */
158 private object DirectExecutor : Executor {
executenull159     override fun execute(r: Runnable) {
160         r.run()
161     }
162 }
163