Commit 812ba636 by Ian Lance Taylor

runtime: copy netpoll code from Go 1.7 runtime

    
    Reviewed-on: https://go-review.googlesource.com/31325

From-SVN: r241307
parent f5de494c
0a49b1dadd862215bdd38b9725a6e193b0d8fd0b
68bb6a9875499037d3eccb79a1f92e1c7a476d58
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
......@@ -427,16 +427,6 @@ endif
endif
endif
if LIBGO_IS_LINUX
runtime_netpoll_files = runtime/netpoll_epoll.c
else
if LIBGO_IS_SOLARIS
runtime_netpoll_files = runtime/netpoll_select.c
else
runtime_netpoll_files = runtime/netpoll_kqueue.c
endif
endif
runtime_files = \
runtime/go-append.c \
runtime/go-assert.c \
......@@ -500,7 +490,6 @@ runtime_files = \
runtime/mgc0.c \
runtime/mheap.c \
runtime/msize.c \
$(runtime_netpoll_files) \
runtime/panic.c \
runtime/parfor.c \
runtime/print.c \
......@@ -514,7 +503,6 @@ runtime_files = \
go-iface.c \
lfstack.c \
malloc.c \
netpoll.c \
reflect.c \
runtime1.c \
sigqueue.c \
......@@ -530,14 +518,6 @@ malloc.c: $(srcdir)/runtime/malloc.goc goc2c
./goc2c $< > $@.tmp
mv -f $@.tmp $@
mprof.c: $(srcdir)/runtime/mprof.goc goc2c
./goc2c $< > $@.tmp
mv -f $@.tmp $@
netpoll.c: $(srcdir)/runtime/netpoll.goc goc2c
./goc2c $< > $@.tmp
mv -f $@.tmp $@
reflect.c: $(srcdir)/runtime/reflect.goc goc2c
./goc2c $< > $@.tmp
mv -f $@.tmp $@
......@@ -546,18 +526,10 @@ runtime1.c: $(srcdir)/runtime/runtime1.goc goc2c
./goc2c $< > $@.tmp
mv -f $@.tmp $@
sema.c: $(srcdir)/runtime/sema.goc goc2c
./goc2c $< > $@.tmp
mv -f $@.tmp $@
sigqueue.c: $(srcdir)/runtime/sigqueue.goc goc2c
./goc2c --go-pkgpath os_signal $< > $@.tmp
mv -f $@.tmp $@
time.c: $(srcdir)/runtime/time.goc goc2c
./goc2c $< > $@.tmp
mv -f $@.tmp $@
%.c: $(srcdir)/runtime/%.goc goc2c
./goc2c $< > $@.tmp
mv -f $@.tmp $@
......
......@@ -186,6 +186,9 @@
/* Define to 1 if you have the `pipe2' function. */
#undef HAVE_PIPE2
/* Define to 1 if you have the <port.h> header file. */
#undef HAVE_PORT_H
/* Define to 1 if you have the `removexattr' function. */
#undef HAVE_REMOVEXATTR
......@@ -259,6 +262,9 @@
/* Define to 1 if you have the <sys/epoll.h> header file. */
#undef HAVE_SYS_EPOLL_H
/* Define to 1 if you have the <sys/event.h> header file. */
#undef HAVE_SYS_EVENT_H
/* Define to 1 if you have the <sys/file.h> header file. */
#undef HAVE_SYS_FILE_H
......
......@@ -14714,7 +14714,7 @@ $as_echo "#define HAVE_GETIPINFO 1" >>confdefs.h
fi
for ac_header in sched.h semaphore.h sys/file.h sys/mman.h syscall.h sys/epoll.h sys/inotify.h sys/ptrace.h sys/syscall.h sys/user.h sys/utsname.h sys/select.h sys/socket.h net/if.h net/if_arp.h net/route.h netpacket/packet.h sys/prctl.h sys/mount.h sys/vfs.h sys/statfs.h sys/timex.h sys/sysinfo.h utime.h linux/ether.h linux/fs.h linux/reboot.h netinet/icmp6.h netinet/in_syst.h netinet/ip.h netinet/ip_mroute.h netinet/if_ether.h
for ac_header in port.h sched.h semaphore.h sys/file.h sys/mman.h syscall.h sys/epoll.h sys/event.h sys/inotify.h sys/ptrace.h sys/syscall.h sys/user.h sys/utsname.h sys/select.h sys/socket.h net/if.h net/if_arp.h net/route.h netpacket/packet.h sys/prctl.h sys/mount.h sys/vfs.h sys/statfs.h sys/timex.h sys/sysinfo.h utime.h linux/ether.h linux/fs.h linux/reboot.h netinet/icmp6.h netinet/in_syst.h netinet/ip.h netinet/ip_mroute.h netinet/if_ether.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
......
......@@ -570,7 +570,7 @@ AC_C_BIGENDIAN
GCC_CHECK_UNWIND_GETIPINFO
AC_CHECK_HEADERS(sched.h semaphore.h sys/file.h sys/mman.h syscall.h sys/epoll.h sys/inotify.h sys/ptrace.h sys/syscall.h sys/user.h sys/utsname.h sys/select.h sys/socket.h net/if.h net/if_arp.h net/route.h netpacket/packet.h sys/prctl.h sys/mount.h sys/vfs.h sys/statfs.h sys/timex.h sys/sysinfo.h utime.h linux/ether.h linux/fs.h linux/reboot.h netinet/icmp6.h netinet/in_syst.h netinet/ip.h netinet/ip_mroute.h netinet/if_ether.h)
AC_CHECK_HEADERS(port.h sched.h semaphore.h sys/file.h sys/mman.h syscall.h sys/epoll.h sys/event.h sys/inotify.h sys/ptrace.h sys/syscall.h sys/user.h sys/utsname.h sys/select.h sys/socket.h net/if.h net/if_arp.h net/route.h netpacket/packet.h sys/prctl.h sys/mount.h sys/vfs.h sys/statfs.h sys/timex.h sys/sysinfo.h utime.h linux/ether.h linux/fs.h linux/reboot.h netinet/icmp6.h netinet/in_syst.h netinet/ip.h netinet/ip_mroute.h netinet/if_ether.h)
AC_CHECK_HEADERS([linux/filter.h linux/if_addr.h linux/if_ether.h linux/if_tun.h linux/netlink.h linux/rtnetlink.h], [], [],
[#ifdef HAVE_SYS_SOCKET_H
......
// 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 linux
package runtime
import "unsafe"
//extern epoll_create
func epollcreate(size int32) int32
//extern epoll_create1
func epollcreate1(flags int32) int32
//go:noescape
//extern epoll_ctl
func epollctl(epfd, op, fd int32, ev *epollevent) int32
//go:noescape
//extern epoll_wait
func epollwait(epfd int32, ev *epollevent, nev, timeout int32) int32
//extern __go_fcntl_uintptr
func fcntlUintptr(fd, cmd, arg uintptr) (uintptr, uintptr)
func closeonexec(fd int32) {
fcntlUintptr(uintptr(fd), _F_SETFD, _FD_CLOEXEC)
}
var (
epfd int32 = -1 // epoll descriptor
)
func netpollinit() {
epfd = epollcreate1(_EPOLL_CLOEXEC)
if epfd >= 0 {
return
}
epfd = epollcreate(1024)
if epfd >= 0 {
closeonexec(epfd)
return
}
println("netpollinit: failed to create epoll descriptor", errno())
throw("netpollinit: failed to create descriptor")
}
func netpollopen(fd uintptr, pd *pollDesc) int32 {
var ev epollevent
ev.events = _EPOLLIN | _EPOLLOUT | _EPOLLRDHUP | _EPOLLET
*(**pollDesc)(unsafe.Pointer(&ev.data)) = pd
if epollctl(epfd, _EPOLL_CTL_ADD, int32(fd), &ev) < 0 {
return int32(errno())
}
return 0
}
func netpollclose(fd uintptr) int32 {
var ev epollevent
if epollctl(epfd, _EPOLL_CTL_DEL, int32(fd), &ev) < 0 {
return int32(errno())
}
return 0
}
func netpollarm(pd *pollDesc, mode int) {
throw("unused")
}
// polls for ready network connections
// returns list of goroutines that become runnable
func netpoll(block bool) *g {
if epfd == -1 {
return nil
}
waitms := int32(-1)
if !block {
waitms = 0
}
var events [128]epollevent
retry:
n := epollwait(epfd, &events[0], int32(len(events)), waitms)
if n < 0 {
e := errno()
if e != _EINTR {
println("runtime: epollwait on fd", epfd, "failed with", e)
throw("epollwait failed")
}
goto retry
}
var gp guintptr
for i := int32(0); i < n; i++ {
ev := &events[i]
if ev.events == 0 {
continue
}
var mode int32
if ev.events&(_EPOLLIN|_EPOLLRDHUP|_EPOLLHUP|_EPOLLERR) != 0 {
mode += 'r'
}
if ev.events&(_EPOLLOUT|_EPOLLHUP|_EPOLLERR) != 0 {
mode += 'w'
}
if mode != 0 {
pd := *(**pollDesc)(unsafe.Pointer(&ev.data))
netpollready(&gp, pd, mode)
}
}
if block && gp == 0 {
goto retry
}
return gp.ptr()
}
// 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 netbsd openbsd
package runtime
// Integrated network poller (kqueue-based implementation).
import "unsafe"
//extern kqueue
func kqueue() int32
//go:noescape
//extern kevent
func kevent(kq int32, ch *keventt, nch uintptr, ev *keventt, nev uintptr, ts *timespec) int32
//extern __go_fcntl_uintptr
func fcntlUintptr(fd, cmd, arg uintptr) (uintptr, uintptr)
func closeonexec(fd int32) {
fcntlUintptr(uintptr(fd), _F_SETFD, _FD_CLOEXEC)
}
var (
kq int32 = -1
)
func netpollinit() {
kq = kqueue()
if kq < 0 {
println("netpollinit: kqueue failed with", errno())
throw("netpollinit: kqueue failed")
}
closeonexec(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
// when fd is closed.
var ev [2]keventt
*(*uintptr)(unsafe.Pointer(&ev[0].ident)) = fd
ev[0].filter = _EVFILT_READ
ev[0].flags = _EV_ADD | _EV_CLEAR
ev[0].fflags = 0
ev[0].data = 0
ev[0].udata = (*byte)(unsafe.Pointer(pd))
ev[1] = ev[0]
ev[1].filter = _EVFILT_WRITE
n := kevent(kq, &ev[0], 2, nil, 0, nil)
if n < 0 {
return int32(errno())
}
return 0
}
func netpollclose(fd uintptr) int32 {
// Don't need to unregister because calling close()
// on fd will remove any kevents that reference the descriptor.
return 0
}
func netpollarm(pd *pollDesc, mode int) {
throw("unused")
}
// Polls for ready network connections.
// Returns list of goroutines that become runnable.
func netpoll(block bool) *g {
if kq == -1 {
return nil
}
var tp *timespec
var ts timespec
if !block {
tp = &ts
}
var events [64]keventt
retry:
n := kevent(kq, nil, 0, &events[0], uintptr(len(events)), tp)
if n < 0 {
e := errno()
if e != _EINTR {
println("runtime: kevent on fd", kq, "failed with", e)
throw("kevent failed")
}
goto retry
}
var gp guintptr
for i := 0; i < int(n); i++ {
ev := &events[i]
var mode int32
if ev.filter == _EVFILT_READ {
mode += 'r'
}
if ev.filter == _EVFILT_WRITE {
mode += 'w'
}
if mode != 0 {
netpollready(&gp, (*pollDesc)(unsafe.Pointer(ev.udata)), mode)
}
}
if block && gp == 0 {
goto retry
}
return gp.ptr()
}
// 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.
// Fake network poller for NaCl.
// Should never be used, because NaCl network connections do not honor "SetNonblock".
package runtime
func netpollinit() {
}
func netpollopen(fd uintptr, pd *pollDesc) int32 {
return 0
}
func netpollclose(fd uintptr) int32 {
return 0
}
func netpollarm(pd *pollDesc, mode int) {
}
func netpoll(block bool) *g {
return nil
}
// Copyright 2014 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.
package runtime
import "unsafe"
// Solaris runtime-integrated network poller.
//
// Solaris uses event ports for scalable network I/O. Event
// ports are level-triggered, unlike epoll and kqueue which
// can be configured in both level-triggered and edge-triggered
// mode. Level triggering means we have to keep track of a few things
// ourselves. After we receive an event for a file descriptor,
// it's our responsibility to ask again to be notified for future
// events for that descriptor. When doing this we must keep track of
// what kind of events the goroutines are currently interested in,
// for example a fd may be open both for reading and writing.
//
// A description of the high level operation of this code
// follows. Networking code will get a file descriptor by some means
// and will register it with the netpolling mechanism by a code path
// that eventually calls runtime·netpollopen. runtime·netpollopen
// calls port_associate with an empty event set. That means that we
// will not receive any events at this point. The association needs
// to be done at this early point because we need to process the I/O
// readiness notification at some point in the future. If I/O becomes
// ready when nobody is listening, when we finally care about it,
// nobody will tell us anymore.
//
// Beside calling runtime·netpollopen, the networking code paths
// will call runtime·netpollarm each time goroutines are interested
// in doing network I/O. Because now we know what kind of I/O we
// are interested in (reading/writing), we can call port_associate
// passing the correct type of event set (POLLIN/POLLOUT). As we made
// sure to have already associated the file descriptor with the port,
// when we now call port_associate, we will unblock the main poller
// loop (in runtime·netpoll) right away if the socket is actually
// ready for I/O.
//
// The main poller loop runs in its own thread waiting for events
// using port_getn. When an event happens, it will tell the scheduler
// about it using runtime·netpollready. Besides doing this, it must
// also re-associate the events that were not part of this current
// notification with the file descriptor. Failing to do this would
// mean each notification will prevent concurrent code using the
// same file descriptor in parallel.
//
// The logic dealing with re-associations is encapsulated in
// runtime·netpollupdate. This function takes care to associate the
// descriptor only with the subset of events that were previously
// part of the association, except the one that just happened. We
// can't re-associate with that right away, because event ports
// are level triggered so it would cause a busy loop. Instead, that
// association is effected only by the runtime·netpollarm code path,
// when Go code actually asks for I/O.
//
// The open and arming mechanisms are serialized using the lock
// inside PollDesc. This is required because the netpoll loop runs
// asynchronously in respect to other Go code and by the time we get
// to call port_associate to update the association in the loop, the
// file descriptor might have been closed and reopened already. The
// lock allows runtime·netpollupdate to be called synchronously from
// the loop thread while preventing other threads operating to the
// same PollDesc, so once we unblock in the main loop, until we loop
// again we know for sure we are always talking about the same file
// descriptor and can safely access the data we want (the event set).
//extern __go_fcntl_uintptr
func fcntlUintptr(fd, cmd, arg uintptr) (uintptr, uintptr)
func fcntl(fd, cmd int32, arg uintptr) int32 {
r, _ := fcntlUintptr(uintptr(fd), uintptr(cmd), arg)
return int32(r)
}
//extern port_create
func port_create() int32
//extern port_associate
func port_associate(port, source int32, object uintptr, events uint32, user uintptr) int32
//extern port_dissociate
func port_dissociate(port, source int32, object uintptr) int32
//extern port_getn
func port_getn(port int32, evs *portevent, max uint32, nget *uint32, timeout *timespec) int32
var portfd int32 = -1
func netpollinit() {
portfd = port_create()
if portfd >= 0 {
fcntl(portfd, _F_SETFD, _FD_CLOEXEC)
return
}
print("netpollinit: failed to create port (", errno(), ")\n")
throw("netpollinit: failed to create port")
}
func netpollopen(fd uintptr, pd *pollDesc) int32 {
lock(&pd.lock)
// We don't register for any specific type of events yet, that's
// netpollarm's job. We merely ensure we call port_associate before
// asynchronous connect/accept completes, so when we actually want
// to do any I/O, the call to port_associate (from netpollarm,
// with the interested event set) will unblock port_getn right away
// because of the I/O readiness notification.
pd.user = 0
r := port_associate(portfd, _PORT_SOURCE_FD, fd, 0, uintptr(unsafe.Pointer(pd)))
unlock(&pd.lock)
if r < 0 {
return int32(errno())
}
return 0
}
func netpollclose(fd uintptr) int32 {
if port_dissociate(portfd, _PORT_SOURCE_FD, fd) < 0 {
return int32(errno())
}
return 0
}
// Updates the association with a new set of interested events. After
// this call, port_getn will return one and only one event for that
// particular descriptor, so this function needs to be called again.
func netpollupdate(pd *pollDesc, set, clear uint32) {
if pd.closing {
return
}
old := pd.user
events := (old & ^clear) | set
if old == events {
return
}
if events != 0 && port_associate(portfd, _PORT_SOURCE_FD, pd.fd, events, uintptr(unsafe.Pointer(pd))) != 0 {
print("netpollupdate: failed to associate (", errno(), ")\n")
throw("netpollupdate: failed to associate")
}
pd.user = events
}
// subscribe the fd to the port such that port_getn will return one event.
func netpollarm(pd *pollDesc, mode int) {
lock(&pd.lock)
switch mode {
case 'r':
netpollupdate(pd, _POLLIN, 0)
case 'w':
netpollupdate(pd, _POLLOUT, 0)
default:
throw("netpollarm: bad mode")
}
unlock(&pd.lock)
}
// polls for ready network connections
// returns list of goroutines that become runnable
func netpoll(block bool) *g {
if portfd == -1 {
return nil
}
var wait *timespec
var zero timespec
if !block {
wait = &zero
}
var events [128]portevent
retry:
var n uint32 = 1
if port_getn(portfd, &events[0], uint32(len(events)), &n, wait) < 0 {
if e := errno(); e != _EINTR {
print("runtime: port_getn on fd ", portfd, " failed with ", e, "\n")
throw("port_getn failed")
}
goto retry
}
var gp guintptr
for i := 0; i < int(n); i++ {
ev := &events[i]
if ev.portev_events == 0 {
continue
}
pd := (*pollDesc)(unsafe.Pointer(ev.portev_user))
var mode, clear int32
if (ev.portev_events & (_POLLIN | _POLLHUP | _POLLERR)) != 0 {
mode += 'r'
clear |= _POLLIN
}
if (ev.portev_events & (_POLLOUT | _POLLHUP | _POLLERR)) != 0 {
mode += 'w'
clear |= _POLLOUT
}
// To effect edge-triggered events, we need to be sure to
// update our association with whatever events were not
// set with the event. For example if we are registered
// for POLLIN|POLLOUT, and we get POLLIN, besides waking
// the goroutine interested in POLLIN we have to not forget
// about the one interested in POLLOUT.
if clear != 0 {
lock(&pd.lock)
netpollupdate(pd, 0, uint32(clear))
unlock(&pd.lock)
}
if mode != 0 {
netpollready(&gp, pd, mode)
}
}
if block && gp == 0 {
goto retry
}
return gp.ptr()
}
......@@ -4,23 +4,16 @@
// +build plan9
#include "runtime.h"
#include "malloc.h"
package runtime
// Polls for ready network connections.
// Returns list of goroutines that become runnable.
G*
runtime_netpoll(bool block)
{
func netpoll(block bool) (gp *g) {
// Implementation for platforms that do not support
// integrated network poller.
USED(block);
return nil;
return
}
void
runtime_netpoll_scan(struct Workbuf** wbufp, void (*enqueue1)(struct Workbuf**, Obj))
{
USED(wbufp);
USED(addroot);
func netpollinited() bool {
return false
}
// 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.
package runtime
import (
"unsafe"
)
const _DWORD_MAX = 0xffffffff
const _INVALID_HANDLE_VALUE = ^uintptr(0)
// net_op must be the same as beginning of net.operation. Keep these in sync.
type net_op struct {
// used by windows
o overlapped
// used by netpoll
pd *pollDesc
mode int32
errno int32
qty uint32
}
type overlappedEntry struct {
key uintptr
op *net_op // In reality it's *overlapped, but we cast it to *net_op anyway.
internal uintptr
qty uint32
}
var iocphandle uintptr = _INVALID_HANDLE_VALUE // completion port io handle
func netpollinit() {
iocphandle = stdcall4(_CreateIoCompletionPort, _INVALID_HANDLE_VALUE, 0, 0, _DWORD_MAX)
if iocphandle == 0 {
println("netpoll: failed to create iocp handle (errno=", getlasterror(), ")")
throw("netpoll: failed to create iocp handle")
}
}
func netpollopen(fd uintptr, pd *pollDesc) int32 {
if stdcall4(_CreateIoCompletionPort, fd, iocphandle, 0, 0) == 0 {
return -int32(getlasterror())
}
return 0
}
func netpollclose(fd uintptr) int32 {
// nothing to do
return 0
}
func netpollarm(pd *pollDesc, mode int) {
throw("unused")
}
// Polls for completed network IO.
// Returns list of goroutines that become runnable.
func netpoll(block bool) *g {
var entries [64]overlappedEntry
var wait, qty, key, flags, n, i uint32
var errno int32
var op *net_op
var gp guintptr
mp := getg().m
if iocphandle == _INVALID_HANDLE_VALUE {
return nil
}
wait = 0
if block {
wait = _INFINITE
}
retry:
if _GetQueuedCompletionStatusEx != nil {
n = uint32(len(entries) / int(gomaxprocs))
if n < 8 {
n = 8
}
if block {
mp.blocked = true
}
if stdcall6(_GetQueuedCompletionStatusEx, iocphandle, uintptr(unsafe.Pointer(&entries[0])), uintptr(n), uintptr(unsafe.Pointer(&n)), uintptr(wait), 0) == 0 {
mp.blocked = false
errno = int32(getlasterror())
if !block && errno == _WAIT_TIMEOUT {
return nil
}
println("netpoll: GetQueuedCompletionStatusEx failed (errno=", errno, ")")
throw("netpoll: GetQueuedCompletionStatusEx failed")
}
mp.blocked = false
for i = 0; i < n; i++ {
op = entries[i].op
errno = 0
qty = 0
if stdcall5(_WSAGetOverlappedResult, op.pd.fd, uintptr(unsafe.Pointer(op)), uintptr(unsafe.Pointer(&qty)), 0, uintptr(unsafe.Pointer(&flags))) == 0 {
errno = int32(getlasterror())
}
handlecompletion(&gp, op, errno, qty)
}
} else {
op = nil
errno = 0
qty = 0
if block {
mp.blocked = true
}
if stdcall5(_GetQueuedCompletionStatus, iocphandle, uintptr(unsafe.Pointer(&qty)), uintptr(unsafe.Pointer(&key)), uintptr(unsafe.Pointer(&op)), uintptr(wait)) == 0 {
mp.blocked = false
errno = int32(getlasterror())
if !block && errno == _WAIT_TIMEOUT {
return nil
}
if op == nil {
println("netpoll: GetQueuedCompletionStatus failed (errno=", errno, ")")
throw("netpoll: GetQueuedCompletionStatus failed")
}
// dequeued failed IO packet, so report that
}
mp.blocked = false
handlecompletion(&gp, op, errno, qty)
}
if block && gp == 0 {
goto retry
}
return gp.ptr()
}
func handlecompletion(gpp *guintptr, op *net_op, errno int32, qty uint32) {
if op == nil {
throw("netpoll: GetQueuedCompletionStatus returned op == nil")
}
mode := op.mode
if mode != 'r' && mode != 'w' {
println("netpoll: GetQueuedCompletionStatus returned invalid mode=", mode)
throw("netpoll: GetQueuedCompletionStatus returned invalid mode")
}
op.errno = errno
op.qty = qty
netpollready(gpp, op.pd, mode)
}
......@@ -296,7 +296,7 @@ func casp(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool {
func lock(l *mutex)
func unlock(l *mutex)
// Here for gccgo for Solaris.
// Here for gccgo for netpoll and Solaris.
func errno() int
// Temporary for gccgo until we port proc.go.
......@@ -460,3 +460,9 @@ func setmaxthreads(int) int
func setMaxThreads(in int) (out int) {
return setmaxthreads(in)
}
// Temporary for gccgo until we port atomic_pointer.go.
//go:nosplit
func atomicstorep(ptr unsafe.Pointer, new unsafe.Pointer) {
atomic.StorepNoWB(noescape(ptr), new)
}
......@@ -8,10 +8,6 @@ package runtime
import "unsafe"
// Export temporarily for gccgo's C code to call:
//go:linkname addtimer runtime.addtimer
//go:linkname deltimer runtime.deltimer
// Package time knows the layout of this structure.
// If this struct changes, adjust ../time/sleep.go:/runtimeTimer.
// For GOOS=nacl, package syscall knows the layout of this structure.
......
......@@ -64,6 +64,27 @@ echo "func (ts *timespec) set_nsec(x int32) {" >> ${OUT}
echo " ts.tv_nsec = timespec_nsec_t(x)" >> ${OUT}
echo "}" >> ${OUT}
# Define the epollevent struct. This needs special attention because
# the C definition uses a union and is sometimes packed.
if grep '^const _epoll_data_offset ' ${OUT} >/dev/null 2>&1; then
val=`grep '^const _epoll_data_offset ' ${OUT} | sed -e 's/const _epoll_data_offset = \(.*\)$/\1/'`
if test "$val" = "4"; then
echo 'type epollevent struct { events uint32; data [8]byte }' >> ${OUT}
elif test "$val" = "8"; then
echo 'type epollevent struct { events uint32; pad [4]byte; data [8]byte }' >> ${OUT}
else
echo 1>&2 "unknown epoll data offset value ${val}"
exit 1
fi
fi
# Make sure EPOLLRDHUP and EPOLL_CLOEXEC are defined.
if ! grep '^const _EPOLLRDHUP' ${OUT} >/dev/null 2>&1; then
echo "const _EPOLLRDHUP = 0x2000" >> ${OUT}
fi
if ! grep '^const _EPOLL_CLOEXEC' ${OUT} >/dev/null 2>&1; then
echo "const _EPOLL_CLOEXEC = 02000000" >> ${OUT}
fi
# The semt structure, for Solaris.
grep '^type _sem_t ' gen-sysinfo.go | \
sed -e 's/_sem_t/semt/' >> ${OUT}
......@@ -101,3 +122,14 @@ grep '^type _mac_ipaddr_t ' gen-sysinfo.go | \
grep '^type _mactun_info_t ' gen-sysinfo.go | \
sed -e 's/_in6_addr_t/[16]byte/g' \
>> ${OUT}
# The Solaris port_event_t struct.
grep '^type _port_event_t ' gen-sysinfo.go | \
sed -e s'/_port_event_t/portevent/' \
>> ${OUT}
# The *BSD kevent struct.
grep '^type _kevent ' gen-sysinfo.go | \
sed -e s'/_kevent/keventt/' \
-e 's/ udata [^;}]*/ udata *byte/' \
>> ${OUT}
......@@ -544,4 +544,3 @@ int32 runtime_setgcpercent(int32)
struct Workbuf;
void runtime_proc_scan(struct Workbuf**, void (*)(struct Workbuf**, Obj));
void runtime_netpoll_scan(struct Workbuf**, void (*)(struct Workbuf**, Obj));
......@@ -1277,7 +1277,6 @@ markroot(ParFor *desc, uint32 i)
enqueue1(&wbuf, (Obj){(byte*)&runtime_allp, sizeof runtime_allp, 0});
enqueue1(&wbuf, (Obj){(byte*)&work, sizeof work, 0});
runtime_proc_scan(&wbuf, enqueue1);
runtime_netpoll_scan(&wbuf, enqueue1);
break;
case RootFinalizers:
......
// 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 linux
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include "runtime.h"
#include "defs.h"
#include "malloc.h"
#ifndef EPOLLRDHUP
#define EPOLLRDHUP 0x2000
#endif
#ifndef EPOLL_CLOEXEC
#define EPOLL_CLOEXEC 02000000
#endif
#ifndef HAVE_EPOLL_CREATE1
extern int epoll_create1(int __flags);
#endif
typedef struct epoll_event EpollEvent;
static int32
runtime_epollcreate(int32 size)
{
int r;
r = epoll_create(size);
if(r >= 0)
return r;
return - errno;
}
static int32
runtime_epollcreate1(int32 flags)
{
int r;
r = epoll_create1(flags);
if(r >= 0)
return r;
return - errno;
}
static int32
runtime_epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev)
{
int r;
r = epoll_ctl(epfd, op, fd, ev);
if(r >= 0)
return r;
return - errno;
}
static int32
runtime_epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout)
{
int r;
r = epoll_wait(epfd, ev, nev, timeout);
if(r >= 0)
return r;
return - errno;
}
static void
runtime_closeonexec(int32 fd)
{
fcntl(fd, F_SETFD, FD_CLOEXEC);
}
static int32 epfd = -1; // epoll descriptor
void
runtime_netpollinit(void)
{
epfd = runtime_epollcreate1(EPOLL_CLOEXEC);
if(epfd >= 0)
return;
epfd = runtime_epollcreate(1024);
if(epfd >= 0) {
runtime_closeonexec(epfd);
return;
}
runtime_printf("netpollinit: failed to create descriptor (%d)\n", -epfd);
runtime_throw("netpollinit: failed to create descriptor");
}
int32
runtime_netpollopen(uintptr fd, PollDesc *pd)
{
EpollEvent ev;
int32 res;
ev.events = EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET;
ev.data.ptr = (void*)pd;
res = runtime_epollctl(epfd, EPOLL_CTL_ADD, (int32)fd, &ev);
return -res;
}
int32
runtime_netpollclose(uintptr fd)
{
EpollEvent ev;
int32 res;
res = runtime_epollctl(epfd, EPOLL_CTL_DEL, (int32)fd, &ev);
return -res;
}
void
runtime_netpollarm(PollDesc* pd, int32 mode)
{
USED(pd);
USED(mode);
runtime_throw("unused");
}
// polls for ready network connections
// returns list of goroutines that become runnable
G*
runtime_netpoll(bool block)
{
static int32 lasterr;
EpollEvent events[128], *ev;
int32 n, i, waitms, mode;
G *gp;
if(epfd == -1)
return nil;
waitms = -1;
if(!block)
waitms = 0;
retry:
n = runtime_epollwait(epfd, events, nelem(events), waitms);
if(n < 0) {
if(n != -EINTR && n != lasterr) {
lasterr = n;
runtime_printf("runtime: epollwait on fd %d failed with %d\n", epfd, -n);
}
goto retry;
}
gp = nil;
for(i = 0; i < n; i++) {
ev = &events[i];
if(ev->events == 0)
continue;
mode = 0;
if(ev->events & (EPOLLIN|EPOLLRDHUP|EPOLLHUP|EPOLLERR))
mode += 'r';
if(ev->events & (EPOLLOUT|EPOLLHUP|EPOLLERR))
mode += 'w';
if(mode)
runtime_netpollready(&gp, (void*)ev->data.ptr, mode);
}
if(block && gp == nil)
goto retry;
return gp;
}
void
runtime_netpoll_scan(struct Workbuf** wbufp, void (*enqueue1)(struct Workbuf**, Obj))
{
USED(wbufp);
USED(enqueue1);
}
// 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 netbsd openbsd
#include "runtime.h"
#include "defs.h"
#include "malloc.h"
// Integrated network poller (kqueue-based implementation).
int32 runtime_kqueue(void);
int32 runtime_kevent(int32, Kevent*, int32, Kevent*, int32, Timespec*);
void runtime_closeonexec(int32);
static int32 kq = -1;
void
runtime_netpollinit(void)
{
kq = runtime_kqueue();
if(kq < 0) {
runtime_printf("netpollinit: kqueue failed with %d\n", -kq);
runtime_throw("netpollinit: kqueue failed");
}
runtime_closeonexec(kq);
}
int32
runtime_netpollopen(uintptr fd, PollDesc *pd)
{
Kevent ev[2];
int32 n;
// Arm both EVFILT_READ and EVFILT_WRITE in edge-triggered mode (EV_CLEAR)
// for the whole fd lifetime. The notifications are automatically unregistered
// when fd is closed.
ev[0].ident = (uint32)fd;
ev[0].filter = EVFILT_READ;
ev[0].flags = EV_ADD|EV_CLEAR;
ev[0].fflags = 0;
ev[0].data = 0;
ev[0].udata = (kevent_udata)pd;
ev[1] = ev[0];
ev[1].filter = EVFILT_WRITE;
n = runtime_kevent(kq, ev, 2, nil, 0, nil);
if(n < 0)
return -n;
return 0;
}
int32
runtime_netpollclose(uintptr fd)
{
// Don't need to unregister because calling close()
// on fd will remove any kevents that reference the descriptor.
USED(fd);
return 0;
}
void
runtime_netpollarm(PollDesc* pd, int32 mode)
{
USED(pd, mode);
runtime_throw("unused");
}
// Polls for ready network connections.
// Returns list of goroutines that become runnable.
G*
runtime_netpoll(bool block)
{
static int32 lasterr;
Kevent events[64], *ev;
Timespec ts, *tp;
int32 n, i, mode;
G *gp;
if(kq == -1)
return nil;
tp = nil;
if(!block) {
ts.tv_sec = 0;
ts.tv_nsec = 0;
tp = &ts;
}
gp = nil;
retry:
n = runtime_kevent(kq, nil, 0, events, nelem(events), tp);
if(n < 0) {
if(n != -EINTR && n != lasterr) {
lasterr = n;
runtime_printf("runtime: kevent on fd %d failed with %d\n", kq, -n);
}
goto retry;
}
for(i = 0; i < n; i++) {
ev = &events[i];
mode = 0;
if(ev->filter == EVFILT_READ)
mode += 'r';
if(ev->filter == EVFILT_WRITE)
mode += 'w';
if(mode)
runtime_netpollready(&gp, (PollDesc*)ev->udata, mode);
}
if(block && gp == nil)
goto retry;
return gp;
}
void
runtime_netpoll_scan(struct Workbuf** wbufp, void (*enqueue1)(struct Workbuf**, Obj))
{
USED(wbufp);
USED(enqueue1);
}
// 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 solaris
#include "config.h"
#include <errno.h>
#include <sys/times.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include "runtime.h"
#include "malloc.h"
static Lock selectlock;
static int rdwake;
static int wrwake;
static fd_set fds;
static PollDesc **data;
static int allocated;
void
runtime_netpollinit(void)
{
int p[2];
int fl;
FD_ZERO(&fds);
allocated = 128;
data = runtime_mallocgc(allocated * sizeof(PollDesc *), 0,
FlagNoScan|FlagNoProfiling|FlagNoInvokeGC);
if(pipe(p) < 0)
runtime_throw("netpollinit: failed to create pipe");
rdwake = p[0];
wrwake = p[1];
fl = fcntl(rdwake, F_GETFL);
if(fl < 0)
runtime_throw("netpollinit: fcntl failed");
fl |= O_NONBLOCK;
if(fcntl(rdwake, F_SETFL, fl))
runtime_throw("netpollinit: fcntl failed");
fcntl(rdwake, F_SETFD, FD_CLOEXEC);
fl = fcntl(wrwake, F_GETFL);
if(fl < 0)
runtime_throw("netpollinit: fcntl failed");
fl |= O_NONBLOCK;
if(fcntl(wrwake, F_SETFL, fl))
runtime_throw("netpollinit: fcntl failed");
fcntl(wrwake, F_SETFD, FD_CLOEXEC);
FD_SET(rdwake, &fds);
}
int32
runtime_netpollopen(uintptr fd, PollDesc *pd)
{
byte b;
runtime_lock(&selectlock);
if((int)fd >= allocated) {
int c;
PollDesc **n;
c = allocated;
runtime_unlock(&selectlock);
while((int)fd >= c)
c *= 2;
n = runtime_mallocgc(c * sizeof(PollDesc *), 0,
FlagNoScan|FlagNoProfiling|FlagNoInvokeGC);
runtime_lock(&selectlock);
if(c > allocated) {
__builtin_memcpy(n, data, allocated * sizeof(PollDesc *));
allocated = c;
data = n;
}
}
FD_SET(fd, &fds);
data[fd] = pd;
runtime_unlock(&selectlock);
b = 0;
write(wrwake, &b, sizeof b);
return 0;
}
int32
runtime_netpollclose(uintptr fd)
{
byte b;
runtime_lock(&selectlock);
FD_CLR(fd, &fds);
data[fd] = nil;
runtime_unlock(&selectlock);
b = 0;
write(wrwake, &b, sizeof b);
return 0;
}
/* Used to avoid using too much stack memory. */
static bool inuse;
static fd_set grfds, gwfds, gefds, gtfds;
G*
runtime_netpoll(bool block)
{
fd_set *prfds, *pwfds, *pefds, *ptfds;
bool allocatedfds;
struct timeval timeout;
struct timeval *pt;
int max, c, i;
G *gp;
int32 mode;
byte b;
struct stat st;
allocatedfds = false;
retry:
runtime_lock(&selectlock);
max = allocated;
if(max == 0) {
runtime_unlock(&selectlock);
return nil;
}
if(inuse) {
if(!allocatedfds) {
prfds = runtime_SysAlloc(4 * sizeof fds, &mstats()->other_sys);
pwfds = prfds + 1;
pefds = pwfds + 1;
ptfds = pefds + 1;
allocatedfds = true;
}
} else {
prfds = &grfds;
pwfds = &gwfds;
pefds = &gefds;
ptfds = &gtfds;
inuse = true;
allocatedfds = false;
}
__builtin_memcpy(prfds, &fds, sizeof fds);
runtime_unlock(&selectlock);
__builtin_memcpy(pwfds, prfds, sizeof fds);
FD_CLR(rdwake, pwfds);
__builtin_memcpy(pefds, pwfds, sizeof fds);
__builtin_memcpy(ptfds, pwfds, sizeof fds);
__builtin_memset(&timeout, 0, sizeof timeout);
pt = &timeout;
if(block)
pt = nil;
c = select(max, prfds, pwfds, pefds, pt);
if(c < 0) {
if(errno == EBADF) {
// Some file descriptor has been closed.
// Check each one, and treat each closed
// descriptor as ready for read/write.
c = 0;
FD_ZERO(prfds);
FD_ZERO(pwfds);
FD_ZERO(pefds);
for(i = 0; i < max; i++) {
if(FD_ISSET(i, ptfds)
&& fstat(i, &st) < 0
&& errno == EBADF) {
FD_SET(i, prfds);
FD_SET(i, pwfds);
c += 2;
}
}
}
else {
if(errno != EINTR)
runtime_printf("runtime: select failed with %d\n", errno);
goto retry;
}
}
gp = nil;
for(i = 0; i < max && c > 0; i++) {
mode = 0;
if(FD_ISSET(i, prfds)) {
mode += 'r';
--c;
}
if(FD_ISSET(i, pwfds)) {
mode += 'w';
--c;
}
if(FD_ISSET(i, pefds)) {
mode = 'r' + 'w';
--c;
}
if(i == rdwake && mode != 0) {
while(read(rdwake, &b, sizeof b) > 0)
;
continue;
}
if(mode) {
PollDesc *pd;
runtime_lock(&selectlock);
pd = data[i];
runtime_unlock(&selectlock);
if(pd != nil)
runtime_netpollready(&gp, pd, mode);
}
}
if(block && gp == nil)
goto retry;
if(allocatedfds) {
runtime_SysFree(prfds, 4 * sizeof fds, &mstats()->other_sys);
} else {
runtime_lock(&selectlock);
inuse = false;
runtime_unlock(&selectlock);
}
return gp;
}
void
runtime_netpoll_scan(struct Workbuf** wbufp, void (*enqueue1)(struct Workbuf**, Obj))
{
enqueue1(wbufp, (Obj){(byte*)&data, sizeof data, 0});
}
......@@ -391,21 +391,8 @@ int64 runtime_tickspersecond(void)
__asm__ (GOSYM_PREFIX "runtime.tickspersecond");
void runtime_blockevent(int64, int32);
extern int64 runtime_blockprofilerate;
void runtime_addtimer(Timer*)
__asm__ (GOSYM_PREFIX "runtime.addtimer");
bool runtime_deltimer(Timer*)
__asm__ (GOSYM_PREFIX "runtime.deltimer");
G* runtime_netpoll(bool);
void runtime_netpollinit(void);
int32 runtime_netpollopen(uintptr, PollDesc*);
int32 runtime_netpollclose(uintptr);
void runtime_netpollready(G**, PollDesc*, int32);
uintptr runtime_netpollfd(PollDesc*);
void runtime_netpollarm(PollDesc*, int32);
void** runtime_netpolluser(PollDesc*);
bool runtime_netpollclosing(PollDesc*);
void runtime_netpolllock(PollDesc*);
void runtime_netpollunlock(PollDesc*);
G* runtime_netpoll(bool)
__asm__ (GOSYM_PREFIX "runtime.netpoll");
void runtime_crash(void);
void runtime_parsedebugvars(void)
__asm__(GOSYM_PREFIX "runtime.parsedebugvars");
......
......@@ -9,6 +9,7 @@
#include "config.h"
#include <stddef.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
......@@ -49,6 +50,9 @@
#if defined(HAVE_SYS_EPOLL_H)
#include <sys/epoll.h>
#endif
#if defined(HAVE_SYS_EVENT_H)
#include <sys/event.h>
#endif
#if defined(HAVE_SYS_FILE_H)
#include <sys/file.h>
#endif
......@@ -155,6 +159,9 @@
#if defined(HAVE_SEMAPHORE_H)
#include <semaphore.h>
#endif
#if defined(HAVE_PORT_H)
#include <port.h>
#endif
/* Constants that may only be defined as expressions on some systems,
expressions too complex for -fdump-go-spec to handle. These are
......@@ -260,3 +267,9 @@ enum {
NLA_HDRLEN_val = NLA_HDRLEN,
#endif
};
#if defined(HAVE_SYS_EPOLL_H)
enum {
epoll_data_offset = offsetof(struct epoll_event, data)
};
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment