=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/aucat/Attic/file.c,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- src/usr.bin/aucat/Attic/file.c 2009/02/04 20:35:14 1.11 +++ src/usr.bin/aucat/Attic/file.c 2009/07/25 08:44:27 1.12 @@ -1,4 +1,4 @@ -/* $OpenBSD: file.c,v 1.11 2009/02/04 20:35:14 ratchov Exp $ */ +/* $OpenBSD: file.c,v 1.12 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov * @@ -21,7 +21,28 @@ * (operation will block), then the file is marked as "for polling", else * the file is not polled again. * + * the module also provides trivial timeout implementation, + * derived from: + * + * anoncvs@moule.caoua.org:/cvs/midish/timo.c rev 1.16 + * + * A timeout is used to schedule the call of a routine (the callback) + * there is a global list of timeouts that is processed inside the + * event loop. Timeouts work as follows: + * + * first the timo structure must be initialized with timo_set() + * + * then the timeout is scheduled (only once) with timo_add() + * + * if the timeout expires, the call-back is called; then it can + * be scheduled again if needed. It's OK to reschedule it again + * from the callback + * + * the timeout can be aborted with timo_del(), it is OK to try to + * abort a timout that has expired + * */ + #include #include @@ -40,9 +61,135 @@ #define MAXFDS 100 extern struct fileops listen_ops, pipe_ops; + +struct timeval file_tv; struct filelist file_list; +struct timo *timo_queue; +unsigned timo_abstime; +/* + * initialise a timeout structure, arguments are callback and argument + * that will be passed to the callback + */ void +timo_set(struct timo *o, void (*cb)(void *), void *arg) +{ + o->cb = cb; + o->arg = arg; + o->set = 0; +} + +/* + * schedule the callback in 'delta' 24-th of microseconds. The timeout + * must not be already scheduled + */ +void +timo_add(struct timo *o, unsigned delta) +{ + struct timo **i; + unsigned val; + int diff; + +#ifdef DEBUG + if (o->set) { + fprintf(stderr, "timo_set: already set\n"); + abort(); + } + if (delta == 0) { + fprintf(stderr, "timo_set: zero timeout is evil\n"); + abort(); + } +#endif + val = timo_abstime + delta; + for (i = &timo_queue; *i != NULL; i = &(*i)->next) { + diff = (*i)->val - val; + if (diff > 0) { + break; + } + } + o->set = 1; + o->val = val; + o->next = *i; + *i = o; +} + +/* + * abort a scheduled timeout + */ +void +timo_del(struct timo *o) +{ + struct timo **i; + + for (i = &timo_queue; *i != NULL; i = &(*i)->next) { + if (*i == o) { + *i = o->next; + o->set = 0; + return; + } + } + DPRINTF("timo_del: not found\n"); +} + +/* + * routine to be called by the timer when 'delta' 24-th of microsecond + * elapsed. This routine updates time referece used by timeouts and + * calls expired timeouts + */ +void +timo_update(unsigned delta) +{ + struct timo *to; + int diff; + + /* + * update time reference + */ + timo_abstime += delta; + + /* + * remove from the queue and run expired timeouts + */ + while (timo_queue != NULL) { + /* + * there is no overflow here because + and - are + * modulo 2^32, they are the same for both signed and + * unsigned integers + */ + diff = timo_queue->val - timo_abstime; + if (diff > 0) + break; + to = timo_queue; + timo_queue = to->next; + to->set = 0; + to->cb(to->arg); + } +} + +/* + * initialize timeout queue + */ +void +timo_init(void) +{ + timo_queue = NULL; + timo_abstime = 0; +} + +/* + * destroy timeout queue + */ +void +timo_done(void) +{ + if (timo_queue != NULL) { + fprintf(stderr, "timo_done: timo_queue not empty!\n"); + abort(); + } + timo_queue = (struct timo *)0xdeadbeef; +} + +void file_dprint(int n, struct file *f) { #ifdef DEBUG @@ -80,7 +227,6 @@ f->state = 0; f->rproc = NULL; f->wproc = NULL; - f->refs = 0; LIST_INSERT_HEAD(&file_list, f, entry); DPRINTF("file_new: %s:%s\n", ops->name, f->name); return f; @@ -91,7 +237,7 @@ { DPRINTF("file_del: "); file_dprint(1, f); - if (f->refs > 0) { + if (f->state & (FILE_RINUSE | FILE_WINUSE)) { DPRINTF(": delayed\n"); f->state |= FILE_ZOMB; return; @@ -111,9 +257,9 @@ struct pollfd pfds[MAXFDS]; struct file *f, *fnext; struct aproc *p; -#ifdef DEBUG - unsigned nused, nfound; -#endif + struct timeval tv; + long delta_usec; + int timo; /* * fill the pfds[] array with files that are blocked on reading @@ -121,22 +267,12 @@ */ DPRINTFN(4, "file_poll:"); nfds = 0; -#ifdef DEBUG - nused = 0; - nfound = 0; -#endif LIST_FOREACH(f, &file_list, entry) { events = 0; if (f->rproc && !(f->state & FILE_ROK)) events |= POLLIN; if (f->wproc && !(f->state & FILE_WOK)) events |= POLLOUT; -#ifdef DEBUG - if (events) - nused++; - if (f->rproc || f->wproc) - nfound++; -#endif DPRINTFN(4, " %s(%x)", f->name, events); n = f->ops->pollfd(f, pfds + nfds, events); if (n == 0) { @@ -147,23 +283,35 @@ nfds += n; } DPRINTFN(4, "\n"); - -#ifdef DEBUG - if (nused == 0 && nfound > 0) { - fprintf(stderr, "file_poll: deadlock\n"); - abort(); + if (debug_level >= 4) { + DPRINTF("file_poll: pfds[] ="); + for (n = 0; n < nfds; n++) + DPRINTF(" %x", pfds[n].events); + DPRINTF("\n"); } -#endif if (LIST_EMPTY(&file_list)) { DPRINTF("file_poll: nothing to do...\n"); return 0; } if (nfds > 0) { - if (poll(pfds, nfds, -1) < 0) { + if (timo_queue) { + timo = (timo_queue->val - timo_abstime) / (2 * 1000); + if (timo == 0) + timo = 1; + } else + timo = -1; + if (poll(pfds, nfds, timo) < 0) { if (errno == EINTR) return 1; err(1, "file_poll: poll failed"); } + gettimeofday(&tv, NULL); + delta_usec = 1000000L * (tv.tv_sec - file_tv.tv_sec); + delta_usec += tv.tv_usec - file_tv.tv_usec; + if (delta_usec > 0) { + file_tv = tv; + timo_update(delta_usec); + } } f = LIST_FIRST(&file_list); while (f != LIST_END(&file_list)) { @@ -171,27 +319,30 @@ f = LIST_NEXT(f, entry); continue; } - f->refs++; revents = f->ops->revents(f, f->pfd); if (!(f->state & FILE_ZOMB) && (revents & POLLIN)) { revents &= ~POLLIN; f->state |= FILE_ROK; DPRINTFN(3, "file_poll: %s rok\n", f->name); + f->state |= FILE_RINUSE; for (;;) { p = f->rproc; if (!p || !p->ops->in(p, NULL)) break; } + f->state &= ~FILE_RINUSE; } if (!(f->state & FILE_ZOMB) && (revents & POLLOUT)) { revents &= ~POLLOUT; f->state |= FILE_WOK; DPRINTFN(3, "file_poll: %s wok\n", f->name); + f->state |= FILE_WINUSE; for (;;) { p = f->wproc; if (!p || !p->ops->out(p, NULL)) break; } + f->state &= ~FILE_WINUSE; } if (!(f->state & FILE_ZOMB) && (revents & POLLHUP)) { DPRINTFN(2, "file_poll: %s: disconnected\n", f->name); @@ -200,18 +351,23 @@ if (!(f->state & FILE_ZOMB) && (f->state & FILE_EOF)) { DPRINTFN(2, "file_poll: %s: eof\n", f->name); p = f->rproc; - if (p) + if (p) { + f->state |= FILE_RINUSE; p->ops->eof(p, NULL); + f->state &= ~FILE_RINUSE; + } f->state &= ~FILE_EOF; } if (!(f->state & FILE_ZOMB) && (f->state & FILE_HUP)) { DPRINTFN(2, "file_poll: %s hup\n", f->name); p = f->wproc; - if (p) + if (p) { + f->state |= FILE_WINUSE; p->ops->hup(p, NULL); + f->state &= ~FILE_WINUSE; + } f->state &= ~FILE_HUP; } - f->refs--; fnext = LIST_NEXT(f, entry); if (f->state & FILE_ZOMB) file_del(f); @@ -233,8 +389,9 @@ (void)sigaddset(&set, SIGPIPE); if (sigprocmask(SIG_BLOCK, &set, NULL)) err(1, "sigprocmask"); - LIST_INIT(&file_list); + timo_init(); + gettimeofday(&file_tv, NULL); } void @@ -242,6 +399,7 @@ { struct file *f; + timo_done(); if (!LIST_EMPTY(&file_list)) { fprintf(stderr, "filelist_done: list not empty:\n"); LIST_FOREACH(f, &file_list, entry) { @@ -287,13 +445,14 @@ { struct aproc *p; - if (f->refs == 0) { + if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) { DPRINTFN(2, "file_eof: %s: immediate\n", f->name); - f->refs++; p = f->rproc; - if (p) + if (p) { + f->state |= FILE_RINUSE; p->ops->eof(p, NULL); - f->refs--; + f->state &= ~FILE_RINUSE; + } if (f->state & FILE_ZOMB) file_del(f); } else { @@ -308,13 +467,14 @@ { struct aproc *p; - if (f->refs == 0) { + if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) { DPRINTFN(2, "file_hup: %s immediate\n", f->name); - f->refs++; p = f->wproc; - if (p) + if (p) { + f->state |= FILE_WINUSE; p->ops->hup(p, NULL); - f->refs--; + f->state &= ~FILE_WINUSE; + } if (f->state & FILE_ZOMB) file_del(f); } else { @@ -329,16 +489,20 @@ { struct aproc *p; - if (f->refs == 0) { + if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) { DPRINTFN(2, "file_close: %s: immediate\n", f->name); - f->refs++; p = f->rproc; - if (p) + if (p) { + f->state |= FILE_RINUSE; p->ops->eof(p, NULL); + f->state &= ~FILE_RINUSE; + } p = f->wproc; - if (p) + if (p) { + f->state |= FILE_WINUSE; p->ops->hup(p, NULL); - f->refs--; + f->state &= ~FILE_WINUSE; + } if (f->state & FILE_ZOMB) file_del(f); } else {