mirror of
https://github.com/status-im/nim-chronos.git
synced 2025-02-01 14:05:59 +00:00
async
proc
types (#346)
Allow `type = proc(...) {.async.}` for pre-declaring async callback types - in this case, the macro applies the correct transformation to the return type and adds the appropriate exception / gcsafe annotations
This commit is contained in:
parent
945c304197
commit
c65cc4c136
@ -221,7 +221,7 @@ const
|
|||||||
SentinelCallback = AsyncCallback(function: sentinelCallbackImpl,
|
SentinelCallback = AsyncCallback(function: sentinelCallbackImpl,
|
||||||
udata: nil)
|
udata: nil)
|
||||||
|
|
||||||
proc isSentinel(acb: AsyncCallback): bool {.raises: [Defect].} =
|
proc isSentinel(acb: AsyncCallback): bool =
|
||||||
acb == SentinelCallback
|
acb == SentinelCallback
|
||||||
|
|
||||||
proc `<`(a, b: TimerCallback): bool =
|
proc `<`(a, b: TimerCallback): bool =
|
||||||
@ -896,22 +896,22 @@ proc removeTimer*(at: uint64, cb: CallbackFunc, udata: pointer = nil) {.
|
|||||||
inline, deprecated: "Use removeTimer(Duration, cb, udata)".} =
|
inline, deprecated: "Use removeTimer(Duration, cb, udata)".} =
|
||||||
removeTimer(Moment.init(int64(at), Millisecond), cb, udata)
|
removeTimer(Moment.init(int64(at), Millisecond), cb, udata)
|
||||||
|
|
||||||
proc callSoon*(acb: AsyncCallback) {.gcsafe, raises: [Defect].} =
|
proc callSoon*(acb: AsyncCallback) =
|
||||||
## Schedule `cbproc` to be called as soon as possible.
|
## Schedule `cbproc` to be called as soon as possible.
|
||||||
## The callback is called when control returns to the event loop.
|
## The callback is called when control returns to the event loop.
|
||||||
getThreadDispatcher().callbacks.addLast(acb)
|
getThreadDispatcher().callbacks.addLast(acb)
|
||||||
|
|
||||||
proc callSoon*(cbproc: CallbackFunc, data: pointer) {.
|
proc callSoon*(cbproc: CallbackFunc, data: pointer) {.
|
||||||
gcsafe, raises: [Defect].} =
|
gcsafe.} =
|
||||||
## Schedule `cbproc` to be called as soon as possible.
|
## Schedule `cbproc` to be called as soon as possible.
|
||||||
## The callback is called when control returns to the event loop.
|
## The callback is called when control returns to the event loop.
|
||||||
doAssert(not isNil(cbproc))
|
doAssert(not isNil(cbproc))
|
||||||
callSoon(AsyncCallback(function: cbproc, udata: data))
|
callSoon(AsyncCallback(function: cbproc, udata: data))
|
||||||
|
|
||||||
proc callSoon*(cbproc: CallbackFunc) {.gcsafe, raises: [Defect].} =
|
proc callSoon*(cbproc: CallbackFunc) =
|
||||||
callSoon(cbproc, nil)
|
callSoon(cbproc, nil)
|
||||||
|
|
||||||
proc callIdle*(acb: AsyncCallback) {.gcsafe, raises: [Defect].} =
|
proc callIdle*(acb: AsyncCallback) =
|
||||||
## Schedule ``cbproc`` to be called when there no pending network events
|
## Schedule ``cbproc`` to be called when there no pending network events
|
||||||
## available.
|
## available.
|
||||||
##
|
##
|
||||||
@ -920,8 +920,7 @@ proc callIdle*(acb: AsyncCallback) {.gcsafe, raises: [Defect].} =
|
|||||||
## actually "idle".
|
## actually "idle".
|
||||||
getThreadDispatcher().idlers.addLast(acb)
|
getThreadDispatcher().idlers.addLast(acb)
|
||||||
|
|
||||||
proc callIdle*(cbproc: CallbackFunc, data: pointer) {.
|
proc callIdle*(cbproc: CallbackFunc, data: pointer) =
|
||||||
gcsafe, raises: [Defect].} =
|
|
||||||
## Schedule ``cbproc`` to be called when there no pending network events
|
## Schedule ``cbproc`` to be called when there no pending network events
|
||||||
## available.
|
## available.
|
||||||
##
|
##
|
||||||
@ -931,7 +930,7 @@ proc callIdle*(cbproc: CallbackFunc, data: pointer) {.
|
|||||||
doAssert(not isNil(cbproc))
|
doAssert(not isNil(cbproc))
|
||||||
callIdle(AsyncCallback(function: cbproc, udata: data))
|
callIdle(AsyncCallback(function: cbproc, udata: data))
|
||||||
|
|
||||||
proc callIdle*(cbproc: CallbackFunc) {.gcsafe, raises: [Defect].} =
|
proc callIdle*(cbproc: CallbackFunc) =
|
||||||
callIdle(cbproc, nil)
|
callIdle(cbproc, nil)
|
||||||
|
|
||||||
include asyncfutures2
|
include asyncfutures2
|
||||||
|
@ -88,13 +88,12 @@ proc cleanupOpenSymChoice(node: NimNode): NimNode {.compileTime.} =
|
|||||||
proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
|
proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
|
||||||
## This macro transforms a single procedure into a closure iterator.
|
## This macro transforms a single procedure into a closure iterator.
|
||||||
## The ``async`` macro supports a stmtList holding multiple async procedures.
|
## The ``async`` macro supports a stmtList holding multiple async procedures.
|
||||||
if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}:
|
if prc.kind notin {nnkProcTy, nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}:
|
||||||
error("Cannot transform this node kind into an async proc." &
|
error("Cannot transform " & $prc.kind & " into an async proc." &
|
||||||
" proc/method definition or lambda node expected.")
|
" proc/method definition or lambda node expected.")
|
||||||
|
|
||||||
let prcName = prc.name.getName
|
let returnType =
|
||||||
|
cleanupOpenSymChoice(if prc.kind == nnkProcTy: prc[0][0] else: prc.params[0])
|
||||||
let returnType = cleanupOpenSymChoice(prc.params[0])
|
|
||||||
var baseType: NimNode
|
var baseType: NimNode
|
||||||
# Verify that the return type is a Future[T]
|
# Verify that the return type is a Future[T]
|
||||||
if returnType.kind == nnkBracketExpr:
|
if returnType.kind == nnkBracketExpr:
|
||||||
@ -109,150 +108,165 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
|
|||||||
let subtypeIsVoid = returnType.kind == nnkEmpty or
|
let subtypeIsVoid = returnType.kind == nnkEmpty or
|
||||||
(baseType.kind == nnkIdent and returnType[1].eqIdent("void"))
|
(baseType.kind == nnkIdent and returnType[1].eqIdent("void"))
|
||||||
|
|
||||||
var outerProcBody = newNimNode(nnkStmtList, prc.body)
|
if prc.kind in {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}:
|
||||||
|
|
||||||
# Copy comment for nimdoc
|
|
||||||
if prc.body.len > 0 and prc.body[0].kind == nnkCommentStmt:
|
|
||||||
outerProcBody.add(prc.body[0])
|
|
||||||
|
|
||||||
|
|
||||||
# -> iterator nameIter(chronosInternalRetFuture: Future[T]): FutureBase {.closure.} =
|
|
||||||
# -> {.push warning[resultshadowed]: off.}
|
|
||||||
# -> var result: T
|
|
||||||
# -> {.pop.}
|
|
||||||
# -> <proc_body>
|
|
||||||
# -> complete(chronosInternalRetFuture, result)
|
|
||||||
let internalFutureSym = ident "chronosInternalRetFuture"
|
|
||||||
var iteratorNameSym = genSym(nskIterator, $prcName)
|
|
||||||
var procBody = prc.body.processBody(internalFutureSym, subtypeIsVoid)
|
|
||||||
# don't do anything with forward bodies (empty)
|
|
||||||
if procBody.kind != nnkEmpty:
|
|
||||||
if subtypeIsVoid:
|
|
||||||
let resultTemplate = quote do:
|
|
||||||
template result: auto {.used.} =
|
|
||||||
{.fatal: "You should not reference the `result` variable inside" &
|
|
||||||
" a void async proc".}
|
|
||||||
procBody = newStmtList(resultTemplate, procBody)
|
|
||||||
|
|
||||||
# fix #13899, `defer` should not escape its original scope
|
|
||||||
procBody = newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody))
|
|
||||||
|
|
||||||
if not subtypeIsVoid:
|
|
||||||
procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
|
|
||||||
newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
|
|
||||||
newIdentNode("warning"), newIdentNode("resultshadowed")),
|
|
||||||
newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
|
|
||||||
|
|
||||||
procBody.insert(1, newNimNode(nnkVarSection, prc.body).add(
|
|
||||||
newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T
|
|
||||||
|
|
||||||
procBody.insert(2, newNimNode(nnkPragma).add(
|
|
||||||
newIdentNode("pop"))) # -> {.pop.})
|
|
||||||
|
|
||||||
procBody.add(
|
|
||||||
newCall(newIdentNode("complete"),
|
|
||||||
internalFutureSym, newIdentNode("result"))) # -> complete(chronosInternalRetFuture, result)
|
|
||||||
else:
|
|
||||||
# -> complete(chronosInternalRetFuture)
|
|
||||||
procBody.add(newCall(newIdentNode("complete"), internalFutureSym))
|
|
||||||
|
|
||||||
let
|
let
|
||||||
internalFutureType =
|
prcName = prc.name.getName
|
||||||
if subtypeIsVoid:
|
outerProcBody = newNimNode(nnkStmtList, prc.body)
|
||||||
newNimNode(nnkBracketExpr, prc).add(newIdentNode("Future")).add(newIdentNode("void"))
|
|
||||||
else: returnType
|
|
||||||
internalFutureParameter = nnkIdentDefs.newTree(internalFutureSym, internalFutureType, newEmptyNode())
|
|
||||||
var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase"), internalFutureParameter],
|
|
||||||
procBody, nnkIteratorDef)
|
|
||||||
closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body)
|
|
||||||
closureIterator.addPragma(newIdentNode("closure"))
|
|
||||||
# **Remark 435**: We generate a proc with an inner iterator which call each other
|
|
||||||
# recursively. The current Nim compiler is not smart enough to infer
|
|
||||||
# the `gcsafe`-ty aspect of this setup, so we always annotate it explicitly
|
|
||||||
# with `gcsafe`. This means that the client code is always enforced to be
|
|
||||||
# `gcsafe`. This is still **safe**, the compiler still checks for `gcsafe`-ty
|
|
||||||
# regardless, it is only helping the compiler's inference algorithm. See
|
|
||||||
# https://github.com/nim-lang/RFCs/issues/435
|
|
||||||
# for more details.
|
|
||||||
closureIterator.addPragma(newIdentNode("gcsafe"))
|
|
||||||
|
|
||||||
# TODO when push raises is active in a module, the iterator here inherits
|
# Copy comment for nimdoc
|
||||||
# that annotation - here we explicitly disable it again which goes
|
if prc.body.len > 0 and prc.body[0].kind == nnkCommentStmt:
|
||||||
# against the spirit of the raises annotation - one should investigate
|
outerProcBody.add(prc.body[0])
|
||||||
# here the possibility of transporting more specific error types here
|
|
||||||
# for example by casting exceptions coming out of `await`..
|
|
||||||
let raises = nnkBracket.newTree()
|
|
||||||
raises.add(newIdentNode("CatchableError"))
|
|
||||||
when not defined(chronosStrictException):
|
|
||||||
raises.add(newIdentNode("Exception"))
|
|
||||||
when (NimMajor, NimMinor) < (1, 4):
|
|
||||||
raises.add(newIdentNode("Defect"))
|
|
||||||
closureIterator.addPragma(nnkExprColonExpr.newTree(
|
|
||||||
newIdentNode("raises"),
|
|
||||||
raises
|
|
||||||
))
|
|
||||||
|
|
||||||
# If proc has an explicit gcsafe pragma, we add it to iterator as well.
|
# -> iterator nameIter(chronosInternalRetFuture: Future[T]): FutureBase {.closure.} =
|
||||||
if prc.pragma.findChild(it.kind in {nnkSym, nnkIdent} and
|
# -> {.push warning[resultshadowed]: off.}
|
||||||
it.strVal == "gcsafe") != nil:
|
# -> var result: T
|
||||||
closureIterator.addPragma(newIdentNode("gcsafe"))
|
# -> {.pop.}
|
||||||
outerProcBody.add(closureIterator)
|
# -> <proc_body>
|
||||||
|
# -> complete(chronosInternalRetFuture, result)
|
||||||
|
let
|
||||||
|
internalFutureSym = ident "chronosInternalRetFuture"
|
||||||
|
iteratorNameSym = genSym(nskIterator, $prcName)
|
||||||
|
var
|
||||||
|
procBody = prc.body.processBody(internalFutureSym, subtypeIsVoid)
|
||||||
|
|
||||||
# -> var resultFuture = newFuture[T]()
|
# don't do anything with forward bodies (empty)
|
||||||
# declared at the end to be sure that the closure
|
if procBody.kind != nnkEmpty:
|
||||||
# doesn't reference it, avoid cyclic ref (#203)
|
if subtypeIsVoid:
|
||||||
var retFutureSym = ident "resultFuture"
|
let resultTemplate = quote do:
|
||||||
var subRetType =
|
template result: auto {.used.} =
|
||||||
if returnType.kind == nnkEmpty:
|
{.fatal: "You should not reference the `result` variable inside" &
|
||||||
newIdentNode("void")
|
" a void async proc".}
|
||||||
|
procBody = newStmtList(resultTemplate, procBody)
|
||||||
|
|
||||||
|
# fix #13899, `defer` should not escape its original scope
|
||||||
|
procBody = newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody))
|
||||||
|
|
||||||
|
if not subtypeIsVoid:
|
||||||
|
procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
|
||||||
|
newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
|
||||||
|
newIdentNode("warning"), newIdentNode("resultshadowed")),
|
||||||
|
newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
|
||||||
|
|
||||||
|
procBody.insert(1, newNimNode(nnkVarSection, prc.body).add(
|
||||||
|
newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T
|
||||||
|
|
||||||
|
procBody.insert(2, newNimNode(nnkPragma).add(
|
||||||
|
newIdentNode("pop"))) # -> {.pop.})
|
||||||
|
|
||||||
|
procBody.add(
|
||||||
|
newCall(newIdentNode("complete"),
|
||||||
|
internalFutureSym, newIdentNode("result"))) # -> complete(chronosInternalRetFuture, result)
|
||||||
else:
|
else:
|
||||||
baseType
|
# -> complete(chronosInternalRetFuture)
|
||||||
# Do not change this code to `quote do` version because `instantiationInfo`
|
procBody.add(newCall(newIdentNode("complete"), internalFutureSym))
|
||||||
# will be broken for `newFuture()` call.
|
|
||||||
outerProcBody.add(
|
let
|
||||||
newVarStmt(
|
internalFutureType =
|
||||||
retFutureSym,
|
if subtypeIsVoid:
|
||||||
newCall(newTree(nnkBracketExpr, ident "newFuture", subRetType),
|
newNimNode(nnkBracketExpr, prc).add(newIdentNode("Future")).add(newIdentNode("void"))
|
||||||
newLit(prcName))
|
else: returnType
|
||||||
|
internalFutureParameter = nnkIdentDefs.newTree(internalFutureSym, internalFutureType, newEmptyNode())
|
||||||
|
closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase"), internalFutureParameter],
|
||||||
|
procBody, nnkIteratorDef)
|
||||||
|
|
||||||
|
closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body)
|
||||||
|
closureIterator.addPragma(newIdentNode("closure"))
|
||||||
|
# **Remark 435**: We generate a proc with an inner iterator which call each other
|
||||||
|
# recursively. The current Nim compiler is not smart enough to infer
|
||||||
|
# the `gcsafe`-ty aspect of this setup, so we always annotate it explicitly
|
||||||
|
# with `gcsafe`. This means that the client code is always enforced to be
|
||||||
|
# `gcsafe`. This is still **safe**, the compiler still checks for `gcsafe`-ty
|
||||||
|
# regardless, it is only helping the compiler's inference algorithm. See
|
||||||
|
# https://github.com/nim-lang/RFCs/issues/435
|
||||||
|
# for more details.
|
||||||
|
closureIterator.addPragma(newIdentNode("gcsafe"))
|
||||||
|
|
||||||
|
# TODO when push raises is active in a module, the iterator here inherits
|
||||||
|
# that annotation - here we explicitly disable it again which goes
|
||||||
|
# against the spirit of the raises annotation - one should investigate
|
||||||
|
# here the possibility of transporting more specific error types here
|
||||||
|
# for example by casting exceptions coming out of `await`..
|
||||||
|
let raises = nnkBracket.newTree()
|
||||||
|
when not defined(chronosStrictException):
|
||||||
|
raises.add(newIdentNode("Exception"))
|
||||||
|
else:
|
||||||
|
raises.add(newIdentNode("CatchableError"))
|
||||||
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
|
raises.add(newIdentNode("Defect"))
|
||||||
|
|
||||||
|
closureIterator.addPragma(nnkExprColonExpr.newTree(
|
||||||
|
newIdentNode("raises"),
|
||||||
|
raises
|
||||||
|
))
|
||||||
|
|
||||||
|
# If proc has an explicit gcsafe pragma, we add it to iterator as well.
|
||||||
|
if prc.pragma.findChild(it.kind in {nnkSym, nnkIdent} and
|
||||||
|
it.strVal == "gcsafe") != nil:
|
||||||
|
closureIterator.addPragma(newIdentNode("gcsafe"))
|
||||||
|
outerProcBody.add(closureIterator)
|
||||||
|
|
||||||
|
# -> var resultFuture = newFuture[T]()
|
||||||
|
# declared at the end to be sure that the closure
|
||||||
|
# doesn't reference it, avoid cyclic ref (#203)
|
||||||
|
var retFutureSym = ident "resultFuture"
|
||||||
|
var subRetType =
|
||||||
|
if returnType.kind == nnkEmpty:
|
||||||
|
newIdentNode("void")
|
||||||
|
else:
|
||||||
|
baseType
|
||||||
|
# Do not change this code to `quote do` version because `instantiationInfo`
|
||||||
|
# will be broken for `newFuture()` call.
|
||||||
|
outerProcBody.add(
|
||||||
|
newVarStmt(
|
||||||
|
retFutureSym,
|
||||||
|
newCall(newTree(nnkBracketExpr, ident "newFuture", subRetType),
|
||||||
|
newLit(prcName))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
# -> resultFuture.closure = iterator
|
# -> resultFuture.closure = iterator
|
||||||
outerProcBody.add(
|
outerProcBody.add(
|
||||||
newAssignment(
|
newAssignment(
|
||||||
newDotExpr(retFutureSym, newIdentNode("closure")),
|
newDotExpr(retFutureSym, newIdentNode("closure")),
|
||||||
iteratorNameSym)
|
iteratorNameSym)
|
||||||
)
|
)
|
||||||
|
|
||||||
# -> futureContinue(resultFuture))
|
# -> futureContinue(resultFuture))
|
||||||
outerProcBody.add(
|
outerProcBody.add(
|
||||||
newCall(newIdentNode("futureContinue"), retFutureSym)
|
newCall(newIdentNode("futureContinue"), retFutureSym)
|
||||||
)
|
)
|
||||||
|
|
||||||
# -> return resultFuture
|
# -> return resultFuture
|
||||||
outerProcBody.add newNimNode(nnkReturnStmt, prc.body[^1]).add(retFutureSym)
|
outerProcBody.add newNimNode(nnkReturnStmt, prc.body[^1]).add(retFutureSym)
|
||||||
|
|
||||||
if prc.kind != nnkLambda: # TODO: Nim bug?
|
prc.body = outerProcBody
|
||||||
|
|
||||||
|
if prc.kind notin {nnkProcTy, nnkLambda}: # TODO: Nim bug?
|
||||||
prc.addPragma(newColonExpr(ident "stackTrace", ident "off"))
|
prc.addPragma(newColonExpr(ident "stackTrace", ident "off"))
|
||||||
|
|
||||||
# See **Remark 435** in this file.
|
# See **Remark 435** in this file.
|
||||||
# https://github.com/nim-lang/RFCs/issues/435
|
# https://github.com/nim-lang/RFCs/issues/435
|
||||||
prc.addPragma(newIdentNode("gcsafe"))
|
prc.addPragma(newIdentNode("gcsafe"))
|
||||||
result = prc
|
|
||||||
|
let raises = nnkBracket.newTree()
|
||||||
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
|
raises.add(newIdentNode("Defect"))
|
||||||
|
prc.addPragma(nnkExprColonExpr.newTree(
|
||||||
|
newIdentNode("raises"),
|
||||||
|
raises
|
||||||
|
))
|
||||||
|
|
||||||
if subtypeIsVoid:
|
if subtypeIsVoid:
|
||||||
# Add discardable pragma.
|
# Add discardable pragma.
|
||||||
if returnType.kind == nnkEmpty:
|
if returnType.kind == nnkEmpty:
|
||||||
# Add Future[void]
|
# Add Future[void]
|
||||||
result.params[0] =
|
prc.params[0] =
|
||||||
newNimNode(nnkBracketExpr, prc)
|
newNimNode(nnkBracketExpr, prc)
|
||||||
.add(newIdentNode("Future"))
|
.add(newIdentNode("Future"))
|
||||||
.add(newIdentNode("void"))
|
.add(newIdentNode("void"))
|
||||||
if procBody.kind != nnkEmpty:
|
|
||||||
result.body = outerProcBody
|
prc
|
||||||
#echo(treeRepr(result))
|
#echo(treeRepr(result))
|
||||||
#if prcName == "recvLineInto":
|
|
||||||
# echo(toStrLit(result))
|
|
||||||
|
|
||||||
template await*[T](f: Future[T]): untyped =
|
template await*[T](f: Future[T]): untyped =
|
||||||
when declared(chronosInternalRetFuture):
|
when declared(chronosInternalRetFuture):
|
||||||
|
@ -11,6 +11,10 @@ import ../chronos
|
|||||||
|
|
||||||
when defined(nimHasUsed): {.used.}
|
when defined(nimHasUsed): {.used.}
|
||||||
|
|
||||||
|
type
|
||||||
|
RetValueType = proc(n: int): Future[int] {.async.}
|
||||||
|
RetVoidType = proc(n: int): Future[void] {.async.}
|
||||||
|
|
||||||
proc asyncRetValue(n: int): Future[int] {.async.} =
|
proc asyncRetValue(n: int): Future[int] {.async.} =
|
||||||
await sleepAsync(n.milliseconds)
|
await sleepAsync(n.milliseconds)
|
||||||
result = n * 10
|
result = n * 10
|
||||||
@ -31,6 +35,7 @@ proc asyncRetExceptionVoid(n: int) {.async.} =
|
|||||||
|
|
||||||
proc testAwait(): Future[bool] {.async.} =
|
proc testAwait(): Future[bool] {.async.} =
|
||||||
var res: int
|
var res: int
|
||||||
|
|
||||||
await asyncRetVoid(100)
|
await asyncRetVoid(100)
|
||||||
res = await asyncRetValue(100)
|
res = await asyncRetValue(100)
|
||||||
if res != 1000:
|
if res != 1000:
|
||||||
@ -50,6 +55,15 @@ proc testAwait(): Future[bool] {.async.} =
|
|||||||
discard
|
discard
|
||||||
if res != 0:
|
if res != 0:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
block:
|
||||||
|
let fn: RetVoidType = asyncRetVoid
|
||||||
|
await fn(100)
|
||||||
|
block:
|
||||||
|
let fn: RetValueType = asyncRetValue
|
||||||
|
if (await fn(100)) != 1000:
|
||||||
|
return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc testAwaitne(): Future[bool] {.async.} =
|
proc testAwaitne(): Future[bool] {.async.} =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user