Add asyncSpawn() procedure and tests. (#123)
Deprecated asyncDiscard() procedure. Bump version to 2.5.2.
This commit is contained in:
parent
e6d50b7736
commit
a5442edfc0
|
@ -1,5 +1,5 @@
|
||||||
packageName = "chronos"
|
packageName = "chronos"
|
||||||
version = "2.5.1"
|
version = "2.5.2"
|
||||||
author = "Status Research & Development GmbH"
|
author = "Status Research & Development GmbH"
|
||||||
description = "Chronos"
|
description = "Chronos"
|
||||||
license = "Apache License 2.0 or MIT"
|
license = "Apache License 2.0 or MIT"
|
||||||
|
|
|
@ -512,7 +512,48 @@ proc asyncCheck*[T](future: Future[T]) =
|
||||||
raise future.error
|
raise future.error
|
||||||
future.callback = cb
|
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]``.
|
## This is async workaround for discard ``Future[T]``.
|
||||||
|
|
||||||
proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] {.
|
proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] {.
|
||||||
|
|
|
@ -969,6 +969,58 @@ suite "Future[T] behavior test suite":
|
||||||
proc testCancellationRace(): bool =
|
proc testCancellationRace(): bool =
|
||||||
waitFor(testCancellationRaceAsync())
|
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":
|
test "Async undefined behavior (#7758) test":
|
||||||
check test1() == true
|
check test1() == true
|
||||||
test "Immediately completed asynchronous procedure test":
|
test "Immediately completed asynchronous procedure test":
|
||||||
|
@ -1022,3 +1074,6 @@ suite "Future[T] behavior test suite":
|
||||||
check testWithTimeout() == true
|
check testWithTimeout() == true
|
||||||
test "Cancellation race test":
|
test "Cancellation race test":
|
||||||
check testCancellationRace() == true
|
check testCancellationRace() == true
|
||||||
|
|
||||||
|
test "asyncSpawn() test":
|
||||||
|
check testAsyncSpawn() == true
|
||||||
|
|
Loading…
Reference in New Issue