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

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