Source file src/runtime/testdata/testprogcgo/notingo.go

     1  // Copyright 2025 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 !plan9 && !windows
     6  
     7  package main
     8  
     9  /*
    10  #include <stdatomic.h>
    11  #include <stddef.h>
    12  #include <pthread.h>
    13  
    14  extern void Ready();
    15  
    16  static _Atomic int spinning;
    17  static _Atomic int released;
    18  
    19  static void* enterGoThenSpinTwice(void* arg __attribute__ ((unused))) {
    20  	Ready();
    21  	atomic_fetch_add(&spinning, 1);
    22  	while(atomic_load(&released) == 0) {};
    23  
    24  	Ready();
    25  	atomic_fetch_add(&spinning, 1);
    26  	while(1) {};
    27  	return NULL;
    28  }
    29  
    30  static void SpinTwiceInNewCThread() {
    31  	pthread_t tid;
    32  	pthread_create(&tid, NULL, enterGoThenSpinTwice, NULL);
    33  }
    34  
    35  static int Spinning() {
    36  	return atomic_load(&spinning);
    37  }
    38  
    39  static void Release() {
    40  	atomic_store(&spinning, 0);
    41  	atomic_store(&released, 1);
    42  }
    43  */
    44  import "C"
    45  
    46  import (
    47  	"os"
    48  	"runtime"
    49  	"runtime/metrics"
    50  )
    51  
    52  func init() {
    53  	register("NotInGoMetricCallback", NotInGoMetricCallback)
    54  }
    55  
    56  func NotInGoMetricCallback() {
    57  	const N = 10
    58  	s := []metrics.Sample{{Name: "/sched/goroutines/not-in-go:goroutines"}}
    59  
    60  	// Create N new C threads that have called into Go at least once.
    61  	for range N {
    62  		C.SpinTwiceInNewCThread()
    63  	}
    64  
    65  	// Synchronize with spinning threads twice.
    66  	//
    67  	// This helps catch bad accounting by taking at least a couple other
    68  	// codepaths which would cause the accounting to change.
    69  	for i := range 2 {
    70  		// Make sure they pass through Go.
    71  		// N.B. Ready is called twice by the new threads.
    72  		for j := range N {
    73  			<-readyCh
    74  			if j == 2 {
    75  				// Try to trigger an update in the immediate STW handoff case.
    76  				runtime.ReadMemStats(&m)
    77  			}
    78  		}
    79  
    80  		// Make sure they're back in C.
    81  		for C.Spinning() < N {
    82  		}
    83  
    84  		// Do something that stops the world to take all the Ps back.
    85  		runtime.ReadMemStats(&m)
    86  
    87  		if i == 0 {
    88  			C.Release()
    89  		}
    90  	}
    91  
    92  	// Read not-in-go.
    93  	metrics.Read(s)
    94  	if n := s[0].Value.Uint64(); n != 0 {
    95  		println("expected 0 not-in-go goroutines, found", n)
    96  		os.Exit(2)
    97  	}
    98  	println("OK")
    99  }
   100  
   101  var m runtime.MemStats
   102  var readyCh = make(chan bool)
   103  
   104  //export Ready
   105  func Ready() {
   106  	readyCh <- true
   107  }
   108  

View as plain text