poller.go RawHistoryFolder

1// Package fdwait parks descriptor waits in Go's netpoller.
2package fdwait
3
4import (
5	"errors"
6	"os"
7	"syscall"
8	"time"
9
10	"golang.org/x/sys/unix"
11)
12
13// Poller owns an epoll descriptor and is used by one goroutine.
14// Watched descriptors remain owned by the caller.
15type Poller struct {
16	file *os.File
17	raw  syscall.RawConn
18}
19
20func New() (*Poller, error) {
21	fd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC)
22	if err != nil {
23		return nil, err
24	}
25	if err = unix.SetNonblock(fd, true); err != nil {
26		unix.Close(fd)
27		return nil, err
28	}
29	f := os.NewFile(uintptr(fd), "fdwait")
30	raw, err := f.SyscallConn()
31	if err != nil {
32		f.Close()
33		return nil, err
34	}
35	return &Poller{file: f, raw: raw}, nil
36}
37func (p *Poller) Close() error { return p.file.Close() }
38
39// Poll waits for readable/error events. It never consumes caller events.
40// Each call explicitly removes its epoll registrations before the caller can
41// close or recycle watched fds. Relying on close to remove them is unsafe:
42// another fd or a kernel fence may retain the open file description.
43func (p *Poller) Poll(fds []unix.PollFd, timeout int) (int, error) {
44	var ctlErr error
45	added := 0
46	err := p.raw.Control(func(ep uintptr) {
47		for _, f := range fds {
48			if f.Fd < 0 {
49				added++
50				continue
51			}
52			ev := unix.EpollEvent{Events: unix.EPOLLIN, Fd: f.Fd}
53			if e := unix.EpollCtl(int(ep), unix.EPOLL_CTL_ADD, int(f.Fd), &ev); e != nil {
54				ctlErr = e
55				return
56			}
57			added++
58		}
59	})
60	defer p.raw.Control(func(ep uintptr) {
61		for _, f := range fds[:added] {
62			if f.Fd >= 0 {
63				unix.EpollCtl(int(ep), unix.EPOLL_CTL_DEL, int(f.Fd), nil)
64			}
65		}
66	})
67	if err != nil {
68		return 0, err
69	}
70	if ctlErr != nil {
71		return 0, ctlErr
72	}
73	if timeout >= 0 {
74		err = p.file.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Millisecond))
75	} else {
76		err = p.file.SetReadDeadline(time.Time{})
77	}
78	if err != nil {
79		return 0, err
80	}
81	var n int
82	var pollErr error
83	err = p.raw.Read(func(_ uintptr) bool {
84		n, pollErr = unix.Poll(fds, 0)
85		return n > 0 || (pollErr != nil && !errors.Is(pollErr, unix.EINTR))
86	})
87	if errors.Is(err, os.ErrDeadlineExceeded) {
88		return 0, nil
89	}
90	if err != nil {
91		return 0, err
92	}
93	return n, pollErr
94}

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 23

Tags 10

↑↓ moveenter openesc close