/* This file is in the public domain. */ #include #include #include #include #include #include "impl.h" typedef struct iofd IOFD; typedef struct blockfn BLOCKFN; struct iofd { int fd; int (*rtest)(void *); int (*wtest)(void *); void (*rd)(void *); void (*wr)(void *); void *arg; unsigned int flags; #define IOFF_WANT_R 0x00000001 #define IOFF_WANT_W 0x00000002 #define IOFF_DEAD 0x00000004 int px; } ; struct blockfn { int (*fn)(void *); void *arg; unsigned int flags; #define BFF_DEAD 0x00000001 } ; struct aio_loop { IOFD **iofds; int niofds; BLOCKFN **blockfns; int nblockfns; int cblockfns; struct pollfd *pfds; int pfdn; int pfdmax; int haveblock; int justloop; } ; static AIO_LOOP gloop; static void aio_poll_init_(AIO_LOOP *l) { l->iofds = 0; l->niofds = 0; l->blockfns = 0; l->nblockfns = 0; l->cblockfns = 0; l->pfds = 0; l->pfdn = 0; l->pfdmax = 0; l->justloop = 0; } static void aio_cleanup(AIO_LOOP *l) { free(l->iofds); free(l->blockfns); free(l->pfds); aio_poll_init_(l); } AIO_LOOP *aio_poll_init_of(void) { AIO_LOOP *l; l = malloc(sizeof(AIO_LOOP)); if (l) aio_poll_init_(l); return(l); } void aio_poll_done_of(AIO_LOOP *l) { if (l) { aio_cleanup(l); free(l); } } void aio_poll_init(void) { aio_poll_init_(&gloop); } void aio_poll_reinit(void) { aio_cleanup(&gloop); } int aio_add_poll_of(AIO_LOOP *l, int fd, int (*rtest)(void *), int (*wtest)(void *), void (*rd)(void *), void (*wr)(void *), void *arg) { IOFD *io; int i; IOFD **t; io = malloc(sizeof(IOFD)); if (! io) return(AIO_ERR); do <"gotspace"> { for (i=l->niofds-1;i>=0;i--) if (l->iofds[i] == 0) break <"gotspace">; i = l->niofds; t = realloc(l->iofds,sizeof(IOFD *)*++l->niofds); if (t == 0) { free(io); return(AIO_ERR); } l->iofds = t; } while (0); l->iofds[i] = io; io->fd = fd; io->rtest = rtest; io->wtest = wtest; io->rd = rd; io->wr = wr; io->arg = arg; io->flags = 0; return(i); } int aio_add_poll(int fd, int (*rtest)(void *), int (*wtest)(void *), void (*rd)(void *), void (*wr)(void *), void *arg) { return(aio_add_poll_of(&gloop,fd,rtest,wtest,rd,wr,arg)); } int aio_add_block_of(AIO_LOOP *l, int (*fn)(void *), void *arg) { BLOCKFN *bf; int i; BLOCKFN **t; bf = malloc(sizeof(BLOCKFN)); if (! bf) return(AIO_ERR); do <"gotspace"> { for (i=l->nblockfns-1;i>=0;i--) if (l->blockfns[i] == 0) break <"gotspace">; i = l->nblockfns; t = realloc(l->blockfns,sizeof(BLOCKFN *)*++l->nblockfns); if (! t) { free(bf); return(AIO_ERR); } l->blockfns = t; } while (0); l->blockfns[i] = bf; bf->fn = fn; bf->arg = arg; bf->flags = 0; l->cblockfns ++; return(i); } int aio_add_block(int (*fn)(void *), void *arg) { return(aio_add_block_of(&gloop,fn,arg)); } void aio_remove_poll_of(AIO_LOOP *l, int id) { if ((id < 0) || (id >= l->niofds) || !l->iofds[id]) panic("id not found"); l->iofds[id]->flags |= IOFF_DEAD; } void aio_remove_poll(int id) { aio_remove_poll_of(&gloop,id); } void aio_remove_block_of(AIO_LOOP *l, int id) { if ((id < 0) || (id >= l->nblockfns) || !l->blockfns[id]) panic("id not found"); l->blockfns[id]->flags |= BFF_DEAD; } void aio_remove_block(int id) { aio_remove_block_of(&gloop,id); } int aio_pre_poll_v_of(int n, AIO_LOOP **v) { int i; IOFD *io; int j; int pfdn; int pfdmax; struct pollfd *pfds; AIO_LOOP *l; int hb; struct pollfd *t; pfdn = 0; pfdmax = v[0]->pfdmax; pfds = v[0]->pfds; hb = 0; for (j=n-1;j>=0;j--) { l = v[j]; if (l->cblockfns) hb = 1; for (i=l->niofds-1;i>=0;i--) { io = l->iofds[i]; if (! io) continue; if (io->flags & IOFF_DEAD) { free(io); l->iofds[i] = 0; continue; } io->flags = (io->flags & ~(IOFF_WANT_R|IOFF_WANT_W)) | ((*io->rtest)(io->arg) ? IOFF_WANT_R : 0) | ((*io->wtest)(io->arg) ? IOFF_WANT_W : 0); if (io->flags & (IOFF_WANT_R|IOFF_WANT_W)) { struct pollfd *pfd; if (pfdn >= pfdmax) { t = realloc(pfds,(pfdmax=pfdn+8)*sizeof(*pfds)); if (! t) return(-1); pfds = t; } io->px = pfdn++; pfd = &pfds[io->px]; pfd->fd = io->fd; pfd->events = ((io->flags & IOFF_WANT_R) ? POLLIN|POLLRDNORM : 0) | ((io->flags & IOFF_WANT_W) ? POLLOUT|POLLWRNORM : 0); } else { io->px = -1; } } } v[0]->pfds = pfds; v[0]->pfdmax = pfdmax; v[0]->pfdn = pfdn; v[0]->haveblock = hb; return(0); } int aio_pre_poll_l_of(int n, ...) { va_list ap; AIO_LOOP **v; int i; v = malloc(n*sizeof(AIO_LOOP *)); if (! v) return(-1); va_start(ap,n); for (i=0;ihaveblock) { prv = poll(v[0]->pfds,v[0]->pfdn,0); if (prv != 0) return(n); again = 0; for (j=n-1;j>=0;j--) { l = v[j]; for (i=l->nblockfns-1;i>=0;i--) { bf = l->blockfns[i]; if (! bf) continue; if (bf->flags & BFF_DEAD) { free(bf); l->blockfns[i] = 0; l->cblockfns --; continue; } us = (*bf->fn)(bf->arg); if (us < 0) { switch (us) { case AIO_BLOCK_NIL: break; case AIO_BLOCK_LOOP: again = 1; break; default: panic("bad status"); break; } } else { if ((tmo == INFTIM) || (us < tmo)) tmo = us; } } } if (again) { v[0]->justloop = 1; return(0); } } return(poll(v[0]->pfds,v[0]->pfdn,tmo)); } int aio_do_poll_l_of(int n, ...) { va_list ap; AIO_LOOP **v; int i; v = malloc(n*sizeof(AIO_LOOP *)); if (! v) { errno = ENOMEM; return(-1); } va_start(ap,n); for (i=0;ijustloop) { v[0]->justloop = 0; return; } pfds = v[0]->pfds; for (j=n-1;j>=0;j--) { l = v[j]; for (i=l->niofds-1;i>=0;i--) { io = l->iofds[i]; if (! io) continue; if (io->flags & IOFF_DEAD) continue; if ( (io->flags & IOFF_WANT_R) && (pfds[io->px].revents & (POLLIN|POLLRDNORM|POLLERR|POLLHUP|POLLNVAL)) ) (*io->rd)(io->arg); if ( (io->flags & IOFF_WANT_W) && (pfds[io->px].revents & (POLLOUT|POLLWRNORM|POLLERR|POLLHUP|POLLNVAL)) ) (*io->wr)(io->arg); } } } int aio_post_poll_l_of(int n, ...) { va_list ap; AIO_LOOP **v; int i; v = malloc(n*sizeof(AIO_LOOP *)); if (! v) return(-1); va_start(ap,n); for (i=0;i