1// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package runtime
6
7import "unsafe"
8
9// FIXME: Improve network poller for hurd.
10// This is based on the former libgo/runtime/netpoll_select.c implementation
11// except that it uses poll instead of select and is written in Go.
12// It's also based on Solaris implementation for the arming mechanisms
13// Inspiration was also taken from netpoll_aix.go and netpoll_solaris.go
14
15//From /usr/include/x86_64-linux-gnu/sys/poll.h
16//go:noescape
17//extern poll
18func libc_poll(pfds *pollfd, nfds int32, timeout int32) int32
19
20//go:noescape
21//extern pipe2
22func libc_pipe2(fd *int32, flags int32) int32
23
24//pollfd represents the poll structure for GNU/Hurd operating system.
25type pollfd struct {
26	fd      int32 // File descriptor to poll.
27	events  int16 // Types of events poller cares about.
28	revents int16 // Types of events that actually occurred.
29}
30
31//From /usr/include/i386-gnu/bits/poll.h
32const _POLLIN = 01    // There is data to read.
33const _POLLPRI = 02   // There is urgent data to read.
34const _POLLOUT = 04   // Writing now will not block.
35const _POLLERR = 010  // Error condition.
36const _POLLHUP = 020  // Hung up.
37const _POLLNVAL = 040 // Invalid polling request.
38
39var (
40	pfds           []pollfd
41	pds            []*pollDesc
42	mtxpoll        mutex
43	mtxset         mutex
44	rdwake         int32
45	wrwake         int32
46	pendingUpdates int32
47)
48
49const pollVerbose = false
50
51func netpollinit() {
52	var p [2]int32
53
54	// Create the pipe we use to wakeup poll.
55	if err := libc_pipe2(&p[0], _O_CLOEXEC|_O_NONBLOCK); err < 0 {
56		throw("runtime:netpollinit(): failed to create pipe2")
57	}
58	rdwake = p[0]
59	wrwake = p[1]
60
61	// Pre-allocate array of pollfd structures for poll.
62	if pollVerbose {
63		println("*** allocating")
64	}
65	pfds = make([]pollfd, 1, 128)
66	if pollVerbose {
67		println("*** allocating done", &pfds[0])
68	}
69
70	// Poll the read side of the pipe.
71	pfds[0].fd = int32(rdwake)
72	pfds[0].events = int16(_POLLIN)
73	pfds[0].revents = int16(0)
74
75	pds = make([]*pollDesc, 1, 128)
76	// Checks for pd != nil are made in netpoll.
77	pds[0] = nil
78}
79
80func netpolldescriptor() uintptr {
81	// Both fds must be returned.
82	if rdwake > 0xFFFF || wrwake > 0xFFFF {
83		throw("netpolldescriptor: invalid fd number")
84	}
85	return uintptr(rdwake<<16 | wrwake)
86}
87
88func netpollIsPollDescriptor(fd uintptr) bool {
89	return fd == uintptr(rdwake) || fd == uintptr(wrwake)
90}
91
92// netpollwakeup writes on wrwake to wakeup poll before any changes.
93func netpollwakeup() {
94	if pendingUpdates == 0 {
95		pendingUpdates = 1
96		if pollVerbose {
97			println("*** writing 1 byte")
98		}
99		b := [1]byte{0}
100		write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1)
101	}
102}
103
104func netpollopen(fd uintptr, pd *pollDesc) int32 {
105	if pollVerbose {
106		println("*** netpollopen", fd)
107	}
108	lock(&mtxpoll)
109	netpollwakeup()
110
111	lock(&mtxset)
112	unlock(&mtxpoll)
113
114	pd.user = uint32(len(pfds))
115	pfds = append(pfds, pollfd{fd: int32(fd)})
116	pds = append(pds, pd)
117	unlock(&mtxset)
118	return 0
119}
120
121func netpollclose(fd uintptr) int32 {
122	if pollVerbose {
123		println("*** netpollclose", fd)
124	}
125	lock(&mtxpoll)
126	netpollwakeup()
127
128	lock(&mtxset)
129	unlock(&mtxpoll)
130
131	for i := 0; i < len(pfds); i++ {
132		if pfds[i].fd == int32(fd) {
133			pfds[i] = pfds[len(pfds)-1]
134			pfds = pfds[:len(pfds)-1]
135
136			pds[i] = pds[len(pds)-1]
137			pds[i].user = uint32(i)
138			pds = pds[:len(pds)-1]
139			break
140		}
141	}
142	unlock(&mtxset)
143	return 0
144}
145
146func netpollarm(pd *pollDesc, mode int) {
147	if pollVerbose {
148		println("*** netpollarm", pd.fd, mode)
149	}
150	lock(&mtxpoll)
151	netpollwakeup()
152
153	lock(&mtxset)
154	unlock(&mtxpoll)
155
156	switch mode {
157	case 'r':
158		pfds[pd.user].events |= _POLLIN
159	case 'w':
160		pfds[pd.user].events |= _POLLOUT
161	}
162	unlock(&mtxset)
163}
164
165// netpollBreak interrupts an epollwait.
166func netpollBreak() {
167	netpollwakeup()
168}
169
170// netpoll checks for ready network connections.
171// Returns list of goroutines that become runnable.
172// delay < 0: blocks indefinitely
173// delay == 0: does not block, just polls
174// delay > 0: block for up to that many nanoseconds
175//go:nowritebarrierrec
176func netpoll(delay int64) gList {
177	timeout := int32(0)
178	if delay < 0 {
179		timeout = 0
180	} else if delay == 0 {
181		// TODO: call poll with timeout == 0
182		return gList{}
183	} else if delay < 1e6 {
184		timeout = 1
185	} else if delay < 1e15 {
186		timeout = int32(delay / 1e6)
187	} else {
188		// An arbitrary cap on how long to wait for a timer.
189		// 1e9 ms == ~11.5 days.
190		timeout = 1e9
191	}
192retry:
193	lock(&mtxpoll)
194	lock(&mtxset)
195	pendingUpdates = 0
196	unlock(&mtxpoll)
197
198	n := libc_poll(&pfds[0], int32(len(pfds)), timeout)
199	if n < 0 {
200		e := errno()
201		if e != _EINTR {
202			println("errno=", e, " len(pfds)=", len(pfds))
203			throw("poll failed")
204		}
205		unlock(&mtxset)
206		// If a timed sleep was interrupted, just return to
207		// recalculate how long we should sleep now.
208		if timeout > 0 {
209			return gList{}
210		}
211		goto retry
212	}
213	// Check if some descriptors need to be changed
214	if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
215		if delay != 0 {
216			// A netpollwakeup could be picked up by a
217			// non-blocking poll. Only clear the wakeup
218			// if blocking.
219			var b [1]byte
220			for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 {
221			}
222		}
223		// Still look at the other fds even if the mode may have
224		// changed, as netpollBreak might have been called.
225		n--
226	}
227	var toRun gList
228	for i := 1; i < len(pfds) && n > 0; i++ {
229		pfd := &pfds[i]
230
231		var mode int32
232		if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
233			mode += 'r'
234			pfd.events &= ^_POLLIN
235		}
236		if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 {
237			mode += 'w'
238			pfd.events &= ^_POLLOUT
239		}
240		if mode != 0 {
241			pds[i].everr = false
242			if pfd.revents == _POLLERR {
243				pds[i].everr = true
244			}
245			netpollready(&toRun, pds[i], mode)
246			n--
247		}
248	}
249	unlock(&mtxset)
250	return toRun
251}
252