--- /dev/null
+pkg weak, func Make[$0 interface{}](*$0) Pointer[$0] #67552
+pkg weak, method (Pointer[$0]) Value() *$0 #67552
+pkg weak, type Pointer[$0 interface{}] struct #67552
--- /dev/null
+### New weak package
+
+The new [weak](/pkg/weak) package provides weak pointers.
+
+Weak pointers are a low-level primitive provided to enable the
+creation of memory-efficient structures, such as weak maps for
+associating values, canonicalization maps for anything not
+covered by package [unique](/pkg/unique), and various kinds
+of caches.
+For supporting these use-cases, this release also provides
+[runtime.AddCleanup](/pkg/runtime#AddCleanup) and
+[maphash.Comparable](/pkg/maphash#Comparable).
--- /dev/null
+<!-- This is a new package; covered in 6-stdlib/1-weak.md. -->
// coroutines
"runtime.coroswitch": {"iter"},
"runtime.newcoro": {"iter"},
- // weak references
- "internal/weak.runtime_registerWeakPointer": {"internal/weak"},
- "internal/weak.runtime_makeStrongFromWeak": {"internal/weak"},
// fips info
"go:fipsinfo": {"crypto/internal/fips/check"},
}
< internal/runtime/maps
< runtime
< sync/atomic
- < internal/weak
< internal/sync
+ < weak
< sync
< internal/bisect
< internal/godebug
"unicode",
"unique",
"unsafe",
+ "weak",
}
+++ /dev/null
-// Copyright 2024 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.
-
-/*
-The weak package is a package for managing weak pointers.
-
-Weak pointers are pointers that explicitly do not keep a value live and
-must be queried for a regular Go pointer.
-The result of such a query may be observed as nil at any point after a
-weakly-pointed-to object becomes eligible for reclamation by the garbage
-collector.
-More specifically, weak pointers become nil as soon as the garbage collector
-identifies that the object is unreachable, before it is made reachable
-again by a finalizer.
-In terms of the C# language, these semantics are roughly equivalent to the
-the semantics of "short" weak references.
-In terms of the Java language, these semantics are roughly equivalent to the
-semantics of the WeakReference type.
-
-Using go:linkname to access this package and the functions it references
-is explicitly forbidden by the toolchain because the semantics of this
-package have not gone through the proposal process. By exposing this
-functionality, we risk locking in the existing semantics due to Hyrum's Law.
-
-If you believe you have a good use-case for weak references not already
-covered by the standard library, file a proposal issue at
-https://github.com/golang/go/issues instead of relying on this package.
-*/
-package weak
-
-import (
- "internal/abi"
- "runtime"
- "unsafe"
-)
-
-// Pointer is a weak pointer to a value of type T.
-//
-// This value is comparable is guaranteed to compare equal if the pointers
-// that they were created from compare equal. This property is retained even
-// after the object referenced by the pointer used to create a weak reference
-// is reclaimed.
-//
-// If multiple weak pointers are made to different offsets within same object
-// (for example, pointers to different fields of the same struct), those pointers
-// will not compare equal.
-// If a weak pointer is created from an object that becomes reachable again due
-// to a finalizer, that weak pointer will not compare equal with weak pointers
-// created before it became unreachable.
-type Pointer[T any] struct {
- u unsafe.Pointer
-}
-
-// Make creates a weak pointer from a strong pointer to some value of type T.
-func Make[T any](ptr *T) Pointer[T] {
- // Explicitly force ptr to escape to the heap.
- ptr = abi.Escape(ptr)
-
- var u unsafe.Pointer
- if ptr != nil {
- u = runtime_registerWeakPointer(unsafe.Pointer(ptr))
- }
- runtime.KeepAlive(ptr)
- return Pointer[T]{u}
-}
-
-// Strong creates a strong pointer from the weak pointer.
-// Returns nil if the original value for the weak pointer was reclaimed by
-// the garbage collector.
-// If a weak pointer points to an object with a finalizer, then Strong will
-// return nil as soon as the object's finalizer is queued for execution.
-func (p Pointer[T]) Strong() *T {
- return (*T)(runtime_makeStrongFromWeak(p.u))
-}
-
-// Implemented in runtime.
-
-//go:linkname runtime_registerWeakPointer
-func runtime_registerWeakPointer(unsafe.Pointer) unsafe.Pointer
-
-//go:linkname runtime_makeStrongFromWeak
-func runtime_makeStrongFromWeak(unsafe.Pointer) unsafe.Pointer
"fmt"
"internal/asan"
"internal/testenv"
- "internal/weak"
"math/bits"
"math/rand"
"os"
"testing"
"time"
"unsafe"
+ "weak"
)
func TestGcSys(t *testing.T) {
// Start a GC, and wait a little bit to get something spinning in mark termination.
// Simultaneously, fire off another goroutine to disable spinning. If everything's
- // working correctly, then weak.Strong will block, so we need to make sure something
+ // working correctly, then weak.Value will block, so we need to make sure something
// prevents the GC from continuing to spin.
done := make(chan struct{})
go func() {
wg.Add(1)
go func() {
defer wg.Done()
- wp.Strong()
+ wp.Value()
}()
}
}
if hasFinAndRevived {
// Pass 2: queue all finalizers and clear any weak handles. Weak handles are cleared
- // before finalization as specified by the internal/weak package. See the documentation
+ // before finalization as specified by the weak package. See the documentation
// for that package for more details.
for siter.valid() && uintptr(siter.s.offset) < endOffset {
// Find the exact byte for which the special was setup
handle *atomic.Uintptr
}
-//go:linkname internal_weak_runtime_registerWeakPointer internal/weak.runtime_registerWeakPointer
+//go:linkname internal_weak_runtime_registerWeakPointer weak.runtime_registerWeakPointer
func internal_weak_runtime_registerWeakPointer(p unsafe.Pointer) unsafe.Pointer {
return unsafe.Pointer(getOrAddWeakHandle(unsafe.Pointer(p)))
}
-//go:linkname internal_weak_runtime_makeStrongFromWeak internal/weak.runtime_makeStrongFromWeak
+//go:linkname internal_weak_runtime_makeStrongFromWeak weak.runtime_makeStrongFromWeak
func internal_weak_runtime_makeStrongFromWeak(u unsafe.Pointer) unsafe.Pointer {
handle := (*atomic.Uintptr)(u)
import (
"internal/abi"
isync "internal/sync"
- "internal/weak"
"runtime"
"sync"
"unsafe"
+ "weak"
)
var zero uintptr
}
// Now that we're sure there's a value in the map, let's
// try to get the pointer we need out of it.
- ptr = wp.Strong()
+ ptr = wp.Value()
if ptr != nil {
break
}
// Delete all the entries whose weak references are nil and clean up
// deleted entries.
m.All()(func(key T, wp weak.Pointer[T]) bool {
- if wp.Strong() == nil {
+ if wp.Value() == nil {
m.CompareAndDelete(key, wp)
}
return true
if !ok {
return
}
- if wp.Strong() != nil {
+ if wp.Value() != nil {
t.Errorf("value %v still referenced a handle (or tiny block?) ", value)
return
}
--- /dev/null
+// Copyright 2024 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 weak provides weak pointers with the goal of memory efficiency.
+The primary use-cases for weak pointers are for implementing caches,
+canonicalization maps (like the unique package), and for tying together
+the lifetimes of separate values.
+
+## Advice
+
+This package is intended to target niche use-cases like the unique
+package, not as a general replacement for regular Go pointers, maps,
+etc.
+Misuse of the structures in this package will generate unexpected and
+hard-to-reproduce bugs.
+Using the facilities in this package to try and resolve out-of-memory
+issues and/or memory leaks is very likely the wrong answer.
+
+The structures in this package are intended to be an implementation
+detail of the package they are used by (again, see the unique package).
+Avoid exposing weak structures across API boundaries, since that exposes
+users of your package to the subtleties of this package.
+*/
+package weak
--- /dev/null
+// Copyright 2024 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 weak
+
+import (
+ "internal/abi"
+ "runtime"
+ "unsafe"
+)
+
+// Pointer is a weak pointer to a value of type T.
+//
+// Two Pointer values compare equal if the pointers
+// that they were created from compare equal. This property is retained even
+// after the object referenced by the pointer used to create a weak reference
+// is reclaimed.
+//
+// If multiple weak pointers are made to different offsets within same object
+// (for example, pointers to different fields of the same struct), those pointers
+// will not compare equal.
+// If a weak pointer is created from an object that becomes unreachable, but is
+// then resurrected due to a finalizer, that weak pointer will not compare equal
+// with weak pointers created after resurrection.
+//
+// Calling Make with a nil pointer returns a weak pointer whose Value method
+// always returns nil. The zero value of a Pointer behaves as if it was created
+// by passing nil to Make and compares equal with such pointers.
+type Pointer[T any] struct {
+ u unsafe.Pointer
+}
+
+// Make creates a weak pointer from a strong pointer to some value of type T.
+func Make[T any](ptr *T) Pointer[T] {
+ // Explicitly force ptr to escape to the heap.
+ ptr = abi.Escape(ptr)
+
+ var u unsafe.Pointer
+ if ptr != nil {
+ u = runtime_registerWeakPointer(unsafe.Pointer(ptr))
+ }
+ runtime.KeepAlive(ptr)
+ return Pointer[T]{u}
+}
+
+// Value returns the original pointer used to create the weak pointer.
+// It returns nil if the value pointed to by the original pointer was reclaimed by
+// the garbage collector.
+// If a weak pointer points to an object with a finalizer, then Value will
+// return nil as soon as the object's finalizer is queued for execution.
+func (p Pointer[T]) Value() *T {
+ return (*T)(runtime_makeStrongFromWeak(p.u))
+}
+
+// Implemented in runtime.
+
+//go:linkname runtime_registerWeakPointer
+func runtime_registerWeakPointer(unsafe.Pointer) unsafe.Pointer
+
+//go:linkname runtime_makeStrongFromWeak
+func runtime_makeStrongFromWeak(unsafe.Pointer) unsafe.Pointer
import (
"context"
- "internal/weak"
"runtime"
"sync"
"testing"
"time"
+ "weak"
)
type T struct {
func TestPointer(t *testing.T) {
bt := new(T)
wt := weak.Make(bt)
- if st := wt.Strong(); st != bt {
+ if st := wt.Value(); st != bt {
t.Fatalf("weak pointer is not the same as strong pointer: %p vs. %p", st, bt)
}
// bt is still referenced.
runtime.GC()
- if st := wt.Strong(); st != bt {
+ if st := wt.Value(); st != bt {
t.Fatalf("weak pointer is not the same as strong pointer after GC: %p vs. %p", st, bt)
}
// bt is no longer referenced.
runtime.GC()
- if st := wt.Strong(); st != nil {
+ if st := wt.Value(); st != nil {
t.Fatalf("expected weak pointer to be nil, got %p", st)
}
}
wt[i] = weak.Make(bt[i])
}
for i := range bt {
- st := wt[i].Strong()
+ st := wt[i].Value()
if st != bt[i] {
t.Fatalf("weak pointer is not the same as strong pointer: %p vs. %p", st, bt[i])
}
// bt is still referenced.
runtime.GC()
for i := range bt {
- st := wt[i].Strong()
+ st := wt[i].Value()
if st != bt[i] {
t.Fatalf("weak pointer is not the same as strong pointer: %p vs. %p", st, bt[i])
}
// bt is no longer referenced.
runtime.GC()
for i := range bt {
- st := wt[i].Strong()
+ st := wt[i].Value()
if st != nil {
t.Fatalf("expected weak pointer to be nil, got %p", st)
}
wt := weak.Make(bt)
done := make(chan struct{}, 1)
runtime.SetFinalizer(bt, func(bt *T) {
- if wt.Strong() != nil {
+ if wt.Value() != nil {
t.Errorf("weak pointer did not go nil before finalizer ran")
}
done <- struct{}{}
// Make sure the weak pointer stays around while bt is live.
runtime.GC()
- if wt.Strong() == nil {
+ if wt.Value() == nil {
t.Errorf("weak pointer went nil too soon")
}
runtime.KeepAlive(bt)
//
// Run one cycle to queue the finalizer.
runtime.GC()
- if wt.Strong() != nil {
+ if wt.Value() != nil {
t.Errorf("weak pointer did not go nil when finalizer was enqueued")
}
// The weak pointer should still be nil after the finalizer runs.
runtime.GC()
- if wt.Strong() != nil {
+ if wt.Value() != nil {
t.Errorf("weak pointer is non-nil even after finalization: %v", wt)
}
}
// bug happens. Specifically, we want:
//
// 1. To create a whole bunch of objects that are only weakly-pointed-to,
- // 2. To call Strong while the GC is in the mark phase,
+ // 2. To call Value while the GC is in the mark phase,
// 3. The new strong pointer to be missed by the GC,
// 4. The following GC cycle to mark a free object.
//
wt := weak.Make(bt)
bt = nil
time.Sleep(1 * time.Millisecond)
- bt = wt.Strong()
+ bt = wt.Value()
if bt != nil {
time.Sleep(4 * time.Millisecond)
bt.t = bt