plan9port/src/libthread/fdwait.c

337 lines
5.3 KiB
C
Raw Normal View History

2004-02-29 22:53:51 +00:00
#define NOPLAN9DEFINES
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
2004-03-02 16:58:49 +00:00
#define debugpoll 0
#ifdef __APPLE__
#include <sys/time.h>
enum { POLLIN=1, POLLOUT=2, POLLERR=4 };
struct pollfd
{
int fd;
int events;
int revents;
};
int
poll(struct pollfd *p, int np, int ms)
{
int i, maxfd, n;
struct timeval tv, *tvp;
fd_set rfd, wfd, efd;
maxfd = -1;
FD_ZERO(&rfd);
FD_ZERO(&wfd);
FD_ZERO(&efd);
for(i=0; i<np; i++){
p[i].revents = 0;
if(p[i].fd == -1)
continue;
if(p[i].fd > maxfd)
maxfd = p[i].fd;
if(p[i].events & POLLIN)
FD_SET(p[i].fd, &rfd);
if(p[i].events & POLLOUT)
FD_SET(p[i].fd, &wfd);
FD_SET(p[i].fd, &efd);
}
if(ms != -1){
tv.tv_usec = (ms%1000)*1000;
tv.tv_sec = ms/1000;
tvp = &tv;
}else
tvp = nil;
if(debugpoll){
fprint(2, "select %d:", maxfd+1);
for(i=0; i<=maxfd; i++){
if(FD_ISSET(i, &rfd))
fprint(2, " r%d", i);
if(FD_ISSET(i, &wfd))
fprint(2, " w%d", i);
if(FD_ISSET(i, &efd))
fprint(2, " e%d", i);
}
fprint(2, "; tp=%p, t=%d.%d\n", tvp, tv.tv_sec, tv.tv_usec);
}
n = select(maxfd+1, &rfd, &wfd, &efd, tvp);
if(n <= 0)
return n;
for(i=0; i<np; i++){
if(p[i].fd == -1)
continue;
if(FD_ISSET(p[i].fd, &rfd))
p[i].revents |= POLLIN;
if(FD_ISSET(p[i].fd, &wfd))
p[i].revents |= POLLOUT;
if(FD_ISSET(p[i].fd, &efd))
p[i].revents |= POLLERR;
}
return n;
}
#else
#include <poll.h>
#endif
2004-02-29 22:53:51 +00:00
/*
* Poll file descriptors in an idle loop.
*/
typedef struct Poll Poll;
struct Poll
{
Channel *c; /* for sending back */
};
static Channel *sleepchan[64];
static int sleeptime[64];
static int nsleep;
static struct pollfd pfd[64];
static struct Poll polls[64];
static int npoll;
static void
pollidle(void *v)
{
int i, n, t;
uint now;
for(;; yield()){
2004-03-02 16:58:49 +00:00
if(debugpoll) fprint(2, "poll %d:", npoll);
2004-02-29 22:53:51 +00:00
for(i=0; i<npoll; i++){
2004-03-02 16:58:49 +00:00
if(debugpoll) fprint(2, " %d%c", pfd[i].fd, pfd[i].events==POLLIN ? 'r' : 'w');
2004-02-29 22:53:51 +00:00
pfd[i].revents = 0;
}
t = -1;
2004-03-02 16:58:49 +00:00
now = p9nsec()/1000000;
2004-02-29 22:53:51 +00:00
for(i=0; i<nsleep; i++){
n = sleeptime[i] - now;
2004-03-02 16:58:49 +00:00
if(debugpoll) fprint(2, " s%d", n);
2004-02-29 22:53:51 +00:00
if(n < 0)
n = 0;
if(t == -1 || n < t)
t = n;
}
2004-03-02 16:58:49 +00:00
if(debugpoll) fprint(2, "; t=%d\n", t);
2004-02-29 22:53:51 +00:00
n = poll(pfd, npoll, t);
//fprint(2, "poll ret %d:", n);
now = p9nsec()/1000000;
for(i=0; i<nsleep; i++){
if((int)(sleeptime[i] - now) < 0){
nbsendul(sleepchan[i], 0);
nsleep--;
sleepchan[i] = sleepchan[nsleep];
sleeptime[i] = sleeptime[nsleep];
i--;
}
}
if(n <= 0)
continue;
for(i=0; i<npoll; i++)
if(pfd[i].fd != -1 && pfd[i].revents){
//fprint(2, " %d", pfd[i].fd);
pfd[i].fd = -1;
pfd[i].events = 0;
pfd[i].revents = 0;
2004-03-05 03:13:13 +00:00
nbsendul(polls[i].c, 1);
//fprint(2, " x%d", pfd[i].fd);
2004-02-29 22:53:51 +00:00
}
//fprint(2, "\n");
}
}
void
threadfdwaitsetup(void)
{
static int setup = 0;
if(!setup){
setup = 1;
threadcreateidle(pollidle, nil, 16384);
}
}
void
_threadfdwait(int fd, int rw, ulong pc)
2004-02-29 22:53:51 +00:00
{
int i;
struct {
Channel c;
ulong x;
} s;
threadfdwaitsetup();
chaninit(&s.c, sizeof(ulong), 1);
for(i=0; i<npoll; i++)
if(pfd[i].fd == -1)
break;
if(i==npoll){
if(npoll >= nelem(polls)){
fprint(2, "Too many polled fds.\n");
abort();
}
npoll++;
}
pfd[i].fd = fd;
pfd[i].events = rw=='r' ? POLLIN : POLLOUT;
polls[i].c = &s.c;
2004-03-05 03:13:13 +00:00
if(0) fprint(2, "%s [%3d] fdwait %d %c list *0x%lux\n",
argv0, threadid(), fd, rw, pc);
2004-02-29 22:53:51 +00:00
recvul(&s.c);
}
void
threadfdwait(int fd, int rw)
{
_threadfdwait(fd, rw, getcallerpc(&fd));
}
2004-02-29 22:53:51 +00:00
void
threadsleep(int ms)
{
struct {
Channel c;
ulong x;
} s;
threadfdwaitsetup();
chaninit(&s.c, sizeof(ulong), 1);
sleepchan[nsleep] = &s.c;
sleeptime[nsleep++] = p9nsec()/1000000+ms;
recvul(&s.c);
}
void
threadfdnoblock(int fd)
{
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0)|O_NONBLOCK);
}
long
threadread(int fd, void *a, long n)
{
int nn;
threadfdnoblock(fd);
again:
nn = read(fd, a, n);
if(nn < 0){
if(errno == EINTR)
goto again;
if(errno == EAGAIN || errno == EWOULDBLOCK){
_threadfdwait(fd, 'r', getcallerpc(&fd));
2004-02-29 22:53:51 +00:00
goto again;
}
}
return nn;
}
int
threadrecvfd(int fd)
{
int nn;
threadfdnoblock(fd);
again:
nn = recvfd(fd);
if(nn < 0){
if(errno == EINTR)
goto again;
if(errno == EAGAIN || errno == EWOULDBLOCK){
_threadfdwait(fd, 'r', getcallerpc(&fd));
2004-02-29 22:53:51 +00:00
goto again;
}
}
return nn;
}
int
threadsendfd(int fd, int sfd)
{
int nn;
threadfdnoblock(fd);
again:
nn = sendfd(fd, sfd);
if(nn < 0){
if(errno == EINTR)
goto again;
if(errno == EAGAIN || errno == EWOULDBLOCK){
_threadfdwait(fd, 'w', getcallerpc(&fd));
2004-02-29 22:53:51 +00:00
goto again;
}
}
return nn;
}
long
threadreadn(int fd, void *a, long n)
{
int tot, nn;
for(tot = 0; tot<n; tot+=nn){
nn = threadread(fd, (char*)a+tot, n-tot);
if(nn <= 0){
if(tot == 0)
return nn;
return tot;
}
}
return tot;
}
long
_threadwrite(int fd, const void *a, long n)
{
int nn;
threadfdnoblock(fd);
again:
nn = write(fd, a, n);
if(nn < 0){
if(errno == EINTR)
goto again;
if(errno == EAGAIN || errno == EWOULDBLOCK){
_threadfdwait(fd, 'w', getcallerpc(&fd));
2004-02-29 22:53:51 +00:00
goto again;
}
}
return nn;
}
long
threadwrite(int fd, const void *a, long n)
{
int tot, nn;
for(tot = 0; tot<n; tot+=nn){
nn = _threadwrite(fd, (char*)a+tot, n-tot);
if(nn <= 0){
if(tot == 0)
return nn;
return tot;
}
}
return tot;
}