ae026741df
PR go/61303 runtime: don't overallocate in select code If we've already allocated an fd_set, don't allocate another one. Also, don't bother to read from rdwake if it wasn't returned in select. Fixes https://gcc.gnu.org/PR61303. Reviewed-on: https://go-review.googlesource.com/17243 From-SVN: r230922
257 lines
4.6 KiB
C
257 lines
4.6 KiB
C
// 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 = >fds;
|
|
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});
|
|
}
|