#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; }