[BACK]Return to file.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sndiod

Annotation of src/usr.bin/sndiod/file.c, Revision 1.3

1.3     ! ratchov     1: /*     $OpenBSD: file.c,v 1.2 2012/12/07 08:04:58 ratchov Exp $        */
1.1       ratchov     2: /*
                      3:  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     16:  */
                     17: /*
                     18:  * non-blocking file i/o module: each file can be read or written (or
                     19:  * both). To achieve non-blocking io, we simply use the poll() syscall
                     20:  * in an event loop and dispatch events to sub-modules.
                     21:  *
                     22:  * the module also provides trivial timeout implementation,
                     23:  * derived from:
                     24:  *
                     25:  *     anoncvs@moule.caoua.org:/midish
                     26:  *
                     27:  *             midish/timo.c rev 1.18
                     28:  *             midish/mdep.c rev 1.71
                     29:  *
                     30:  * A timeout is used to schedule the call of a routine (the callback)
                     31:  * there is a global list of timeouts that is processed inside the
                     32:  * event loop. Timeouts work as follows:
                     33:  *
                     34:  *     first the timo structure must be initialized with timo_set()
                     35:  *
                     36:  *     then the timeout is scheduled (only once) with timo_add()
                     37:  *
                     38:  *     if the timeout expires, the call-back is called; then it can
                     39:  *     be scheduled again if needed. It's OK to reschedule it again
                     40:  *     from the callback
                     41:  *
                     42:  *     the timeout can be aborted with timo_del(), it is OK to try to
                     43:  *     abort a timout that has expired
                     44:  *
                     45:  */
                     46:
                     47: #include <sys/time.h>
                     48: #include <sys/types.h>
                     49:
                     50: #include <err.h>
                     51: #include <errno.h>
                     52: #include <fcntl.h>
                     53: #include <poll.h>
                     54: #include <signal.h>
                     55: #include <stdio.h>
                     56: #include <stdlib.h>
                     57: #include <time.h>
                     58:
                     59: #include "file.h"
                     60: #include "utils.h"
                     61:
                     62: #define MAXFDS 100
                     63: #define TIMER_USEC 10000
                     64:
                     65: struct timespec file_ts;
                     66: struct file *file_list;
                     67: struct timo *timo_queue;
                     68: unsigned int timo_abstime;
                     69: int file_slowaccept = 0, file_nfds;
                     70: #ifdef DEBUG
                     71: long long file_wtime, file_utime;
                     72: #endif
                     73:
                     74: /*
                     75:  * initialise a timeout structure, arguments are callback and argument
                     76:  * that will be passed to the callback
                     77:  */
                     78: void
                     79: timo_set(struct timo *o, void (*cb)(void *), void *arg)
                     80: {
                     81:        o->cb = cb;
                     82:        o->arg = arg;
                     83:        o->set = 0;
                     84: }
                     85:
                     86: /*
                     87:  * schedule the callback in 'delta' 24-th of microseconds. The timeout
                     88:  * must not be already scheduled
                     89:  */
                     90: void
                     91: timo_add(struct timo *o, unsigned int delta)
                     92: {
                     93:        struct timo **i;
                     94:        unsigned int val;
                     95:        int diff;
                     96:
                     97: #ifdef DEBUG
                     98:        if (o->set) {
                     99:                log_puts("timo_add: already set\n");
                    100:                panic();
                    101:        }
                    102:        if (delta == 0) {
                    103:                log_puts("timo_add: zero timeout is evil\n");
                    104:                panic();
                    105:        }
                    106: #endif
                    107:        val = timo_abstime + delta;
                    108:        for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
                    109:                diff = (*i)->val - val;
                    110:                if (diff > 0) {
                    111:                        break;
                    112:                }
                    113:        }
                    114:        o->set = 1;
                    115:        o->val = val;
                    116:        o->next = *i;
                    117:        *i = o;
                    118: }
                    119:
                    120: /*
                    121:  * abort a scheduled timeout
                    122:  */
                    123: void
                    124: timo_del(struct timo *o)
                    125: {
                    126:        struct timo **i;
                    127:
                    128:        for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
                    129:                if (*i == o) {
                    130:                        *i = o->next;
                    131:                        o->set = 0;
                    132:                        return;
                    133:                }
                    134:        }
                    135: #ifdef DEBUG
                    136:        if (log_level >= 4)
                    137:                log_puts("timo_del: not found\n");
                    138: #endif
                    139: }
                    140:
                    141: /*
                    142:  * routine to be called by the timer when 'delta' 24-th of microsecond
                    143:  * elapsed. This routine updates time referece used by timeouts and
                    144:  * calls expired timeouts
                    145:  */
                    146: void
                    147: timo_update(unsigned int delta)
                    148: {
                    149:        struct timo *to;
                    150:        int diff;
                    151:
                    152:        /*
                    153:         * update time reference
                    154:         */
                    155:        timo_abstime += delta;
                    156:
                    157:        /*
                    158:         * remove from the queue and run expired timeouts
                    159:         */
                    160:        while (timo_queue != NULL) {
                    161:                /*
                    162:                 * there is no overflow here because + and - are
                    163:                 * modulo 2^32, they are the same for both signed and
                    164:                 * unsigned integers
                    165:                 */
                    166:                diff = timo_queue->val - timo_abstime;
                    167:                if (diff > 0)
                    168:                        break;
                    169:                to = timo_queue;
                    170:                timo_queue = to->next;
                    171:                to->set = 0;
                    172:                to->cb(to->arg);
                    173:        }
                    174: }
                    175:
                    176: /*
                    177:  * initialize timeout queue
                    178:  */
                    179: void
                    180: timo_init(void)
                    181: {
                    182:        timo_queue = NULL;
                    183:        timo_abstime = 0;
                    184: }
                    185:
                    186: /*
                    187:  * destroy timeout queue
                    188:  */
                    189: void
                    190: timo_done(void)
                    191: {
                    192: #ifdef DEBUG
                    193:        if (timo_queue != NULL) {
                    194:                log_puts("timo_done: timo_queue not empty!\n");
                    195:                panic();
                    196:        }
                    197: #endif
                    198:        timo_queue = (struct timo *)0xdeadbeef;
                    199: }
                    200:
                    201: #ifdef DEBUG
                    202: void
                    203: file_log(struct file *f)
                    204: {
                    205:        static char *states[] = { "ini", "bus", "clo", "zom" };
                    206:
                    207:        log_puts(f->ops->name);
                    208:        if (log_level >= 3) {
                    209:                log_puts("(");
                    210:                log_puts(f->name);
                    211:                log_puts("|");
                    212:                log_puts(states[f->state]);
                    213:                log_puts(")");
                    214:        }
                    215: }
                    216: #endif
                    217:
                    218: struct file *
                    219: file_new(struct fileops *ops, void *arg, char *name, unsigned int nfds)
                    220: {
                    221:        struct file *f;
                    222:
                    223:        if (file_nfds + nfds > MAXFDS) {
                    224: #ifdef DEBUG
                    225:                if (log_level >= 1) {
                    226:                        log_puts(name);
                    227:                        log_puts(": too many polled files\n");
                    228:                }
                    229: #endif
                    230:                return NULL;
                    231:        }
                    232:        f = xmalloc(sizeof(struct file));
                    233:        f->nfds = nfds;
                    234:        f->ops = ops;
                    235:        f->arg = arg;
                    236:        f->name = name;
                    237:        f->state = FILE_INIT;
                    238:        f->next = file_list;
                    239:        file_list = f;
                    240: #ifdef DEBUG
                    241:        if (log_level >= 3) {
                    242:                file_log(f);
                    243:                log_puts(": created\n");
                    244:        }
                    245: #endif
                    246:        file_nfds += f->nfds;
                    247:        return f;
                    248: }
                    249:
                    250: void
                    251: file_del(struct file *f)
                    252: {
                    253: #ifdef DEBUG
                    254:        if (f->state == FILE_ZOMB) {
                    255:                log_puts("bad state in file_del()\n");
                    256:                panic();
                    257:        }
                    258: #endif
                    259:        file_nfds -= f->nfds;
                    260:        f->state = FILE_ZOMB;
                    261: #ifdef DEBUG
                    262:        if (log_level >= 3) {
                    263:                file_log(f);
                    264:                log_puts(": destroyed\n");
                    265:        }
                    266: #endif
                    267: }
                    268:
                    269: int
                    270: file_poll(void)
                    271: {
                    272:        nfds_t nfds, n;
                    273:        struct pollfd pfds[MAXFDS];
                    274:        struct file *f, **pf;
                    275:        struct timespec ts;
                    276: #ifdef DEBUG
                    277:        struct timespec sleepts;
                    278:        struct timespec ts0, ts1;
                    279:        long us;
                    280:        int i;
                    281: #endif
                    282:        long long delta_nsec;
                    283:        int revents, res;
                    284:
                    285:        /*
                    286:         * cleanup zombies
                    287:         */
                    288:        pf = &file_list;
                    289:        while ((f = *pf) != NULL) {
                    290:                if (f->state == FILE_ZOMB) {
                    291:                        *pf = f->next;
                    292:                        xfree(f);
                    293:                } else
                    294:                        pf = &f->next;
                    295:        }
                    296:
                    297:        if (file_list == NULL && timo_queue == NULL) {
                    298: #ifdef DEBUG
                    299:                if (log_level >= 3)
                    300:                        log_puts("nothing to do...\n");
                    301: #endif
                    302:                return 0;
                    303:        }
                    304:
                    305:        log_flush();
                    306: #ifdef DEBUG
                    307:        if (log_level >= 4)
                    308:                log_puts("poll:");
                    309: #endif
                    310:        nfds = 0;
                    311:        for (f = file_list; f != NULL; f = f->next) {
                    312: #ifdef DEBUG
                    313:                if (log_level >= 4) {
                    314:                        log_puts(" ");
                    315:                        file_log(f);
                    316:                }
                    317: #endif
                    318:                n = f->ops->pollfd(f->arg, pfds + nfds);
                    319:                if (n == 0) {
                    320:                        f->pfd = NULL;
                    321:                        continue;
                    322:                }
                    323:                f->pfd = pfds + nfds;
                    324:                nfds += n;
                    325: #ifdef DEBUG
                    326:                if (log_level >= 4) {
                    327:                        log_puts("=");
                    328:                        for (i = 0; i < n; i++) {
                    329:                                if (i > 0)
                    330:                                        log_puts(",");
                    331:                                log_putx(f->pfd[i].events);
                    332:                        }
                    333:                }
                    334: #endif
                    335:        }
                    336: #ifdef DEBUG
                    337:        if (log_level >= 4)
                    338:                log_puts("\n");
                    339: #endif
                    340:
                    341: #ifdef DEBUG
                    342:        clock_gettime(CLOCK_MONOTONIC, &sleepts);
                    343:        file_utime += 1000000000LL * (sleepts.tv_sec - file_ts.tv_sec);
                    344:        file_utime += sleepts.tv_nsec - file_ts.tv_nsec;
                    345: #endif
                    346:        res = poll(pfds, nfds, -1);
                    347:        if (res < 0 && errno != EINTR)
                    348:                err(1, "poll");
                    349: #ifdef DEBUG
                    350:        if (log_level >= 4) {
                    351:                log_puts("poll: return:");
                    352:                for (i = 0; i < nfds; i++) {
                    353:                        log_puts(" ");
                    354:                        log_putx(pfds[i].revents);
                    355:                }
                    356:                log_puts("\n");
                    357:        }
                    358: #endif
                    359:        clock_gettime(CLOCK_MONOTONIC, &ts);
                    360: #ifdef DEBUG
                    361:        file_wtime += 1000000000LL * (ts.tv_sec - sleepts.tv_sec);
                    362:        file_wtime += ts.tv_nsec - sleepts.tv_nsec;
                    363: #endif
                    364:        delta_nsec = 1000000000LL * (ts.tv_sec - file_ts.tv_sec);
                    365:        delta_nsec += ts.tv_nsec - file_ts.tv_nsec;
                    366: #ifdef DEBUG
                    367:        if (delta_nsec < 0)
                    368:                log_puts("file_poll: negative time interval\n");
                    369: #endif
                    370:        file_ts = ts;
                    371:        if (delta_nsec >= 0 && delta_nsec < 1000000000LL)
                    372:                timo_update(delta_nsec / 1000);
                    373:        else {
                    374:                if (log_level >= 2)
                    375:                        log_puts("ignored huge clock delta\n");
                    376:        }
                    377:        if (res <= 0)
                    378:                return 1;
                    379:
                    380:        for (f = file_list; f != NULL; f = f->next) {
                    381:                if (f->pfd == NULL)
                    382:                        continue;
                    383: #ifdef DEBUG
                    384:                clock_gettime(CLOCK_MONOTONIC, &ts0);
                    385: #endif
1.3     ! ratchov   386:                revents = (f->state != FILE_ZOMB) ?
        !           387:                    f->ops->revents(f->arg, f->pfd) : 0;
1.1       ratchov   388:                if ((revents & POLLHUP) && (f->state != FILE_ZOMB))
                    389:                        f->ops->hup(f->arg);
                    390:                if ((revents & POLLIN) && (f->state != FILE_ZOMB))
                    391:                        f->ops->in(f->arg);
                    392:                if ((revents & POLLOUT) && (f->state != FILE_ZOMB))
                    393:                        f->ops->out(f->arg);
                    394: #ifdef DEBUG
                    395:                clock_gettime(CLOCK_MONOTONIC, &ts1);
                    396:                us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
                    397:                us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
                    398:                if (log_level >= 4 || (log_level >= 3 && us >= 5000)) {
                    399:                        file_log(f);
                    400:                        log_puts(": processed in ");
                    401:                        log_putu(us);
                    402:                        log_puts("us\n");
                    403:                }
                    404: #endif
                    405:        }
                    406:        return 1;
                    407: }
                    408:
                    409: /*
                    410:  * handler for SIGALRM, invoked periodically
                    411:  */
                    412: void
                    413: file_sigalrm(int i)
                    414: {
                    415:        /* nothing to do, we only want poll() to return EINTR */
                    416: }
                    417:
                    418:
                    419: void
                    420: filelist_init(void)
                    421: {
                    422:        static struct sigaction sa;
                    423:        struct itimerval it;
                    424:        sigset_t set;
                    425:
                    426:        sigemptyset(&set);
                    427:        (void)sigaddset(&set, SIGPIPE);
                    428:        if (sigprocmask(SIG_BLOCK, &set, NULL))
                    429:                err(1, "sigprocmask");
                    430:        file_list = NULL;
                    431:        if (clock_gettime(CLOCK_MONOTONIC, &file_ts) < 0) {
                    432:                perror("clock_gettime");
                    433:                exit(1);
                    434:        }
                    435:         sa.sa_flags = SA_RESTART;
                    436:         sa.sa_handler = file_sigalrm;
                    437:         sigfillset(&sa.sa_mask);
                    438:         if (sigaction(SIGALRM, &sa, NULL) < 0) {
                    439:                perror("sigaction");
                    440:                exit(1);
                    441:        }
                    442:        it.it_interval.tv_sec = 0;
                    443:        it.it_interval.tv_usec = TIMER_USEC;
                    444:        it.it_value.tv_sec = 0;
                    445:        it.it_value.tv_usec = TIMER_USEC;
                    446:        if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
                    447:                perror("setitimer");
                    448:                exit(1);
                    449:        }
                    450:        log_sync = 0;
                    451:        timo_init();
                    452: }
                    453:
                    454: void
                    455: filelist_done(void)
                    456: {
                    457:        struct itimerval it;
                    458: #ifdef DEBUG
                    459:        struct file *f;
                    460:
                    461:        if (file_list != NULL) {
                    462:                for (f = file_list; f != NULL; f = f->next) {
                    463:                        file_log(f);
                    464:                        log_puts(" not closed\n");
                    465:                }
                    466:                panic();
                    467:        }
                    468:        log_sync = 1;
                    469:        log_flush();
                    470: #endif
                    471:        timerclear(&it.it_value);
                    472:        timerclear(&it.it_interval);
                    473:        if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
                    474:                perror("setitimer");
                    475:                exit(1);
                    476:        }
                    477:        timo_done();
                    478: }