<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