2019-02-06 14:49:11 +00:00
|
|
|
# Chronos Test Suite
|
|
|
|
# (c) Copyright 2018-Present
|
2018-05-22 23:28:16 +00:00
|
|
|
# Status Research & Development GmbH
|
|
|
|
#
|
|
|
|
# Licensed under either of
|
|
|
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
|
|
|
# MIT license (LICENSE-MIT)
|
exception tracking (#166)
* exception tracking
This PR adds minimal exception tracking to chronos, moving the goalpost
one step further.
In particular, it becomes invalid to raise exceptions from `callSoon`
callbacks: this is critical for writing correct error handling because
there's no reasonable way that a user of chronos can possibly _reason_
about exceptions coming out of there: the event loop will be in an
indeterminite state when the loop is executing an _random_ callback.
As expected, there are several issues in the error handling of chronos:
in particular, it will end up in an inconsistent internal state whenever
the selector loop operations fail, because the internal state update
functions are not written in an exception-safe way. This PR turns this
into a Defect, which probably is not the optimal way of handling things
- expect more work to be done here.
Some API have no way of reporting back errors to callers - for example,
when something fails in the accept loop, there's not much it can do, and
no way to report it back to the user of the API - this has been fixed
with the new accept flow - the old one should be deprecated.
Finally, there is information loss in the API: in composite operations
like `poll` and `waitFor` there's no way to differentiate internal
errors from user-level errors originating from callbacks.
* store `CatchableError` in future
* annotate proc's with correct raises information
* `selectors2` to avoid non-CatchableError IOSelectorsException
* `$` should never raise
* remove unnecessary gcsafe annotations
* fix exceptions leaking out of timer waits
* fix some imports
* functions must signal raising the union of all exceptions across all
platforms to enable cross-platform code
* switch to unittest2
* add `selectors2` which supercedes the std library version and fixes
several exception handling issues in there
* fixes
* docs, platform-independent eh specifiers for some functions
* add feature flag for strict exception mode
also bump version to 3.0.0 - _most_ existing code should be compatible
with this version of exception handling but some things might need
fixing - callbacks, existing raises specifications etc.
* fix AsyncCheck for non-void T
2021-03-24 09:08:33 +00:00
|
|
|
import unittest2
|
2019-02-06 14:49:11 +00:00
|
|
|
import ../chronos
|
2018-05-22 23:28:16 +00:00
|
|
|
|
2019-10-24 13:01:57 +00:00
|
|
|
when defined(nimHasUsed): {.used.}
|
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
suite "Future[T] behavior test suite":
|
|
|
|
proc testFuture1(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(0.milliseconds)
|
2018-05-22 23:28:16 +00:00
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
proc testFuture2(): Future[int] {.async.} =
|
|
|
|
return 1
|
2018-05-22 23:28:16 +00:00
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
proc testFuture3(): Future[int] {.async.} =
|
|
|
|
result = await testFuture2()
|
2018-05-22 23:28:16 +00:00
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
proc testFuture100(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
2019-03-24 16:57:36 +00:00
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
proc testFuture4(): Future[int] {.async.} =
|
|
|
|
## Test for not immediately completed future and timeout = -1
|
2018-07-20 08:58:01 +00:00
|
|
|
result = 0
|
2019-03-30 22:31:10 +00:00
|
|
|
try:
|
2019-10-29 21:19:41 +00:00
|
|
|
discard await wait(testFuture1(), InfiniteDuration)
|
2019-03-30 22:31:10 +00:00
|
|
|
result = 1
|
|
|
|
except:
|
|
|
|
result = 0
|
2018-07-20 08:58:01 +00:00
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
if result == 0:
|
|
|
|
return -1
|
2018-07-20 08:58:01 +00:00
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
## Test for immediately completed future and timeout = -1
|
2018-07-20 08:58:01 +00:00
|
|
|
result = 0
|
2019-03-30 22:31:10 +00:00
|
|
|
try:
|
2019-10-29 21:19:41 +00:00
|
|
|
discard await wait(testFuture2(), InfiniteDuration)
|
2019-03-30 22:31:10 +00:00
|
|
|
result = 2
|
|
|
|
except:
|
|
|
|
result = 0
|
2018-07-20 08:58:01 +00:00
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
if result == 0:
|
|
|
|
return -2
|
2019-03-15 00:54:16 +00:00
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
## Test for not immediately completed future and timeout = 0
|
|
|
|
result = 0
|
|
|
|
try:
|
2019-10-29 21:19:41 +00:00
|
|
|
discard await wait(testFuture1(), 0.milliseconds)
|
2019-03-30 22:31:10 +00:00
|
|
|
except AsyncTimeoutError:
|
|
|
|
result = 3
|
2019-03-15 00:54:16 +00:00
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
if result == 0:
|
|
|
|
return -3
|
2019-03-15 00:54:16 +00:00
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
## Test for immediately completed future and timeout = 0
|
|
|
|
result = 0
|
|
|
|
try:
|
2019-10-29 21:19:41 +00:00
|
|
|
discard await wait(testFuture2(), 0.milliseconds)
|
2019-03-30 22:31:10 +00:00
|
|
|
result = 4
|
|
|
|
except:
|
|
|
|
result = 0
|
2019-03-15 00:54:16 +00:00
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
if result == 0:
|
|
|
|
return -4
|
2019-03-15 00:54:16 +00:00
|
|
|
|
2019-03-30 22:31:10 +00:00
|
|
|
## Test for future which cannot be completed in timeout period
|
|
|
|
result = 0
|
|
|
|
try:
|
2019-10-29 21:19:41 +00:00
|
|
|
discard await wait(testFuture100(), 50.milliseconds)
|
2019-03-30 22:31:10 +00:00
|
|
|
except AsyncTimeoutError:
|
|
|
|
result = 5
|
|
|
|
|
|
|
|
if result == 0:
|
|
|
|
return -5
|
|
|
|
|
|
|
|
## Test for future which will be completed before timeout exceeded.
|
|
|
|
try:
|
2019-10-29 21:19:41 +00:00
|
|
|
discard await wait(testFuture100(), 500.milliseconds)
|
2019-03-30 22:31:10 +00:00
|
|
|
result = 6
|
|
|
|
except:
|
|
|
|
result = -6
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "Async undefined behavior (#7758) test":
|
2019-03-30 22:31:10 +00:00
|
|
|
var fut = testFuture1()
|
|
|
|
poll()
|
|
|
|
poll()
|
|
|
|
if not fut.finished:
|
|
|
|
poll()
|
2021-06-04 10:28:04 +00:00
|
|
|
check: fut.finished
|
2019-03-30 22:31:10 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "Immediately completed asynchronous procedure test":
|
2019-03-30 22:31:10 +00:00
|
|
|
var fut = testFuture3()
|
2021-06-04 10:28:04 +00:00
|
|
|
check: fut.finished
|
2019-03-30 22:31:10 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "Future[T] callbacks are invoked in reverse order (#7197) test":
|
2019-03-30 22:31:10 +00:00
|
|
|
var testResult = ""
|
|
|
|
var fut = testFuture1()
|
|
|
|
fut.addCallback proc(udata: pointer) =
|
|
|
|
testResult &= "1"
|
|
|
|
fut.addCallback proc(udata: pointer) =
|
|
|
|
testResult &= "2"
|
|
|
|
fut.addCallback proc(udata: pointer) =
|
|
|
|
testResult &= "3"
|
|
|
|
fut.addCallback proc(udata: pointer) =
|
|
|
|
testResult &= "4"
|
|
|
|
fut.addCallback proc(udata: pointer) =
|
|
|
|
testResult &= "5"
|
|
|
|
discard waitFor(fut)
|
|
|
|
poll()
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
fut.finished
|
|
|
|
testResult == "12345"
|
|
|
|
|
|
|
|
test "Future[T] callbacks not changing order after removeCallback()":
|
2019-03-30 22:31:10 +00:00
|
|
|
var testResult = ""
|
|
|
|
var fut = testFuture1()
|
|
|
|
proc cb1(udata: pointer) =
|
|
|
|
testResult &= "1"
|
|
|
|
proc cb2(udata: pointer) =
|
|
|
|
testResult &= "2"
|
|
|
|
proc cb3(udata: pointer) =
|
|
|
|
testResult &= "3"
|
|
|
|
proc cb4(udata: pointer) =
|
|
|
|
testResult &= "4"
|
|
|
|
proc cb5(udata: pointer) =
|
|
|
|
testResult &= "5"
|
|
|
|
fut.addCallback cb1
|
|
|
|
fut.addCallback cb2
|
|
|
|
fut.addCallback cb3
|
|
|
|
fut.addCallback cb4
|
|
|
|
fut.addCallback cb5
|
|
|
|
fut.removeCallback cb3
|
|
|
|
discard waitFor(fut)
|
|
|
|
poll()
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
fut.finished
|
|
|
|
testResult == "1245"
|
2019-03-30 22:31:10 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "wait[T]() test":
|
|
|
|
check:
|
|
|
|
waitFor(testFuture4()) == 6
|
2019-03-30 22:31:10 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "discard result Future[T] test":
|
2019-07-04 12:04:59 +00:00
|
|
|
var completedFutures = 0
|
|
|
|
|
|
|
|
proc client1() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc client2() {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc client3() {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc client4() {.async.} =
|
|
|
|
await sleepAsync(400.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc client5() {.async.} =
|
|
|
|
await sleepAsync(500.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc client1f() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client2f() {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client3f() {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client4f() {.async.} =
|
|
|
|
await sleepAsync(400.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client5f() {.async.} =
|
|
|
|
await sleepAsync(500.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
2020-09-03 11:37:53 +00:00
|
|
|
discard client1()
|
|
|
|
discard client1f()
|
|
|
|
discard client2()
|
|
|
|
discard client2f()
|
|
|
|
discard client3()
|
|
|
|
discard client3f()
|
|
|
|
discard client4()
|
|
|
|
discard client4f()
|
|
|
|
discard client5()
|
|
|
|
discard client5f()
|
2019-07-04 12:04:59 +00:00
|
|
|
|
|
|
|
waitFor(sleepAsync(2000.milliseconds))
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
completedFutures == 10
|
2019-07-04 12:04:59 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "allFutures(zero) test":
|
2019-07-04 12:04:59 +00:00
|
|
|
var tseq = newSeq[Future[int]]()
|
|
|
|
var fut = allFutures(tseq)
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
fut.finished
|
2019-07-04 12:04:59 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "allFutures(varargs) test":
|
2019-03-30 22:31:10 +00:00
|
|
|
var completedFutures = 0
|
|
|
|
|
|
|
|
proc vlient1() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc vlient2() {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc vlient3() {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc vlient4() {.async.} =
|
|
|
|
await sleepAsync(400.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc vlient5() {.async.} =
|
|
|
|
await sleepAsync(500.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc vlient1f() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc vlient2f() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc vlient3f() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc vlient4f() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc vlient5f() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client1(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client2(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client3(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client4(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(400.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client5(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(500.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client1f(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client2f(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client3f(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client4f(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(400.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client5f(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(500.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
waitFor(allFutures(vlient1(), vlient2(), vlient3(), vlient4(), vlient5()))
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check: completedFutures == 5
|
2019-03-30 22:31:10 +00:00
|
|
|
completedFutures = 0
|
2021-06-04 10:28:04 +00:00
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
waitFor(allFutures(vlient1(), vlient1f(),
|
|
|
|
vlient2(), vlient2f(),
|
|
|
|
vlient3(), vlient3f(),
|
|
|
|
vlient4(), vlient4f(),
|
|
|
|
vlient5(), vlient5f()))
|
2021-06-04 10:28:04 +00:00
|
|
|
|
|
|
|
check: completedFutures == 10
|
2019-03-30 22:31:10 +00:00
|
|
|
|
|
|
|
completedFutures = 0
|
2019-07-04 12:04:59 +00:00
|
|
|
waitFor(allFutures(client1(), client2(), client3(), client4(), client5()))
|
2021-06-04 10:28:04 +00:00
|
|
|
check: completedFutures == 5
|
2019-03-30 22:31:10 +00:00
|
|
|
|
|
|
|
completedFutures = 0
|
2019-07-04 12:04:59 +00:00
|
|
|
waitFor(allFutures(client1(), client1f(),
|
|
|
|
client2(), client2f(),
|
|
|
|
client3(), client3f(),
|
|
|
|
client4(), client4f(),
|
|
|
|
client5(), client5f()))
|
2021-06-04 10:28:04 +00:00
|
|
|
check: completedFutures == 10
|
2019-03-30 22:31:10 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "allFutures(varargs) test":
|
2019-03-30 22:31:10 +00:00
|
|
|
var completedFutures = 0
|
|
|
|
var vfutures = newSeq[Future[void]]()
|
|
|
|
var nfutures = newSeq[Future[int]]()
|
|
|
|
|
|
|
|
proc vlient1() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc vlient2() {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc vlient3() {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc vlient4() {.async.} =
|
|
|
|
await sleepAsync(400.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc vlient5() {.async.} =
|
|
|
|
await sleepAsync(500.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
|
|
|
|
proc vlient1f() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc vlient2f() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc vlient3f() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc vlient4f() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc vlient5f() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client1(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client2(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client3(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client4(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(400.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client5(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(500.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client1f(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client2f(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client3f(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client4f(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(400.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client5f(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(500.milliseconds)
|
|
|
|
inc(completedFutures)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
vfutures.setLen(0)
|
|
|
|
for i in 0..<10:
|
|
|
|
vfutures.add(vlient1())
|
|
|
|
vfutures.add(vlient2())
|
|
|
|
vfutures.add(vlient3())
|
|
|
|
vfutures.add(vlient4())
|
|
|
|
vfutures.add(vlient5())
|
2019-03-15 00:54:16 +00:00
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
waitFor(allFutures(vfutures))
|
2019-03-30 22:31:10 +00:00
|
|
|
# 5 * 10 completed futures = 50
|
2021-06-04 10:28:04 +00:00
|
|
|
check: completedFutures == 50
|
2019-03-30 22:31:10 +00:00
|
|
|
|
|
|
|
completedFutures = 0
|
|
|
|
vfutures.setLen(0)
|
|
|
|
for i in 0..<10:
|
|
|
|
vfutures.add(vlient1())
|
|
|
|
vfutures.add(vlient1f())
|
|
|
|
vfutures.add(vlient2())
|
|
|
|
vfutures.add(vlient2f())
|
|
|
|
vfutures.add(vlient3())
|
|
|
|
vfutures.add(vlient3f())
|
|
|
|
vfutures.add(vlient4())
|
|
|
|
vfutures.add(vlient4f())
|
|
|
|
vfutures.add(vlient5())
|
|
|
|
vfutures.add(vlient5f())
|
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
waitFor(allFutures(vfutures))
|
2019-03-30 22:31:10 +00:00
|
|
|
# 10 * 10 completed futures = 100
|
2021-06-04 10:28:04 +00:00
|
|
|
check: completedFutures == 100
|
2019-03-30 22:31:10 +00:00
|
|
|
|
|
|
|
completedFutures = 0
|
|
|
|
nfutures.setLen(0)
|
|
|
|
for i in 0..<10:
|
|
|
|
nfutures.add(client1())
|
|
|
|
nfutures.add(client2())
|
|
|
|
nfutures.add(client3())
|
|
|
|
nfutures.add(client4())
|
|
|
|
nfutures.add(client5())
|
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
waitFor(allFutures(nfutures))
|
|
|
|
# 5 * 10 completed futures = 50
|
2021-06-04 10:28:04 +00:00
|
|
|
check: completedFutures == 50
|
2019-03-30 22:31:10 +00:00
|
|
|
|
|
|
|
completedFutures = 0
|
|
|
|
nfutures.setLen(0)
|
|
|
|
for i in 0..<10:
|
|
|
|
nfutures.add(client1())
|
|
|
|
nfutures.add(client1f())
|
|
|
|
nfutures.add(client2())
|
|
|
|
nfutures.add(client2f())
|
|
|
|
nfutures.add(client3())
|
|
|
|
nfutures.add(client3f())
|
|
|
|
nfutures.add(client4())
|
|
|
|
nfutures.add(client4f())
|
|
|
|
nfutures.add(client5())
|
|
|
|
nfutures.add(client5f())
|
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
waitFor(allFutures(nfutures))
|
2019-03-30 22:31:10 +00:00
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
# 10 * 10 completed futures = 100
|
2021-06-04 10:28:04 +00:00
|
|
|
check: completedFutures == 100
|
|
|
|
|
|
|
|
test "allFutures() already completed test":
|
|
|
|
proc client1(): Future[int] {.async.} =
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client2(): Future[int] {.async.} =
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
var fut = allFutures(client1(), client2())
|
|
|
|
check:
|
|
|
|
fut.finished()
|
|
|
|
not(fut.failed())
|
2019-03-30 22:31:10 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "allFinished() already completed test":
|
2020-04-06 10:56:24 +00:00
|
|
|
proc client1(): Future[int] {.async.} =
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client2(): Future[int] {.async.} =
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
var fut = allFinished(client1(), client2())
|
|
|
|
check:
|
|
|
|
fut.finished()
|
|
|
|
not(fut.failed())
|
|
|
|
len(fut.read()) == 2
|
2020-04-06 10:56:24 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "one(zero) test":
|
2019-03-30 22:31:10 +00:00
|
|
|
var tseq = newSeq[Future[int]]()
|
2019-07-04 12:04:59 +00:00
|
|
|
var fut = one(tseq)
|
2021-06-04 10:28:04 +00:00
|
|
|
check: fut.finished and fut.failed
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "one(varargs) test":
|
2019-06-04 16:51:35 +00:00
|
|
|
proc vlient1() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
|
|
|
|
proc vlient2() {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
|
|
|
|
proc vlient3() {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
|
|
|
|
proc client1(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
result = 10
|
|
|
|
|
|
|
|
proc client2(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
result = 20
|
|
|
|
|
|
|
|
proc client3(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
result = 30
|
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
var fut11 = vlient1()
|
|
|
|
var fut12 = vlient2()
|
|
|
|
var fut13 = vlient3()
|
|
|
|
var res1 = waitFor(one(fut11, fut12, fut13))
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
var fut21 = vlient2()
|
|
|
|
var fut22 = vlient1()
|
|
|
|
var fut23 = vlient3()
|
|
|
|
var res2 = waitFor(one(fut21, fut22, fut23))
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
var fut31 = vlient3()
|
|
|
|
var fut32 = vlient2()
|
|
|
|
var fut33 = vlient1()
|
|
|
|
var res3 = waitFor(one(fut31, fut32, fut33))
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
fut11 == res1
|
|
|
|
fut22 == res2
|
|
|
|
fut33 == res3
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
var cut11 = client1()
|
|
|
|
var cut12 = client2()
|
|
|
|
var cut13 = client3()
|
|
|
|
var res4 = waitFor(one(cut11, cut12, cut13))
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
var cut21 = client2()
|
|
|
|
var cut22 = client1()
|
|
|
|
var cut23 = client3()
|
|
|
|
var res5 = waitFor(one(cut21, cut22, cut23))
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
var cut31 = client3()
|
|
|
|
var cut32 = client2()
|
|
|
|
var cut33 = client1()
|
|
|
|
var res6 = waitFor(one(cut31, cut32, cut33))
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
cut11 == res4
|
|
|
|
cut22 == res5
|
|
|
|
cut33 == res6
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "one(seq) test":
|
2019-06-04 16:51:35 +00:00
|
|
|
proc vlient1() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
|
|
|
|
proc vlient2() {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
|
|
|
|
proc vlient3() {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
|
|
|
|
proc client1(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
result = 10
|
|
|
|
|
|
|
|
proc client2(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
result = 20
|
|
|
|
|
|
|
|
proc client3(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
result = 30
|
|
|
|
|
|
|
|
var v10 = vlient1()
|
|
|
|
var v11 = vlient2()
|
|
|
|
var v12 = vlient3()
|
2019-07-04 12:04:59 +00:00
|
|
|
var res1 = waitFor(one(@[v10, v11, v12]))
|
|
|
|
|
2019-06-04 16:51:35 +00:00
|
|
|
var v20 = vlient2()
|
|
|
|
var v21 = vlient1()
|
|
|
|
var v22 = vlient3()
|
2019-07-04 12:04:59 +00:00
|
|
|
var res2 = waitFor(one(@[v20, v21, v22]))
|
|
|
|
|
2019-06-04 16:51:35 +00:00
|
|
|
var v30 = vlient3()
|
|
|
|
var v31 = vlient2()
|
|
|
|
var v32 = vlient1()
|
2019-07-04 12:04:59 +00:00
|
|
|
var res3 = waitFor(one(@[v30, v31, v32]))
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
res1 == v10
|
|
|
|
res2 == v21
|
|
|
|
res3 == v32
|
2019-07-04 12:04:59 +00:00
|
|
|
|
|
|
|
var c10 = client1()
|
|
|
|
var c11 = client2()
|
|
|
|
var c12 = client3()
|
|
|
|
var res4 = waitFor(one(@[c10, c11, c12]))
|
|
|
|
|
|
|
|
var c20 = client2()
|
|
|
|
var c21 = client1()
|
|
|
|
var c22 = client3()
|
|
|
|
var res5 = waitFor(one(@[c20, c21, c22]))
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2019-07-04 12:04:59 +00:00
|
|
|
var c30 = client3()
|
|
|
|
var c31 = client2()
|
|
|
|
var c32 = client1()
|
|
|
|
var res6 = waitFor(one(@[c30, c31, c32]))
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
res4 == c10
|
|
|
|
res5 == c21
|
|
|
|
res6 == c32
|
2019-06-04 16:51:35 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "one(completed) test":
|
2020-04-06 10:56:24 +00:00
|
|
|
proc client1(): Future[int] {.async.} =
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client2(): Future[int] {.async.} =
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client3(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
result = 3
|
|
|
|
|
|
|
|
var f10 = client1()
|
|
|
|
var f20 = client2()
|
|
|
|
var f30 = client3()
|
|
|
|
var fut1 = one(f30, f10, f20)
|
|
|
|
var f11 = client1()
|
|
|
|
var f21 = client2()
|
|
|
|
var f31 = client3()
|
|
|
|
var fut2 = one(f31, f21, f11)
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
fut1.finished()
|
|
|
|
not(fut1.failed())
|
|
|
|
fut1.read() == f10
|
|
|
|
fut2.finished()
|
|
|
|
not(fut2.failed())
|
|
|
|
fut2.read() == f21
|
2020-04-06 10:56:24 +00:00
|
|
|
|
2020-04-21 04:07:49 +00:00
|
|
|
proc waitForNeLocal[T](fut: Future[T]): Future[T] =
|
|
|
|
## **Blocks** the current thread until the specified future completes.
|
|
|
|
while not(fut.finished()):
|
|
|
|
poll()
|
|
|
|
result = fut
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "or() test":
|
2020-04-21 04:07:49 +00:00
|
|
|
proc client1() {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
|
|
|
|
proc client2() {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
|
|
|
|
proc client3() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client4() {.async.} =
|
|
|
|
await sleepAsync(400.milliseconds)
|
|
|
|
if true:
|
|
|
|
raise newException(KeyError, "")
|
|
|
|
|
|
|
|
var f1 = waitForNeLocal(client1() or client2())
|
|
|
|
var f2 = waitForNeLocal(client2() or client1())
|
|
|
|
var f3 = waitForNeLocal(client1() or client4())
|
|
|
|
var f4 = waitForNeLocal(client2() or client4())
|
|
|
|
var f5 = waitForNeLocal(client1() or client3())
|
|
|
|
var f6 = waitForNeLocal(client3() or client1())
|
|
|
|
var f7 = waitForNeLocal(client2() or client4())
|
|
|
|
var f8 = waitForNeLocal(client4() or client2())
|
|
|
|
var f9 = waitForNeLocal(client3() or client4())
|
|
|
|
var f10 = waitForNeLocal(client4() or client3())
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
(f1.finished() and not(f1.failed()))
|
|
|
|
(f2.finished() and not(f2.failed()))
|
|
|
|
(f3.finished() and not(f3.failed()))
|
|
|
|
(f4.finished() and not(f4.failed()))
|
|
|
|
(f5.finished() and f5.failed())
|
|
|
|
(f6.finished() and f6.failed())
|
|
|
|
(f7.finished() and not(f7.failed()))
|
|
|
|
(f8.finished() and not(f8.failed()))
|
|
|
|
(f9.finished() and f9.failed())
|
|
|
|
(f10.finished() and f10.failed())
|
|
|
|
|
|
|
|
test "or() already completed test":
|
2020-04-21 04:07:49 +00:00
|
|
|
proc client1(): Future[int] {.async.} =
|
|
|
|
result = 1
|
|
|
|
proc client2(): Future[int] {.async.} =
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
proc client3(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
result = 3
|
|
|
|
|
|
|
|
var f1 = client1() or client2()
|
|
|
|
var f2 = client1() or client3()
|
|
|
|
var f3 = client2() or client3()
|
|
|
|
var f4 = client2() or client1()
|
|
|
|
var f5 = client3() or client1()
|
|
|
|
var f6 = client3() or client2()
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
(f1.finished() and not(f1.failed())) and
|
2020-04-21 04:07:49 +00:00
|
|
|
(f2.finished() and not(f2.failed())) and
|
|
|
|
(f3.finished() and f3.failed()) and
|
|
|
|
(f4.finished() and f4.failed()) and
|
|
|
|
(f5.finished() and not(f5.failed())) and
|
|
|
|
(f6.finished() and f6.failed())
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "cancel() async procedure test":
|
2019-06-26 12:36:01 +00:00
|
|
|
var completed = 0
|
|
|
|
|
|
|
|
proc client1() {.async.} =
|
|
|
|
await sleepAsync(1.seconds)
|
|
|
|
inc(completed)
|
|
|
|
|
|
|
|
proc client2() {.async.} =
|
|
|
|
await client1()
|
|
|
|
inc(completed)
|
|
|
|
|
|
|
|
proc client3() {.async.} =
|
|
|
|
await client2()
|
|
|
|
inc(completed)
|
|
|
|
|
|
|
|
proc client4() {.async.} =
|
|
|
|
await client3()
|
|
|
|
inc(completed)
|
|
|
|
|
|
|
|
var fut = client4()
|
|
|
|
fut.cancel()
|
|
|
|
|
|
|
|
# Future must not be cancelled immediately, because it has many nested
|
|
|
|
# futures.
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
not fut.cancelled()
|
2019-06-26 12:36:01 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
expect(CancelledError):
|
2019-06-26 12:36:01 +00:00
|
|
|
waitFor fut
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check: completed == 0
|
|
|
|
|
|
|
|
test "cancelAndWait() test":
|
2019-06-26 12:36:01 +00:00
|
|
|
var completed = 0
|
|
|
|
|
|
|
|
proc client1() {.async.} =
|
|
|
|
await sleepAsync(1.seconds)
|
|
|
|
inc(completed)
|
|
|
|
|
|
|
|
proc client2() {.async.} =
|
|
|
|
await client1()
|
|
|
|
inc(completed)
|
|
|
|
|
|
|
|
proc client3() {.async.} =
|
|
|
|
await client2()
|
|
|
|
inc(completed)
|
|
|
|
|
|
|
|
proc client4() {.async.} =
|
|
|
|
await client3()
|
|
|
|
inc(completed)
|
|
|
|
|
|
|
|
var fut = client4()
|
|
|
|
waitFor cancelAndWait(fut)
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
fut.cancelled()
|
2019-06-26 12:36:01 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "Break cancellation propagation test":
|
2019-06-26 12:36:01 +00:00
|
|
|
var completed = 0
|
|
|
|
|
|
|
|
proc client1() {.async.} =
|
|
|
|
await sleepAsync(1.seconds)
|
|
|
|
inc(completed)
|
|
|
|
|
|
|
|
proc client2() {.async.} =
|
|
|
|
try:
|
|
|
|
await client1()
|
|
|
|
except CancelledError:
|
|
|
|
discard
|
|
|
|
inc(completed)
|
|
|
|
|
|
|
|
var fut1 = client2()
|
|
|
|
var fut2 = client2()
|
|
|
|
fut1.cancel()
|
|
|
|
waitFor fut1
|
|
|
|
waitFor cancelAndWait(fut2)
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
not fut1.cancelled()
|
|
|
|
not fut2.cancelled()
|
|
|
|
completed == 2
|
2019-06-26 12:36:01 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "Cancellation callback test":
|
2019-06-26 12:36:01 +00:00
|
|
|
var completed = 0
|
|
|
|
var cancelled = 0
|
|
|
|
|
|
|
|
proc client1(duration: Duration): Future[void] =
|
|
|
|
## Suspends the execution of the current async procedure for the next
|
|
|
|
## ``duration`` time.
|
|
|
|
var retFuture = newFuture[void]()
|
|
|
|
let moment = Moment.fromNow(duration)
|
|
|
|
|
|
|
|
proc completion(data: pointer) {.gcsafe.} =
|
|
|
|
inc(completed)
|
|
|
|
if not(retFuture.finished()):
|
|
|
|
retFuture.complete()
|
|
|
|
|
|
|
|
proc cancel(udata: pointer) {.gcsafe.} =
|
|
|
|
inc(cancelled)
|
|
|
|
if not(retFuture.finished()):
|
|
|
|
removeTimer(moment, completion, cast[pointer](retFuture))
|
|
|
|
|
|
|
|
retFuture.cancelCallback = cancel
|
2020-01-27 18:28:44 +00:00
|
|
|
discard setTimer(moment, completion, cast[pointer](retFuture))
|
2019-06-26 12:36:01 +00:00
|
|
|
return retFuture
|
|
|
|
|
|
|
|
var fut = client1(100.milliseconds)
|
|
|
|
fut.cancel()
|
2019-06-26 12:50:40 +00:00
|
|
|
waitFor(sleepAsync(500.milliseconds))
|
2019-06-26 12:36:01 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
fut.cancelled()
|
|
|
|
completed == 0
|
|
|
|
cancelled == 1
|
2019-06-26 12:36:01 +00:00
|
|
|
|
2020-01-27 18:28:44 +00:00
|
|
|
proc testWaitAsync(): Future[bool] {.async.} =
|
|
|
|
var neverFlag1, neverFlag2, neverFlag3: bool
|
|
|
|
var waitProc1, waitProc2: bool
|
|
|
|
proc neverEndingProc(): Future[void] =
|
|
|
|
var res = newFuture[void]()
|
|
|
|
proc continuation(udata: pointer) {.gcsafe.} =
|
|
|
|
neverFlag2 = true
|
|
|
|
proc cancellation(udata: pointer) {.gcsafe.} =
|
|
|
|
neverFlag3 = true
|
|
|
|
res.addCallback(continuation)
|
|
|
|
res.cancelCallback = cancellation
|
|
|
|
result = res
|
|
|
|
neverFlag1 = true
|
|
|
|
|
|
|
|
proc waitProc() {.async.} =
|
|
|
|
try:
|
|
|
|
await wait(neverEndingProc(), 100.milliseconds)
|
|
|
|
except CancelledError:
|
|
|
|
waitProc1 = true
|
|
|
|
finally:
|
|
|
|
waitProc2 = true
|
|
|
|
|
|
|
|
var fut = waitProc()
|
|
|
|
await cancelAndWait(fut)
|
|
|
|
result = (fut.state == FutureState.Finished) and
|
|
|
|
neverFlag1 and neverFlag2 and neverFlag3 and
|
|
|
|
waitProc1 and waitProc2
|
|
|
|
|
|
|
|
proc testWithTimeoutAsync(): Future[bool] {.async.} =
|
|
|
|
var neverFlag1, neverFlag2, neverFlag3: bool
|
|
|
|
var waitProc1, waitProc2: bool
|
|
|
|
proc neverEndingProc(): Future[void] =
|
|
|
|
var res = newFuture[void]()
|
|
|
|
proc continuation(udata: pointer) {.gcsafe.} =
|
|
|
|
neverFlag2 = true
|
|
|
|
proc cancellation(udata: pointer) {.gcsafe.} =
|
|
|
|
neverFlag3 = true
|
|
|
|
res.addCallback(continuation)
|
|
|
|
res.cancelCallback = cancellation
|
|
|
|
result = res
|
|
|
|
neverFlag1 = true
|
|
|
|
|
|
|
|
proc withTimeoutProc() {.async.} =
|
|
|
|
try:
|
|
|
|
discard await withTimeout(neverEndingProc(), 100.milliseconds)
|
|
|
|
doAssert(false)
|
|
|
|
except CancelledError:
|
|
|
|
waitProc1 = true
|
|
|
|
finally:
|
|
|
|
waitProc2 = true
|
|
|
|
|
|
|
|
var fut = withTimeoutProc()
|
|
|
|
await cancelAndWait(fut)
|
|
|
|
result = (fut.state == FutureState.Finished) and
|
|
|
|
neverFlag1 and neverFlag2 and neverFlag3 and
|
|
|
|
waitProc1 and waitProc2
|
|
|
|
|
2020-07-03 12:03:59 +00:00
|
|
|
proc testCancellationRaceAsync(): Future[bool] {.async.} =
|
|
|
|
var someFut = newFuture[void]()
|
|
|
|
|
|
|
|
proc raceProc(): Future[void] {.async.} =
|
|
|
|
await someFut
|
|
|
|
|
|
|
|
var raceFut1 = raceProc()
|
|
|
|
someFut.complete()
|
|
|
|
await cancelAndWait(raceFut1)
|
|
|
|
|
|
|
|
someFut = newFuture[void]()
|
|
|
|
var raceFut2 = raceProc()
|
|
|
|
someFut.fail(newException(ValueError, ""))
|
|
|
|
await cancelAndWait(raceFut2)
|
|
|
|
|
|
|
|
someFut = newFuture[void]()
|
|
|
|
var raceFut3 = raceProc()
|
|
|
|
someFut.cancel()
|
|
|
|
await cancelAndWait(raceFut3)
|
|
|
|
|
|
|
|
result = (raceFut1.state == FutureState.Cancelled) and
|
|
|
|
(raceFut2.state == FutureState.Cancelled) and
|
|
|
|
(raceFut3.state == FutureState.Cancelled)
|
2020-01-27 18:28:44 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "Cancellation wait() test":
|
|
|
|
check: waitFor(testWaitAsync())
|
2020-07-03 12:03:59 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "Cancellation withTimeout() test":
|
|
|
|
check: waitFor(testWithTimeoutAsync())
|
2020-01-27 18:28:44 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "Cancellation race test":
|
|
|
|
check: waitFor(testCancellationRaceAsync())
|
2020-09-01 18:41:18 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "asyncSpawn() test":
|
|
|
|
check: waitFor(testAsyncSpawnAsync())
|
2020-09-01 18:41:18 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "location test":
|
2020-09-03 11:37:53 +00:00
|
|
|
# WARNING: This test is very sensitive to line numbers and module name.
|
|
|
|
|
|
|
|
proc macroFuture() {.async.} =
|
|
|
|
let someVar {.used.} = 5
|
|
|
|
let someOtherVar {.used.} = 4
|
|
|
|
if true:
|
|
|
|
let otherVar {.used.} = 3
|
|
|
|
|
|
|
|
template templateFuture(): untyped =
|
|
|
|
newFuture[void]("template")
|
|
|
|
|
|
|
|
proc procFuture(): Future[void] =
|
|
|
|
newFuture[void]("procedure")
|
|
|
|
|
|
|
|
var fut1 = macroFuture()
|
|
|
|
var fut2 = templateFuture()
|
|
|
|
var fut3 = procFuture()
|
|
|
|
|
|
|
|
fut2.complete()
|
|
|
|
fut3.complete()
|
|
|
|
|
|
|
|
let loc10 = fut1.location[0]
|
|
|
|
let loc11 = fut1.location[1]
|
|
|
|
let loc20 = fut2.location[0]
|
|
|
|
let loc21 = fut2.location[1]
|
|
|
|
let loc30 = fut3.location[0]
|
|
|
|
let loc31 = fut3.location[1]
|
|
|
|
|
|
|
|
proc chk(loc: ptr SrcLoc, file: string, line: int,
|
|
|
|
procedure: string): bool =
|
|
|
|
if len(procedure) == 0:
|
|
|
|
(loc.line == line) and ($loc.file == file)
|
|
|
|
else:
|
|
|
|
(loc.line == line) and ($loc.file == file) and
|
|
|
|
(loc.procedure == procedure)
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
chk(loc10, "testfut.nim", 1041, "macroFuture")
|
|
|
|
chk(loc11, "testfut.nim", 1042, "")
|
|
|
|
chk(loc20, "testfut.nim", 1054, "template")
|
|
|
|
chk(loc21, "testfut.nim", 1057, "")
|
|
|
|
chk(loc30, "testfut.nim", 1051, "procedure")
|
|
|
|
chk(loc31, "testfut.nim", 1058, "")
|
2020-09-03 11:37:53 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "withTimeout(fut) should wait cancellation test":
|
2020-11-17 09:59:02 +00:00
|
|
|
proc futureNeverEnds(): Future[void] =
|
|
|
|
newFuture[void]("neverending.future")
|
|
|
|
|
|
|
|
proc futureOneLevelMore() {.async.} =
|
|
|
|
await futureNeverEnds()
|
|
|
|
|
|
|
|
proc testWithTimeout(): Future[bool] {.async.} =
|
|
|
|
var fut = futureOneLevelMore()
|
|
|
|
try:
|
|
|
|
let res = await withTimeout(fut, 100.milliseconds)
|
|
|
|
# Because `fut` is never-ending Future[T], `withTimeout` should return
|
|
|
|
# `false` but it also has to wait until `fut` is cancelled.
|
|
|
|
if not(res) and fut.cancelled():
|
|
|
|
return true
|
|
|
|
else:
|
|
|
|
return false
|
|
|
|
except CatchableError:
|
|
|
|
return false
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check: waitFor testWithTimeout()
|
2020-11-17 09:59:02 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "wait(fut) should wait cancellation test":
|
2020-11-17 09:59:02 +00:00
|
|
|
proc futureNeverEnds(): Future[void] =
|
|
|
|
newFuture[void]("neverending.future")
|
|
|
|
|
|
|
|
proc futureOneLevelMore() {.async.} =
|
|
|
|
await futureNeverEnds()
|
|
|
|
|
|
|
|
proc testWait(): Future[bool] {.async.} =
|
|
|
|
var fut = futureOneLevelMore()
|
|
|
|
try:
|
|
|
|
await wait(fut, 100.milliseconds)
|
|
|
|
return false
|
|
|
|
except AsyncTimeoutError:
|
|
|
|
# Because `fut` is never-ending Future[T], `wait` should raise
|
|
|
|
# `AsyncTimeoutError`, but only after `fut` is cancelled.
|
|
|
|
if fut.cancelled():
|
|
|
|
return true
|
|
|
|
else:
|
|
|
|
return false
|
|
|
|
except CatchableError:
|
|
|
|
return false
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check: waitFor testWait()
|
2020-11-17 09:59:02 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "race(zero) test":
|
2020-11-26 22:50:55 +00:00
|
|
|
var tseq = newSeq[FutureBase]()
|
|
|
|
var fut1 = race(tseq)
|
|
|
|
var fut2 = race()
|
|
|
|
var fut3 = race([])
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
fut1.failed()
|
|
|
|
fut2.failed()
|
|
|
|
fut3.failed()
|
2020-11-26 22:50:55 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "race(varargs) test":
|
2020-11-26 22:50:55 +00:00
|
|
|
proc vlient1() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
|
|
|
|
proc vlient2() {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
|
|
|
|
proc vlient3() {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
|
|
|
|
proc ilient1(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
result = 10
|
|
|
|
|
|
|
|
proc ilient2(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
result = 20
|
|
|
|
|
|
|
|
proc ilient3(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
result = 30
|
|
|
|
|
|
|
|
proc slient1(): Future[string] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
result = "sclient1"
|
|
|
|
|
|
|
|
proc slient2(): Future[string] {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
result = "sclient2"
|
|
|
|
|
|
|
|
proc slient3(): Future[string] {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
result = "sclient3"
|
|
|
|
|
|
|
|
var fut11 = vlient1()
|
|
|
|
var fut12 = ilient2()
|
|
|
|
var fut13 = slient3()
|
|
|
|
var res1 = waitFor(race(fut11, fut12, fut13))
|
|
|
|
|
|
|
|
var fut21 = vlient2()
|
|
|
|
var fut22 = ilient1()
|
|
|
|
var fut23 = slient3()
|
|
|
|
var res2 = waitFor(race(fut21, fut22, fut23))
|
|
|
|
|
|
|
|
var fut31 = vlient3()
|
|
|
|
var fut32 = ilient2()
|
|
|
|
var fut33 = slient1()
|
|
|
|
var res3 = waitFor(race(fut31, fut32, fut33))
|
|
|
|
|
|
|
|
var fut41 = vlient1()
|
|
|
|
var fut42 = slient2()
|
|
|
|
var fut43 = ilient3()
|
|
|
|
var res4 = waitFor(race(fut41, fut42, fut43))
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
FutureBase(fut11) == res1
|
|
|
|
FutureBase(fut22) == res2
|
|
|
|
FutureBase(fut33) == res3
|
|
|
|
FutureBase(fut41) == res4
|
2020-11-26 22:50:55 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "race(seq) test":
|
2020-11-26 22:50:55 +00:00
|
|
|
proc vlient1() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
|
|
|
|
proc vlient2() {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
|
|
|
|
proc vlient3() {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
|
|
|
|
proc ilient1(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
result = 10
|
|
|
|
|
|
|
|
proc ilient2(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
result = 20
|
|
|
|
|
|
|
|
proc ilient3(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
result = 30
|
|
|
|
|
|
|
|
proc slient1(): Future[string] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
result = "slient1"
|
|
|
|
|
|
|
|
proc slient2(): Future[string] {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
result = "slient2"
|
|
|
|
|
|
|
|
proc slient3(): Future[string] {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
result = "slient3"
|
|
|
|
|
|
|
|
var v10 = vlient1()
|
|
|
|
var v11 = ilient2()
|
|
|
|
var v12 = slient3()
|
|
|
|
var res1 = waitFor(race(@[FutureBase(v10), FutureBase(v11),
|
|
|
|
FutureBase(v12)]))
|
|
|
|
|
|
|
|
var v20 = vlient2()
|
|
|
|
var v21 = ilient1()
|
|
|
|
var v22 = slient3()
|
|
|
|
var res2 = waitFor(race(@[FutureBase(v20), FutureBase(v21),
|
|
|
|
FutureBase(v22)]))
|
|
|
|
|
|
|
|
var v30 = vlient3()
|
|
|
|
var v31 = ilient2()
|
|
|
|
var v32 = slient1()
|
|
|
|
var res3 = waitFor(race(@[FutureBase(v30), FutureBase(v31),
|
|
|
|
FutureBase(v32)]))
|
|
|
|
|
|
|
|
var v40 = vlient1()
|
|
|
|
var v41 = slient2()
|
|
|
|
var v42 = ilient3()
|
|
|
|
var res4 = waitFor(race(@[FutureBase(v40), FutureBase(v41),
|
|
|
|
FutureBase(v42)]))
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
res1 == FutureBase(v10)
|
|
|
|
res2 == FutureBase(v21)
|
|
|
|
res3 == FutureBase(v32)
|
|
|
|
res4 == FutureBase(v40)
|
2020-11-26 22:50:55 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "race() already completed test":
|
2020-11-26 22:50:55 +00:00
|
|
|
proc client1(): Future[int] {.async.} =
|
|
|
|
result = 1
|
|
|
|
|
|
|
|
proc client2() {.async.} =
|
|
|
|
if true:
|
|
|
|
raise newException(ValueError, "")
|
|
|
|
|
|
|
|
proc client3(): Future[string] {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
result = "client3"
|
|
|
|
|
|
|
|
var f10 = client1()
|
|
|
|
var f20 = client2()
|
|
|
|
var f30 = client3()
|
|
|
|
var fut1 = race(f30, f10, f20)
|
|
|
|
var f11 = client1()
|
|
|
|
var f21 = client2()
|
|
|
|
var f31 = client3()
|
|
|
|
var fut2 = race(f31, f21, f11)
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
fut1.done() and fut1.read() == FutureBase(f10)
|
|
|
|
fut2.done() and fut2.read() == FutureBase(f21)
|
2020-11-26 22:50:55 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
test "race() cancellation test":
|
2020-11-26 22:50:55 +00:00
|
|
|
proc client1() {.async.} =
|
|
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
|
|
|
|
proc client2(): Future[int] {.async.} =
|
|
|
|
await sleepAsync(200.milliseconds)
|
|
|
|
return 10
|
|
|
|
|
|
|
|
proc client3(): Future[string] {.async.} =
|
|
|
|
await sleepAsync(300.milliseconds)
|
|
|
|
return "client3"
|
|
|
|
|
|
|
|
var f1 = client1()
|
|
|
|
var f2 = client2()
|
|
|
|
var f3 = client3()
|
|
|
|
var fut = race(f1, f2, f3)
|
|
|
|
waitFor(cancelAndWait(fut))
|
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
not f1.finished()
|
|
|
|
not f2.finished()
|
|
|
|
not f3.finished()
|
2020-11-26 22:50:55 +00:00
|
|
|
|
|
|
|
waitFor(sleepAsync(400.milliseconds))
|
2019-07-04 12:04:59 +00:00
|
|
|
|
2021-06-04 10:28:04 +00:00
|
|
|
check:
|
|
|
|
f1.finished()
|
|
|
|
f2.finished()
|
|
|
|
f3.finished()
|
2019-06-26 12:36:01 +00:00
|
|
|
|
2020-04-21 04:07:49 +00:00
|
|
|
|