diff options
Diffstat (limited to 'src/runtime/abi_test.go')
-rw-r--r-- | src/runtime/abi_test.go | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/src/runtime/abi_test.go b/src/runtime/abi_test.go new file mode 100644 index 0000000..d7039e7 --- /dev/null +++ b/src/runtime/abi_test.go @@ -0,0 +1,112 @@ +// Copyright 2021 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. + +//go:build goexperiment.regabiargs + +// This file contains tests specific to making sure the register ABI +// works in a bunch of contexts in the runtime. + +package runtime_test + +import ( + "internal/abi" + "internal/testenv" + "os" + "os/exec" + "runtime" + "strings" + "testing" + "time" +) + +var regConfirmRun chan int + +//go:registerparams +func regFinalizerPointer(v *Tint) (int, float32, [10]byte) { + regConfirmRun <- *(*int)(v) + return 5151, 4.0, [10]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} +} + +//go:registerparams +func regFinalizerIface(v Tinter) (int, float32, [10]byte) { + regConfirmRun <- *(*int)(v.(*Tint)) + return 5151, 4.0, [10]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} +} + +func TestFinalizerRegisterABI(t *testing.T) { + testenv.MustHaveExec(t) + + // Actually run the test in a subprocess because we don't want + // finalizers from other tests interfering. + if os.Getenv("TEST_FINALIZER_REGABI") != "1" { + cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=^TestFinalizerRegisterABI$", "-test.v")) + cmd.Env = append(cmd.Env, "TEST_FINALIZER_REGABI=1") + out, err := cmd.CombinedOutput() + if !strings.Contains(string(out), "PASS\n") || err != nil { + t.Fatalf("%s\n(exit status %v)", string(out), err) + } + return + } + + // Optimistically clear any latent finalizers from e.g. the testing + // package before continuing. + // + // It's possible that a finalizer only becomes available to run + // after this point, which would interfere with the test and could + // cause a crash, but because we're running in a separate process + // it's extremely unlikely. + runtime.GC() + runtime.GC() + + // fing will only pick the new IntRegArgs up if it's currently + // sleeping and wakes up, so wait for it to go to sleep. + success := false + for i := 0; i < 100; i++ { + if runtime.FinalizerGAsleep() { + success = true + break + } + time.Sleep(20 * time.Millisecond) + } + if !success { + t.Fatal("finalizer not asleep?") + } + + argRegsBefore := runtime.SetIntArgRegs(abi.IntArgRegs) + defer runtime.SetIntArgRegs(argRegsBefore) + + tests := []struct { + name string + fin any + confirmValue int + }{ + {"Pointer", regFinalizerPointer, -1}, + {"Interface", regFinalizerIface, -2}, + } + for i := range tests { + test := &tests[i] + t.Run(test.name, func(t *testing.T) { + regConfirmRun = make(chan int) + + x := new(Tint) + *x = (Tint)(test.confirmValue) + runtime.SetFinalizer(x, test.fin) + + runtime.KeepAlive(x) + + // Queue the finalizer. + runtime.GC() + runtime.GC() + + select { + case <-time.After(time.Second): + t.Fatal("finalizer failed to execute") + case gotVal := <-regConfirmRun: + if gotVal != test.confirmValue { + t.Fatalf("wrong finalizer executed? got %d, want %d", gotVal, test.confirmValue) + } + } + }) + } +} |