v0.3.0 99dc266

transport_health.go RawHistoryFolder

1package dgram
2
3import (
4	"time"
5
6	"github.com/bnema/vev/internal/ports"
7)
8
9// healthLoop owns link-health timing so bulk retransmission cannot delay state
10// transitions or socket hopping.
11func (t *Transport) healthLoop() {
12	health := t.clock.NewTimer(t.resendAfter)
13	defer health.Stop()
14	for {
15		select {
16		case <-health.C():
17			t.checkSilence()
18			health.Reset(t.resendAfter)
19			t.notifyTimerHook(func() func() { return t.afterHealthTimer })
20		case <-t.done:
21			return
22		}
23	}
24}
25
26func (t *Transport) heartbeatLoop() {
27	heartbeat := t.clock.NewTimer(t.heartbeat)
28	defer heartbeat.Stop()
29	for {
30		select {
31		case <-heartbeat.C():
32			t.queueControl(recProbe, 0)
33			heartbeat.Reset(t.heartbeat)
34			t.notifyTimerHook(func() func() { return t.afterHeartbeatTimer })
35		case <-t.done:
36			return
37		}
38	}
39}
40
41func (t *Transport) notifyTimerHook(get func() func()) {
42	t.mu.Lock()
43	hook := get()
44	t.mu.Unlock()
45	if hook != nil {
46		hook()
47	}
48}
49
50func (t *Transport) hopPacketConnOnce(generation uint64) {
51	t.mu.Lock()
52	if t.rebind == nil || t.hoppedOffline || t.closed || t.linkState != ports.LinkStateProbing || t.health.generation != generation {
53		t.mu.Unlock()
54		return
55	}
56	old := t.pc
57	rebind := t.rebind
58	t.hoppedOffline = true
59	t.hopGeneration = generation
60	t.mu.Unlock()
61	pc, err := rebind(old)
62
63	// Lock order is writeMu -> controlConnMu -> mu. Data writes take only
64	// writeMu, while control writes hold controlConnMu's read lock without
65	// writeMu; therefore a hop waits for active control writes but does not make
66	// them wait for a blocked data write.
67	t.writeMu.Lock()
68	defer t.writeMu.Unlock()
69	t.controlConnMu.Lock()
70	defer t.controlConnMu.Unlock()
71	t.mu.Lock()
72	if err != nil {
73		t.rollbackHopLocked(generation)
74		t.mu.Unlock()
75		return
76	}
77	if t.closed || t.pc != old || t.health.generation != generation || t.linkState != ports.LinkStateProbing {
78		t.rollbackHopLocked(generation)
79		t.mu.Unlock()
80		_ = pc.Close()
81		return
82	}
83	t.pc = pc
84	t.writeDeadlines = newWriteDeadlineState(pc)
85	t.mu.Unlock()
86	go t.readLoop(pc)
87	_ = old.Close()
88}
89
90func (t *Transport) rollbackHopLocked(generation uint64) {
91	if t.hoppedOffline && t.hopGeneration == generation {
92		t.hoppedOffline = false
93		t.hopGeneration = 0
94	}
95}
96
97func (t *Transport) checkSilence() {
98	runHook := true
99	for {
100		t.mu.Lock()
101		if t.closed {
102			t.mu.Unlock()
103			return
104		}
105		now := t.clock.Now()
106		generation := t.health.generation
107		state, hop, dead := t.health.decide(now, len(t.pending) > 0, t.degradedAfter, t.probeAfter, t.offlineAfter, t.deadAfter)
108		afterDecision := t.afterHealthDecision
109		t.mu.Unlock()
110
111		if runHook {
112			runHook = false
113			if afterDecision != nil {
114				afterDecision()
115			}
116		}
117
118		t.mu.Lock()
119		if t.closed {
120			t.mu.Unlock()
121			return
122		}
123		if t.health.generation != generation {
124			t.mu.Unlock()
125			continue
126		}
127		changed, stateGeneration := t.setLinkStateLocked(state)
128		afterStateCommit := t.afterLinkStateCommit
129		closed := false
130		if dead {
131			closed = t.closeWithErrorLocked(ErrLinkDead)
132		}
133		t.mu.Unlock()
134
135		if changed {
136			if afterStateCommit != nil {
137				afterStateCommit(state)
138			}
139			var linkErr error
140			if dead {
141				linkErr = ErrLinkDead
142			}
143			t.publishLinkState(state, now, linkErr, stateGeneration)
144		}
145		if closed {
146			t.broadcastClosed()
147			return
148		}
149		if hop {
150			t.hopPacketConnOnce(generation)
151		}
152		return
153	}
154}
155
156func (t *Transport) setLinkState(state ports.LinkState, err error) {
157	now := t.clock.Now()
158	t.mu.Lock()
159	changed, stateGeneration := t.setLinkStateLocked(state)
160	afterStateCommit := t.afterLinkStateCommit
161	t.mu.Unlock()
162	if changed {
163		if afterStateCommit != nil {
164			afterStateCommit(state)
165		}
166		t.publishLinkState(state, now, err, stateGeneration)
167	}
168}
169
170func (t *Transport) setLinkStateLocked(state ports.LinkState) (bool, uint64) {
171	if t.linkState == state {
172		return false, t.linkStateGeneration
173	}
174	t.linkState = state
175	t.linkStateGeneration++
176	if state == ports.LinkStateConnected {
177		t.hoppedOffline = false
178		t.hopGeneration = 0
179		for _, p := range t.pending {
180			if p != nil {
181				p.attempts = 0
182			}
183		}
184	}
185	t.notifySendWaitersLocked()
186	return true, t.linkStateGeneration
187}
188
189func (t *Transport) publishLinkState(state ports.LinkState, now time.Time, err error, generation uint64) {
190	t.linkEventMu.Lock()
191	defer t.linkEventMu.Unlock()
192	t.mu.Lock()
193	if t.linkState != state || t.linkStateGeneration != generation {
194		t.mu.Unlock()
195		return
196	}
197	event := ports.LinkEvent{State: state, At: now, Err: err}
198	select {
199	case t.linkEvents <- event:
200	default:
201	}
202	t.mu.Unlock()
203	t.emitDiagnostic()
204}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close