Coroutine support for i386/amd64 platforms unix/windows OSes markAndSweep/refCounting GCs.
This commit is contained in:
parent
df0e1a515b
commit
43bfda057b
13 changed files with 843 additions and 320 deletions
145
lib/pure/coro.nim
Normal file
145
lib/pure/coro.nim
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue