mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 11:37:46 +00:00
internal/task (threads): save stack bounds instead of scanning under a lock
In order to scan stacks, the GC preempts all other threads and has them scan their own stack. This is somewhat expensive since all of these threads have to fight over a single lock. Instead, save the stack bounds and let the GC thread perform the scan. This also fixes a few other bugs I ran into: 1. The GC starts scanning before the world stops. This can cause it to miss some objects (and mistakenly free them) if memory is modified while stopping. 2. The GC does not wait for threads to resume. This can cause notifications to be misinterpreted due to signal nesting if the GC is re-run before all threads wake.
This commit is contained in:
+2
-24
@@ -31,10 +31,6 @@ var zeroSizedAlloc uint8
|
||||
|
||||
var gcLock task.PMutex
|
||||
|
||||
// Normally false, set to true during a GC scan when all other threads get
|
||||
// paused.
|
||||
var needsResumeWorld bool
|
||||
|
||||
func initHeap() {
|
||||
libgc_init()
|
||||
|
||||
@@ -48,20 +44,8 @@ func gcInit()
|
||||
|
||||
//export tinygo_runtime_bdwgc_callback
|
||||
func gcCallback() {
|
||||
if hasParallelism && needsResumeWorld {
|
||||
// Should never happen, check for it anyway.
|
||||
runtimePanic("gc: world already stopped")
|
||||
}
|
||||
|
||||
// Mark globals and all stacks, and stop the world if we're using threading.
|
||||
gcMarkReachable()
|
||||
|
||||
// If we use a scheduler with parallelism (the threads scheduler for
|
||||
// example), we need to call gcResumeWorld() after scanning has finished.
|
||||
if hasParallelism {
|
||||
// Note that we need to resume the world after finishing the GC call.
|
||||
needsResumeWorld = true
|
||||
}
|
||||
}
|
||||
|
||||
func markRoots(start, end uintptr) {
|
||||
@@ -87,7 +71,6 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
|
||||
}
|
||||
|
||||
gcLock.Lock()
|
||||
needsResumeWorld = false
|
||||
var ptr unsafe.Pointer
|
||||
if layout == gclayout.NoPtrs.AsPtr() {
|
||||
// This object is entirely pointer free, for example make([]int, ...).
|
||||
@@ -104,9 +87,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
|
||||
// Memory returned from libgc_malloc has already been zeroed, so nothing
|
||||
// to do here.
|
||||
}
|
||||
if needsResumeWorld {
|
||||
gcResumeWorld()
|
||||
}
|
||||
gcResumeWorld()
|
||||
gcLock.Unlock()
|
||||
if ptr == nil {
|
||||
runtimePanic("gc: out of memory")
|
||||
@@ -121,11 +102,8 @@ func free(ptr unsafe.Pointer) {
|
||||
|
||||
func GC() {
|
||||
gcLock.Lock()
|
||||
needsResumeWorld = false
|
||||
libgc_gcollect()
|
||||
if needsResumeWorld {
|
||||
gcResumeWorld()
|
||||
}
|
||||
gcResumeWorld()
|
||||
gcLock.Unlock()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user