detached 602baa7

xid.go RawHistoryFolder

1package xturbo
2
3import (
4	"errors"
5	"time"
6)
7
8// errXIDsExhausted reports that the server has no resource IDs left for this
9// client: XC-MISC GetXIDRange returned an empty range, or one outside this
10// client's base and mask.
11var errXIDsExhausted = errors.New("xturbo: server has no resource IDs left")
12
13const (
14	// xidFreeCap bounds the FreeXID list; IDs freed beyond it are not reused.
15	xidFreeCap = 256
16	// xidLowMark is the remaining-ID count below which XIDsLow reports true.
17	xidLowMark = 4096
18)
19
20// xidRange is a run of count IDs base|next, base|next+inc, … (inc = lowest
21// bit of the mask).
22type xidRange struct {
23	next, left uint32
24}
25
26// initXIDs starts allocation over the setup range: base|0, base|inc, …
27// up to base|mask, in libxcb's order.
28func (c *Conn) initXIDs() {
29	inc := c.xidMask & -c.xidMask
30	if inc == 0 {
31		return
32	}
33	c.xids = xidRange{0, c.xidMask/inc + 1}
34}
35
36// NewXID returns a fresh resource ID for this client. IDs released with
37// FreeXID are reused first, then the current range, then a range fetched
38// ahead by PrefetchXIDs. When all are empty, outside a Handler callback, one
39// blocking XC-MISC GetXIDRange round trip (bounded like Sync) refills it;
40// packets read meanwhile go to the last Handler given to Dispatch. Inside a
41// callback that refill is impossible and NewXID fails instead, so an event
42// loop allocating from callbacks calls PrefetchXIDs whenever XIDsLow reports
43// true (the XWM does it once per loop iteration). It also fails when XC-MISC
44// is absent or the server has no IDs left.
45func (c *Conn) NewXID() (uint32, error) {
46	if c.xidFreeN > 0 {
47		c.xidFreeN--
48		return c.xidFree[c.xidFreeN], nil
49	}
50	if c.xids.left == 0 {
51		if c.xidPend.left == 0 {
52			if err := c.refillXIDs(); err != nil {
53				return 0, err
54			}
55		}
56		c.xids, c.xidPend = c.xidPend, xidRange{}
57	}
58	id := c.xidBase | c.xids.next
59	c.xids.next += c.xidMask & -c.xidMask
60	c.xids.left--
61	return id, nil
62}
63
64// FreeXID gives back id for reuse by a later NewXID. id must come from
65// NewXID on this Conn and be freed once, after the caller destroyed its
66// resource (the destroy request may still be queued, it precedes any reuse
67// on the wire). IDs outside this client's range, 0, and IDs beyond the free
68// list capacity are ignored; freeing an ID twice hands it out twice.
69func (c *Conn) FreeXID(id uint32) {
70	if id&^c.xidMask != c.xidBase || c.xidFreeN == xidFreeCap {
71		return
72	}
73	c.xidFree[c.xidFreeN] = id
74	c.xidFreeN++
75}
76
77// XIDsLow reports that fewer than 4096 IDs remain in the current range with
78// an empty free list, and that PrefetchXIDs would help: XC-MISC is present,
79// no prefetched range is waiting, no prefetch is in flight and the last one
80// did not report the server out of IDs (after that only NewXID's blocking
81// refill retries).
82func (c *Conn) XIDsLow() bool {
83	if c.xidFreeN > 0 || c.xidPend.left > 0 || c.xidNone || c.xidInFlight() || !c.ext.Info(ExtXCMisc).Present {
84		return false
85	}
86	return c.xids.left < xidLowMark
87}
88
89// PrefetchXIDs queues an XC-MISC GetXIDRange without waiting; its reply,
90// read by a later Dispatch, becomes the range NewXID switches to when the
91// current one runs out. Callers flush as usual. It does nothing while a
92// prefetched range is unused or a prefetch is in flight, and may be called
93// from a Handler callback. It fails with ErrExtMissing without XC-MISC,
94// ErrRingFull when the next request's Expect slot is taken, and with the
95// Buf's sticky error.
96func (c *Conn) PrefetchXIDs() error {
97	if c.fd < 0 {
98		return ErrClosed
99	}
100	if !c.ext.Info(ExtXCMisc).Present {
101		return ErrExtMissing
102	}
103	if c.xidPend.left > 0 || c.xidInFlight() {
104		return nil
105	}
106	return c.queueXIDRange()
107}
108
109// queueXIDRange queues a GetXIDRange registered with kindXIDRange.
110func (c *Conn) queueXIDRange() error {
111	if c.ring[c.buf.next&ringMask].seq != 0 {
112		return ErrRingFull
113	}
114	seq := encodeExtRequest(c.buf, ExtXCMisc, minorGetXIDRange)
115	if seq == 0 {
116		return c.buf.Err()
117	}
118	if err := c.expect(seq, kindXIDRange, [4]uint32{}); err != nil {
119		return err
120	}
121	c.xidReq = seq
122	c.xidNone, c.xidFailed = false, false
123	return nil
124}
125
126// xidInFlight reports a GetXIDRange whose reply or error has not arrived.
127func (c *Conn) xidInFlight() bool {
128	return c.xidReq != 0 && c.ring[c.xidReq&ringMask].seq == c.xidReq
129}
130
131// gotXIDRange records a GetXIDRange reply (from consumeReply).
132func (c *Conn) gotXIDRange(start, count uint32) {
133	if !c.validXIDRange(start, count) {
134		c.xidNone = true
135		return
136	}
137	c.xidPend = xidRange{start & c.xidMask, count}
138}
139
140// validXIDRange accepts a GetXIDRange answer only when every ID of it lies in
141// this client's base and mask. The server answers start 0 (count 0 or 1)
142// when it has none.
143func (c *Conn) validXIDRange(start, count uint32) bool {
144	inc := uint64(c.xidMask & -c.xidMask)
145	off := uint64(start & c.xidMask)
146	return inc != 0 && count != 0 && start != 0 &&
147		start&^c.xidMask == c.xidBase && off%inc == 0 &&
148		off+uint64(count-1)*inc <= uint64(c.xidMask)
149}
150
151// refillXIDs blocks until a prefetched range is available: it waits for the
152// prefetch in flight, or queues one.
153func (c *Conn) refillXIDs() error {
154	if c.dispatching {
155		return errReentrantDispatch
156	}
157	if c.fd < 0 {
158		return ErrClosed
159	}
160	if !c.ext.Info(ExtXCMisc).Present {
161		return ErrExtMissing
162	}
163	deadline := time.Now().Add(c.timeout)
164	if !c.xidInFlight() {
165		if err := c.waitSlot(deadline); err != nil {
166			return err
167		}
168		if err := c.queueXIDRange(); err != nil {
169			return err
170		}
171	}
172	if err := c.waitUntil(c.h, deadline); err != nil {
173		return err
174	}
175	switch {
176	case c.xidFailed:
177		e := c.xidErr
178		return &e
179	case c.xidPend.left == 0:
180		return errXIDsExhausted
181	}
182	return nil
183}
184
185// waitSlot makes sure the ring slot of the next request is free, so a
186// reserved registration can always be made for it: when an earlier request
187// still holds it, it waits for every outstanding request (fenced, so even a
188// registered request without reply completes).
189func (c *Conn) waitSlot(deadline time.Time) error {
190	if c.ring[c.buf.next&ringMask].seq == 0 {
191		return nil
192	}
193	return c.waitUntil(c.h, deadline)
194}

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 1

Tags 1

↑↓ moveenter openesc close