1// Copyright 2018 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// This is based on the former libgo/runtime/netpoll_select.c implementation
10// except that it uses poll instead of select and is written in Go.
11// It's also based on Solaris implementation for the arming mechanisms
12
13//go:noescape
14//extern poll
15func libc_poll(pfds *pollfd, npfds uintptr, timeout uintptr) int32
16
17//go:noescape
18//extern pipe
19func libc_pipe(fd *int32) int32
20
21//extern __go_fcntl_uintptr
22func fcntlUintptr(fd, cmd, arg uintptr) (uintptr, uintptr)
23
24func fcntl(fd, cmd int32, arg uintptr) int32 {
25	r, _ := fcntlUintptr(uintptr(fd), uintptr(cmd), arg)
26	return int32(r)
27}
28
29// pollfd represents the poll structure for AIX operating system.
30type pollfd struct {
31	fd      int32
32	events  int16
33	revents int16
34}
35
36const _POLLIN = 0x0001
37const _POLLOUT = 0x0002
38const _POLLHUP = 0x2000
39const _POLLERR = 0x4000
40
41var (
42	pfds           []pollfd
43	pds            []*pollDesc
44	mtxpoll        mutex
45	mtxset         mutex
46	rdwake         int32
47	wrwake         int32
48	pendingUpdates int32
49)
50
51const pollVerbose = false
52
53func netpollinit() {
54	var p [2]int32
55
56	// Create the pipe we use to wakeup poll.
57	if err := libc_pipe(&p[0]); err < 0 {
58		throw("netpollinit: failed to create pipe")
59	}
60	rdwake = p[0]
61	wrwake = p[1]
62
63	fl := uintptr(fcntl(rdwake, _F_GETFL, 0))
64	fcntl(rdwake, _F_SETFL, fl|_O_NONBLOCK)
65	fcntl(rdwake, _F_SETFD, _FD_CLOEXEC)
66
67	fl = uintptr(fcntl(wrwake, _F_GETFL, 0))
68	fcntl(wrwake, _F_SETFL, fl|_O_NONBLOCK)
69	fcntl(wrwake, _F_SETFD, _FD_CLOEXEC)
70
71	// Pre-allocate array of pollfd structures for poll.
72	if pollVerbose {
73		println("*** allocating")
74	}
75	pfds = make([]pollfd, 1, 128)
76	if pollVerbose {
77		println("*** allocating done", &pfds[0])
78	}
79
80	// Poll the read side of the pipe.
81	pfds[0].fd = rdwake
82	pfds[0].events = _POLLIN
83
84	pds = make([]*pollDesc, 1, 128)
85	pds[0] = nil
86}
87
88func netpolldescriptor() uintptr {
89	// Both fd must be returned
90	if rdwake > 0xFFFF || wrwake > 0xFFFF {
91		throw("netpolldescriptor: invalid fd number")
92	}
93	return uintptr(rdwake<<16 | wrwake)
94}
95
96// netpollwakeup writes on wrwake to wakeup poll before any changes.
97func netpollwakeup() {
98	if pendingUpdates == 0 {
99		pendingUpdates = 1
100		if pollVerbose {
101			println("*** writing 1 byte")
102		}
103		b := [1]byte{0}
104		write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1)
105	}
106}
107
108func netpollopen(fd uintptr, pd *pollDesc) int32 {
109	if pollVerbose {
110		println("*** netpollopen", fd)
111	}
112	lock(&mtxpoll)
113	netpollwakeup()
114
115	lock(&mtxset)
116	unlock(&mtxpoll)
117
118	pd.user = uint32(len(pfds))
119	pfds = append(pfds, pollfd{fd: int32(fd)})
120	pds = append(pds, pd)
121	unlock(&mtxset)
122	return 0
123}
124
125func netpollclose(fd uintptr) int32 {
126	if pollVerbose {
127		println("*** netpollclose", fd)
128	}
129	lock(&mtxpoll)
130	netpollwakeup()
131
132	lock(&mtxset)
133	unlock(&mtxpoll)
134
135	for i := 0; i < len(pfds); i++ {
136		if pfds[i].fd == int32(fd) {
137			pfds[i] = pfds[len(pfds)-1]
138			pfds = pfds[:len(pfds)-1]
139
140			pds[i] = pds[len(pds)-1]
141			pds[i].user = uint32(i)
142			pds = pds[:len(pds)-1]
143			break
144		}
145	}
146	unlock(&mtxset)
147	return 0
148}
149
150func netpollarm(pd *pollDesc, mode int) {
151	if pollVerbose {
152		println("*** netpollarm", pd.fd, mode)
153	}
154	lock(&mtxpoll)
155	netpollwakeup()
156
157	lock(&mtxset)
158	unlock(&mtxpoll)
159
160	switch mode {
161	case 'r':
162		pfds[pd.user].events |= _POLLIN
163	case 'w':
164		pfds[pd.user].events |= _POLLOUT
165	}
166	unlock(&mtxset)
167}
168
169//go:nowritebarrierrec
170func netpoll(block bool) gList {
171	timeout := ^uintptr(0)
172	if !block {
173		timeout = 0
174		return gList{}
175	}
176	if pollVerbose {
177		println("*** netpoll", block)
178	}
179retry:
180	lock(&mtxpoll)
181	lock(&mtxset)
182	pendingUpdates = 0
183	unlock(&mtxpoll)
184
185	if pollVerbose {
186		println("*** netpoll before poll")
187	}
188	n := libc_poll(&pfds[0], uintptr(len(pfds)), timeout)
189	if pollVerbose {
190		println("*** netpoll after poll", n)
191	}
192	if n < 0 {
193		e := errno()
194		if e != _EINTR {
195			println("errno=", e, " len(pfds)=", len(pfds))
196			throw("poll failed")
197		}
198		if pollVerbose {
199			println("*** poll failed")
200		}
201		unlock(&mtxset)
202		goto retry
203	}
204	// Check if some descriptors need to be changed
205	if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
206		var b [1]byte
207		for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 {
208			if pollVerbose {
209				println("*** read 1 byte from pipe")
210			}
211		}
212		// Do not look at the other fds in this case as the mode may have changed
213		// XXX only additions of flags are made, so maybe it is ok
214		unlock(&mtxset)
215		goto retry
216	}
217	var toRun gList
218	for i := 0; i < len(pfds) && n > 0; i++ {
219		pfd := &pfds[i]
220
221		var mode int32
222		if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
223			mode += 'r'
224			pfd.events &= ^_POLLIN
225		}
226		if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 {
227			mode += 'w'
228			pfd.events &= ^_POLLOUT
229		}
230		if mode != 0 {
231			if pollVerbose {
232				println("*** netpollready i=", i, "revents=", pfd.revents, "events=", pfd.events, "pd=", pds[i])
233			}
234			netpollready(&toRun, pds[i], mode)
235			n--
236		}
237	}
238	unlock(&mtxset)
239	if block && toRun.empty() {
240		goto retry
241	}
242	if pollVerbose {
243		println("*** netpoll returning end")
244	}
245	return toRun
246}
247