I want to catch an exception that is thrown from async coroutines.
The following code demonstrates a problem:
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
try {
println(failedConcurrentSum())
} catch (e: ArithmeticException) {
println("Computation failed with ArithmeticException")
}
}
suspend fun failedConcurrentSum() = coroutineScope {
try {
val one = async {
try {
delay(1000L)
42
} finally {
println("First child was cancelled")
}
}
val two = async<Int> {
println("Second child throws an exception")
throw ArithmeticException()
}
one.await() + two.await()
} catch (e: ArithmeticException) {
println("Using a default value...")
0
}
}
This prints:
Second child throws an exception
First child was cancelled
Computation failed with ArithmeticException
The try-catch inside the failedConcurrentSum doesn't handle the exception thrown by val two.
I can convince myself that this is due to the "structured concurrency".
However, this doesn't explain why wrapping the async's inside a coroutineScope catches the exception:
suspend fun failedConcurrentSum() = coroutineScope {
try {
val one = coroutineScope {
async {
try {
delay(1000L)
42
} finally {
println("First child was cancelled")
}
}
}
val two = coroutineScope {
async<Int> {
println("Second child throws an exception")
throw ArithmeticException()
}
}
one.await() + two.await()
} catch (e: ArithmeticException) {
println("Using a default value...")
0
}
}
This prints:
First child was cancelled
Second child throws an exception
Using a default value...
0
Why does the latter catches an exception whereas the first doesn't?
coroutineScope is just a function, it sets up its scope internally and from the outside it always completes like a regular function, not messing with any outer scopes. This is because it doesn't leak any concurrent coroutines started within its scope. You can always reliably catch and handle exceptions thrown by coroutineScope.
async, on the other hand, completes immediately after launching the coroutine, therefore you have two concurrent coroutines: one running the async code and another calling the corresponding await. Since the async one is also the child of the one calling await, its failure cancels the parent before the parent's await call completes.
The try-catch inside the failedConcurrentSum doesn't handle the exception thrown by val two.
It actually does, if it gets the chance. But since the try-catch block is in a coroutine that runs concurrently to the one completing val two's Deferred, it just doesn't get the chance to do so before being cancelled due to the failure of the child coroutine.
coroutineScope uses Job
By default, a failure of any of the job’s children leads to an immediate failure of its parent and cancellation of the rest of its children.
Job
You can use supervisorScope instead of coroutineScope
A failure or cancellation of a child does not cause the supervisor job to fail and does not affect its other children.
SupervisorJob
but you have to wait for the completion of the first async block.
Use coroutineScope inside try catch to return a default value immediately when an exception occurs
suspend fun failedConcurrentSum() = try {
coroutineScope {
val one = async {
try {
delay(1000L)
42
} finally {
println("First child was cancelled")
}
}
val two = async<Int> {
println("Second child throws an exception")
throw ArithmeticException()
}
one.await() + two.await()
}
} catch (e: ArithmeticException) {
println("Using a default value...")
0
}
Related
Firebase anonymous sign in returns a task (which is basically Google promise implementation):
val task:Task<AuthResult> = FirebaseAuth.getInstance().signInAnonymously()
How it would be possible create a signInAnonymous wrapper where:
It is a suspend function, waiting for the task completion
suspend fun signInAnonymous(): Unit
It returns a Deferred object, delivering the result asynchronously
fun signInAnonymous() : Deferred
The package kotlinx.coroutines.tasks now includes the follwing utility functions:
public suspend fun <T> Task<T>.await(): T { ... }
From the docs:
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 fun <T> Task<T>.asDeferred(): Deferred<T> { ... }
From the docs:
Converts this task to an instance of Deferred.
If task is cancelled then resulting deferred will be cancelled as well.
So you can just do:
suspend fun signInAnonymouslyAwait(): AuthResult {
return FirebaseAuth.getInstance().signInAnonymously().await()
}
or:
fun signInAnonymouslyDeferred(): Deferred<AuthResult> {
return FirebaseAuth.getInstance().signInAnonymously().asDeferred()
}
Based on this GitHub library, here's a way to transform a Task into a suspending function in the "usual" way to adapt callback based async calls to coroutines:
suspend fun <T> Task<T>.await(): T = suspendCoroutine { continuation ->
addOnCompleteListener { task ->
if (task.isSuccessful) {
continuation.resume(task.result)
} else {
continuation.resumeWithException(task.exception ?: RuntimeException("Unknown task exception"))
}
}
}
You can also wrap it in a Deferred of course, CompletableDeferred comes in handy here:
fun <T> Task<T>.asDeferred(): Deferred<T> {
val deferred = CompletableDeferred<T>()
deferred.invokeOnCompletion {
if (deferred.isCancelled) {
// optional, handle coroutine cancellation however you'd like here
}
}
this.addOnSuccessListener { result -> deferred.complete(result) }
this.addOnFailureListener { exception -> deferred.completeExceptionally(exception) }
return deferred
}
Add this to gradle
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.4.3'
And then you can use it like this:
suspend fun login(email: String, pass: String) {
FirebaseAuth.getInstance().signInWithEmailAndPassword(email, pass).await()
}
To transform it into a coroutine-ready function, I would use the Tasks.await() function from the Tasks API:
suspend fun FirebaseAuth.signInAnonymouslyAwait(): AuthResult {
return Tasks.await(this.signInAnonymously())
}
As for Deferred, i'd stick with zsmb13's answer
I'm trying to get my head around suspendCoroutine and suspendCancellableCoroutine. I think they could be useful in the following case:
When the coroutine is launched, check if the user is logged in.
If not, ask for credentials and pause the currently executing coroutine.
When the credentials are submitted, resume the coroutine from the same line where it was suspended.
This compiles but never makes it past "delay over", i.e. the continuation never resumes:
import kotlinx.coroutines.*
fun main(args: Array<String>) {
println("Hello, world!")
runBlocking {
launch {
postComment()
}
}
}
var isLoggedIn = false
var loginContinuation: CancellableContinuation<Unit>? = null
suspend fun postComment() {
if (!isLoggedIn) {
showLoginForm()
suspendCancellableCoroutine<Unit> {
loginContinuation = it
}
}
// call the api or whatever
delay(1000)
println("comment posted!")
}
suspend fun showLoginForm() {
println("show login form")
// simulate delay while user enters credentials
delay(1000)
println("delay over")
isLoggedIn = true
// resume coroutine on submit
loginContinuation?.resume(Unit) { println("login cancelled") }
}
I've tried everything I can think of, including moving the call to suspendCancellableCoroutine outside of the login check, wrapping the contents of showLoginForm in withContext(Dispatchers.IO), using coroutineScope.launch(newSingleThreadContext("MyOwnThread"), etc. The impression I get from reading the internet is that this is a valid use case. What am I doing wrong?
First of all, you misunderstand the concept of suspend functions. Calling function showLoginForm() does not start a new coroutine. Code in a single coroutine is always executed sequentially - at first you call showLoginForm(), it delays, it does not resume any continuations because loginContinuation is null, and then suspendCancellableCoroutine suspends your coroutine forever and causes a deadlock.
Starting a new coroutine that executes showLoginForm() can make your code work:
suspend fun CoroutineScope.postComment() {
if (!isLoggedIn) {
launch {
showLoginForm()
}
suspendCancellableCoroutine<Unit> {
loginContinuation = it
}
}
// call the api or whatever
delay(1000)
println("comment posted!")
}
This code still can fail (*), but in this particular case it does not. Working version of this code can look like this:
import kotlin.coroutines.*
import kotlinx.coroutines.*
fun main(args: Array<String>) {
println("Hello, world!")
runBlocking {
postComment()
}
}
var isLoggedIn = false
suspend fun CoroutineScope.postComment() {
if (!isLoggedIn) {
suspendCancellableCoroutine<Unit> { continuation ->
launch {
showLoginForm(continuation)
}
}
}
delay(1000)
println("comment posted!")
}
suspend fun showLoginForm(continuation: CancellableContinuation<Unit>) {
println("show login form")
delay(1000)
println("delay over")
isLoggedIn = true
continuation.resume(Unit) { println("login cancelled") }
}
Also, in your example suspending coroutines is not needed. Why do we need another coroutine if we can just execute its code in the same coroutine? We need to wait until it finishes anyway. Since coroutines execute code sequentially, we will go to the code after if branch only after showLoginForm() finishes:
var isLoggedIn = false
suspend fun postComment() {
if (!isLoggedIn) {
showLoginForm()
}
delay(1000)
println("comment posted!")
}
suspend fun showLoginForm() {
println("show login form")
delay(1000)
println("delay over")
isLoggedIn = true
}
This approach is the best for your example, where all code is sequential.
(*) - This code still can cause deadlock if suspendCancellableCoroutine is invoked after showLoginForm finishes - for example, if you remove delay call in showLoginForm or if you use a multithreaded dispatcher - in JVM there is no guarantee that suspendCancellableCoroutine will be invoked earlier than showLoginForm. Moreover, loginContinuation is not #Volatile, so with multithreaded dispatcher the code can fail also from visibility issues - thread that executes showLoginForm may observe that loginContinuation is null.
Passing around Continuations is messy and can easily lead to the error you have...one function finishes before the continuation has even been assigned to the continuation property.
Since the login form is what you want to turn into a suspend function, that's where you should use suspendCoroutine. suspendCoroutine is low level code that you should put as low as possible so your main program logic can use easy-to-read sequential coroutines without the nested launch/suspendCoroutine calls.
var isLoggedIn = false
suspend fun postComment() {
if (!isLoggedIn) {
showLoginForm()
}
println("is logged in: $isLoggedIn")
if (isLoggedIn) {
// call the api or whatever
delay(1000)
println("comment posted!")
}
}
suspend fun showLoginForm(): Unit = suspendCancellableCoroutine { cont ->
println("Login or leave blank to cancel:")
//Simulate user login or cancel with console input
val userInput = readLine()
isLoggedIn = !userInput.isNullOrBlank()
cont.resume(Unit)
}
I didn't use delay() in showLoginForm() because you can't call suspend functions within a suspendCancellableCoroutine block. Those last three lines could also be wrapped in a scope.launch and use delay instead of readLine, but in reality, your UI interaction wouldn't be a coroutine with a delay anyway.
EDIT:
Trying to pass a continuation to another Activity would be especially messy. Google does not even recommend using multiple Activities in an app because it is difficult to pass objects between them. To do it with Fragments, you could maybe write your LoginFragment class to have a private continuation property like this:
class LoginFragment(): Fragment {
private val continuation: Continuation<Boolean>? = null
private var loginComplete = false
suspend fun show(manager: FragmentManager, #IdRes containerViewId: Int, tag: String? = null): Boolean = suspendCancelableCoroutine { cont ->
continuation = cont
retainInstance = true
manager.beginTransaction().apply {
replace(containerViewId, this#LoginFragment, tag)
addToBackStack(null)
commit()
}
}
// Call this when login is complete:
private fun onLoginSuccessful() {
loginComplete = true
activity?.fragmentManager?.popBackStack()
}
override fun onDestroy() {
super.onDestroy()
continuation?.resume(loginComplete)
}
}
Then you would show this fragment from another fragment like this:
lifecycleScope.launch {
val loggedIn = LoginFragment().show(requireActivity().fragmentManager, R.id.fragContainer)
// respond to login state here
}
So long as you are using a Fragment's lifecycleScope rather than an Activity's lifecycleScope and the first Fragment also uses retainInstance = true, I think you should be safe from screen rotations. But I haven't done this myself.
I wrote a little code to download some files from internet..if user click on cancel button this must be stopped..i use the cancel() method for do it..but it didn't work.
ScheduledService<Object> service = new ScheduledService<Object>() {
protected Task<Object> createTask() {
return new Task<Object>() {
protected Object call() {
if (checkinternet()) {
downloadFiles();
}
return null;
}
};
}
};
service.start();
In buttons action event handler i called cancel method for stop service..
but it wasn't successful..
service.cancel();
How do i do that...
There is no automatic way to cancel a task or service.
From the documentation (https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm):
Cancelling the Task.
There is no reliable way in Java to stop a thread in process. However, the task must stop processing whenever cancel is called on the task. The task is supposed to check periodically during its work whether it was cancelled by using the isCancelled method within the body of the call method.
The example referenced in the above block looks like this:
Task<Integer> task = new Task<Integer>() {
#Override protected Integer call() throws Exception {
int iterations;
for (iterations = 0; iterations < 100000; iterations++) {
if (isCancelled()) {
break;
}
System.out.println("Iteration " + iterations);
}
return iterations;
}
};
So you will have to implement the cancel logic by yourself, unfortunately.
I have a task created as follows.
var task = Task.Factory.FromAsync<Request, Response>(
service.BeginOp,
service.EndOp,
request,
null);
When I await the task in a try/catch, the exception is not caught.
try
{
await task;
}
catch (Exception e)
{
// Block never reached
}
However when I use ContinueWith(), the exception is caught.
await task.ContinueWith(t =>
{
if (t.Exception != null)
{
// Block reached
}
});
Why is it not caught in the first case? I have try/caught other Tasks and it catches the exception.
I suspect that further up your call stack, your code is calling Task<T>.Result or Task.Wait. This will cause a deadlock, as I explain on my blog.
By default, await will capture a "context" whenever it awaits a Task. In this case, the "context" is the ASP.NET request context, which only allows one thread in at a time. If your code blocks a thread within that request context (e.g., by calling Result/Wait), then when the task completes, it cannot resume executing the async method because the context only allows one thread in.
Turns out that in the above callstack I was not using await. I am not sure why the compiler allowed this. Adding the await fixed the problem.
I am new to Windows Workflow and trying to write a Long Running process.
I am also trying to limit how long this process can run for.
I am calling WorkflowInvoker.Invoke to trigger my Workflow passing it a small timespan for testing.
If I try this certain activities, this seems to work perfectly.
But If I use a CodeActivity, it seems to ignore my timeout entirely.
Why is this? And how do I cause my CodeActivity to timeout if it takes too long?
An example working with a Delay Activity:
(In this example the TimeOutException is thrown)
Activity wf = new Sequence()
{
Activities =
{
new Delay()
{
Duration = TimeSpan.FromSeconds(10)
},
}
};
try
{
WorkflowInvoker.Invoke(wf, TimeSpan.FromSeconds(5));
}
catch (TimeoutException ex)
{
Console.WriteLine(ex.Message);
}
An example trying to use a CodeActivity:
(In this example the TimeOutException is not thrown)
public class LongActivity : CodeActivity
{
protected override void Execute(CodeActivityContext context)
{
Thread.Sleep(TimeSpan.FromSeconds(10));
}
}
Activity wf = new Sequence()
{
Activities =
{
new LongActivity()
}
};
try
{
WorkflowInvoker.Invoke(wf, TimeSpan.FromSeconds(5));
}
catch (TimeoutException ex)
{
Console.WriteLine(ex.Message);
}
The workflow runtime can only take action when it is in charge and if your activity takes 10 seconds to execute, or sleep, the runtime can't do anything about it. It won't schedule any new activities though because there is no remaining time left and would throw a TimeoutException instead.
Normally when you have long running work you would use an asynchronous activity, either an AsyncCodeActivity or a NativeActivity with a bookmark so the runtime is in control and can abort the workflow.