/* * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("RedundantVisibilityModifier") package kotlinx.coroutines.tasks import com.google.android.gms.tasks.CancellationTokenSource import com.google.android.gms.tasks.RuntimeExecutionException import com.google.android.gms.tasks.Task import com.google.android.gms.tasks.TaskCompletionSource import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Deferred import kotlinx.coroutines.Job import kotlinx.coroutines.suspendCancellableCoroutine import kotlin.coroutines.* /** * Converts this deferred to the instance of [Task]. * If deferred is cancelled then resulting task will be cancelled as well. */ public fun Deferred.asTask(): Task { val cancellation = CancellationTokenSource() val source = TaskCompletionSource(cancellation.token) invokeOnCompletion callback@{ if (it is CancellationException) { cancellation.cancel() return@callback } val t = getCompletionExceptionOrNull() if (t == null) { source.setResult(getCompleted()) } else { source.setException(t as? Exception ?: RuntimeExecutionException(t)) } } return source.task } /** * Converts this task to an instance of [Deferred]. * If task is cancelled then resulting deferred will be cancelled as well. */ public fun Task.asDeferred(): Deferred { if (isComplete) { val e = exception return if (e == null) { @Suppress("UNCHECKED_CAST") CompletableDeferred().apply { if (isCanceled) cancel() else complete(result as T) } } else { CompletableDeferred().apply { completeExceptionally(e) } } } val result = CompletableDeferred() addOnCompleteListener { val e = it.exception if (e == null) { @Suppress("UNCHECKED_CAST") if (isCanceled) result.cancel() else result.complete(it.result as T) } else { result.completeExceptionally(e) } } return result } /** * Awaits for completion of the task without blocking a thread. * * This suspending function is cancellable. * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function * stops waiting for the completion stage and immediately resumes with [CancellationException]. */ public suspend fun Task.await(): T { // fast path if (isComplete) { val e = exception return if (e == null) { if (isCanceled) { throw CancellationException("Task $this was cancelled normally.") } else { @Suppress("UNCHECKED_CAST") result as T } } else { throw e } } return suspendCancellableCoroutine { cont -> addOnCompleteListener { val e = exception if (e == null) { @Suppress("UNCHECKED_CAST") if (isCanceled) cont.cancel() else cont.resume(result as T) } else { cont.resumeWithException(e) } } } }