Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async exception tracking #24582

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 34 additions & 9 deletions lib/pure/asyncfutures.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#

import std/[os, sets, tables, strutils, times, heapqueue, options, deques, cstrutils, typetraits]
import std/[os, macros, sets, tables, strutils, strformat, times, heapqueue, options, deques, cstrutils, typetraits]

import system/stacktraces

Expand Down Expand Up @@ -39,6 +39,8 @@ type

FutureVar*[T] = distinct Future[T]

FutureEx*[T, E] = distinct Future[T]

FutureError* = object of Defect
cause*: FutureBase

Expand Down Expand Up @@ -141,7 +143,7 @@ proc clean*[T](future: FutureVar[T]) =
Future[T](future).finished = false
Future[T](future).error = nil

proc checkFinished[T](future: Future[T]) =
proc checkFinished[T](future: Future[T]) {.raises: [].} =
## Checks whether `future` is finished. If it is then raises a
## `FutureError`.
when not defined(release):
Expand All @@ -162,11 +164,13 @@ proc checkFinished[T](future: Future[T]) =
err.cause = future
raise err

proc call(callbacks: var CallbackList) =
proc call(callbacks: var CallbackList) {.raises: [].} =
var current = callbacks
while true:
if not current.function.isNil:
callSoon(current.function)
# XXX make callbacks {.raise: [].}
{.cast(raises: []).}:
callSoon(current.function)

if current.next.isNil:
break
Expand Down Expand Up @@ -293,12 +297,11 @@ template getFilenameProcname(entry: StackTraceEntry): (string, string) =
else:
($entry.filename, $entry.procname)

proc format(entry: StackTraceEntry): string =
proc format(entry: StackTraceEntry): string {.raises: [].} =
let (filename, procname) = getFilenameProcname(entry)
let left = "$#($#)" % [filename, $entry.line]
result = spaces(2) & "$# $#\n" % [left, procname]
result = &"{spaces(2)}{filename}({$entry.line}) {procname}\n"

proc isInternal(entry: StackTraceEntry): bool =
proc isInternal(entry: StackTraceEntry): bool {.raises: [].} =
# --excessiveStackTrace:off
const internals = [
"asyncdispatch.nim",
Expand All @@ -311,7 +314,7 @@ proc isInternal(entry: StackTraceEntry): bool =
return true
return false

proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string =
proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string {.raises: [].} =
result = ""
when defined(nimStackTraceOverride):
let entries = addDebuggingInfo(stackTraceEntries)
Expand Down Expand Up @@ -384,6 +387,28 @@ proc read*[T](future: Future[T] | FutureVar[T]): lent T =
proc read*(future: Future[void] | FutureVar[void]) =
readImpl(future, void)

macro readTrackedImpl(future: FutureEx): untyped =
# XXX refactor readImpl
let t = getTypeInst(future)[1]
let e = getTypeInst(future)[2]
let types = getType(e)
var raisesList = newNimNode(nnkBracket)
for r in types[1..^1]:
raisesList.add(r)
#echo repr raisesList
#echo repr t
let raisesCast = quote do:
cast(raises: `raisesList`)
quote do:
{.`raisesCast`.}:
readImpl(`future`, `t`)

proc read*[T, E](future: FutureEx[T, E]): lent T =
readTrackedImpl(future)

proc read*[E](future: FutureEx[void, E]) =
readTrackedImpl(future)

proc readError*[T](future: Future[T]): ref Exception =
## Retrieves the exception stored in `future`.
##
Expand Down
93 changes: 87 additions & 6 deletions lib/pure/asyncmacro.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

## Implements the `async` and `multisync` macros for `asyncdispatch`.

import std/[macros, strutils, asyncfutures]
import std/[macros, strutils, asyncfutures, effecttraits]

type
Context = ref object
Expand Down Expand Up @@ -49,9 +49,10 @@ template createCb(futTyp, strName, identName, futureVarCompletions: untyped) =
except:
futureVarCompletions
if fut.finished:
# Take a look at tasyncexceptions for the bug which this fixes.
# That test explains it better than I can here.
raise
{.cast(raises: []).}:
# Take a look at tasyncexceptions for the bug which this fixes.
# That test explains it better than I can here.
raise
else:
fut.fail(getCurrentException())
{.pop.}
Expand Down Expand Up @@ -158,6 +159,52 @@ proc verifyReturnType(typeName: string, node: NimNode = nil) =
error("Expected return type of 'Future' got '$1'" %
typeName, node)

proc isAsyncPrc0(n: NimNode): bool =
if n.kind == nnkBlockStmt and n[0].strVal == "asynctrack":
return true
if n.kind in RoutineNodes:
return false
for i in 0 .. n.len-1:
if isAsyncPrc0(n[i]):
return true
return false

proc isAsyncPrc(n: NimNode): bool =
for i in 0 .. n.len-1:
if isAsyncPrc0(n[i]):
return true
return false

macro withRaises[T](f: Future[T], body: untyped): untyped =
#echo repr f.kind
# XXX support more cases
let prcSym = case f.kind
of nnkSym:
if f.getImpl.kind == nnkIdentDefs and f.getImpl[^1].kind == nnkCall:
#echo repr f.getImpl[^1][0]
f.getImpl[^1][0]
else:
nil
of nnkCall:
if f[0].kind == nnkSym: f[0] else: nil
else:
nil
#echo repr prcSym
#echo repr prcSym.getImpl
if prcSym != nil and isAsyncPrc(prcSym.getImpl):
let raisesList = getRaisesList(prcSym)
var raisesListTyp = newNimNode(nnkBracket)
if raisesList.len > 0:
for r in raisesList[1..^1]:
raisesListTyp.add(r)
let raisesCast = quote do:
cast(raises: `raisesListTyp`)
quote do:
{.`raisesCast`.}:
`body`
else:
body

template await*(f: typed): untyped {.used.} =
static:
error "await expects Future[T], got " & $typeof(f)
Expand All @@ -171,7 +218,8 @@ template await*[T](f: Future[T]): auto {.used.} =
var internalTmpFuture: FutureBase = f
yield internalTmpFuture
{.line: instantiationInfo(fullPaths = true).}:
(cast[typeof(f)](internalTmpFuture)).read()
withRaises f:
cast[typeof(f)](internalTmpFuture).read()
else:
macro errorAsync(futureError: Future[T]) =
error(
Expand All @@ -180,6 +228,16 @@ template await*[T](f: Future[T]): auto {.used.} =
futureError)
errorAsync(f)

template await*[T, E](f: FutureEx[T, E]): untyped =
template yieldFuture = yield FutureBase()
when compiles(yieldFuture):
var internalTmpFuture: FutureBase = Future[T](f)
yield internalTmpFuture
{.line: instantiationInfo(fullPaths = true).}:
cast[typeof(f)](internalTmpFuture).read()
else:
{.error: "await is only available within {.async.}".}

proc asyncSingleProc(prc: NimNode): NimNode =
## This macro transforms a single procedure into a closure iterator.
## The `async` macro supports a stmtList holding multiple async procedures.
Expand Down Expand Up @@ -321,8 +379,10 @@ proc asyncSingleProc(prc: NimNode): NimNode =

# based on the yglukhov's patch to chronos: https://github.com/status-im/nim-chronos/pull/47
if procBody.kind != nnkEmpty:
let asynctrack = ident"asynctrack"
body2.add quote do:
`outerProcBody`
block `asynctrack`:
`outerProcBody`
result.body = body2

macro async*(prc: untyped): untyped =
Expand Down Expand Up @@ -394,3 +454,24 @@ macro multisync*(prc: untyped): untyped =
result = newStmtList()
result.add(asyncSingleProc(asyncPrc))
result.add(sync)

macro toFutureEx*(prc: typed): untyped =
template check(cond: untyped): untyped =
if not cond:
error("async proc call expected", prc)
check prc.kind == nnkCall
check prc[0].kind == nnkSym
check isAsyncPrc(prc[0].getImpl)
let procImpl = getTypeImpl(prc[0])
check procImpl.kind == nnkProcTy
let retTyp = procImpl.params[0]
let baseTyp = retTyp[1]
let raisesList = getRaisesList(prc[0])
let exTyp = if raisesList.len == 0:
newIdentNode("void")
else:
newNimNode(nnkTupleConstr)
for r in raisesList:
exTyp.add r
result = quote do:
FutureEx[`baseTyp`, `exTyp`](`prc`)
7 changes: 3 additions & 4 deletions nimdoc/testproject/expected/testproject.html
Original file line number Diff line number Diff line change
Expand Up @@ -559,8 +559,7 @@ <h1><a class="toc-backref" href="#12">Procs</a></h1>
</div>
<div id="asyncFun1-procs-all">
<div id="asyncFun1">
<dt><pre><span class="Keyword">proc</span> <a href="#asyncFun1"><span class="Identifier">asyncFun1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span>
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span>
<dt><pre><span class="Keyword">proc</span> <a href="#asyncFun1"><span class="Identifier">asyncFun1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span>
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>

Expand All @@ -572,7 +571,7 @@ <h1><a class="toc-backref" href="#12">Procs</a></h1>
</div>
<div id="asyncFun2-procs-all">
<div id="asyncFun2">
<dt><pre><span class="Keyword">proc</span> <a href="#asyncFun2"><span class="Identifier">asyncFun2</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span>
<dt><pre><span class="Keyword">proc</span> <a href="#asyncFun2"><span class="Identifier">asyncFun2</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span>
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>

Expand All @@ -584,7 +583,7 @@ <h1><a class="toc-backref" href="#12">Procs</a></h1>
</div>
<div id="asyncFun3-procs-all">
<div id="asyncFun3">
<dt><pre><span class="Keyword">proc</span> <a href="#asyncFun3"><span class="Identifier">asyncFun3</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span>
<dt><pre><span class="Keyword">proc</span> <a href="#asyncFun3"><span class="Identifier">asyncFun3</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span>
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>

Expand Down
Loading