blob: 033661d17f243134f3656d3e1f8d02dd4946abf4 [file] [log] [blame]
// 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});
}