supervisor_navigation.go RawHistoryFolder
1package client
2
3import (
4 "github.com/bnema/vev/internal/ports"
5 "github.com/bnema/vev/internal/protocol"
6)
7
8// navigationState is the supervisor's run-goroutine navigation bookkeeping:
9// the adopted connection's publication wake, the swap and in-place choices
10// still waiting on a live attachment, and the client route ledger. It is only
11// touched from the run goroutine, so it has no mutex. The zero value is ready.
12type navigationState struct {
13 // readySub is the adopted connection's subscription while the ready phase
14 // runs, so the picker overlay over a live attachment keeps folding broker
15 // publications.
16 readySub ports.BrokerSubscription
17 // pendingSwap is the request the picker overlay or a daemon navigation
18 // committed to another target while an attachment was live. Consumed by
19 // settleAttachment.
20 pendingSwap *pickerAttachmentTarget
21 // pendingInPlace is the choice the live attachment is switching to in
22 // place on its own daemon. A refusal turns it into pendingSwap. Cleared
23 // when the attachment settles.
24 pendingInPlace *pendingInPlace
25 // inPlaceSeq numbers in-place choices so a late outcome for an older one
26 // is never applied to a newer one.
27 inPlaceSeq uint64
28 // routes is the client route ledger published to the serving daemon;
29 // routesSent is the attachment that received its latest snapshot.
30 routes *routeLedger
31 routesSent AttachmentToken
32}
33
34// brokerChanged is the adopted connection's publication wake, or nil.
35func (n *navigationState) brokerChanged() <-chan struct{} {
36 if supervisorNil(n.readySub) {
37 return nil
38 }
39 return n.readySub.Changed()
40}
41
42// takeSwap returns and clears the committed swap target.
43func (n *navigationState) takeSwap() *pickerAttachmentTarget {
44 swap := n.pendingSwap
45 n.pendingSwap = nil
46 return swap
47}
48
49// switchingInPlaceTo reports whether an in-place switch to target is in flight.
50func (n *navigationState) switchingInPlaceTo(target protocol.ExactSessionTarget) bool {
51 return n.pendingInPlace != nil && n.pendingInPlace.target.request.Target == target
52}
53
54// inPlaceIdle reports whether no in-place switch is in flight.
55func (n *navigationState) inPlaceIdle() bool { return n.pendingInPlace == nil }
56
57// nextInPlaceSeq numbers one new in-place choice.
58func (n *navigationState) nextInPlaceSeq() uint64 {
59 n.inPlaceSeq++
60 return n.inPlaceSeq
61}
62
63// startInPlace records the in-place switch numbered seq as in flight.
64func (n *navigationState) startInPlace(seq uint64, target pickerAttachmentTarget) {
65 n.pendingInPlace = &pendingInPlace{seq: seq, target: target}
66}
67
68// clearInPlace forgets any in-place switch in flight.
69func (n *navigationState) clearInPlace() { n.pendingInPlace = nil }
70
71// takeInPlace returns and clears the in-flight in-place switch when it is the
72// one numbered seq; an outcome for an older choice returns nil and changes
73// nothing.
74func (n *navigationState) takeInPlace(seq uint64) *pendingInPlace {
75 pending := n.pendingInPlace
76 if pending == nil || pending.seq != seq {
77 return nil
78 }
79 n.pendingInPlace = nil
80 return pending
81}
82
83// buildRoutes rebuilds the route ledger over the broker snapshot, creating it
84// on first use.
85func (n *navigationState) buildRoutes(snapshot ports.BrokerSnapshot, active routeActive) (protocol.RecentRouteSnapshot, bool) {
86 if n.routes == nil {
87 n.routes = newRouteLedger()
88 }
89 return n.routes.build(snapshot, active)
90}
91
92// resolveRoute resolves one route reference against the ledger.
93func (n *navigationState) resolveRoute(ref protocol.RouteRef) (routeLedgerTarget, bool) {
94 if n.routes == nil || ref.IsZero() {
95 return routeLedgerTarget{}, false
96 }
97 return n.routes.resolve(ref)
98}