<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