aboutsummaryrefslogtreecommitdiff
path: root/src/internal/weak/pointer_test.go
diff options
context:
space:
mode:
authorMichael Anthony Knyszek <mknyszek@google.com>2024-04-04 04:50:13 +0000
committerGopher Robot <gobot@golang.org>2024-04-18 21:25:11 +0000
commitdfc86e922cd033155339c22aff64e109f6c8cc89 (patch)
treede0ed468258f04af1eaa6b60c16a13dd4698347e /src/internal/weak/pointer_test.go
parentfa470f6245191b3c2f0b715194edf7cdf951af48 (diff)
downloadgo-dfc86e922cd033155339c22aff64e109f6c8cc89.tar.xz
internal/weak: add package implementing weak pointers
This change adds the internal/weak package, which exposes GC-supported weak pointers to the standard library. This is for the upcoming weak package, but may be useful for other future constructs. For #62483. Change-Id: I4aa8fa9400110ad5ea022a43c094051699ccab9d Reviewed-on: https://go-review.googlesource.com/c/go/+/576297 Auto-Submit: Michael Knyszek <mknyszek@google.com> Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/internal/weak/pointer_test.go')
-rw-r--r--src/internal/weak/pointer_test.go130
1 files changed, 130 insertions, 0 deletions
diff --git a/src/internal/weak/pointer_test.go b/src/internal/weak/pointer_test.go
new file mode 100644
index 0000000000..e143749230
--- /dev/null
+++ b/src/internal/weak/pointer_test.go
@@ -0,0 +1,130 @@
+// 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_test
+
+import (
+ "internal/weak"
+ "runtime"
+ "testing"
+)
+
+type T struct {
+ // N.B. This must contain a pointer, otherwise the weak handle might get placed
+ // in a tiny block making the tests in this package flaky.
+ t *T
+ a int
+}
+
+func TestPointer(t *testing.T) {
+ bt := new(T)
+ wt := weak.Make(bt)
+ if st := wt.Strong(); 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 {
+ 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 {
+ t.Fatalf("expected weak pointer to be nil, got %p", st)
+ }
+}
+
+func TestPointerEquality(t *testing.T) {
+ bt := make([]*T, 10)
+ wt := make([]weak.Pointer[T], 10)
+ for i := range bt {
+ bt[i] = new(T)
+ wt[i] = weak.Make(bt[i])
+ }
+ for i := range bt {
+ st := wt[i].Strong()
+ if st != bt[i] {
+ t.Fatalf("weak pointer is not the same as strong pointer: %p vs. %p", st, bt[i])
+ }
+ if wp := weak.Make(st); wp != wt[i] {
+ t.Fatalf("new weak pointer not equal to existing weak pointer: %v vs. %v", wp, wt[i])
+ }
+ if i == 0 {
+ continue
+ }
+ if wt[i] == wt[i-1] {
+ t.Fatalf("expected weak pointers to not be equal to each other, but got %v", wt[i])
+ }
+ }
+ // bt is still referenced.
+ runtime.GC()
+ for i := range bt {
+ st := wt[i].Strong()
+ if st != bt[i] {
+ t.Fatalf("weak pointer is not the same as strong pointer: %p vs. %p", st, bt[i])
+ }
+ if wp := weak.Make(st); wp != wt[i] {
+ t.Fatalf("new weak pointer not equal to existing weak pointer: %v vs. %v", wp, wt[i])
+ }
+ if i == 0 {
+ continue
+ }
+ if wt[i] == wt[i-1] {
+ t.Fatalf("expected weak pointers to not be equal to each other, but got %v", wt[i])
+ }
+ }
+ bt = nil
+ // bt is no longer referenced.
+ runtime.GC()
+ for i := range bt {
+ st := wt[i].Strong()
+ if st != nil {
+ t.Fatalf("expected weak pointer to be nil, got %p", st)
+ }
+ if i == 0 {
+ continue
+ }
+ if wt[i] == wt[i-1] {
+ t.Fatalf("expected weak pointers to not be equal to each other, but got %v", wt[i])
+ }
+ }
+}
+
+func TestPointerFinalizer(t *testing.T) {
+ bt := new(T)
+ wt := weak.Make(bt)
+ done := make(chan struct{}, 1)
+ runtime.SetFinalizer(bt, func(bt *T) {
+ if wt.Strong() != 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 {
+ t.Errorf("weak pointer went nil too soon")
+ }
+ runtime.KeepAlive(bt)
+
+ // bt is no longer referenced.
+ //
+ // Run one cycle to queue the finalizer.
+ runtime.GC()
+ if wt.Strong() != nil {
+ t.Errorf("weak pointer did not go nil when finalizer was enqueued")
+ }
+
+ // Wait for the finalizer to run.
+ <-done
+
+ // The weak pointer should still be nil after the finalizer runs.
+ runtime.GC()
+ if wt.Strong() != nil {
+ t.Errorf("weak pointer is non-nil even after finalization: %v", wt)
+ }
+}