1616 LocCreateIndex = 0
1717 LocCompleteIndex = 1
1818
19+ stackTraces = compileOption (" stackTrace" )
20+
1921type
2022 CallbackFunc * = proc (arg: pointer = nil ) {.gcsafe .}
2123 CallSoonProc * = proc (c: CallbackFunc , u: pointer = nil ) {.gcsafe .}
3537
3638 FutureBase * = ref object of RootObj # # Untyped future.
3739 location: array [2 , ptr SrcLoc ]
40+ when stackTraces:
41+ creationLocation: SrcLoc
3842 callbacks: Deque [AsyncCallback ]
3943 cancelcb* : CallbackFunc
4044 child* : FutureBase
6973var currentID* {.threadvar .}: int
7074currentID = 0
7175
76+ {.push stackTrace : off .}
77+ proc callerLocation (): SrcLoc =
78+ when stackTraces:
79+ var f = getFrame ()
80+ if not f.isNil:
81+ result .file = f.filename
82+ result .line = f.line
83+ result .procedure = f.procname
84+ {.pop .}
85+
7286# ZAH: This seems unnecessary. Isn't it easy to introduce a seperate
7387# module for the dispatcher type, so it can be directly referenced here?
7488var callSoonHolder {.threadvar .}: CallSoonProc
@@ -85,61 +99,63 @@ proc callSoon*(c: CallbackFunc, u: pointer = nil) =
8599 # # Call ``cbproc`` "soon".
86100 callSoonHolder (c, u)
87101
88- template setupFutureBase (loc: ptr SrcLoc ) =
102+ template setupFutureBase (loc: ptr SrcLoc , caller: SrcLoc ) =
89103 new (result )
90104 result .state = FutureState .Pending
91105 result .stackTrace = getStackTrace ()
92106 result .id = currentID
93107 result .location[LocCreateIndex ] = loc
108+ when stackTraces:
109+ result .creationLocation = caller
94110 currentID.inc ()
95111
96112# # ZAH: As far as I undestand `fromProc` is just a debugging helper.
97113# # It would be more efficient if it's represented as a simple statically
98114# # known `char *` in the final program (so it needs to be a `cstring` in Nim).
99115# # The public API can be defined as a template expecting a `static[string]`
100116# # and converting this immediately to a `cstring`.
101- proc newFuture [T](loc: ptr SrcLoc ): Future [T] =
102- setupFutureBase (loc)
117+ proc newFuture [T](loc: ptr SrcLoc , srcLoc: SrcLoc ): Future [T] =
118+ setupFutureBase (loc, srcLoc )
103119
104- proc newFutureSeq [A, B](loc: ptr SrcLoc ): FutureSeq [A, B] =
105- setupFutureBase (loc)
120+ proc newFutureSeq [A, B](loc: ptr SrcLoc , srcLoc: SrcLoc ): FutureSeq [A, B] =
121+ setupFutureBase (loc, srcLoc )
106122
107- proc newFutureStr [T](loc: ptr SrcLoc ): FutureStr [T] =
108- setupFutureBase (loc)
123+ proc newFutureStr [T](loc: ptr SrcLoc , srcLoc: SrcLoc ): FutureStr [T] =
124+ setupFutureBase (loc, srcLoc )
109125
110- proc newFutureVar [T](loc: ptr SrcLoc ): FutureVar [T] =
111- FutureVar [T](newFuture [T](loc))
126+ proc newFutureVar [T](loc: ptr SrcLoc , srcLoc: SrcLoc ): FutureVar [T] =
127+ FutureVar [T](newFuture [T](loc, srcLoc ))
112128
113129template newFuture * [T](fromProc: static [string ] = " " ): auto =
114130 # # Creates a new future.
115131 # #
116132 # # Specifying ``fromProc``, which is a string specifying the name of the proc
117133 # # that this future belongs to, is a good habit as it helps with debugging.
118- newFuture [T](getSrcLocation (fromProc))
134+ newFuture [T](getSrcLocation (fromProc), callerLocation () )
119135
120136template newFutureSeq * [A, B](fromProc: static [string ] = " " ): auto =
121137 # # Create a new future which can hold/preserve GC sequence until future will
122138 # # not be completed.
123139 # #
124140 # # Specifying ``fromProc``, which is a string specifying the name of the proc
125141 # # that this future belongs to, is a good habit as it helps with debugging.
126- newFutureSeq [A, B](getSrcLocation (fromProc))
142+ newFutureSeq [A, B](getSrcLocation (fromProc), callerLocation () )
127143
128144template newFutureStr * [T](fromProc: static [string ] = " " ): auto =
129145 # # Create a new future which can hold/preserve GC string until future will
130146 # # not be completed.
131147 # #
132148 # # Specifying ``fromProc``, which is a string specifying the name of the proc
133149 # # that this future belongs to, is a good habit as it helps with debugging.
134- newFutureStr [T](getSrcLocation (fromProc))
150+ newFutureStr [T](getSrcLocation (fromProc), callerLocation () )
135151
136152template newFutureVar * [T](fromProc: static [string ] = " " ): auto =
137153 # # Create a new ``FutureVar``. This Future type is ideally suited for
138154 # # situations where you want to avoid unnecessary allocations of Futures.
139155 # #
140156 # # Specifying ``fromProc``, which is a string specifying the name of the proc
141157 # # that this future belongs to, is a good habit as it helps with debugging.
142- newFutureVar [T](getSrcLocation (fromProc))
158+ newFutureVar [T](getSrcLocation (fromProc), callerLocation () )
143159
144160proc clean * [T](future: FutureVar [T]) =
145161 # # Resets the ``finished`` status of ``future``.
@@ -395,6 +411,40 @@ proc `$`*(entries: seq[StackTraceEntry]): string =
395411 if hint.len > 0 :
396412 result .add (spaces (indent+ 2 ) & " ## " & hint & " \n " )
397413
414+ proc getStackTraceEntries (fut: FutureBase ): string =
415+ var f = fut
416+
417+ result = " ---- Nice stacktrace ------\n "
418+
419+ var topFut = fut
420+
421+ while not f.isNil:
422+ topFut = f
423+ result &= $ f.creationLocation.file & " (" & $ f.creationLocation.line & " ) " & $ f.creationLocation.procedure & " ---\n "
424+ f = f.child
425+
426+ var stStart = 0
427+
428+ let s = fut.error.getStackTraceEntries ()
429+ var i = 1
430+ for e in s:
431+ if e.line == - 10 : break
432+ if e.procName == " poll" :
433+ stStart = i
434+ elif e.filename == topFut.creationLocation.file and e.line == topFut.creationLocation.line:
435+ stStart = i
436+ break
437+ inc i
438+
439+ for i in stStart ..< s.len:
440+ if s[i].line == - 10 : break
441+ result &= $ s[i].filename & " (" & $ s[i].line & " ) " & $ s[i].procname & " \n "
442+
443+ result &= " ---- End of nice stacktrace ------\n "
444+ result &= " ---- Raw upper exception stacktrace ------\n "
445+ result &= $ fut.error.getStackTraceEntries ()
446+ result &= " stStart: " & $ stStart & " \n "
447+
398448proc injectStacktrace (future: FutureBase ) =
399449 const header = " \n Async traceback:\n "
400450
@@ -407,7 +457,7 @@ proc injectStacktrace(future: FutureBase) =
407457
408458 var newMsg = exceptionMsg & header
409459
410- let entries = getStackTraceEntries (future.error )
460+ let entries = getStackTraceEntries (future)
411461 newMsg.add ($ entries)
412462
413463 newMsg.add (" Exception message: " & exceptionMsg & " \n " )
0 commit comments