Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/libbegemot/rpoll.c
39475 views
1
/*
2
* Copyright (c)1996-2002 by Hartmut Brandt
3
* All rights reserved.
4
*
5
* Author: Hartmut Brandt
6
*
7
* Redistribution of this software and documentation and use in source and
8
* binary forms, with or without modification, are permitted provided that
9
* the following conditions are met:
10
*
11
* 1. Redistributions of source code or documentation must retain the above
12
* copyright notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
*
17
* THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR
18
* AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
19
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21
* THE AUTHOR OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
*/
29
/*
30
* These functions try to hide the poll/select/setitimer interface from the
31
* user. You associate callback functions with file descriptors and timers.
32
*
33
* $Begemot: libbegemot/rpoll.c,v 1.14 2004/09/21 15:59:00 brandt Exp $
34
*/
35
# include <stdio.h>
36
# include <stdlib.h>
37
# include <stddef.h>
38
# include <stdarg.h>
39
# include <signal.h>
40
# include <string.h>
41
# include <errno.h>
42
# include <time.h>
43
# include <assert.h>
44
# include <unistd.h>
45
# include <sys/time.h>
46
47
# include "rpoll.h"
48
49
/*
50
# define DEBUG
51
*/
52
53
# ifdef USE_POLL
54
# ifdef NEED_POLL_XOPEN_TWIDDLE
55
# define __USE_XOPEN
56
# endif
57
# include <poll.h>
58
# ifdef NEED_POLL_XOPEN_TWIDDLE
59
# undef __USE_XOPEN
60
# endif
61
# include <stropts.h>
62
# endif
63
64
/*
65
* the second define is for Linux, which sometimes fails to
66
* declare INFTIM.
67
*/
68
# if defined(USE_SELECT) || !defined(INFTIM)
69
# define INFTIM (-1)
70
# endif
71
72
# if defined(SIGPOLL)
73
# define SIGNAL SIGPOLL
74
# else
75
# if defined(SIGIO)
76
# define SIGNAL SIGIO
77
# endif
78
# endif
79
80
# ifdef USE_POLL
81
# define poll_in (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
82
# define poll_out (POLLOUT | POLLWRNORM | POLLWRBAND)
83
# define poll_except (POLLERR | POLLHUP)
84
# endif
85
86
# ifdef BROKEN_SELECT_PROTO
87
# define SELECT_CAST(P) (int *)P
88
# else
89
# define SELECT_CAST(P) P
90
# endif
91
92
93
typedef int64_t tval_t;
94
95
static inline tval_t GETUSECS(void);
96
97
static inline tval_t
98
GETUSECS(void) {
99
struct timeval tval;
100
101
(void)gettimeofday(&tval, NULL);
102
return (tval_t)tval.tv_sec * 1000000 + tval.tv_usec;
103
}
104
105
/*
106
* Simple fatal exit.
107
*/
108
static void
109
_panic(const char *fmt, ...)
110
{
111
va_list ap;
112
113
va_start(ap, fmt);
114
fprintf(stderr, "panic: ");
115
vfprintf(stderr, fmt, ap);
116
fprintf(stderr, "\n");
117
va_end(ap);
118
119
exit(1);
120
}
121
122
static void *
123
_xrealloc(void *p, size_t s)
124
{
125
void *ptr;
126
127
if(p == NULL) {
128
if((ptr=malloc(s)) == NULL && (s!=0 || (ptr=malloc(1)) == NULL))
129
_panic("out of memory: xrealloc(%lx, %lu)",
130
(unsigned long)p, (unsigned long)s);
131
} else if(s == 0) {
132
free(p);
133
if((ptr=malloc(s)) == NULL && (ptr=malloc(1)) == NULL)
134
_panic("out of memory: xrealloc(%lx, %lu)",
135
(unsigned long)p, (unsigned long)s);
136
} else {
137
if((ptr = realloc(p, s)) == NULL)
138
_panic("out of memory: xrealloc(%lx, %lu)",
139
(unsigned long)p, (unsigned long)s);
140
}
141
142
return ptr;
143
}
144
145
/*
146
* This structure holds one registration record for files
147
*/
148
typedef struct {
149
int fd; /* file descriptor (-1 if struct unused) */
150
int mask; /* event flags */
151
void * arg; /* client arg */
152
poll_f func; /* handler */
153
# ifdef USE_POLL
154
struct pollfd *pfd; /* pointer to corresponding poll() structure */
155
# endif
156
} PollReg_t;
157
158
/*
159
* Now for timers
160
*/
161
typedef struct {
162
uint64_t usecs; /* microsecond value of the timer */
163
int repeat; /* one shot or repeat? */
164
void *arg; /* client arg */
165
timer_f func; /* handler, 0 means disfunct */
166
tval_t when; /* next time to trigger in usecs! */
167
} PollTim_t;
168
169
/* how many records should our table grow at once? */
170
# define POLL_REG_GROW 100
171
172
# ifdef USE_POLL
173
static struct pollfd * pfd; /* fd list for poll() */
174
# endif
175
176
# ifdef USE_SELECT
177
static fd_set rset, wset, xset; /* file descriptor sets for select() */
178
static int maxfd; /* maximum fd number */
179
# endif
180
181
static int in_dispatch;
182
183
static PollReg_t * regs; /* registration records */
184
static u_int regs_alloc; /* how many are allocated */
185
static u_int regs_used; /* upper used limit */
186
static sigset_t bset; /* blocked signals */
187
static int rebuild; /* rebuild table on next dispatch() */
188
189
static int * tfd; /* sorted entries */
190
static u_int tfd_alloc; /* number of entries allocated */
191
static u_int tfd_used; /* number of entries used */
192
static PollTim_t * tims; /* timer registration records */
193
static u_int tims_alloc; /* how many are allocated */
194
static u_int tims_used; /* how many are used */
195
static int resort; /* resort on next dispatch */
196
197
int rpoll_trace;
198
int rpoll_policy; /* if 0 start sched callbacks from 0 else try round robin */
199
200
static void poll_build(void);
201
static void poll_blocksig(void);
202
static void poll_unblocksig(void);
203
static void sort_timers(void);
204
205
206
/*
207
* Private function to block SIGPOLL or SIGIO for a short time.
208
* Don't forget to call poll_unblock before return from the calling function.
209
* Don't change the mask between this calls (your changes will be lost).
210
*/
211
static void
212
poll_blocksig(void)
213
{
214
sigset_t set;
215
216
sigemptyset(&set);
217
sigaddset(&set, SIGNAL);
218
219
if(sigprocmask(SIG_BLOCK, &set, &bset))
220
_panic("sigprocmask(SIG_BLOCK): %s", strerror(errno));
221
}
222
223
/*
224
* unblock the previously blocked signal
225
*/
226
static void
227
poll_unblocksig(void)
228
{
229
if(sigprocmask(SIG_SETMASK, &bset, NULL))
230
_panic("sigprocmask(SIG_SETMASK): %s", strerror(errno));
231
}
232
233
/*
234
* Register the file descriptor fd. If the event corresponding to
235
* mask arrives func is called with arg.
236
* If fd is already registered with that func and arg, only the mask
237
* is changed.
238
* We block the IO-signal, so the dispatch function can be called from
239
* within the signal handler.
240
*/
241
int
242
poll_register(int fd, poll_f func, void *arg, int mask)
243
{
244
PollReg_t * p;
245
246
poll_blocksig();
247
248
/* already registered? */
249
for(p = regs; p < &regs[regs_alloc]; p++)
250
if(p->fd == fd && p->func == func && p->arg == arg) {
251
p->mask = mask;
252
break;
253
}
254
255
if(p == &regs[regs_alloc]) {
256
/* no - register */
257
258
/* find a free slot */
259
for(p = regs; p < &regs[regs_alloc]; p++)
260
if(p->fd == -1)
261
break;
262
263
if(p == &regs[regs_alloc]) {
264
size_t newsize = regs_alloc + POLL_REG_GROW;
265
regs = _xrealloc(regs, sizeof(regs[0]) * newsize);
266
for(p = &regs[regs_alloc]; p < &regs[newsize]; p++) {
267
p->fd = -1;
268
# ifdef USE_POLL
269
p->pfd = NULL;
270
# endif
271
}
272
p = &regs[regs_alloc];
273
regs_alloc = newsize;
274
}
275
276
p->fd = fd;
277
p->arg = arg;
278
p->mask = mask;
279
p->func = func;
280
281
regs_used++;
282
rebuild = 1;
283
}
284
285
poll_unblocksig();
286
287
if(rpoll_trace)
288
fprintf(stderr, "%s(%d, %p, %p, %#x)->%tu\n", __func__, fd,
289
(void *)func, (void *)arg, mask, p - regs);
290
return p - regs;
291
}
292
293
/*
294
* remove registration
295
*/
296
void
297
poll_unregister(int handle)
298
{
299
if(rpoll_trace)
300
fprintf(stderr, "%s(%d)\n", __func__, handle);
301
302
poll_blocksig();
303
304
regs[handle].fd = -1;
305
# ifdef USE_POLL
306
regs[handle].pfd = NULL;
307
# endif
308
rebuild = 1;
309
regs_used--;
310
311
poll_unblocksig();
312
}
313
314
/*
315
* Build the structures used by poll() or select()
316
*/
317
static void
318
poll_build(void)
319
{
320
PollReg_t * p;
321
322
# ifdef USE_POLL
323
struct pollfd * f;
324
325
f = pfd = _xrealloc(pfd, sizeof(pfd[0]) * regs_used);
326
327
for(p = regs; p < &regs[regs_alloc]; p++)
328
if(p->fd >= 0) {
329
f->fd = p->fd;
330
f->events = 0;
331
if(p->mask & RPOLL_IN)
332
f->events |= poll_in;
333
if(p->mask & RPOLL_OUT)
334
f->events |= poll_out;
335
if(p->mask & RPOLL_EXCEPT)
336
f->events |= poll_except;
337
f->revents = 0;
338
p->pfd = f++;
339
}
340
assert(f == &pfd[regs_used]);
341
# endif
342
343
# ifdef USE_SELECT
344
FD_ZERO(&rset);
345
FD_ZERO(&wset);
346
FD_ZERO(&xset);
347
maxfd = -1;
348
for(p = regs; p < &regs[regs_alloc]; p++)
349
if(p->fd >= 0) {
350
if(p->fd > maxfd)
351
maxfd = p->fd;
352
if(p->mask & RPOLL_IN)
353
FD_SET(p->fd, &rset);
354
if(p->mask & RPOLL_OUT)
355
FD_SET(p->fd, &wset);
356
if(p->mask & RPOLL_EXCEPT)
357
FD_SET(p->fd, &xset);
358
}
359
# endif
360
}
361
362
int
363
poll_start_timer(u_int msecs, int repeat, timer_f func, void *arg)
364
{
365
return (poll_start_utimer((unsigned long long)msecs * 1000,
366
repeat, func, arg));
367
}
368
369
int
370
poll_start_utimer(unsigned long long usecs, int repeat, timer_f func, void *arg)
371
{
372
PollTim_t *p;
373
374
/* find unused entry */
375
for(p = tims; p < &tims[tims_alloc]; p++)
376
if(p->func == NULL)
377
break;
378
379
if(p == &tims[tims_alloc]) {
380
if(tims_alloc == tims_used) {
381
size_t newsize = tims_alloc + POLL_REG_GROW;
382
tims = _xrealloc(tims, sizeof(tims[0]) * newsize);
383
for(p = &tims[tims_alloc]; p < &tims[newsize]; p++)
384
p->func = NULL;
385
p = &tims[tims_alloc];
386
tims_alloc = newsize;
387
}
388
}
389
390
/* create entry */
391
p->usecs = usecs;
392
p->repeat = repeat;
393
p->arg = arg;
394
p->func = func;
395
p->when = GETUSECS() + usecs;
396
397
tims_used++;
398
399
resort = 1;
400
401
if(rpoll_trace)
402
fprintf(stderr, "%s(%llu, %d, %p, %p)->%tu\n", __func__, usecs,
403
repeat, (void *)func, (void *)arg, p - tims);
404
405
return p - tims;
406
}
407
408
/*
409
* Here we have to look into the sorted table, whether any entry there points
410
* into the registration table for the deleted entry. This is needed,
411
* because a unregistration can occure while we are scanning through the
412
* table in dispatch(). Do this only, if we are really there - resorting
413
* will sort out things if we are called from outside the loop.
414
*/
415
void
416
poll_stop_timer(int handle)
417
{
418
u_int i;
419
420
if(rpoll_trace)
421
fprintf(stderr, "%s(%d)\n", __func__, handle);
422
423
tims[handle].func = NULL;
424
tims_used--;
425
426
resort = 1;
427
428
if(!in_dispatch)
429
return;
430
431
for(i = 0; i < tfd_used; i++)
432
if(tfd[i] == handle) {
433
tfd[i] = -1;
434
break;
435
}
436
}
437
438
/*
439
* Squeeze and sort timer table.
440
* Should perhaps use a custom sort.
441
*/
442
static int
443
tim_cmp(const void *p1, const void *p2)
444
{
445
int t1 = *(const int *)p1;
446
int t2 = *(const int *)p2;
447
448
return tims[t1].when < tims[t2].when ? -1
449
: tims[t1].when > tims[t2].when ? +1
450
: 0;
451
}
452
453
/*
454
* Reconstruct the tfd-array. This will be an sorted array of indexes
455
* to the used entries in tims. The next timer to expire will be infront
456
* of the array. tfd_used is the number of used entries. The array is
457
* re-allocated if needed.
458
*/
459
static void
460
sort_timers(void)
461
{
462
int *pp;
463
u_int i;
464
465
if(tims_used > tfd_alloc) {
466
tfd_alloc = tims_used;
467
tfd = _xrealloc(tfd, sizeof(int *) * tfd_alloc);
468
}
469
470
pp = tfd;
471
472
for(i = 0; i < tims_alloc; i++)
473
if(tims[i].func)
474
*pp++ = i;
475
assert(pp - tfd == (ptrdiff_t)tims_used);
476
477
tfd_used = tims_used;
478
if(tfd_used > 1)
479
qsort(tfd, tfd_used, sizeof(int), tim_cmp);
480
}
481
482
/*
483
* Poll the file descriptors and dispatch to the right function
484
* If wait is true the poll blocks until somewhat happens.
485
* Don't use a pointer here, because the called function may cause
486
* a reallocation! The check for pfd != NULL is required, because
487
* a sequence of unregister/register could make the wrong callback
488
* to be called. So we clear pfd in unregister and check here.
489
*/
490
void
491
poll_dispatch(int wait)
492
{
493
u_int i, idx;
494
int ret;
495
tval_t now;
496
tval_t tout;
497
static u_int last_index;
498
499
# ifdef USE_SELECT
500
fd_set nrset, nwset, nxset;
501
struct timeval tv;
502
# endif
503
504
in_dispatch = 1;
505
506
if(rebuild) {
507
rebuild = 0;
508
poll_build();
509
}
510
if(resort) {
511
resort = 0;
512
sort_timers();
513
}
514
515
/* in wait mode - compute the timeout */
516
if(wait) {
517
if(tfd_used) {
518
now = GETUSECS();
519
# ifdef DEBUG
520
{
521
fprintf(stderr, "now=%llu", now);
522
for(i = 0; i < tims_used; i++)
523
fprintf(stderr, "timers[%2d] = %lld",
524
i, tfd[i]->when - now);
525
}
526
# endif
527
if((tout = tims[tfd[0]].when - now) < 0)
528
tout = 0;
529
} else
530
tout = INFTIM;
531
} else
532
tout = 0;
533
534
# ifdef DEBUG
535
fprintf(stderr, "rpoll -- selecting with tout=%u", tout);
536
# endif
537
538
# ifdef USE_POLL
539
ret = poll(pfd, regs_used, tout == INFTIM ? INFTIM : (tout / 1000));
540
# endif
541
542
# ifdef USE_SELECT
543
nrset = rset;
544
nwset = wset;
545
nxset = xset;
546
if(tout != INFTIM) {
547
tv.tv_sec = tout / 1000000;
548
tv.tv_usec = tout % 1000000;
549
}
550
ret = select(maxfd+1,
551
SELECT_CAST(&nrset),
552
SELECT_CAST(&nwset),
553
SELECT_CAST(&nxset), (tout==INFTIM) ? NULL : &tv);
554
# endif
555
556
if(ret == -1) {
557
if(errno == EINTR)
558
return;
559
_panic("poll/select: %s", strerror(errno));
560
}
561
562
/* dispatch files */
563
if(ret > 0) {
564
for(i = 0; i < regs_alloc; i++) {
565
idx = rpoll_policy ? ((last_index+i) % regs_alloc) : i;
566
567
assert(idx < regs_alloc);
568
569
if(regs[idx].fd >= 0) {
570
int mask = 0;
571
572
# ifdef USE_POLL
573
if(regs[idx].pfd) {
574
if ((regs[idx].mask & RPOLL_IN) &&
575
(regs[idx].pfd->revents & poll_in))
576
mask |= RPOLL_IN;
577
if ((regs[idx].mask & RPOLL_OUT) &&
578
(regs[idx].pfd->revents & poll_out))
579
mask |= RPOLL_OUT;
580
if((regs[idx].mask & RPOLL_EXCEPT) &&
581
(regs[idx].pfd->revents & poll_except))
582
mask |= RPOLL_EXCEPT;
583
}
584
# endif
585
# ifdef USE_SELECT
586
if ((regs[idx].mask & RPOLL_IN) &&
587
FD_ISSET(regs[idx].fd, &nrset))
588
mask |= RPOLL_IN;
589
if ((regs[idx].mask & RPOLL_OUT) &&
590
FD_ISSET(regs[idx].fd, &nwset))
591
mask |= RPOLL_OUT;
592
if ((regs[idx].mask & RPOLL_EXCEPT) &&
593
FD_ISSET(regs[idx].fd, &nxset))
594
mask |= RPOLL_EXCEPT;
595
# endif
596
assert(idx < regs_alloc);
597
598
if(mask) {
599
if(rpoll_trace)
600
fprintf(stderr,
601
"%s() -- file %d/%d %x\n",
602
__func__, regs[idx].fd,
603
idx, mask);
604
(*regs[idx].func)(regs[idx].fd, mask, regs[idx].arg);
605
}
606
}
607
608
}
609
last_index++;
610
}
611
612
/* dispatch timeouts */
613
if(tfd_used) {
614
now = GETUSECS();
615
for(i = 0; i < tfd_used; i++) {
616
if(tfd[i] < 0)
617
continue;
618
if(tims[tfd[i]].when > now)
619
break;
620
if(rpoll_trace)
621
fprintf(stderr, "%s() -- timeout %d\n",
622
__func__, tfd[i]);
623
(*tims[tfd[i]].func)(tfd[i], tims[tfd[i]].arg);
624
if(tfd[i] < 0)
625
continue;
626
if(tims[tfd[i]].repeat)
627
tims[tfd[i]].when = now + tims[tfd[i]].usecs;
628
else {
629
tims[tfd[i]].func = NULL;
630
tims_used--;
631
tfd[i] = -1;
632
}
633
resort = 1;
634
}
635
}
636
in_dispatch = 0;
637
}
638
639
640
# ifdef TESTME
641
struct timeval start, now;
642
int t0, t1;
643
644
double elaps(void);
645
void infunc(int fd, int mask, void *arg);
646
647
double
648
elaps(void)
649
{
650
gettimeofday(&now, NULL);
651
652
return (double)(10 * now.tv_sec + now.tv_usec / 100000 -
653
10 * start.tv_sec - start.tv_usec / 100000) / 10;
654
}
655
656
void
657
infunc(int fd, int mask, void *arg)
658
{
659
char buf[1024];
660
int ret;
661
662
mask = mask;
663
arg = arg;
664
if((ret = read(fd, buf, sizeof(buf))) < 0)
665
_panic("read: %s", strerror(errno));
666
write(1, "stdin:", 6);
667
write(1, buf, ret);
668
}
669
670
void tfunc0(int tid, void *arg);
671
void tfunc1(int tid, void *arg);
672
673
void
674
tfunc0(int tid, void *arg)
675
{
676
printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
677
}
678
void
679
tfunc1(int tid, void *arg)
680
{
681
printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
682
}
683
void
684
tfunc2(int tid, void *arg)
685
{
686
static u_int count = 0;
687
688
if (++count % 10000 == 0)
689
printf("%4.1f -- %d\n", elaps(), tid);
690
}
691
692
void first(int tid, void *arg);
693
void second(int tid, void *arg);
694
695
void
696
second(int tid, void *arg)
697
{
698
printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
699
poll_start_utimer(5500000, 0, first, "first");
700
poll_stop_timer(t1);
701
t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
702
}
703
void
704
first(int tid, void *arg)
705
{
706
printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
707
poll_start_timer(3700, 0, second, "second");
708
poll_stop_timer(t0);
709
t1 = poll_start_timer(250, 1, tfunc1, "1/4 second");
710
}
711
712
int
713
main(int argc, char *argv[])
714
{
715
argv = argv;
716
gettimeofday(&start, NULL);
717
poll_register(0, infunc, NULL, RPOLL_IN);
718
719
if (argc < 2) {
720
t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
721
poll_start_timer(2500, 0, first, "first");
722
} else {
723
t0 = poll_start_utimer(300, 1, tfunc2, NULL);
724
}
725
726
while(1)
727
poll_dispatch(1);
728
729
return 0;
730
}
731
# endif
732
733