attachment_routes.go RawHistoryFolder
1package client
2
3import (
4 "github.com/bnema/vev/internal/domain"
5 "github.com/bnema/vev/internal/protocol"
6)
7
8// Route publication and daemon navigation seams of the attached foreground.
9// The worker owns the stream, so the supervisor reaches the
10// daemon only through these bounded channels: the newest route snapshot
11// replaces an unsent one, and navigation failures are dropped rather than
12// blocking when the worker is gone. In the other direction the worker hands
13// every daemon navigation request to the supervisor, which resolves it
14// through the broker catalogue exactly like a picker commit.
15
16// attachmentReplyCapacity bounds queued navigation failures per foreground.
17const attachmentReplyCapacity = 4
18
19// demandRoutes wakes the supervisor to republish routes for this foreground.
20func (f *attachmentForeground) demandRoutes() {
21 if f == nil || f.host == nil || !f.authority.actionAuthorized(f) {
22 return
23 }
24 select {
25 case f.host.routeDemand <- struct{}{}:
26 default:
27 }
28}
29
30func (f *attachmentForeground) routeSnapshots() <-chan protocol.RecentRouteSnapshot {
31 if f == nil {
32 return nil
33 }
34 return f.routes
35}
36
37func (f *attachmentForeground) navigationReplies() <-chan protocol.ClientMessage {
38 if f == nil {
39 return nil
40 }
41 return f.replies
42}
43
44// requestNavigation hands one daemon navigation request to the supervisor.
45// The newest request replaces one the supervisor has not taken yet.
46func (f *attachmentForeground) requestNavigation(message protocol.ServerMessage) {
47 if f == nil || f.host == nil || !f.authority.actionAuthorized(f) {
48 return
49 }
50 for {
51 select {
52 case f.host.navigations <- message:
53 return
54 default:
55 }
56 select {
57 case <-f.host.navigations:
58 default:
59 }
60 }
61}
62
63// RouteDemands wakes the supervisor when the attached session changed.
64func (h *attachmentHost) RouteDemands() <-chan struct{} {
65 if h == nil {
66 return nil
67 }
68 return h.routeDemand
69}
70
71// Navigations carries the daemon's navigation requests to the supervisor.
72func (h *attachmentHost) Navigations() <-chan protocol.ServerMessage {
73 if h == nil {
74 return nil
75 }
76 return h.navigations
77}
78
79// takeNavigation returns a navigation request left behind by a worker that
80// already settled, if any.
81func (h *attachmentHost) takeNavigation() (protocol.ServerMessage, bool) {
82 if h == nil {
83 return nil, false
84 }
85 select {
86 case message := <-h.navigations:
87 return message, true
88 default:
89 return nil, false
90 }
91}
92
93// publishRoutes hands the newest route snapshot to the current foreground's
94// worker. It reports false when token no longer owns the foreground.
95func (h *attachmentHost) publishRoutes(token AttachmentToken, snapshot protocol.RecentRouteSnapshot) bool {
96 fg := h.currentForeground(token)
97 if fg == nil {
98 return false
99 }
100 for {
101 select {
102 case fg.routes <- snapshot:
103 return true
104 default:
105 }
106 select {
107 case <-fg.routes:
108 default:
109 }
110 }
111}
112
113// replyNavigation queues one navigation failure for the current worker.
114func (h *attachmentHost) replyNavigation(token AttachmentToken, message protocol.ClientMessage) bool {
115 fg := h.currentForeground(token)
116 if fg == nil {
117 return false
118 }
119 select {
120 case fg.replies <- message:
121 return true
122 default:
123 return false
124 }
125}
126
127// inPlaceSwitch is one supervisor-committed session on the daemon already
128// serving the attachment. The worker asks that daemon to move the live
129// attachment there instead of reconnecting. seq identifies this choice, so a
130// result for an older one is never mistaken for it.
131type inPlaceSwitch struct {
132 seq uint64
133 target protocol.ExactSessionTarget
134 tab domain.TabStableID
135}
136
137// inPlaceOutcome is how one in-place switch ended.
138type inPlaceOutcome uint8
139
140const (
141 // inPlaceArrived: the daemon moved the attachment to the target.
142 inPlaceArrived inPlaceOutcome = iota + 1
143 // inPlaceRefused: the daemon kept the source; reconnect to the target.
144 inPlaceRefused
145 // inPlaceSuperseded: a newer navigation replaced this one; drop it.
146 inPlaceSuperseded
147)
148
149// inPlaceResult reports the outcome of the in-place switch seq.
150type inPlaceResult struct {
151 seq uint64
152 outcome inPlaceOutcome
153}
154
155// requestInPlace hands one in-place switch to the current foreground's worker.
156// The newest request replaces an unsent one. It reports false when token no
157// longer owns the foreground.
158func (h *attachmentHost) requestInPlace(token AttachmentToken, request inPlaceSwitch) bool {
159 fg := h.currentForeground(token)
160 if fg == nil {
161 return false
162 }
163 for {
164 select {
165 case fg.inPlace <- request:
166 return true
167 default:
168 }
169 select {
170 case <-fg.inPlace:
171 default:
172 }
173 }
174}
175
176// InPlaceResults carries the outcome of each in-place switch to the
177// supervisor.
178func (h *attachmentHost) InPlaceResults() <-chan inPlaceResult {
179 if h == nil {
180 return nil
181 }
182 return h.inPlaceResults
183}
184
185func (f *attachmentForeground) inPlaceSwitches() <-chan inPlaceSwitch {
186 if f == nil {
187 return nil
188 }
189 return f.inPlace
190}
191
192// reportInPlace hands the outcome of one in-place switch to the supervisor.
193// The newest outcome replaces one the supervisor has not taken yet; it is
194// always the latest choice, the only one the supervisor still waits for.
195func (f *attachmentForeground) reportInPlace(seq uint64, outcome inPlaceOutcome) {
196 if f == nil || f.host == nil || seq == 0 {
197 return
198 }
199 result := inPlaceResult{seq: seq, outcome: outcome}
200 for {
201 select {
202 case f.host.inPlaceResults <- result:
203 return
204 default:
205 }
206 select {
207 case <-f.host.inPlaceResults:
208 default:
209 }
210 }
211}
212
213func (h *attachmentHost) currentForeground(token AttachmentToken) *attachmentForeground {
214 if h == nil {
215 return nil
216 }
217 fg := h.authority.foreground()
218 if fg == nil || fg.token != token {
219 return nil
220 }
221 return fg
222}