]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/runtime/mfinal.go
runtime: implement experiment to replace heap bitmap with alloc headers
[gostls13.git] / src / runtime / mfinal.go
index 3f9cd4ec746ec5d92707ab2833154da4cd1d7226..be501e6fcad4e2e6fe37e5306b7ba894cb6ce955 100644 (file)
@@ -9,6 +9,7 @@ package runtime
 import (
        "internal/abi"
        "internal/goarch"
+       "internal/goexperiment"
        "runtime/internal/atomic"
        "runtime/internal/sys"
        "unsafe"
@@ -190,7 +191,7 @@ func runfinq() {
                fb := finq
                finq = nil
                if fb == nil {
-                       gopark(finalizercommit, unsafe.Pointer(&finlock), waitReasonFinalizerWait, traceEvGoBlock, 1)
+                       gopark(finalizercommit, unsafe.Pointer(&finlock), waitReasonFinalizerWait, traceBlockSystemGoroutine, 1)
                        continue
                }
                argRegs = intArgRegs
@@ -274,6 +275,31 @@ func runfinq() {
        }
 }
 
+func isGoPointerWithoutSpan(p unsafe.Pointer) bool {
+       // 0-length objects are okay.
+       if p == unsafe.Pointer(&zerobase) {
+               return true
+       }
+
+       // 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.
+       for datap := &firstmoduledata; datap != nil; datap = datap.next {
+               if datap.noptrdata <= uintptr(p) && uintptr(p) < datap.enoptrdata ||
+                       datap.data <= uintptr(p) && uintptr(p) < datap.edata ||
+                       datap.bss <= uintptr(p) && uintptr(p) < datap.ebss ||
+                       datap.noptrbss <= uintptr(p) && uintptr(p) < datap.enoptrbss {
+                       return true
+               }
+       }
+       return false
+}
+
 // SetFinalizer sets the finalizer associated with obj to the provided
 // finalizer function. When the garbage collector finds an unreachable block
 // with an associated finalizer, it clears the association and runs
@@ -305,11 +331,11 @@ func runfinq() {
 // 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
+// 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.
+// [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 *obj is
 // zero bytes, because it may share same address with other zero-size
@@ -332,14 +358,14 @@ func runfinq() {
 // the object is reachable until it is no longer required.
 // Objects stored in global variables, or that can be found by tracing
 // pointers from a global variable, are reachable. For other objects,
-// pass the object to a call of the KeepAlive function to mark the
+// pass the object to a call of the [KeepAlive] function to mark the
 // last point in the function where the object must be reachable.
 //
 // For example, if p points to a struct, such as os.File, that contains
 // a file descriptor d, and p has a finalizer that closes that file
 // descriptor, and if the last use of p in a function is a call to
 // syscall.Write(p.d, buf, size), then p may be unreachable as soon as
-// the program enters syscall.Write. The finalizer may run at that moment,
+// the program enters [syscall.Write]. The finalizer may run at that moment,
 // closing p.d, causing syscall.Write to fail because it is writing to
 // a closed file descriptor (or, worse, to an entirely different
 // file descriptor opened by a different goroutine). To avoid this problem,
@@ -385,33 +411,20 @@ func SetFinalizer(obj any, finalizer any) {
        }
 
        // find the containing object
-       base, _, _ := findObject(uintptr(e.data), 0, 0)
+       base, span, _ := findObject(uintptr(e.data), 0, 0)
 
        if base == 0 {
-               // 0-length objects are okay.
-               if e.data == unsafe.Pointer(&zerobase) {
+               if isGoPointerWithoutSpan(e.data) {
                        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.
-               for datap := &firstmoduledata; datap != nil; datap = datap.next {
-                       if datap.noptrdata <= uintptr(e.data) && uintptr(e.data) < datap.enoptrdata ||
-                               datap.data <= uintptr(e.data) && uintptr(e.data) < datap.edata ||
-                               datap.bss <= uintptr(e.data) && uintptr(e.data) < datap.ebss ||
-                               datap.noptrbss <= uintptr(e.data) && uintptr(e.data) < datap.enoptrbss {
-                               return
-                       }
-               }
                throw("runtime.SetFinalizer: pointer not in allocated block")
        }
 
+       // Move base forward if we've got an allocation header.
+       if goexperiment.AllocHeaders && !span.spanclass.noscan() && !heapBitsInSpan(span.elemsize) && span.spanclass.sizeclass() != 0 {
+               base += mallocHeaderSize
+       }
+
        if uintptr(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).
@@ -457,7 +470,7 @@ func SetFinalizer(obj any, finalizer any) {
                        // ok - satisfies empty interface
                        goto okarg
                }
-               if iface := assertE2I2(ityp, *efaceOf(&obj)); iface.tab != nil {
+               if itab := assertE2I2(ityp, efaceOf(&obj)._type); itab != nil {
                        goto okarg
                }
        }
@@ -466,7 +479,7 @@ okarg:
        // compute size needed for return parameters
        nret := uintptr(0)
        for _, t := range ft.OutSlice() {
-               nret = alignUp(nret, uintptr(t.Align_)) + uintptr(t.Size_)
+               nret = alignUp(nret, uintptr(t.Align_)) + t.Size_
        }
        nret = alignUp(nret, goarch.PtrSize)
 
@@ -502,11 +515,11 @@ okarg:
 //     // No more uses of p after this point.
 //
 // Without the KeepAlive call, the finalizer could run at the start of
-// syscall.Read, closing the file descriptor before syscall.Read makes
+// [syscall.Read], closing the file descriptor before syscall.Read makes
 // the actual system call.
 //
 // Note: KeepAlive should only be used to prevent finalizers from
-// running prematurely. In particular, when used with unsafe.Pointer,
+// running prematurely. In particular, when used with [unsafe.Pointer],
 // the rules for valid uses of unsafe.Pointer still apply.
 func KeepAlive(x any) {
        // Introduce a use of x that the compiler can't eliminate.