diff options
| author | Ian Lance Taylor <iant@golang.org> | 2017-02-10 15:17:38 -0800 |
|---|---|---|
| committer | Ian Lance Taylor <iant@golang.org> | 2017-02-15 19:31:55 +0000 |
| commit | c05b06a12d005f50e4776095a60d6bd9c2c91fac (patch) | |
| tree | ff6becc629ab17efd3d90355e71ce8f6f7ffe488 /src/runtime | |
| parent | 81ec3f6a6ccd65abc85fc1f5d16af0a4b426029b (diff) | |
| download | go-c05b06a12d005f50e4776095a60d6bd9c2c91fac.tar.xz | |
os: use poller for file I/O
This changes the os package to use the runtime poller for file I/O
where possible. When a system call blocks on a pollable descriptor,
the goroutine will be blocked on the poller but the thread will be
released to run other goroutines. When using a non-pollable
descriptor, the os package will continue to use thread-blocking system
calls as before.
For example, on GNU/Linux, the runtime poller uses epoll. epoll does
not support ordinary disk files, so they will continue to use blocking
I/O as before. The poller will be used for pipes.
Since this means that the poller is used for many more programs, this
modifies the runtime to only block waiting for the poller if there is
some goroutine that is waiting on the poller. Otherwise, there is no
point, as the poller will never make any goroutine ready. This
preserves the runtime's current simple deadlock detection.
This seems to crash FreeBSD systems, so it is disabled on FreeBSD.
This is issue 19093.
Using the poller on Windows requires opening the file with
FILE_FLAG_OVERLAPPED. We should only do that if we can remove that
flag if the program calls the Fd method. This is issue 19098.
Update #6817.
Update #7903.
Update #15021.
Update #18507.
Update #19093.
Update #19098.
Change-Id: Ia5197dcefa7c6fbcca97d19a6f8621b2abcbb1fe
Reviewed-on: https://go-review.googlesource.com/36800
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Diffstat (limited to 'src/runtime')
| -rw-r--r-- | src/runtime/netpoll.go | 39 | ||||
| -rw-r--r-- | src/runtime/netpoll_epoll.go | 4 | ||||
| -rw-r--r-- | src/runtime/netpoll_kqueue.go | 4 | ||||
| -rw-r--r-- | src/runtime/netpoll_nacl.go | 4 | ||||
| -rw-r--r-- | src/runtime/netpoll_solaris.go | 4 | ||||
| -rw-r--r-- | src/runtime/netpoll_stub.go | 2 | ||||
| -rw-r--r-- | src/runtime/netpoll_windows.go | 4 | ||||
| -rw-r--r-- | src/runtime/proc.go | 4 | ||||
| -rw-r--r-- | src/runtime/trace/trace_stack_test.go | 1 |
9 files changed, 55 insertions, 11 deletions
diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go index ac8d071045..56fb286c3c 100644 --- a/src/runtime/netpoll.go +++ b/src/runtime/netpoll.go @@ -77,8 +77,9 @@ type pollCache struct { } var ( - netpollInited uint32 - pollcache pollCache + netpollInited uint32 + pollcache pollCache + netpollWaiters uint32 ) //go:linkname poll_runtime_pollServerInit internal/poll.runtime_pollServerInit @@ -91,6 +92,14 @@ func netpollinited() bool { return atomic.Load(&netpollInited) != 0 } +//go:linkname poll_runtime_pollServerDescriptor internal/poll.runtime_pollServerDescriptor + +// poll_runtime_pollServerDescriptor returns the descriptor being used, +// or ^uintptr(0) if the system does not use a poll descriptor. +func poll_runtime_pollServerDescriptor() uintptr { + return netpolldescriptor() +} + //go:linkname poll_runtime_pollOpen internal/poll.runtime_pollOpen func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) { pd := pollcache.alloc() @@ -244,10 +253,10 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { } unlock(&pd.lock) if rg != nil { - goready(rg, 3) + netpollgoready(rg, 3) } if wg != nil { - goready(wg, 3) + netpollgoready(wg, 3) } } @@ -273,10 +282,10 @@ func poll_runtime_pollUnblock(pd *pollDesc) { } unlock(&pd.lock) if rg != nil { - goready(rg, 3) + netpollgoready(rg, 3) } if wg != nil { - goready(wg, 3) + netpollgoready(wg, 3) } } @@ -312,7 +321,19 @@ func netpollcheckerr(pd *pollDesc, mode int32) int { } func netpollblockcommit(gp *g, gpp unsafe.Pointer) bool { - return atomic.Casuintptr((*uintptr)(gpp), pdWait, uintptr(unsafe.Pointer(gp))) + r := atomic.Casuintptr((*uintptr)(gpp), pdWait, uintptr(unsafe.Pointer(gp))) + if r { + // Bump the count of goroutines waiting for the poller. + // The scheduler uses this to decide whether to block + // waiting for the poller if there is nothing else to do. + atomic.Xadd(&netpollWaiters, 1) + } + return r +} + +func netpollgoready(gp *g, traceskip int) { + atomic.Xadd(&netpollWaiters, -1) + goready(gp, traceskip+1) } // returns true if IO is ready, or false if timedout or closed @@ -410,10 +431,10 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) { } unlock(&pd.lock) if rg != nil { - goready(rg, 0) + netpollgoready(rg, 0) } if wg != nil { - goready(wg, 0) + netpollgoready(wg, 0) } } diff --git a/src/runtime/netpoll_epoll.go b/src/runtime/netpoll_epoll.go index e06eff83be..63f943bc6a 100644 --- a/src/runtime/netpoll_epoll.go +++ b/src/runtime/netpoll_epoll.go @@ -36,6 +36,10 @@ func netpollinit() { throw("netpollinit: failed to create descriptor") } +func netpolldescriptor() uintptr { + return uintptr(epfd) +} + func netpollopen(fd uintptr, pd *pollDesc) int32 { var ev epollevent ev.events = _EPOLLIN | _EPOLLOUT | _EPOLLRDHUP | _EPOLLET diff --git a/src/runtime/netpoll_kqueue.go b/src/runtime/netpoll_kqueue.go index 337377a95b..5adf19ca09 100644 --- a/src/runtime/netpoll_kqueue.go +++ b/src/runtime/netpoll_kqueue.go @@ -29,6 +29,10 @@ func netpollinit() { closeonexec(kq) } +func netpolldescriptor() uintptr { + return uintptr(kq) +} + func netpollopen(fd uintptr, pd *pollDesc) int32 { // Arm both EVFILT_READ and EVFILT_WRITE in edge-triggered mode (EV_CLEAR) // for the whole fd lifetime. The notifications are automatically unregistered diff --git a/src/runtime/netpoll_nacl.go b/src/runtime/netpoll_nacl.go index 5cbc300321..dc5a55ec84 100644 --- a/src/runtime/netpoll_nacl.go +++ b/src/runtime/netpoll_nacl.go @@ -10,6 +10,10 @@ package runtime func netpollinit() { } +func netpolldescriptor() uintptr { + return ^uintptr(0) +} + func netpollopen(fd uintptr, pd *pollDesc) int32 { return 0 } diff --git a/src/runtime/netpoll_solaris.go b/src/runtime/netpoll_solaris.go index 53b2aacdb5..a19bd16fd2 100644 --- a/src/runtime/netpoll_solaris.go +++ b/src/runtime/netpoll_solaris.go @@ -121,6 +121,10 @@ func netpollinit() { throw("netpollinit: failed to create port") } +func netpolldescriptor() uintptr { + return uintptr(portfd) +} + func netpollopen(fd uintptr, pd *pollDesc) int32 { lock(&pd.lock) // We don't register for any specific type of events yet, that's diff --git a/src/runtime/netpoll_stub.go b/src/runtime/netpoll_stub.go index 09f64ad9b5..a4d6b4608a 100644 --- a/src/runtime/netpoll_stub.go +++ b/src/runtime/netpoll_stub.go @@ -6,6 +6,8 @@ package runtime +var netpollWaiters uint32 + // Polls for ready network connections. // Returns list of goroutines that become runnable. func netpoll(block bool) (gp *g) { diff --git a/src/runtime/netpoll_windows.go b/src/runtime/netpoll_windows.go index 32c120c4c3..d714d0ac6e 100644 --- a/src/runtime/netpoll_windows.go +++ b/src/runtime/netpoll_windows.go @@ -41,6 +41,10 @@ func netpollinit() { } } +func netpolldescriptor() uintptr { + return iocphandle +} + func netpollopen(fd uintptr, pd *pollDesc) int32 { if stdcall4(_CreateIoCompletionPort, fd, iocphandle, 0, 0) == 0 { return -int32(getlasterror()) diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 5fc7d25390..6562eaa8a0 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -2060,7 +2060,7 @@ stop: } // poll network - if netpollinited() && atomic.Xchg64(&sched.lastpoll, 0) != 0 { + if netpollinited() && atomic.Load(&netpollWaiters) > 0 && atomic.Xchg64(&sched.lastpoll, 0) != 0 { if _g_.m.p != 0 { throw("findrunnable: netpoll with p") } @@ -2101,7 +2101,7 @@ func pollWork() bool { if !runqempty(p) { return true } - if netpollinited() && sched.lastpoll != 0 { + if netpollinited() && atomic.Load(&netpollWaiters) > 0 && sched.lastpoll != 0 { if gp := netpoll(false); gp != nil { injectglist(gp) return true diff --git a/src/runtime/trace/trace_stack_test.go b/src/runtime/trace/trace_stack_test.go index d6a3858b91..fed6bad3a0 100644 --- a/src/runtime/trace/trace_stack_test.go +++ b/src/runtime/trace/trace_stack_test.go @@ -240,6 +240,7 @@ func TestTraceSymbolize(t *testing.T) { {trace.EvGoSysCall, []frame{ {"syscall.read", 0}, {"syscall.Read", 0}, + {"internal/poll.(*FD).Read", 0}, {"os.(*File).read", 0}, {"os.(*File).Read", 0}, {"runtime/trace_test.TestTraceSymbolize.func11", 102}, |
