Source file src/internal/synctest/synctest_test.go

     1  // Copyright 2024 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 synctest_test
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"internal/synctest"
    11  	"internal/testenv"
    12  	"iter"
    13  	"os"
    14  	"reflect"
    15  	"runtime"
    16  	"slices"
    17  	"strconv"
    18  	"strings"
    19  	"sync"
    20  	"sync/atomic"
    21  	"testing"
    22  	"time"
    23  	"weak"
    24  )
    25  
    26  func TestNow(t *testing.T) {
    27  	start := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).In(time.Local)
    28  	synctest.Run(func() {
    29  		// Time starts at 2000-1-1 00:00:00.
    30  		if got, want := time.Now(), start; !got.Equal(want) {
    31  			t.Errorf("at start: time.Now = %v, want %v", got, want)
    32  		}
    33  		go func() {
    34  			// New goroutines see the same fake clock.
    35  			if got, want := time.Now(), start; !got.Equal(want) {
    36  				t.Errorf("time.Now = %v, want %v", got, want)
    37  			}
    38  		}()
    39  		// Time advances after a sleep.
    40  		time.Sleep(1 * time.Second)
    41  		if got, want := time.Now(), start.Add(1*time.Second); !got.Equal(want) {
    42  			t.Errorf("after sleep: time.Now = %v, want %v", got, want)
    43  		}
    44  	})
    45  }
    46  
    47  // TestMonotonicClock exercises comparing times from within a bubble
    48  // with ones from outside the bubble.
    49  func TestMonotonicClock(t *testing.T) {
    50  	start := time.Now()
    51  	synctest.Run(func() {
    52  		time.Sleep(time.Until(start.Round(0)))
    53  		if got, want := time.Now().In(time.UTC), start.In(time.UTC); !got.Equal(want) {
    54  			t.Fatalf("time.Now() = %v, want %v", got, want)
    55  		}
    56  
    57  		wait := 1 * time.Second
    58  		time.Sleep(wait)
    59  		if got := time.Since(start); got != wait {
    60  			t.Fatalf("time.Since(start) = %v, want %v", got, wait)
    61  		}
    62  		if got := time.Now().Sub(start); got != wait {
    63  			t.Fatalf("time.Now().Sub(start) = %v, want %v", got, wait)
    64  		}
    65  	})
    66  }
    67  
    68  func TestRunEmpty(t *testing.T) {
    69  	synctest.Run(func() {
    70  	})
    71  }
    72  
    73  func TestSimpleWait(t *testing.T) {
    74  	synctest.Run(func() {
    75  		synctest.Wait()
    76  	})
    77  }
    78  
    79  func TestGoroutineWait(t *testing.T) {
    80  	synctest.Run(func() {
    81  		go func() {}()
    82  		synctest.Wait()
    83  	})
    84  }
    85  
    86  // TestWait starts a collection of goroutines.
    87  // It checks that synctest.Wait waits for all goroutines to exit before returning.
    88  func TestWait(t *testing.T) {
    89  	synctest.Run(func() {
    90  		done := false
    91  		ch := make(chan int)
    92  		var f func()
    93  		f = func() {
    94  			count := <-ch
    95  			if count == 0 {
    96  				done = true
    97  			} else {
    98  				go f()
    99  				ch <- count - 1
   100  			}
   101  		}
   102  		go f()
   103  		ch <- 100
   104  		synctest.Wait()
   105  		if !done {
   106  			t.Fatalf("done = false, want true")
   107  		}
   108  	})
   109  }
   110  
   111  func TestMallocs(t *testing.T) {
   112  	for i := 0; i < 100; i++ {
   113  		synctest.Run(func() {
   114  			done := false
   115  			ch := make(chan []byte)
   116  			var f func()
   117  			f = func() {
   118  				b := <-ch
   119  				if len(b) == 0 {
   120  					done = true
   121  				} else {
   122  					go f()
   123  					ch <- make([]byte, len(b)-1)
   124  				}
   125  			}
   126  			go f()
   127  			ch <- make([]byte, 100)
   128  			synctest.Wait()
   129  			if !done {
   130  				t.Fatalf("done = false, want true")
   131  			}
   132  		})
   133  	}
   134  }
   135  
   136  func TestTimerReadBeforeDeadline(t *testing.T) {
   137  	synctest.Run(func() {
   138  		start := time.Now()
   139  		tm := time.NewTimer(5 * time.Second)
   140  		<-tm.C
   141  		if got, want := time.Since(start), 5*time.Second; got != want {
   142  			t.Errorf("after sleep: time.Since(start) = %v, want %v", got, want)
   143  		}
   144  	})
   145  }
   146  
   147  func TestTimerReadAfterDeadline(t *testing.T) {
   148  	synctest.Run(func() {
   149  		delay := 1 * time.Second
   150  		want := time.Now().Add(delay)
   151  		tm := time.NewTimer(delay)
   152  		time.Sleep(2 * delay)
   153  		got := <-tm.C
   154  		if got != want {
   155  			t.Errorf("<-tm.C = %v, want %v", got, want)
   156  		}
   157  	})
   158  }
   159  
   160  func TestTimerReset(t *testing.T) {
   161  	synctest.Run(func() {
   162  		start := time.Now()
   163  		tm := time.NewTimer(1 * time.Second)
   164  		if got, want := <-tm.C, start.Add(1*time.Second); got != want {
   165  			t.Errorf("first sleep: <-tm.C = %v, want %v", got, want)
   166  		}
   167  
   168  		tm.Reset(2 * time.Second)
   169  		if got, want := <-tm.C, start.Add((1+2)*time.Second); got != want {
   170  			t.Errorf("second sleep: <-tm.C = %v, want %v", got, want)
   171  		}
   172  
   173  		tm.Reset(3 * time.Second)
   174  		time.Sleep(1 * time.Second)
   175  		tm.Reset(3 * time.Second)
   176  		if got, want := <-tm.C, start.Add((1+2+4)*time.Second); got != want {
   177  			t.Errorf("third sleep: <-tm.C = %v, want %v", got, want)
   178  		}
   179  	})
   180  }
   181  
   182  func TestTimeAfter(t *testing.T) {
   183  	synctest.Run(func() {
   184  		i := 0
   185  		time.AfterFunc(1*time.Second, func() {
   186  			// Ensure synctest group membership propagates through the AfterFunc.
   187  			i++ // 1
   188  			go func() {
   189  				time.Sleep(1 * time.Second)
   190  				i++ // 2
   191  			}()
   192  		})
   193  		time.Sleep(3 * time.Second)
   194  		synctest.Wait()
   195  		if got, want := i, 2; got != want {
   196  			t.Errorf("after sleep and wait: i = %v, want %v", got, want)
   197  		}
   198  	})
   199  }
   200  
   201  func TestTimerAfterBubbleExit(t *testing.T) {
   202  	run := false
   203  	synctest.Run(func() {
   204  		time.AfterFunc(1*time.Second, func() {
   205  			run = true
   206  		})
   207  	})
   208  	if run {
   209  		t.Errorf("timer ran before bubble exit")
   210  	}
   211  }
   212  
   213  func TestTimerFromOutsideBubble(t *testing.T) {
   214  	tm := time.NewTimer(10 * time.Millisecond)
   215  	synctest.Run(func() {
   216  		<-tm.C
   217  	})
   218  	if tm.Stop() {
   219  		t.Errorf("synctest.Run unexpectedly returned before timer fired")
   220  	}
   221  }
   222  
   223  // TestTimerNondeterminism verifies that timers firing at the same instant
   224  // don't always fire in exactly the same order.
   225  func TestTimerNondeterminism(t *testing.T) {
   226  	synctest.Run(func() {
   227  		const iterations = 1000
   228  		var seen1, seen2 bool
   229  		for range iterations {
   230  			tm1 := time.NewTimer(1)
   231  			tm2 := time.NewTimer(1)
   232  			select {
   233  			case <-tm1.C:
   234  				seen1 = true
   235  			case <-tm2.C:
   236  				seen2 = true
   237  			}
   238  			if seen1 && seen2 {
   239  				return
   240  			}
   241  			synctest.Wait()
   242  		}
   243  		t.Errorf("after %v iterations, seen timer1:%v, timer2:%v; want both", iterations, seen1, seen2)
   244  	})
   245  }
   246  
   247  // TestSleepNondeterminism verifies that goroutines sleeping to the same instant
   248  // don't always schedule in exactly the same order.
   249  func TestSleepNondeterminism(t *testing.T) {
   250  	synctest.Run(func() {
   251  		const iterations = 1000
   252  		var seen1, seen2 bool
   253  		for range iterations {
   254  			var first atomic.Int32
   255  			go func() {
   256  				time.Sleep(1)
   257  				first.CompareAndSwap(0, 1)
   258  			}()
   259  			go func() {
   260  				time.Sleep(1)
   261  				first.CompareAndSwap(0, 2)
   262  			}()
   263  			time.Sleep(1)
   264  			synctest.Wait()
   265  			switch v := first.Load(); v {
   266  			case 1:
   267  				seen1 = true
   268  			case 2:
   269  				seen2 = true
   270  			default:
   271  				t.Fatalf("first = %v, want 1 or 2", v)
   272  			}
   273  			if seen1 && seen2 {
   274  				return
   275  			}
   276  			synctest.Wait()
   277  		}
   278  		t.Errorf("after %v iterations, seen goroutine 1:%v, 2:%v; want both", iterations, seen1, seen2)
   279  	})
   280  }
   281  
   282  // TestTimerRunsImmediately verifies that a 0-duration timer sends on its channel
   283  // without waiting for the bubble to block.
   284  func TestTimerRunsImmediately(t *testing.T) {
   285  	synctest.Run(func() {
   286  		start := time.Now()
   287  		tm := time.NewTimer(0)
   288  		select {
   289  		case got := <-tm.C:
   290  			if !got.Equal(start) {
   291  				t.Errorf("<-tm.C = %v, want %v", got, start)
   292  			}
   293  		default:
   294  			t.Errorf("0-duration timer channel is not readable; want it to be")
   295  		}
   296  	})
   297  }
   298  
   299  // TestTimerRunsLater verifies that reading from a timer's channel receives the
   300  // timer fired, even when that time is in reading from a timer's channel receives the
   301  // time the timer fired, even when that time is in the past.
   302  func TestTimerRanInPast(t *testing.T) {
   303  	synctest.Run(func() {
   304  		delay := 1 * time.Second
   305  		want := time.Now().Add(delay)
   306  		tm := time.NewTimer(delay)
   307  		time.Sleep(2 * delay)
   308  		select {
   309  		case got := <-tm.C:
   310  			if !got.Equal(want) {
   311  				t.Errorf("<-tm.C = %v, want %v", got, want)
   312  			}
   313  		default:
   314  			t.Errorf("0-duration timer channel is not readable; want it to be")
   315  		}
   316  	})
   317  }
   318  
   319  // TestAfterFuncRunsImmediately verifies that a 0-duration AfterFunc is scheduled
   320  // without waiting for the bubble to block.
   321  func TestAfterFuncRunsImmediately(t *testing.T) {
   322  	synctest.Run(func() {
   323  		var b atomic.Bool
   324  		time.AfterFunc(0, func() {
   325  			b.Store(true)
   326  		})
   327  		for !b.Load() {
   328  			runtime.Gosched()
   329  		}
   330  	})
   331  }
   332  
   333  // TestTimerResetZeroDoNotHang verifies that using timer.Reset(0) does not
   334  // cause the test to hang indefinitely. See https://go.dev/issue/76052.
   335  func TestTimerResetZeroDoNotHang(t *testing.T) {
   336  	synctest.Run(func() {
   337  		timer := time.NewTimer(0)
   338  		ctx, cancel := context.WithCancel(context.Background())
   339  
   340  		go func() {
   341  			for {
   342  				select {
   343  				case <-ctx.Done():
   344  					return
   345  				case <-timer.C:
   346  				}
   347  			}
   348  		}()
   349  
   350  		synctest.Wait()
   351  		timer.Reset(0)
   352  		synctest.Wait()
   353  		cancel()
   354  		synctest.Wait()
   355  	})
   356  }
   357  
   358  func TestChannelFromOutsideBubble(t *testing.T) {
   359  	choutside := make(chan struct{})
   360  	for _, test := range []struct {
   361  		desc    string
   362  		outside func(ch chan int)
   363  		inside  func(ch chan int)
   364  	}{{
   365  		desc:    "read closed",
   366  		outside: func(ch chan int) { close(ch) },
   367  		inside:  func(ch chan int) { <-ch },
   368  	}, {
   369  		desc:    "read value",
   370  		outside: func(ch chan int) { ch <- 0 },
   371  		inside:  func(ch chan int) { <-ch },
   372  	}, {
   373  		desc:    "write value",
   374  		outside: func(ch chan int) { <-ch },
   375  		inside:  func(ch chan int) { ch <- 0 },
   376  	}, {
   377  		desc:    "select outside only",
   378  		outside: func(ch chan int) { close(ch) },
   379  		inside: func(ch chan int) {
   380  			select {
   381  			case <-ch:
   382  			case <-choutside:
   383  			}
   384  		},
   385  	}, {
   386  		desc:    "select mixed",
   387  		outside: func(ch chan int) { close(ch) },
   388  		inside: func(ch chan int) {
   389  			ch2 := make(chan struct{})
   390  			select {
   391  			case <-ch:
   392  			case <-ch2:
   393  			}
   394  		},
   395  	}} {
   396  		t.Run(test.desc, func(t *testing.T) {
   397  			ch := make(chan int)
   398  			time.AfterFunc(1*time.Millisecond, func() {
   399  				test.outside(ch)
   400  			})
   401  			synctest.Run(func() {
   402  				test.inside(ch)
   403  			})
   404  		})
   405  	}
   406  }
   407  
   408  func TestChannelMovedOutOfBubble(t *testing.T) {
   409  	for _, test := range []struct {
   410  		desc      string
   411  		f         func(chan struct{})
   412  		wantFatal string
   413  	}{{
   414  		desc: "receive",
   415  		f: func(ch chan struct{}) {
   416  			<-ch
   417  		},
   418  		wantFatal: "receive on synctest channel from outside bubble",
   419  	}, {
   420  		desc: "send",
   421  		f: func(ch chan struct{}) {
   422  			ch <- struct{}{}
   423  		},
   424  		wantFatal: "send on synctest channel from outside bubble",
   425  	}, {
   426  		desc: "close",
   427  		f: func(ch chan struct{}) {
   428  			close(ch)
   429  		},
   430  		wantFatal: "close of synctest channel from outside bubble",
   431  	}} {
   432  		t.Run(test.desc, func(t *testing.T) {
   433  			// Bubbled channel accessed from outside any bubble.
   434  			t.Run("outside_bubble", func(t *testing.T) {
   435  				wantFatal(t, test.wantFatal, func() {
   436  					donec := make(chan struct{})
   437  					ch := make(chan chan struct{})
   438  					go func() {
   439  						defer close(donec)
   440  						test.f(<-ch)
   441  					}()
   442  					synctest.Run(func() {
   443  						ch <- make(chan struct{})
   444  					})
   445  					<-donec
   446  				})
   447  			})
   448  			// Bubbled channel accessed from a different bubble.
   449  			t.Run("different_bubble", func(t *testing.T) {
   450  				wantFatal(t, test.wantFatal, func() {
   451  					donec := make(chan struct{})
   452  					ch := make(chan chan struct{})
   453  					go func() {
   454  						defer close(donec)
   455  						c := <-ch
   456  						synctest.Run(func() {
   457  							test.f(c)
   458  						})
   459  					}()
   460  					synctest.Run(func() {
   461  						ch <- make(chan struct{})
   462  					})
   463  					<-donec
   464  				})
   465  			})
   466  		})
   467  	}
   468  }
   469  
   470  func TestTimerFromInsideBubble(t *testing.T) {
   471  	for _, test := range []struct {
   472  		desc      string
   473  		f         func(tm *time.Timer)
   474  		wantFatal string
   475  	}{{
   476  		desc: "read channel",
   477  		f: func(tm *time.Timer) {
   478  			<-tm.C
   479  		},
   480  		wantFatal: "receive on synctest channel from outside bubble",
   481  	}, {
   482  		desc: "Reset",
   483  		f: func(tm *time.Timer) {
   484  			tm.Reset(1 * time.Second)
   485  		},
   486  		wantFatal: "reset of synctest timer from outside bubble",
   487  	}, {
   488  		desc: "Stop",
   489  		f: func(tm *time.Timer) {
   490  			tm.Stop()
   491  		},
   492  		wantFatal: "stop of synctest timer from outside bubble",
   493  	}} {
   494  		t.Run(test.desc, func(t *testing.T) {
   495  			wantFatal(t, test.wantFatal, func() {
   496  				donec := make(chan struct{})
   497  				ch := make(chan *time.Timer)
   498  				go func() {
   499  					defer close(donec)
   500  					test.f(<-ch)
   501  				}()
   502  				synctest.Run(func() {
   503  					tm := time.NewTimer(1 * time.Second)
   504  					ch <- tm
   505  				})
   506  				<-donec
   507  			})
   508  		})
   509  	}
   510  }
   511  
   512  func TestDeadlockRoot(t *testing.T) {
   513  	defer wantPanic(t, "deadlock: all goroutines in bubble are blocked")
   514  	synctest.Run(func() {
   515  		select {}
   516  	})
   517  }
   518  
   519  func TestDeadlockChild(t *testing.T) {
   520  	defer wantPanic(t, "deadlock: main bubble goroutine has exited but blocked goroutines remain")
   521  	synctest.Run(func() {
   522  		go func() {
   523  			select {}
   524  		}()
   525  	})
   526  }
   527  
   528  func TestDeadlockTicker(t *testing.T) {
   529  	defer wantPanic(t, "deadlock: main bubble goroutine has exited but blocked goroutines remain")
   530  	synctest.Run(func() {
   531  		go func() {
   532  			for range time.Tick(1 * time.Second) {
   533  				t.Errorf("ticker unexpectedly ran")
   534  				return
   535  			}
   536  		}()
   537  	})
   538  }
   539  
   540  func TestCond(t *testing.T) {
   541  	synctest.Run(func() {
   542  		var mu sync.Mutex
   543  		cond := sync.NewCond(&mu)
   544  		start := time.Now()
   545  		const waitTime = 1 * time.Millisecond
   546  
   547  		go func() {
   548  			// Signal the cond.
   549  			time.Sleep(waitTime)
   550  			mu.Lock()
   551  			cond.Signal()
   552  			mu.Unlock()
   553  
   554  			// Broadcast to the cond.
   555  			time.Sleep(waitTime)
   556  			mu.Lock()
   557  			cond.Broadcast()
   558  			mu.Unlock()
   559  		}()
   560  
   561  		// Wait for cond.Signal.
   562  		mu.Lock()
   563  		cond.Wait()
   564  		mu.Unlock()
   565  		if got, want := time.Since(start), waitTime; got != want {
   566  			t.Errorf("after cond.Signal: time elapsed = %v, want %v", got, want)
   567  		}
   568  
   569  		// Wait for cond.Broadcast in two goroutines.
   570  		waiterDone := false
   571  		go func() {
   572  			mu.Lock()
   573  			cond.Wait()
   574  			mu.Unlock()
   575  			waiterDone = true
   576  		}()
   577  		mu.Lock()
   578  		cond.Wait()
   579  		mu.Unlock()
   580  		synctest.Wait()
   581  		if !waiterDone {
   582  			t.Errorf("after cond.Broadcast: waiter not done")
   583  		}
   584  		if got, want := time.Since(start), 2*waitTime; got != want {
   585  			t.Errorf("after cond.Broadcast: time elapsed = %v, want %v", got, want)
   586  		}
   587  	})
   588  }
   589  
   590  func TestIteratorPush(t *testing.T) {
   591  	synctest.Run(func() {
   592  		seq := func(yield func(time.Time) bool) {
   593  			for yield(time.Now()) {
   594  				time.Sleep(1 * time.Second)
   595  			}
   596  		}
   597  		var got []time.Time
   598  		go func() {
   599  			for now := range seq {
   600  				got = append(got, now)
   601  				if len(got) >= 3 {
   602  					break
   603  				}
   604  			}
   605  		}()
   606  		want := []time.Time{
   607  			time.Now(),
   608  			time.Now().Add(1 * time.Second),
   609  			time.Now().Add(2 * time.Second),
   610  		}
   611  		time.Sleep(5 * time.Second)
   612  		synctest.Wait()
   613  		if !slices.Equal(got, want) {
   614  			t.Errorf("got: %v; want: %v", got, want)
   615  		}
   616  	})
   617  }
   618  
   619  func TestIteratorPull(t *testing.T) {
   620  	synctest.Run(func() {
   621  		seq := func(yield func(time.Time) bool) {
   622  			for yield(time.Now()) {
   623  				time.Sleep(1 * time.Second)
   624  			}
   625  		}
   626  		var got []time.Time
   627  		go func() {
   628  			next, stop := iter.Pull(seq)
   629  			defer stop()
   630  			for len(got) < 3 {
   631  				now, _ := next()
   632  				got = append(got, now)
   633  			}
   634  		}()
   635  		want := []time.Time{
   636  			time.Now(),
   637  			time.Now().Add(1 * time.Second),
   638  			time.Now().Add(2 * time.Second),
   639  		}
   640  		time.Sleep(5 * time.Second)
   641  		synctest.Wait()
   642  		if !slices.Equal(got, want) {
   643  			t.Errorf("got: %v; want: %v", got, want)
   644  		}
   645  	})
   646  }
   647  
   648  func TestReflectFuncOf(t *testing.T) {
   649  	mkfunc := func(name string, i int) {
   650  		reflect.FuncOf([]reflect.Type{
   651  			reflect.StructOf([]reflect.StructField{{
   652  				Name: name + strconv.Itoa(i),
   653  				Type: reflect.TypeOf(0),
   654  			}}),
   655  		}, nil, false)
   656  	}
   657  	go func() {
   658  		for i := 0; i < 100000; i++ {
   659  			mkfunc("A", i)
   660  		}
   661  	}()
   662  	synctest.Run(func() {
   663  		for i := 0; i < 100000; i++ {
   664  			mkfunc("A", i)
   665  		}
   666  	})
   667  }
   668  
   669  func TestWaitGroupInBubble(t *testing.T) {
   670  	synctest.Run(func() {
   671  		var wg sync.WaitGroup
   672  		wg.Add(1)
   673  		const delay = 1 * time.Second
   674  		go func() {
   675  			time.Sleep(delay)
   676  			wg.Done()
   677  		}()
   678  		start := time.Now()
   679  		wg.Wait()
   680  		if got := time.Since(start); got != delay {
   681  			t.Fatalf("WaitGroup.Wait() took %v, want %v", got, delay)
   682  		}
   683  	})
   684  }
   685  
   686  // https://go.dev/issue/74386
   687  func TestWaitGroupRacingAdds(t *testing.T) {
   688  	synctest.Run(func() {
   689  		var wg sync.WaitGroup
   690  		for range 100 {
   691  			wg.Go(func() {})
   692  		}
   693  		wg.Wait()
   694  	})
   695  }
   696  
   697  func TestWaitGroupOutOfBubble(t *testing.T) {
   698  	var wg sync.WaitGroup
   699  	wg.Add(1)
   700  	donec := make(chan struct{})
   701  	go synctest.Run(func() {
   702  		// Since wg.Add was called outside the bubble, Wait is not durably blocking
   703  		// and this waits until wg.Done is called below.
   704  		wg.Wait()
   705  		close(donec)
   706  	})
   707  	select {
   708  	case <-donec:
   709  		t.Fatalf("synctest.Run finished before WaitGroup.Done called")
   710  	case <-time.After(1 * time.Millisecond):
   711  	}
   712  	wg.Done()
   713  	<-donec
   714  }
   715  
   716  func TestWaitGroupMovedIntoBubble(t *testing.T) {
   717  	wantFatal(t, "fatal error: sync: WaitGroup.Add called from inside and outside synctest bubble", func() {
   718  		var wg sync.WaitGroup
   719  		wg.Add(1)
   720  		synctest.Run(func() {
   721  			wg.Add(1)
   722  		})
   723  	})
   724  }
   725  
   726  func TestWaitGroupMovedOutOfBubble(t *testing.T) {
   727  	wantFatal(t, "fatal error: sync: WaitGroup.Add called from inside and outside synctest bubble", func() {
   728  		var wg sync.WaitGroup
   729  		synctest.Run(func() {
   730  			wg.Add(1)
   731  		})
   732  		wg.Add(1)
   733  	})
   734  }
   735  
   736  func TestWaitGroupMovedBetweenBubblesWithNonZeroCount(t *testing.T) {
   737  	wantFatal(t, "fatal error: sync: WaitGroup.Add called from multiple synctest bubbles", func() {
   738  		var wg sync.WaitGroup
   739  		synctest.Run(func() {
   740  			wg.Add(1)
   741  		})
   742  		synctest.Run(func() {
   743  			wg.Add(1)
   744  		})
   745  	})
   746  }
   747  
   748  func TestWaitGroupDisassociateInWait(t *testing.T) {
   749  	var wg sync.WaitGroup
   750  	synctest.Run(func() {
   751  		wg.Add(1)
   752  		wg.Done()
   753  		// Count and waiters are 0, so Wait disassociates the WaitGroup.
   754  		wg.Wait()
   755  	})
   756  	synctest.Run(func() {
   757  		// Reusing the WaitGroup is safe, because it is no longer bubbled.
   758  		wg.Add(1)
   759  		wg.Done()
   760  	})
   761  }
   762  
   763  func TestWaitGroupDisassociateInAdd(t *testing.T) {
   764  	var wg sync.WaitGroup
   765  	synctest.Run(func() {
   766  		wg.Add(1)
   767  		go wg.Wait()
   768  		synctest.Wait() // wait for Wait to block
   769  		// Count is 0 and waiters != 0, so Done wakes the waiters and
   770  		// disassociates the WaitGroup.
   771  		wg.Done()
   772  	})
   773  	synctest.Run(func() {
   774  		// Reusing the WaitGroup is safe, because it is no longer bubbled.
   775  		wg.Add(1)
   776  		wg.Done()
   777  	})
   778  }
   779  
   780  var testWaitGroupLinkerAllocatedWG sync.WaitGroup
   781  
   782  func TestWaitGroupLinkerAllocated(t *testing.T) {
   783  	synctest.Run(func() {
   784  		// This WaitGroup is probably linker-allocated and has no span,
   785  		// so we won't be able to add a special to it associating it with
   786  		// this bubble.
   787  		//
   788  		// Operations on it may not be durably blocking,
   789  		// but they shouldn't fail.
   790  		testWaitGroupLinkerAllocatedWG.Go(func() {})
   791  		testWaitGroupLinkerAllocatedWG.Wait()
   792  	})
   793  }
   794  
   795  var testWaitGroupHeapAllocatedWG = new(sync.WaitGroup)
   796  
   797  func TestWaitGroupHeapAllocated(t *testing.T) {
   798  	synctest.Run(func() {
   799  		// This package-scoped WaitGroup var should have been heap-allocated,
   800  		// so we can associate it with a bubble.
   801  		testWaitGroupHeapAllocatedWG.Add(1)
   802  		go testWaitGroupHeapAllocatedWG.Wait()
   803  		synctest.Wait()
   804  		testWaitGroupHeapAllocatedWG.Done()
   805  	})
   806  }
   807  
   808  // Issue #75134: Many racing bubble associations.
   809  func TestWaitGroupManyBubbles(t *testing.T) {
   810  	var wg sync.WaitGroup
   811  	for range 100 {
   812  		wg.Go(func() {
   813  			synctest.Run(func() {
   814  				cancelc := make(chan struct{})
   815  				var wg2 sync.WaitGroup
   816  				for range 100 {
   817  					wg2.Go(func() {
   818  						<-cancelc
   819  					})
   820  				}
   821  				synctest.Wait()
   822  				close(cancelc)
   823  				wg2.Wait()
   824  			})
   825  		})
   826  	}
   827  	wg.Wait()
   828  }
   829  
   830  func TestHappensBefore(t *testing.T) {
   831  	// Use two parallel goroutines accessing different vars to ensure that
   832  	// we correctly account for multiple goroutines in the bubble.
   833  	var v1 int
   834  	var v2 int
   835  	synctest.Run(func() {
   836  		v1++ // 1
   837  		v2++ // 1
   838  
   839  		// Wait returns after these goroutines exit.
   840  		go func() {
   841  			v1++ // 2
   842  		}()
   843  		go func() {
   844  			v2++ // 2
   845  		}()
   846  		synctest.Wait()
   847  
   848  		v1++ // 3
   849  		v2++ // 3
   850  
   851  		// Wait returns after these goroutines block.
   852  		ch1 := make(chan struct{})
   853  		go func() {
   854  			v1++ // 4
   855  			<-ch1
   856  		}()
   857  		go func() {
   858  			v2++ // 4
   859  			<-ch1
   860  		}()
   861  		synctest.Wait()
   862  
   863  		v1++ // 5
   864  		v2++ // 5
   865  		close(ch1)
   866  
   867  		// Wait returns after these timers run.
   868  		time.AfterFunc(0, func() {
   869  			v1++ // 6
   870  		})
   871  		time.AfterFunc(0, func() {
   872  			v2++ // 6
   873  		})
   874  		synctest.Wait()
   875  
   876  		v1++ // 7
   877  		v2++ // 7
   878  
   879  		// Wait returns after these timer goroutines block.
   880  		ch2 := make(chan struct{})
   881  		time.AfterFunc(0, func() {
   882  			v1++ // 8
   883  			<-ch2
   884  		})
   885  		time.AfterFunc(0, func() {
   886  			v2++ // 8
   887  			<-ch2
   888  		})
   889  		synctest.Wait()
   890  
   891  		v1++ // 9
   892  		v2++ // 9
   893  		close(ch2)
   894  	})
   895  	// This Run happens after the previous Run returns.
   896  	synctest.Run(func() {
   897  		go func() {
   898  			go func() {
   899  				v1++ // 10
   900  			}()
   901  		}()
   902  		go func() {
   903  			go func() {
   904  				v2++ // 10
   905  			}()
   906  		}()
   907  	})
   908  	// These tests happen after Run returns.
   909  	if got, want := v1, 10; got != want {
   910  		t.Errorf("v1 = %v, want %v", got, want)
   911  	}
   912  	if got, want := v2, 10; got != want {
   913  		t.Errorf("v2 = %v, want %v", got, want)
   914  	}
   915  }
   916  
   917  // https://go.dev/issue/73817
   918  func TestWeak(t *testing.T) {
   919  	synctest.Run(func() {
   920  		for range 5 {
   921  			runtime.GC()
   922  			b := make([]byte, 1024)
   923  			weak.Make(&b)
   924  		}
   925  	})
   926  }
   927  
   928  func wantPanic(t *testing.T, want string) {
   929  	if e := recover(); e != nil {
   930  		if got := fmt.Sprint(e); got != want {
   931  			t.Errorf("got panic message %q, want %q", got, want)
   932  		}
   933  	} else {
   934  		t.Errorf("got no panic, want one")
   935  	}
   936  }
   937  
   938  func wantFatal(t *testing.T, want string, f func()) {
   939  	t.Helper()
   940  
   941  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   942  		f()
   943  		return
   944  	}
   945  
   946  	cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^"+t.Name()+"$")
   947  	cmd = testenv.CleanCmdEnv(cmd)
   948  	cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
   949  	out, err := cmd.CombinedOutput()
   950  	if err == nil {
   951  		t.Errorf("expected test function to panic, but test returned successfully")
   952  	}
   953  	if !strings.Contains(string(out), want) {
   954  		t.Errorf("wanted test output contaiing %q; got %q", want, string(out))
   955  	}
   956  }
   957  

View as plain text