gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/timer_test.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package tcpip_test
    16  
    17  import (
    18  	"math"
    19  	"sync"
    20  	"testing"
    21  	"time"
    22  
    23  	"gvisor.dev/gvisor/pkg/tcpip"
    24  )
    25  
    26  func TestMonotonicTimeBefore(t *testing.T) {
    27  	var mt tcpip.MonotonicTime
    28  	if mt.Before(mt) {
    29  		t.Errorf("%#v.Before(%#v)", mt, mt)
    30  	}
    31  
    32  	one := mt.Add(1)
    33  	if one.Before(mt) {
    34  		t.Errorf("%#v.Before(%#v)", one, mt)
    35  	}
    36  	if !mt.Before(one) {
    37  		t.Errorf("!%#v.Before(%#v)", mt, one)
    38  	}
    39  }
    40  
    41  func TestMonotonicTimeAfter(t *testing.T) {
    42  	var mt tcpip.MonotonicTime
    43  	if mt.After(mt) {
    44  		t.Errorf("%#v.After(%#v)", mt, mt)
    45  	}
    46  
    47  	one := mt.Add(1)
    48  	if mt.After(one) {
    49  		t.Errorf("%#v.After(%#v)", mt, one)
    50  	}
    51  	if !one.After(mt) {
    52  		t.Errorf("!%#v.After(%#v)", one, mt)
    53  	}
    54  }
    55  
    56  func TestMonotonicTimeAddSub(t *testing.T) {
    57  	var mt tcpip.MonotonicTime
    58  	if one, two := mt.Add(2), mt.Add(1).Add(1); one != two {
    59  		t.Errorf("mt.Add(2) != mt.Add(1).Add(1) (%#v != %#v)", one, two)
    60  	}
    61  
    62  	min := mt.Add(math.MinInt64)
    63  	max := mt.Add(math.MaxInt64)
    64  
    65  	if overflow := mt.Add(1).Add(math.MaxInt64); overflow != max {
    66  		t.Errorf("mt.Add(math.MaxInt64) != mt.Add(1).Add(math.MaxInt64) (%#v != %#v)", max, overflow)
    67  	}
    68  	if underflow := mt.Add(-1).Add(math.MinInt64); underflow != min {
    69  		t.Errorf("mt.Add(math.MinInt64) != mt.Add(-1).Add(math.MinInt64) (%#v != %#v)", min, underflow)
    70  	}
    71  
    72  	if got, want := min.Sub(min), time.Duration(0); want != got {
    73  		t.Errorf("got min.Sub(min) = %d, want %d", got, want)
    74  	}
    75  	if got, want := max.Sub(max), time.Duration(0); want != got {
    76  		t.Errorf("got max.Sub(max) = %d, want %d", got, want)
    77  	}
    78  
    79  	if overflow, want := max.Sub(min), time.Duration(math.MaxInt64); overflow != want {
    80  		t.Errorf("mt.Add(math.MaxInt64).Sub(mt.Add(math.MinInt64) != %s (%#v)", want, overflow)
    81  	}
    82  	if underflow, want := min.Sub(max), time.Duration(math.MinInt64); underflow != want {
    83  		t.Errorf("mt.Add(math.MinInt64).Sub(mt.Add(math.MaxInt64) != %s (%#v)", want, underflow)
    84  	}
    85  }
    86  
    87  func TestMonotonicTimeSub(t *testing.T) {
    88  	var mt tcpip.MonotonicTime
    89  
    90  	if one, two := mt.Add(2), mt.Add(1).Add(1); one != two {
    91  		t.Errorf("mt.Add(2) != mt.Add(1).Add(1) (%#v != %#v)", one, two)
    92  	}
    93  
    94  	if max, overflow := mt.Add(math.MaxInt64), mt.Add(1).Add(math.MaxInt64); max != overflow {
    95  		t.Errorf("mt.Add(math.MaxInt64) != mt.Add(1).Add(math.MaxInt64) (%#v != %#v)", max, overflow)
    96  	}
    97  	if max, underflow := mt.Add(math.MinInt64), mt.Add(-1).Add(math.MinInt64); max != underflow {
    98  		t.Errorf("mt.Add(math.MinInt64) != mt.Add(-1).Add(math.MinInt64) (%#v != %#v)", max, underflow)
    99  	}
   100  }
   101  
   102  const (
   103  	shortDuration  = 1 * time.Nanosecond
   104  	middleDuration = 100 * time.Millisecond
   105  )
   106  
   107  func TestJobReschedule(t *testing.T) {
   108  	clock := tcpip.NewStdClock()
   109  	var wg sync.WaitGroup
   110  	var lock sync.Mutex
   111  
   112  	for i := 0; i < 2; i++ {
   113  		wg.Add(1)
   114  
   115  		go func() {
   116  			lock.Lock()
   117  			// Assigning a new timer value updates the timer's locker and function.
   118  			// This test makes sure there is no data race when reassigning a timer
   119  			// that has an active timer (even if it has been stopped as a stopped
   120  			// timer may be blocked on a lock before it can check if it has been
   121  			// stopped while another goroutine holds the same lock).
   122  			job := tcpip.NewJob(clock, &lock, func() {
   123  				wg.Done()
   124  			})
   125  			job.Schedule(shortDuration)
   126  			lock.Unlock()
   127  		}()
   128  	}
   129  	wg.Wait()
   130  }
   131  
   132  func stdClockWithAfter() (tcpip.Clock, func(time.Duration) <-chan time.Time) {
   133  	return tcpip.NewStdClock(), time.After
   134  }
   135  
   136  func TestJobExecution(t *testing.T) {
   137  	t.Parallel()
   138  
   139  	clock, after := stdClockWithAfter()
   140  	var lock sync.Mutex
   141  	ch := make(chan struct{})
   142  
   143  	job := tcpip.NewJob(clock, &lock, func() {
   144  		ch <- struct{}{}
   145  	})
   146  	job.Schedule(shortDuration)
   147  
   148  	// Wait for timer to fire.
   149  	select {
   150  	case <-ch:
   151  	case <-after(middleDuration):
   152  		t.Fatal("timed out waiting for timer to fire")
   153  	}
   154  
   155  	// The timer should have fired only once.
   156  	select {
   157  	case <-ch:
   158  		t.Fatal("no other timers should have fired")
   159  	case <-after(middleDuration):
   160  	}
   161  }
   162  
   163  func TestCancellableTimerResetFromLongDuration(t *testing.T) {
   164  	t.Parallel()
   165  
   166  	clock, after := stdClockWithAfter()
   167  	var lock sync.Mutex
   168  	ch := make(chan struct{})
   169  
   170  	job := tcpip.NewJob(clock, &lock, func() { ch <- struct{}{} })
   171  	job.Schedule(middleDuration)
   172  
   173  	lock.Lock()
   174  	job.Cancel()
   175  	lock.Unlock()
   176  
   177  	job.Schedule(shortDuration)
   178  
   179  	// Wait for timer to fire.
   180  	select {
   181  	case <-ch:
   182  	case <-after(middleDuration):
   183  		t.Fatal("timed out waiting for timer to fire")
   184  	}
   185  
   186  	// The timer should have fired only once.
   187  	select {
   188  	case <-ch:
   189  		t.Fatal("no other timers should have fired")
   190  	case <-after(middleDuration):
   191  	}
   192  }
   193  
   194  func TestJobRescheduleFromShortDuration(t *testing.T) {
   195  	t.Parallel()
   196  
   197  	clock, after := stdClockWithAfter()
   198  	var lock sync.Mutex
   199  	ch := make(chan struct{})
   200  
   201  	lock.Lock()
   202  	job := tcpip.NewJob(clock, &lock, func() { ch <- struct{}{} })
   203  	job.Schedule(shortDuration)
   204  	job.Cancel()
   205  	lock.Unlock()
   206  
   207  	// Wait for timer to fire if it wasn't correctly stopped.
   208  	select {
   209  	case <-ch:
   210  		t.Fatal("timer fired after being stopped")
   211  	case <-after(middleDuration):
   212  	}
   213  
   214  	job.Schedule(shortDuration)
   215  
   216  	// Wait for timer to fire.
   217  	select {
   218  	case <-ch:
   219  	case <-after(middleDuration):
   220  		t.Fatal("timed out waiting for timer to fire")
   221  	}
   222  
   223  	// The timer should have fired only once.
   224  	select {
   225  	case <-ch:
   226  		t.Fatal("no other timers should have fired")
   227  	case <-after(middleDuration):
   228  	}
   229  }
   230  
   231  func TestJobImmediatelyCancel(t *testing.T) {
   232  	t.Parallel()
   233  
   234  	clock, after := stdClockWithAfter()
   235  	var lock sync.Mutex
   236  	ch := make(chan struct{})
   237  
   238  	for i := 0; i < 1000; i++ {
   239  		lock.Lock()
   240  		job := tcpip.NewJob(clock, &lock, func() { ch <- struct{}{} })
   241  		job.Schedule(shortDuration)
   242  		job.Cancel()
   243  		lock.Unlock()
   244  	}
   245  
   246  	// Wait for timer to fire if it wasn't correctly stopped.
   247  	select {
   248  	case <-ch:
   249  		t.Fatal("timer fired after being stopped")
   250  	case <-after(middleDuration):
   251  	}
   252  }
   253  
   254  func stdClockWithAfterAndSleep() (tcpip.Clock, func(time.Duration) <-chan time.Time, func(time.Duration)) {
   255  	clock, after := stdClockWithAfter()
   256  	return clock, after, time.Sleep
   257  }
   258  
   259  func TestJobCancelledRescheduleWithoutLock(t *testing.T) {
   260  	t.Parallel()
   261  
   262  	clock, after, sleep := stdClockWithAfterAndSleep()
   263  	var lock sync.Mutex
   264  	ch := make(chan struct{})
   265  
   266  	lock.Lock()
   267  	job := tcpip.NewJob(clock, &lock, func() { ch <- struct{}{} })
   268  	job.Schedule(shortDuration)
   269  	job.Cancel()
   270  	lock.Unlock()
   271  
   272  	for i := 0; i < 10; i++ {
   273  		job.Schedule(middleDuration)
   274  
   275  		lock.Lock()
   276  		// Sleep until the timer fires and gets blocked trying to take the lock.
   277  		sleep(middleDuration * 2)
   278  		job.Cancel()
   279  		lock.Unlock()
   280  	}
   281  
   282  	// Wait for double the duration so timers that weren't correctly stopped can
   283  	// fire.
   284  	select {
   285  	case <-ch:
   286  		t.Fatal("timer fired after being stopped")
   287  	case <-after(middleDuration * 2):
   288  	}
   289  }
   290  
   291  func TestManyCancellableTimerResetAfterBlockedOnLock(t *testing.T) {
   292  	t.Parallel()
   293  
   294  	clock, after, sleep := stdClockWithAfterAndSleep()
   295  	var lock sync.Mutex
   296  	ch := make(chan struct{})
   297  
   298  	lock.Lock()
   299  	job := tcpip.NewJob(clock, &lock, func() { ch <- struct{}{} })
   300  	job.Schedule(shortDuration)
   301  	for i := 0; i < 10; i++ {
   302  		// Sleep until the timer fires and gets blocked trying to take the lock.
   303  		sleep(middleDuration)
   304  		job.Cancel()
   305  		job.Schedule(shortDuration)
   306  	}
   307  	lock.Unlock()
   308  
   309  	// Wait for double the duration for the last timer to fire.
   310  	select {
   311  	case <-ch:
   312  	case <-after(middleDuration):
   313  		t.Fatal("timed out waiting for timer to fire")
   314  	}
   315  
   316  	// The timer should have fired only once.
   317  	select {
   318  	case <-ch:
   319  		t.Fatal("no other timers should have fired")
   320  	case <-after(middleDuration):
   321  	}
   322  }
   323  
   324  func TestManyJobReschedulesUnderLock(t *testing.T) {
   325  	t.Parallel()
   326  
   327  	clock, after := stdClockWithAfter()
   328  	var lock sync.Mutex
   329  	ch := make(chan struct{})
   330  
   331  	lock.Lock()
   332  	job := tcpip.NewJob(clock, &lock, func() { ch <- struct{}{} })
   333  	job.Schedule(shortDuration)
   334  	for i := 0; i < 10; i++ {
   335  		job.Cancel()
   336  		job.Schedule(shortDuration)
   337  	}
   338  	lock.Unlock()
   339  
   340  	// Wait for double the duration for the last timer to fire.
   341  	select {
   342  	case <-ch:
   343  	case <-after(middleDuration):
   344  		t.Fatal("timed out waiting for timer to fire")
   345  	}
   346  
   347  	// The timer should have fired only once.
   348  	select {
   349  	case <-ch:
   350  		t.Fatal("no other timers should have fired")
   351  	case <-after(middleDuration):
   352  	}
   353  }