GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/sndiod/file.c Lines: 0 169 0.0 %
Date: 2017-11-07 Branches: 0 96 0.0 %

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