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

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