2018-05-16 08:22:34 +00:00
|
|
|
#
|
|
|
|
#
|
|
|
|
# Nim's Runtime Library
|
|
|
|
# (c) Copyright 2015 Dominik Picheta
|
|
|
|
#
|
|
|
|
# See the file "copying.txt", included in this
|
|
|
|
# distribution, for details about the copyright.
|
|
|
|
#
|
|
|
|
|
2021-05-07 20:52:24 +00:00
|
|
|
import std/[macros]
|
2018-05-16 08:22:34 +00:00
|
|
|
|
|
|
|
proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} =
|
|
|
|
# Skips a nest of StmtList's.
|
|
|
|
result = node
|
|
|
|
if node[0].kind == nnkStmtList:
|
|
|
|
result = skipUntilStmtList(node[0])
|
|
|
|
|
|
|
|
proc processBody(node, retFutureSym: NimNode,
|
2021-12-08 11:30:12 +00:00
|
|
|
subTypeIsVoid: bool): NimNode {.compileTime.} =
|
2018-05-16 08:22:34 +00:00
|
|
|
#echo(node.treeRepr)
|
|
|
|
result = node
|
|
|
|
case node.kind
|
|
|
|
of nnkReturnStmt:
|
|
|
|
result = newNimNode(nnkStmtList, node)
|
|
|
|
|
|
|
|
# As I've painfully found out, the order here really DOES matter.
|
|
|
|
if node[0].kind == nnkEmpty:
|
|
|
|
if not subTypeIsVoid:
|
|
|
|
result.add newCall(newIdentNode("complete"), retFutureSym,
|
|
|
|
newIdentNode("result"))
|
|
|
|
else:
|
|
|
|
result.add newCall(newIdentNode("complete"), retFutureSym)
|
|
|
|
else:
|
2021-12-08 11:30:12 +00:00
|
|
|
let x = node[0].processBody(retFutureSym, subTypeIsVoid)
|
2018-05-16 08:22:34 +00:00
|
|
|
if x.kind == nnkYieldStmt: result.add x
|
|
|
|
else:
|
|
|
|
result.add newCall(newIdentNode("complete"), retFutureSym, x)
|
|
|
|
|
|
|
|
result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
|
|
|
|
return # Don't process the children of this return stmt
|
2019-07-19 10:06:32 +00:00
|
|
|
of RoutineNodes-{nnkTemplateDef}:
|
|
|
|
# skip all the nested procedure definitions
|
|
|
|
return node
|
2018-05-16 08:22:34 +00:00
|
|
|
else: discard
|
|
|
|
|
|
|
|
for i in 0 ..< result.len:
|
2019-07-17 13:12:31 +00:00
|
|
|
# We must not transform nested procedures of any form, otherwise
|
|
|
|
# `retFutureSym` will be used for all nested procedures as their own
|
|
|
|
# `retFuture`.
|
2021-12-08 11:30:12 +00:00
|
|
|
result[i] = processBody(result[i], retFutureSym, subTypeIsVoid)
|
2018-05-16 08:22:34 +00:00
|
|
|
|
|
|
|
proc getName(node: NimNode): string {.compileTime.} =
|
|
|
|
case node.kind
|
2018-07-06 01:17:08 +00:00
|
|
|
of nnkSym:
|
2021-03-30 14:48:20 +00:00
|
|
|
return node.strVal
|
2018-05-16 08:22:34 +00:00
|
|
|
of nnkPostfix:
|
|
|
|
return node[1].strVal
|
|
|
|
of nnkIdent:
|
|
|
|
return node.strVal
|
|
|
|
of nnkEmpty:
|
|
|
|
return "anonymous"
|
|
|
|
else:
|
|
|
|
error("Unknown name.")
|
|
|
|
|
|
|
|
proc isInvalidReturnType(typeName: string): bool =
|
|
|
|
return typeName notin ["Future"] #, "FutureStream"]
|
|
|
|
|
|
|
|
proc verifyReturnType(typeName: string) {.compileTime.} =
|
|
|
|
if typeName.isInvalidReturnType:
|
2021-03-30 14:48:20 +00:00
|
|
|
error("Expected return type of 'Future' got '" & typeName & "'")
|
2018-05-16 08:22:34 +00:00
|
|
|
|
2019-08-15 16:35:42 +00:00
|
|
|
macro unsupported(s: static[string]): untyped =
|
|
|
|
error s
|
|
|
|
|
2022-03-30 13:13:58 +00:00
|
|
|
proc cleanupOpenSymChoice(node: NimNode): NimNode {.compileTime.} =
|
|
|
|
# Replace every Call -> OpenSymChoice by a Bracket expr
|
|
|
|
# ref https://github.com/nim-lang/Nim/issues/11091
|
|
|
|
if node.kind in nnkCallKinds and
|
|
|
|
node[0].kind == nnkOpenSymChoice and node[0].eqIdent("[]"):
|
2022-05-05 09:05:04 +00:00
|
|
|
result = newNimNode(nnkBracketExpr)
|
|
|
|
for child in node[1..^1]:
|
|
|
|
result.add(cleanupOpenSymChoice(child))
|
2022-03-30 13:13:58 +00:00
|
|
|
else:
|
|
|
|
result = node.copyNimNode()
|
|
|
|
for child in node:
|
|
|
|
result.add(cleanupOpenSymChoice(child))
|
|
|
|
|
2018-05-16 08:22:34 +00:00
|
|
|
proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
|
|
|
|
## This macro transforms a single procedure into a closure iterator.
|
|
|
|
## The ``async`` macro supports a stmtList holding multiple async procedures.
|
|
|
|
if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}:
|
|
|
|
error("Cannot transform this node kind into an async proc." &
|
|
|
|
" proc/method definition or lambda node expected.")
|
|
|
|
|
|
|
|
let prcName = prc.name.getName
|
|
|
|
|
2022-03-30 13:13:58 +00:00
|
|
|
let returnType = cleanupOpenSymChoice(prc.params[0])
|
2018-05-16 08:22:34 +00:00
|
|
|
var baseType: NimNode
|
|
|
|
# Verify that the return type is a Future[T]
|
|
|
|
if returnType.kind == nnkBracketExpr:
|
|
|
|
let fut = repr(returnType[0])
|
|
|
|
verifyReturnType(fut)
|
|
|
|
baseType = returnType[1]
|
|
|
|
elif returnType.kind == nnkEmpty:
|
|
|
|
baseType = returnType
|
|
|
|
else:
|
|
|
|
verifyReturnType(repr(returnType))
|
|
|
|
|
|
|
|
let subtypeIsVoid = returnType.kind == nnkEmpty or
|
|
|
|
(baseType.kind == nnkIdent and returnType[1].eqIdent("void"))
|
|
|
|
|
|
|
|
var outerProcBody = newNimNode(nnkStmtList, prc.body)
|
|
|
|
|
2022-04-05 10:16:00 +00:00
|
|
|
# Copy comment for nimdoc
|
|
|
|
if prc.body.len > 0 and prc.body[0].kind == nnkCommentStmt:
|
|
|
|
outerProcBody.add(prc.body[0])
|
|
|
|
|
2018-05-16 08:22:34 +00:00
|
|
|
|
2021-12-10 10:19:14 +00:00
|
|
|
# -> iterator nameIter(chronosInternalRetFuture: Future[T]): FutureBase {.closure.} =
|
2018-05-16 08:22:34 +00:00
|
|
|
# -> {.push warning[resultshadowed]: off.}
|
|
|
|
# -> var result: T
|
|
|
|
# -> {.pop.}
|
|
|
|
# -> <proc_body>
|
2021-12-10 10:19:14 +00:00
|
|
|
# -> complete(chronosInternalRetFuture, result)
|
|
|
|
let internalFutureSym = ident "chronosInternalRetFuture"
|
2019-08-15 15:26:00 +00:00
|
|
|
var iteratorNameSym = genSym(nskIterator, $prcName)
|
2021-12-10 10:19:14 +00:00
|
|
|
var procBody = prc.body.processBody(internalFutureSym, subtypeIsVoid)
|
2018-05-16 08:22:34 +00:00
|
|
|
# don't do anything with forward bodies (empty)
|
|
|
|
if procBody.kind != nnkEmpty:
|
2020-08-05 13:28:11 +00:00
|
|
|
if subtypeIsVoid:
|
|
|
|
let resultTemplate = quote do:
|
|
|
|
template result: auto {.used.} =
|
2021-05-07 20:52:24 +00:00
|
|
|
{.fatal: "You should not reference the `result` variable inside" &
|
|
|
|
" a void async proc".}
|
2020-08-05 13:28:11 +00:00
|
|
|
procBody = newStmtList(resultTemplate, procBody)
|
|
|
|
|
2020-06-24 10:03:36 +00:00
|
|
|
# fix #13899, `defer` should not escape its original scope
|
|
|
|
procBody = newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody))
|
|
|
|
|
2018-05-16 08:22:34 +00:00
|
|
|
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"),
|
2021-12-10 10:19:14 +00:00
|
|
|
internalFutureSym, newIdentNode("result"))) # -> complete(chronosInternalRetFuture, result)
|
2018-05-16 08:22:34 +00:00
|
|
|
else:
|
2021-12-10 10:19:14 +00:00
|
|
|
# -> complete(chronosInternalRetFuture)
|
|
|
|
procBody.add(newCall(newIdentNode("complete"), internalFutureSym))
|
|
|
|
|
|
|
|
let
|
|
|
|
internalFutureType =
|
|
|
|
if subtypeIsVoid:
|
|
|
|
newNimNode(nnkBracketExpr, prc).add(newIdentNode("Future")).add(newIdentNode("void"))
|
|
|
|
else: returnType
|
|
|
|
internalFutureParameter = nnkIdentDefs.newTree(internalFutureSym, internalFutureType, newEmptyNode())
|
|
|
|
var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase"), internalFutureParameter],
|
2018-05-16 08:22:34 +00:00
|
|
|
procBody, nnkIteratorDef)
|
|
|
|
closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body)
|
|
|
|
closureIterator.addPragma(newIdentNode("closure"))
|
2021-11-22 12:48:59 +00:00
|
|
|
# **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
|
2021-11-19 00:07:46 +00:00
|
|
|
# https://github.com/nim-lang/RFCs/issues/435
|
2021-11-22 12:48:59 +00:00
|
|
|
# for more details.
|
2021-11-19 00:05:47 +00:00
|
|
|
closureIterator.addPragma(newIdentNode("gcsafe"))
|
2018-05-16 08:22:34 +00:00
|
|
|
|
exception tracking (#166)
* exception tracking
This PR adds minimal exception tracking to chronos, moving the goalpost
one step further.
In particular, it becomes invalid to raise exceptions from `callSoon`
callbacks: this is critical for writing correct error handling because
there's no reasonable way that a user of chronos can possibly _reason_
about exceptions coming out of there: the event loop will be in an
indeterminite state when the loop is executing an _random_ callback.
As expected, there are several issues in the error handling of chronos:
in particular, it will end up in an inconsistent internal state whenever
the selector loop operations fail, because the internal state update
functions are not written in an exception-safe way. This PR turns this
into a Defect, which probably is not the optimal way of handling things
- expect more work to be done here.
Some API have no way of reporting back errors to callers - for example,
when something fails in the accept loop, there's not much it can do, and
no way to report it back to the user of the API - this has been fixed
with the new accept flow - the old one should be deprecated.
Finally, there is information loss in the API: in composite operations
like `poll` and `waitFor` there's no way to differentiate internal
errors from user-level errors originating from callbacks.
* store `CatchableError` in future
* annotate proc's with correct raises information
* `selectors2` to avoid non-CatchableError IOSelectorsException
* `$` should never raise
* remove unnecessary gcsafe annotations
* fix exceptions leaking out of timer waits
* fix some imports
* functions must signal raising the union of all exceptions across all
platforms to enable cross-platform code
* switch to unittest2
* add `selectors2` which supercedes the std library version and fixes
several exception handling issues in there
* fixes
* docs, platform-independent eh specifiers for some functions
* add feature flag for strict exception mode
also bump version to 3.0.0 - _most_ existing code should be compatible
with this version of exception handling but some things might need
fixing - callbacks, existing raises specifications etc.
* fix AsyncCheck for non-void T
2021-03-24 09:08:33 +00:00
|
|
|
# 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`..
|
|
|
|
when defined(chronosStrictException):
|
|
|
|
closureIterator.addPragma(nnkExprColonExpr.newTree(
|
|
|
|
newIdentNode("raises"),
|
|
|
|
nnkBracket.newTree(
|
|
|
|
newIdentNode("Defect"),
|
|
|
|
newIdentNode("CatchableError")
|
|
|
|
)
|
|
|
|
))
|
|
|
|
else:
|
|
|
|
closureIterator.addPragma(nnkExprColonExpr.newTree(
|
|
|
|
newIdentNode("raises"),
|
|
|
|
nnkBracket.newTree(
|
|
|
|
newIdentNode("Defect"),
|
|
|
|
newIdentNode("CatchableError"),
|
|
|
|
newIdentNode("Exception") # Allow exception effects
|
|
|
|
)
|
|
|
|
))
|
|
|
|
|
2018-05-16 08:22:34 +00:00
|
|
|
# If proc has an explicit gcsafe pragma, we add it to iterator as well.
|
2021-05-07 20:52:24 +00:00
|
|
|
if prc.pragma.findChild(it.kind in {nnkSym, nnkIdent} and
|
|
|
|
it.strVal == "gcsafe") != nil:
|
2018-05-16 08:22:34 +00:00
|
|
|
closureIterator.addPragma(newIdentNode("gcsafe"))
|
|
|
|
outerProcBody.add(closureIterator)
|
|
|
|
|
2021-12-10 10:19:14 +00:00
|
|
|
# -> 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
|
|
|
|
outerProcBody.add(
|
|
|
|
newAssignment(
|
|
|
|
newDotExpr(retFutureSym, newIdentNode("closure")),
|
|
|
|
iteratorNameSym)
|
|
|
|
)
|
|
|
|
|
|
|
|
# -> futureContinue(resultFuture))
|
|
|
|
outerProcBody.add(
|
|
|
|
newCall(newIdentNode("futureContinue"), retFutureSym)
|
|
|
|
)
|
2018-05-16 08:22:34 +00:00
|
|
|
|
2021-12-10 10:19:14 +00:00
|
|
|
# -> return resultFuture
|
2018-05-16 08:22:34 +00:00
|
|
|
outerProcBody.add newNimNode(nnkReturnStmt, prc.body[^1]).add(retFutureSym)
|
|
|
|
|
2019-08-19 17:19:26 +00:00
|
|
|
if prc.kind != nnkLambda: # TODO: Nim bug?
|
|
|
|
prc.addPragma(newColonExpr(ident "stackTrace", ident "off"))
|
2019-08-15 16:35:42 +00:00
|
|
|
|
2021-11-22 12:48:59 +00:00
|
|
|
# See **Remark 435** in this file.
|
2021-11-19 00:07:46 +00:00
|
|
|
# https://github.com/nim-lang/RFCs/issues/435
|
2021-11-19 00:05:47 +00:00
|
|
|
prc.addPragma(newIdentNode("gcsafe"))
|
2018-05-16 08:22:34 +00:00
|
|
|
result = prc
|
|
|
|
|
|
|
|
if subtypeIsVoid:
|
|
|
|
# Add discardable pragma.
|
|
|
|
if returnType.kind == nnkEmpty:
|
|
|
|
# Add Future[void]
|
2021-03-30 14:48:20 +00:00
|
|
|
result.params[0] =
|
|
|
|
newNimNode(nnkBracketExpr, prc)
|
|
|
|
.add(newIdentNode("Future"))
|
|
|
|
.add(newIdentNode("void"))
|
2018-05-16 08:22:34 +00:00
|
|
|
if procBody.kind != nnkEmpty:
|
|
|
|
result.body = outerProcBody
|
|
|
|
#echo(treeRepr(result))
|
|
|
|
#if prcName == "recvLineInto":
|
|
|
|
# echo(toStrLit(result))
|
|
|
|
|
exception tracking (#166)
* exception tracking
This PR adds minimal exception tracking to chronos, moving the goalpost
one step further.
In particular, it becomes invalid to raise exceptions from `callSoon`
callbacks: this is critical for writing correct error handling because
there's no reasonable way that a user of chronos can possibly _reason_
about exceptions coming out of there: the event loop will be in an
indeterminite state when the loop is executing an _random_ callback.
As expected, there are several issues in the error handling of chronos:
in particular, it will end up in an inconsistent internal state whenever
the selector loop operations fail, because the internal state update
functions are not written in an exception-safe way. This PR turns this
into a Defect, which probably is not the optimal way of handling things
- expect more work to be done here.
Some API have no way of reporting back errors to callers - for example,
when something fails in the accept loop, there's not much it can do, and
no way to report it back to the user of the API - this has been fixed
with the new accept flow - the old one should be deprecated.
Finally, there is information loss in the API: in composite operations
like `poll` and `waitFor` there's no way to differentiate internal
errors from user-level errors originating from callbacks.
* store `CatchableError` in future
* annotate proc's with correct raises information
* `selectors2` to avoid non-CatchableError IOSelectorsException
* `$` should never raise
* remove unnecessary gcsafe annotations
* fix exceptions leaking out of timer waits
* fix some imports
* functions must signal raising the union of all exceptions across all
platforms to enable cross-platform code
* switch to unittest2
* add `selectors2` which supercedes the std library version and fixes
several exception handling issues in there
* fixes
* docs, platform-independent eh specifiers for some functions
* add feature flag for strict exception mode
also bump version to 3.0.0 - _most_ existing code should be compatible
with this version of exception handling but some things might need
fixing - callbacks, existing raises specifications etc.
* fix AsyncCheck for non-void T
2021-03-24 09:08:33 +00:00
|
|
|
template await*[T](f: Future[T]): untyped =
|
2019-08-15 13:32:46 +00:00
|
|
|
when declared(chronosInternalRetFuture):
|
2021-12-10 10:19:14 +00:00
|
|
|
#work around https://github.com/nim-lang/Nim/issues/19193
|
2019-08-15 16:35:42 +00:00
|
|
|
when not declaredInScope(chronosInternalTmpFuture):
|
2021-12-10 10:19:14 +00:00
|
|
|
var chronosInternalTmpFuture {.inject.}: FutureBase = f
|
|
|
|
else:
|
|
|
|
chronosInternalTmpFuture = f
|
2019-08-15 13:32:46 +00:00
|
|
|
chronosInternalRetFuture.child = chronosInternalTmpFuture
|
2020-07-13 15:59:11 +00:00
|
|
|
|
|
|
|
# This "yield" is meant for a closure iterator in the caller.
|
2019-08-15 13:32:46 +00:00
|
|
|
yield chronosInternalTmpFuture
|
2020-07-13 15:59:11 +00:00
|
|
|
|
|
|
|
# By the time we get control back here, we're guaranteed that the Future we
|
|
|
|
# just yielded has been completed (success, failure or cancellation),
|
|
|
|
# through a very complicated mechanism in which the caller proc (a regular
|
|
|
|
# closure) adds itself as a callback to chronosInternalTmpFuture.
|
|
|
|
#
|
|
|
|
# Callbacks are called only after completion and a copy of the closure
|
|
|
|
# iterator that calls this template is still in that callback's closure
|
|
|
|
# environment. That's where control actually gets back to us.
|
|
|
|
|
2019-08-15 13:32:46 +00:00
|
|
|
chronosInternalRetFuture.child = nil
|
2020-07-03 12:03:59 +00:00
|
|
|
if chronosInternalRetFuture.mustCancel:
|
2020-07-08 16:48:01 +00:00
|
|
|
raise newCancelledError()
|
2020-07-03 12:03:59 +00:00
|
|
|
chronosInternalTmpFuture.internalCheckComplete()
|
exception tracking (#166)
* exception tracking
This PR adds minimal exception tracking to chronos, moving the goalpost
one step further.
In particular, it becomes invalid to raise exceptions from `callSoon`
callbacks: this is critical for writing correct error handling because
there's no reasonable way that a user of chronos can possibly _reason_
about exceptions coming out of there: the event loop will be in an
indeterminite state when the loop is executing an _random_ callback.
As expected, there are several issues in the error handling of chronos:
in particular, it will end up in an inconsistent internal state whenever
the selector loop operations fail, because the internal state update
functions are not written in an exception-safe way. This PR turns this
into a Defect, which probably is not the optimal way of handling things
- expect more work to be done here.
Some API have no way of reporting back errors to callers - for example,
when something fails in the accept loop, there's not much it can do, and
no way to report it back to the user of the API - this has been fixed
with the new accept flow - the old one should be deprecated.
Finally, there is information loss in the API: in composite operations
like `poll` and `waitFor` there's no way to differentiate internal
errors from user-level errors originating from callbacks.
* store `CatchableError` in future
* annotate proc's with correct raises information
* `selectors2` to avoid non-CatchableError IOSelectorsException
* `$` should never raise
* remove unnecessary gcsafe annotations
* fix exceptions leaking out of timer waits
* fix some imports
* functions must signal raising the union of all exceptions across all
platforms to enable cross-platform code
* switch to unittest2
* add `selectors2` which supercedes the std library version and fixes
several exception handling issues in there
* fixes
* docs, platform-independent eh specifiers for some functions
* add feature flag for strict exception mode
also bump version to 3.0.0 - _most_ existing code should be compatible
with this version of exception handling but some things might need
fixing - callbacks, existing raises specifications etc.
* fix AsyncCheck for non-void T
2021-03-24 09:08:33 +00:00
|
|
|
when T isnot void:
|
|
|
|
cast[type(f)](chronosInternalTmpFuture).internalRead()
|
2019-08-15 13:32:46 +00:00
|
|
|
else:
|
2019-08-15 16:35:42 +00:00
|
|
|
unsupported "await is only available within {.async.}"
|
2019-08-15 13:32:46 +00:00
|
|
|
|
|
|
|
template awaitne*[T](f: Future[T]): Future[T] =
|
|
|
|
when declared(chronosInternalRetFuture):
|
2021-12-10 10:19:14 +00:00
|
|
|
#work around https://github.com/nim-lang/Nim/issues/19193
|
2019-08-15 16:35:42 +00:00
|
|
|
when not declaredInScope(chronosInternalTmpFuture):
|
2021-12-10 10:19:14 +00:00
|
|
|
var chronosInternalTmpFuture {.inject.}: FutureBase = f
|
|
|
|
else:
|
|
|
|
chronosInternalTmpFuture = f
|
2019-08-15 13:32:46 +00:00
|
|
|
chronosInternalRetFuture.child = chronosInternalTmpFuture
|
|
|
|
yield chronosInternalTmpFuture
|
|
|
|
chronosInternalRetFuture.child = nil
|
2020-07-03 12:03:59 +00:00
|
|
|
if chronosInternalRetFuture.mustCancel:
|
2020-07-08 16:48:01 +00:00
|
|
|
raise newCancelledError()
|
2019-08-15 13:32:46 +00:00
|
|
|
cast[type(f)](chronosInternalTmpFuture)
|
|
|
|
else:
|
2019-08-15 16:35:42 +00:00
|
|
|
unsupported "awaitne is only available within {.async.}"
|
2019-07-19 10:06:32 +00:00
|
|
|
|
2018-05-16 08:22:34 +00:00
|
|
|
macro async*(prc: untyped): untyped =
|
|
|
|
## Macro which processes async procedures into the appropriate
|
|
|
|
## iterators and yield statements.
|
|
|
|
if prc.kind == nnkStmtList:
|
|
|
|
for oneProc in prc:
|
|
|
|
result = newStmtList()
|
|
|
|
result.add asyncSingleProc(oneProc)
|
|
|
|
else:
|
|
|
|
result = asyncSingleProc(prc)
|
|
|
|
when defined(nimDumpAsync):
|
|
|
|
echo repr result
|