1package daemon
2
3// paneOwner is an immutable routing snapshot for one published pane. A zero
4// floatingSlotGeneration identifies a tiled pane; installed floating panes
5// carry the tab slot generation that owns them.
6type paneOwner struct {
7 session *session
8 tab *tab
9 generation uint64
10 floatingSlotGeneration uint64
11}
12
13// paneEffectLease binds an effect to the exact immutable owner generation that
14// existed while the pane was parsed or otherwise inspected under pane.mu.
15type paneEffectLease struct {
16 pane *pane
17 owner *paneOwner
18}
19
20// Current reports whether the lease still names the published pane owner. For
21// floating panes it also verifies that the same tab slot and slot generation
22// remain installed. Callers must not hold pane.mu or tab.mu.
23func (l paneEffectLease) Current() bool {
24 return l.pane != nil && l.pane.ownerLeaseCurrent(l)
25}
26
27// ownerSnapshot returns the immutable owner visible to lock-free routing
28// readers. Ownership publication itself is serialized under pane.mu.
29func (p *pane) ownerSnapshot() *paneOwner {
30 if p == nil {
31 return nil
32 }
33 return p.owner.Load()
34}
35
36// effectLeaseLocked captures the current owner while pane.mu linearizes VT
37// parsing against transfer. The caller must hold pane.mu.
38func (p *pane) effectLeaseLocked() paneEffectLease {
39 if p == nil {
40 return paneEffectLease{}
41 }
42 return paneEffectLease{pane: p, owner: p.owner.Load()}
43}
44
45// effectLease captures an owner generation outside an existing pane critical
46// section. Callers use effectLeaseLocked when parsing already owns pane.mu.
47func (p *pane) effectLease() paneEffectLease {
48 if p == nil {
49 return paneEffectLease{}
50 }
51 p.mu.Lock()
52 lease := p.effectLeaseLocked()
53 p.mu.Unlock()
54 return lease
55}
56
57// paneEffectSessionName snapshots log metadata only while the lease remains
58// current. Holding session.mu across the final pointer check linearizes the
59// snapshot with owner publication, whose move commit also owns the session.
60func paneEffectSessionName(lease paneEffectLease) (string, bool) {
61 if lease.pane == nil || lease.owner == nil || lease.owner.session == nil || !lease.Current() {
62 return "", false
63 }
64 lease.owner.session.mu.Lock()
65 current := lease.pane.owner.Load() == lease.owner
66 name := lease.owner.session.name
67 lease.owner.session.mu.Unlock()
68 return name, current
69}
70
71// publishOwnerLocked publishes a new immutable owner generation. The caller
72// must hold pane.mu, and must also hold tab.mu when publishing a floating owner.
73func (p *pane) publishOwnerLocked(sess *session, tb *tab, floatingSlotGeneration uint64) (paneEffectLease, paneEffectLease) {
74 if p == nil {
75 return paneEffectLease{}, paneEffectLease{}
76 }
77 old := paneEffectLease{pane: p, owner: p.owner.Load()}
78 p.ownerGeneration++
79 next := &paneOwner{
80 session: sess,
81 tab: tb,
82 generation: p.ownerGeneration,
83 floatingSlotGeneration: floatingSlotGeneration,
84 }
85 p.owner.Store(next)
86 return old, paneEffectLease{pane: p, owner: next}
87}
88
89// clearOwnerLocked revokes the published owner during terminal teardown. The
90// caller must hold pane.mu. The returned lease identifies the retired owner.
91func (p *pane) clearOwnerLocked() paneEffectLease {
92 if p == nil {
93 return paneEffectLease{}
94 }
95 old := paneEffectLease{pane: p, owner: p.owner.Load()}
96 if old.owner != nil {
97 p.ownerGeneration++
98 p.owner.Store(nil)
99 }
100 return old
101}
102
103func (p *pane) ownerLeaseCurrent(lease paneEffectLease) bool {
104 if p == nil || lease.pane != p || lease.owner == nil || p.owner.Load() != lease.owner {
105 return false
106 }
107 owner := lease.owner
108 if owner.session == nil || owner.tab == nil {
109 return false
110 }
111 if owner.floatingSlotGeneration == 0 {
112 return true
113 }
114 owner.tab.mu.Lock()
115 current := owner.tab.floating.pane == p &&
116 owner.tab.floating.generation == owner.floatingSlotGeneration &&
117 (owner.tab.floating.state == floatingHidden || owner.tab.floating.state == floatingVisible)
118 owner.tab.mu.Unlock()
119 return current && p.owner.Load() == owner
120}
121
122func publishPaneOwner(p *pane, sess *session, tb *tab, floatingSlotGeneration uint64) paneEffectLease {
123 if p == nil {
124 return paneEffectLease{}
125 }
126 p.mu.Lock()
127 _, lease := p.publishOwnerLocked(sess, tb, floatingSlotGeneration)
128 p.mu.Unlock()
129 return lease
130}
131
132// publishTiledPaneOwners initializes every pane in an unpublished tab before
133// that tab is made visible or any reader starts.
134func publishTiledPaneOwners(sess *session, tb *tab) {
135 if sess == nil || tb == nil {
136 return
137 }
138 tb.mu.Lock()
139 for _, p := range tb.panes {
140 publishPaneOwner(p, sess, tb, 0)
141 }
142 tb.mu.Unlock()
143}