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 }