ui_fence.go RawHistoryFolder

1package daemon
2
3import "time"
4
5const uiFenceTimeout = 30 * time.Second
6
7// pendingUIFence is owned by coordinator.mu. Its cancellation channel releases
8// its one lifecycle-owned timer without invoking clock methods under that lock.
9type pendingUIFence struct {
10	actionID  uint64
11	threshold uint64
12	retiring  bool
13	cancel    chan struct{}
14	canceled  bool
15}
16
17func (p *pendingUIFence) cancelLocked() {
18	if !p.canceled {
19		p.canceled = true
20		close(p.cancel)
21	}
22}
23
24func (lease *attachmentLease) cancelUIFenceLocked() {
25	if p := lease.pendingUIFence; p != nil {
26		p.cancelLocked()
27		lease.pendingUIFence = nil
28	}
29}
30
31// registerUIFence reserves the capture threshold and urgent invalidation in one
32// transaction. Dispatch must not inherit timer fallbacks that synchronously
33// render: an ACK later on the same receive loop may be needed to make progress.
34func (c *renderCoordinator) registerUIFence(lease *attachmentLease, actionID uint64, expired func(uint64)) bool {
35	c.mu.Lock()
36	defer c.mu.Unlock()
37	if !c.leaseCurrentLocked(lease, true) || actionID == 0 || lease.pendingUIFence != nil || lease.requestedUIFence == ^uint64(0) {
38		return false
39	}
40	reservation, ok := c.reserveInvalidationForLeaseAtResizeEpochLocked(lease.attachment, lease, 0, renderInvalidation{
41		class: invalidateUrgent, producer: "ui-fence",
42	})
43	if !ok {
44		return false
45	}
46	lease.requestedUIFence++
47	pending := &pendingUIFence{actionID: actionID, threshold: lease.requestedUIFence, cancel: make(chan struct{})}
48	lease.pendingUIFence = pending
49	c.supervisor.startTaskLocked(reservation.finish)
50	if clock := c.opts.clock; clock != nil {
51		c.supervisor.startTaskLocked(func() {
52			timer := clock.NewTimer(uiFenceTimeout)
53			defer timer.Stop()
54			select {
55			case <-pending.cancel:
56				return
57			case <-timer.C():
58			}
59			c.mu.Lock()
60			current := c.leaseCurrentLocked(lease, true) && lease.pendingUIFence == pending && !pending.retiring
61			if current {
62				lease.cancelUIFenceLocked()
63			}
64			c.mu.Unlock()
65			if current && expired != nil {
66				expired(actionID)
67			}
68		})
69	}
70	return true
71}
72
73// captureUIFence is sampled after send/capacity admission and before any render
74// capture. It must never be called while holding pane ownership.
75func (c *renderCoordinator) captureUIFence(lease *attachmentLease) uint64 {
76	c.mu.Lock()
77	defer c.mu.Unlock()
78	if !c.leaseCurrentLocked(lease, true) {
79		return 0
80	}
81	return lease.requestedUIFence
82}
83
84func (c *renderCoordinator) needsUIFence(lease *attachmentLease, captured uint64) bool {
85	c.mu.Lock()
86	defer c.mu.Unlock()
87	return c.uiFenceEligibleLocked(lease, captured)
88}
89
90func (c *renderCoordinator) uiFenceEligibleLocked(lease *attachmentLease, captured uint64) bool {
91	if !c.leaseCurrentLocked(lease, true) {
92		return false
93	}
94	p := lease.pendingUIFence
95	return p != nil && !p.retiring && p.threshold <= captured
96}
97
98// retireUIFence selects a single sender only after a successful publication.
99// The slot stays occupied until that sender's ordered send succeeds.
100func (c *renderCoordinator) retireUIFence(lease *attachmentLease, captured uint64) *pendingUIFence {
101	c.mu.Lock()
102	defer c.mu.Unlock()
103	if !c.uiFenceEligibleLocked(lease, captured) {
104		return nil
105	}
106	p := lease.pendingUIFence
107	p.retiring = true
108	p.cancelLocked()
109	return p
110}
111
112func (c *renderCoordinator) finishUIFence(lease *attachmentLease, pending *pendingUIFence) {
113	c.mu.Lock()
114	defer c.mu.Unlock()
115	if c.leaseCurrentLocked(lease, true) && lease.pendingUIFence == pending && pending.retiring {
116		lease.cancelUIFenceLocked()
117	}
118}
119
120// failUIFence releases a fence selected for a receipt that could not be sent.
121// Retirement already canceled its expiry timer, so leaving the pending fence in
122// place would permanently occupy the attachment's single fence slot.
123func (c *renderCoordinator) failUIFence(lease *attachmentLease, pending *pendingUIFence) {
124	c.finishUIFence(lease, pending)
125}

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