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

1.1     ! ratchov     1: /*     $OpenBSD$       */
        !             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 "defs.h"
        !            60: #include "file.h"
        !            61: #include "utils.h"
        !            62:
        !            63: #define MAXFDS 100
        !            64: #define TIMER_USEC 10000
        !            65:
        !            66: struct timespec file_ts;
        !            67: struct file *file_list;
        !            68: struct timo *timo_queue;
        !            69: unsigned int timo_abstime;
        !            70: int file_slowaccept = 0, file_nfds;
        !            71: #ifdef DEBUG
        !            72: long long file_wtime, file_utime;
        !            73: #endif
        !            74:
        !            75: /*
        !            76:  * initialise a timeout structure, arguments are callback and argument
        !            77:  * that will be passed to the callback
        !            78:  */
        !            79: void
        !            80: timo_set(struct timo *o, void (*cb)(void *), void *arg)
        !            81: {
        !            82:        o->cb = cb;
        !            83:        o->arg = arg;
        !            84:        o->set = 0;
        !            85: }
        !            86:
        !            87: /*
        !            88:  * schedule the callback in 'delta' 24-th of microseconds. The timeout
        !            89:  * must not be already scheduled
        !            90:  */
        !            91: void
        !            92: timo_add(struct timo *o, unsigned int delta)
        !            93: {
        !            94:        struct timo **i;
        !            95:        unsigned int val;
        !            96:        int diff;
        !            97:
        !            98: #ifdef DEBUG
        !            99:        if (o->set) {
        !           100:                log_puts("timo_add: already set\n");
        !           101:                panic();
        !           102:        }
        !           103:        if (delta == 0) {
        !           104:                log_puts("timo_add: zero timeout is evil\n");
        !           105:                panic();
        !           106:        }
        !           107: #endif
        !           108:        val = timo_abstime + delta;
        !           109:        for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
        !           110:                diff = (*i)->val - val;
        !           111:                if (diff > 0) {
        !           112:                        break;
        !           113:                }
        !           114:        }
        !           115:        o->set = 1;
        !           116:        o->val = val;
        !           117:        o->next = *i;
        !           118:        *i = o;
        !           119: }
        !           120:
        !           121: /*
        !           122:  * abort a scheduled timeout
        !           123:  */
        !           124: void
        !           125: timo_del(struct timo *o)
        !           126: {
        !           127:        struct timo **i;
        !           128:
        !           129:        for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
        !           130:                if (*i == o) {
        !           131:                        *i = o->next;
        !           132:                        o->set = 0;
        !           133:                        return;
        !           134:                }
        !           135:        }
        !           136: #ifdef DEBUG
        !           137:        if (log_level >= 4)
        !           138:                log_puts("timo_del: not found\n");
        !           139: #endif
        !           140: }
        !           141:
        !           142: /*
        !           143:  * routine to be called by the timer when 'delta' 24-th of microsecond
        !           144:  * elapsed. This routine updates time referece used by timeouts and
        !           145:  * calls expired timeouts
        !           146:  */
        !           147: void
        !           148: timo_update(unsigned int delta)
        !           149: {
        !           150:        struct timo *to;
        !           151:        int diff;
        !           152:
        !           153:        /*
        !           154:         * update time reference
        !           155:         */
        !           156:        timo_abstime += delta;
        !           157:
        !           158:        /*
        !           159:         * remove from the queue and run expired timeouts
        !           160:         */
        !           161:        while (timo_queue != NULL) {
        !           162:                /*
        !           163:                 * there is no overflow here because + and - are
        !           164:                 * modulo 2^32, they are the same for both signed and
        !           165:                 * unsigned integers
        !           166:                 */
        !           167:                diff = timo_queue->val - timo_abstime;
        !           168:                if (diff > 0)
        !           169:                        break;
        !           170:                to = timo_queue;
        !           171:                timo_queue = to->next;
        !           172:                to->set = 0;
        !           173:                to->cb(to->arg);
        !           174:        }
        !           175: }
        !           176:
        !           177: /*
        !           178:  * initialize timeout queue
        !           179:  */
        !           180: void
        !           181: timo_init(void)
        !           182: {
        !           183:        timo_queue = NULL;
        !           184:        timo_abstime = 0;
        !           185: }
        !           186:
        !           187: /*
        !           188:  * destroy timeout queue
        !           189:  */
        !           190: void
        !           191: timo_done(void)
        !           192: {
        !           193: #ifdef DEBUG
        !           194:        if (timo_queue != NULL) {
        !           195:                log_puts("timo_done: timo_queue not empty!\n");
        !           196:                panic();
        !           197:        }
        !           198: #endif
        !           199:        timo_queue = (struct timo *)0xdeadbeef;
        !           200: }
        !           201:
        !           202: #ifdef DEBUG
        !           203: void
        !           204: file_log(struct file *f)
        !           205: {
        !           206:        static char *states[] = { "ini", "bus", "clo", "zom" };
        !           207:
        !           208:        log_puts(f->ops->name);
        !           209:        if (log_level >= 3) {
        !           210:                log_puts("(");
        !           211:                log_puts(f->name);
        !           212:                log_puts("|");
        !           213:                log_puts(states[f->state]);
        !           214:                log_puts(")");
        !           215:        }
        !           216: }
        !           217: #endif
        !           218:
        !           219: struct file *
        !           220: file_new(struct fileops *ops, void *arg, char *name, unsigned int nfds)
        !           221: {
        !           222:        struct file *f;
        !           223:
        !           224:        if (file_nfds + nfds > MAXFDS) {
        !           225: #ifdef DEBUG
        !           226:                if (log_level >= 1) {
        !           227:                        log_puts(name);
        !           228:                        log_puts(": too many polled files\n");
        !           229:                }
        !           230: #endif
        !           231:                return NULL;
        !           232:        }
        !           233:        f = xmalloc(sizeof(struct file));
        !           234:        f->nfds = nfds;
        !           235:        f->ops = ops;
        !           236:        f->arg = arg;
        !           237:        f->name = name;
        !           238:        f->state = FILE_INIT;
        !           239:        f->next = file_list;
        !           240:        file_list = f;
        !           241: #ifdef DEBUG
        !           242:        if (log_level >= 3) {
        !           243:                file_log(f);
        !           244:                log_puts(": created\n");
        !           245:        }
        !           246: #endif
        !           247:        file_nfds += f->nfds;
        !           248:        return f;
        !           249: }
        !           250:
        !           251: void
        !           252: file_del(struct file *f)
        !           253: {
        !           254: #ifdef DEBUG
        !           255:        if (f->state == FILE_ZOMB) {
        !           256:                log_puts("bad state in file_del()\n");
        !           257:                panic();
        !           258:        }
        !           259: #endif
        !           260:        file_nfds -= f->nfds;
        !           261:        f->state = FILE_ZOMB;
        !           262: #ifdef DEBUG
        !           263:        if (log_level >= 3) {
        !           264:                file_log(f);
        !           265:                log_puts(": destroyed\n");
        !           266:        }
        !           267: #endif
        !           268: }
        !           269:
        !           270: int
        !           271: file_poll(void)
        !           272: {
        !           273:        nfds_t nfds, n;
        !           274:        struct pollfd pfds[MAXFDS];
        !           275:        struct file *f, **pf;
        !           276:        struct timespec ts;
        !           277: #ifdef DEBUG
        !           278:        struct timespec sleepts;
        !           279:        struct timespec ts0, ts1;
        !           280:        long us;
        !           281:        int i;
        !           282: #endif
        !           283:        long long delta_nsec;
        !           284:        int revents, res;
        !           285:
        !           286:        /*
        !           287:         * cleanup zombies
        !           288:         */
        !           289:        pf = &file_list;
        !           290:        while ((f = *pf) != NULL) {
        !           291:                if (f->state == FILE_ZOMB) {
        !           292:                        *pf = f->next;
        !           293:                        xfree(f);
        !           294:                } else
        !           295:                        pf = &f->next;
        !           296:        }
        !           297:
        !           298:        if (file_list == NULL && timo_queue == NULL) {
        !           299: #ifdef DEBUG
        !           300:                if (log_level >= 3)
        !           301:                        log_puts("nothing to do...\n");
        !           302: #endif
        !           303:                return 0;
        !           304:        }
        !           305:
        !           306:        log_flush();
        !           307: #ifdef DEBUG
        !           308:        if (log_level >= 4)
        !           309:                log_puts("poll:");
        !           310: #endif
        !           311:        nfds = 0;
        !           312:        for (f = file_list; f != NULL; f = f->next) {
        !           313: #ifdef DEBUG
        !           314:                if (log_level >= 4) {
        !           315:                        log_puts(" ");
        !           316:                        file_log(f);
        !           317:                }
        !           318: #endif
        !           319:                n = f->ops->pollfd(f->arg, pfds + nfds);
        !           320:                if (n == 0) {
        !           321:                        f->pfd = NULL;
        !           322:                        continue;
        !           323:                }
        !           324:                f->pfd = pfds + nfds;
        !           325:                nfds += n;
        !           326: #ifdef DEBUG
        !           327:                if (log_level >= 4) {
        !           328:                        log_puts("=");
        !           329:                        for (i = 0; i < n; i++) {
        !           330:                                if (i > 0)
        !           331:                                        log_puts(",");
        !           332:                                log_putx(f->pfd[i].events);
        !           333:                        }
        !           334:                }
        !           335: #endif
        !           336:        }
        !           337: #ifdef DEBUG
        !           338:        if (log_level >= 4)
        !           339:                log_puts("\n");
        !           340: #endif
        !           341:
        !           342: #ifdef DEBUG
        !           343:        clock_gettime(CLOCK_MONOTONIC, &sleepts);
        !           344:        file_utime += 1000000000LL * (sleepts.tv_sec - file_ts.tv_sec);
        !           345:        file_utime += sleepts.tv_nsec - file_ts.tv_nsec;
        !           346: #endif
        !           347:        res = poll(pfds, nfds, -1);
        !           348:        if (res < 0 && errno != EINTR)
        !           349:                err(1, "poll");
        !           350: #ifdef DEBUG
        !           351:        if (log_level >= 4) {
        !           352:                log_puts("poll: return:");
        !           353:                for (i = 0; i < nfds; i++) {
        !           354:                        log_puts(" ");
        !           355:                        log_putx(pfds[i].revents);
        !           356:                }
        !           357:                log_puts("\n");
        !           358:        }
        !           359: #endif
        !           360:        clock_gettime(CLOCK_MONOTONIC, &ts);
        !           361: #ifdef DEBUG
        !           362:        file_wtime += 1000000000LL * (ts.tv_sec - sleepts.tv_sec);
        !           363:        file_wtime += ts.tv_nsec - sleepts.tv_nsec;
        !           364: #endif
        !           365:        delta_nsec = 1000000000LL * (ts.tv_sec - file_ts.tv_sec);
        !           366:        delta_nsec += ts.tv_nsec - file_ts.tv_nsec;
        !           367: #ifdef DEBUG
        !           368:        if (delta_nsec < 0)
        !           369:                log_puts("file_poll: negative time interval\n");
        !           370: #endif
        !           371:        file_ts = ts;
        !           372:        if (delta_nsec >= 0 && delta_nsec < 1000000000LL)
        !           373:                timo_update(delta_nsec / 1000);
        !           374:        else {
        !           375:                if (log_level >= 2)
        !           376:                        log_puts("ignored huge clock delta\n");
        !           377:        }
        !           378:        if (res <= 0)
        !           379:                return 1;
        !           380:
        !           381:        for (f = file_list; f != NULL; f = f->next) {
        !           382:                if (f->pfd == NULL)
        !           383:                        continue;
        !           384: #ifdef DEBUG
        !           385:                clock_gettime(CLOCK_MONOTONIC, &ts0);
        !           386: #endif
        !           387:                revents = f->ops->revents(f->arg, f->pfd);
        !           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: }