1 // Copyright 2014 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.
8 "runtime/internal/atomic"
12 // Solaris runtime-integrated network poller.
14 // Solaris uses event ports for scalable network I/O. Event
15 // ports are level-triggered, unlike epoll and kqueue which
16 // can be configured in both level-triggered and edge-triggered
17 // mode. Level triggering means we have to keep track of a few things
18 // ourselves. After we receive an event for a file descriptor,
19 // it's our responsibility to ask again to be notified for future
20 // events for that descriptor. When doing this we must keep track of
21 // what kind of events the goroutines are currently interested in,
22 // for example a fd may be open both for reading and writing.
24 // A description of the high level operation of this code
25 // follows. Networking code will get a file descriptor by some means
26 // and will register it with the netpolling mechanism by a code path
27 // that eventually calls runtime·netpollopen. runtime·netpollopen
28 // calls port_associate with an empty event set. That means that we
29 // will not receive any events at this point. The association needs
30 // to be done at this early point because we need to process the I/O
31 // readiness notification at some point in the future. If I/O becomes
32 // ready when nobody is listening, when we finally care about it,
33 // nobody will tell us anymore.
35 // Beside calling runtime·netpollopen, the networking code paths
36 // will call runtime·netpollarm each time goroutines are interested
37 // in doing network I/O. Because now we know what kind of I/O we
38 // are interested in (reading/writing), we can call port_associate
39 // passing the correct type of event set (POLLIN/POLLOUT). As we made
40 // sure to have already associated the file descriptor with the port,
41 // when we now call port_associate, we will unblock the main poller
42 // loop (in runtime·netpoll) right away if the socket is actually
45 // The main poller loop runs in its own thread waiting for events
46 // using port_getn. When an event happens, it will tell the scheduler
47 // about it using runtime·netpollready. Besides doing this, it must
48 // also re-associate the events that were not part of this current
49 // notification with the file descriptor. Failing to do this would
50 // mean each notification will prevent concurrent code using the
51 // same file descriptor in parallel.
53 // The logic dealing with re-associations is encapsulated in
54 // runtime·netpollupdate. This function takes care to associate the
55 // descriptor only with the subset of events that were previously
56 // part of the association, except the one that just happened. We
57 // can't re-associate with that right away, because event ports
58 // are level triggered so it would cause a busy loop. Instead, that
59 // association is effected only by the runtime·netpollarm code path,
60 // when Go code actually asks for I/O.
62 // The open and arming mechanisms are serialized using the lock
63 // inside PollDesc. This is required because the netpoll loop runs
64 // asynchronously in respect to other Go code and by the time we get
65 // to call port_associate to update the association in the loop, the
66 // file descriptor might have been closed and reopened already. The
67 // lock allows runtime·netpollupdate to be called synchronously from
68 // the loop thread while preventing other threads operating to the
69 // same PollDesc, so once we unblock in the main loop, until we loop
70 // again we know for sure we are always talking about the same file
71 // descriptor and can safely access the data we want (the event set).
74 func port_create() int32
76 //extern port_associate
77 func port_associate(port
, source
int32, object
uintptr, events
uint32, user
uintptr) int32
79 //extern port_dissociate
80 func port_dissociate(port
, source
int32, object
uintptr) int32
84 func port_getn(port
int32, evs
*portevent
, max
uint32, nget
*uint32, timeout
*timespec
) int32
87 func port_alert(port
int32, flags
, events
uint32, user
uintptr) int32
90 netpollWakeSig
uint32 // used to avoid duplicate calls of netpollBreak
96 portfd
= port_create()
102 print("runtime: port_create failed (errno=", errno(), ")\n")
103 throw("runtime: netpollinit failed")
106 func netpollIsPollDescriptor(fd
uintptr) bool {
107 return fd
== uintptr(portfd
)
110 func netpollopen(fd
uintptr, pd
*pollDesc
) int32 {
112 // We don't register for any specific type of events yet, that's
113 // netpollarm's job. We merely ensure we call port_associate before
114 // asynchronous connect/accept completes, so when we actually want
115 // to do any I/O, the call to port_associate (from netpollarm,
116 // with the interested event set) will unblock port_getn right away
117 // because of the I/O readiness notification.
119 r
:= port_associate(portfd
, _PORT_SOURCE_FD
, fd
, 0, uintptr(unsafe
.Pointer(pd
)))
122 return int32(errno())
127 func netpollclose(fd
uintptr) int32 {
128 if port_dissociate(portfd
, _PORT_SOURCE_FD
, fd
) < 0 {
129 return int32(errno())
134 // Updates the association with a new set of interested events. After
135 // this call, port_getn will return one and only one event for that
136 // particular descriptor, so this function needs to be called again.
137 func netpollupdate(pd
*pollDesc
, set
, clear
uint32) {
138 if pd
.info().closing() {
143 events
:= (old
& ^clear
) | set
148 if events
!= 0 && port_associate(portfd
, _PORT_SOURCE_FD
, pd
.fd
, events
, uintptr(unsafe
.Pointer(pd
))) != 0 {
149 print("runtime: port_associate failed (errno=", errno(), ")\n")
150 throw("runtime: netpollupdate failed")
155 // subscribe the fd to the port such that port_getn will return one event.
156 func netpollarm(pd
*pollDesc
, mode
int) {
160 netpollupdate(pd
, _POLLIN
, 0)
162 netpollupdate(pd
, _POLLOUT
, 0)
164 throw("runtime: bad mode")
169 // netpollBreak interrupts a port_getn wait.
170 func netpollBreak() {
171 if atomic
.Cas(&netpollWakeSig
, 0, 1) {
172 // Use port_alert to put portfd into alert mode.
173 // This will wake up all threads sleeping in port_getn on portfd,
174 // and cause their calls to port_getn to return immediately.
175 // Further, until portfd is taken out of alert mode,
176 // all calls to port_getn will return immediately.
177 if port_alert(portfd
, _PORT_ALERT_UPDATE
, _POLLHUP
, uintptr(unsafe
.Pointer(&portfd
))) < 0 {
178 if e
:= errno(); e
!= _EBUSY
{
179 println("runtime: port_alert failed with", e
)
180 throw("runtime: netpoll: port_alert failed")
186 // netpoll checks for ready network connections.
187 // Returns list of goroutines that become runnable.
188 // delay < 0: blocks indefinitely
189 // delay == 0: does not block, just polls
190 // delay > 0: block for up to that many nanoseconds
191 func netpoll(delay
int64) gList
{
200 } else if delay
== 0 {
205 // An arbitrary cap on how long to wait for a timer.
206 // 1e6 s == ~11.5 days.
212 var events
[128]portevent
215 r
:= port_getn(portfd
, &events
[0], uint32(len(events
)), &n
, wait
)
217 if r
< 0 && e
== _ETIME
&& n
> 0 {
218 // As per port_getn(3C), an ETIME failure does not preclude the
219 // delivery of some number of events. Treat a timeout failure
220 // with delivered events as a success.
224 if e
!= _EINTR
&& e
!= _ETIME
{
225 print("runtime: port_getn on fd ", portfd
, " failed (errno=", e
, ")\n")
226 throw("runtime: netpoll failed")
228 // If a timed sleep was interrupted and there are no events,
229 // just return to recalculate how long we should sleep now.
237 for i
:= 0; i
< int(n
); i
++ {
240 if ev
.portev_source
== _PORT_SOURCE_ALERT
{
241 if ev
.portev_events
!= _POLLHUP || unsafe
.Pointer(ev
.portev_user
) != unsafe
.Pointer(&portfd
) {
242 throw("runtime: netpoll: bad port_alert wakeup")
245 // Now that a blocking call to netpoll
246 // has seen the alert, take portfd
247 // back out of alert mode.
248 // See the comment in netpollBreak.
249 if port_alert(portfd
, 0, 0, 0) < 0 {
251 println("runtime: port_alert failed with", e
)
252 throw("runtime: netpoll: port_alert failed")
254 atomic
.Store(&netpollWakeSig
, 0)
259 if ev
.portev_events
== 0 {
262 pd
:= (*pollDesc
)(unsafe
.Pointer(ev
.portev_user
))
264 var mode
, clear
int32
265 if (ev
.portev_events
& (_POLLIN | _POLLHUP | _POLLERR
)) != 0 {
269 if (ev
.portev_events
& (_POLLOUT | _POLLHUP | _POLLERR
)) != 0 {
273 // To effect edge-triggered events, we need to be sure to
274 // update our association with whatever events were not
275 // set with the event. For example if we are registered
276 // for POLLIN|POLLOUT, and we get POLLIN, besides waking
277 // the goroutine interested in POLLIN we have to not forget
278 // about the one interested in POLLOUT.
281 netpollupdate(pd
, 0, uint32(clear
))
286 // TODO(mikio): Consider implementing event
287 // scanning error reporting once we are sure
288 // about the event port on SmartOS.
290 // See golang.org/x/issue/30840.
291 netpollready(&toRun
, pd
, mode
)