return
}
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ for _, datap := range activeModules() {
if cgoInRange(p, datap.data, datap.edata) || cgoInRange(p, datap.bss, datap.ebss) {
// We have no way to know the size of the object.
// We have to assume that it might contain a pointer.
return true
}
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ for _, datap := range activeModules() {
if cgoInRange(p, datap.data, datap.edata) || cgoInRange(p, datap.bss, datap.ebss) {
return true
}
}
// The type has a GC program. Try to find GC bits somewhere else.
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ for _, datap := range activeModules() {
if cgoInRange(src, datap.data, datap.edata) {
doff := uintptr(src) - datap.data
cgoCheckBits(add(src, -doff), datap.gcdatamask.bytedata, off+doff, size)
func itabsinit() {
lock(&ifaceLock)
- for m := &firstmoduledata; m != nil; m = m.next {
- for _, i := range m.itablinks {
+ for _, md := range activeModules() {
+ for _, i := range md.itablinks {
additab(i, true, false)
}
}
// If dst is a global, use the data or BSS bitmaps to
// execute write barriers.
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ for _, datap := range activeModules() {
if datap.data <= dst && dst < datap.edata {
bulkBarrierBitmap(dst, src, size, dst-datap.data, datap.gcdatamask.bytedata)
return
}
}
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ for _, datap := range activeModules() {
if datap.bss <= dst && dst < datap.ebss {
bulkBarrierBitmap(dst, src, size, dst-datap.bss, datap.gcbssmask.bytedata)
return
p := e.data
t := e._type
// data or bss
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ for _, datap := range activeModules() {
// data
if datap.data <= uintptr(p) && uintptr(p) < datap.edata {
bitmap := datap.gcdatamask.bytedata
}
_ = setGCPercent(readgogc())
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
- datap.gcdatamask = progToPointerMask((*byte)(unsafe.Pointer(datap.gcdata)), datap.edata-datap.data)
- datap.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(datap.gcbss)), datap.ebss-datap.bss)
- }
memstats.gc_trigger = heapminimum
// Compute the goal heap size based on the trigger:
// trigger = marked * (1 + triggerRatio)
// Only scan globals once per cycle; preferably concurrently.
if !work.markrootDone {
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ for _, datap := range activeModules() {
nDataRoots := nBlocks(datap.edata - datap.data)
if nDataRoots > work.nDataRoots {
work.nDataRoots = nDataRoots
}
}
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ for _, datap := range activeModules() {
nBSSRoots := nBlocks(datap.ebss - datap.bss)
if nBSSRoots > work.nBSSRoots {
work.nBSSRoots = nBSSRoots
flushmcache(int(i - baseFlushCache))
case baseData <= i && i < baseBSS:
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ for _, datap := range activeModules() {
markrootBlock(datap.data, datap.edata-datap.data, datap.gcdatamask.bytedata, gcw, int(i-baseData))
}
case baseBSS <= i && i < baseSpans:
- for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ for _, datap := range activeModules() {
markrootBlock(datap.bss, datap.ebss-datap.bss, datap.gcbssmask.bytedata, gcw, int(i-baseBSS))
}
throw("runtime: plugin already initialized")
}
- for pmd := &firstmoduledata; pmd != md; pmd = pmd.next {
+ for _, pmd := range activeModules() {
if pmd.pluginpath == md.pluginpath {
println("plugin: plugin", md.pluginpath, "already loaded")
throw("plugin: plugin already loaded")
}
// Initialize the freshly loaded module.
+ modulesinit()
typelinksinit()
- md.gcdatamask = progToPointerMask((*byte)(unsafe.Pointer(md.gcdata)), md.edata-md.data)
- md.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(md.gcbss)), md.ebss-md.bss)
lock(&ifaceLock)
for _, i := range md.itablinks {
mallocinit()
mcommoninit(_g_.m)
alginit() // maps must not be used before this call
- typelinksinit() // uses maps
- itabsinit()
+ modulesinit() // provides activeModules
+ typelinksinit() // uses maps, activeModules
+ itabsinit() // uses activeModules
msigsave(_g_.m)
initSigmask = _g_.m.sigmask
goargs()
goenvs()
parsedebugvars()
- gcinit()
+ gcinit() // requires modulesinit
sched.lastpoll = uint64(nanotime())
procs := ncpu
//go:linkname reflect_typelinks reflect.typelinks
func reflect_typelinks() ([]unsafe.Pointer, [][]int32) {
- sections := []unsafe.Pointer{unsafe.Pointer(firstmoduledata.types)}
- ret := [][]int32{firstmoduledata.typelinks}
- for datap := firstmoduledata.next; datap != nil; datap = datap.next {
- sections = append(sections, unsafe.Pointer(datap.types))
- ret = append(ret, datap.typelinks)
+ modules := activeModules()
+ sections := []unsafe.Pointer{unsafe.Pointer(modules[0].types)}
+ ret := [][]int32{modules[0].typelinks}
+ for _, md := range modules[1:] {
+ sections = append(sections, unsafe.Pointer(md.types))
+ ret = append(ret, md.typelinks)
}
return sections, ret
}
package runtime
import (
+ "runtime/internal/atomic"
"runtime/internal/sys"
"unsafe"
)
var firstmoduledata moduledata // linker symbol
var lastmoduledatap *moduledata // linker symbol
+var modulesSlice unsafe.Pointer // see activeModules
+
+// activeModules returns a slice of active modules.
+//
+// A module is active once its gcdatamask and gcbssmask have been
+// assembled and it is usable by the GC.
+func activeModules() []*moduledata {
+ p := (*[]*moduledata)(atomic.Loadp(unsafe.Pointer(&modulesSlice)))
+ if p == nil {
+ return nil
+ }
+ return *p
+}
+
+// modulesinit creates the active modules slice out of all loaded modules.
+//
+// When a module is first loaded by the dynamic linker, an .init_array
+// function (written by cmd/link) is invoked to call addmoduledata,
+// appending to the module to the linked list that starts with
+// firstmoduledata.
+//
+// There are two times this can happen in the lifecycle of a Go
+// program. First, if compiled with -linkshared, a number of modules
+// built with -buildmode=shared can be loaded at program initialization.
+// Second, a Go program can load a module while running that was built
+// with -buildmode=plugin.
+//
+// After loading, this function is called which initializes the
+// moduledata so it is usable by the GC and creates a new activeModules
+// list.
+//
+// Only one goroutine may call modulesinit at a time.
+func modulesinit() {
+ oldNum := len(activeModules())
+ modules := new([]*moduledata)
+ num := 0
+ for md := &firstmoduledata; md != nil; md = md.next {
+ *modules = append(*modules, md)
+ num++
+ if num > oldNum {
+ md.gcdatamask = progToPointerMask((*byte)(unsafe.Pointer(md.gcdata)), md.edata-md.data)
+ md.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(md.gcbss)), md.ebss-md.bss)
+ }
+ }
+ atomicstorep(unsafe.Pointer(&modulesSlice), unsafe.Pointer(modules))
+}
type functab struct {
entry uintptr
}
typehash := make(map[uint32][]*_type, len(firstmoduledata.typelinks))
- prev := &firstmoduledata
- md := firstmoduledata.next
- for md != nil {
+ modules := activeModules()
+ prev := modules[0]
+ for _, md := range modules[1:] {
// Collect types from the previous module into typehash.
collect:
for _, tl := range prev.typelinks {
}
prev = md
- md = md.next
}
}