lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/tracing/internal/xruntime/runtime_test.go (about)

     1  // Copyright (C) 2017  Nexedi SA and Contributors.
     2  //                     Kirill Smelkov <kirr@nexedi.com>
     3  //
     4  // This program is free software: you can Use, Study, Modify and Redistribute
     5  // it under the terms of the GNU General Public License version 3, or (at your
     6  // option) any later version, as published by the Free Software Foundation.
     7  //
     8  // You can also Link and Combine this program with other software covered by
     9  // the terms of any of the Free Software licenses or any of the Open Source
    10  // Initiative approved licenses and Convey the resulting work. Corresponding
    11  // source of such a combination shall include the source code for all other
    12  // software used.
    13  //
    14  // This program is distributed WITHOUT ANY WARRANTY; without even the implied
    15  // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    16  //
    17  // See COPYING file for full licensing terms.
    18  // See https://www.nexedi.com/licensing for rationale and options.
    19  
    20  package xruntime
    21  
    22  import (
    23  	"runtime"
    24  	"sync/atomic"
    25  	"testing"
    26  	"time"
    27  )
    28  
    29  func TestStartStopTheWorld(t *testing.T) {
    30  	var x, stop int32
    31  	ready := make(chan int)
    32  
    33  	// g2
    34  	go func() {
    35  		// make sure the thread running this goroutine is different from thread for main g.
    36  		// this way we can be sure there are 2 OS threads in action and communicating via busywait should work.
    37  		runtime.LockOSThread()
    38  		ready <- 0
    39  
    40  		for atomic.LoadInt32(&stop) == 0 {
    41  			atomic.AddInt32(&x, 1)
    42  
    43  			// XXX as of go19 tight loops are not preemptible (golang.org/issues/10958)
    44  			//     -> explicitly make sure we do not miss STW request.
    45  			runtime.Gosched()
    46  		}
    47  	}()
    48  
    49  
    50  	// wait for spawned goroutine to jump into its own thread
    51  	<-ready
    52  
    53  	// verify g and g2 are indeed running in parallel
    54  	check_g_g2_running := func(bad string) {
    55  		xprev := atomic.LoadInt32(&x)
    56  		xnext := xprev
    57  		nδ := 0
    58  		tstart := time.Now()
    59  		for nδ < 100 && time.Now().Sub(tstart) < time.Second {
    60  			xnext = atomic.LoadInt32(&x)
    61  			if xnext != xprev {
    62  				nδ += 1
    63  				xprev = xnext
    64  			}
    65  		}
    66  
    67  		if nδ == 0 {
    68  			t.Fatal(bad)
    69  		}
    70  	}
    71  
    72  	check_g_g2_running("g and g2 are not running in parallel")
    73  
    74  	// now stop the world and for 1s make sure g2 is not running in parallel with us
    75  	StopTheWorld("just for my reason")
    76  
    77  	xprev := atomic.LoadInt32(&x)
    78  	xnext := xprev
    79  	nδ := 0
    80  	tstart := time.Now()
    81  	for time.Now().Sub(tstart) < time.Second {
    82  		for i := 0; i < 100; i++ {
    83  			xnext = atomic.LoadInt32(&x)
    84  			if xnext != xprev {
    85  				nδ += 1
    86  				xprev = xnext
    87  			}
    88  		}
    89  	}
    90  
    91  	StartTheWorld()
    92  
    93  	if nδ != 0 {
    94  		t.Fatalf("g2 modified x at least %d times while the world was stopped", nδ)
    95  	}
    96  
    97  	// make sure g2 is now running again
    98  	check_g_g2_running("g2 did not restarted after StartTheWorld")
    99  
   100  	atomic.StoreInt32(&stop, 1)
   101  }