Solution: Correct mistakes with cancellation
There are two changes that need to be made:
- We should not block suspending function unless we set a dispatcher that can be used for blocking operations. This is because suspending functions should not be blocking (they should be dispatcher-agnostic). To correct it, we should wrap the whole function with
withContext
with a dispatcher that can be used for blocking operations, likeDispatchers.IO
. - We should use
yield
between two blocking operations, to allow cancellation and redispatching between them. - We should wrap
revertUnfinishedTransactions
with the blockwithContext(NonCancellable)
so it (a suspending function) can be executed (otherwise a suspension would throw an exception in cancelled coroutine). - We should rethrow the
CancellationException
, because the functions that callupdateUser
might also want to specify what to do when the operation is cancelled.
Notice, that using async
here is not useful, because all operations must be called sequentially.
There are three changes that need to be made:
- We should use a dispatcher that can be used for blocking operations, like
Dispatchers.IO
, because we have blocking operationsreadText
anddelete
. - We should make sure we rethrow the
CancellationException
. - We should use
yield
between a blocking and CPU operations, to allow cancellation between them (though this is not such important, ascalculateSignature
is likely lightweight, and there is a suspending call straight after it).
Notice, that using async
here is not useful, because all operations must be called sequentially.
There is one change that needs to be made:
- We should rethrow the
CancellationException
.
That example is especially interesting, because if we do not rethrow the CancellationException
, in case of cancellation, this function will be in an infinite loop. You can use the following code to see it yourself: