github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/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  	"math"
    19  	"time"
    20  
    21  	"github.com/MerlinKodo/gvisor/pkg/tcpip"
    22  )
    23  
    24  type timerState int
    25  
    26  const (
    27  	// The timer is disabled.
    28  	timerStateDisabled timerState = iota
    29  	// The timer is enabled, but the clock timer may be set to an earlier
    30  	// expiration time due to a previous orphaned state.
    31  	timerStateEnabled
    32  	// The timer is disabled, but the clock timer is enabled, which means that
    33  	// it will cause a spurious wakeup unless the timer is enabled before the
    34  	// clock timer fires.
    35  	timerStateOrphaned
    36  )
    37  
    38  // timer is a timer implementation that reduces the interactions with the
    39  // clock timer infrastructure by letting timers run (and potentially
    40  // eventually expire) even if they are stopped. It makes it cheaper to
    41  // disable/reenable timers at the expense of spurious wakes. This is useful for
    42  // cases when the same timer is disabled/reenabled repeatedly with relatively
    43  // long timeouts farther into the future.
    44  //
    45  // TCP retransmit timers benefit from this because they the timeouts are long
    46  // (currently at least 200ms), and get disabled when acks are received, and
    47  // reenabled when new pending segments are sent.
    48  //
    49  // It is advantageous to avoid interacting with the clock because it acquires
    50  // a global mutex and performs O(log n) operations, where n is the global number
    51  // of timers, whenever a timer is enabled or disabled, and may make a syscall.
    52  //
    53  // This struct is thread-compatible.
    54  type timer struct {
    55  	state timerState
    56  
    57  	clock tcpip.Clock
    58  
    59  	// target is the expiration time of the current timer. It is only
    60  	// meaningful in the enabled state.
    61  	target tcpip.MonotonicTime
    62  
    63  	// clockTarget is the expiration time of the clock timer. It is
    64  	// meaningful in the enabled and orphaned states.
    65  	clockTarget tcpip.MonotonicTime
    66  
    67  	// timer is the clock timer used to wait on.
    68  	timer tcpip.Timer
    69  }
    70  
    71  // init initializes the timer. Once it expires the function callback
    72  // passed will be called.
    73  func (t *timer) init(clock tcpip.Clock, f func()) {
    74  	t.state = timerStateDisabled
    75  	t.clock = clock
    76  
    77  	// Initialize a clock timer that will call the callback func, then
    78  	// immediately stop it.
    79  	t.timer = t.clock.AfterFunc(math.MaxInt64, f)
    80  	t.timer.Stop()
    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  // isZero returns true if the timer is in the zero state. This is usually
    94  // only true if init() has never been called or if cleanup has been called.
    95  func (t *timer) isZero() bool {
    96  	return *t == timer{}
    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 supurious 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.timer.Reset(d)
   147  	}
   148  
   149  	t.state = timerStateEnabled
   150  }