diff options
author | rku <rokups@zoho.com> | 2015-07-31 17:18:16 +0300 |
---|---|---|
committer | rku <rokups@zoho.com> | 2015-07-31 17:59:50 +0300 |
commit | 43bfda057b56d6c67222d4c86e3be90dd3f01e59 (patch) | |
tree | 45fd5d1b1934b698618d221334b172bfd23b1567 /lib/pure/coro.nim | |
parent | df0e1a515b40d09527f0ad7f5aafa226d3ff37a5 (diff) | |
download | Nim-43bfda057b56d6c67222d4c86e3be90dd3f01e59.tar.gz |
Coroutine support for i386/amd64 platforms unix/windows OSes markAndSweep/refCounting GCs.
Diffstat (limited to 'lib/pure/coro.nim')
-rw-r--r-- | lib/pure/coro.nim | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim new file mode 100644 index 000000000..6ef5f6f54 --- /dev/null +++ b/lib/pure/coro.nim @@ -0,0 +1,145 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Rokas Kupstys +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +when not defined(nimCoroutines): + {.error: "Coroutines require -d:nimCoroutines".} + +import os, times +import macros +import arch +import lists + +const coroDefaultStackSize = 512 * 1024 + + +type Coroutine = ref object + # prev: ptr Coroutine + # next: ptr Coroutine + ctx: JmpBuf + fn: proc() + started: bool + lastRun: float + sleepTime: float + stack: pointer + stacksize: int + +var coroutines = initDoublyLinkedList[Coroutine]() +var current: Coroutine +var mainCtx: JmpBuf + + +proc GC_addStack(starts: pointer) {.cdecl, importc.} +proc GC_removeStack(starts: pointer) {.cdecl, importc.} +proc GC_setCurrentStack(starts, pos: pointer) {.cdecl, importc.} + + +proc coroStart*(c: proc(), stacksize: int=coroDefaultStackSize) = + ## Adds coroutine to event loop. It does not run immediately. + var coro = Coroutine() + coro.fn = c + while coro.stack == nil: + coro.stack = alloc0(stacksize) + coro.stacksize = stacksize + coroutines.append(coro) + +{.push stackTrace: off.} +proc coroYield*(sleepTime: float=0) = + ## Stops coroutine execution and resumes no sooner than after ``sleeptime`` seconds. + ## Until then other coroutines are executed. + var oldFrame = getFrame() + var sp {.volatile.}: pointer + GC_setCurrentStack(current.stack, cast[pointer](addr sp)) + current.sleepTime = sleep_time + current.lastRun = epochTime() + if setjmp(current.ctx) == 0: + longjmp(mainCtx, 1) + setFrame(oldFrame) +{.pop.} + +proc coroRun*() = + ## Starts main event loop which exits when all coroutines exit. Calling this proc + ## starts execution of first coroutine. + var node = coroutines.head + var minDelay: float = 0 + var frame: PFrame + while node != nil: + var coro = node.value + current = coro + os.sleep(int(minDelay * 1000)) + + var remaining = coro.sleepTime - (epochTime() - coro.lastRun); + if remaining <= 0: + remaining = 0 + let res = setjmp(mainCtx) + if res == 0: + frame = getFrame() + if coro.started: # coroutine resumes + longjmp(coro.ctx, 1) + else: + coro.started = true # coroutine starts + var stackEnd = cast[pointer](cast[ByteAddress](coro.stack) + coro.stacksize) + GC_addStack(coro.stack) + coroSwitchStack(stackEnd) + coro.fn() + coroRestoreStack() + GC_removeStack(coro.stack) + var next = node.prev + coroutines.remove(node) + dealloc(coro.stack) + node = next + setFrame(frame) + else: + setFrame(frame) + + elif remaining > 0: + if minDelay > 0 and remaining > 0: + minDelay = min(remaining, minDelay) + else: + minDelay = remaining + + if node == nil or node.next == nil: + node = coroutines.head + else: + node = node.next + + +proc coroAlive*(c: proc()): bool = + ## Returns ``true`` if coroutine has not returned, ``false`` otherwise. + for coro in items(coroutines): + if coro.fn == c: + return true + +proc coroWait*(c: proc(), interval=0.01) = + ## Returns only after coroutine ``c`` has returned. ``interval`` is time in seconds how often. + while coroAlive(c): + coroYield interval + + +when isMainModule: + var stackCheckValue = 1100220033 + proc c2() + + proc c1() = + for i in 0 .. 3: + echo "c1" + coroYield 0.05 + echo "c1 exits" + + + proc c2() = + for i in 0 .. 3: + echo "c2" + coroYield 0.025 + coroWait(c1) + echo "c2 exits" + + coroStart(c1) + coroStart(c2) + coroRun() + echo "done ", stackCheckValue |