mirror of
https://github.com/status-im/nim-chronos.git
synced 2025-02-22 16:08:23 +00:00
commit
1ffa329fe1
@ -9,6 +9,12 @@
|
|||||||
# MIT license (LICENSE-MIT)
|
# MIT license (LICENSE-MIT)
|
||||||
|
|
||||||
import os, tables, strutils, times, heapqueue, options, deques, cstrutils
|
import os, tables, strutils, times, heapqueue, options, deques, cstrutils
|
||||||
|
import srcloc
|
||||||
|
export srcloc
|
||||||
|
|
||||||
|
const
|
||||||
|
LocCreateIndex = 0
|
||||||
|
LocCompleteIndex = 1
|
||||||
|
|
||||||
type
|
type
|
||||||
CallbackFunc* = proc (arg: pointer = nil) {.gcsafe.}
|
CallbackFunc* = proc (arg: pointer = nil) {.gcsafe.}
|
||||||
@ -24,14 +30,13 @@ type
|
|||||||
StackTrace = string
|
StackTrace = string
|
||||||
|
|
||||||
FutureBase* = ref object of RootObj ## Untyped future.
|
FutureBase* = ref object of RootObj ## Untyped future.
|
||||||
|
location: array[2, ptr SrcLoc]
|
||||||
callbacks: Deque[AsyncCallback]
|
callbacks: Deque[AsyncCallback]
|
||||||
|
|
||||||
finished: bool
|
finished: bool
|
||||||
error*: ref Exception ## Stored exception
|
error*: ref Exception ## Stored exception
|
||||||
errorStackTrace*: StackTrace
|
errorStackTrace*: StackTrace
|
||||||
stackTrace: StackTrace ## For debugging purposes only.
|
stackTrace: StackTrace ## For debugging purposes only.
|
||||||
id: int
|
id: int
|
||||||
fromProc: string
|
|
||||||
|
|
||||||
# ZAH: we have discussed some possible optimizations where
|
# ZAH: we have discussed some possible optimizations where
|
||||||
# the future can be stored within the caller's stack frame.
|
# the future can be stored within the caller's stack frame.
|
||||||
@ -72,12 +77,12 @@ proc callSoon*(c: CallbackFunc, u: pointer = nil) =
|
|||||||
## Call ``cbproc`` "soon".
|
## Call ``cbproc`` "soon".
|
||||||
callSoonHolder(c, u)
|
callSoonHolder(c, u)
|
||||||
|
|
||||||
template setupFutureBase(fromProc: string) =
|
template setupFutureBase(loc: ptr SrcLoc) =
|
||||||
new(result)
|
new(result)
|
||||||
result.finished = false
|
result.finished = false
|
||||||
result.stackTrace = getStackTrace()
|
result.stackTrace = getStackTrace()
|
||||||
result.id = currentID
|
result.id = currentID
|
||||||
result.fromProc = fromProc
|
result.location[LocCreateIndex] = loc
|
||||||
currentID.inc()
|
currentID.inc()
|
||||||
|
|
||||||
## ZAH: As far as I undestand `fromProc` is just a debugging helper.
|
## ZAH: As far as I undestand `fromProc` is just a debugging helper.
|
||||||
@ -85,43 +90,55 @@ template setupFutureBase(fromProc: string) =
|
|||||||
## known `char *` in the final program (so it needs to be a `cstring` in Nim).
|
## known `char *` in the final program (so it needs to be a `cstring` in Nim).
|
||||||
## The public API can be defined as a template expecting a `static[string]`
|
## The public API can be defined as a template expecting a `static[string]`
|
||||||
## and converting this immediately to a `cstring`.
|
## and converting this immediately to a `cstring`.
|
||||||
proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
|
proc newFuture[T](loc: ptr SrcLoc): Future[T] =
|
||||||
|
setupFutureBase(loc)
|
||||||
|
|
||||||
|
proc newFutureSeq[A, B](loc: ptr SrcLoc): FutureSeq[A, B] =
|
||||||
|
setupFutureBase(loc)
|
||||||
|
|
||||||
|
proc newFutureStr[T](loc: ptr SrcLoc): FutureStr[T] =
|
||||||
|
setupFutureBase(loc)
|
||||||
|
|
||||||
|
proc newFutureVar[T](loc: ptr SrcLoc): FutureVar[T] =
|
||||||
|
FutureVar[T](newFuture[T](loc))
|
||||||
|
|
||||||
|
template newFuture*[T](fromProc: static[string] = ""): auto =
|
||||||
## Creates a new future.
|
## Creates a new future.
|
||||||
##
|
##
|
||||||
## Specifying ``fromProc``, which is a string specifying the name of the proc
|
## 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.
|
## that this future belongs to, is a good habit as it helps with debugging.
|
||||||
setupFutureBase(fromProc)
|
newFuture[T](getSrcLocation(fromProc))
|
||||||
|
|
||||||
proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] =
|
template newFutureSeq*[A, B](fromProc: static[string] = ""): auto =
|
||||||
## Create a new ``FutureVar``. This Future type is ideally suited for
|
## Create a new future which can hold/preserve GC sequence until future will
|
||||||
## situations where you want to avoid unnecessary allocations of Futures.
|
## not be completed.
|
||||||
##
|
##
|
||||||
## Specifying ``fromProc``, which is a string specifying the name of the proc
|
## 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.
|
## that this future belongs to, is a good habit as it helps with debugging.
|
||||||
result = FutureVar[T](newFuture[T](fromProc))
|
newFutureSeq[A, B](getSrcLocation(fromProc))
|
||||||
|
|
||||||
proc newFutureSeq*[A, B](fromProc = "unspecified"): FutureSeq[A, B] =
|
template newFutureStr*[T](fromProc: static[string] = ""): auto =
|
||||||
## Create a new future which can hold/preserve GC string until future will
|
## Create a new future which can hold/preserve GC string until future will
|
||||||
## not be completed.
|
## not be completed.
|
||||||
##
|
##
|
||||||
## Specifying ``fromProc``, which is a string specifying the name of the proc
|
## 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.
|
## that this future belongs to, is a good habit as it helps with debugging.
|
||||||
setupFutureBase(fromProc)
|
newFutureStr[T](getSrcLocation(fromProc))
|
||||||
|
|
||||||
proc newFutureStr*[A](fromProc = "unspecified"): FutureStr[A] =
|
template newFutureVar*[T](fromProc: static[string] = ""): auto =
|
||||||
## Create a new future which can hold/preserve GC seq[T] until future will
|
## Create a new ``FutureVar``. This Future type is ideally suited for
|
||||||
## not be completed.
|
## situations where you want to avoid unnecessary allocations of Futures.
|
||||||
##
|
##
|
||||||
## Specifying ``fromProc``, which is a string specifying the name of the proc
|
## 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.
|
## that this future belongs to, is a good habit as it helps with debugging.
|
||||||
setupFutureBase(fromProc)
|
newFutureVar[T](getSrcLocation(fromProc))
|
||||||
|
|
||||||
proc clean*[T](future: FutureVar[T]) =
|
proc clean*[T](future: FutureVar[T]) =
|
||||||
## Resets the ``finished`` status of ``future``.
|
## Resets the ``finished`` status of ``future``.
|
||||||
Future[T](future).finished = false
|
Future[T](future).finished = false
|
||||||
Future[T](future).error = nil
|
Future[T](future).error = nil
|
||||||
|
|
||||||
proc checkFinished[T](future: Future[T]) =
|
proc checkFinished[T](future: Future[T], loc: ptr SrcLoc) =
|
||||||
## Checks whether `future` is finished. If it is then raises a
|
## Checks whether `future` is finished. If it is then raises a
|
||||||
## ``FutureError``.
|
## ``FutureError``.
|
||||||
if future.finished:
|
if future.finished:
|
||||||
@ -129,7 +146,12 @@ proc checkFinished[T](future: Future[T]) =
|
|||||||
msg.add("An attempt was made to complete a Future more than once. ")
|
msg.add("An attempt was made to complete a Future more than once. ")
|
||||||
msg.add("Details:")
|
msg.add("Details:")
|
||||||
msg.add("\n Future ID: " & $future.id)
|
msg.add("\n Future ID: " & $future.id)
|
||||||
msg.add("\n Created in proc: " & future.fromProc)
|
msg.add("\n Creation location:")
|
||||||
|
msg.add("\n " & $future.location[LocCreateIndex])
|
||||||
|
msg.add("\n First completion location:")
|
||||||
|
msg.add("\n " & $future.location[LocCompleteIndex])
|
||||||
|
msg.add("\n Second completion location:")
|
||||||
|
msg.add("\n " & $loc)
|
||||||
msg.add("\n Stack trace to moment of creation:")
|
msg.add("\n Stack trace to moment of creation:")
|
||||||
msg.add("\n" & indent(future.stackTrace.strip(), 4))
|
msg.add("\n" & indent(future.stackTrace.strip(), 4))
|
||||||
when T is string:
|
when T is string:
|
||||||
@ -137,9 +159,12 @@ proc checkFinished[T](future: Future[T]) =
|
|||||||
msg.add("\n" & indent(future.value.repr, 4))
|
msg.add("\n" & indent(future.value.repr, 4))
|
||||||
msg.add("\n Stack trace to moment of secondary completion:")
|
msg.add("\n Stack trace to moment of secondary completion:")
|
||||||
msg.add("\n" & indent(getStackTrace().strip(), 4))
|
msg.add("\n" & indent(getStackTrace().strip(), 4))
|
||||||
|
msg.add("\n\n")
|
||||||
var err = newException(FutureError, msg)
|
var err = newException(FutureError, msg)
|
||||||
err.cause = future
|
err.cause = future
|
||||||
raise err
|
raise err
|
||||||
|
else:
|
||||||
|
future.location[LocCompleteIndex] = loc
|
||||||
|
|
||||||
proc call(callbacks: var Deque[AsyncCallback]) =
|
proc call(callbacks: var Deque[AsyncCallback]) =
|
||||||
var count = len(callbacks)
|
var count = len(callbacks)
|
||||||
@ -150,7 +175,6 @@ proc call(callbacks: var Deque[AsyncCallback]) =
|
|||||||
dec(count)
|
dec(count)
|
||||||
|
|
||||||
proc add(callbacks: var Deque[AsyncCallback], item: AsyncCallback) =
|
proc add(callbacks: var Deque[AsyncCallback], item: AsyncCallback) =
|
||||||
# ZAH: perhaps this is the default behavior with latest Nim (no need for the `len` check)
|
|
||||||
if len(callbacks) == 0:
|
if len(callbacks) == 0:
|
||||||
callbacks = initDeque[AsyncCallback]()
|
callbacks = initDeque[AsyncCallback]()
|
||||||
callbacks.addLast(item)
|
callbacks.addLast(item)
|
||||||
@ -160,54 +184,65 @@ proc remove(callbacks: var Deque[AsyncCallback], item: AsyncCallback) =
|
|||||||
if p.function == item.function and p.udata == item.udata:
|
if p.function == item.function and p.udata == item.udata:
|
||||||
p.deleted = true
|
p.deleted = true
|
||||||
|
|
||||||
proc complete*[T](future: Future[T], val: T) =
|
proc complete[T](future: Future[T], val: T, loc: ptr SrcLoc) =
|
||||||
## Completes ``future`` with value ``val``.
|
checkFinished(future, loc)
|
||||||
#doAssert(not future.finished, "Future already finished, cannot finish twice.")
|
|
||||||
checkFinished(future)
|
|
||||||
doAssert(isNil(future.error))
|
doAssert(isNil(future.error))
|
||||||
future.value = val
|
future.value = val
|
||||||
future.finished = true
|
future.finished = true
|
||||||
future.callbacks.call()
|
future.callbacks.call()
|
||||||
|
|
||||||
proc complete*(future: Future[void]) =
|
template complete*[T](future: Future[T], val: T) =
|
||||||
|
## Completes ``future`` with value ``val``.
|
||||||
|
complete(future, val, getSrcLocation())
|
||||||
|
|
||||||
|
proc complete(future: Future[void], loc: ptr SrcLoc) =
|
||||||
## Completes a void ``future``.
|
## Completes a void ``future``.
|
||||||
#doAssert(not future.finished, "Future already finished, cannot finish twice.")
|
checkFinished(future, loc)
|
||||||
checkFinished(future)
|
|
||||||
doAssert(isNil(future.error))
|
doAssert(isNil(future.error))
|
||||||
future.finished = true
|
future.finished = true
|
||||||
future.callbacks.call()
|
future.callbacks.call()
|
||||||
|
|
||||||
proc complete*[T](future: FutureVar[T]) =
|
template complete*(future: Future[void]) =
|
||||||
## Completes a ``FutureVar``.
|
complete(future, getSrcLocation())
|
||||||
|
|
||||||
|
proc complete[T](future: FutureVar[T], loc: ptr SrcLoc) =
|
||||||
template fut: untyped = Future[T](future)
|
template fut: untyped = Future[T](future)
|
||||||
checkFinished(fut)
|
checkFinished(fut, loc)
|
||||||
doAssert(isNil(fut.error))
|
doAssert(isNil(fut.error))
|
||||||
fut.finished = true
|
fut.finished = true
|
||||||
fut.callbacks.call()
|
fut.callbacks.call()
|
||||||
|
|
||||||
proc complete*[T](future: FutureVar[T], val: T) =
|
template complete*[T](futvar: FutureVar[T]) =
|
||||||
## Completes a ``FutureVar`` with value ``val``.
|
## Completes a ``FutureVar``.
|
||||||
##
|
complete(futvar, getSrcLocation())
|
||||||
## Any previously stored value will be overwritten.
|
|
||||||
template fut: untyped = Future[T](future)
|
proc complete[T](futvar: FutureVar[T], val: T, loc: ptr SrcLoc) =
|
||||||
checkFinished(fut)
|
template fut: untyped = Future[T](futvar)
|
||||||
|
checkFinished(fut, loc)
|
||||||
doAssert(isNil(fut.error))
|
doAssert(isNil(fut.error))
|
||||||
fut.finished = true
|
fut.finished = true
|
||||||
fut.value = val
|
fut.value = val
|
||||||
fut.callbacks.call()
|
fut.callbacks.call()
|
||||||
|
|
||||||
proc fail*[T](future: Future[T], error: ref Exception) =
|
template complete*[T](futvar: FutureVar[T], val: T) =
|
||||||
## Completes ``future`` with ``error``.
|
## Completes a ``FutureVar`` with value ``val``.
|
||||||
#doAssert(not future.finished, "Future already finished, cannot finish twice.")
|
##
|
||||||
checkFinished(future)
|
## Any previously stored value will be overwritten.
|
||||||
|
complete(futvar, val, getSrcLocation())
|
||||||
|
|
||||||
|
proc fail[T](future: Future[T], error: ref Exception, loc: ptr SrcLoc) =
|
||||||
|
checkFinished(future, loc)
|
||||||
future.finished = true
|
future.finished = true
|
||||||
future.error = error
|
future.error = error
|
||||||
future.errorStackTrace =
|
future.errorStackTrace =
|
||||||
if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error)
|
if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error)
|
||||||
future.callbacks.call()
|
future.callbacks.call()
|
||||||
|
|
||||||
|
template fail*[T](future: Future[T], error: ref Exception) =
|
||||||
|
## Completes ``future`` with ``error``.
|
||||||
|
fail(future, error, getSrcLocation())
|
||||||
|
|
||||||
proc clearCallbacks(future: FutureBase) =
|
proc clearCallbacks(future: FutureBase) =
|
||||||
# ZAH: This could have been a single call to `setLen`
|
|
||||||
var count = len(future.callbacks)
|
var count = len(future.callbacks)
|
||||||
while count > 0:
|
while count > 0:
|
||||||
discard future.callbacks.popFirst()
|
discard future.callbacks.popFirst()
|
||||||
@ -315,7 +350,6 @@ proc injectStacktrace[T](future: Future[T]) =
|
|||||||
let start = exceptionMsg.find(header)
|
let start = exceptionMsg.find(header)
|
||||||
exceptionMsg = exceptionMsg[0..<start]
|
exceptionMsg = exceptionMsg[0..<start]
|
||||||
|
|
||||||
|
|
||||||
var newMsg = exceptionMsg & header
|
var newMsg = exceptionMsg & header
|
||||||
|
|
||||||
let entries = getStackTraceEntries(future.error)
|
let entries = getStackTraceEntries(future.error)
|
||||||
|
27
chronos/srcloc.nim
Normal file
27
chronos/srcloc.nim
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
type
|
||||||
|
SrcLoc* = object
|
||||||
|
procedure*: cstring
|
||||||
|
file*: cstring
|
||||||
|
line*: int
|
||||||
|
|
||||||
|
proc `$`*(loc: ptr SrcLoc): string =
|
||||||
|
result.add loc.file
|
||||||
|
result.add "("
|
||||||
|
result.add $loc.line
|
||||||
|
result.add ")"
|
||||||
|
result.add " "
|
||||||
|
if len(loc.procedure) == 0:
|
||||||
|
result.add "[unspecified]"
|
||||||
|
else:
|
||||||
|
result.add loc.procedure
|
||||||
|
|
||||||
|
proc srcLocImpl(procedure: static string,
|
||||||
|
file: static string, line: static int): ptr SrcLoc =
|
||||||
|
var loc {.global.} = SrcLoc(
|
||||||
|
file: cstring(file), line: line, procedure: procedure
|
||||||
|
)
|
||||||
|
return addr(loc)
|
||||||
|
|
||||||
|
template getSrcLocation*(procedure: static string = ""): ptr SrcLoc =
|
||||||
|
srcLocImpl(procedure,
|
||||||
|
instantiationInfo(-2).filename, instantiationInfo(-2).line)
|
Loading…
x
Reference in New Issue
Block a user