[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.6

1.6     ! ratchov     1: /*     $OpenBSD: file.c,v 1.5 2014/03/17 17:17:01 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
1.4       ratchov    64:
                     65: void timo_update(unsigned int);
                     66: void timo_init(void);
                     67: void timo_done(void);
                     68: void file_sigalrm(int);
1.1       ratchov    69:
                     70: struct timespec file_ts;
                     71: struct file *file_list;
                     72: struct timo *timo_queue;
                     73: unsigned int timo_abstime;
                     74: int file_slowaccept = 0, file_nfds;
                     75: #ifdef DEBUG
                     76: long long file_wtime, file_utime;
                     77: #endif
                     78:
                     79: /*
                     80:  * initialise a timeout structure, arguments are callback and argument
                     81:  * that will be passed to the callback
                     82:  */
                     83: void
                     84: timo_set(struct timo *o, void (*cb)(void *), void *arg)
                     85: {
                     86:        o->cb = cb;
                     87:        o->arg = arg;
                     88:        o->set = 0;
                     89: }
                     90:
                     91: /*
                     92:  * schedule the callback in 'delta' 24-th of microseconds. The timeout
                     93:  * must not be already scheduled
                     94:  */
                     95: void
                     96: timo_add(struct timo *o, unsigned int delta)
                     97: {
                     98:        struct timo **i;
                     99:        unsigned int val;
                    100:        int diff;
                    101:
                    102: #ifdef DEBUG
                    103:        if (o->set) {
                    104:                log_puts("timo_add: already set\n");
                    105:                panic();
                    106:        }
                    107:        if (delta == 0) {
                    108:                log_puts("timo_add: zero timeout is evil\n");
                    109:                panic();
                    110:        }
                    111: #endif
                    112:        val = timo_abstime + delta;
                    113:        for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
                    114:                diff = (*i)->val - val;
                    115:                if (diff > 0) {
                    116:                        break;
                    117:                }
                    118:        }
                    119:        o->set = 1;
                    120:        o->val = val;
                    121:        o->next = *i;
                    122:        *i = o;
                    123: }
                    124:
                    125: /*
                    126:  * abort a scheduled timeout
                    127:  */
                    128: void
                    129: timo_del(struct timo *o)
                    130: {
                    131:        struct timo **i;
                    132:
                    133:        for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
                    134:                if (*i == o) {
                    135:                        *i = o->next;
                    136:                        o->set = 0;
                    137:                        return;
                    138:                }
                    139:        }
                    140: #ifdef DEBUG
                    141:        if (log_level >= 4)
                    142:                log_puts("timo_del: not found\n");
                    143: #endif
                    144: }
                    145:
                    146: /*
                    147:  * routine to be called by the timer when 'delta' 24-th of microsecond
                    148:  * elapsed. This routine updates time referece used by timeouts and
                    149:  * calls expired timeouts
                    150:  */
                    151: void
                    152: timo_update(unsigned int delta)
                    153: {
                    154:        struct timo *to;
                    155:        int diff;
                    156:
                    157:        /*
                    158:         * update time reference
                    159:         */
                    160:        timo_abstime += delta;
                    161:
                    162:        /*
                    163:         * remove from the queue and run expired timeouts
                    164:         */
                    165:        while (timo_queue != NULL) {
                    166:                /*
                    167:                 * there is no overflow here because + and - are
                    168:                 * modulo 2^32, they are the same for both signed and
                    169:                 * unsigned integers
                    170:                 */
                    171:                diff = timo_queue->val - timo_abstime;
                    172:                if (diff > 0)
                    173:                        break;
                    174:                to = timo_queue;
                    175:                timo_queue = to->next;
                    176:                to->set = 0;
                    177:                to->cb(to->arg);
                    178:        }
                    179: }
                    180:
                    181: /*
                    182:  * initialize timeout queue
                    183:  */
                    184: void
                    185: timo_init(void)
                    186: {
                    187:        timo_queue = NULL;
                    188:        timo_abstime = 0;
                    189: }
                    190:
                    191: /*
                    192:  * destroy timeout queue
                    193:  */
                    194: void
                    195: timo_done(void)
                    196: {
                    197: #ifdef DEBUG
                    198:        if (timo_queue != NULL) {
                    199:                log_puts("timo_done: timo_queue not empty!\n");
                    200:                panic();
                    201:        }
                    202: #endif
                    203:        timo_queue = (struct timo *)0xdeadbeef;
                    204: }
                    205:
                    206: #ifdef DEBUG
                    207: void
                    208: file_log(struct file *f)
                    209: {
1.5       ratchov   210:        static char *states[] = { "ini", "zom" };
1.1       ratchov   211:
                    212:        log_puts(f->ops->name);
                    213:        if (log_level >= 3) {
                    214:                log_puts("(");
                    215:                log_puts(f->name);
                    216:                log_puts("|");
                    217:                log_puts(states[f->state]);
                    218:                log_puts(")");
                    219:        }
                    220: }
                    221: #endif
                    222:
                    223: struct file *
                    224: file_new(struct fileops *ops, void *arg, char *name, unsigned int nfds)
                    225: {
                    226:        struct file *f;
                    227:
                    228:        if (file_nfds + nfds > MAXFDS) {
                    229: #ifdef DEBUG
                    230:                if (log_level >= 1) {
                    231:                        log_puts(name);
                    232:                        log_puts(": too many polled files\n");
                    233:                }
                    234: #endif
                    235:                return NULL;
                    236:        }
                    237:        f = xmalloc(sizeof(struct file));
                    238:        f->nfds = nfds;
                    239:        f->ops = ops;
                    240:        f->arg = arg;
                    241:        f->name = name;
                    242:        f->state = FILE_INIT;
                    243:        f->next = file_list;
                    244:        file_list = f;
                    245: #ifdef DEBUG
                    246:        if (log_level >= 3) {
                    247:                file_log(f);
                    248:                log_puts(": created\n");
                    249:        }
                    250: #endif
                    251:        file_nfds += f->nfds;
                    252:        return f;
                    253: }
                    254:
                    255: void
                    256: file_del(struct file *f)
                    257: {
                    258: #ifdef DEBUG
                    259:        if (f->state == FILE_ZOMB) {
                    260:                log_puts("bad state in file_del()\n");
                    261:                panic();
                    262:        }
                    263: #endif
                    264:        file_nfds -= f->nfds;
                    265:        f->state = FILE_ZOMB;
                    266: #ifdef DEBUG
                    267:        if (log_level >= 3) {
                    268:                file_log(f);
                    269:                log_puts(": destroyed\n");
                    270:        }
                    271: #endif
                    272: }
                    273:
                    274: int
                    275: file_poll(void)
                    276: {
                    277:        struct pollfd pfds[MAXFDS];
                    278:        struct file *f, **pf;
                    279:        struct timespec ts;
                    280: #ifdef DEBUG
                    281:        struct timespec sleepts;
                    282:        struct timespec ts0, ts1;
                    283:        long us;
1.6     ! ratchov   284:        int i, n, nfds;
1.1       ratchov   285: #endif
                    286:        long long delta_nsec;
1.6     ! ratchov   287:        int revents, res, immed;
1.1       ratchov   288:
                    289:        /*
                    290:         * cleanup zombies
                    291:         */
                    292:        pf = &file_list;
                    293:        while ((f = *pf) != NULL) {
                    294:                if (f->state == FILE_ZOMB) {
                    295:                        *pf = f->next;
                    296:                        xfree(f);
                    297:                } else
                    298:                        pf = &f->next;
                    299:        }
                    300:
                    301:        if (file_list == NULL && timo_queue == NULL) {
                    302: #ifdef DEBUG
                    303:                if (log_level >= 3)
                    304:                        log_puts("nothing to do...\n");
                    305: #endif
                    306:                return 0;
                    307:        }
                    308:
                    309:        log_flush();
                    310: #ifdef DEBUG
                    311:        if (log_level >= 4)
                    312:                log_puts("poll:");
                    313: #endif
                    314:        nfds = 0;
1.6     ! ratchov   315:        immed = 0;
1.1       ratchov   316:        for (f = file_list; f != NULL; f = f->next) {
                    317: #ifdef DEBUG
                    318:                if (log_level >= 4) {
                    319:                        log_puts(" ");
                    320:                        file_log(f);
                    321:                }
                    322: #endif
                    323:                n = f->ops->pollfd(f->arg, pfds + nfds);
                    324:                if (n == 0) {
                    325:                        f->pfd = NULL;
                    326:                        continue;
                    327:                }
1.6     ! ratchov   328:                if (n < 0) {
        !           329:                        immed = 1;
        !           330:                        n = 0;
        !           331:                }
1.1       ratchov   332:                f->pfd = pfds + nfds;
                    333:                nfds += n;
                    334: #ifdef DEBUG
                    335:                if (log_level >= 4) {
                    336:                        log_puts("=");
                    337:                        for (i = 0; i < n; i++) {
                    338:                                if (i > 0)
                    339:                                        log_puts(",");
                    340:                                log_putx(f->pfd[i].events);
                    341:                        }
                    342:                }
                    343: #endif
                    344:        }
                    345: #ifdef DEBUG
                    346:        if (log_level >= 4)
                    347:                log_puts("\n");
                    348: #endif
                    349:
                    350: #ifdef DEBUG
                    351:        clock_gettime(CLOCK_MONOTONIC, &sleepts);
                    352:        file_utime += 1000000000LL * (sleepts.tv_sec - file_ts.tv_sec);
                    353:        file_utime += sleepts.tv_nsec - file_ts.tv_nsec;
                    354: #endif
1.6     ! ratchov   355:        if (!immed) {
        !           356:                res = poll(pfds, nfds, -1);
        !           357:                if (res < 0 && errno != EINTR)
        !           358:                        err(1, "poll");
        !           359: #ifdef DEBUG
        !           360:                if (log_level >= 4 && res >= 0) {
        !           361:                        log_puts("poll: return:");
        !           362:                        for (i = 0; i < nfds; i++) {
        !           363:                                log_puts(" ");
        !           364:                                log_putx(pfds[i].revents);
        !           365:                        }
        !           366:                        log_puts("\n");
1.1       ratchov   367:                }
                    368: #endif
1.6     ! ratchov   369:        } else
        !           370:                res = 0;
1.1       ratchov   371:        clock_gettime(CLOCK_MONOTONIC, &ts);
                    372: #ifdef DEBUG
                    373:        file_wtime += 1000000000LL * (ts.tv_sec - sleepts.tv_sec);
                    374:        file_wtime += ts.tv_nsec - sleepts.tv_nsec;
                    375: #endif
                    376:        delta_nsec = 1000000000LL * (ts.tv_sec - file_ts.tv_sec);
                    377:        delta_nsec += ts.tv_nsec - file_ts.tv_nsec;
                    378: #ifdef DEBUG
                    379:        if (delta_nsec < 0)
                    380:                log_puts("file_poll: negative time interval\n");
                    381: #endif
                    382:        file_ts = ts;
                    383:        if (delta_nsec >= 0 && delta_nsec < 1000000000LL)
                    384:                timo_update(delta_nsec / 1000);
                    385:        else {
                    386:                if (log_level >= 2)
                    387:                        log_puts("ignored huge clock delta\n");
                    388:        }
1.6     ! ratchov   389:        if (!immed && res <= 0)
1.1       ratchov   390:                return 1;
                    391:
                    392:        for (f = file_list; f != NULL; f = f->next) {
                    393:                if (f->pfd == NULL)
                    394:                        continue;
                    395: #ifdef DEBUG
                    396:                clock_gettime(CLOCK_MONOTONIC, &ts0);
                    397: #endif
1.3       ratchov   398:                revents = (f->state != FILE_ZOMB) ?
                    399:                    f->ops->revents(f->arg, f->pfd) : 0;
1.1       ratchov   400:                if ((revents & POLLHUP) && (f->state != FILE_ZOMB))
                    401:                        f->ops->hup(f->arg);
                    402:                if ((revents & POLLIN) && (f->state != FILE_ZOMB))
                    403:                        f->ops->in(f->arg);
                    404:                if ((revents & POLLOUT) && (f->state != FILE_ZOMB))
                    405:                        f->ops->out(f->arg);
                    406: #ifdef DEBUG
                    407:                clock_gettime(CLOCK_MONOTONIC, &ts1);
                    408:                us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
                    409:                us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
                    410:                if (log_level >= 4 || (log_level >= 3 && us >= 5000)) {
                    411:                        file_log(f);
                    412:                        log_puts(": processed in ");
                    413:                        log_putu(us);
                    414:                        log_puts("us\n");
                    415:                }
                    416: #endif
                    417:        }
                    418:        return 1;
                    419: }
                    420:
                    421: /*
                    422:  * handler for SIGALRM, invoked periodically
                    423:  */
                    424: void
                    425: file_sigalrm(int i)
                    426: {
                    427:        /* nothing to do, we only want poll() to return EINTR */
                    428: }
                    429:
                    430:
                    431: void
                    432: filelist_init(void)
                    433: {
                    434:        static struct sigaction sa;
                    435:        struct itimerval it;
                    436:        sigset_t set;
                    437:
                    438:        sigemptyset(&set);
                    439:        (void)sigaddset(&set, SIGPIPE);
                    440:        if (sigprocmask(SIG_BLOCK, &set, NULL))
                    441:                err(1, "sigprocmask");
                    442:        file_list = NULL;
                    443:        if (clock_gettime(CLOCK_MONOTONIC, &file_ts) < 0) {
                    444:                perror("clock_gettime");
                    445:                exit(1);
                    446:        }
                    447:         sa.sa_flags = SA_RESTART;
                    448:         sa.sa_handler = file_sigalrm;
                    449:         sigfillset(&sa.sa_mask);
                    450:         if (sigaction(SIGALRM, &sa, NULL) < 0) {
                    451:                perror("sigaction");
                    452:                exit(1);
                    453:        }
                    454:        it.it_interval.tv_sec = 0;
                    455:        it.it_interval.tv_usec = TIMER_USEC;
                    456:        it.it_value.tv_sec = 0;
                    457:        it.it_value.tv_usec = TIMER_USEC;
                    458:        if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
                    459:                perror("setitimer");
                    460:                exit(1);
                    461:        }
                    462:        log_sync = 0;
                    463:        timo_init();
                    464: }
                    465:
                    466: void
                    467: filelist_done(void)
                    468: {
                    469:        struct itimerval it;
                    470: #ifdef DEBUG
                    471:        struct file *f;
                    472:
                    473:        if (file_list != NULL) {
                    474:                for (f = file_list; f != NULL; f = f->next) {
                    475:                        file_log(f);
                    476:                        log_puts(" not closed\n");
                    477:                }
                    478:                panic();
                    479:        }
                    480:        log_sync = 1;
                    481:        log_flush();
                    482: #endif
                    483:        timerclear(&it.it_value);
                    484:        timerclear(&it.it_interval);
                    485:        if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
                    486:                perror("setitimer");
                    487:                exit(1);
                    488:        }
                    489:        timo_done();
                    490: }