render_resize_deadline.go RawHistoryFolder

1package daemon
2
3import (
4	"github.com/bnema/vev/internal/domain"
5	"github.com/bnema/vev/internal/ports"
6)
7
8const maxResizeRetryAttempts = 3
9
10// Resize deadlines and retries retain coordinator-owned attachment, epoch, and
11// token validation at every callback effect boundary.
12func (c *renderCoordinator) recordResizeRequest(size domain.Size, source *attachedClient) uint64 {
13	return c.recordResizeRequestForLeaseWithClaim(size, source, c.attachmentLease(source), nil)
14}
15
16func (c *renderCoordinator) recordResizeRequestForLease(size domain.Size, source *attachedClient, lease *attachmentLease) uint64 {
17	return c.recordResizeRequestForLeaseWithClaim(size, source, lease, nil)
18}
19
20func (c *renderCoordinator) recordResizeRequestForLeaseWithClaim(size domain.Size, source *attachedClient, lease *attachmentLease, geometryClaim *sharedPTYClaim) uint64 {
21	c.mu.Lock()
22	if !c.leaseCurrentLocked(lease, false) || lease.attachment != source {
23		c.mu.Unlock()
24		return 0
25	}
26	_, retry := c.retryLane.replaceLocked()
27	c.resize.epoch++
28	c.resize.size, c.resize.source, c.resize.lease = size, source, lease
29	c.resize.geometryClaim = geometryClaim
30	c.resize.retryAttempts = 0
31	epoch := c.resize.epoch
32	c.mu.Unlock()
33	stopDetachedTimer(retry)
34	return epoch
35}
36
37func (c *renderCoordinator) scheduleResizeForLease(size domain.Size, source *attachedClient, lease *attachmentLease, run func(uint64)) uint64 {
38	return c.scheduleResizeForLeaseWithClaim(size, source, lease, nil, run)
39}
40
41func (c *renderCoordinator) scheduleResizeForLeaseWithClaim(size domain.Size, source *attachedClient, lease *attachmentLease, geometryClaim *sharedPTYClaim, run func(uint64)) uint64 {
42	epoch := c.recordResizeRequestForLeaseWithClaim(size, source, lease, geometryClaim)
43	if epoch == 0 {
44		return 0
45	}
46	c.mu.Lock()
47	gen, old := c.resizeLane.replaceLocked()
48	clock := c.opts.clock
49	c.mu.Unlock()
50	stopDetachedTimer(old)
51	if clock == nil {
52		if c.resizeCurrentForLease(epoch, source, lease, false) {
53			run(epoch)
54		}
55		return epoch
56	}
57	timer := clock.NewTimer(minOutputRenderDeadline)
58	timerC := timer.C()
59	if timerC == nil {
60		timer.Stop()
61		if c.resizeCurrentForLease(epoch, source, lease, false) {
62			run(epoch)
63		}
64		return epoch
65	}
66	c.mu.Lock()
67	valid := !c.torndown && c.resizeLane.generation == gen && c.leaseCurrentLocked(lease, false) && c.resize.lease == lease && c.resize.epoch == epoch && c.resize.source == source
68	var token *timerToken
69	if valid {
70		token = c.resizeLane.publishLocked(gen, timer)
71		valid = token != nil
72		if valid {
73			c.supervisor.startLocked(token, timerC, func() {
74				c.mu.Lock()
75				valid := !c.torndown && c.resizeLane.token == token && c.resizeLane.generation == token.generation && c.leaseCurrentLocked(lease, false) && c.resize.lease == lease && c.resize.epoch == epoch && c.resize.source == source
76				if valid {
77					c.resizeLane.clearLocked(token)
78				}
79				c.mu.Unlock()
80				if valid {
81					run(epoch)
82				}
83			})
84		}
85	}
86	c.mu.Unlock()
87	if !valid {
88		timer.Stop()
89	}
90	return epoch
91}
92
93func (c *renderCoordinator) resizeCurrentForLease(epoch uint64, source *attachedClient, lease *attachmentLease, commit bool) bool {
94	c.mu.Lock()
95	defer c.mu.Unlock()
96	if !c.leaseCurrentLocked(lease, false) || lease.attachment != source || c.resize.lease != lease || c.resize.source != source || c.resize.epoch != epoch {
97		return false
98	}
99	if commit {
100		if c.resize.committed >= epoch {
101			return false
102		}
103		c.resize.committed = epoch
104	}
105	return true
106}
107
108type resizeRetryReservation struct {
109	coordinator *renderCoordinator
110	epoch       uint64
111	source      *attachedClient
112	lease       *attachmentLease
113	run         func()
114	generation  uint64
115	old         *timerToken
116	clock       ports.Clock
117}
118
119// reserveResizeRetryForLease publishes the retry-lane generation only. Timer
120// calls are deferred to finish so owner fences protect no external operation.
121func (c *renderCoordinator) reserveResizeRetryForLease(epoch uint64, source *attachedClient, lease *attachmentLease, run func()) (*resizeRetryReservation, bool) {
122	c.mu.Lock()
123	defer c.mu.Unlock()
124	if c.torndown || !c.leaseCurrentLocked(lease, false) || lease.attachment != source || c.resize.lease != lease || c.resize.source != source || c.resize.epoch != epoch || c.resize.committed != epoch || c.resize.retryAttempts >= maxResizeRetryAttempts {
125		return nil, false
126	}
127	c.resize.retryAttempts++
128	gen, old := c.retryLane.replaceLocked()
129	return &resizeRetryReservation{coordinator: c, epoch: epoch, source: source, lease: lease, run: run, generation: gen, old: old, clock: c.opts.clock}, true
130}
131
132func (r *resizeRetryReservation) finish() {
133	if r == nil || r.coordinator == nil {
134		return
135	}
136	c := r.coordinator
137	stopDetachedTimer(r.old)
138	if r.clock == nil {
139		r.run()
140		return
141	}
142	timer := r.clock.NewTimer(minOutputRenderDeadline)
143	timerC := timer.C()
144	if timerC == nil {
145		timer.Stop()
146		return
147	}
148	c.mu.Lock()
149	valid := !c.torndown && c.retryLane.generation == r.generation && c.leaseCurrentLocked(r.lease, false) && c.resize.lease == r.lease && c.resize.epoch == r.epoch && c.resize.committed == r.epoch && c.resize.source == r.source
150	if valid {
151		token := c.retryLane.publishLocked(r.generation, timer)
152		valid = token != nil
153		if valid {
154			c.supervisor.startLocked(token, timerC, func() {
155				c.mu.Lock()
156				valid := !c.torndown && c.retryLane.token == token && c.retryLane.generation == token.generation && c.leaseCurrentLocked(r.lease, false) && c.resize.lease == r.lease && c.resize.epoch == r.epoch && c.resize.committed == r.epoch && c.resize.source == r.source
157				if valid {
158					c.retryLane.clearLocked(token)
159				}
160				c.mu.Unlock()
161				if valid {
162					r.run()
163				}
164			})
165		}
166	}
167	c.mu.Unlock()
168	if !valid {
169		timer.Stop()
170	}
171}
172
173func (c *renderCoordinator) scheduleResizeRetryForLease(epoch uint64, source *attachedClient, lease *attachmentLease, run func()) {
174	reservation, ok := c.reserveResizeRetryForLease(epoch, source, lease, run)
175	if !ok {
176		return
177	}
178	reservation.finish()
179}
180
181func (c *renderCoordinator) resizeRetryAvailableForLease(epoch uint64, source *attachedClient, lease *attachmentLease) bool {
182	c.mu.Lock()
183	defer c.mu.Unlock()
184	return !c.torndown && c.leaseCurrentLocked(lease, false) && lease.attachment == source && c.resize.lease == lease && c.resize.source == source && c.resize.epoch == epoch && c.resize.committed == epoch && c.resize.retryAttempts < maxResizeRetryAttempts
185}
186
187func (c *renderCoordinator) retryCurrentForLease(epoch uint64, source *attachedClient, lease *attachmentLease) bool {
188	c.mu.Lock()
189	defer c.mu.Unlock()
190	return c.leaseCurrentLocked(lease, false) && lease.attachment == source && c.resize.lease == lease && c.resize.source == source && c.resize.epoch == epoch && c.resize.committed == epoch
191}
192
193func (c *renderCoordinator) resizeSnapshot() resizeRequestMetadata {
194	c.mu.Lock()
195	defer c.mu.Unlock()
196	return c.resize
197}

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