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

     1  // Copyright 2018 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 tcp
    16  
    17  import (
    18  	"time"
    19  
    20  	"gvisor.dev/gvisor/pkg/tcpip"
    21  )
    22  
    23  type timerState int
    24  
    25  const (
    26  	// The timer has not been initialized yet or has been cleaned up.
    27  	timerUninitialized timerState = iota
    28  	// The timer is disabled.
    29  	timerStateDisabled
    30  	// The timer is enabled, but the clock timer may be set to an earlier
    31  	// expiration time due to a previous orphaned state.
    32  	timerStateEnabled
    33  	// The timer is disabled, but the clock timer is enabled, which means that
    34  	// it will cause a spurious wakeup unless the timer is enabled before the
    35  	// clock timer fires.
    36  	timerStateOrphaned
    37  )
    38  
    39  // timer is a timer implementation that reduces the interactions with the
    40  // clock timer infrastructure by letting timers run (and potentially
    41  // eventually expire) even if they are stopped. It makes it cheaper to
    42  // disable/reenable timers at the expense of spurious wakes. This is useful for
    43  // cases when the same timer is disabled/reenabled repeatedly with relatively
    44  // long timeouts farther into the future.
    45  //
    46  // TCP retransmit timers benefit from this because they the timeouts are long
    47  // (currently at least 200ms), and get disabled when acks are received, and
    48  // reenabled when new pending segments are sent.
    49  //
    50  // It is advantageous to avoid interacting with the clock because it acquires
    51  // a global mutex and performs O(log n) operations, where n is the global number
    52  // of timers, whenever a timer is enabled or disabled, and may make a syscall.
    53  //
    54  // This struct is thread-compatible.
    55  type timer struct {
    56  	state timerState
    57  
    58  	clock tcpip.Clock
    59  
    60  	// target is the expiration time of the current timer. It is only
    61  	// meaningful in the enabled state.
    62  	target tcpip.MonotonicTime
    63  
    64  	// clockTarget is the expiration time of the clock timer. It is
    65  	// meaningful in the enabled and orphaned states.
    66  	clockTarget tcpip.MonotonicTime
    67  
    68  	// timer is the clock timer used to wait on.
    69  	timer tcpip.Timer
    70  
    71  	// callback is the function that's called when the timer expires.
    72  	callback func()
    73  }
    74  
    75  // init initializes the timer. Once it expires the function callback
    76  // passed will be called.
    77  func (t *timer) init(clock tcpip.Clock, f func()) {
    78  	t.state = timerStateDisabled
    79  	t.clock = clock
    80  	t.callback = f
    81  }
    82  
    83  // cleanup frees all resources associated with the timer.
    84  func (t *timer) cleanup() {
    85  	if t.timer == nil {
    86  		// No cleanup needed.
    87  		return
    88  	}
    89  	t.timer.Stop()
    90  	*t = timer{}
    91  }
    92  
    93  // isUninitialized returns true if the timer is in the uninitialized state. This
    94  // is only true if init() has never been called or if cleanup has been called.
    95  func (t *timer) isUninitialized() bool {
    96  	return t.state == timerUninitialized
    97  }
    98  
    99  // checkExpiration checks if the given timer has actually expired, it should be
   100  // called whenever the callback function is called, and is used to check if it's
   101  // a spurious timer expiration (due to a previously orphaned timer) or a
   102  // legitimate one.
   103  func (t *timer) checkExpiration() bool {
   104  	// Transition to fully disabled state if we're just consuming an
   105  	// orphaned timer.
   106  	if t.state == timerStateOrphaned {
   107  		t.state = timerStateDisabled
   108  		return false
   109  	}
   110  
   111  	// The timer is enabled, but it may have expired early. Check if that's
   112  	// the case, and if so, reset the runtime timer to the correct time.
   113  	now := t.clock.NowMonotonic()
   114  	if now.Before(t.target) {
   115  		t.clockTarget = t.target
   116  		t.timer.Reset(t.target.Sub(now))
   117  		return false
   118  	}
   119  
   120  	// The timer has actually expired, disable it for now and inform the
   121  	// caller.
   122  	t.state = timerStateDisabled
   123  	return true
   124  }
   125  
   126  // disable disables the timer, leaving it in an orphaned state if it wasn't
   127  // already disabled.
   128  func (t *timer) disable() {
   129  	if t.state != timerStateDisabled {
   130  		t.state = timerStateOrphaned
   131  	}
   132  }
   133  
   134  // enabled returns true if the timer is currently enabled, false otherwise.
   135  func (t *timer) enabled() bool {
   136  	return t.state == timerStateEnabled
   137  }
   138  
   139  // enable enables the timer, programming the runtime timer if necessary.
   140  func (t *timer) enable(d time.Duration) {
   141  	t.target = t.clock.NowMonotonic().Add(d)
   142  
   143  	// Check if we need to set the runtime timer.
   144  	if t.state == timerStateDisabled || t.target.Before(t.clockTarget) {
   145  		t.clockTarget = t.target
   146  		t.resetOrStart(d)
   147  	}
   148  
   149  	t.state = timerStateEnabled
   150  }
   151  
   152  // resetOrStart creates the timer if it doesn't already exist or resets it with
   153  // the given duration if it does.
   154  func (t *timer) resetOrStart(d time.Duration) {
   155  	if t.timer == nil {
   156  		t.timer = t.clock.AfterFunc(d, t.callback)
   157  	} else {
   158  		t.timer.Reset(d)
   159  	}
   160  }