cancelAndWait(): add comments (#112)

The cancellation process has a very complicated control flow, split over
different files and hidden by macros. These comments should help shed a
light on this madness.
This commit is contained in:
Ștefan Talpalaru 2020-07-30 02:47:11 +02:00 committed by GitHub
parent 3968f09ae1
commit f5634f499d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 16 additions and 5 deletions

View File

@ -314,6 +314,10 @@ template cancelAndSchedule*[T](future: Future[T]) =
proc cancel(future: FutureBase, loc: ptr SrcLoc) =
if not(future.finished()):
# Cancel the bottom-most child. When that happens, its parent's `await` call
# will raise CancelledError. Some macro will catch that and call
# `cancelAndSchedule()` on that parent, thus propagating the cancellation
# up the chain.
if not(isNil(future.child)):
cancel(future.child, getSrcLocation())
future.mustCancel = true
@ -730,16 +734,23 @@ proc oneValue*[T](futs: varargs[Future[T]]): Future[T] {.
return retFuture
proc cancelAndWait*[T](future: Future[T]): Future[void] =
## Cancel future ``future`` and wait until it completes.
var retFuture = newFuture[void]("chronos.cancelAndWait(T)")
proc cancelAndWait*[T](fut: Future[T]): Future[void] =
## Cancel ``fut`` and wait until it completes, in case it already
## ``await``s on another Future.
# When `retFuture` completes, `fut` and all its children have been
# cancelled. If `fut` doesn't have any children, the `continuation()` callback
# runs immediately, without control getting back to the dispatcher.
var retFuture = newFuture[void]("chronos.cancelAndWait(T)")
proc continuation(udata: pointer) {.gcsafe.} =
if not(retFuture.finished()):
retFuture.complete()
fut.addCallback(continuation)
# Start the cancellation process. If `fut` has children, multiple event loop
# steps will be needed for it to complete.
fut.cancel()
future.addCallback(continuation)
future.cancel()
return retFuture
proc allFutures*[T](futs: varargs[Future[T]]): Future[void] =