Source file src/runtime/abi_test.go

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build goexperiment.regabireflect
     6  
     7  // This file contains tests specific to making sure the register ABI
     8  // works in a bunch of contexts in the runtime.
     9  
    10  package runtime_test
    11  
    12  import (
    13  	"internal/abi"
    14  	"internal/testenv"
    15  	"os"
    16  	"os/exec"
    17  	"runtime"
    18  	"strings"
    19  	"testing"
    20  	"time"
    21  )
    22  
    23  var regConfirmRun chan int
    24  
    25  //go:registerparams
    26  func regFinalizerPointer(v *Tint) (int, float32, [10]byte) {
    27  	regConfirmRun <- *(*int)(v)
    28  	return 5151, 4.0, [10]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    29  }
    30  
    31  //go:registerparams
    32  func regFinalizerIface(v Tinter) (int, float32, [10]byte) {
    33  	regConfirmRun <- *(*int)(v.(*Tint))
    34  	return 5151, 4.0, [10]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    35  }
    36  
    37  func TestFinalizerRegisterABI(t *testing.T) {
    38  	testenv.MustHaveExec(t)
    39  
    40  	// Actually run the test in a subprocess because we don't want
    41  	// finalizers from other tests interfering.
    42  	if os.Getenv("TEST_FINALIZER_REGABI") != "1" {
    43  		cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestFinalizerRegisterABI", "-test.v"))
    44  		cmd.Env = append(cmd.Env, "TEST_FINALIZER_REGABI=1")
    45  		out, err := cmd.CombinedOutput()
    46  		if !strings.Contains(string(out), "PASS\n") || err != nil {
    47  			t.Fatalf("%s\n(exit status %v)", string(out), err)
    48  		}
    49  		return
    50  	}
    51  
    52  	// Optimistically clear any latent finalizers from e.g. the testing
    53  	// package before continuing.
    54  	//
    55  	// It's possible that a finalizer only becomes available to run
    56  	// after this point, which would interfere with the test and could
    57  	// cause a crash, but because we're running in a separate process
    58  	// it's extremely unlikely.
    59  	runtime.GC()
    60  	runtime.GC()
    61  
    62  	// fing will only pick the new IntRegArgs up if it's currently
    63  	// sleeping and wakes up, so wait for it to go to sleep.
    64  	success := false
    65  	for i := 0; i < 100; i++ {
    66  		if runtime.FinalizerGAsleep() {
    67  			success = true
    68  			break
    69  		}
    70  		time.Sleep(20 * time.Millisecond)
    71  	}
    72  	if !success {
    73  		t.Fatal("finalizer not asleep?")
    74  	}
    75  
    76  	argRegsBefore := runtime.SetIntArgRegs(abi.IntArgRegs)
    77  	defer runtime.SetIntArgRegs(argRegsBefore)
    78  
    79  	tests := []struct {
    80  		name         string
    81  		fin          any
    82  		confirmValue int
    83  	}{
    84  		{"Pointer", regFinalizerPointer, -1},
    85  		{"Interface", regFinalizerIface, -2},
    86  	}
    87  	for i := range tests {
    88  		test := &tests[i]
    89  		t.Run(test.name, func(t *testing.T) {
    90  			regConfirmRun = make(chan int)
    91  
    92  			x := new(Tint)
    93  			*x = (Tint)(test.confirmValue)
    94  			runtime.SetFinalizer(x, test.fin)
    95  
    96  			runtime.KeepAlive(x)
    97  
    98  			// Queue the finalizer.
    99  			runtime.GC()
   100  			runtime.GC()
   101  
   102  			select {
   103  			case <-time.After(time.Second):
   104  				t.Fatal("finalizer failed to execute")
   105  			case gotVal := <-regConfirmRun:
   106  				if gotVal != test.confirmValue {
   107  					t.Fatalf("wrong finalizer executed? got %d, want %d", gotVal, test.confirmValue)
   108  				}
   109  			}
   110  		})
   111  	}
   112  }
   113  

View as plain text