Source file src/runtime/testdata/testprog/stw_mexit.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  package main
     6  
     7  import (
     8  	"runtime"
     9  )
    10  
    11  func init() {
    12  	register("mexitSTW", mexitSTW)
    13  }
    14  
    15  // Stress test for pp.oldm pointing to an exited M.
    16  //
    17  // If pp.oldm points to an exited M it should be ignored and another M used
    18  // instead. To stress:
    19  //
    20  // 1. Start and exit many threads (thus setting oldm on some P).
    21  // 2. Meanwhile, frequently stop the world.
    22  //
    23  // If procresize incorrect attempts to assign a P to an exited M, likely
    24  // failure modes are:
    25  //
    26  // 1. Crash in startTheWorldWithSema attempting to access the M, if it is nil.
    27  //
    28  // 2. Memory corruption elsewhere after startTheWorldWithSema writes to the M,
    29  // if it is not nil, but is freed and reused for another allocation.
    30  //
    31  // 3. Hang on a subsequent stop the world waiting for the P to stop, if the M
    32  // object is valid, but the M is exited, because startTheWorldWithSema didn't
    33  // actually wake anything to run the P. The P is _Pidle, but not in the pidle
    34  // list, thus startTheWorldWithSema will wake for it to actively stop.
    35  //
    36  // For this to go wrong, an exited M must fail to clear mp.self and must leave
    37  // the M on the sched.midle list.
    38  //
    39  // Similar to TraceSTW.
    40  func mexitSTW() {
    41  	// Ensure we have multiple Ps, but not too many, as we want the
    42  	// runnable goroutines likely to run on Ps with oldm set.
    43  	runtime.GOMAXPROCS(4)
    44  
    45  	// Background busy work so there is always something runnable.
    46  	for i := range 2 {
    47  		go traceSTWTarget(i)
    48  	}
    49  
    50  	// Wait for children to start running.
    51  	ping.Store(1)
    52  	for pong[0].Load() != 1 {}
    53  	for pong[1].Load() != 1 {}
    54  
    55  	for range 100 {
    56  		// Exit a thread. The last P to run this will have it in oldm.
    57  		go func() {
    58  			runtime.LockOSThread()
    59  		}()
    60  
    61  		// STW
    62  		var ms runtime.MemStats
    63  		runtime.ReadMemStats(&ms)
    64  	}
    65  
    66  	stop.Store(true)
    67  
    68  	println("OK")
    69  }
    70  

View as plain text