2699 lines
72 KiB
Nim
2699 lines
72 KiB
Nim
# Chronos Test Suite
|
|
# (c) Copyright 2018-Present
|
|
# Status Research & Development GmbH
|
|
#
|
|
# Licensed under either of
|
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
|
# MIT license (LICENSE-MIT)
|
|
import unittest2
|
|
import results
|
|
import ../chronos, ../chronos/unittest2/asynctests
|
|
|
|
{.used.}
|
|
|
|
type
|
|
TestFooConnection* = ref object
|
|
id*: int
|
|
|
|
suite "Future[T] behavior test suite":
|
|
proc testFuture1(): Future[int] {.async.} =
|
|
await sleepAsync(0.milliseconds)
|
|
|
|
proc testFuture2(): Future[int] {.async.} =
|
|
return 1
|
|
|
|
proc testFuture3(): Future[int] {.async.} =
|
|
result = await testFuture2()
|
|
|
|
proc testFuture100(): Future[int] {.async.} =
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
test "Async undefined behavior (#7758) test":
|
|
var fut = testFuture1()
|
|
poll()
|
|
poll()
|
|
if not fut.finished:
|
|
poll()
|
|
check: fut.finished
|
|
|
|
test "Immediately completed asynchronous procedure test":
|
|
var fut = testFuture3()
|
|
check: fut.finished
|
|
|
|
test "Future[T] callbacks are invoked in reverse order (#7197) test":
|
|
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)
|
|
|
|
check:
|
|
fut.finished
|
|
testResult == "12345"
|
|
|
|
test "Future[T] callbacks not changing order after removeCallback()":
|
|
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)
|
|
check:
|
|
fut.finished
|
|
testResult == "1245"
|
|
|
|
asyncTest "wait(duration) test":
|
|
block:
|
|
## Test for not immediately completed future and timeout = -1
|
|
let res =
|
|
try:
|
|
discard await wait(testFuture1(), InfiniteDuration)
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
block:
|
|
## Test for immediately completed future and timeout = -1
|
|
let res =
|
|
try:
|
|
discard await wait(testFuture2(), InfiniteDuration)
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
block:
|
|
## Test for not immediately completed future and timeout = 0
|
|
let res =
|
|
try:
|
|
discard await wait(testFuture1(), 0.milliseconds)
|
|
false
|
|
except AsyncTimeoutError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
## Test for immediately completed future and timeout = 0
|
|
let res =
|
|
try:
|
|
discard await wait(testFuture2(), 0.milliseconds)
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
## Test for future which cannot be completed in timeout period
|
|
let res =
|
|
try:
|
|
discard await wait(testFuture100(), 50.milliseconds)
|
|
false
|
|
except AsyncTimeoutError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
## Test for future which will be completed before timeout exceeded.
|
|
let res =
|
|
try:
|
|
discard await wait(testFuture100(), 500.milliseconds)
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
asyncTest "wait(future) test":
|
|
block:
|
|
## Test for not immediately completed future and deadline which is not
|
|
## going to be finished
|
|
let
|
|
deadline = newFuture[void]()
|
|
future1 = testFuture1()
|
|
let res =
|
|
try:
|
|
discard await wait(future1, deadline)
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check:
|
|
deadline.finished() == false
|
|
future1.finished() == true
|
|
res == true
|
|
|
|
await deadline.cancelAndWait()
|
|
|
|
check deadline.finished() == true
|
|
block:
|
|
## Test for immediately completed future and timeout = -1
|
|
let
|
|
deadline = newFuture[void]()
|
|
future2 = testFuture2()
|
|
let res =
|
|
try:
|
|
discard await wait(future2, deadline)
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check:
|
|
deadline.finished() == false
|
|
future2.finished() == true
|
|
res
|
|
|
|
await deadline.cancelAndWait()
|
|
|
|
check deadline.finished() == true
|
|
block:
|
|
## Test for not immediately completed future and timeout = 0
|
|
let
|
|
deadline = newFuture[void]()
|
|
future1 = testFuture1()
|
|
deadline.complete()
|
|
let res =
|
|
try:
|
|
discard await wait(future1, deadline)
|
|
false
|
|
except AsyncTimeoutError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check:
|
|
future1.finished() == false
|
|
deadline.finished() == true
|
|
res
|
|
|
|
block:
|
|
## Test for immediately completed future and timeout = 0
|
|
let
|
|
deadline = newFuture[void]()
|
|
future2 = testFuture2()
|
|
deadline.complete()
|
|
let (res1, res2) =
|
|
try:
|
|
let res = await wait(future2, deadline)
|
|
(true, res)
|
|
except CatchableError:
|
|
(false, -1)
|
|
check:
|
|
future2.finished() == true
|
|
deadline.finished() == true
|
|
res1 == true
|
|
res2 == 1
|
|
|
|
block:
|
|
## Test for future which cannot be completed in timeout period
|
|
let
|
|
deadline = sleepAsync(50.milliseconds)
|
|
future100 = testFuture100()
|
|
let res =
|
|
try:
|
|
discard await wait(future100, deadline)
|
|
false
|
|
except AsyncTimeoutError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check:
|
|
deadline.finished() == true
|
|
res
|
|
await future100.cancelAndWait()
|
|
check:
|
|
future100.finished() == true
|
|
|
|
block:
|
|
## Test for future which will be completed before timeout exceeded.
|
|
let
|
|
deadline = sleepAsync(500.milliseconds)
|
|
future100 = testFuture100()
|
|
let (res1, res2) =
|
|
try:
|
|
let res = await wait(future100, deadline)
|
|
(true, res)
|
|
except CatchableError:
|
|
(false, -1)
|
|
check:
|
|
future100.finished() == true
|
|
deadline.finished() == false
|
|
res1 == true
|
|
res2 == 0
|
|
await deadline.cancelAndWait()
|
|
check:
|
|
deadline.finished() == true
|
|
|
|
asyncTest "wait(future) cancellation behavior test":
|
|
proc deepTest3(future: Future[void]) {.async.} =
|
|
await future
|
|
|
|
proc deepTest2(future: Future[void]) {.async.} =
|
|
await deepTest3(future)
|
|
|
|
proc deepTest1(future: Future[void]) {.async.} =
|
|
await deepTest2(future)
|
|
|
|
let
|
|
|
|
deadlineFuture = newFuture[void]()
|
|
|
|
block:
|
|
# Cancellation should affect `testFuture` because it is in pending state.
|
|
let monitorFuture = newFuture[void]()
|
|
var testFuture = deepTest1(monitorFuture)
|
|
let waitFut = wait(testFuture, deadlineFuture)
|
|
await cancelAndWait(waitFut)
|
|
check:
|
|
monitorFuture.cancelled() == true
|
|
testFuture.cancelled() == true
|
|
waitFut.cancelled() == true
|
|
deadlineFuture.finished() == false
|
|
|
|
block:
|
|
# Cancellation should not affect `testFuture` because it is completed.
|
|
let monitorFuture = newFuture[void]()
|
|
var testFuture = deepTest1(monitorFuture)
|
|
let waitFut = wait(testFuture, deadlineFuture)
|
|
monitorFuture.complete()
|
|
await cancelAndWait(waitFut)
|
|
check:
|
|
monitorFuture.completed() == true
|
|
monitorFuture.cancelled() == false
|
|
testFuture.completed() == true
|
|
waitFut.completed() == true
|
|
deadlineFuture.finished() == false
|
|
|
|
block:
|
|
# Cancellation should not affect `testFuture` because it is failed.
|
|
let monitorFuture = newFuture[void]()
|
|
var testFuture = deepTest1(monitorFuture)
|
|
let waitFut = wait(testFuture, deadlineFuture)
|
|
monitorFuture.fail(newException(ValueError, "TEST"))
|
|
await cancelAndWait(waitFut)
|
|
check:
|
|
monitorFuture.failed() == true
|
|
monitorFuture.cancelled() == false
|
|
testFuture.failed() == true
|
|
testFuture.cancelled() == false
|
|
waitFut.failed() == true
|
|
testFuture.cancelled() == false
|
|
deadlineFuture.finished() == false
|
|
|
|
await cancelAndWait(deadlineFuture)
|
|
|
|
check deadlineFuture.finished() == true
|
|
|
|
asyncTest "Discarded result Future[T] test":
|
|
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, "")
|
|
|
|
discard client1()
|
|
discard client1f()
|
|
discard client2()
|
|
discard client2f()
|
|
discard client3()
|
|
discard client3f()
|
|
discard client4()
|
|
discard client4f()
|
|
discard client5()
|
|
discard client5f()
|
|
|
|
await sleepAsync(1.seconds)
|
|
check completedFutures == 10
|
|
|
|
test "allFutures(zero) test":
|
|
var tseq = newSeq[Future[int]]()
|
|
var fut = allFutures(tseq)
|
|
check:
|
|
fut.finished
|
|
|
|
asyncTest "allFutures(varargs) test":
|
|
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, "")
|
|
|
|
await allFutures(vlient1(), vlient2(), vlient3(), vlient4(), vlient5())
|
|
check completedFutures == 5
|
|
|
|
completedFutures = 0
|
|
|
|
await allFutures(vlient1(), vlient1f(), vlient2(), vlient2f(), vlient3(),
|
|
vlient3f(), vlient4(), vlient4f(), vlient5(), vlient5f())
|
|
check completedFutures == 10
|
|
|
|
completedFutures = 0
|
|
|
|
await allFutures(client1(), client2(), client3(), client4(), client5())
|
|
check completedFutures == 5
|
|
|
|
completedFutures = 0
|
|
|
|
await allFutures(client1(), client1f(), client2(), client2f(), client3(),
|
|
client3f(), client4(), client4f(), client5(), client5f())
|
|
check completedFutures == 10
|
|
|
|
asyncTest "allFutures(varargs) test":
|
|
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())
|
|
|
|
await allFutures(vfutures)
|
|
# 5 * 10 completed futures = 50
|
|
check completedFutures == 50
|
|
|
|
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())
|
|
|
|
await allFutures(vfutures)
|
|
# 10 * 10 completed futures = 100
|
|
check completedFutures == 100
|
|
|
|
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())
|
|
|
|
await allFutures(nfutures)
|
|
# 5 * 10 completed futures = 50
|
|
check completedFutures == 50
|
|
|
|
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())
|
|
|
|
await allFutures(nfutures)
|
|
|
|
# 10 * 10 completed futures = 100
|
|
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())
|
|
|
|
test "allFinished() already completed test":
|
|
proc client1(): Future[int] {.async.} =
|
|
result = 1
|
|
|
|
proc client2(): Future[int] {.async.} =
|
|
if true:
|
|
raise newException(ValueError, "")
|
|
|
|
var fut = allFinished(client1(), client2())
|
|
check:
|
|
fut.finished()
|
|
not(fut.failed())
|
|
len(fut.read()) == 2
|
|
|
|
test "one(zero) test":
|
|
var tseq = newSeq[Future[int]]()
|
|
var fut = one(tseq)
|
|
check: fut.finished and fut.failed
|
|
|
|
asyncTest "one(varargs) test":
|
|
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 fut11 = vlient1()
|
|
var fut12 = vlient2()
|
|
var fut13 = vlient3()
|
|
var res1 = await one(fut11, fut12, fut13)
|
|
|
|
var fut21 = vlient2()
|
|
var fut22 = vlient1()
|
|
var fut23 = vlient3()
|
|
var res2 = await one(fut21, fut22, fut23)
|
|
|
|
var fut31 = vlient3()
|
|
var fut32 = vlient2()
|
|
var fut33 = vlient1()
|
|
var res3 = await one(fut31, fut32, fut33)
|
|
|
|
check:
|
|
fut11 == res1
|
|
fut22 == res2
|
|
fut33 == res3
|
|
|
|
var cut11 = client1()
|
|
var cut12 = client2()
|
|
var cut13 = client3()
|
|
var res4 = await one(cut11, cut12, cut13)
|
|
|
|
var cut21 = client2()
|
|
var cut22 = client1()
|
|
var cut23 = client3()
|
|
var res5 = await one(cut21, cut22, cut23)
|
|
|
|
var cut31 = client3()
|
|
var cut32 = client2()
|
|
var cut33 = client1()
|
|
var res6 = await one(cut31, cut32, cut33)
|
|
|
|
check:
|
|
cut11 == res4
|
|
cut22 == res5
|
|
cut33 == res6
|
|
|
|
asyncTest "one(seq) test":
|
|
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()
|
|
var res1 = await one(@[v10, v11, v12])
|
|
|
|
var v20 = vlient2()
|
|
var v21 = vlient1()
|
|
var v22 = vlient3()
|
|
var res2 = await one(@[v20, v21, v22])
|
|
|
|
var v30 = vlient3()
|
|
var v31 = vlient2()
|
|
var v32 = vlient1()
|
|
var res3 = await one(@[v30, v31, v32])
|
|
|
|
check:
|
|
res1 == v10
|
|
res2 == v21
|
|
res3 == v32
|
|
|
|
var c10 = client1()
|
|
var c11 = client2()
|
|
var c12 = client3()
|
|
var res4 = await one(@[c10, c11, c12])
|
|
|
|
var c20 = client2()
|
|
var c21 = client1()
|
|
var c22 = client3()
|
|
var res5 = await one(@[c20, c21, c22])
|
|
|
|
var c30 = client3()
|
|
var c31 = client2()
|
|
var c32 = client1()
|
|
var res6 = await one(@[c30, c31, c32])
|
|
|
|
check:
|
|
res4 == c10
|
|
res5 == c21
|
|
res6 == c32
|
|
|
|
test "one(completed) test":
|
|
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)
|
|
|
|
check:
|
|
fut1.finished()
|
|
not(fut1.failed())
|
|
fut1.read() == f10
|
|
fut2.finished()
|
|
not(fut2.failed())
|
|
fut2.read() == f21
|
|
|
|
asyncTest "one() exception effect":
|
|
proc checkraises() {.async: (raises: [CancelledError]).} =
|
|
let f = Future[void].Raising([CancelledError]).init()
|
|
f.complete()
|
|
one(f).cancelSoon()
|
|
|
|
await checkraises()
|
|
|
|
asyncTest "or() test":
|
|
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(IOError, "")
|
|
|
|
proc client5() {.async.} =
|
|
discard
|
|
|
|
proc client6() {.async.} =
|
|
if true:
|
|
raise newException(ValueError, "")
|
|
|
|
proc client7() {.async.} =
|
|
if true:
|
|
raise newException(IOError, "")
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client1() or client2()
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client2() or client1()
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client1() or client4()
|
|
true
|
|
except IOError:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client2() or client4()
|
|
true
|
|
except IOError:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client4() or client2()
|
|
true
|
|
except IOError:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client1() or client3()
|
|
false
|
|
except ValueError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client3() or client1()
|
|
false
|
|
except ValueError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client3() or client4()
|
|
false
|
|
except ValueError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client4() or client3()
|
|
false
|
|
except ValueError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client5() or client6()
|
|
true
|
|
except ValueError:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client6() or client5()
|
|
false
|
|
except ValueError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client6() or client7()
|
|
false
|
|
except ValueError:
|
|
true
|
|
except IOError:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client7() or client6()
|
|
false
|
|
except ValueError:
|
|
false
|
|
except IOError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
asyncTest "or() already completed test":
|
|
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
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client1() or client2()
|
|
true
|
|
except ValueError:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
var fut1 = client1()
|
|
var fut2 = client3()
|
|
let res =
|
|
try:
|
|
await fut1 or fut2
|
|
true
|
|
except ValueError:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
let discarded {.used.} = await fut2
|
|
check res
|
|
|
|
block:
|
|
var fut1 = client2()
|
|
var fut2 = client3()
|
|
let res =
|
|
try:
|
|
await fut1 or fut2
|
|
false
|
|
except ValueError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
let discarded {.used.} = await fut2
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
await client2() or client1()
|
|
false
|
|
except ValueError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
var fut1 = client3()
|
|
var fut2 = client1()
|
|
let res =
|
|
try:
|
|
await fut1 or fut2
|
|
true
|
|
except ValueError:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
let discarded {.used.} = await fut1
|
|
check res
|
|
|
|
block:
|
|
var fut1 = client3()
|
|
var fut2 = client2()
|
|
let res =
|
|
try:
|
|
await fut1 or fut2
|
|
false
|
|
except ValueError:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
let discarded {.used.} = await fut1
|
|
check res
|
|
|
|
asyncTest "tryCancel() async procedure test":
|
|
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()
|
|
discard fut.tryCancel()
|
|
|
|
# Future must not be cancelled immediately, because it has many nested
|
|
# futures.
|
|
check:
|
|
not fut.cancelled()
|
|
|
|
expect(CancelledError):
|
|
await fut
|
|
|
|
check completed == 0
|
|
|
|
asyncTest "cancelAndWait() test":
|
|
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()
|
|
await cancelAndWait(fut)
|
|
check fut.cancelled()
|
|
|
|
asyncTest "Break cancellation propagation test":
|
|
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()
|
|
discard fut1.tryCancel()
|
|
await fut1
|
|
await cancelAndWait(fut2)
|
|
check:
|
|
not fut1.cancelled()
|
|
not fut2.cancelled()
|
|
completed == 2
|
|
|
|
asyncTest "Cancellation callback test":
|
|
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 cancellation(udata: pointer) {.gcsafe.} =
|
|
inc(cancelled)
|
|
if not(retFuture.finished()):
|
|
removeTimer(moment, completion, cast[pointer](retFuture))
|
|
|
|
retFuture.cancelCallback = cancellation
|
|
discard setTimer(moment, completion, cast[pointer](retFuture))
|
|
return retFuture
|
|
|
|
var fut = client1(100.milliseconds)
|
|
discard fut.tryCancel()
|
|
await sleepAsync(500.milliseconds)
|
|
check:
|
|
fut.cancelled()
|
|
completed == 0
|
|
cancelled == 1
|
|
|
|
asyncTest "Cancellation wait(duration) test":
|
|
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
|
|
except CatchableError:
|
|
doAssert(false)
|
|
finally:
|
|
waitProc2 = true
|
|
|
|
var fut = waitProc()
|
|
await cancelAndWait(fut)
|
|
check:
|
|
fut.state == FutureState.Completed
|
|
neverFlag1 and neverFlag2 and neverFlag3 and waitProc1 and waitProc2
|
|
|
|
asyncTest "Cancellation withTimeout() test":
|
|
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
|
|
neverFlag1 = true
|
|
res
|
|
|
|
proc withTimeoutProc() {.async.} =
|
|
try:
|
|
discard await withTimeout(neverEndingProc(), 100.milliseconds)
|
|
doAssert(false)
|
|
except CancelledError:
|
|
waitProc1 = true
|
|
except CatchableError:
|
|
doAssert(false)
|
|
finally:
|
|
waitProc2 = true
|
|
|
|
var fut = withTimeoutProc()
|
|
await cancelAndWait(fut)
|
|
check:
|
|
fut.state == FutureState.Completed
|
|
neverFlag1 and neverFlag2 and neverFlag3 and waitProc1 and waitProc2
|
|
|
|
asyncTest "Cancellation wait(future) test":
|
|
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.} =
|
|
let deadline = sleepAsync(100.milliseconds)
|
|
try:
|
|
await wait(neverEndingProc(), deadline)
|
|
except CancelledError:
|
|
waitProc1 = true
|
|
except CatchableError:
|
|
doAssert(false)
|
|
finally:
|
|
await cancelAndWait(deadline)
|
|
waitProc2 = true
|
|
|
|
var fut = waitProc()
|
|
await cancelAndWait(fut)
|
|
check:
|
|
fut.state == FutureState.Completed
|
|
neverFlag1 and neverFlag2 and neverFlag3 and waitProc1 and waitProc2
|
|
|
|
asyncTest "Cancellation race() test":
|
|
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()
|
|
discard someFut.tryCancel()
|
|
await cancelAndWait(raceFut3)
|
|
|
|
check:
|
|
raceFut1.state == FutureState.Completed
|
|
raceFut2.state == FutureState.Failed
|
|
raceFut3.state == FutureState.Cancelled
|
|
|
|
asyncTest "asyncSpawn() test":
|
|
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)
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
var fut1 = completeTask1()
|
|
var fut2 = completeTask2()
|
|
asyncSpawn fut1
|
|
asyncSpawn fut2
|
|
await sleepAsync(200.milliseconds)
|
|
if not(fut1.finished()) or not(fut2.finished()):
|
|
false
|
|
else:
|
|
if fut1.failed() or fut1.cancelled() or fut2.failed() or
|
|
fut2.cancelled():
|
|
false
|
|
else:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
asyncSpawn errorTask()
|
|
false
|
|
except FutureDefect:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
block:
|
|
let res =
|
|
try:
|
|
var fut = cancelTask()
|
|
await cancelAndWait(fut)
|
|
asyncSpawn fut
|
|
false
|
|
except FutureDefect:
|
|
true
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
test "location test":
|
|
# WARNING: This test is very sensitive to line numbers and module name.
|
|
template start(): int =
|
|
instantiationInfo().line
|
|
|
|
const first = start()
|
|
proc macroFuture() {.async.} =
|
|
let someVar {.used.} = 5 # LINE POSITION 1
|
|
let someOtherVar {.used.} = 4
|
|
if true:
|
|
let otherVar {.used.} = 3 # LINE POSITION 2
|
|
|
|
template templateFuture(): untyped =
|
|
newFuture[void]("template")
|
|
|
|
proc procFuture(): Future[void] =
|
|
newFuture[void]("procedure") # LINE POSITION 5
|
|
|
|
var fut1 = macroFuture()
|
|
var fut2 = templateFuture() # LINE POSITION 3
|
|
var fut3 = procFuture()
|
|
|
|
fut2.complete() # LINE POSITION 4
|
|
fut3.complete() # LINE POSITION 6
|
|
|
|
{.push warning[Deprecated]: off.} # testing backwards compatibility interface
|
|
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]
|
|
{.pop.}
|
|
|
|
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)
|
|
|
|
check:
|
|
chk(loc10, "testfut.nim", first + 2, "macroFuture")
|
|
chk(loc11, "testfut.nim", first + 5, "")
|
|
chk(loc20, "testfut.nim", first + 14, "template")
|
|
chk(loc21, "testfut.nim", first + 17, "")
|
|
chk(loc30, "testfut.nim", first + 11, "procedure")
|
|
chk(loc31, "testfut.nim", first + 18, "")
|
|
|
|
asyncTest "withTimeout(fut) should wait cancellation test":
|
|
proc futureNeverEnds(): Future[void] =
|
|
newFuture[void]("neverending.future")
|
|
|
|
proc futureOneLevelMore() {.async.} =
|
|
await futureNeverEnds()
|
|
|
|
let res =
|
|
block:
|
|
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():
|
|
true
|
|
else:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
asyncTest "wait(future) should wait cancellation test":
|
|
proc futureNeverEnds(): Future[void] =
|
|
newFuture[void]("neverending.future")
|
|
|
|
proc futureOneLevelMore() {.async.} =
|
|
await futureNeverEnds()
|
|
|
|
var fut = futureOneLevelMore()
|
|
let res =
|
|
try:
|
|
await wait(fut, 100.milliseconds)
|
|
false
|
|
except AsyncTimeoutError:
|
|
# Because `fut` is never-ending Future[T], `wait` should raise
|
|
# `AsyncTimeoutError`, but only after `fut` is cancelled.
|
|
if fut.cancelled():
|
|
true
|
|
else:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
|
|
check res
|
|
|
|
asyncTest "wait(future) should wait cancellation test":
|
|
proc futureNeverEnds(): Future[void] =
|
|
newFuture[void]("neverending.future")
|
|
|
|
proc futureOneLevelMore() {.async.} =
|
|
await futureNeverEnds()
|
|
|
|
var fut = futureOneLevelMore()
|
|
let res =
|
|
try:
|
|
await wait(fut, sleepAsync(100.milliseconds))
|
|
false
|
|
except AsyncTimeoutError:
|
|
# Because `fut` is never-ending Future[T], `wait` should raise
|
|
# `AsyncTimeoutError`, but only after `fut` is cancelled.
|
|
if fut.cancelled():
|
|
true
|
|
else:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
check res
|
|
|
|
test "race(zero) test":
|
|
var tseq = newSeq[FutureBase]()
|
|
var fut1 = race(tseq)
|
|
check:
|
|
# https://github.com/nim-lang/Nim/issues/22964
|
|
not compiles(block:
|
|
var fut2 = race())
|
|
not compiles(block:
|
|
var fut3 = race([]))
|
|
|
|
check:
|
|
fut1.failed()
|
|
# fut2.failed()
|
|
# fut3.failed()
|
|
|
|
asyncTest "race(varargs) test":
|
|
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 = await race(fut11, fut12, fut13)
|
|
check FutureBase(fut11) == res1
|
|
await allFutures(fut12, fut13)
|
|
|
|
var fut21 = vlient2()
|
|
var fut22 = ilient1()
|
|
var fut23 = slient3()
|
|
var res2 = await race(fut21, fut22, fut23)
|
|
check FutureBase(fut22) == res2
|
|
await allFutures(fut21, fut23)
|
|
|
|
var fut31 = vlient3()
|
|
var fut32 = ilient2()
|
|
var fut33 = slient1()
|
|
var res3 = await race(fut31, fut32, fut33)
|
|
check FutureBase(fut33) == res3
|
|
await allFutures(fut31, fut32)
|
|
|
|
var fut41 = vlient1()
|
|
var fut42 = slient2()
|
|
var fut43 = ilient3()
|
|
var res4 = await race(fut41, fut42, fut43)
|
|
check FutureBase(fut41) == res4
|
|
await allFutures(fut42, fut43)
|
|
|
|
asyncTest "race(seq) test":
|
|
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 = await race(@[FutureBase(v10), FutureBase(v11), FutureBase(v12)])
|
|
check res1 == FutureBase(v10)
|
|
await allFutures(v11, v12)
|
|
|
|
var v20 = vlient2()
|
|
var v21 = ilient1()
|
|
var v22 = slient3()
|
|
var res2 = await race(@[FutureBase(v20), FutureBase(v21), FutureBase(v22)])
|
|
check res2 == FutureBase(v21)
|
|
await allFutures(v20, v22)
|
|
|
|
var v30 = vlient3()
|
|
var v31 = ilient2()
|
|
var v32 = slient1()
|
|
var res3 = await race(@[FutureBase(v30), FutureBase(v31), FutureBase(v32)])
|
|
check res3 == FutureBase(v32)
|
|
await allFutures(v30, v31)
|
|
|
|
var v40 = vlient1()
|
|
var v41 = slient2()
|
|
var v42 = ilient3()
|
|
var res4 = await race(@[FutureBase(v40), FutureBase(v41), FutureBase(v42)])
|
|
check res4 == FutureBase(v40)
|
|
await allFutures(v41, v42)
|
|
|
|
asyncTest "race() already completed test":
|
|
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)
|
|
|
|
check:
|
|
fut1.completed() and fut1.read() == FutureBase(f10)
|
|
fut2.completed() and fut2.read() == FutureBase(f21)
|
|
|
|
await allFutures(f20, f30, f11, f31)
|
|
|
|
asyncTest "race() cancellation test":
|
|
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)
|
|
await cancelAndWait(fut)
|
|
|
|
check:
|
|
not(f1.finished())
|
|
not(f2.finished())
|
|
not(f3.finished())
|
|
|
|
await sleepAsync(500.milliseconds)
|
|
|
|
check:
|
|
f1.finished()
|
|
f2.finished()
|
|
f3.finished()
|
|
|
|
asyncTest "race() exception effect":
|
|
proc checkraises() {.async: (raises: [CancelledError]).} =
|
|
let f = Future[void].Raising([CancelledError]).init()
|
|
f.complete()
|
|
race(f).cancelSoon()
|
|
|
|
await checkraises()
|
|
|
|
test "Unsigned integer overflow test":
|
|
check:
|
|
0xFFFF_FFFF_FFFF_FFFF'u64 + 1'u64 == 0'u64
|
|
0xFFFF_FFFF'u32 + 1'u32 == 0'u32
|
|
|
|
when sizeof(uint) == 8:
|
|
check 0xFFFF_FFFF_FFFF_FFFF'u + 1'u == 0'u
|
|
else:
|
|
check 0xFFFF_FFFF'u + 1'u == 0'u
|
|
|
|
var v1_64 = 0xFFFF_FFFF_FFFF_FFFF'u64
|
|
var v2_64 = 0xFFFF_FFFF_FFFF_FFFF'u64
|
|
var v1_32 = 0xFFFF_FFFF'u32
|
|
var v2_32 = 0xFFFF_FFFF'u32
|
|
inc(v1_64)
|
|
inc(v1_32)
|
|
check:
|
|
v1_64 == 0'u64
|
|
v2_64 + 1'u64 == 0'u64
|
|
v1_32 == 0'u32
|
|
v2_32 + 1'u32 == 0'u32
|
|
|
|
when sizeof(uint) == 8:
|
|
var v1_u = 0xFFFF_FFFF_FFFF_FFFF'u
|
|
var v2_u = 0xFFFF_FFFF_FFFF_FFFF'u
|
|
inc(v1_u)
|
|
check:
|
|
v1_u == 0'u
|
|
v2_u + 1'u == 0'u
|
|
else:
|
|
var v1_u = 0xFFFF_FFFF'u
|
|
var v2_u = 0xFFFF_FFFF'u
|
|
inc(v1_u)
|
|
check:
|
|
v1_u == 0'u
|
|
v2_u + 1'u == 0'u
|
|
|
|
asyncTest "wait(duration) cancellation undefined behavior test #1":
|
|
proc testInnerFoo(fooFut: Future[void]): Future[TestFooConnection] {.
|
|
async.} =
|
|
await fooFut
|
|
return TestFooConnection()
|
|
|
|
proc testFoo(fooFut: Future[void]) {.async.} =
|
|
let connection =
|
|
try:
|
|
let res = await testInnerFoo(fooFut).wait(10.seconds)
|
|
Result[TestFooConnection, int].ok(res)
|
|
except CancelledError:
|
|
Result[TestFooConnection, int].err(0)
|
|
except CatchableError:
|
|
Result[TestFooConnection, int].err(1)
|
|
check connection.isOk()
|
|
|
|
var future = newFuture[void]("last.child.future")
|
|
var someFut = testFoo(future)
|
|
future.complete()
|
|
discard someFut.tryCancel()
|
|
await someFut
|
|
|
|
asyncTest "wait(duration) cancellation undefined behavior test #2":
|
|
proc testInnerFoo(fooFut: Future[void]): Future[TestFooConnection] {.
|
|
async.} =
|
|
await fooFut
|
|
return TestFooConnection()
|
|
|
|
proc testMiddleFoo(fooFut: Future[void]): Future[TestFooConnection] {.
|
|
async.} =
|
|
await testInnerFoo(fooFut)
|
|
|
|
proc testFoo(fooFut: Future[void]) {.async.} =
|
|
let connection =
|
|
try:
|
|
let res = await testMiddleFoo(fooFut).wait(10.seconds)
|
|
Result[TestFooConnection, int].ok(res)
|
|
except CancelledError:
|
|
Result[TestFooConnection, int].err(0)
|
|
except CatchableError:
|
|
Result[TestFooConnection, int].err(1)
|
|
check connection.isOk()
|
|
|
|
var future = newFuture[void]("last.child.future")
|
|
var someFut = testFoo(future)
|
|
future.complete()
|
|
discard someFut.tryCancel()
|
|
await someFut
|
|
|
|
asyncTest "wait(duration) should allow cancellation test (depends on race())":
|
|
proc testFoo(): Future[bool] {.async.} =
|
|
let
|
|
resFut = sleepAsync(2.seconds).wait(3.seconds)
|
|
timeFut = sleepAsync(1.seconds)
|
|
cancelFut = cancelAndWait(resFut)
|
|
discard await race(cancelFut, timeFut)
|
|
if cancelFut.finished():
|
|
return (resFut.cancelled() and cancelFut.completed())
|
|
false
|
|
|
|
check (await testFoo()) == true
|
|
|
|
asyncTest "withTimeout() cancellation undefined behavior test #1":
|
|
proc testInnerFoo(fooFut: Future[void]): Future[TestFooConnection] {.
|
|
async.} =
|
|
await fooFut
|
|
return TestFooConnection()
|
|
|
|
proc testFoo(fooFut: Future[void]) {.async.} =
|
|
let connection =
|
|
try:
|
|
let
|
|
checkFut = testInnerFoo(fooFut)
|
|
res = await withTimeout(checkFut, 10.seconds)
|
|
if res:
|
|
Result[TestFooConnection, int].ok(checkFut.value)
|
|
else:
|
|
Result[TestFooConnection, int].err(0)
|
|
except CancelledError:
|
|
Result[TestFooConnection, int].err(1)
|
|
except CatchableError:
|
|
Result[TestFooConnection, int].err(2)
|
|
check connection.isOk()
|
|
|
|
var future = newFuture[void]("last.child.future")
|
|
var someFut = testFoo(future)
|
|
future.complete()
|
|
discard someFut.tryCancel()
|
|
await someFut
|
|
|
|
asyncTest "withTimeout() cancellation undefined behavior test #2":
|
|
proc testInnerFoo(fooFut: Future[void]): Future[TestFooConnection] {.
|
|
async.} =
|
|
await fooFut
|
|
return TestFooConnection()
|
|
|
|
proc testMiddleFoo(fooFut: Future[void]): Future[TestFooConnection] {.
|
|
async.} =
|
|
await testInnerFoo(fooFut)
|
|
|
|
proc testFoo(fooFut: Future[void]) {.async.} =
|
|
let connection =
|
|
try:
|
|
let
|
|
checkFut = testMiddleFoo(fooFut)
|
|
res = await withTimeout(checkFut, 10.seconds)
|
|
if res:
|
|
Result[TestFooConnection, int].ok(checkFut.value)
|
|
else:
|
|
Result[TestFooConnection, int].err(0)
|
|
except CancelledError:
|
|
Result[TestFooConnection, int].err(1)
|
|
except CatchableError:
|
|
Result[TestFooConnection, int].err(2)
|
|
check connection.isOk()
|
|
|
|
var future = newFuture[void]("last.child.future")
|
|
var someFut = testFoo(future)
|
|
future.complete()
|
|
discard someFut.tryCancel()
|
|
await someFut
|
|
|
|
asyncTest "withTimeout() should allow cancellation test (depends on race())":
|
|
proc testFoo(): Future[bool] {.async.} =
|
|
let
|
|
resFut = sleepAsync(2.seconds).withTimeout(3.seconds)
|
|
timeFut = sleepAsync(1.seconds)
|
|
cancelFut = cancelAndWait(resFut)
|
|
discard await race(cancelFut, timeFut)
|
|
if cancelFut.finished():
|
|
return (resFut.cancelled() and cancelFut.completed())
|
|
false
|
|
|
|
check (await testFoo()) == true
|
|
|
|
asyncTest "wait(future) cancellation undefined behavior test #1":
|
|
proc testInnerFoo(fooFut: Future[void]): Future[TestFooConnection] {.
|
|
async.} =
|
|
await fooFut
|
|
return TestFooConnection()
|
|
|
|
proc testFoo(fooFut: Future[void]) {.async.} =
|
|
let deadline = sleepAsync(10.seconds)
|
|
let connection =
|
|
try:
|
|
let res = await testInnerFoo(fooFut).wait(deadline)
|
|
Result[TestFooConnection, int].ok(res)
|
|
except CancelledError:
|
|
Result[TestFooConnection, int].err(0)
|
|
except CatchableError:
|
|
Result[TestFooConnection, int].err(1)
|
|
finally:
|
|
await deadline.cancelAndWait()
|
|
|
|
check connection.isOk()
|
|
|
|
var future = newFuture[void]("last.child.future")
|
|
var someFut = testFoo(future)
|
|
future.complete()
|
|
discard someFut.tryCancel()
|
|
await someFut
|
|
|
|
asyncTest "wait(future) cancellation undefined behavior test #2":
|
|
proc testInnerFoo(fooFut: Future[void]): Future[TestFooConnection] {.
|
|
async.} =
|
|
await fooFut
|
|
return TestFooConnection()
|
|
|
|
proc testMiddleFoo(fooFut: Future[void]): Future[TestFooConnection] {.
|
|
async.} =
|
|
await testInnerFoo(fooFut)
|
|
|
|
proc testFoo(fooFut: Future[void]) {.async.} =
|
|
let deadline = sleepAsync(10.seconds)
|
|
let connection =
|
|
try:
|
|
let res = await testMiddleFoo(fooFut).wait(deadline)
|
|
Result[TestFooConnection, int].ok(res)
|
|
except CancelledError:
|
|
Result[TestFooConnection, int].err(0)
|
|
except CatchableError:
|
|
Result[TestFooConnection, int].err(1)
|
|
finally:
|
|
await deadline.cancelAndWait()
|
|
check connection.isOk()
|
|
|
|
var future = newFuture[void]("last.child.future")
|
|
var someFut = testFoo(future)
|
|
future.complete()
|
|
discard someFut.tryCancel()
|
|
await someFut
|
|
|
|
asyncTest "wait(future) should allow cancellation test (depends on race())":
|
|
proc testFoo(): Future[bool] {.async.} =
|
|
let
|
|
deadline = sleepAsync(3.seconds)
|
|
resFut = sleepAsync(2.seconds).wait(deadline)
|
|
timeFut = sleepAsync(1.seconds)
|
|
cancelFut = cancelAndWait(resFut)
|
|
discard await race(cancelFut, timeFut)
|
|
await deadline.cancelAndWait()
|
|
if cancelFut.finished():
|
|
return (resFut.cancelled() and cancelFut.completed())
|
|
false
|
|
|
|
check (await testFoo()) == true
|
|
|
|
asyncTest "Cancellation behavior test":
|
|
proc testInnerFoo(fooFut: Future[void]) {.async.} =
|
|
await fooFut
|
|
|
|
proc testMiddleFoo(fooFut: Future[void]) {.async.} =
|
|
await testInnerFoo(fooFut)
|
|
|
|
proc testOuterFoo(fooFut: Future[void]) {.async.} =
|
|
await testMiddleFoo(fooFut)
|
|
|
|
block:
|
|
# Cancellation of pending Future
|
|
let future = newFuture[void]("last.child.pending.future")
|
|
await cancelAndWait(future)
|
|
check:
|
|
future.cancelled() == true
|
|
|
|
block:
|
|
# Cancellation of completed Future
|
|
let future = newFuture[void]("last.child.completed.future")
|
|
future.complete()
|
|
await cancelAndWait(future)
|
|
check:
|
|
future.cancelled() == false
|
|
future.completed() == true
|
|
|
|
block:
|
|
# Cancellation of failed Future
|
|
let future = newFuture[void]("last.child.failed.future")
|
|
future.fail(newException(ValueError, "ABCD"))
|
|
await cancelAndWait(future)
|
|
check:
|
|
future.cancelled() == false
|
|
future.failed() == true
|
|
|
|
block:
|
|
# Cancellation of already cancelled Future
|
|
let future = newFuture[void]("last.child.cancelled.future")
|
|
future.cancelAndSchedule()
|
|
await cancelAndWait(future)
|
|
check:
|
|
future.cancelled() == true
|
|
|
|
block:
|
|
# Cancellation of Pending->Pending->Pending->Pending sequence
|
|
let future = newFuture[void]("last.child.pending.future")
|
|
let testFut = testOuterFoo(future)
|
|
await cancelAndWait(testFut)
|
|
check:
|
|
testFut.cancelled() == true
|
|
|
|
block:
|
|
# Cancellation of Pending->Pending->Pending->Completed sequence
|
|
let future = newFuture[void]("last.child.completed.future")
|
|
let testFut = testOuterFoo(future)
|
|
future.complete()
|
|
await cancelAndWait(testFut)
|
|
check:
|
|
testFut.cancelled() == false
|
|
testFut.completed() == true
|
|
|
|
block:
|
|
# Cancellation of Pending->Pending->Pending->Failed sequence
|
|
let future = newFuture[void]("last.child.failed.future")
|
|
let testFut = testOuterFoo(future)
|
|
future.fail(newException(ValueError, "ABCD"))
|
|
await cancelAndWait(testFut)
|
|
check:
|
|
testFut.cancelled() == false
|
|
testFut.failed() == true
|
|
|
|
block:
|
|
# Cancellation of Pending->Pending->Pending->Cancelled sequence
|
|
let future = newFuture[void]("last.child.cancelled.future")
|
|
let testFut = testOuterFoo(future)
|
|
future.cancelAndSchedule()
|
|
await cancelAndWait(testFut)
|
|
check:
|
|
testFut.cancelled() == true
|
|
|
|
block:
|
|
# Cancellation of pending Future, when automatic scheduling disabled
|
|
let future = newFuture[void]("last.child.pending.future",
|
|
{FutureFlag.OwnCancelSchedule})
|
|
proc cancellation(udata: pointer) {.gcsafe.} =
|
|
discard
|
|
future.cancelCallback = cancellation
|
|
# Note, future will never be finished in such case, until we manually not
|
|
# finish it
|
|
let cancelFut = cancelAndWait(future)
|
|
await sleepAsync(100.milliseconds)
|
|
check:
|
|
cancelFut.finished() == false
|
|
future.cancelled() == false
|
|
# Now we manually changing Future's state, so `cancelAndWait` could
|
|
# finish
|
|
future.complete()
|
|
await cancelFut
|
|
check:
|
|
cancelFut.finished() == true
|
|
future.cancelled() == false
|
|
future.finished() == true
|
|
|
|
block:
|
|
# Cancellation of pending Future, which will fail Future on cancellation,
|
|
# when automatic scheduling disabled
|
|
let future = newFuture[void]("last.child.completed.future",
|
|
{FutureFlag.OwnCancelSchedule})
|
|
proc cancellation(udata: pointer) {.gcsafe.} =
|
|
future.complete()
|
|
future.cancelCallback = cancellation
|
|
# Note, future will never be finished in such case, until we manually not
|
|
# finish it
|
|
await cancelAndWait(future)
|
|
check:
|
|
future.cancelled() == false
|
|
future.completed() == true
|
|
|
|
block:
|
|
# Cancellation of pending Future, which will fail Future on cancellation,
|
|
# when automatic scheduling disabled
|
|
let future = newFuture[void]("last.child.failed.future",
|
|
{FutureFlag.OwnCancelSchedule})
|
|
proc cancellation(udata: pointer) {.gcsafe.} =
|
|
future.fail(newException(ValueError, "ABCD"))
|
|
future.cancelCallback = cancellation
|
|
# Note, future will never be finished in such case, until we manually not
|
|
# finish it
|
|
await cancelAndWait(future)
|
|
check:
|
|
future.cancelled() == false
|
|
future.failed() == true
|
|
|
|
block:
|
|
# Cancellation of pending Future, which will fail Future on cancellation,
|
|
# when automatic scheduling disabled
|
|
let future = newFuture[void]("last.child.cancelled.future",
|
|
{FutureFlag.OwnCancelSchedule})
|
|
proc cancellation(udata: pointer) {.gcsafe.} =
|
|
future.cancelAndSchedule()
|
|
future.cancelCallback = cancellation
|
|
# Note, future will never be finished in such case, until we manually not
|
|
# finish it
|
|
await cancelAndWait(future)
|
|
check:
|
|
future.cancelled() == true
|
|
|
|
block:
|
|
# Cancellation of pending Pending->Pending->Pending->Pending, when
|
|
# automatic scheduling disabled and Future do nothing in cancellation
|
|
# callback
|
|
let future = newFuture[void]("last.child.pending.future",
|
|
{FutureFlag.OwnCancelSchedule})
|
|
proc cancellation(udata: pointer) {.gcsafe.} =
|
|
discard
|
|
future.cancelCallback = cancellation
|
|
# Note, future will never be finished in such case, until we manually not
|
|
# finish it
|
|
let testFut = testOuterFoo(future)
|
|
let cancelFut = cancelAndWait(testFut)
|
|
await sleepAsync(100.milliseconds)
|
|
check:
|
|
cancelFut.finished() == false
|
|
testFut.cancelled() == false
|
|
future.cancelled() == false
|
|
# Now we manually changing Future's state, so `cancelAndWait` could
|
|
# finish
|
|
future.complete()
|
|
await cancelFut
|
|
check:
|
|
cancelFut.finished() == true
|
|
future.cancelled() == false
|
|
future.finished() == true
|
|
testFut.cancelled() == false
|
|
testFut.finished() == true
|
|
|
|
block:
|
|
# Cancellation of pending Pending->Pending->Pending->Pending, when
|
|
# automatic scheduling disabled and Future completes in cancellation
|
|
# callback
|
|
let future = newFuture[void]("last.child.pending.future",
|
|
{FutureFlag.OwnCancelSchedule})
|
|
proc cancellation(udata: pointer) {.gcsafe.} =
|
|
future.complete()
|
|
future.cancelCallback = cancellation
|
|
# Note, future will never be finished in such case, until we manually not
|
|
# finish it
|
|
let testFut = testOuterFoo(future)
|
|
await cancelAndWait(testFut)
|
|
await sleepAsync(100.milliseconds)
|
|
check:
|
|
testFut.cancelled() == false
|
|
testFut.finished() == true
|
|
future.cancelled() == false
|
|
future.finished() == true
|
|
|
|
block:
|
|
# Cancellation of pending Pending->Pending->Pending->Pending, when
|
|
# automatic scheduling disabled and Future fails in cancellation callback
|
|
let future = newFuture[void]("last.child.pending.future",
|
|
{FutureFlag.OwnCancelSchedule})
|
|
proc cancellation(udata: pointer) {.gcsafe.} =
|
|
future.fail(newException(ValueError, "ABCD"))
|
|
future.cancelCallback = cancellation
|
|
# Note, future will never be finished in such case, until we manually not
|
|
# finish it
|
|
let testFut = testOuterFoo(future)
|
|
await cancelAndWait(testFut)
|
|
await sleepAsync(100.milliseconds)
|
|
check:
|
|
testFut.cancelled() == false
|
|
testFut.failed() == true
|
|
future.cancelled() == false
|
|
future.failed() == true
|
|
|
|
block:
|
|
# Cancellation of pending Pending->Pending->Pending->Pending, when
|
|
# automatic scheduling disabled and Future fails in cancellation callback
|
|
let future = newFuture[void]("last.child.pending.future",
|
|
{FutureFlag.OwnCancelSchedule})
|
|
proc cancellation(udata: pointer) {.gcsafe.} =
|
|
future.cancelAndSchedule()
|
|
future.cancelCallback = cancellation
|
|
# Note, future will never be finished in such case, until we manually not
|
|
# finish it
|
|
let testFut = testOuterFoo(future)
|
|
await cancelAndWait(testFut)
|
|
await sleepAsync(100.milliseconds)
|
|
check:
|
|
testFut.cancelled() == true
|
|
future.cancelled() == true
|
|
|
|
test "Issue #334 test":
|
|
proc test(): bool =
|
|
var testres = ""
|
|
|
|
proc a() {.async.} =
|
|
try:
|
|
await sleepAsync(seconds(1))
|
|
except CatchableError as exc:
|
|
testres.add("A")
|
|
raise exc
|
|
|
|
proc b() {.async.} =
|
|
try:
|
|
await a()
|
|
except CatchableError as exc:
|
|
testres.add("B")
|
|
raise exc
|
|
|
|
proc c() {.async.} =
|
|
try:
|
|
echo $(await b().withTimeout(seconds(2)))
|
|
except CatchableError as exc:
|
|
testres.add("C")
|
|
raise exc
|
|
|
|
let x = c()
|
|
x.cancelSoon()
|
|
|
|
try:
|
|
waitFor x
|
|
except CatchableError:
|
|
testres.add("D")
|
|
|
|
testres.add("E")
|
|
|
|
waitFor sleepAsync(milliseconds(100))
|
|
|
|
testres == "ABCDE"
|
|
|
|
check test() == true
|
|
|
|
asyncTest "cancelAndWait() should be able to cancel test":
|
|
proc test1() {.async.} =
|
|
await noCancel sleepAsync(100.milliseconds)
|
|
await noCancel sleepAsync(100.milliseconds)
|
|
await sleepAsync(100.milliseconds)
|
|
|
|
proc test2() {.async.} =
|
|
await noCancel sleepAsync(100.milliseconds)
|
|
await sleepAsync(100.milliseconds)
|
|
await noCancel sleepAsync(100.milliseconds)
|
|
|
|
proc test3() {.async.} =
|
|
await sleepAsync(100.milliseconds)
|
|
await noCancel sleepAsync(100.milliseconds)
|
|
await noCancel sleepAsync(100.milliseconds)
|
|
|
|
proc test4() {.async.} =
|
|
while true:
|
|
await noCancel sleepAsync(50.milliseconds)
|
|
await sleepAsync(0.milliseconds)
|
|
|
|
proc test5() {.async.} =
|
|
while true:
|
|
await sleepAsync(0.milliseconds)
|
|
await noCancel sleepAsync(50.milliseconds)
|
|
|
|
block:
|
|
let future1 = test1()
|
|
await cancelAndWait(future1)
|
|
let future2 = test1()
|
|
await sleepAsync(10.milliseconds)
|
|
await cancelAndWait(future2)
|
|
check:
|
|
future1.cancelled() == true
|
|
future2.cancelled() == true
|
|
|
|
block:
|
|
let future1 = test2()
|
|
await cancelAndWait(future1)
|
|
let future2 = test2()
|
|
await sleepAsync(10.milliseconds)
|
|
await cancelAndWait(future2)
|
|
check:
|
|
future1.cancelled() == true
|
|
future2.cancelled() == true
|
|
|
|
block:
|
|
let future1 = test3()
|
|
await cancelAndWait(future1)
|
|
let future2 = test3()
|
|
await sleepAsync(10.milliseconds)
|
|
await cancelAndWait(future2)
|
|
check:
|
|
future1.cancelled() == true
|
|
future2.cancelled() == true
|
|
|
|
block:
|
|
let future1 = test4()
|
|
await cancelAndWait(future1)
|
|
let future2 = test4()
|
|
await sleepAsync(333.milliseconds)
|
|
await cancelAndWait(future2)
|
|
check:
|
|
future1.cancelled() == true
|
|
future2.cancelled() == true
|
|
|
|
block:
|
|
let future1 = test5()
|
|
await cancelAndWait(future1)
|
|
let future2 = test5()
|
|
await sleepAsync(333.milliseconds)
|
|
await cancelAndWait(future2)
|
|
check:
|
|
future1.cancelled() == true
|
|
future2.cancelled() == true
|
|
|
|
asyncTest "join() test":
|
|
proc joinFoo0(future: FutureBase) {.async.} =
|
|
await join(future)
|
|
|
|
proc joinFoo1(future: Future[void]) {.async.} =
|
|
await join(future)
|
|
|
|
proc joinFoo2(future: Future[void]) {.
|
|
async: (raises: [CancelledError]).} =
|
|
await join(future)
|
|
|
|
let
|
|
future0 = newFuture[void]()
|
|
future1 = newFuture[void]()
|
|
future2 = Future[void].Raising([CancelledError]).init()
|
|
|
|
let
|
|
resfut0 = joinFoo0(future0)
|
|
resfut1 = joinFoo1(future1)
|
|
resfut2 = joinFoo2(future2)
|
|
|
|
check:
|
|
resfut0.finished() == false
|
|
resfut1.finished() == false
|
|
resfut2.finished() == false
|
|
|
|
future0.complete()
|
|
future1.complete()
|
|
future2.complete()
|
|
|
|
let res =
|
|
try:
|
|
await noCancel allFutures(resfut0, resfut1, resfut2).wait(1.seconds)
|
|
true
|
|
except AsyncTimeoutError:
|
|
false
|
|
|
|
check:
|
|
res == true
|
|
resfut0.finished() == true
|
|
resfut1.finished() == true
|
|
resfut2.finished() == true
|
|
future0.finished() == true
|
|
future1.finished() == true
|
|
future2.finished() == true
|
|
|
|
asyncTest "join() cancellation test":
|
|
proc joinFoo0(future: FutureBase) {.async.} =
|
|
await join(future)
|
|
|
|
proc joinFoo1(future: Future[void]) {.async.} =
|
|
await join(future)
|
|
|
|
proc joinFoo2(future: Future[void]) {.
|
|
async: (raises: [CancelledError]).} =
|
|
await join(future)
|
|
|
|
let
|
|
future0 = newFuture[void]()
|
|
future1 = newFuture[void]()
|
|
future2 = Future[void].Raising([CancelledError]).init()
|
|
|
|
let
|
|
resfut0 = joinFoo0(future0)
|
|
resfut1 = joinFoo1(future1)
|
|
resfut2 = joinFoo2(future2)
|
|
|
|
check:
|
|
resfut0.finished() == false
|
|
resfut1.finished() == false
|
|
resfut2.finished() == false
|
|
|
|
let
|
|
cancelfut0 = cancelAndWait(resfut0)
|
|
cancelfut1 = cancelAndWait(resfut1)
|
|
cancelfut2 = cancelAndWait(resfut2)
|
|
|
|
let res =
|
|
try:
|
|
await noCancel allFutures(cancelfut0, cancelfut1,
|
|
cancelfut2).wait(1.seconds)
|
|
true
|
|
except AsyncTimeoutError:
|
|
false
|
|
|
|
check:
|
|
res == true
|
|
cancelfut0.finished() == true
|
|
cancelfut1.finished() == true
|
|
cancelfut2.finished() == true
|
|
resfut0.cancelled() == true
|
|
resfut1.cancelled() == true
|
|
resfut2.cancelled() == true
|
|
future0.finished() == false
|
|
future1.finished() == false
|
|
future2.finished() == false
|
|
|
|
future0.complete()
|
|
future1.complete()
|
|
future2.complete()
|
|
|
|
check:
|
|
future0.finished() == true
|
|
future1.finished() == true
|
|
future2.finished() == true
|
|
|
|
test "Sink with literals":
|
|
# https://github.com/nim-lang/Nim/issues/22175
|
|
let fut = newFuture[string]()
|
|
fut.complete("test")
|
|
check:
|
|
fut.value() == "test"
|
|
|
|
test "Raising type matching":
|
|
type X[E] = Future[void].Raising(E)
|
|
|
|
proc f(x: X) = discard
|
|
|
|
var v: Future[void].Raising([ValueError])
|
|
f(v)
|
|
|
|
type Object = object
|
|
# TODO cannot use X[[ValueError]] here..
|
|
field: Future[void].Raising([ValueError])
|
|
discard Object(field: v)
|
|
|
|
check:
|
|
not compiles(Future[void].Raising([42]))
|
|
not compiles(Future[void].Raising(42))
|
|
|
|
asyncTest "Timeout/cancellation race wait(duration) test":
|
|
proc raceTest(T: typedesc, itype: int) {.async.} =
|
|
let monitorFuture = newFuture[T]("monitor",
|
|
{FutureFlag.OwnCancelSchedule})
|
|
|
|
proc raceProc0(future: Future[T]): Future[T] {.async.} =
|
|
await future
|
|
proc raceProc1(future: Future[T]): Future[T] {.async.} =
|
|
await raceProc0(future)
|
|
proc raceProc2(future: Future[T]): Future[T] {.async.} =
|
|
await raceProc1(future)
|
|
|
|
proc activation(udata: pointer) {.gcsafe.} =
|
|
if itype == 0:
|
|
when T is void:
|
|
monitorFuture.complete()
|
|
elif T is int:
|
|
monitorFuture.complete(100)
|
|
elif itype == 1:
|
|
monitorFuture.fail(newException(ValueError, "test"))
|
|
else:
|
|
monitorFuture.cancelAndSchedule()
|
|
|
|
monitorFuture.cancelCallback = activation
|
|
let
|
|
testFut = raceProc2(monitorFuture)
|
|
waitFut = wait(testFut, 10.milliseconds)
|
|
|
|
when T is void:
|
|
let waitRes =
|
|
try:
|
|
await waitFut
|
|
if itype == 0:
|
|
true
|
|
else:
|
|
false
|
|
except CancelledError:
|
|
false
|
|
except CatchableError:
|
|
if itype != 0:
|
|
true
|
|
else:
|
|
false
|
|
check waitRes == true
|
|
elif T is int:
|
|
let waitRes =
|
|
try:
|
|
let res = await waitFut
|
|
if itype == 0:
|
|
(true, res)
|
|
else:
|
|
(false, -1)
|
|
except CancelledError:
|
|
(false, -1)
|
|
except CatchableError:
|
|
if itype != 0:
|
|
(true, 0)
|
|
else:
|
|
(false, -1)
|
|
if itype == 0:
|
|
check:
|
|
waitRes[0] == true
|
|
waitRes[1] == 100
|
|
else:
|
|
check:
|
|
waitRes[0] == true
|
|
|
|
await raceTest(void, 0)
|
|
await raceTest(void, 1)
|
|
await raceTest(void, 2)
|
|
await raceTest(int, 0)
|
|
await raceTest(int, 1)
|
|
await raceTest(int, 2)
|
|
|
|
asyncTest "Timeout/cancellation race wait(future) test":
|
|
proc raceTest(T: typedesc, itype: int) {.async.} =
|
|
let monitorFuture = newFuture[T]()
|
|
|
|
proc raceProc0(future: Future[T]): Future[T] {.async.} =
|
|
await future
|
|
proc raceProc1(future: Future[T]): Future[T] {.async.} =
|
|
await raceProc0(future)
|
|
proc raceProc2(future: Future[T]): Future[T] {.async.} =
|
|
await raceProc1(future)
|
|
|
|
proc continuation(udata: pointer) {.gcsafe.} =
|
|
if itype == 0:
|
|
when T is void:
|
|
monitorFuture.complete()
|
|
elif T is int:
|
|
monitorFuture.complete(100)
|
|
elif itype == 1:
|
|
monitorFuture.fail(newException(ValueError, "test"))
|
|
else:
|
|
monitorFuture.cancelAndSchedule()
|
|
|
|
let deadlineFuture = newFuture[void]()
|
|
deadlineFuture.addCallback continuation
|
|
|
|
let
|
|
testFut = raceProc2(monitorFuture)
|
|
waitFut = wait(testFut, deadlineFuture)
|
|
|
|
deadlineFuture.complete()
|
|
|
|
when T is void:
|
|
let waitRes =
|
|
try:
|
|
await waitFut
|
|
if itype == 0:
|
|
true
|
|
else:
|
|
false
|
|
except CancelledError:
|
|
false
|
|
except CatchableError:
|
|
if itype != 0:
|
|
true
|
|
else:
|
|
false
|
|
check waitRes == true
|
|
elif T is int:
|
|
let waitRes =
|
|
try:
|
|
let res = await waitFut
|
|
if itype == 0:
|
|
(true, res)
|
|
else:
|
|
(false, -1)
|
|
except CancelledError:
|
|
(false, -1)
|
|
except CatchableError:
|
|
if itype != 0:
|
|
(true, 0)
|
|
else:
|
|
(false, -1)
|
|
if itype == 0:
|
|
check:
|
|
waitRes[0] == true
|
|
waitRes[1] == 100
|
|
else:
|
|
check:
|
|
waitRes[0] == true
|
|
|
|
await raceTest(void, 0)
|
|
await raceTest(void, 1)
|
|
await raceTest(void, 2)
|
|
await raceTest(int, 0)
|
|
await raceTest(int, 1)
|
|
await raceTest(int, 2)
|
|
|
|
asyncTest "Timeout/cancellation race withTimeout() test":
|
|
proc raceTest(T: typedesc, itype: int) {.async.} =
|
|
let monitorFuture = newFuture[T]("monitor",
|
|
{FutureFlag.OwnCancelSchedule})
|
|
|
|
proc raceProc0(future: Future[T]): Future[T] {.async.} =
|
|
await future
|
|
proc raceProc1(future: Future[T]): Future[T] {.async.} =
|
|
await raceProc0(future)
|
|
proc raceProc2(future: Future[T]): Future[T] {.async.} =
|
|
await raceProc1(future)
|
|
|
|
proc activation(udata: pointer) {.gcsafe.} =
|
|
if itype == 0:
|
|
when T is void:
|
|
monitorFuture.complete()
|
|
elif T is int:
|
|
monitorFuture.complete(100)
|
|
elif itype == 1:
|
|
monitorFuture.fail(newException(ValueError, "test"))
|
|
else:
|
|
monitorFuture.cancelAndSchedule()
|
|
|
|
monitorFuture.cancelCallback = activation
|
|
let
|
|
testFut = raceProc2(monitorFuture)
|
|
waitFut = withTimeout(testFut, 10.milliseconds)
|
|
|
|
when T is void:
|
|
let waitRes =
|
|
try:
|
|
await waitFut
|
|
except CancelledError:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
if itype == 0:
|
|
check waitRes == true
|
|
elif itype == 1:
|
|
check waitRes == true
|
|
else:
|
|
check waitRes == false
|
|
elif T is int:
|
|
let waitRes =
|
|
try:
|
|
await waitFut
|
|
except CancelledError:
|
|
false
|
|
except CatchableError:
|
|
false
|
|
if itype == 0:
|
|
check waitRes == true
|
|
elif itype == 1:
|
|
check waitRes == true
|
|
else:
|
|
check waitRes == false
|
|
|
|
await raceTest(void, 0)
|
|
await raceTest(void, 1)
|
|
await raceTest(void, 2)
|
|
await raceTest(int, 0)
|
|
await raceTest(int, 1)
|
|
await raceTest(int, 2)
|