fix coro proc crash for stack problem when run long enough than a GC cycle (#7612) (#11410)

Co-authored-by: drswinghead <drswinghead@gmail.com>
Co-authored-by: Clyybber <darkmine956@gmail.com>
This commit is contained in:
yatsen1 2020-09-18 23:17:09 +08:00 • committed by GitHub
commit 341cd844b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 14 additions and 0 deletions

View file

@ -36,6 +36,7 @@ const defaultStackSize = 512 * 1024
proc GC_addStack(bottom: pointer) {.cdecl, importc.} proc GC_addStack(bottom: pointer) {.cdecl, importc.}
proc GC_removeStack(bottom: pointer) {.cdecl, importc.} proc GC_removeStack(bottom: pointer) {.cdecl, importc.}
proc GC_setActiveStack(bottom: pointer) {.cdecl, importc.} proc GC_setActiveStack(bottom: pointer) {.cdecl, importc.}
proc GC_getActiveStack() : pointer {.cdecl, importc.}
const const
CORO_BACKEND_UCONTEXT = 0 CORO_BACKEND_UCONTEXT = 0
@ -166,6 +167,7 @@ type
coroutines: DoublyLinkedList[CoroutinePtr] coroutines: DoublyLinkedList[CoroutinePtr]
current: DoublyLinkedNode[CoroutinePtr] current: DoublyLinkedNode[CoroutinePtr]
loop: Coroutine loop: Coroutine
ncbottom: pointer # non coroutine stack botttom
var ctx {.threadvar.}: CoroutineLoopContext var ctx {.threadvar.}: CoroutineLoopContext
@ -183,6 +185,7 @@ proc initialize() =
ctx.coroutines = initDoublyLinkedList[CoroutinePtr]() ctx.coroutines = initDoublyLinkedList[CoroutinePtr]()
ctx.loop = Coroutine() ctx.loop = Coroutine()
ctx.loop.state = CORO_EXECUTING ctx.loop.state = CORO_EXECUTING
ctx.ncbottom = GC_getActiveStack()
when coroBackend == CORO_BACKEND_FIBERS: when coroBackend == CORO_BACKEND_FIBERS:
ctx.loop.execContext = ConvertThreadToFiberEx(nil, FIBER_FLAG_FLOAT_SWITCH) ctx.loop.execContext = ConvertThreadToFiberEx(nil, FIBER_FLAG_FLOAT_SWITCH)
@ -193,6 +196,7 @@ proc switchTo(current, to: CoroutinePtr) =
to.lastRun = getTicks() to.lastRun = getTicks()
# Update position of current stack so gc invoked from another stack knows how much to scan. # Update position of current stack so gc invoked from another stack knows how much to scan.
GC_setActiveStack(current.stack.bottom) GC_setActiveStack(current.stack.bottom)
nimGC_setStackBottom(current.stack.bottom)
var frame = getFrameState() var frame = getFrameState()
block: block:
# Execution will switch to another fiber now. We do not need to update current stack # Execution will switch to another fiber now. We do not need to update current stack
@ -215,12 +219,14 @@ proc switchTo(current, to: CoroutinePtr) =
# Execution was just resumed. Restore frame information and set active stack. # Execution was just resumed. Restore frame information and set active stack.
setFrameState(frame) setFrameState(frame)
GC_setActiveStack(current.stack.bottom) GC_setActiveStack(current.stack.bottom)
nimGC_setStackBottom(ctx.ncbottom)
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 current = getCurrent() var current = getCurrent()
current.sleepTime = sleepTime current.sleepTime = sleepTime
nimGC_setStackBottom(ctx.ncbottom)
switchTo(current, addr(ctx.loop)) switchTo(current, addr(ctx.loop))
proc runCurrentTask() = proc runCurrentTask() =
@ -230,6 +236,7 @@ proc runCurrentTask() =
block: block:
var current = getCurrent() var current = getCurrent()
current.stack.bottom = sp current.stack.bottom = sp
nimGC_setStackBottom(current.stack.bottom)
# Execution of new fiber just started. Since it was entered not through `switchTo` we # Execution of new fiber just started. Since it was entered not through `switchTo` we
# have to set active stack here as well. GC_removeStack() has to be called in main loop # have to set active stack here as well. GC_removeStack() has to be called in main loop
# because we still need stack available in final suspend(0) call from which we will not # because we still need stack available in final suspend(0) call from which we will not
@ -244,6 +251,7 @@ proc runCurrentTask() =
echo "Unhandled exception in coroutine." echo "Unhandled exception in coroutine."
writeStackTrace() writeStackTrace()
current.state = CORO_FINISHED current.state = CORO_FINISHED
nimGC_setStackBottom(ctx.ncbottom)
suspend(0) # Exit coroutine without returning from coroExecWithStack() suspend(0) # Exit coroutine without returning from coroExecWithStack()
doAssert false doAssert false

View file

@ -273,6 +273,9 @@ when nimCoroutines:
gch.activeStack = gch.stack.find(bottom) gch.activeStack = gch.stack.find(bottom)
gch.activeStack.setPosition(addr(sp)) gch.activeStack.setPosition(addr(sp))
proc GC_getActiveStack() : pointer {.cdecl, exportc.} =
return gch.activeStack.bottom
when not defined(useNimRtl): when not defined(useNimRtl):
proc nimGC_setStackBottom(theStackBottom: pointer) = proc nimGC_setStackBottom(theStackBottom: pointer) =
# Initializes main stack of the thread. # Initializes main stack of the thread.
@ -300,6 +303,9 @@ when not defined(useNimRtl):
else: else:
gch.stack.bottom = cast[pointer](max(a, b)) gch.stack.bottom = cast[pointer](max(a, b))
when nimCoroutines:
if theStackBottom != nil: gch.stack.bottom = theStackBottom
gch.stack.setPosition(theStackBottom) gch.stack.setPosition(theStackBottom)
{.pop.} {.pop.}