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:
Rokas Kupstys 2017-02-11 11:45:50 +02:00
commit 9a754156d7
2 changed files with 46 additions and 9 deletions

View file

@ -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

View file

@ -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.} =