Added support for exceptions in coroutines and added exception test
Fix bug where first coroutine ending would terminate main loop
This commit is contained in:
parent
373e667dbc
commit
9a754156d7
2 changed files with 46 additions and 9 deletions
|
|
@ -117,6 +117,7 @@ when defined(unix):
|
||||||
{.passC: "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0"}
|
{.passC: "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0"}
|
||||||
|
|
||||||
const
|
const
|
||||||
|
CORO_CREATED = 0
|
||||||
CORO_EXECUTING = 1
|
CORO_EXECUTING = 1
|
||||||
CORO_FINISHED = 2
|
CORO_FINISHED = 2
|
||||||
|
|
||||||
|
|
@ -139,8 +140,7 @@ type
|
||||||
current: DoublyLinkedNode[Coroutine]
|
current: DoublyLinkedNode[Coroutine]
|
||||||
loop: Coroutine
|
loop: Coroutine
|
||||||
|
|
||||||
# Per-thread coroutine loop state.
|
var ctx {.threadvar.}: CoroutineLoopContext
|
||||||
var ctx: CoroutineLoopContext
|
|
||||||
|
|
||||||
proc getCurrent(): Coroutine =
|
proc getCurrent(): Coroutine =
|
||||||
## Returns current executing coroutine object.
|
## Returns current executing coroutine object.
|
||||||
|
|
@ -184,13 +184,13 @@ proc switchTo(current, to: Coroutine) =
|
||||||
proc suspend*(sleepTime: float=0) =
|
proc suspend*(sleepTime: float=0) =
|
||||||
## Stops coroutine execution and resumes no sooner than after ``sleeptime`` seconds.
|
## Stops coroutine execution and resumes no sooner than after ``sleeptime`` seconds.
|
||||||
## Until then other coroutines are executed.
|
## Until then other coroutines are executed.
|
||||||
var frame = getFrame()
|
|
||||||
var sp {.volatile.}: pointer
|
var sp {.volatile.}: pointer
|
||||||
var current = getCurrent()
|
var current = getCurrent()
|
||||||
GC_setCurrentStack(current.stack.start, cast[pointer](addr sp))
|
GC_setCurrentStack(current.stack.start, cast[pointer](addr sp))
|
||||||
current.sleepTime = sleepTime
|
current.sleepTime = sleepTime
|
||||||
|
var frame = getFrameState()
|
||||||
switchTo(current, ctx.loop)
|
switchTo(current, ctx.loop)
|
||||||
setFrame(frame)
|
setFrameState(frame)
|
||||||
|
|
||||||
proc runCurrentTask() =
|
proc runCurrentTask() =
|
||||||
## Starts execution of current coroutine and updates it's state through coroutine's life.
|
## Starts execution of current coroutine and updates it's state through coroutine's life.
|
||||||
|
|
@ -243,9 +243,9 @@ proc run*() =
|
||||||
var remaining = current.sleepTime - (float(getTicks() - current.lastRun) / 1_000_000_000)
|
var remaining = current.sleepTime - (float(getTicks() - current.lastRun) / 1_000_000_000)
|
||||||
if remaining <= 0:
|
if remaining <= 0:
|
||||||
# Save main loop context. Suspending coroutine will resume after this statement with
|
# Save main loop context. Suspending coroutine will resume after this statement with
|
||||||
var frame = getFrame()
|
var frame = getFrameState()
|
||||||
switchTo(ctx.loop, current)
|
switchTo(ctx.loop, current)
|
||||||
setFrame(frame)
|
setFrameState(frame)
|
||||||
else:
|
else:
|
||||||
if minDelay > 0 and remaining > 0:
|
if minDelay > 0 and remaining > 0:
|
||||||
minDelay = min(remaining, minDelay)
|
minDelay = min(remaining, minDelay)
|
||||||
|
|
@ -255,6 +255,10 @@ proc run*() =
|
||||||
if current.state == CORO_FINISHED:
|
if current.state == CORO_FINISHED:
|
||||||
GC_removeStack(current.stack.start)
|
GC_removeStack(current.stack.start)
|
||||||
var next = ctx.current.prev
|
var next = ctx.current.prev
|
||||||
|
if next == nil:
|
||||||
|
# If first coroutine ends then `prev` is nil even if more coroutines
|
||||||
|
# are to be scheduled.
|
||||||
|
next = ctx.current.next
|
||||||
ctx.coroutines.remove(ctx.current)
|
ctx.coroutines.remove(ctx.current)
|
||||||
when coroBackend != CORO_BACKEND_FIBERS:
|
when coroBackend != CORO_BACKEND_FIBERS:
|
||||||
dealloc(current.stack.start)
|
dealloc(current.stack.start)
|
||||||
|
|
@ -287,7 +291,7 @@ when isMainModule:
|
||||||
i: int
|
i: int
|
||||||
order = newSeq[int](10)
|
order = newSeq[int](10)
|
||||||
|
|
||||||
proc Fibonacci(id: int, sleep: float32) =
|
proc testFibonacci(id: int, sleep: float32) =
|
||||||
var sleepTime: float
|
var sleepTime: float
|
||||||
while steps > 0:
|
while steps > 0:
|
||||||
echo id, " executing, slept for ", sleepTime
|
echo id, " executing, slept for ", sleepTime
|
||||||
|
|
@ -300,9 +304,31 @@ when isMainModule:
|
||||||
suspend(sleep)
|
suspend(sleep)
|
||||||
sleepTime = float(getTicks() - sleepStart) / 1_000_000_000
|
sleepTime = float(getTicks() - sleepStart) / 1_000_000_000
|
||||||
|
|
||||||
start(proc() = Fibonacci(1, 0.01))
|
start(proc() = testFibonacci(1, 0.01))
|
||||||
start(proc() = Fibonacci(2, 0.021))
|
start(proc() = testFibonacci(2, 0.021))
|
||||||
run()
|
run()
|
||||||
doAssert stackCheckValue == 1100220033
|
doAssert stackCheckValue == 1100220033
|
||||||
doAssert first == 55.0
|
doAssert first == 55.0
|
||||||
doAssert order == @[1, 2, 1, 1, 2, 1, 1, 2, 1, 1]
|
doAssert order == @[1, 2, 1, 1, 2, 1, 1, 2, 1, 1]
|
||||||
|
|
||||||
|
order = newSeq[int](10)
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
proc testExceptions(id: int, sleep: float) =
|
||||||
|
try:
|
||||||
|
order[i] = id; i += 1
|
||||||
|
suspend(sleep)
|
||||||
|
order[i] = id; i += 1
|
||||||
|
raise (ref ValueError)()
|
||||||
|
except:
|
||||||
|
order[i] = id; i += 1
|
||||||
|
suspend(sleep)
|
||||||
|
order[i] = id; i += 1
|
||||||
|
suspend(sleep)
|
||||||
|
order[i] = id; i += 1
|
||||||
|
|
||||||
|
start(proc() = testExceptions(1, 0.01))
|
||||||
|
start(proc() = testExceptions(2, 0.021))
|
||||||
|
run()
|
||||||
|
doAssert order == @[1, 2, 1, 1, 1, 2, 2, 1, 2, 2]
|
||||||
|
doAssert stackCheckValue == 1100220033
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,17 @@ var
|
||||||
# a global variable for the root of all try blocks
|
# a global variable for the root of all try blocks
|
||||||
currException {.threadvar.}: ref Exception
|
currException {.threadvar.}: ref Exception
|
||||||
|
|
||||||
|
type
|
||||||
|
FrameState = tuple[framePtr: PFrame, excHandler: PSafePoint, currException: ref Exception]
|
||||||
|
|
||||||
|
proc getFrameState*(): FrameState {.compilerRtl, inl.} =
|
||||||
|
return (framePtr, excHandler, currException)
|
||||||
|
|
||||||
|
proc setFrameState*(state: FrameState) {.compilerRtl, inl.} =
|
||||||
|
framePtr = state.framePtr
|
||||||
|
excHandler = state.excHandler
|
||||||
|
currException = state.currException
|
||||||
|
|
||||||
proc getFrame*(): PFrame {.compilerRtl, inl.} = framePtr
|
proc getFrame*(): PFrame {.compilerRtl, inl.} = framePtr
|
||||||
|
|
||||||
proc popFrame {.compilerRtl, inl.} =
|
proc popFrame {.compilerRtl, inl.} =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue