github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/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/nicocha30/gvisor-ligolo/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 }