File: timer.go

package info (click to toggle)
golang-gvisor-gvisor 0.0~20221219.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bookworm-proposed-updates
  • size: 17,136 kB
  • sloc: asm: 2,860; cpp: 348; python: 89; sh: 40; makefile: 34; ansic: 21
file content (150 lines) | stat: -rw-r--r-- 4,733 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tcp

import (
	"math"
	"time"

	"gvisor.dev/gvisor/pkg/tcpip"
)

type timerState int

const (
	// The timer is disabled.
	timerStateDisabled timerState = iota
	// The timer is enabled, but the clock timer may be set to an earlier
	// expiration time due to a previous orphaned state.
	timerStateEnabled
	// The timer is disabled, but the clock timer is enabled, which means that
	// it will cause a spurious wakeup unless the timer is enabled before the
	// clock timer fires.
	timerStateOrphaned
)

// timer is a timer implementation that reduces the interactions with the
// clock timer infrastructure by letting timers run (and potentially
// eventually expire) even if they are stopped. It makes it cheaper to
// disable/reenable timers at the expense of spurious wakes. This is useful for
// cases when the same timer is disabled/reenabled repeatedly with relatively
// long timeouts farther into the future.
//
// TCP retransmit timers benefit from this because they the timeouts are long
// (currently at least 200ms), and get disabled when acks are received, and
// reenabled when new pending segments are sent.
//
// It is advantageous to avoid interacting with the clock because it acquires
// a global mutex and performs O(log n) operations, where n is the global number
// of timers, whenever a timer is enabled or disabled, and may make a syscall.
//
// This struct is thread-compatible.
type timer struct {
	state timerState

	clock tcpip.Clock

	// target is the expiration time of the current timer. It is only
	// meaningful in the enabled state.
	target tcpip.MonotonicTime

	// clockTarget is the expiration time of the clock timer. It is
	// meaningful in the enabled and orphaned states.
	clockTarget tcpip.MonotonicTime

	// timer is the clock timer used to wait on.
	timer tcpip.Timer
}

// init initializes the timer. Once it expires the function callback
// passed will be called.
func (t *timer) init(clock tcpip.Clock, f func()) {
	t.state = timerStateDisabled
	t.clock = clock

	// Initialize a clock timer that will call the callback func, then
	// immediately stop it.
	t.timer = t.clock.AfterFunc(math.MaxInt64, f)
	t.timer.Stop()
}

// cleanup frees all resources associated with the timer.
func (t *timer) cleanup() {
	if t.timer == nil {
		// No cleanup needed.
		return
	}
	t.timer.Stop()
	*t = timer{}
}

// isZero returns true if the timer is in the zero state. This is usually
// only true if init() has never been called or if cleanup has been called.
func (t *timer) isZero() bool {
	return *t == timer{}
}

// checkExpiration checks if the given timer has actually expired, it should be
// called whenever the callback function is called, and is used to check if it's
// a supurious timer expiration (due to a previously orphaned timer) or a
// legitimate one.
func (t *timer) checkExpiration() bool {
	// Transition to fully disabled state if we're just consuming an
	// orphaned timer.
	if t.state == timerStateOrphaned {
		t.state = timerStateDisabled
		return false
	}

	// The timer is enabled, but it may have expired early. Check if that's
	// the case, and if so, reset the runtime timer to the correct time.
	now := t.clock.NowMonotonic()
	if now.Before(t.target) {
		t.clockTarget = t.target
		t.timer.Reset(t.target.Sub(now))
		return false
	}

	// The timer has actually expired, disable it for now and inform the
	// caller.
	t.state = timerStateDisabled
	return true
}

// disable disables the timer, leaving it in an orphaned state if it wasn't
// already disabled.
func (t *timer) disable() {
	if t.state != timerStateDisabled {
		t.state = timerStateOrphaned
	}
}

// enabled returns true if the timer is currently enabled, false otherwise.
func (t *timer) enabled() bool {
	return t.state == timerStateEnabled
}

// enable enables the timer, programming the runtime timer if necessary.
func (t *timer) enable(d time.Duration) {
	t.target = t.clock.NowMonotonic().Add(d)

	// Check if we need to set the runtime timer.
	if t.state == timerStateDisabled || t.target.Before(t.clockTarget) {
		t.clockTarget = t.target
		t.timer.Reset(d)
	}

	t.state = timerStateEnabled
}