diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c index e369e5425c9..0de7b1bf4ad 100644 --- a/src/runtime/mgc0.c +++ b/src/runtime/mgc0.c @@ -1445,6 +1445,8 @@ gc(struct gc_args *args) if(runtime·work.nproc > 1) runtime·notesleep(&runtime·work.alldone); + runtime·shrinkfinish(); + cachestats(); // next_gc calculation is tricky with concurrent sweep since we don't know size of live heap // estimate what was live heap size after previous GC (for tracing only) diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 27a809a07ed..a84a32525eb 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -852,6 +852,7 @@ void runtime·stackinit(void); Stack runtime·stackalloc(uint32); void runtime·stackfree(Stack); void runtime·shrinkstack(G*); +void runtime·shrinkfinish(void); MCache* runtime·allocmcache(void); void runtime·freemcache(MCache*); void runtime·mallocinit(void); diff --git a/src/runtime/stack.c b/src/runtime/stack.c index 8562b94076c..d1ea3ff73bf 100644 --- a/src/runtime/stack.c +++ b/src/runtime/stack.c @@ -36,6 +36,8 @@ MSpan runtime·stackpool[NumStackOrders]; Mutex runtime·stackpoolmu; // TODO: one lock per order? +static Stack stackfreequeue; + void runtime·stackinit(void) { @@ -656,7 +658,24 @@ copystack(G *gp, uintptr newsize) while(p < ep) *p++ = 0xfc; } - runtime·stackfree(old); + if(newsize > old.hi-old.lo) { + // growing, free stack immediately + runtime·stackfree(old); + } else { + // shrinking, queue up free operation. We can't actually free the stack + // just yet because we might run into the following situation: + // 1) GC starts, scans a SudoG but does not yet mark the SudoG.elem pointer + // 2) The stack that pointer points to is shrunk + // 3) The old stack is freed + // 4) The containing span is marked free + // 5) GC attempts to mark the SudoG.elem pointer. The marking fails because + // the pointer looks like a pointer into a free span. + // By not freeing, we prevent step #4 until GC is done. + runtime·lock(&runtime·stackpoolmu); + *(Stack*)old.lo = stackfreequeue; + stackfreequeue = old; + runtime·unlock(&runtime·stackpoolmu); + } } // round x up to a power of 2. @@ -841,6 +860,23 @@ runtime·shrinkstack(G *gp) copystack(gp, newsize); } +// Do any delayed stack freeing that was queued up during GC. +void +runtime·shrinkfinish(void) +{ + Stack s, t; + + runtime·lock(&runtime·stackpoolmu); + s = stackfreequeue; + stackfreequeue = (Stack){0,0}; + runtime·unlock(&runtime·stackpoolmu); + while(s.lo != 0) { + t = *(Stack*)s.lo; + runtime·stackfree(s); + s = t; + } +} + static void badc(void); #pragma textflag NOSPLIT