]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/unbound/util/mini_event.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / unbound / util / mini_event.c
1 /*
2  * mini_event.c - implementation of part of libevent api, portably.
3  *
4  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5  * 
6  * This software is open source.
7  * 
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 
12  * Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  * 
15  * Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  * 
19  * Neither the name of the NLNET LABS nor the names of its contributors may
20  * be used to endorse or promote products derived from this software without
21  * specific prior written permission.
22  * 
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  * 
35  */
36
37 /**
38  * \file
39  * fake libevent implementation. Less broad in functionality, and only
40  * supports select(2).
41  */
42
43 #include "config.h"
44 #ifdef HAVE_TIME_H
45 #include <time.h>
46 #endif
47 #include <sys/time.h>
48
49 #if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
50 #include <signal.h>
51 #include "util/mini_event.h"
52 #include "util/fptr_wlist.h"
53
54 /** compare events in tree, based on timevalue, ptr for uniqueness */
55 int mini_ev_cmp(const void* a, const void* b)
56 {
57         const struct event *e = (const struct event*)a;
58         const struct event *f = (const struct event*)b;
59         if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec)
60                 return -1;
61         if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec)
62                 return 1;
63         if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec)
64                 return -1;
65         if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec)
66                 return 1;
67         if(e < f)
68                 return -1;
69         if(e > f)
70                 return 1;
71         return 0;
72 }
73
74 /** set time */
75 static int
76 settime(struct event_base* base)
77 {
78         if(gettimeofday(base->time_tv, NULL) < 0) {
79                 return -1;
80         }
81 #ifndef S_SPLINT_S
82         *base->time_secs = (time_t)base->time_tv->tv_sec;
83 #endif
84         return 0;
85 }
86
87 /** create event base */
88 void *event_init(time_t* time_secs, struct timeval* time_tv)
89 {
90         struct event_base* base = (struct event_base*)malloc(
91                 sizeof(struct event_base));
92         if(!base)
93                 return NULL;
94         memset(base, 0, sizeof(*base));
95         base->time_secs = time_secs;
96         base->time_tv = time_tv;
97         if(settime(base) < 0) {
98                 event_base_free(base);
99                 return NULL;
100         }
101         base->times = rbtree_create(mini_ev_cmp);
102         if(!base->times) {
103                 event_base_free(base);
104                 return NULL;
105         }
106         base->capfd = MAX_FDS;
107 #ifdef FD_SETSIZE
108         if((int)FD_SETSIZE < base->capfd)
109                 base->capfd = (int)FD_SETSIZE;
110 #endif
111         base->fds = (struct event**)calloc((size_t)base->capfd, 
112                 sizeof(struct event*));
113         if(!base->fds) {
114                 event_base_free(base);
115                 return NULL;
116         }
117         base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*));
118         if(!base->signals) {
119                 event_base_free(base);
120                 return NULL;
121         }
122 #ifndef S_SPLINT_S
123         FD_ZERO(&base->reads);
124         FD_ZERO(&base->writes);
125 #endif
126         return base;
127 }
128
129 /** get version */
130 const char *event_get_version(void)
131 {
132         return "mini-event-"PACKAGE_VERSION;
133 }
134
135 /** get polling method, select */
136 const char *event_get_method(void)
137 {
138         return "select";
139 }
140
141 /** call timeouts handlers, and return how long to wait for next one or -1 */
142 static void handle_timeouts(struct event_base* base, struct timeval* now, 
143         struct timeval* wait)
144 {
145         struct event* p;
146 #ifndef S_SPLINT_S
147         wait->tv_sec = (time_t)-1;
148 #endif
149
150         while((rbnode_t*)(p = (struct event*)rbtree_first(base->times))
151                 !=RBTREE_NULL) {
152 #ifndef S_SPLINT_S
153                 if(p->ev_timeout.tv_sec > now->tv_sec ||
154                         (p->ev_timeout.tv_sec==now->tv_sec && 
155                         p->ev_timeout.tv_usec > now->tv_usec)) {
156                         /* there is a next larger timeout. wait for it */
157                         wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec;
158                         if(now->tv_usec > p->ev_timeout.tv_usec) {
159                                 wait->tv_sec--;
160                                 wait->tv_usec = 1000000 - (now->tv_usec -
161                                         p->ev_timeout.tv_usec);
162                         } else {
163                                 wait->tv_usec = p->ev_timeout.tv_usec 
164                                         - now->tv_usec;
165                         }
166                         return;
167                 }
168 #endif
169                 /* event times out, remove it */
170                 (void)rbtree_delete(base->times, p);
171                 p->ev_events &= ~EV_TIMEOUT;
172                 fptr_ok(fptr_whitelist_event(p->ev_callback));
173                 (*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg);
174         }
175 }
176
177 /** call select and callbacks for that */
178 static int handle_select(struct event_base* base, struct timeval* wait)
179 {
180         fd_set r, w;
181         int ret, i;
182
183 #ifndef S_SPLINT_S
184         if(wait->tv_sec==(time_t)-1)
185                 wait = NULL;
186 #endif
187         memmove(&r, &base->reads, sizeof(fd_set));
188         memmove(&w, &base->writes, sizeof(fd_set));
189         memmove(&base->ready, &base->content, sizeof(fd_set));
190
191         if((ret = select(base->maxfd+1, &r, &w, NULL, wait)) == -1) {
192                 ret = errno;
193                 if(settime(base) < 0)
194                         return -1;
195                 errno = ret;
196                 if(ret == EAGAIN || ret == EINTR)
197                         return 0;
198                 return -1;
199         }
200         if(settime(base) < 0)
201                 return -1;
202         
203         for(i=0; i<base->maxfd+1; i++) {
204                 short bits = 0;
205                 if(!base->fds[i] || !(FD_ISSET(i, &base->ready))) {
206                         continue;
207                 }
208                 if(FD_ISSET(i, &r)) {
209                         bits |= EV_READ;
210                         ret--;
211                 }
212                 if(FD_ISSET(i, &w)) {
213                         bits |= EV_WRITE;
214                         ret--;
215                 }
216                 bits &= base->fds[i]->ev_events;
217                 if(bits) {
218                         fptr_ok(fptr_whitelist_event(
219                                 base->fds[i]->ev_callback));
220                         (*base->fds[i]->ev_callback)(base->fds[i]->ev_fd, 
221                                 bits, base->fds[i]->ev_arg);
222                         if(ret==0)
223                                 break;
224                 }
225         }
226         return 0;
227 }
228
229 /** run select in a loop */
230 int event_base_dispatch(struct event_base* base)
231 {
232         struct timeval wait;
233         if(settime(base) < 0)
234                 return -1;
235         while(!base->need_to_exit)
236         {
237                 /* see if timeouts need handling */
238                 handle_timeouts(base, base->time_tv, &wait);
239                 if(base->need_to_exit)
240                         return 0;
241                 /* do select */
242                 if(handle_select(base, &wait) < 0) {
243                         if(base->need_to_exit)
244                                 return 0;
245                         return -1;
246                 }
247         }
248         return 0;
249 }
250
251 /** exit that loop */
252 int event_base_loopexit(struct event_base* base, 
253         struct timeval* ATTR_UNUSED(tv))
254 {
255         base->need_to_exit = 1;
256         return 0;
257 }
258
259 /* free event base, free events yourself */
260 void event_base_free(struct event_base* base)
261 {
262         if(!base)
263                 return;
264         if(base->times)
265                 free(base->times);
266         if(base->fds)
267                 free(base->fds);
268         if(base->signals)
269                 free(base->signals);
270         free(base);
271 }
272
273 /** set content of event */
274 void event_set(struct event* ev, int fd, short bits, 
275         void (*cb)(int, short, void *), void* arg)
276 {
277         ev->node.key = ev;
278         ev->ev_fd = fd;
279         ev->ev_events = bits;
280         ev->ev_callback = cb;
281         fptr_ok(fptr_whitelist_event(ev->ev_callback));
282         ev->ev_arg = arg;
283         ev->added = 0;
284 }
285
286 /* add event to a base */
287 int event_base_set(struct event_base* base, struct event* ev)
288 {
289         ev->ev_base = base;
290         ev->added = 0;
291         return 0;
292 }
293
294 /* add event to make it active, you may not change it with event_set anymore */
295 int event_add(struct event* ev, struct timeval* tv)
296 {
297         if(ev->added)
298                 event_del(ev);
299         if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
300                 return -1;
301         if( (ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
302                 ev->ev_base->fds[ev->ev_fd] = ev;
303                 if(ev->ev_events&EV_READ) {
304                         FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
305                 }
306                 if(ev->ev_events&EV_WRITE) {
307                         FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
308                 }
309                 FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->content);
310                 FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
311                 if(ev->ev_fd > ev->ev_base->maxfd)
312                         ev->ev_base->maxfd = ev->ev_fd;
313         }
314         if(tv && (ev->ev_events&EV_TIMEOUT)) {
315 #ifndef S_SPLINT_S
316                 struct timeval *now = ev->ev_base->time_tv;
317                 ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
318                 ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
319                 while(ev->ev_timeout.tv_usec > 1000000) {
320                         ev->ev_timeout.tv_usec -= 1000000;
321                         ev->ev_timeout.tv_sec++;
322                 }
323 #endif
324                 (void)rbtree_insert(ev->ev_base->times, &ev->node);
325         }
326         ev->added = 1;
327         return 0;
328 }
329
330 /* remove event, you may change it again */
331 int event_del(struct event* ev)
332 {
333         if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
334                 return -1;
335         if((ev->ev_events&EV_TIMEOUT))
336                 (void)rbtree_delete(ev->ev_base->times, &ev->node);
337         if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
338                 ev->ev_base->fds[ev->ev_fd] = NULL;
339                 FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
340                 FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
341                 FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
342                 FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->content);
343         }
344         ev->added = 0;
345         return 0;
346 }
347
348 /** which base gets to handle signals */
349 static struct event_base* signal_base = NULL;
350 /** signal handler */
351 static RETSIGTYPE sigh(int sig)
352 {
353         struct event* ev;
354         if(!signal_base || sig < 0 || sig >= MAX_SIG)
355                 return;
356         ev = signal_base->signals[sig];
357         if(!ev)
358                 return;
359         fptr_ok(fptr_whitelist_event(ev->ev_callback));
360         (*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
361 }
362
363 /** install signal handler */
364 int signal_add(struct event* ev, struct timeval* ATTR_UNUSED(tv))
365 {
366         if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
367                 return -1;
368         signal_base = ev->ev_base;
369         ev->ev_base->signals[ev->ev_fd] = ev;
370         ev->added = 1;
371         if(signal(ev->ev_fd, sigh) == SIG_ERR) {
372                 return -1;
373         }
374         return 0;
375 }
376
377 /** remove signal handler */
378 int signal_del(struct event* ev)
379 {
380         if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
381                 return -1;
382         ev->ev_base->signals[ev->ev_fd] = NULL;
383         ev->added = 0;
384         return 0;
385 }
386
387 #else /* USE_MINI_EVENT */
388 #ifndef USE_WINSOCK
389 int mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
390 {
391         return 0;
392 }
393 #endif /* not USE_WINSOCK */
394 #endif /* USE_MINI_EVENT */