2014-09-04 00:04:04 -06:00
|
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
|
|
|
|
|
|
|
|
package runtime
|
|
|
|
|
|
|
|
import "unsafe"
|
|
|
|
|
|
|
|
// Integrated network poller (platform-independent part).
|
|
|
|
// A particular implementation (epoll/kqueue) must define the following functions:
|
|
|
|
// func netpollinit() // to initialize the poller
|
|
|
|
// func netpollopen(fd uintptr, pd *pollDesc) int32 // to arm edge-triggered notifications
|
|
|
|
// and associate fd with pd.
|
|
|
|
// An implementation must call the following function to denote that the pd is ready.
|
2014-09-04 01:23:37 -06:00
|
|
|
// func netpollready(gpp **g, pd *pollDesc, mode int32)
|
2014-09-04 00:04:04 -06:00
|
|
|
|
|
|
|
// pollDesc contains 2 binary semaphores, rg and wg, to park reader and writer
|
|
|
|
// goroutines respectively. The semaphore can be in the following states:
|
|
|
|
// pdReady - io readiness notification is pending;
|
|
|
|
// a goroutine consumes the notification by changing the state to nil.
|
|
|
|
// pdWait - a goroutine prepares to park on the semaphore, but not yet parked;
|
|
|
|
// the goroutine commits to park by changing the state to G pointer,
|
|
|
|
// or, alternatively, concurrent io notification changes the state to READY,
|
|
|
|
// or, alternatively, concurrent timeout/close changes the state to nil.
|
|
|
|
// G pointer - the goroutine is blocked on the semaphore;
|
|
|
|
// io notification or timeout/close changes the state to READY or nil respectively
|
|
|
|
// and unparks the goroutine.
|
|
|
|
// nil - nothing of the above.
|
|
|
|
const (
|
|
|
|
pdReady uintptr = 1
|
|
|
|
pdWait uintptr = 2
|
|
|
|
)
|
|
|
|
|
|
|
|
const pollBlockSize = 4 * 1024
|
|
|
|
|
|
|
|
// Network poller descriptor.
|
|
|
|
type pollDesc struct {
|
|
|
|
link *pollDesc // in pollcache, protected by pollcache.lock
|
|
|
|
|
|
|
|
// The lock protects pollOpen, pollSetDeadline, pollUnblock and deadlineimpl operations.
|
|
|
|
// This fully covers seq, rt and wt variables. fd is constant throughout the PollDesc lifetime.
|
|
|
|
// pollReset, pollWait, pollWaitCanceled and runtime·netpollready (IO readiness notification)
|
|
|
|
// proceed w/o taking the lock. So closing, rg, rd, wg and wd are manipulated
|
|
|
|
// in a lock-free way by all operations.
|
|
|
|
// NOTE(dvyukov): the following code uses uintptr to store *g (rg/wg),
|
|
|
|
// that will blow up when GC starts moving objects.
|
|
|
|
lock mutex // protectes the following fields
|
|
|
|
fd uintptr
|
|
|
|
closing bool
|
|
|
|
seq uintptr // protects from stale timers and ready notifications
|
|
|
|
rg uintptr // pdReady, pdWait, G waiting for read or nil
|
|
|
|
rt timer // read deadline timer (set if rt.f != nil)
|
|
|
|
rd int64 // read deadline
|
|
|
|
wg uintptr // pdReady, pdWait, G waiting for write or nil
|
|
|
|
wt timer // write deadline timer
|
|
|
|
wd int64 // write deadline
|
|
|
|
user unsafe.Pointer // user settable cookie
|
|
|
|
}
|
|
|
|
|
|
|
|
type pollCache struct {
|
|
|
|
lock mutex
|
|
|
|
first *pollDesc
|
|
|
|
// PollDesc objects must be type-stable,
|
|
|
|
// because we can get ready notification from epoll/kqueue
|
|
|
|
// after the descriptor is closed/reused.
|
|
|
|
// Stale notifications are detected using seq variable,
|
|
|
|
// seq is incremented when deadlines are changed or descriptor is reused.
|
|
|
|
}
|
|
|
|
|
|
|
|
var pollcache pollCache
|
|
|
|
|
|
|
|
func netpollServerInit() {
|
[dev.cc] runtime: delete scalararg, ptrarg; rename onM to systemstack
Scalararg and ptrarg are not "signal safe".
Go code filling them out can be interrupted by a signal,
and then the signal handler runs, and if it also ends up
in Go code that uses scalararg or ptrarg, now the old
values have been smashed.
For the pieces of code that do need to run in a signal handler,
we introduced onM_signalok, which is really just onM
except that the _signalok is meant to convey that the caller
asserts that scalarg and ptrarg will be restored to their old
values after the call (instead of the usual behavior, zeroing them).
Scalararg and ptrarg are also untyped and therefore error-prone.
Go code can always pass a closure instead of using scalararg
and ptrarg; they were only really necessary for C code.
And there's no more C code.
For all these reasons, delete scalararg and ptrarg, converting
the few remaining references to use closures.
Once those are gone, there is no need for a distinction between
onM and onM_signalok, so replace both with a single function
equivalent to the current onM_signalok (that is, it can be called
on any of the curg, g0, and gsignal stacks).
The name onM and the phrase 'm stack' are misnomers,
because on most system an M has two system stacks:
the main thread stack and the signal handling stack.
Correct the misnomer by naming the replacement function systemstack.
Fix a few references to "M stack" in code.
The main motivation for this change is to eliminate scalararg/ptrarg.
Rick and I have already seen them cause problems because
the calling sequence m.ptrarg[0] = p is a heap pointer assignment,
so it gets a write barrier. The write barrier also uses onM, so it has
all the same problems as if it were being invoked by a signal handler.
We worked around this by saving and restoring the old values
and by calling onM_signalok, but there's no point in keeping this nice
home for bugs around any longer.
This CL also changes funcline to return the file name as a result
instead of filling in a passed-in *string. (The *string signature is
left over from when the code was written in and called from C.)
That's arguably an unrelated change, except that once I had done
the ptrarg/scalararg/onM cleanup I started getting false positives
about the *string argument escaping (not allowed in package runtime).
The compiler is wrong, but the easiest fix is to write the code like
Go code instead of like C code. I am a bit worried that the compiler
is wrong because of some use of uninitialized memory in the escape
analysis. If that's the reason, it will go away when we convert the
compiler to Go. (And if not, we'll debug it the next time.)
LGTM=khr
R=r, khr
CC=austin, golang-codereviews, iant, rlh
https://golang.org/cl/174950043
2014-11-12 12:54:31 -07:00
|
|
|
systemstack(netpollinit)
|
2014-09-04 00:04:04 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func netpollOpen(fd uintptr) (*pollDesc, int) {
|
|
|
|
pd := pollcache.alloc()
|
|
|
|
lock(&pd.lock)
|
|
|
|
if pd.wg != 0 && pd.wg != pdReady {
|
|
|
|
gothrow("netpollOpen: blocked write on free descriptor")
|
|
|
|
}
|
|
|
|
if pd.rg != 0 && pd.rg != pdReady {
|
|
|
|
gothrow("netpollOpen: blocked read on free descriptor")
|
|
|
|
}
|
|
|
|
pd.fd = fd
|
|
|
|
pd.closing = false
|
|
|
|
pd.seq++
|
|
|
|
pd.rg = 0
|
|
|
|
pd.rd = 0
|
|
|
|
pd.wg = 0
|
|
|
|
pd.wd = 0
|
|
|
|
unlock(&pd.lock)
|
|
|
|
|
2014-09-06 19:16:35 -06:00
|
|
|
var errno int32
|
[dev.cc] runtime: delete scalararg, ptrarg; rename onM to systemstack
Scalararg and ptrarg are not "signal safe".
Go code filling them out can be interrupted by a signal,
and then the signal handler runs, and if it also ends up
in Go code that uses scalararg or ptrarg, now the old
values have been smashed.
For the pieces of code that do need to run in a signal handler,
we introduced onM_signalok, which is really just onM
except that the _signalok is meant to convey that the caller
asserts that scalarg and ptrarg will be restored to their old
values after the call (instead of the usual behavior, zeroing them).
Scalararg and ptrarg are also untyped and therefore error-prone.
Go code can always pass a closure instead of using scalararg
and ptrarg; they were only really necessary for C code.
And there's no more C code.
For all these reasons, delete scalararg and ptrarg, converting
the few remaining references to use closures.
Once those are gone, there is no need for a distinction between
onM and onM_signalok, so replace both with a single function
equivalent to the current onM_signalok (that is, it can be called
on any of the curg, g0, and gsignal stacks).
The name onM and the phrase 'm stack' are misnomers,
because on most system an M has two system stacks:
the main thread stack and the signal handling stack.
Correct the misnomer by naming the replacement function systemstack.
Fix a few references to "M stack" in code.
The main motivation for this change is to eliminate scalararg/ptrarg.
Rick and I have already seen them cause problems because
the calling sequence m.ptrarg[0] = p is a heap pointer assignment,
so it gets a write barrier. The write barrier also uses onM, so it has
all the same problems as if it were being invoked by a signal handler.
We worked around this by saving and restoring the old values
and by calling onM_signalok, but there's no point in keeping this nice
home for bugs around any longer.
This CL also changes funcline to return the file name as a result
instead of filling in a passed-in *string. (The *string signature is
left over from when the code was written in and called from C.)
That's arguably an unrelated change, except that once I had done
the ptrarg/scalararg/onM cleanup I started getting false positives
about the *string argument escaping (not allowed in package runtime).
The compiler is wrong, but the easiest fix is to write the code like
Go code instead of like C code. I am a bit worried that the compiler
is wrong because of some use of uninitialized memory in the escape
analysis. If that's the reason, it will go away when we convert the
compiler to Go. (And if not, we'll debug it the next time.)
LGTM=khr
R=r, khr
CC=austin, golang-codereviews, iant, rlh
https://golang.org/cl/174950043
2014-11-12 12:54:31 -07:00
|
|
|
systemstack(func() {
|
2014-09-06 19:16:35 -06:00
|
|
|
errno = netpollopen(fd, pd)
|
|
|
|
})
|
2014-09-04 00:04:04 -06:00
|
|
|
return pd, int(errno)
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollClose(pd *pollDesc) {
|
|
|
|
if !pd.closing {
|
2014-09-04 01:23:37 -06:00
|
|
|
gothrow("netpollClose: close w/o unblock")
|
2014-09-04 00:04:04 -06:00
|
|
|
}
|
|
|
|
if pd.wg != 0 && pd.wg != pdReady {
|
|
|
|
gothrow("netpollClose: blocked write on closing descriptor")
|
|
|
|
}
|
|
|
|
if pd.rg != 0 && pd.rg != pdReady {
|
|
|
|
gothrow("netpollClose: blocked read on closing descriptor")
|
|
|
|
}
|
[dev.cc] runtime: delete scalararg, ptrarg; rename onM to systemstack
Scalararg and ptrarg are not "signal safe".
Go code filling them out can be interrupted by a signal,
and then the signal handler runs, and if it also ends up
in Go code that uses scalararg or ptrarg, now the old
values have been smashed.
For the pieces of code that do need to run in a signal handler,
we introduced onM_signalok, which is really just onM
except that the _signalok is meant to convey that the caller
asserts that scalarg and ptrarg will be restored to their old
values after the call (instead of the usual behavior, zeroing them).
Scalararg and ptrarg are also untyped and therefore error-prone.
Go code can always pass a closure instead of using scalararg
and ptrarg; they were only really necessary for C code.
And there's no more C code.
For all these reasons, delete scalararg and ptrarg, converting
the few remaining references to use closures.
Once those are gone, there is no need for a distinction between
onM and onM_signalok, so replace both with a single function
equivalent to the current onM_signalok (that is, it can be called
on any of the curg, g0, and gsignal stacks).
The name onM and the phrase 'm stack' are misnomers,
because on most system an M has two system stacks:
the main thread stack and the signal handling stack.
Correct the misnomer by naming the replacement function systemstack.
Fix a few references to "M stack" in code.
The main motivation for this change is to eliminate scalararg/ptrarg.
Rick and I have already seen them cause problems because
the calling sequence m.ptrarg[0] = p is a heap pointer assignment,
so it gets a write barrier. The write barrier also uses onM, so it has
all the same problems as if it were being invoked by a signal handler.
We worked around this by saving and restoring the old values
and by calling onM_signalok, but there's no point in keeping this nice
home for bugs around any longer.
This CL also changes funcline to return the file name as a result
instead of filling in a passed-in *string. (The *string signature is
left over from when the code was written in and called from C.)
That's arguably an unrelated change, except that once I had done
the ptrarg/scalararg/onM cleanup I started getting false positives
about the *string argument escaping (not allowed in package runtime).
The compiler is wrong, but the easiest fix is to write the code like
Go code instead of like C code. I am a bit worried that the compiler
is wrong because of some use of uninitialized memory in the escape
analysis. If that's the reason, it will go away when we convert the
compiler to Go. (And if not, we'll debug it the next time.)
LGTM=khr
R=r, khr
CC=austin, golang-codereviews, iant, rlh
https://golang.org/cl/174950043
2014-11-12 12:54:31 -07:00
|
|
|
systemstack(func() {
|
2014-09-06 19:16:35 -06:00
|
|
|
netpollclose(uintptr(pd.fd))
|
|
|
|
})
|
2014-09-04 00:04:04 -06:00
|
|
|
pollcache.free(pd)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *pollCache) free(pd *pollDesc) {
|
|
|
|
lock(&c.lock)
|
|
|
|
pd.link = c.first
|
|
|
|
c.first = pd
|
|
|
|
unlock(&c.lock)
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollReset(pd *pollDesc, mode int) int {
|
|
|
|
err := netpollcheckerr(pd, int32(mode))
|
|
|
|
if err != 0 {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if mode == 'r' {
|
|
|
|
pd.rg = 0
|
|
|
|
} else if mode == 'w' {
|
|
|
|
pd.wg = 0
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollWait(pd *pollDesc, mode int) int {
|
|
|
|
err := netpollcheckerr(pd, int32(mode))
|
|
|
|
if err != 0 {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// As for now only Solaris uses level-triggered IO.
|
|
|
|
if GOOS == "solaris" {
|
[dev.cc] runtime: delete scalararg, ptrarg; rename onM to systemstack
Scalararg and ptrarg are not "signal safe".
Go code filling them out can be interrupted by a signal,
and then the signal handler runs, and if it also ends up
in Go code that uses scalararg or ptrarg, now the old
values have been smashed.
For the pieces of code that do need to run in a signal handler,
we introduced onM_signalok, which is really just onM
except that the _signalok is meant to convey that the caller
asserts that scalarg and ptrarg will be restored to their old
values after the call (instead of the usual behavior, zeroing them).
Scalararg and ptrarg are also untyped and therefore error-prone.
Go code can always pass a closure instead of using scalararg
and ptrarg; they were only really necessary for C code.
And there's no more C code.
For all these reasons, delete scalararg and ptrarg, converting
the few remaining references to use closures.
Once those are gone, there is no need for a distinction between
onM and onM_signalok, so replace both with a single function
equivalent to the current onM_signalok (that is, it can be called
on any of the curg, g0, and gsignal stacks).
The name onM and the phrase 'm stack' are misnomers,
because on most system an M has two system stacks:
the main thread stack and the signal handling stack.
Correct the misnomer by naming the replacement function systemstack.
Fix a few references to "M stack" in code.
The main motivation for this change is to eliminate scalararg/ptrarg.
Rick and I have already seen them cause problems because
the calling sequence m.ptrarg[0] = p is a heap pointer assignment,
so it gets a write barrier. The write barrier also uses onM, so it has
all the same problems as if it were being invoked by a signal handler.
We worked around this by saving and restoring the old values
and by calling onM_signalok, but there's no point in keeping this nice
home for bugs around any longer.
This CL also changes funcline to return the file name as a result
instead of filling in a passed-in *string. (The *string signature is
left over from when the code was written in and called from C.)
That's arguably an unrelated change, except that once I had done
the ptrarg/scalararg/onM cleanup I started getting false positives
about the *string argument escaping (not allowed in package runtime).
The compiler is wrong, but the easiest fix is to write the code like
Go code instead of like C code. I am a bit worried that the compiler
is wrong because of some use of uninitialized memory in the escape
analysis. If that's the reason, it will go away when we convert the
compiler to Go. (And if not, we'll debug it the next time.)
LGTM=khr
R=r, khr
CC=austin, golang-codereviews, iant, rlh
https://golang.org/cl/174950043
2014-11-12 12:54:31 -07:00
|
|
|
systemstack(func() {
|
2014-09-06 19:16:35 -06:00
|
|
|
netpollarm(pd, mode)
|
|
|
|
})
|
2014-09-04 00:04:04 -06:00
|
|
|
}
|
|
|
|
for !netpollblock(pd, int32(mode), false) {
|
|
|
|
err = netpollcheckerr(pd, int32(mode))
|
|
|
|
if err != 0 {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Can happen if timeout has fired and unblocked us,
|
|
|
|
// but before we had a chance to run, timeout has been reset.
|
|
|
|
// Pretend it has not happened and retry.
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollWaitCanceled(pd *pollDesc, mode int) {
|
|
|
|
// This function is used only on windows after a failed attempt to cancel
|
|
|
|
// a pending async IO operation. Wait for ioready, ignore closing or timeouts.
|
|
|
|
for !netpollblock(pd, int32(mode), true) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollSetDeadline(pd *pollDesc, d int64, mode int) {
|
|
|
|
lock(&pd.lock)
|
|
|
|
if pd.closing {
|
|
|
|
unlock(&pd.lock)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
pd.seq++ // invalidate current timers
|
|
|
|
// Reset current timers.
|
|
|
|
if pd.rt.f != nil {
|
|
|
|
deltimer(&pd.rt)
|
|
|
|
pd.rt.f = nil
|
|
|
|
}
|
|
|
|
if pd.wt.f != nil {
|
|
|
|
deltimer(&pd.wt)
|
|
|
|
pd.wt.f = nil
|
|
|
|
}
|
|
|
|
// Setup new timers.
|
|
|
|
if d != 0 && d <= nanotime() {
|
|
|
|
d = -1
|
|
|
|
}
|
|
|
|
if mode == 'r' || mode == 'r'+'w' {
|
|
|
|
pd.rd = d
|
|
|
|
}
|
|
|
|
if mode == 'w' || mode == 'r'+'w' {
|
|
|
|
pd.wd = d
|
|
|
|
}
|
|
|
|
if pd.rd > 0 && pd.rd == pd.wd {
|
|
|
|
pd.rt.f = netpollDeadline
|
|
|
|
pd.rt.when = pd.rd
|
|
|
|
// Copy current seq into the timer arg.
|
|
|
|
// Timer func will check the seq against current descriptor seq,
|
|
|
|
// if they differ the descriptor was reused or timers were reset.
|
|
|
|
pd.rt.arg = pd
|
|
|
|
pd.rt.seq = pd.seq
|
|
|
|
addtimer(&pd.rt)
|
|
|
|
} else {
|
|
|
|
if pd.rd > 0 {
|
|
|
|
pd.rt.f = netpollReadDeadline
|
|
|
|
pd.rt.when = pd.rd
|
|
|
|
pd.rt.arg = pd
|
|
|
|
pd.rt.seq = pd.seq
|
|
|
|
addtimer(&pd.rt)
|
|
|
|
}
|
|
|
|
if pd.wd > 0 {
|
|
|
|
pd.wt.f = netpollWriteDeadline
|
|
|
|
pd.wt.when = pd.wd
|
|
|
|
pd.wt.arg = pd
|
|
|
|
pd.wt.seq = pd.seq
|
|
|
|
addtimer(&pd.wt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If we set the new deadline in the past, unblock currently pending IO if any.
|
|
|
|
var rg, wg *g
|
|
|
|
atomicstorep(unsafe.Pointer(&wg), nil) // full memory barrier between stores to rd/wd and load of rg/wg in netpollunblock
|
|
|
|
if pd.rd < 0 {
|
|
|
|
rg = netpollunblock(pd, 'r', false)
|
|
|
|
}
|
|
|
|
if pd.wd < 0 {
|
|
|
|
wg = netpollunblock(pd, 'w', false)
|
|
|
|
}
|
|
|
|
unlock(&pd.lock)
|
|
|
|
if rg != nil {
|
|
|
|
goready(rg)
|
|
|
|
}
|
|
|
|
if wg != nil {
|
|
|
|
goready(wg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollUnblock(pd *pollDesc) {
|
|
|
|
lock(&pd.lock)
|
|
|
|
if pd.closing {
|
|
|
|
gothrow("netpollUnblock: already closing")
|
|
|
|
}
|
|
|
|
pd.closing = true
|
|
|
|
pd.seq++
|
|
|
|
var rg, wg *g
|
|
|
|
atomicstorep(unsafe.Pointer(&rg), nil) // full memory barrier between store to closing and read of rg/wg in netpollunblock
|
|
|
|
rg = netpollunblock(pd, 'r', false)
|
|
|
|
wg = netpollunblock(pd, 'w', false)
|
|
|
|
if pd.rt.f != nil {
|
|
|
|
deltimer(&pd.rt)
|
|
|
|
pd.rt.f = nil
|
|
|
|
}
|
|
|
|
if pd.wt.f != nil {
|
|
|
|
deltimer(&pd.wt)
|
|
|
|
pd.wt.f = nil
|
|
|
|
}
|
|
|
|
unlock(&pd.lock)
|
|
|
|
if rg != nil {
|
|
|
|
goready(rg)
|
|
|
|
}
|
|
|
|
if wg != nil {
|
|
|
|
goready(wg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollfd(pd *pollDesc) uintptr {
|
|
|
|
return pd.fd
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpolluser(pd *pollDesc) *unsafe.Pointer {
|
|
|
|
return &pd.user
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollclosing(pd *pollDesc) bool {
|
|
|
|
return pd.closing
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpolllock(pd *pollDesc) {
|
|
|
|
lock(&pd.lock)
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollunlock(pd *pollDesc) {
|
2014-09-04 01:34:01 -06:00
|
|
|
unlock(&pd.lock)
|
2014-09-04 00:04:04 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// make pd ready, newly runnable goroutines (if any) are returned in rg/wg
|
|
|
|
func netpollready(gpp **g, pd *pollDesc, mode int32) {
|
|
|
|
var rg, wg *g
|
|
|
|
if mode == 'r' || mode == 'r'+'w' {
|
|
|
|
rg = netpollunblock(pd, 'r', true)
|
|
|
|
}
|
|
|
|
if mode == 'w' || mode == 'r'+'w' {
|
|
|
|
wg = netpollunblock(pd, 'w', true)
|
|
|
|
}
|
|
|
|
if rg != nil {
|
|
|
|
rg.schedlink = *gpp
|
|
|
|
*gpp = rg
|
|
|
|
}
|
|
|
|
if wg != nil {
|
|
|
|
wg.schedlink = *gpp
|
|
|
|
*gpp = wg
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollcheckerr(pd *pollDesc, mode int32) int {
|
|
|
|
if pd.closing {
|
|
|
|
return 1 // errClosing
|
|
|
|
}
|
|
|
|
if (mode == 'r' && pd.rd < 0) || (mode == 'w' && pd.wd < 0) {
|
|
|
|
return 2 // errTimeout
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollblockcommit(gp *g, gpp unsafe.Pointer) bool {
|
|
|
|
return casuintptr((*uintptr)(gpp), pdWait, uintptr(unsafe.Pointer(gp)))
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns true if IO is ready, or false if timedout or closed
|
|
|
|
// waitio - wait only for completed IO, ignore errors
|
|
|
|
func netpollblock(pd *pollDesc, mode int32, waitio bool) bool {
|
|
|
|
gpp := &pd.rg
|
|
|
|
if mode == 'w' {
|
|
|
|
gpp = &pd.wg
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the gpp semaphore to WAIT
|
|
|
|
for {
|
|
|
|
old := *gpp
|
|
|
|
if old == pdReady {
|
|
|
|
*gpp = 0
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if old != 0 {
|
|
|
|
gothrow("netpollblock: double wait")
|
|
|
|
}
|
|
|
|
if casuintptr(gpp, 0, pdWait) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// need to recheck error states after setting gpp to WAIT
|
|
|
|
// this is necessary because runtime_pollUnblock/runtime_pollSetDeadline/deadlineimpl
|
|
|
|
// do the opposite: store to closing/rd/wd, membarrier, load of rg/wg
|
|
|
|
if waitio || netpollcheckerr(pd, mode) == 0 {
|
2014-11-11 15:08:33 -07:00
|
|
|
gopark(netpollblockcommit, unsafe.Pointer(gpp), "IO wait")
|
2014-09-04 00:04:04 -06:00
|
|
|
}
|
|
|
|
// be careful to not lose concurrent READY notification
|
|
|
|
old := xchguintptr(gpp, 0)
|
|
|
|
if old > pdWait {
|
|
|
|
gothrow("netpollblock: corrupted state")
|
|
|
|
}
|
|
|
|
return old == pdReady
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollunblock(pd *pollDesc, mode int32, ioready bool) *g {
|
|
|
|
gpp := &pd.rg
|
|
|
|
if mode == 'w' {
|
|
|
|
gpp = &pd.wg
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
old := *gpp
|
|
|
|
if old == pdReady {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if old == 0 && !ioready {
|
|
|
|
// Only set READY for ioready. runtime_pollWait
|
|
|
|
// will check for timeout/cancel before waiting.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
var new uintptr
|
|
|
|
if ioready {
|
|
|
|
new = pdReady
|
|
|
|
}
|
|
|
|
if casuintptr(gpp, old, new) {
|
|
|
|
if old == pdReady || old == pdWait {
|
|
|
|
old = 0
|
|
|
|
}
|
|
|
|
return (*g)(unsafe.Pointer(old))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
|
|
|
|
lock(&pd.lock)
|
|
|
|
// Seq arg is seq when the timer was set.
|
|
|
|
// If it's stale, ignore the timer event.
|
|
|
|
if seq != pd.seq {
|
|
|
|
// The descriptor was reused or timers were reset.
|
|
|
|
unlock(&pd.lock)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var rg *g
|
|
|
|
if read {
|
|
|
|
if pd.rd <= 0 || pd.rt.f == nil {
|
2014-09-04 01:23:37 -06:00
|
|
|
gothrow("netpolldeadlineimpl: inconsistent read deadline")
|
2014-09-04 00:04:04 -06:00
|
|
|
}
|
|
|
|
pd.rd = -1
|
|
|
|
atomicstorep(unsafe.Pointer(&pd.rt.f), nil) // full memory barrier between store to rd and load of rg in netpollunblock
|
|
|
|
rg = netpollunblock(pd, 'r', false)
|
|
|
|
}
|
|
|
|
var wg *g
|
|
|
|
if write {
|
|
|
|
if pd.wd <= 0 || pd.wt.f == nil && !read {
|
|
|
|
gothrow("netpolldeadlineimpl: inconsistent write deadline")
|
|
|
|
}
|
|
|
|
pd.wd = -1
|
|
|
|
atomicstorep(unsafe.Pointer(&pd.wt.f), nil) // full memory barrier between store to wd and load of wg in netpollunblock
|
|
|
|
wg = netpollunblock(pd, 'w', false)
|
|
|
|
}
|
|
|
|
unlock(&pd.lock)
|
|
|
|
if rg != nil {
|
|
|
|
goready(rg)
|
|
|
|
}
|
|
|
|
if wg != nil {
|
|
|
|
goready(wg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollDeadline(arg interface{}, seq uintptr) {
|
|
|
|
netpolldeadlineimpl(arg.(*pollDesc), seq, true, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollReadDeadline(arg interface{}, seq uintptr) {
|
|
|
|
netpolldeadlineimpl(arg.(*pollDesc), seq, true, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
func netpollWriteDeadline(arg interface{}, seq uintptr) {
|
|
|
|
netpolldeadlineimpl(arg.(*pollDesc), seq, false, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *pollCache) alloc() *pollDesc {
|
|
|
|
lock(&c.lock)
|
|
|
|
if c.first == nil {
|
|
|
|
const pdSize = unsafe.Sizeof(pollDesc{})
|
|
|
|
n := pollBlockSize / pdSize
|
|
|
|
if n == 0 {
|
|
|
|
n = 1
|
|
|
|
}
|
|
|
|
// Must be in non-GC memory because can be referenced
|
|
|
|
// only from epoll/kqueue internals.
|
|
|
|
mem := persistentalloc(n*pdSize, 0, &memstats.other_sys)
|
|
|
|
for i := uintptr(0); i < n; i++ {
|
|
|
|
pd := (*pollDesc)(add(mem, i*pdSize))
|
|
|
|
pd.link = c.first
|
|
|
|
c.first = pd
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pd := c.first
|
|
|
|
c.first = pd.link
|
|
|
|
unlock(&c.lock)
|
|
|
|
return pd
|
|
|
|
}
|