| #define NOPLAN9DEFINES |
| #include <u.h> |
| #include <libc.h> |
| #include <thread.h> |
| |
| #include <errno.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| |
| #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 |
| |
| /* |
| * 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()){ |
| if(debugpoll) fprint(2, "poll %d:", npoll); |
| for(i=0; i<npoll; i++){ |
| if(debugpoll) fprint(2, " %d%c", pfd[i].fd, pfd[i].events==POLLIN ? 'r' : 'w'); |
| pfd[i].revents = 0; |
| } |
| t = -1; |
| now = p9nsec()/1000000; |
| for(i=0; i<nsleep; i++){ |
| n = sleeptime[i] - now; |
| if(debugpoll) fprint(2, " s%d", n); |
| if(n < 0) |
| n = 0; |
| if(t == -1 || n < t) |
| t = n; |
| } |
| if(debugpoll) fprint(2, "; t=%d\n", t); |
| |
| 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; |
| nbsendul(polls[i].c, 1); |
| //fprint(2, " x%d", pfd[i].fd); |
| } |
| //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) |
| { |
| 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; |
| if(0) fprint(2, "%s [%3d] fdwait %d %c list *0x%lux\n", |
| argv0, threadid(), fd, rw, pc); |
| recvul(&s.c); |
| } |
| |
| void |
| threadfdwait(int fd, int rw) |
| { |
| _threadfdwait(fd, rw, getcallerpc(&fd)); |
| } |
| |
| 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)); |
| 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)); |
| 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)); |
| 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)); |
| 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; |
| } |
| |