Remove future var (#247)

This commit is contained in:
Tanguy 2021-12-08 12:30:12 +01:00 committed by GitHub
parent c25fa1f6cd
commit cedc603b81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 11 additions and 105 deletions

View File

@ -64,8 +64,6 @@ type
## Future to hold GC seqs
gcholder*: seq[B]
FutureVar*[T] = distinct Future[T]
FutureDefect* = object of Defect
cause*: FutureBase
@ -113,9 +111,6 @@ proc newFutureSeqImpl[A, B](loc: ptr SrcLoc): FutureSeq[A, B] =
proc newFutureStrImpl[T](loc: ptr SrcLoc): FutureStr[T] =
setupFutureBase(loc)
proc newFutureVarImpl[T](loc: ptr SrcLoc): FutureVar[T] =
FutureVar[T](newFutureImpl[T](loc))
template newFuture*[T](fromProc: static[string] = ""): Future[T] =
## Creates a new future.
##
@ -139,28 +134,11 @@ template newFutureStr*[T](fromProc: static[string] = ""): FutureStr[T] =
## that this future belongs to, is a good habit as it helps with debugging.
newFutureStrImpl[T](getSrcLocation(fromProc))
template newFutureVar*[T](fromProc: static[string] = ""): FutureVar[T] =
## Create a new ``FutureVar``. This Future type is ideally suited for
## situations where you want to avoid unnecessary allocations of Futures.
##
## Specifying ``fromProc``, which is a string specifying the name of the proc
## that this future belongs to, is a good habit as it helps with debugging.
newFutureVarImpl[T](getSrcLocation(fromProc))
proc clean*[T](future: FutureVar[T]) =
## Resets the ``finished`` status of ``future``.
Future[T](future).state = FutureState.Pending
Future[T](future).value = default(T)
Future[T](future).error = nil
proc finished*(future: FutureBase | FutureVar): bool {.inline.} =
proc finished*(future: FutureBase): bool {.inline.} =
## Determines whether ``future`` has completed, i.e. ``future`` state changed
## from state ``Pending`` to one of the states (``Finished``, ``Cancelled``,
## ``Failed``).
when future is FutureVar:
result = (FutureBase(future).state != FutureState.Pending)
else:
result = (future.state != FutureState.Pending)
result = (future.state != FutureState.Pending)
proc cancelled*(future: FutureBase): bool {.inline.} =
## Determines whether ``future`` has cancelled.
@ -254,31 +232,6 @@ template complete*(future: Future[void]) =
## Completes a void ``future``.
complete(future, getSrcLocation())
proc complete[T](future: FutureVar[T], loc: ptr SrcLoc) =
if not(future.cancelled()):
template fut: untyped = Future[T](future)
checkFinished(FutureBase(fut), loc)
doAssert(isNil(fut.error))
fut.finish(FutureState.Finished)
template complete*[T](futvar: FutureVar[T]) =
## Completes a ``FutureVar``.
complete(futvar, getSrcLocation())
proc complete[T](futvar: FutureVar[T], val: T, loc: ptr SrcLoc) =
if not(futvar.cancelled()):
template fut: untyped = Future[T](futvar)
checkFinished(FutureBase(fut), loc)
doAssert(isNil(fut.error))
fut.value = val
fut.finish(FutureState.Finished)
template complete*[T](futvar: FutureVar[T], val: T) =
## Completes a ``FutureVar`` with value ``val``.
##
## Any previously stored value will be overwritten.
complete(futvar, val, getSrcLocation())
proc fail[T](future: Future[T], error: ref CatchableError, loc: ptr SrcLoc) =
if not(future.cancelled()):
checkFinished(FutureBase(future), loc)
@ -503,12 +456,12 @@ proc internalCheckComplete*(fut: FutureBase) {.
injectStacktrace(fut)
raise fut.error
proc internalRead*[T](fut: Future[T] | FutureVar[T]): T {.inline.} =
proc internalRead*[T](fut: Future[T]): T {.inline.} =
# For internal use only. Used in asyncmacro
when T isnot void:
return fut.value
proc read*[T](future: Future[T] | FutureVar[T]): T {.
proc read*[T](future: Future[T] ): T {.
raises: [Defect, CatchableError].} =
## Retrieves the value of ``future``. Future must be finished otherwise
## this function will fail with a ``ValueError`` exception.
@ -533,13 +486,6 @@ proc readError*[T](future: Future[T]): ref CatchableError {.
# TODO: Make a custom exception type for this?
raise newException(ValueError, "No error in future.")
proc mget*[T](future: FutureVar[T]): var T =
## Returns a mutable value stored in ``future``.
##
## Unlike ``read``, this function will not raise an exception if the
## Future has not been finished.
result = Future[T](future).value
template taskFutureLocation(future: FutureBase): string =
let loc = future.location[0]
"[" & (

View File

@ -21,7 +21,7 @@ proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} =
# result = node[0]
when defined(chronosStrictException):
template createCb(retFutureSym, iteratorNameSym,
strName, identName, futureVarCompletions: untyped) =
strName, identName: untyped) =
bind finished
var nameIterVar = iteratorNameSym
@ -51,14 +51,13 @@ when defined(chronosStrictException):
except CancelledError:
retFutureSym.cancelAndSchedule()
except CatchableError as exc:
futureVarCompletions
retFutureSym.fail(exc)
identName(nil)
{.pop.}
else:
template createCb(retFutureSym, iteratorNameSym,
strName, identName, futureVarCompletions: untyped) =
strName, identName: untyped) =
bind finished
var nameIterVar = iteratorNameSym
@ -88,40 +87,19 @@ else:
except CancelledError:
retFutureSym.cancelAndSchedule()
except CatchableError as exc:
futureVarCompletions
retFutureSym.fail(exc)
except Exception as exc:
# TODO remove Exception handler to turn on strict mode
if exc of Defect:
raise (ref Defect)(exc)
futureVarCompletions
retFutureSym.fail((ref ValueError)(msg: exc.msg, parent: exc))
identName(nil)
{.pop.}
proc createFutureVarCompletions(futureVarIdents: seq[NimNode],
fromNode: NimNode): NimNode {.compileTime.} =
result = newNimNode(nnkStmtList, fromNode)
# Add calls to complete each FutureVar parameter.
for ident in futureVarIdents:
# Only complete them if they have not been completed already by the user.
# TODO: Once https://github.com/nim-lang/Nim/issues/5617 is fixed.
# TODO: Add line info to the complete() call!
# In the meantime, this was really useful for debugging :)
#result.add(newCall(newIdentNode("echo"), newStrLitNode(fromNode.lineinfo)))
result.add newIfStmt(
(
newCall(newIdentNode("not"),
newDotExpr(ident, newIdentNode("finished"))),
newCall(newIdentNode("complete"), ident)
)
)
proc processBody(node, retFutureSym: NimNode,
subTypeIsVoid: bool,
futureVarIdents: seq[NimNode]): NimNode {.compileTime.} =
subTypeIsVoid: bool): NimNode {.compileTime.} =
#echo(node.treeRepr)
result = node
case node.kind
@ -129,8 +107,6 @@ proc processBody(node, retFutureSym: NimNode,
result = newNimNode(nnkStmtList, node)
# As I've painfully found out, the order here really DOES matter.
result.add createFutureVarCompletions(futureVarIdents, node)
if node[0].kind == nnkEmpty:
if not subTypeIsVoid:
result.add newCall(newIdentNode("complete"), retFutureSym,
@ -138,8 +114,7 @@ proc processBody(node, retFutureSym: NimNode,
else:
result.add newCall(newIdentNode("complete"), retFutureSym)
else:
let x = node[0].processBody(retFutureSym, subTypeIsVoid,
futureVarIdents)
let x = node[0].processBody(retFutureSym, subTypeIsVoid)
if x.kind == nnkYieldStmt: result.add x
else:
result.add newCall(newIdentNode("complete"), retFutureSym, x)
@ -155,8 +130,7 @@ proc processBody(node, retFutureSym: NimNode,
# We must not transform nested procedures of any form, otherwise
# `retFutureSym` will be used for all nested procedures as their own
# `retFuture`.
result[i] = processBody(result[i], retFutureSym, subTypeIsVoid,
futureVarIdents)
result[i] = processBody(result[i], retFutureSym, subTypeIsVoid)
proc getName(node: NimNode): string {.compileTime.} =
case node.kind
@ -171,14 +145,6 @@ proc getName(node: NimNode): string {.compileTime.} =
else:
error("Unknown name.")
proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} =
result = @[]
for i in 1 ..< len(params):
expectKind(params[i], nnkIdentDefs)
if params[i][1].kind == nnkBracketExpr and
params[i][1][0].eqIdent("futurevar"):
result.add(params[i][0])
proc isInvalidReturnType(typeName: string): bool =
return typeName notin ["Future"] #, "FutureStream"]
@ -217,8 +183,6 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
let subtypeIsVoid = returnType.kind == nnkEmpty or
(baseType.kind == nnkIdent and returnType[1].eqIdent("void"))
let futureVarIdents = getFutureVarIdents(prc.params)
var outerProcBody = newNimNode(nnkStmtList, prc.body)
# -> var retFuture = newFuture[T]()
@ -245,8 +209,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
# -> <proc_body>
# -> complete(retFuture, result)
var iteratorNameSym = genSym(nskIterator, $prcName)
var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid,
futureVarIdents)
var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid)
# don't do anything with forward bodies (empty)
if procBody.kind != nnkEmpty:
if subtypeIsVoid:
@ -259,8 +222,6 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
# fix #13899, `defer` should not escape its original scope
procBody = newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody))
procBody.add(createFutureVarCompletions(futureVarIdents, nil))
if not subtypeIsVoid:
procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
@ -329,8 +290,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
var cbName = genSym(nskVar, prcName & "_continue")
var procCb = getAst createCb(retFutureSym, iteratorNameSym,
newStrLitNode(prcName),
cbName,
createFutureVarCompletions(futureVarIdents, nil))
cbName)
outerProcBody.add procCb
# -> return retFuture