Annotation of src/usr.bin/sndiod/file.c, Revision 1.3
1.3 ! ratchov 1: /* $OpenBSD: file.c,v 1.2 2012/12/07 08:04:58 ratchov Exp $ */
1.1 ratchov 2: /*
3: * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: /*
18: * non-blocking file i/o module: each file can be read or written (or
19: * both). To achieve non-blocking io, we simply use the poll() syscall
20: * in an event loop and dispatch events to sub-modules.
21: *
22: * the module also provides trivial timeout implementation,
23: * derived from:
24: *
25: * anoncvs@moule.caoua.org:/midish
26: *
27: * midish/timo.c rev 1.18
28: * midish/mdep.c rev 1.71
29: *
30: * A timeout is used to schedule the call of a routine (the callback)
31: * there is a global list of timeouts that is processed inside the
32: * event loop. Timeouts work as follows:
33: *
34: * first the timo structure must be initialized with timo_set()
35: *
36: * then the timeout is scheduled (only once) with timo_add()
37: *
38: * if the timeout expires, the call-back is called; then it can
39: * be scheduled again if needed. It's OK to reschedule it again
40: * from the callback
41: *
42: * the timeout can be aborted with timo_del(), it is OK to try to
43: * abort a timout that has expired
44: *
45: */
46:
47: #include <sys/time.h>
48: #include <sys/types.h>
49:
50: #include <err.h>
51: #include <errno.h>
52: #include <fcntl.h>
53: #include <poll.h>
54: #include <signal.h>
55: #include <stdio.h>
56: #include <stdlib.h>
57: #include <time.h>
58:
59: #include "file.h"
60: #include "utils.h"
61:
62: #define MAXFDS 100
63: #define TIMER_USEC 10000
64:
65: struct timespec file_ts;
66: struct file *file_list;
67: struct timo *timo_queue;
68: unsigned int timo_abstime;
69: int file_slowaccept = 0, file_nfds;
70: #ifdef DEBUG
71: long long file_wtime, file_utime;
72: #endif
73:
74: /*
75: * initialise a timeout structure, arguments are callback and argument
76: * that will be passed to the callback
77: */
78: void
79: timo_set(struct timo *o, void (*cb)(void *), void *arg)
80: {
81: o->cb = cb;
82: o->arg = arg;
83: o->set = 0;
84: }
85:
86: /*
87: * schedule the callback in 'delta' 24-th of microseconds. The timeout
88: * must not be already scheduled
89: */
90: void
91: timo_add(struct timo *o, unsigned int delta)
92: {
93: struct timo **i;
94: unsigned int val;
95: int diff;
96:
97: #ifdef DEBUG
98: if (o->set) {
99: log_puts("timo_add: already set\n");
100: panic();
101: }
102: if (delta == 0) {
103: log_puts("timo_add: zero timeout is evil\n");
104: panic();
105: }
106: #endif
107: val = timo_abstime + delta;
108: for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
109: diff = (*i)->val - val;
110: if (diff > 0) {
111: break;
112: }
113: }
114: o->set = 1;
115: o->val = val;
116: o->next = *i;
117: *i = o;
118: }
119:
120: /*
121: * abort a scheduled timeout
122: */
123: void
124: timo_del(struct timo *o)
125: {
126: struct timo **i;
127:
128: for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
129: if (*i == o) {
130: *i = o->next;
131: o->set = 0;
132: return;
133: }
134: }
135: #ifdef DEBUG
136: if (log_level >= 4)
137: log_puts("timo_del: not found\n");
138: #endif
139: }
140:
141: /*
142: * routine to be called by the timer when 'delta' 24-th of microsecond
143: * elapsed. This routine updates time referece used by timeouts and
144: * calls expired timeouts
145: */
146: void
147: timo_update(unsigned int delta)
148: {
149: struct timo *to;
150: int diff;
151:
152: /*
153: * update time reference
154: */
155: timo_abstime += delta;
156:
157: /*
158: * remove from the queue and run expired timeouts
159: */
160: while (timo_queue != NULL) {
161: /*
162: * there is no overflow here because + and - are
163: * modulo 2^32, they are the same for both signed and
164: * unsigned integers
165: */
166: diff = timo_queue->val - timo_abstime;
167: if (diff > 0)
168: break;
169: to = timo_queue;
170: timo_queue = to->next;
171: to->set = 0;
172: to->cb(to->arg);
173: }
174: }
175:
176: /*
177: * initialize timeout queue
178: */
179: void
180: timo_init(void)
181: {
182: timo_queue = NULL;
183: timo_abstime = 0;
184: }
185:
186: /*
187: * destroy timeout queue
188: */
189: void
190: timo_done(void)
191: {
192: #ifdef DEBUG
193: if (timo_queue != NULL) {
194: log_puts("timo_done: timo_queue not empty!\n");
195: panic();
196: }
197: #endif
198: timo_queue = (struct timo *)0xdeadbeef;
199: }
200:
201: #ifdef DEBUG
202: void
203: file_log(struct file *f)
204: {
205: static char *states[] = { "ini", "bus", "clo", "zom" };
206:
207: log_puts(f->ops->name);
208: if (log_level >= 3) {
209: log_puts("(");
210: log_puts(f->name);
211: log_puts("|");
212: log_puts(states[f->state]);
213: log_puts(")");
214: }
215: }
216: #endif
217:
218: struct file *
219: file_new(struct fileops *ops, void *arg, char *name, unsigned int nfds)
220: {
221: struct file *f;
222:
223: if (file_nfds + nfds > MAXFDS) {
224: #ifdef DEBUG
225: if (log_level >= 1) {
226: log_puts(name);
227: log_puts(": too many polled files\n");
228: }
229: #endif
230: return NULL;
231: }
232: f = xmalloc(sizeof(struct file));
233: f->nfds = nfds;
234: f->ops = ops;
235: f->arg = arg;
236: f->name = name;
237: f->state = FILE_INIT;
238: f->next = file_list;
239: file_list = f;
240: #ifdef DEBUG
241: if (log_level >= 3) {
242: file_log(f);
243: log_puts(": created\n");
244: }
245: #endif
246: file_nfds += f->nfds;
247: return f;
248: }
249:
250: void
251: file_del(struct file *f)
252: {
253: #ifdef DEBUG
254: if (f->state == FILE_ZOMB) {
255: log_puts("bad state in file_del()\n");
256: panic();
257: }
258: #endif
259: file_nfds -= f->nfds;
260: f->state = FILE_ZOMB;
261: #ifdef DEBUG
262: if (log_level >= 3) {
263: file_log(f);
264: log_puts(": destroyed\n");
265: }
266: #endif
267: }
268:
269: int
270: file_poll(void)
271: {
272: nfds_t nfds, n;
273: struct pollfd pfds[MAXFDS];
274: struct file *f, **pf;
275: struct timespec ts;
276: #ifdef DEBUG
277: struct timespec sleepts;
278: struct timespec ts0, ts1;
279: long us;
280: int i;
281: #endif
282: long long delta_nsec;
283: int revents, res;
284:
285: /*
286: * cleanup zombies
287: */
288: pf = &file_list;
289: while ((f = *pf) != NULL) {
290: if (f->state == FILE_ZOMB) {
291: *pf = f->next;
292: xfree(f);
293: } else
294: pf = &f->next;
295: }
296:
297: if (file_list == NULL && timo_queue == NULL) {
298: #ifdef DEBUG
299: if (log_level >= 3)
300: log_puts("nothing to do...\n");
301: #endif
302: return 0;
303: }
304:
305: log_flush();
306: #ifdef DEBUG
307: if (log_level >= 4)
308: log_puts("poll:");
309: #endif
310: nfds = 0;
311: for (f = file_list; f != NULL; f = f->next) {
312: #ifdef DEBUG
313: if (log_level >= 4) {
314: log_puts(" ");
315: file_log(f);
316: }
317: #endif
318: n = f->ops->pollfd(f->arg, pfds + nfds);
319: if (n == 0) {
320: f->pfd = NULL;
321: continue;
322: }
323: f->pfd = pfds + nfds;
324: nfds += n;
325: #ifdef DEBUG
326: if (log_level >= 4) {
327: log_puts("=");
328: for (i = 0; i < n; i++) {
329: if (i > 0)
330: log_puts(",");
331: log_putx(f->pfd[i].events);
332: }
333: }
334: #endif
335: }
336: #ifdef DEBUG
337: if (log_level >= 4)
338: log_puts("\n");
339: #endif
340:
341: #ifdef DEBUG
342: clock_gettime(CLOCK_MONOTONIC, &sleepts);
343: file_utime += 1000000000LL * (sleepts.tv_sec - file_ts.tv_sec);
344: file_utime += sleepts.tv_nsec - file_ts.tv_nsec;
345: #endif
346: res = poll(pfds, nfds, -1);
347: if (res < 0 && errno != EINTR)
348: err(1, "poll");
349: #ifdef DEBUG
350: if (log_level >= 4) {
351: log_puts("poll: return:");
352: for (i = 0; i < nfds; i++) {
353: log_puts(" ");
354: log_putx(pfds[i].revents);
355: }
356: log_puts("\n");
357: }
358: #endif
359: clock_gettime(CLOCK_MONOTONIC, &ts);
360: #ifdef DEBUG
361: file_wtime += 1000000000LL * (ts.tv_sec - sleepts.tv_sec);
362: file_wtime += ts.tv_nsec - sleepts.tv_nsec;
363: #endif
364: delta_nsec = 1000000000LL * (ts.tv_sec - file_ts.tv_sec);
365: delta_nsec += ts.tv_nsec - file_ts.tv_nsec;
366: #ifdef DEBUG
367: if (delta_nsec < 0)
368: log_puts("file_poll: negative time interval\n");
369: #endif
370: file_ts = ts;
371: if (delta_nsec >= 0 && delta_nsec < 1000000000LL)
372: timo_update(delta_nsec / 1000);
373: else {
374: if (log_level >= 2)
375: log_puts("ignored huge clock delta\n");
376: }
377: if (res <= 0)
378: return 1;
379:
380: for (f = file_list; f != NULL; f = f->next) {
381: if (f->pfd == NULL)
382: continue;
383: #ifdef DEBUG
384: clock_gettime(CLOCK_MONOTONIC, &ts0);
385: #endif
1.3 ! ratchov 386: revents = (f->state != FILE_ZOMB) ?
! 387: f->ops->revents(f->arg, f->pfd) : 0;
1.1 ratchov 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: }