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}