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
88// netpollwakeup writes on wrwake to wakeup poll before any changes.
89func netpollwakeup() {
90	if pendingUpdates == 0 {
91		pendingUpdates = 1
92		if pollVerbose {
93			println("*** writing 1 byte")
94		}
95		b := [1]byte{0}
96		write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1)
97	}
98}
99
100func netpollopen(fd uintptr, pd *pollDesc) int32 {
101	if pollVerbose {
102		println("*** netpollopen", fd)
103	}
104	lock(&mtxpoll)
105	netpollwakeup()
106
107	lock(&mtxset)
108	unlock(&mtxpoll)
109
110	pd.user = uint32(len(pfds))
111	pfds = append(pfds, pollfd{fd: int32(fd)})
112	pds = append(pds, pd)
113	unlock(&mtxset)
114	return 0
115}
116
117func netpollclose(fd uintptr) int32 {
118	if pollVerbose {
119		println("*** netpollclose", fd)
120	}
121	lock(&mtxpoll)
122	netpollwakeup()
123
124	lock(&mtxset)
125	unlock(&mtxpoll)
126
127	for i := 0; i < len(pfds); i++ {
128		if pfds[i].fd == int32(fd) {
129			pfds[i] = pfds[len(pfds)-1]
130			pfds = pfds[:len(pfds)-1]
131
132			pds[i] = pds[len(pds)-1]
133			pds[i].user = uint32(i)
134			pds = pds[:len(pds)-1]
135			break
136		}
137	}
138	unlock(&mtxset)
139	return 0
140}
141
142func netpollarm(pd *pollDesc, mode int) {
143	if pollVerbose {
144		println("*** netpollarm", pd.fd, mode)
145	}
146	lock(&mtxpoll)
147	netpollwakeup()
148
149	lock(&mtxset)
150	unlock(&mtxpoll)
151
152	switch mode {
153	case 'r':
154		pfds[pd.user].events |= _POLLIN
155	case 'w':
156		pfds[pd.user].events |= _POLLOUT
157	}
158	unlock(&mtxset)
159}
160
161// polls for ready network connections
162// returns list of goroutines that become runnable
163//go:nowritebarrierrec
164func netpoll(block bool) gList {
165	timeout := int32(0)
166	if !block {
167		timeout = 0
168		return gList{}
169	}
170	if pollVerbose {
171		println("*** netpoll", block)
172	}
173retry:
174	lock(&mtxpoll)
175	lock(&mtxset)
176	pendingUpdates = 0
177	unlock(&mtxpoll)
178
179	if pollVerbose {
180		println("*** netpoll before poll")
181	}
182	n := libc_poll(&pfds[0], int32(len(pfds)), timeout)
183	if pollVerbose {
184		println("*** netpoll after poll", n)
185	}
186	if n < 0 {
187		e := errno()
188		if e != _EINTR {
189			println("errno=", e, " len(pfds)=", len(pfds))
190			throw("poll failed")
191		}
192		if pollVerbose {
193			println("*** poll failed")
194		}
195		unlock(&mtxset)
196		goto retry
197	}
198	// Check if some descriptors need to be changed
199	if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
200		var b [1]byte
201		for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 {
202			if pollVerbose {
203				println("*** read 1 byte from pipe")
204			}
205		}
206		// Do not look at the other fds in this case as the mode may have changed
207		// XXX only additions of flags are made, so maybe it is ok
208		unlock(&mtxset)
209		goto retry
210	}
211	var toRun gList
212	for i := 0; i < len(pfds) && n > 0; i++ {
213		pfd := &pfds[i]
214
215		var mode int32
216		if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
217			mode += 'r'
218			pfd.events &= ^_POLLIN
219		}
220		if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 {
221			mode += 'w'
222			pfd.events &= ^_POLLOUT
223		}
224		if mode != 0 {
225			if pollVerbose {
226				println("*** netpollready i=", i, "revents=", pfd.revents, "events=", pfd.events, "pd=", pds[i])
227			}
228			netpollready(&toRun, pds[i], mode)
229			n--
230		}
231	}
232	unlock(&mtxset)
233	if block && toRun.empty() {
234		goto retry
235	}
236	if pollVerbose {
237		println("*** netpoll returning end")
238	}
239	return toRun
240}
241