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

1.5     ! ratchov     1: /*     $OpenBSD: file.c,v 1.4 2013/11/18 17:37:45 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:        nfds_t nfds, n;
                    278:        struct pollfd pfds[MAXFDS];
                    279:        struct file *f, **pf;
                    280:        struct timespec ts;
                    281: #ifdef DEBUG
                    282:        struct timespec sleepts;
                    283:        struct timespec ts0, ts1;
                    284:        long us;
                    285:        int i;
                    286: #endif
                    287:        long long delta_nsec;
                    288:        int revents, res;
                    289:
                    290:        /*
                    291:         * cleanup zombies
                    292:         */
                    293:        pf = &file_list;
                    294:        while ((f = *pf) != NULL) {
                    295:                if (f->state == FILE_ZOMB) {
                    296:                        *pf = f->next;
                    297:                        xfree(f);
                    298:                } else
                    299:                        pf = &f->next;
                    300:        }
                    301:
                    302:        if (file_list == NULL && timo_queue == NULL) {
                    303: #ifdef DEBUG
                    304:                if (log_level >= 3)
                    305:                        log_puts("nothing to do...\n");
                    306: #endif
                    307:                return 0;
                    308:        }
                    309:
                    310:        log_flush();
                    311: #ifdef DEBUG
                    312:        if (log_level >= 4)
                    313:                log_puts("poll:");
                    314: #endif
                    315:        nfds = 0;
                    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:                }
                    328:                f->pfd = pfds + nfds;
                    329:                nfds += n;
                    330: #ifdef DEBUG
                    331:                if (log_level >= 4) {
                    332:                        log_puts("=");
                    333:                        for (i = 0; i < n; i++) {
                    334:                                if (i > 0)
                    335:                                        log_puts(",");
                    336:                                log_putx(f->pfd[i].events);
                    337:                        }
                    338:                }
                    339: #endif
                    340:        }
                    341: #ifdef DEBUG
                    342:        if (log_level >= 4)
                    343:                log_puts("\n");
                    344: #endif
                    345:
                    346: #ifdef DEBUG
                    347:        clock_gettime(CLOCK_MONOTONIC, &sleepts);
                    348:        file_utime += 1000000000LL * (sleepts.tv_sec - file_ts.tv_sec);
                    349:        file_utime += sleepts.tv_nsec - file_ts.tv_nsec;
                    350: #endif
                    351:        res = poll(pfds, nfds, -1);
                    352:        if (res < 0 && errno != EINTR)
                    353:                err(1, "poll");
                    354: #ifdef DEBUG
                    355:        if (log_level >= 4) {
                    356:                log_puts("poll: return:");
                    357:                for (i = 0; i < nfds; i++) {
                    358:                        log_puts(" ");
                    359:                        log_putx(pfds[i].revents);
                    360:                }
                    361:                log_puts("\n");
                    362:        }
                    363: #endif
                    364:        clock_gettime(CLOCK_MONOTONIC, &ts);
                    365: #ifdef DEBUG
                    366:        file_wtime += 1000000000LL * (ts.tv_sec - sleepts.tv_sec);
                    367:        file_wtime += ts.tv_nsec - sleepts.tv_nsec;
                    368: #endif
                    369:        delta_nsec = 1000000000LL * (ts.tv_sec - file_ts.tv_sec);
                    370:        delta_nsec += ts.tv_nsec - file_ts.tv_nsec;
                    371: #ifdef DEBUG
                    372:        if (delta_nsec < 0)
                    373:                log_puts("file_poll: negative time interval\n");
                    374: #endif
                    375:        file_ts = ts;
                    376:        if (delta_nsec >= 0 && delta_nsec < 1000000000LL)
                    377:                timo_update(delta_nsec / 1000);
                    378:        else {
                    379:                if (log_level >= 2)
                    380:                        log_puts("ignored huge clock delta\n");
                    381:        }
                    382:        if (res <= 0)
                    383:                return 1;
                    384:
                    385:        for (f = file_list; f != NULL; f = f->next) {
                    386:                if (f->pfd == NULL)
                    387:                        continue;
                    388: #ifdef DEBUG
                    389:                clock_gettime(CLOCK_MONOTONIC, &ts0);
                    390: #endif
1.3       ratchov   391:                revents = (f->state != FILE_ZOMB) ?
                    392:                    f->ops->revents(f->arg, f->pfd) : 0;
1.1       ratchov   393:                if ((revents & POLLHUP) && (f->state != FILE_ZOMB))
                    394:                        f->ops->hup(f->arg);
                    395:                if ((revents & POLLIN) && (f->state != FILE_ZOMB))
                    396:                        f->ops->in(f->arg);
                    397:                if ((revents & POLLOUT) && (f->state != FILE_ZOMB))
                    398:                        f->ops->out(f->arg);
                    399: #ifdef DEBUG
                    400:                clock_gettime(CLOCK_MONOTONIC, &ts1);
                    401:                us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
                    402:                us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
                    403:                if (log_level >= 4 || (log_level >= 3 && us >= 5000)) {
                    404:                        file_log(f);
                    405:                        log_puts(": processed in ");
                    406:                        log_putu(us);
                    407:                        log_puts("us\n");
                    408:                }
                    409: #endif
                    410:        }
                    411:        return 1;
                    412: }
                    413:
                    414: /*
                    415:  * handler for SIGALRM, invoked periodically
                    416:  */
                    417: void
                    418: file_sigalrm(int i)
                    419: {
                    420:        /* nothing to do, we only want poll() to return EINTR */
                    421: }
                    422:
                    423:
                    424: void
                    425: filelist_init(void)
                    426: {
                    427:        static struct sigaction sa;
                    428:        struct itimerval it;
                    429:        sigset_t set;
                    430:
                    431:        sigemptyset(&set);
                    432:        (void)sigaddset(&set, SIGPIPE);
                    433:        if (sigprocmask(SIG_BLOCK, &set, NULL))
                    434:                err(1, "sigprocmask");
                    435:        file_list = NULL;
                    436:        if (clock_gettime(CLOCK_MONOTONIC, &file_ts) < 0) {
                    437:                perror("clock_gettime");
                    438:                exit(1);
                    439:        }
                    440:         sa.sa_flags = SA_RESTART;
                    441:         sa.sa_handler = file_sigalrm;
                    442:         sigfillset(&sa.sa_mask);
                    443:         if (sigaction(SIGALRM, &sa, NULL) < 0) {
                    444:                perror("sigaction");
                    445:                exit(1);
                    446:        }
                    447:        it.it_interval.tv_sec = 0;
                    448:        it.it_interval.tv_usec = TIMER_USEC;
                    449:        it.it_value.tv_sec = 0;
                    450:        it.it_value.tv_usec = TIMER_USEC;
                    451:        if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
                    452:                perror("setitimer");
                    453:                exit(1);
                    454:        }
                    455:        log_sync = 0;
                    456:        timo_init();
                    457: }
                    458:
                    459: void
                    460: filelist_done(void)
                    461: {
                    462:        struct itimerval it;
                    463: #ifdef DEBUG
                    464:        struct file *f;
                    465:
                    466:        if (file_list != NULL) {
                    467:                for (f = file_list; f != NULL; f = f->next) {
                    468:                        file_log(f);
                    469:                        log_puts(" not closed\n");
                    470:                }
                    471:                panic();
                    472:        }
                    473:        log_sync = 1;
                    474:        log_flush();
                    475: #endif
                    476:        timerclear(&it.it_value);
                    477:        timerclear(&it.it_interval);
                    478:        if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
                    479:                perror("setitimer");
                    480:                exit(1);
                    481:        }
                    482:        timo_done();
                    483: }