summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/coro.nim13
-rw-r--r--lib/system/gc.nim7
-rw-r--r--lib/system/gc_common.nim20
3 files changed, 33 insertions, 7 deletions
diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim
index 75cd9ddac..b227a5a3d 100644
--- a/lib/pure/coro.nim
+++ b/lib/pure/coro.nim
@@ -164,6 +164,7 @@ proc runCurrentTask()
 proc switchTo(current, to: Coroutine) =
   ## Switches execution from `current` into `to` context.
   to.lastRun = getTicks()
+  # Execution will switch to another fiber now.
   when coroBackend == CORO_BACKEND_FIBERS:
     SwitchToFiber(to.execContext)
   elif coroBackend == CORO_BACKEND_UCONTEXT:
@@ -180,13 +181,13 @@ proc switchTo(current, to: Coroutine) =
         doAssert false
   else:
     {.error: "Invalid coroutine backend set.".}
+  # Execution was just resumed. Set active stack to current one.
+  GC_setCurrentStack(current.stack.start)
 
 proc suspend*(sleepTime: float=0) =
   ## Stops coroutine execution and resumes no sooner than after ``sleeptime`` seconds.
   ## Until then other coroutines are executed.
-  var sp {.volatile.}: pointer
   var current = getCurrent()
-  GC_setCurrentStack(current.stack.start, cast[pointer](addr sp))
   current.sleepTime = sleepTime
   var frame = getFrameState()
   switchTo(current, ctx.loop)
@@ -195,6 +196,9 @@ proc suspend*(sleepTime: float=0) =
 proc runCurrentTask() =
   ## Starts execution of current coroutine and updates it's state through coroutine's life.
   var current = getCurrent()
+  # Execution of new fiber just started. Since it was entered not through `switchTo` we
+  # have to set active stack here as well.
+  GC_setCurrentStack(current.stack.start)
   current.state = CORO_EXECUTING
   current.fn()                    # Start coroutine execution
   current.state = CORO_FINISHED
@@ -228,6 +232,7 @@ proc start*(c: proc(), stacksize: int=defaultStackSize) =
       coro.execContext.uc_link = addr ctx.loop.execContext
       makecontext(coro.execContext, runCurrentTask, 0)
   coro.stack.size = stacksize
+  coro.state = CORO_CREATED
   GC_addStack(coro.stack.ends)
   ctx.coroutines.append(coro)
 
@@ -260,7 +265,9 @@ proc run*() =
         # are to be scheduled.
         next = ctx.current.next
       ctx.coroutines.remove(ctx.current)
-      when coroBackend != CORO_BACKEND_FIBERS:
+      when coroBackend == CORO_BACKEND_FIBERS:
+        DeleteFiber(coro.execContext)
+      else:
         dealloc(current.stack.start)
       current.stack.start = nil
       current.stack.ends = nil
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index c17442a97..eaf68c0c4 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -72,8 +72,11 @@ type
 
   GcHeap {.final, pure.} = object # this contains the zero count and
                                    # non-zero count table
-    stack: ptr GcStack
-    stackBottom: pointer
+    when defined(nimCoroutines):
+      stack: ptr GcStack
+      stackActive: ptr GcStack
+    else:
+      stackBottom: pointer
     cycleThreshold: int
     when useCellIds:
       idGenerator: int
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index 4116a8525..feea454e7 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -108,12 +108,15 @@ when defined(nimCoroutines):
       else:
         stack = stack.next
 
-  proc GC_setCurrentStack*(starts, pos: pointer) {.cdecl, exportc.} =
+  proc GC_setCurrentStack*(starts: pointer) {.cdecl, exportc.} =
+    var pos {.volatile.}: pointer
+    pos = addr(pos)
     var stack = gch.stack
     while stack != nil:
       if stack.starts == starts:
         stack.pos = pos
         stack.maxStackSize = max(stack.maxStackSize, stackSize(stack.starts, pos))
+        gch.stackActive = stack
         return
       stack = stack.next
     gcAssert(false, "Current stack position does not belong to registered stack")
@@ -183,7 +186,11 @@ when not defined(useNimRtl):
     #c_fprintf(stdout, "stack bottom: %p;\n", theStackBottom)
     # the first init must be the one that defines the stack bottom:
     when defined(nimCoroutines):
-      GC_addStack(theStackBottom)
+      if gch.stack == nil:
+        # `setStackBottom()` gets called multiple times from main thread.
+        # Add it only once.
+        GC_addStack(theStackBottom)
+        GC_setCurrentStack(theStackBottom)
     else:
       if gch.stackBottom == nil: gch.stackBottom = theStackBottom
       else:
@@ -279,10 +286,19 @@ else:
       type PStackSlice = ptr array[0..7, pointer]
       var registers {.noinit.}: C_JmpBuf
       discard c_setjmp(registers)
+      gch.stackActive.pos = addr(registers)
       for stack in items(gch.stack):
         stack.maxStackSize = max(stack.maxStackSize, stackSize(stack.starts))
         var max = cast[ByteAddress](stack.starts)
         var sp = cast[ByteAddress](stack.pos)
+        when defined(amd64):
+          if stack == gch.stackActive:
+            # words within the jmp_buf structure may not be properly aligned.
+            let regEnd = sp +% sizeof(registers)
+            while sp <% regEnd:
+              gcMark(gch, cast[PPointer](sp)[])
+              gcMark(gch, cast[PPointer](sp +% sizeof(pointer) div 2)[])
+              sp = sp +% sizeof(pointer)
         # loop unrolled:
         while sp <% max - 8*sizeof(pointer):
           gcMark(gch, cast[PStackSlice](sp)[0])