Add asyncSpawn() procedure and tests. (#123)

Deprecated asyncDiscard() procedure.
Bump version to 2.5.2.
This commit is contained in:
Eugene Kabanov 2020-09-01 21:41:18 +03:00 committed by GitHub
parent e6d50b7736
commit a5442edfc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 98 additions and 2 deletions

View File

@ -1,5 +1,5 @@
packageName = "chronos"
version = "2.5.1"
version = "2.5.2"
author = "Status Research & Development GmbH"
description = "Chronos"
license = "Apache License 2.0 or MIT"

View File

@ -512,7 +512,48 @@ proc asyncCheck*[T](future: Future[T]) =
raise future.error
future.callback = cb
proc asyncDiscard*[T](future: Future[T]) = discard
proc asyncSpawn*(future: Future[void]) =
## Spawns a new concurrent async task.
##
## Tasks may not raise exceptions or be cancelled - a ``Defect`` will be
## raised when this happens.
##
## This should be used instead of ``discard`` and ``asyncCheck`` when calling
## an ``async`` procedure without ``await``, to ensure exceptions in the
## returned future are not silently discarded.
##
## Note, that if passed ``future`` is already finished, it will be checked
## and processed immediately.
doAssert(not isNil(future), "Future is nil")
template getFutureLocation(): string =
let loc = future.location[0]
"[" & (
if len(loc.procedure) == 0: "[unspecified]" else: $loc.procedure & "()"
) & " at " & $loc.file & ":" & $(loc.line) & "]"
template getErrorMessage(): string =
"Asynchronous task " & getFutureLocation() &
" finished with an exception \"" & $future.error.name & "\"!"
template getCancelMessage(): string =
"Asynchronous task " & getFutureLocation() & " was cancelled!"
proc cb(data: pointer) =
if future.failed():
raise newException(FutureDefect, getErrorMessage())
elif future.cancelled():
raise newException(FutureDefect, getCancelMessage())
if not(future.finished()):
# We adding completion callback only if ``future`` is not finished yet.
future.addCallback(cb)
else:
if future.failed():
raise newException(FutureDefect, getErrorMessage())
elif future.cancelled():
raise newException(FutureDefect, getCancelMessage())
proc asyncDiscard*[T](future: Future[T]) {.deprecated.} = discard
## This is async workaround for discard ``Future[T]``.
proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] {.

View File

@ -969,6 +969,58 @@ suite "Future[T] behavior test suite":
proc testCancellationRace(): bool =
waitFor(testCancellationRaceAsync())
proc testAsyncSpawnAsync(): Future[bool] {.async.} =
proc completeTask1() {.async.} =
discard
proc completeTask2() {.async.} =
await sleepAsync(100.milliseconds)
proc errorTask() {.async.} =
if true:
raise newException(ValueError, "")
proc cancelTask() {.async.} =
await sleepAsync(10.seconds)
try:
var fut1 = completeTask1()
var fut2 = completeTask2()
asyncSpawn fut1
asyncSpawn fut2
await sleepAsync(200.milliseconds)
if not(fut1.finished()) or not(fut2.finished()):
return false
if fut1.failed() or fut1.cancelled() or fut2.failed() or fut2.cancelled():
return false
except:
return false
try:
asyncSpawn errorTask()
return false
except FutureDefect:
discard
except:
return false
try:
var fut = cancelTask()
await cancelAndWait(fut)
asyncSpawn fut
return false
except FutureDefect:
discard
except:
return false
return true
proc testAsyncSpawn(): bool =
waitFor(testAsyncSpawnAsync())
test "Async undefined behavior (#7758) test":
check test1() == true
test "Immediately completed asynchronous procedure test":
@ -1022,3 +1074,6 @@ suite "Future[T] behavior test suite":
check testWithTimeout() == true
test "Cancellation race test":
check testCancellationRace() == true
test "asyncSpawn() test":
check testAsyncSpawn() == true