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

Annotation of src/usr.bin/aucat/file.c, Revision 1.25

1.25    ! ratchov     1: /*     $OpenBSD: file.c,v 1.24 2011/03/15 10:31:31 okan Exp $  */
1.1       ratchov     2: /*
                      3:  * Copyright (c) 2008 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. If a read() or write() syscall return EAGAIN
                     21:  * (operation will block), then the file is marked as "for polling", else
                     22:  * the file is not polled again.
                     23:  *
1.12      ratchov    24:  * the module also provides trivial timeout implementation,
                     25:  * derived from:
                     26:  *
1.19      ratchov    27:  *     anoncvs@moule.caoua.org:/cvs
                     28:  *
                     29:  *             midish/timo.c rev 1.16
                     30:  *             midish/mdep.c rev 1.69
1.12      ratchov    31:  *
                     32:  * A timeout is used to schedule the call of a routine (the callback)
                     33:  * there is a global list of timeouts that is processed inside the
                     34:  * event loop. Timeouts work as follows:
                     35:  *
                     36:  *     first the timo structure must be initialized with timo_set()
                     37:  *
                     38:  *     then the timeout is scheduled (only once) with timo_add()
                     39:  *
                     40:  *     if the timeout expires, the call-back is called; then it can
                     41:  *     be scheduled again if needed. It's OK to reschedule it again
                     42:  *     from the callback
                     43:  *
                     44:  *     the timeout can be aborted with timo_del(), it is OK to try to
                     45:  *     abort a timout that has expired
                     46:  *
1.1       ratchov    47:  */
1.12      ratchov    48:
1.22      ratchov    49: #include <sys/time.h>
1.1       ratchov    50: #include <sys/types.h>
                     51:
                     52: #include <err.h>
                     53: #include <errno.h>
                     54: #include <fcntl.h>
                     55: #include <poll.h>
                     56: #include <signal.h>
                     57: #include <stdio.h>
                     58: #include <stdlib.h>
1.22      ratchov    59: #include <time.h>
1.1       ratchov    60:
1.13      ratchov    61: #include "abuf.h"
                     62: #include "aproc.h"
1.1       ratchov    63: #include "conf.h"
                     64: #include "file.h"
1.15      ratchov    65: #ifdef DEBUG
                     66: #include "dbg.h"
                     67: #endif
1.1       ratchov    68:
                     69: #define MAXFDS 100
1.19      ratchov    70: #define TIMER_USEC 10000
1.1       ratchov    71:
1.19      ratchov    72: struct timespec file_ts;
1.1       ratchov    73: struct filelist file_list;
1.12      ratchov    74: struct timo *timo_queue;
                     75: unsigned timo_abstime;
                     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 delta)
                     95: {
                     96:        struct timo **i;
                     97:        unsigned val;
                     98:        int diff;
                     99:
1.15      ratchov   100: #ifdef DEBUG
                    101:        if (o->set) {
1.19      ratchov   102:                dbg_puts("timo_add: already set\n");
1.15      ratchov   103:                dbg_panic();
                    104:        }
                    105:        if (delta == 0) {
1.19      ratchov   106:                dbg_puts("timo_add: zero timeout is evil\n");
1.15      ratchov   107:                dbg_panic();
                    108:        }
                    109: #endif
1.12      ratchov   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:        }
1.15      ratchov   138: #ifdef DEBUG
                    139:        if (debug_level >= 4)
                    140:                dbg_puts("timo_del: not found\n");
                    141: #endif
1.12      ratchov   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 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: {
1.15      ratchov   195: #ifdef DEBUG
                    196:        if (timo_queue != NULL) {
                    197:                dbg_puts("timo_done: timo_queue not empty!\n");
                    198:                dbg_panic();
                    199:        }
                    200: #endif
1.12      ratchov   201:        timo_queue = (struct timo *)0xdeadbeef;
                    202: }
1.1       ratchov   203:
1.15      ratchov   204: #ifdef DEBUG
                    205: void
                    206: file_dbg(struct file *f)
                    207: {
                    208:        dbg_puts(f->ops->name);
                    209:        dbg_puts("(");
                    210:        dbg_puts(f->name);
                    211:        dbg_puts("|");
                    212:        if (f->state & FILE_ROK)
                    213:                dbg_puts("r");
                    214:        if (f->state & FILE_RINUSE)
                    215:                dbg_puts("R");
                    216:        if (f->state & FILE_WOK)
                    217:                dbg_puts("w");
                    218:        if (f->state & FILE_WINUSE)
                    219:                dbg_puts("W");
                    220:        if (f->state & FILE_EOF)
                    221:                dbg_puts("e");
                    222:        if (f->state & FILE_HUP)
                    223:                dbg_puts("h");
                    224:        if (f->state & FILE_ZOMB)
                    225:                dbg_puts("Z");
                    226:        dbg_puts(")");
                    227: }
                    228: #endif
1.3       ratchov   229:
1.1       ratchov   230: struct file *
1.4       ratchov   231: file_new(struct fileops *ops, char *name, unsigned nfds)
1.1       ratchov   232: {
                    233:        struct file *f;
                    234:
                    235:        LIST_FOREACH(f, &file_list, entry)
1.4       ratchov   236:                nfds += f->ops->nfds(f);
1.11      ratchov   237:        if (nfds > MAXFDS) {
1.15      ratchov   238: #ifdef DEBUG
                    239:                if (debug_level >= 1) {
                    240:                        dbg_puts(name);
                    241:                        dbg_puts(": too many polled files\n");
                    242:                }
                    243: #endif
1.11      ratchov   244:                return NULL;
                    245:        }
1.4       ratchov   246:        f = malloc(ops->size);
1.1       ratchov   247:        if (f == NULL)
1.4       ratchov   248:                err(1, "file_new: %s", ops->name);
                    249:        f->ops = ops;
1.1       ratchov   250:        f->name = name;
                    251:        f->state = 0;
1.23      ratchov   252: #ifdef DEBUG
1.21      ratchov   253:        f->cycles = 0;
1.23      ratchov   254: #endif
1.1       ratchov   255:        f->rproc = NULL;
                    256:        f->wproc = NULL;
                    257:        LIST_INSERT_HEAD(&file_list, f, entry);
1.15      ratchov   258: #ifdef DEBUG
                    259:        if (debug_level >= 3) {
                    260:                file_dbg(f);
                    261:                dbg_puts(": created\n");
                    262:        }
                    263: #endif
1.1       ratchov   264:        return f;
                    265: }
                    266:
                    267: void
                    268: file_del(struct file *f)
                    269: {
1.15      ratchov   270: #ifdef DEBUG
                    271:        if (debug_level >= 3) {
                    272:                file_dbg(f);
                    273:                dbg_puts(": terminating...\n");
                    274:        }
                    275: #endif
1.12      ratchov   276:        if (f->state & (FILE_RINUSE | FILE_WINUSE)) {
1.4       ratchov   277:                f->state |= FILE_ZOMB;
                    278:        } else {
                    279:                LIST_REMOVE(f, entry);
1.15      ratchov   280: #ifdef DEBUG
                    281:                if (debug_level >= 3) {
                    282:                        file_dbg(f);
                    283:                        dbg_puts(": destroyed\n");
                    284:                }
                    285: #endif
1.4       ratchov   286:                f->ops->close(f);
                    287:                free(f);
                    288:        }
1.1       ratchov   289: }
                    290:
                    291: int
                    292: file_poll(void)
                    293: {
1.4       ratchov   294:        nfds_t nfds, n;
                    295:        short events, revents;
1.1       ratchov   296:        struct pollfd pfds[MAXFDS];
                    297:        struct file *f, *fnext;
1.2       ratchov   298:        struct aproc *p;
1.19      ratchov   299:        struct timespec ts;
                    300:        long delta_nsec;
1.1       ratchov   301:
1.25    ! ratchov   302:        if (LIST_EMPTY(&file_list) && timo_queue == NULL) {
1.19      ratchov   303: #ifdef DEBUG
                    304:                if (debug_level >= 3)
                    305:                        dbg_puts("nothing to do...\n");
                    306: #endif
                    307:                return 0;
                    308:        }
1.4       ratchov   309:        /*
1.13      ratchov   310:         * Fill the pfds[] array with files that are blocked on reading
                    311:         * and/or writing, skipping those that are just waiting.
1.4       ratchov   312:         */
1.15      ratchov   313: #ifdef DEBUG
                    314:        dbg_flush();
                    315:        if (debug_level >= 4)
                    316:                dbg_puts("poll:");
                    317: #endif
1.1       ratchov   318:        nfds = 0;
                    319:        LIST_FOREACH(f, &file_list, entry) {
1.4       ratchov   320:                events = 0;
                    321:                if (f->rproc && !(f->state & FILE_ROK))
                    322:                        events |= POLLIN;
                    323:                if (f->wproc && !(f->state & FILE_WOK))
                    324:                        events |= POLLOUT;
1.15      ratchov   325: #ifdef DEBUG
                    326:                if (debug_level >= 4) {
                    327:                        dbg_puts(" ");
                    328:                        file_dbg(f);
                    329:                }
                    330: #endif
1.4       ratchov   331:                n = f->ops->pollfd(f, pfds + nfds, events);
                    332:                if (n == 0) {
1.1       ratchov   333:                        f->pfd = NULL;
                    334:                        continue;
                    335:                }
1.4       ratchov   336:                f->pfd = pfds + nfds;
                    337:                nfds += n;
1.1       ratchov   338:        }
1.15      ratchov   339: #ifdef DEBUG
                    340:        if (debug_level >= 4) {
                    341:                dbg_puts("\npfds[] =");
                    342:                for (n = 0; n < nfds; n++) {
                    343:                        dbg_puts(" ");
                    344:                        dbg_putx(pfds[n].events);
                    345:                }
                    346:                dbg_puts("\n");
                    347:        }
                    348: #endif
1.9       ratchov   349:        if (nfds > 0) {
1.19      ratchov   350:                if (poll(pfds, nfds, -1) < 0) {
1.9       ratchov   351:                        if (errno == EINTR)
                    352:                                return 1;
                    353:                        err(1, "file_poll: poll failed");
                    354:                }
1.19      ratchov   355:                clock_gettime(CLOCK_MONOTONIC, &ts);
                    356:                delta_nsec = 1000000000L * (ts.tv_sec - file_ts.tv_sec);
                    357:                delta_nsec += ts.tv_nsec - file_ts.tv_nsec;
                    358:                if (delta_nsec > 0) {
                    359:                        file_ts = ts;
                    360:                        timo_update(delta_nsec / 1000);
1.12      ratchov   361:                }
1.1       ratchov   362:        }
1.4       ratchov   363:        f = LIST_FIRST(&file_list);
1.22      ratchov   364:        while (f != NULL) {
1.4       ratchov   365:                if (f->pfd == NULL) {
                    366:                        f = LIST_NEXT(f, entry);
1.1       ratchov   367:                        continue;
1.4       ratchov   368:                }
                    369:                revents = f->ops->revents(f, f->pfd);
1.20      ratchov   370: #ifdef DEBUG
                    371:                if (revents) {
                    372:                        f->cycles++;
                    373:                        if (f->cycles > FILE_MAXCYCLES) {
                    374:                                file_dbg(f);
                    375:                                dbg_puts(": busy loop, disconnecting\n");
                    376:                                revents = POLLHUP;
                    377:                        }
                    378:                }
                    379: #endif
1.4       ratchov   380:                if (!(f->state & FILE_ZOMB) && (revents & POLLIN)) {
                    381:                        revents &= ~POLLIN;
1.15      ratchov   382: #ifdef DEBUG
                    383:                        if (debug_level >= 4) {
                    384:                                file_dbg(f);
                    385:                                dbg_puts(": rok\n");
                    386:                        }
                    387: #endif
1.1       ratchov   388:                        f->state |= FILE_ROK;
1.12      ratchov   389:                        f->state |= FILE_RINUSE;
1.4       ratchov   390:                        for (;;) {
1.2       ratchov   391:                                p = f->rproc;
1.14      ratchov   392:                                if (!p)
                    393:                                        break;
1.15      ratchov   394: #ifdef DEBUG
                    395:                                if (debug_level >= 4) {
                    396:                                        aproc_dbg(p);
                    397:                                        dbg_puts(": in\n");
                    398:                                }
                    399: #endif
1.14      ratchov   400:                                if (!p->ops->in(p, NULL))
1.1       ratchov   401:                                        break;
                    402:                        }
1.12      ratchov   403:                        f->state &= ~FILE_RINUSE;
1.1       ratchov   404:                }
1.4       ratchov   405:                if (!(f->state & FILE_ZOMB) && (revents & POLLOUT)) {
                    406:                        revents &= ~POLLOUT;
1.15      ratchov   407: #ifdef DEBUG
                    408:                        if (debug_level >= 4) {
                    409:                                file_dbg(f);
                    410:                                dbg_puts(": wok\n");
                    411:                        }
                    412: #endif
1.1       ratchov   413:                        f->state |= FILE_WOK;
1.12      ratchov   414:                        f->state |= FILE_WINUSE;
1.4       ratchov   415:                        for (;;) {
1.2       ratchov   416:                                p = f->wproc;
1.14      ratchov   417:                                if (!p)
                    418:                                        break;
1.15      ratchov   419: #ifdef DEBUG
                    420:                                if (debug_level >= 4) {
                    421:                                        aproc_dbg(p);
                    422:                                        dbg_puts(": out\n");
                    423:                                }
                    424: #endif
1.14      ratchov   425:                                if (!p->ops->out(p, NULL))
1.1       ratchov   426:                                        break;
                    427:                        }
1.12      ratchov   428:                        f->state &= ~FILE_WINUSE;
1.7       ratchov   429:                }
                    430:                if (!(f->state & FILE_ZOMB) && (revents & POLLHUP)) {
1.15      ratchov   431: #ifdef DEBUG
                    432:                        if (debug_level >= 3) {
                    433:                                file_dbg(f);
                    434:                                dbg_puts(": disconnected\n");
                    435:                        }
                    436: #endif
1.7       ratchov   437:                        f->state |= (FILE_EOF | FILE_HUP);
1.1       ratchov   438:                }
1.4       ratchov   439:                if (!(f->state & FILE_ZOMB) && (f->state & FILE_EOF)) {
1.15      ratchov   440: #ifdef DEBUG
                    441:                        if (debug_level >= 3) {
                    442:                                file_dbg(f);
                    443:                                dbg_puts(": eof\n");
                    444:                        }
                    445: #endif
1.2       ratchov   446:                        p = f->rproc;
1.12      ratchov   447:                        if (p) {
                    448:                                f->state |= FILE_RINUSE;
1.15      ratchov   449: #ifdef DEBUG
                    450:                                if (debug_level >= 3) {
                    451:                                        aproc_dbg(p);
                    452:                                        dbg_puts(": eof\n");
                    453:                                }
                    454: #endif
1.2       ratchov   455:                                p->ops->eof(p, NULL);
1.12      ratchov   456:                                f->state &= ~FILE_RINUSE;
                    457:                        }
1.1       ratchov   458:                        f->state &= ~FILE_EOF;
                    459:                }
1.4       ratchov   460:                if (!(f->state & FILE_ZOMB) && (f->state & FILE_HUP)) {
1.15      ratchov   461: #ifdef DEBUG
                    462:                        if (debug_level >= 3) {
                    463:                                file_dbg(f);
                    464:                                dbg_puts(": hup\n");
                    465:                        }
                    466: #endif
1.2       ratchov   467:                        p = f->wproc;
1.12      ratchov   468:                        if (p) {
                    469:                                f->state |= FILE_WINUSE;
1.15      ratchov   470: #ifdef DEBUG
                    471:                                if (debug_level >= 3) {
                    472:                                        aproc_dbg(p);
                    473:                                        dbg_puts(": hup\n");
                    474:                                }
                    475: #endif
1.2       ratchov   476:                                p->ops->hup(p, NULL);
1.12      ratchov   477:                                f->state &= ~FILE_WINUSE;
                    478:                        }
1.1       ratchov   479:                        f->state &= ~FILE_HUP;
                    480:                }
                    481:                fnext = LIST_NEXT(f, entry);
1.4       ratchov   482:                if (f->state & FILE_ZOMB)
1.3       ratchov   483:                        file_del(f);
1.4       ratchov   484:                f = fnext;
1.1       ratchov   485:        }
1.25    ! ratchov   486:        if (LIST_EMPTY(&file_list) && timo_queue == NULL) {
1.15      ratchov   487: #ifdef DEBUG
                    488:                if (debug_level >= 3)
                    489:                        dbg_puts("no files anymore...\n");
                    490: #endif
1.1       ratchov   491:                return 0;
                    492:        }
                    493:        return 1;
                    494: }
                    495:
1.19      ratchov   496: /*
                    497:  * handler for SIGALRM, invoked periodically
                    498:  */
                    499: void
                    500: file_sigalrm(int i)
                    501: {
                    502:        /* nothing to do, we only want poll() to return EINTR */
                    503: }
                    504:
                    505:
1.1       ratchov   506: void
1.4       ratchov   507: filelist_init(void)
1.1       ratchov   508: {
1.19      ratchov   509:        static struct sigaction sa;
                    510:        struct itimerval it;
1.1       ratchov   511:        sigset_t set;
                    512:
                    513:        sigemptyset(&set);
                    514:        (void)sigaddset(&set, SIGPIPE);
                    515:        if (sigprocmask(SIG_BLOCK, &set, NULL))
                    516:                err(1, "sigprocmask");
                    517:        LIST_INIT(&file_list);
1.19      ratchov   518:        if (clock_gettime(CLOCK_MONOTONIC, &file_ts) < 0) {
                    519:                perror("clock_gettime");
                    520:                exit(1);
                    521:        }
                    522:         sa.sa_flags = SA_RESTART;
                    523:         sa.sa_handler = file_sigalrm;
                    524:         sigfillset(&sa.sa_mask);
                    525:         if (sigaction(SIGALRM, &sa, NULL) < 0) {
                    526:                perror("sigaction");
                    527:                exit(1);
                    528:        }
                    529:        it.it_interval.tv_sec = 0;
                    530:        it.it_interval.tv_usec = TIMER_USEC;
                    531:        it.it_value.tv_sec = 0;
                    532:        it.it_value.tv_usec = TIMER_USEC;
                    533:        if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
                    534:                perror("setitimer");
                    535:                exit(1);
                    536:        }
1.12      ratchov   537:        timo_init();
1.15      ratchov   538: #ifdef DEBUG
                    539:        dbg_sync = 0;
                    540: #endif
1.1       ratchov   541: }
                    542:
                    543: void
1.4       ratchov   544: filelist_done(void)
1.1       ratchov   545: {
1.19      ratchov   546:        struct itimerval it;
1.15      ratchov   547: #ifdef DEBUG
                    548:        struct file *f;
                    549:
                    550:        if (!LIST_EMPTY(&file_list)) {
                    551:                LIST_FOREACH(f, &file_list, entry) {
                    552:                        file_dbg(f);
1.19      ratchov   553:                        dbg_puts(" not closed\n");
1.15      ratchov   554:                }
                    555:                dbg_panic();
                    556:        }
                    557:        dbg_sync = 1;
                    558:        dbg_flush();
                    559: #endif
1.24      okan      560:        timerclear(&it.it_value);
                    561:        timerclear(&it.it_interval);
1.19      ratchov   562:        if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
                    563:                perror("setitimer");
                    564:                exit(1);
                    565:        }
1.12      ratchov   566:        timo_done();
1.1       ratchov   567: }
                    568:
1.4       ratchov   569: unsigned
1.14      ratchov   570: file_read(struct file *f, unsigned char *data, unsigned count)
1.4       ratchov   571: {
1.14      ratchov   572:        unsigned n;
1.15      ratchov   573: #ifdef DEBUG
1.19      ratchov   574:        struct timespec ts0, ts1;
                    575:        long us;
1.15      ratchov   576:
                    577:        if (!(f->state & FILE_ROK)) {
                    578:                file_dbg(f);
                    579:                dbg_puts(": read: bad state\n");
                    580:                dbg_panic();
                    581:        }
1.19      ratchov   582:        clock_gettime(CLOCK_MONOTONIC, &ts0);
1.15      ratchov   583: #endif
1.14      ratchov   584:        n = f->ops->read(f, data, count);
1.15      ratchov   585: #ifdef DEBUG
1.20      ratchov   586:        if (n > 0)
                    587:                f->cycles = 0;
1.19      ratchov   588:        clock_gettime(CLOCK_MONOTONIC, &ts1);
                    589:        us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
                    590:        us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
                    591:        if (debug_level >= 4 || (debug_level >= 2 && us >= 5000)) {
1.15      ratchov   592:                dbg_puts(f->name);
                    593:                dbg_puts(": read ");
                    594:                dbg_putu(n);
                    595:                dbg_puts(" bytes in ");
                    596:                dbg_putu(us);
                    597:                dbg_puts("us\n");
                    598:        }
                    599: #endif
1.14      ratchov   600:        return n;
1.4       ratchov   601: }
1.1       ratchov   602:
                    603: unsigned
1.14      ratchov   604: file_write(struct file *f, unsigned char *data, unsigned count)
1.1       ratchov   605: {
1.14      ratchov   606:        unsigned n;
1.15      ratchov   607: #ifdef DEBUG
1.19      ratchov   608:        struct timespec ts0, ts1;
                    609:        long us;
1.15      ratchov   610:
                    611:        if (!(f->state & FILE_WOK)) {
                    612:                file_dbg(f);
                    613:                dbg_puts(": write: bad state\n");
                    614:                dbg_panic();
                    615:        }
1.19      ratchov   616:        clock_gettime(CLOCK_MONOTONIC, &ts0);
1.15      ratchov   617: #endif
1.14      ratchov   618:        n = f->ops->write(f, data, count);
1.15      ratchov   619: #ifdef DEBUG
1.20      ratchov   620:        if (n > 0)
                    621:                f->cycles = 0;
1.19      ratchov   622:        clock_gettime(CLOCK_MONOTONIC, &ts1);
                    623:        us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
                    624:        us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
                    625:        if (debug_level >= 4 || (debug_level >= 2 && us >= 5000)) {
1.15      ratchov   626:                dbg_puts(f->name);
                    627:                dbg_puts(": wrote ");
                    628:                dbg_putu(n);
                    629:                dbg_puts(" bytes in ");
                    630:                dbg_putu(us);
                    631:                dbg_puts("us\n");
                    632:        }
                    633: #endif
1.14      ratchov   634:        return n;
1.1       ratchov   635: }
                    636:
                    637: void
                    638: file_eof(struct file *f)
                    639: {
1.4       ratchov   640:        struct aproc *p;
                    641:
1.15      ratchov   642: #ifdef DEBUG
                    643:        if (debug_level >= 3) {
                    644:                file_dbg(f);
                    645:                dbg_puts(": eof requested\n");
                    646:        }
                    647: #endif
1.12      ratchov   648:        if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.4       ratchov   649:                p = f->rproc;
1.12      ratchov   650:                if (p) {
                    651:                        f->state |= FILE_RINUSE;
1.15      ratchov   652: #ifdef DEBUG
                    653:                        if (debug_level >= 3) {
                    654:                                aproc_dbg(p);
                    655:                                dbg_puts(": eof\n");
                    656:                        }
                    657: #endif
1.4       ratchov   658:                        p->ops->eof(p, NULL);
1.12      ratchov   659:                        f->state &= ~FILE_RINUSE;
                    660:                }
1.4       ratchov   661:                if (f->state & FILE_ZOMB)
                    662:                        file_del(f);
                    663:        } else {
                    664:                f->state &= ~FILE_ROK;
                    665:                f->state |= FILE_EOF;
                    666:        }
1.1       ratchov   667: }
                    668:
                    669: void
                    670: file_hup(struct file *f)
                    671: {
1.4       ratchov   672:        struct aproc *p;
                    673:
1.15      ratchov   674: #ifdef DEBUG
                    675:        if (debug_level >= 3) {
                    676:                file_dbg(f);
                    677:                dbg_puts(": hup requested\n");
                    678:        }
                    679: #endif
1.12      ratchov   680:        if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.4       ratchov   681:                p = f->wproc;
1.12      ratchov   682:                if (p) {
                    683:                        f->state |= FILE_WINUSE;
1.15      ratchov   684: #ifdef DEBUG
                    685:                        if (debug_level >= 3) {
                    686:                                aproc_dbg(p);
                    687:                                dbg_puts(": hup\n");
                    688:                        }
                    689: #endif
1.4       ratchov   690:                        p->ops->hup(p, NULL);
1.12      ratchov   691:                        f->state &= ~FILE_WINUSE;
                    692:                }
1.4       ratchov   693:                if (f->state & FILE_ZOMB)
                    694:                        file_del(f);
                    695:        } else {
                    696:                f->state &= ~FILE_WOK;
                    697:                f->state |= FILE_HUP;
1.9       ratchov   698:        }
                    699: }
                    700:
                    701: void
                    702: file_close(struct file *f)
                    703: {
                    704:        struct aproc *p;
                    705:
1.15      ratchov   706: #ifdef DEBUG
                    707:        if (debug_level >= 3) {
                    708:                file_dbg(f);
                    709:                dbg_puts(": closing\n");
                    710:        }
                    711: #endif
1.18      ratchov   712:        if (f->wproc == NULL && f->rproc == NULL)
                    713:                f->state |= FILE_ZOMB;
1.12      ratchov   714:        if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.9       ratchov   715:                p = f->rproc;
1.12      ratchov   716:                if (p) {
                    717:                        f->state |= FILE_RINUSE;
1.15      ratchov   718: #ifdef DEBUG
                    719:                        if (debug_level >= 3) {
                    720:                                aproc_dbg(p);
                    721:                                dbg_puts(": eof\n");
                    722:                        }
                    723: #endif
1.9       ratchov   724:                        p->ops->eof(p, NULL);
1.12      ratchov   725:                        f->state &= ~FILE_RINUSE;
                    726:                }
1.9       ratchov   727:                p = f->wproc;
1.12      ratchov   728:                if (p) {
                    729:                        f->state |= FILE_WINUSE;
1.15      ratchov   730: #ifdef DEBUG
                    731:                        if (debug_level >= 3) {
                    732:                                aproc_dbg(p);
                    733:                                dbg_puts(": hup\n");
                    734:                        }
                    735: #endif
1.9       ratchov   736:                        p->ops->hup(p, NULL);
1.12      ratchov   737:                        f->state &= ~FILE_WINUSE;
                    738:                }
1.9       ratchov   739:                if (f->state & FILE_ZOMB)
                    740:                        file_del(f);
                    741:        } else {
                    742:                f->state &= ~(FILE_ROK | FILE_WOK);
                    743:                f->state |= (FILE_EOF | FILE_HUP);
1.4       ratchov   744:        }
1.1       ratchov   745: }