Add awaitne command which will have yield behavior in async cancellation world.

Add tests for both `await` and `awaitne`.
This commit is contained in:
cheatfate 2019-07-06 11:16:31 +03:00
parent 45ca9e1184
commit 8ba4fc9876
No known key found for this signature in database
GPG Key ID: 46ADD633A7201F95
4 changed files with 116 additions and 22 deletions

View File

@ -6,5 +6,5 @@
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import chronos/[asyncloop, asyncfutures2, asyncsync, handles, transport,
timer]
export asyncloop, asyncfutures2, asyncsync, handles, transport, timer
timer, version]
export asyncloop, asyncfutures2, asyncsync, handles, transport, timer, version

View File

@ -66,7 +66,7 @@ template createCb(retFutureSym, iteratorNameSym,
#{.pop.}
template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
rootReceiver: untyped, fromNode: NimNode) =
rootReceiver: untyped, fromNode: NimNode, isawait: bool) =
## Params:
## futureVarNode: The NimNode which is a symbol identifying the Future[T]
## variable to yield.
@ -75,16 +75,22 @@ template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
## future's value.
##
## rootReceiver: ??? TODO
# -> yield future<x>
result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
# -> future<x>.read
valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
result.add rootReceiver
if isawait:
# -> yield future<x>
result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
# -> future<x>.read
valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
result.add rootReceiver
else:
# -> yield future<x>
result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
valueReceiver = futureVarNode
result.add rootReceiver
template createVar(result: var NimNode, futSymName: string,
asyncProc: NimNode,
valueReceiver, rootReceiver, retFutSym: untyped,
fromNode: NimNode) =
fromNode: NimNode, isawait: bool) =
result = newNimNode(nnkStmtList, fromNode)
var futSym = genSym(nskVar, "future")
result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
@ -96,7 +102,7 @@ template createVar(result: var NimNode, futSymName: string,
),
newCall(newIdentNode("FutureBase"), copyNimNode(futSym))
)
useVar(result, futSym, valueReceiver, rootReceiver, fromNode)
useVar(result, futSym, valueReceiver, rootReceiver, fromNode, isawait)
proc createFutureVarCompletions(futureVarIdents: seq[NimNode],
fromNode: NimNode): NimNode {.compileTime.} =
@ -144,7 +150,8 @@ proc processBody(node, retFutureSym: NimNode,
result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
return # Don't process the children of this return stmt
of nnkCommand, nnkCall:
if node[0].kind == nnkIdent and node[0].eqIdent("await"):
if node[0].kind == nnkIdent and
(node[0].eqIdent("await") or node[0].eqIdent("awaitne")):
case node[1].kind
of nnkIdent, nnkInfix, nnkDotExpr, nnkCall, nnkCommand:
# await x
@ -153,41 +160,48 @@ proc processBody(node, retFutureSym: NimNode,
# await foo p, x
var futureValue: NimNode
result.createVar("future" & $node[1][0].toStrLit, node[1], futureValue,
futureValue, retFutureSym, node)
futureValue, retFutureSym, node,
node[0].eqIdent("await"))
else:
error("Invalid node kind in 'await', got: " & $node[1].kind)
elif node.len > 1 and node[1].kind == nnkCommand and
node[1][0].kind == nnkIdent and node[1][0].eqIdent("await"):
node[1][0].kind == nnkIdent and
(node[1][0].eqIdent("await") or node[1][0].eqIdent("awaitne")):
# foo await x
var newCommand = node
result.createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1],
newCommand, retFutureSym, node)
newCommand, retFutureSym, node,
node[1][0].eqIdent("await"))
of nnkVarSection, nnkLetSection:
case node[0][2].kind
of nnkCommand:
if node[0][2][0].kind == nnkIdent and node[0][2][0].eqIdent("await"):
if node[0][2][0].kind == nnkIdent and
(node[0][2][0].eqIdent("await") or node[0][2][0].eqIdent("awaitne")):
# var x = await y
var newVarSection = node # TODO: Should this use copyNimNode?
result.createVar("future" & node[0][0].strVal, node[0][2][1],
newVarSection[0][2], newVarSection, retFutureSym, node)
newVarSection[0][2], newVarSection, retFutureSym, node,
node[0][2][0].eqIdent("await"))
else: discard
of nnkAsgn:
case node[1].kind
of nnkCommand:
if node[1][0].eqIdent("await"):
if node[1][0].eqIdent("await") or node[1][0].eqIdent("awaitne"):
# x = await y
var newAsgn = node
result.createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1],
newAsgn, retFutureSym, node)
newAsgn, retFutureSym, node,
node[1][0].eqIdent("await"))
else: discard
of nnkDiscardStmt:
# discard await x
if node[0].kind == nnkCommand and node[0][0].kind == nnkIdent and
node[0][0].eqIdent("await"):
(node[0][0].eqIdent("await") or node[0][0].eqIdent("awaitne")):
var newDiscard = node
result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
newDiscard[0], newDiscard, retFutureSym, node)
newDiscard[0], newDiscard, retFutureSym, node,
node[0][0].eqIdent("await"))
else: discard
for i in 0 ..< result.len:

View File

@ -5,6 +5,6 @@
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import testsync, testsoon, testtime, testfut, testsignal, testaddress,
testdatagram, teststream, testserver, testbugs, testnet,
import testmacro, testsync, testsoon, testtime, testfut, testsignal,
testaddress, testdatagram, teststream, testserver, testbugs, testnet,
testasyncstream

80
tests/testmacro.nim Normal file
View File

@ -0,0 +1,80 @@
# 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 unittest
import ../chronos
proc asyncRetValue(n: int): Future[int] {.async.} =
await sleepAsync(n.milliseconds)
result = n * 10
proc asyncRetVoid(n: int) {.async.} =
await sleepAsync(n.milliseconds)
proc asyncRetExceptionValue(n: int): Future[int] {.async.} =
await sleepAsync(n.milliseconds)
result = n * 10
if true:
raise newException(ValueError, "Test exception")
proc asyncRetExceptionVoid(n: int) {.async.} =
await sleepAsync(n.milliseconds)
if true:
raise newException(ValueError, "Test exception")
proc testAwait(): Future[bool] {.async.} =
var res: int
await asyncRetVoid(100)
res = await asyncRetValue(100)
if res != 1000:
return false
if (await asyncRetValue(100)) != 1000:
return false
try:
await asyncRetExceptionVoid(100)
return false
except ValueError:
discard
res = 0
try:
var res = await asyncRetExceptionValue(100)
return false
except ValueError:
discard
if res != 0:
return false
return true
proc testAwaitne(): Future[bool] {.async.} =
var res1: Future[void]
var res2: Future[int]
res1 = awaitne asyncRetVoid(100)
res2 = awaitne asyncRetValue(100)
if res1.failed():
return false
if res2.read() != 1000:
return false
res1 = awaitne asyncRetExceptionVoid(100)
if not(res1.failed()):
return false
res2 = awaitne asyncRetExceptionValue(100)
try:
var res = res2.read()
return false
except ValueError:
discard
return true
suite "Macro transformations test suite":
test "`await` command test":
check waitFor(testAwait()) == true
test "`awaitne` command test":
check waitFor(testAwaitne()) == true