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:cgo_import_dynamic libc_poll poll "libc.a/shr_64.o"
14//go:linkname libc_poll libc_poll
15
16var libc_poll libFunc
17
18//go:nosplit
19func poll(pfds *pollfd, npfds uintptr, timeout uintptr) (int32, int32) {
20	r, err := syscall3(&libc_poll, uintptr(unsafe.Pointer(pfds)), npfds, timeout)
21	return int32(r), int32(err)
22}
23
24// pollfd represents the poll structure for AIX operating system.
25type pollfd struct {
26	fd      int32
27	events  int16
28	revents int16
29}
30
31const _POLLIN = 0x0001
32const _POLLOUT = 0x0002
33const _POLLHUP = 0x2000
34const _POLLERR = 0x4000
35
36var (
37	pfds           []pollfd
38	pds            []*pollDesc
39	mtxpoll        mutex
40	mtxset         mutex
41	rdwake         int32
42	wrwake         int32
43	pendingUpdates int32
44)
45
46func netpollinit() {
47	// Create the pipe we use to wakeup poll.
48	r, w, errno := nonblockingPipe()
49	if errno != 0 {
50		throw("netpollinit: failed to create pipe")
51	}
52	rdwake = r
53	wrwake = w
54
55	// Pre-allocate array of pollfd structures for poll.
56	pfds = make([]pollfd, 1, 128)
57
58	// Poll the read side of the pipe.
59	pfds[0].fd = rdwake
60	pfds[0].events = _POLLIN
61
62	pds = make([]*pollDesc, 1, 128)
63	pds[0] = nil
64}
65
66func netpollIsPollDescriptor(fd uintptr) bool {
67	return fd == uintptr(rdwake) || fd == uintptr(wrwake)
68}
69
70// netpollwakeup writes on wrwake to wakeup poll before any changes.
71func netpollwakeup() {
72	if pendingUpdates == 0 {
73		pendingUpdates = 1
74		b := [1]byte{0}
75		write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1)
76	}
77}
78
79func netpollopen(fd uintptr, pd *pollDesc) int32 {
80	lock(&mtxpoll)
81	netpollwakeup()
82
83	lock(&mtxset)
84	unlock(&mtxpoll)
85
86	pd.user = uint32(len(pfds))
87	pfds = append(pfds, pollfd{fd: int32(fd)})
88	pds = append(pds, pd)
89	unlock(&mtxset)
90	return 0
91}
92
93func netpollclose(fd uintptr) int32 {
94	lock(&mtxpoll)
95	netpollwakeup()
96
97	lock(&mtxset)
98	unlock(&mtxpoll)
99
100	for i := 0; i < len(pfds); i++ {
101		if pfds[i].fd == int32(fd) {
102			pfds[i] = pfds[len(pfds)-1]
103			pfds = pfds[:len(pfds)-1]
104
105			pds[i] = pds[len(pds)-1]
106			pds[i].user = uint32(i)
107			pds = pds[:len(pds)-1]
108			break
109		}
110	}
111	unlock(&mtxset)
112	return 0
113}
114
115func netpollarm(pd *pollDesc, mode int) {
116	lock(&mtxpoll)
117	netpollwakeup()
118
119	lock(&mtxset)
120	unlock(&mtxpoll)
121
122	switch mode {
123	case 'r':
124		pfds[pd.user].events |= _POLLIN
125	case 'w':
126		pfds[pd.user].events |= _POLLOUT
127	}
128	unlock(&mtxset)
129}
130
131// netpollBreak interrupts an epollwait.
132func netpollBreak() {
133	netpollwakeup()
134}
135
136// netpoll checks for ready network connections.
137// Returns list of goroutines that become runnable.
138// delay < 0: blocks indefinitely
139// delay == 0: does not block, just polls
140// delay > 0: block for up to that many nanoseconds
141//go:nowritebarrierrec
142func netpoll(delay int64) gList {
143	var timeout uintptr
144	if delay < 0 {
145		timeout = ^uintptr(0)
146	} else if delay == 0 {
147		// TODO: call poll with timeout == 0
148		return gList{}
149	} else if delay < 1e6 {
150		timeout = 1
151	} else if delay < 1e15 {
152		timeout = uintptr(delay / 1e6)
153	} else {
154		// An arbitrary cap on how long to wait for a timer.
155		// 1e9 ms == ~11.5 days.
156		timeout = 1e9
157	}
158retry:
159	lock(&mtxpoll)
160	lock(&mtxset)
161	pendingUpdates = 0
162	unlock(&mtxpoll)
163
164	n, e := poll(&pfds[0], uintptr(len(pfds)), timeout)
165	if n < 0 {
166		if e != _EINTR {
167			println("errno=", e, " len(pfds)=", len(pfds))
168			throw("poll failed")
169		}
170		unlock(&mtxset)
171		// If a timed sleep was interrupted, just return to
172		// recalculate how long we should sleep now.
173		if timeout > 0 {
174			return gList{}
175		}
176		goto retry
177	}
178	// Check if some descriptors need to be changed
179	if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
180		if delay != 0 {
181			// A netpollwakeup could be picked up by a
182			// non-blocking poll. Only clear the wakeup
183			// if blocking.
184			var b [1]byte
185			for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 {
186			}
187		}
188		// Still look at the other fds even if the mode may have
189		// changed, as netpollBreak might have been called.
190		n--
191	}
192	var toRun gList
193	for i := 1; i < len(pfds) && n > 0; i++ {
194		pfd := &pfds[i]
195
196		var mode int32
197		if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
198			mode += 'r'
199			pfd.events &= ^_POLLIN
200		}
201		if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 {
202			mode += 'w'
203			pfd.events &= ^_POLLOUT
204		}
205		if mode != 0 {
206			pds[i].everr = false
207			if pfd.revents == _POLLERR {
208				pds[i].everr = true
209			}
210			netpollready(&toRun, pds[i], mode)
211			n--
212		}
213	}
214	unlock(&mtxset)
215	return toRun
216}
217