| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package runtime |
| |
| import ( |
| "unsafe" |
| ) |
| |
| const ( |
| debugMalloc = false |
| |
| flagNoScan = _FlagNoScan |
| flagNoZero = _FlagNoZero |
| |
| maxTinySize = _TinySize |
| tinySizeClass = _TinySizeClass |
| maxSmallSize = _MaxSmallSize |
| |
| pageShift = _PageShift |
| pageSize = _PageSize |
| pageMask = _PageMask |
| |
| bitsPerPointer = _BitsPerPointer |
| bitsMask = _BitsMask |
| pointersPerByte = _PointersPerByte |
| maxGCMask = _MaxGCMask |
| bitsDead = _BitsDead |
| bitsPointer = _BitsPointer |
| |
| mSpanInUse = _MSpanInUse |
| |
| concurrentSweep = _ConcurrentSweep != 0 |
| ) |
| |
| // Page number (address>>pageShift) |
| type pageID uintptr |
| |
| // base address for all 0-byte allocations |
| var zerobase uintptr |
| |
| // Allocate an object of size bytes. |
| // Small objects are allocated from the per-P cache's free lists. |
| // Large objects (> 32 kB) are allocated straight from the heap. |
| func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer { |
| if size == 0 { |
| return unsafe.Pointer(&zerobase) |
| } |
| size0 := size |
| |
| if flags&flagNoScan == 0 && typ == nil { |
| gothrow("malloc missing type") |
| } |
| |
| // This function must be atomic wrt GC, but for performance reasons |
| // we don't acquirem/releasem on fast path. The code below does not have |
| // split stack checks, so it can't be preempted by GC. |
| // Functions like roundup/add are inlined. And onM/racemalloc are nosplit. |
| // If debugMalloc = true, these assumptions are checked below. |
| if debugMalloc { |
| mp := acquirem() |
| if mp.mallocing != 0 { |
| gothrow("malloc deadlock") |
| } |
| mp.mallocing = 1 |
| if mp.curg != nil { |
| mp.curg.stackguard0 = ^uintptr(0xfff) | 0xbad |
| } |
| } |
| |
| c := gomcache() |
| var s *mspan |
| var x unsafe.Pointer |
| if size <= maxSmallSize { |
| if flags&flagNoScan != 0 && size < maxTinySize { |
| // Tiny allocator. |
| // |
| // Tiny allocator combines several tiny allocation requests |
| // into a single memory block. The resulting memory block |
| // is freed when all subobjects are unreachable. The subobjects |
| // must be FlagNoScan (don't have pointers), this ensures that |
| // the amount of potentially wasted memory is bounded. |
| // |
| // Size of the memory block used for combining (maxTinySize) is tunable. |
| // Current setting is 16 bytes, which relates to 2x worst case memory |
| // wastage (when all but one subobjects are unreachable). |
| // 8 bytes would result in no wastage at all, but provides less |
| // opportunities for combining. |
| // 32 bytes provides more opportunities for combining, |
| // but can lead to 4x worst case wastage. |
| // The best case winning is 8x regardless of block size. |
| // |
| // Objects obtained from tiny allocator must not be freed explicitly. |
| // So when an object will be freed explicitly, we ensure that |
| // its size >= maxTinySize. |
| // |
| // SetFinalizer has a special case for objects potentially coming |
| // from tiny allocator, it such case it allows to set finalizers |
| // for an inner byte of a memory block. |
| // |
| // The main targets of tiny allocator are small strings and |
| // standalone escaping variables. On a json benchmark |
| // the allocator reduces number of allocations by ~12% and |
| // reduces heap size by ~20%. |
| tinysize := uintptr(c.tinysize) |
| if size <= tinysize { |
| tiny := unsafe.Pointer(c.tiny) |
| // Align tiny pointer for required (conservative) alignment. |
| if size&7 == 0 { |
| tiny = roundup(tiny, 8) |
| } else if size&3 == 0 { |
| tiny = roundup(tiny, 4) |
| } else if size&1 == 0 { |
| tiny = roundup(tiny, 2) |
| } |
| size1 := size + (uintptr(tiny) - uintptr(unsafe.Pointer(c.tiny))) |
| if size1 <= tinysize { |
| // The object fits into existing tiny block. |
| x = tiny |
| c.tiny = (*byte)(add(x, size)) |
| c.tinysize -= uintptr(size1) |
| c.local_tinyallocs++ |
| if debugMalloc { |
| mp := acquirem() |
| if mp.mallocing == 0 { |
| gothrow("bad malloc") |
| } |
| mp.mallocing = 0 |
| if mp.curg != nil { |
| mp.curg.stackguard0 = mp.curg.stack.lo + _StackGuard |
| } |
| // Note: one releasem for the acquirem just above. |
| // The other for the acquirem at start of malloc. |
| releasem(mp) |
| releasem(mp) |
| } |
| return x |
| } |
| } |
| // Allocate a new maxTinySize block. |
| s = c.alloc[tinySizeClass] |
| v := s.freelist |
| if v == nil { |
| mp := acquirem() |
| mp.scalararg[0] = tinySizeClass |
| onM(mcacheRefill_m) |
| releasem(mp) |
| s = c.alloc[tinySizeClass] |
| v = s.freelist |
| } |
| s.freelist = v.next |
| s.ref++ |
| //TODO: prefetch v.next |
| x = unsafe.Pointer(v) |
| (*[2]uint64)(x)[0] = 0 |
| (*[2]uint64)(x)[1] = 0 |
| // See if we need to replace the existing tiny block with the new one |
| // based on amount of remaining free space. |
| if maxTinySize-size > tinysize { |
| c.tiny = (*byte)(add(x, size)) |
| c.tinysize = uintptr(maxTinySize - size) |
| } |
| size = maxTinySize |
| } else { |
| var sizeclass int8 |
| if size <= 1024-8 { |
| sizeclass = size_to_class8[(size+7)>>3] |
| } else { |
| sizeclass = size_to_class128[(size-1024+127)>>7] |
| } |
| size = uintptr(class_to_size[sizeclass]) |
| s = c.alloc[sizeclass] |
| v := s.freelist |
| if v == nil { |
| mp := acquirem() |
| mp.scalararg[0] = uintptr(sizeclass) |
| onM(mcacheRefill_m) |
| releasem(mp) |
| s = c.alloc[sizeclass] |
| v = s.freelist |
| } |
| s.freelist = v.next |
| s.ref++ |
| //TODO: prefetch |
| x = unsafe.Pointer(v) |
| if flags&flagNoZero == 0 { |
| v.next = nil |
| if size > 2*ptrSize && ((*[2]uintptr)(x))[1] != 0 { |
| memclr(unsafe.Pointer(v), size) |
| } |
| } |
| } |
| c.local_cachealloc += intptr(size) |
| } else { |
| mp := acquirem() |
| mp.scalararg[0] = uintptr(size) |
| mp.scalararg[1] = uintptr(flags) |
| onM(largeAlloc_m) |
| s = (*mspan)(mp.ptrarg[0]) |
| mp.ptrarg[0] = nil |
| releasem(mp) |
| x = unsafe.Pointer(uintptr(s.start << pageShift)) |
| size = uintptr(s.elemsize) |
| } |
| |
| if flags&flagNoScan != 0 { |
| // All objects are pre-marked as noscan. |
| goto marked |
| } |
| |
| // If allocating a defer+arg block, now that we've picked a malloc size |
| // large enough to hold everything, cut the "asked for" size down to |
| // just the defer header, so that the GC bitmap will record the arg block |
| // as containing nothing at all (as if it were unused space at the end of |
| // a malloc block caused by size rounding). |
| // The defer arg areas are scanned as part of scanstack. |
| if typ == deferType { |
| size0 = unsafe.Sizeof(_defer{}) |
| } |
| |
| // From here till marked label marking the object as allocated |
| // and storing type info in the GC bitmap. |
| { |
| arena_start := uintptr(unsafe.Pointer(mheap_.arena_start)) |
| off := (uintptr(x) - arena_start) / ptrSize |
| xbits := (*uint8)(unsafe.Pointer(arena_start - off/wordsPerBitmapByte - 1)) |
| shift := (off % wordsPerBitmapByte) * gcBits |
| if debugMalloc && ((*xbits>>shift)&(bitMask|bitPtrMask)) != bitBoundary { |
| println("runtime: bits =", (*xbits>>shift)&(bitMask|bitPtrMask)) |
| gothrow("bad bits in markallocated") |
| } |
| |
| var ti, te uintptr |
| var ptrmask *uint8 |
| if size == ptrSize { |
| // It's one word and it has pointers, it must be a pointer. |
| *xbits |= (bitsPointer << 2) << shift |
| goto marked |
| } |
| if typ.kind&kindGCProg != 0 { |
| nptr := (uintptr(typ.size) + ptrSize - 1) / ptrSize |
| masksize := nptr |
| if masksize%2 != 0 { |
| masksize *= 2 // repeated |
| } |
| masksize = masksize * pointersPerByte / 8 // 4 bits per word |
| masksize++ // unroll flag in the beginning |
| if masksize > maxGCMask && typ.gc[1] != 0 { |
| // If the mask is too large, unroll the program directly |
| // into the GC bitmap. It's 7 times slower than copying |
| // from the pre-unrolled mask, but saves 1/16 of type size |
| // memory for the mask. |
| mp := acquirem() |
| mp.ptrarg[0] = x |
| mp.ptrarg[1] = unsafe.Pointer(typ) |
| mp.scalararg[0] = uintptr(size) |
| mp.scalararg[1] = uintptr(size0) |
| onM(unrollgcproginplace_m) |
| releasem(mp) |
| goto marked |
| } |
| ptrmask = (*uint8)(unsafe.Pointer(uintptr(typ.gc[0]))) |
| // Check whether the program is already unrolled. |
| if uintptr(atomicloadp(unsafe.Pointer(ptrmask)))&0xff == 0 { |
| mp := acquirem() |
| mp.ptrarg[0] = unsafe.Pointer(typ) |
| onM(unrollgcprog_m) |
| releasem(mp) |
| } |
| ptrmask = (*uint8)(add(unsafe.Pointer(ptrmask), 1)) // skip the unroll flag byte |
| } else { |
| ptrmask = (*uint8)(unsafe.Pointer(typ.gc[0])) // pointer to unrolled mask |
| } |
| if size == 2*ptrSize { |
| *xbits = *ptrmask | bitBoundary |
| goto marked |
| } |
| te = uintptr(typ.size) / ptrSize |
| // If the type occupies odd number of words, its mask is repeated. |
| if te%2 == 0 { |
| te /= 2 |
| } |
| // Copy pointer bitmask into the bitmap. |
| for i := uintptr(0); i < size0; i += 2 * ptrSize { |
| v := *(*uint8)(add(unsafe.Pointer(ptrmask), ti)) |
| ti++ |
| if ti == te { |
| ti = 0 |
| } |
| if i == 0 { |
| v |= bitBoundary |
| } |
| if i+ptrSize == size0 { |
| v &^= uint8(bitPtrMask << 4) |
| } |
| |
| *xbits = v |
| xbits = (*byte)(add(unsafe.Pointer(xbits), ^uintptr(0))) |
| } |
| if size0%(2*ptrSize) == 0 && size0 < size { |
| // Mark the word after last object's word as bitsDead. |
| *xbits = bitsDead << 2 |
| } |
| } |
| marked: |
| if raceenabled { |
| racemalloc(x, size) |
| } |
| |
| if debugMalloc { |
| mp := acquirem() |
| if mp.mallocing == 0 { |
| gothrow("bad malloc") |
| } |
| mp.mallocing = 0 |
| if mp.curg != nil { |
| mp.curg.stackguard0 = mp.curg.stack.lo + _StackGuard |
| } |
| // Note: one releasem for the acquirem just above. |
| // The other for the acquirem at start of malloc. |
| releasem(mp) |
| releasem(mp) |
| } |
| |
| if debug.allocfreetrace != 0 { |
| tracealloc(x, size, typ) |
| } |
| |
| if rate := MemProfileRate; rate > 0 { |
| if size < uintptr(rate) && int32(size) < c.next_sample { |
| c.next_sample -= int32(size) |
| } else { |
| mp := acquirem() |
| profilealloc(mp, x, size) |
| releasem(mp) |
| } |
| } |
| |
| if memstats.heap_alloc >= memstats.next_gc { |
| gogc(0) |
| } |
| |
| return x |
| } |
| |
| // implementation of new builtin |
| func newobject(typ *_type) unsafe.Pointer { |
| flags := uint32(0) |
| if typ.kind&kindNoPointers != 0 { |
| flags |= flagNoScan |
| } |
| return mallocgc(uintptr(typ.size), typ, flags) |
| } |
| |
| // implementation of make builtin for slices |
| func newarray(typ *_type, n uintptr) unsafe.Pointer { |
| flags := uint32(0) |
| if typ.kind&kindNoPointers != 0 { |
| flags |= flagNoScan |
| } |
| if int(n) < 0 || (typ.size > 0 && n > maxmem/uintptr(typ.size)) { |
| panic("runtime: allocation size out of range") |
| } |
| return mallocgc(uintptr(typ.size)*n, typ, flags) |
| } |
| |
| // rawmem returns a chunk of pointerless memory. It is |
| // not zeroed. |
| func rawmem(size uintptr) unsafe.Pointer { |
| return mallocgc(size, nil, flagNoScan|flagNoZero) |
| } |
| |
| // round size up to next size class |
| func goroundupsize(size uintptr) uintptr { |
| if size < maxSmallSize { |
| if size <= 1024-8 { |
| return uintptr(class_to_size[size_to_class8[(size+7)>>3]]) |
| } |
| return uintptr(class_to_size[size_to_class128[(size-1024+127)>>7]]) |
| } |
| if size+pageSize < size { |
| return size |
| } |
| return (size + pageSize - 1) &^ pageMask |
| } |
| |
| func profilealloc(mp *m, x unsafe.Pointer, size uintptr) { |
| c := mp.mcache |
| rate := MemProfileRate |
| if size < uintptr(rate) { |
| // pick next profile time |
| // If you change this, also change allocmcache. |
| if rate > 0x3fffffff { // make 2*rate not overflow |
| rate = 0x3fffffff |
| } |
| next := int32(fastrand1()) % (2 * int32(rate)) |
| // Subtract the "remainder" of the current allocation. |
| // Otherwise objects that are close in size to sampling rate |
| // will be under-sampled, because we consistently discard this remainder. |
| next -= (int32(size) - c.next_sample) |
| if next < 0 { |
| next = 0 |
| } |
| c.next_sample = next |
| } |
| |
| mProf_Malloc(x, size) |
| } |
| |
| // force = 1 - do GC regardless of current heap usage |
| // force = 2 - go GC and eager sweep |
| func gogc(force int32) { |
| // The gc is turned off (via enablegc) until the bootstrap has completed. |
| // Also, malloc gets called in the guts of a number of libraries that might be |
| // holding locks. To avoid deadlocks during stoptheworld, don't bother |
| // trying to run gc while holding a lock. The next mallocgc without a lock |
| // will do the gc instead. |
| mp := acquirem() |
| if gp := getg(); gp == mp.g0 || mp.locks > 1 || !memstats.enablegc || panicking != 0 || gcpercent < 0 { |
| releasem(mp) |
| return |
| } |
| releasem(mp) |
| mp = nil |
| |
| semacquire(&worldsema, false) |
| |
| if force == 0 && memstats.heap_alloc < memstats.next_gc { |
| // typically threads which lost the race to grab |
| // worldsema exit here when gc is done. |
| semrelease(&worldsema) |
| return |
| } |
| |
| // Ok, we're doing it! Stop everybody else |
| startTime := nanotime() |
| mp = acquirem() |
| mp.gcing = 1 |
| releasem(mp) |
| onM(stoptheworld) |
| if mp != acquirem() { |
| gothrow("gogc: rescheduled") |
| } |
| |
| clearpools() |
| |
| // Run gc on the g0 stack. We do this so that the g stack |
| // we're currently running on will no longer change. Cuts |
| // the root set down a bit (g0 stacks are not scanned, and |
| // we don't need to scan gc's internal state). We also |
| // need to switch to g0 so we can shrink the stack. |
| n := 1 |
| if debug.gctrace > 1 { |
| n = 2 |
| } |
| for i := 0; i < n; i++ { |
| if i > 0 { |
| startTime = nanotime() |
| } |
| // switch to g0, call gc, then switch back |
| mp.scalararg[0] = uintptr(uint32(startTime)) // low 32 bits |
| mp.scalararg[1] = uintptr(startTime >> 32) // high 32 bits |
| if force >= 2 { |
| mp.scalararg[2] = 1 // eagersweep |
| } else { |
| mp.scalararg[2] = 0 |
| } |
| onM(gc_m) |
| } |
| |
| // all done |
| mp.gcing = 0 |
| semrelease(&worldsema) |
| onM(starttheworld) |
| releasem(mp) |
| mp = nil |
| |
| // now that gc is done, kick off finalizer thread if needed |
| if !concurrentSweep { |
| // give the queued finalizers, if any, a chance to run |
| Gosched() |
| } |
| } |
| |
| // GC runs a garbage collection. |
| func GC() { |
| gogc(2) |
| } |
| |
| // linker-provided |
| var noptrdata struct{} |
| var enoptrdata struct{} |
| var noptrbss struct{} |
| var enoptrbss struct{} |
| |
| // SetFinalizer sets the finalizer associated with x to f. |
| // When the garbage collector finds an unreachable block |
| // with an associated finalizer, it clears the association and runs |
| // f(x) in a separate goroutine. This makes x reachable again, but |
| // now without an associated finalizer. Assuming that SetFinalizer |
| // is not called again, the next time the garbage collector sees |
| // that x is unreachable, it will free x. |
| // |
| // SetFinalizer(x, nil) clears any finalizer associated with x. |
| // |
| // The argument x must be a pointer to an object allocated by |
| // calling new or by taking the address of a composite literal. |
| // The argument f must be a function that takes a single argument |
| // to which x's type can be assigned, and can have arbitrary ignored return |
| // values. If either of these is not true, SetFinalizer aborts the |
| // program. |
| // |
| // Finalizers are run in dependency order: if A points at B, both have |
| // finalizers, and they are otherwise unreachable, only the finalizer |
| // for A runs; once A is freed, the finalizer for B can run. |
| // If a cyclic structure includes a block with a finalizer, that |
| // cycle is not guaranteed to be garbage collected and the finalizer |
| // is not guaranteed to run, because there is no ordering that |
| // respects the dependencies. |
| // |
| // The finalizer for x is scheduled to run at some arbitrary time after |
| // x becomes unreachable. |
| // There is no guarantee that finalizers will run before a program exits, |
| // so typically they are useful only for releasing non-memory resources |
| // associated with an object during a long-running program. |
| // For example, an os.File object could use a finalizer to close the |
| // associated operating system file descriptor when a program discards |
| // an os.File without calling Close, but it would be a mistake |
| // to depend on a finalizer to flush an in-memory I/O buffer such as a |
| // bufio.Writer, because the buffer would not be flushed at program exit. |
| // |
| // It is not guaranteed that a finalizer will run if the size of *x is |
| // zero bytes. |
| // |
| // It is not guaranteed that a finalizer will run for objects allocated |
| // in initializers for package-level variables. Such objects may be |
| // linker-allocated, not heap-allocated. |
| // |
| // A single goroutine runs all finalizers for a program, sequentially. |
| // If a finalizer must run for a long time, it should do so by starting |
| // a new goroutine. |
| func SetFinalizer(obj interface{}, finalizer interface{}) { |
| e := (*eface)(unsafe.Pointer(&obj)) |
| etyp := e._type |
| if etyp == nil { |
| gothrow("runtime.SetFinalizer: first argument is nil") |
| } |
| if etyp.kind&kindMask != kindPtr { |
| gothrow("runtime.SetFinalizer: first argument is " + *etyp._string + ", not pointer") |
| } |
| ot := (*ptrtype)(unsafe.Pointer(etyp)) |
| if ot.elem == nil { |
| gothrow("nil elem type!") |
| } |
| |
| // find the containing object |
| _, base, _ := findObject(e.data) |
| |
| if base == nil { |
| // 0-length objects are okay. |
| if e.data == unsafe.Pointer(&zerobase) { |
| return |
| } |
| |
| // Global initializers might be linker-allocated. |
| // var Foo = &Object{} |
| // func main() { |
| // runtime.SetFinalizer(Foo, nil) |
| // } |
| // The relevant segments are: noptrdata, data, bss, noptrbss. |
| // We cannot assume they are in any order or even contiguous, |
| // due to external linking. |
| if uintptr(unsafe.Pointer(&noptrdata)) <= uintptr(e.data) && uintptr(e.data) < uintptr(unsafe.Pointer(&enoptrdata)) || |
| uintptr(unsafe.Pointer(&data)) <= uintptr(e.data) && uintptr(e.data) < uintptr(unsafe.Pointer(&edata)) || |
| uintptr(unsafe.Pointer(&bss)) <= uintptr(e.data) && uintptr(e.data) < uintptr(unsafe.Pointer(&ebss)) || |
| uintptr(unsafe.Pointer(&noptrbss)) <= uintptr(e.data) && uintptr(e.data) < uintptr(unsafe.Pointer(&enoptrbss)) { |
| return |
| } |
| gothrow("runtime.SetFinalizer: pointer not in allocated block") |
| } |
| |
| if e.data != base { |
| // As an implementation detail we allow to set finalizers for an inner byte |
| // of an object if it could come from tiny alloc (see mallocgc for details). |
| if ot.elem == nil || ot.elem.kind&kindNoPointers == 0 || ot.elem.size >= maxTinySize { |
| gothrow("runtime.SetFinalizer: pointer not at beginning of allocated block") |
| } |
| } |
| |
| f := (*eface)(unsafe.Pointer(&finalizer)) |
| ftyp := f._type |
| if ftyp == nil { |
| // switch to M stack and remove finalizer |
| mp := acquirem() |
| mp.ptrarg[0] = e.data |
| onM(removeFinalizer_m) |
| releasem(mp) |
| return |
| } |
| |
| if ftyp.kind&kindMask != kindFunc { |
| gothrow("runtime.SetFinalizer: second argument is " + *ftyp._string + ", not a function") |
| } |
| ft := (*functype)(unsafe.Pointer(ftyp)) |
| ins := *(*[]*_type)(unsafe.Pointer(&ft.in)) |
| if ft.dotdotdot || len(ins) != 1 { |
| gothrow("runtime.SetFinalizer: cannot pass " + *etyp._string + " to finalizer " + *ftyp._string) |
| } |
| fint := ins[0] |
| switch { |
| case fint == etyp: |
| // ok - same type |
| goto okarg |
| case fint.kind&kindMask == kindPtr: |
| if (fint.x == nil || fint.x.name == nil || etyp.x == nil || etyp.x.name == nil) && (*ptrtype)(unsafe.Pointer(fint)).elem == ot.elem { |
| // ok - not same type, but both pointers, |
| // one or the other is unnamed, and same element type, so assignable. |
| goto okarg |
| } |
| case fint.kind&kindMask == kindInterface: |
| ityp := (*interfacetype)(unsafe.Pointer(fint)) |
| if len(ityp.mhdr) == 0 { |
| // ok - satisfies empty interface |
| goto okarg |
| } |
| if _, ok := assertE2I2(ityp, obj); ok { |
| goto okarg |
| } |
| } |
| gothrow("runtime.SetFinalizer: cannot pass " + *etyp._string + " to finalizer " + *ftyp._string) |
| okarg: |
| // compute size needed for return parameters |
| nret := uintptr(0) |
| for _, t := range *(*[]*_type)(unsafe.Pointer(&ft.out)) { |
| nret = round(nret, uintptr(t.align)) + uintptr(t.size) |
| } |
| nret = round(nret, ptrSize) |
| |
| // make sure we have a finalizer goroutine |
| createfing() |
| |
| // switch to M stack to add finalizer record |
| mp := acquirem() |
| mp.ptrarg[0] = f.data |
| mp.ptrarg[1] = e.data |
| mp.scalararg[0] = nret |
| mp.ptrarg[2] = unsafe.Pointer(fint) |
| mp.ptrarg[3] = unsafe.Pointer(ot) |
| onM(setFinalizer_m) |
| if mp.scalararg[0] != 1 { |
| gothrow("runtime.SetFinalizer: finalizer already set") |
| } |
| releasem(mp) |
| } |
| |
| // round n up to a multiple of a. a must be a power of 2. |
| func round(n, a uintptr) uintptr { |
| return (n + a - 1) &^ (a - 1) |
| } |
| |
| // Look up pointer v in heap. Return the span containing the object, |
| // the start of the object, and the size of the object. If the object |
| // does not exist, return nil, nil, 0. |
| func findObject(v unsafe.Pointer) (s *mspan, x unsafe.Pointer, n uintptr) { |
| c := gomcache() |
| c.local_nlookup++ |
| if ptrSize == 4 && c.local_nlookup >= 1<<30 { |
| // purge cache stats to prevent overflow |
| lock(&mheap_.lock) |
| purgecachedstats(c) |
| unlock(&mheap_.lock) |
| } |
| |
| // find span |
| arena_start := uintptr(unsafe.Pointer(mheap_.arena_start)) |
| arena_used := uintptr(unsafe.Pointer(mheap_.arena_used)) |
| if uintptr(v) < arena_start || uintptr(v) >= arena_used { |
| return |
| } |
| p := uintptr(v) >> pageShift |
| q := p - arena_start>>pageShift |
| s = *(**mspan)(add(unsafe.Pointer(mheap_.spans), q*ptrSize)) |
| if s == nil { |
| return |
| } |
| x = unsafe.Pointer(uintptr(s.start) << pageShift) |
| |
| if uintptr(v) < uintptr(x) || uintptr(v) >= uintptr(unsafe.Pointer(s.limit)) || s.state != mSpanInUse { |
| s = nil |
| x = nil |
| return |
| } |
| |
| n = uintptr(s.elemsize) |
| if s.sizeclass != 0 { |
| x = add(x, (uintptr(v)-uintptr(x))/n*n) |
| } |
| return |
| } |
| |
| var fingCreate uint32 |
| |
| func createfing() { |
| // start the finalizer goroutine exactly once |
| if fingCreate == 0 && cas(&fingCreate, 0, 1) { |
| go runfinq() |
| } |
| } |
| |
| // This is the goroutine that runs all of the finalizers |
| func runfinq() { |
| var ( |
| frame unsafe.Pointer |
| framecap uintptr |
| ) |
| |
| for { |
| lock(&finlock) |
| fb := finq |
| finq = nil |
| if fb == nil { |
| gp := getg() |
| fing = gp |
| fingwait = true |
| gp.issystem = true |
| goparkunlock(&finlock, "finalizer wait") |
| gp.issystem = false |
| continue |
| } |
| unlock(&finlock) |
| if raceenabled { |
| racefingo() |
| } |
| for fb != nil { |
| for i := int32(0); i < fb.cnt; i++ { |
| f := (*finalizer)(add(unsafe.Pointer(&fb.fin), uintptr(i)*unsafe.Sizeof(finalizer{}))) |
| |
| framesz := unsafe.Sizeof((interface{})(nil)) + uintptr(f.nret) |
| if framecap < framesz { |
| // The frame does not contain pointers interesting for GC, |
| // all not yet finalized objects are stored in finq. |
| // If we do not mark it as FlagNoScan, |
| // the last finalized object is not collected. |
| frame = mallocgc(framesz, nil, flagNoScan) |
| framecap = framesz |
| } |
| |
| if f.fint == nil { |
| gothrow("missing type in runfinq") |
| } |
| switch f.fint.kind & kindMask { |
| case kindPtr: |
| // direct use of pointer |
| *(*unsafe.Pointer)(frame) = f.arg |
| case kindInterface: |
| ityp := (*interfacetype)(unsafe.Pointer(f.fint)) |
| // set up with empty interface |
| (*eface)(frame)._type = &f.ot.typ |
| (*eface)(frame).data = f.arg |
| if len(ityp.mhdr) != 0 { |
| // convert to interface with methods |
| // this conversion is guaranteed to succeed - we checked in SetFinalizer |
| *(*fInterface)(frame) = assertE2I(ityp, *(*interface{})(frame)) |
| } |
| default: |
| gothrow("bad kind in runfinq") |
| } |
| reflectcall(unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz)) |
| |
| // drop finalizer queue references to finalized object |
| f.fn = nil |
| f.arg = nil |
| f.ot = nil |
| } |
| fb.cnt = 0 |
| next := fb.next |
| lock(&finlock) |
| fb.next = finc |
| finc = fb |
| unlock(&finlock) |
| fb = next |
| } |
| } |
| } |
| |
| var persistent struct { |
| lock mutex |
| pos unsafe.Pointer |
| end unsafe.Pointer |
| } |
| |
| // Wrapper around sysAlloc that can allocate small chunks. |
| // There is no associated free operation. |
| // Intended for things like function/type/debug-related persistent data. |
| // If align is 0, uses default align (currently 8). |
| func persistentalloc(size, align uintptr, stat *uint64) unsafe.Pointer { |
| const ( |
| chunk = 256 << 10 |
| maxBlock = 64 << 10 // VM reservation granularity is 64K on windows |
| ) |
| |
| if align != 0 { |
| if align&(align-1) != 0 { |
| gothrow("persistentalloc: align is not a power of 2") |
| } |
| if align > _PageSize { |
| gothrow("persistentalloc: align is too large") |
| } |
| } else { |
| align = 8 |
| } |
| |
| if size >= maxBlock { |
| return sysAlloc(size, stat) |
| } |
| |
| lock(&persistent.lock) |
| persistent.pos = roundup(persistent.pos, align) |
| if uintptr(persistent.pos)+size > uintptr(persistent.end) { |
| persistent.pos = sysAlloc(chunk, &memstats.other_sys) |
| if persistent.pos == nil { |
| unlock(&persistent.lock) |
| gothrow("runtime: cannot allocate memory") |
| } |
| persistent.end = add(persistent.pos, chunk) |
| } |
| p := persistent.pos |
| persistent.pos = add(persistent.pos, size) |
| unlock(&persistent.lock) |
| |
| if stat != &memstats.other_sys { |
| xadd64(stat, int64(size)) |
| xadd64(&memstats.other_sys, -int64(size)) |
| } |
| return p |
| } |