mirror of
https://github.com/status-im/nim-chronos.git
synced 2025-02-08 01:14:29 +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,22 +108,27 @@ 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}:
|
||||||
|
let
|
||||||
|
prcName = prc.name.getName
|
||||||
|
outerProcBody = newNimNode(nnkStmtList, prc.body)
|
||||||
|
|
||||||
# Copy comment for nimdoc
|
# Copy comment for nimdoc
|
||||||
if prc.body.len > 0 and prc.body[0].kind == nnkCommentStmt:
|
if prc.body.len > 0 and prc.body[0].kind == nnkCommentStmt:
|
||||||
outerProcBody.add(prc.body[0])
|
outerProcBody.add(prc.body[0])
|
||||||
|
|
||||||
|
|
||||||
# -> iterator nameIter(chronosInternalRetFuture: Future[T]): FutureBase {.closure.} =
|
# -> iterator nameIter(chronosInternalRetFuture: Future[T]): FutureBase {.closure.} =
|
||||||
# -> {.push warning[resultshadowed]: off.}
|
# -> {.push warning[resultshadowed]: off.}
|
||||||
# -> var result: T
|
# -> var result: T
|
||||||
# -> {.pop.}
|
# -> {.pop.}
|
||||||
# -> <proc_body>
|
# -> <proc_body>
|
||||||
# -> complete(chronosInternalRetFuture, result)
|
# -> complete(chronosInternalRetFuture, result)
|
||||||
let internalFutureSym = ident "chronosInternalRetFuture"
|
let
|
||||||
var iteratorNameSym = genSym(nskIterator, $prcName)
|
internalFutureSym = ident "chronosInternalRetFuture"
|
||||||
var procBody = prc.body.processBody(internalFutureSym, subtypeIsVoid)
|
iteratorNameSym = genSym(nskIterator, $prcName)
|
||||||
|
var
|
||||||
|
procBody = prc.body.processBody(internalFutureSym, subtypeIsVoid)
|
||||||
|
|
||||||
# don't do anything with forward bodies (empty)
|
# don't do anything with forward bodies (empty)
|
||||||
if procBody.kind != nnkEmpty:
|
if procBody.kind != nnkEmpty:
|
||||||
if subtypeIsVoid:
|
if subtypeIsVoid:
|
||||||
@ -162,8 +166,9 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
|
|||||||
newNimNode(nnkBracketExpr, prc).add(newIdentNode("Future")).add(newIdentNode("void"))
|
newNimNode(nnkBracketExpr, prc).add(newIdentNode("Future")).add(newIdentNode("void"))
|
||||||
else: returnType
|
else: returnType
|
||||||
internalFutureParameter = nnkIdentDefs.newTree(internalFutureSym, internalFutureType, newEmptyNode())
|
internalFutureParameter = nnkIdentDefs.newTree(internalFutureSym, internalFutureType, newEmptyNode())
|
||||||
var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase"), internalFutureParameter],
|
closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase"), internalFutureParameter],
|
||||||
procBody, nnkIteratorDef)
|
procBody, nnkIteratorDef)
|
||||||
|
|
||||||
closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body)
|
closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body)
|
||||||
closureIterator.addPragma(newIdentNode("closure"))
|
closureIterator.addPragma(newIdentNode("closure"))
|
||||||
# **Remark 435**: We generate a proc with an inner iterator which call each other
|
# **Remark 435**: We generate a proc with an inner iterator which call each other
|
||||||
@ -182,11 +187,13 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
|
|||||||
# here the possibility of transporting more specific error types here
|
# here the possibility of transporting more specific error types here
|
||||||
# for example by casting exceptions coming out of `await`..
|
# for example by casting exceptions coming out of `await`..
|
||||||
let raises = nnkBracket.newTree()
|
let raises = nnkBracket.newTree()
|
||||||
raises.add(newIdentNode("CatchableError"))
|
|
||||||
when not defined(chronosStrictException):
|
when not defined(chronosStrictException):
|
||||||
raises.add(newIdentNode("Exception"))
|
raises.add(newIdentNode("Exception"))
|
||||||
|
else:
|
||||||
|
raises.add(newIdentNode("CatchableError"))
|
||||||
when (NimMajor, NimMinor) < (1, 4):
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
raises.add(newIdentNode("Defect"))
|
raises.add(newIdentNode("Defect"))
|
||||||
|
|
||||||
closureIterator.addPragma(nnkExprColonExpr.newTree(
|
closureIterator.addPragma(nnkExprColonExpr.newTree(
|
||||||
newIdentNode("raises"),
|
newIdentNode("raises"),
|
||||||
raises
|
raises
|
||||||
@ -232,27 +239,34 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
|
|||||||
# -> 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