winsock_event.c revision 356345
1/*
2 * util/winsock_event.c - implementation of the unbound winsock event handler.
3 *
4 * Copyright (c) 2008, 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 * \file
37 * Implementation of the unbound WinSock2 API event notification handler
38 * for the Windows port.
39 */
40
41#include "config.h"
42#ifdef USE_WINSOCK
43#include <signal.h>
44#ifdef HAVE_TIME_H
45#include <time.h>
46#endif
47#include <sys/time.h>
48#include "util/winsock_event.h"
49#include "util/fptr_wlist.h"
50
51int mini_ev_cmp(const void* a, const void* b)
52{
53        const struct event *e = (const struct event*)a;
54        const struct event *f = (const struct event*)b;
55        if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec)
56                return -1;
57        if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec)
58                return 1;
59        if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec)
60                return -1;
61        if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec)
62                return 1;
63        if(e < f)
64                return -1;
65        if(e > f)
66                return 1;
67	return 0;
68}
69
70/** set time */
71static int
72settime(struct event_base* base)
73{
74        if(gettimeofday(base->time_tv, NULL) < 0) {
75                return -1;
76        }
77#ifndef S_SPLINT_S
78        *base->time_secs = (time_t)base->time_tv->tv_sec;
79#endif
80        return 0;
81}
82
83#ifdef UNBOUND_DEBUG
84/**
85 * Find a fd in the list of items.
86 * Note that not all items have a fd associated (those are -1).
87 * Signals are stored separately, and not searched.
88 * @param base: event base to look in.
89 * @param fd: what socket to look for.
90 * @return the index in the array, or -1 on failure.
91 */
92static int
93find_fd(struct event_base* base, int fd)
94{
95	int i;
96	for(i=0; i<base->max; i++) {
97		if(base->items[i]->ev_fd == fd)
98			return i;
99	}
100	return -1;
101}
102#endif
103
104/** Find ptr in base array */
105static void
106zero_waitfor(WSAEVENT waitfor[], WSAEVENT x)
107{
108	int i;
109	for(i=0; i<WSK_MAX_ITEMS; i++) {
110		if(waitfor[i] == x)
111			waitfor[i] = 0;
112	}
113}
114
115void *event_init(time_t* time_secs, struct timeval* time_tv)
116{
117        struct event_base* base = (struct event_base*)malloc(
118		sizeof(struct event_base));
119        if(!base)
120                return NULL;
121        memset(base, 0, sizeof(*base));
122        base->time_secs = time_secs;
123        base->time_tv = time_tv;
124        if(settime(base) < 0) {
125                event_base_free(base);
126                return NULL;
127        }
128	base->items = (struct event**)calloc(WSK_MAX_ITEMS,
129		sizeof(struct event*));
130	if(!base->items) {
131                event_base_free(base);
132                return NULL;
133	}
134	base->cap = WSK_MAX_ITEMS;
135	base->max = 0;
136        base->times = rbtree_create(mini_ev_cmp);
137        if(!base->times) {
138                event_base_free(base);
139                return NULL;
140        }
141        base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*));
142        if(!base->signals) {
143                event_base_free(base);
144                return NULL;
145        }
146	base->tcp_stickies = 0;
147	base->tcp_reinvigorated = 0;
148	verbose(VERB_CLIENT, "winsock_event inited");
149        return base;
150}
151
152const char *event_get_version(void)
153{
154	return "winsock-event-"PACKAGE_VERSION;
155}
156
157const char *event_get_method(void)
158{
159	return "WSAWaitForMultipleEvents";
160}
161
162/** call timeouts handlers, and return how long to wait for next one or -1 */
163static void handle_timeouts(struct event_base* base, struct timeval* now,
164        struct timeval* wait)
165{
166        struct event* p;
167#ifndef S_SPLINT_S
168        wait->tv_sec = (time_t)-1;
169#endif
170	verbose(VERB_CLIENT, "winsock_event handle_timeouts");
171
172        while((rbnode_type*)(p = (struct event*)rbtree_first(base->times))
173                !=RBTREE_NULL) {
174#ifndef S_SPLINT_S
175                if(p->ev_timeout.tv_sec > now->tv_sec ||
176                        (p->ev_timeout.tv_sec==now->tv_sec &&
177                        p->ev_timeout.tv_usec > now->tv_usec)) {
178                        /* there is a next larger timeout. wait for it */
179                        wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec;
180                        if(now->tv_usec > p->ev_timeout.tv_usec) {
181                                wait->tv_sec--;
182                                wait->tv_usec = 1000000 - (now->tv_usec -
183                                        p->ev_timeout.tv_usec);
184                        } else {
185                                wait->tv_usec = p->ev_timeout.tv_usec
186                                        - now->tv_usec;
187                        }
188			verbose(VERB_CLIENT, "winsock_event wait=" ARG_LL "d.%6.6d",
189				(long long)wait->tv_sec, (int)wait->tv_usec);
190                        return;
191                }
192#endif
193                /* event times out, remove it */
194                (void)rbtree_delete(base->times, p);
195                p->ev_events &= ~EV_TIMEOUT;
196                fptr_ok(fptr_whitelist_event(p->ev_callback));
197                (*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg);
198        }
199	verbose(VERB_CLIENT, "winsock_event wait=(-1)");
200}
201
202/** handle is_signal events and see if signalled */
203static void handle_signal(struct event* ev)
204{
205	DWORD ret;
206	log_assert(ev->is_signal && ev->hEvent);
207	/* see if the event is signalled */
208	ret = WSAWaitForMultipleEvents(1, &ev->hEvent, 0 /* any object */,
209		0 /* return immediately */, 0 /* not alertable for IOcomple*/);
210	if(ret == WSA_WAIT_IO_COMPLETION || ret == WSA_WAIT_FAILED) {
211		log_err("WSAWaitForMultipleEvents(signal) failed: %s",
212			wsa_strerror(WSAGetLastError()));
213		return;
214	}
215	if(ret == WSA_WAIT_TIMEOUT) {
216		/* not signalled */
217		return;
218	}
219
220	/* reset the signal */
221	if(!WSAResetEvent(ev->hEvent))
222		log_err("WSAResetEvent failed: %s",
223			wsa_strerror(WSAGetLastError()));
224	/* do the callback (which may set the signal again) */
225	fptr_ok(fptr_whitelist_event(ev->ev_callback));
226	(*ev->ev_callback)(ev->ev_fd, ev->ev_events, ev->ev_arg);
227}
228
229/** call select and callbacks for that */
230static int handle_select(struct event_base* base, struct timeval* wait)
231{
232	DWORD timeout = 0; /* in milliseconds */
233	DWORD ret;
234	struct event* eventlist[WSK_MAX_ITEMS];
235	WSANETWORKEVENTS netev;
236	int i, numwait = 0, startidx = 0, was_timeout = 0;
237	int newstickies = 0;
238	struct timeval nultm;
239
240	verbose(VERB_CLIENT, "winsock_event handle_select");
241
242#ifndef S_SPLINT_S
243        if(wait->tv_sec==(time_t)-1)
244                wait = NULL;
245	if(wait)
246		timeout = wait->tv_sec*1000 + wait->tv_usec/1000;
247	if(base->tcp_stickies) {
248		wait = &nultm;
249		nultm.tv_sec = 0;
250		nultm.tv_usec = 0;
251		timeout = 0; /* no waiting, we have sticky events */
252	}
253#endif
254
255	/* prepare event array */
256	for(i=0; i<base->max; i++) {
257		if(base->items[i]->ev_fd == -1 && !base->items[i]->is_signal)
258			continue; /* skip timer only events */
259		eventlist[numwait] = base->items[i];
260		base->waitfor[numwait++] = base->items[i]->hEvent;
261		if(numwait == WSK_MAX_ITEMS)
262			break; /* sanity check */
263	}
264	log_assert(numwait <= WSA_MAXIMUM_WAIT_EVENTS);
265	verbose(VERB_CLIENT, "winsock_event bmax=%d numwait=%d wait=%s "
266		"timeout=%d", base->max, numwait, (wait?"<wait>":"<null>"),
267		(int)timeout);
268
269	/* do the wait */
270	if(numwait == 0) {
271		/* WSAWaitFor.. doesn't like 0 event objects */
272		if(wait) {
273			Sleep(timeout);
274		}
275		was_timeout = 1;
276	} else {
277		ret = WSAWaitForMultipleEvents(numwait, base->waitfor,
278			0 /* do not wait for all, just one will do */,
279			wait?timeout:WSA_INFINITE,
280			0); /* we are not alertable (IO completion events) */
281		if(ret == WSA_WAIT_IO_COMPLETION) {
282			log_err("WSAWaitForMultipleEvents failed: WSA_WAIT_IO_COMPLETION");
283			return -1;
284		} else if(ret == WSA_WAIT_FAILED) {
285			log_err("WSAWaitForMultipleEvents failed: %s",
286				wsa_strerror(WSAGetLastError()));
287			return -1;
288		} else if(ret == WSA_WAIT_TIMEOUT) {
289			was_timeout = 1;
290		} else
291			startidx = ret - WSA_WAIT_EVENT_0;
292	}
293	verbose(VERB_CLIENT, "winsock_event wake was_timeout=%d startidx=%d",
294		was_timeout, startidx);
295
296	/* get new time after wait */
297        if(settime(base) < 0)
298               return -1;
299
300	/* callbacks */
301	if(base->tcp_stickies)
302		startidx = 0; /* process all events, some are sticky */
303	for(i=startidx; i<numwait; i++)
304		eventlist[i]->just_checked = 1;
305
306	verbose(VERB_CLIENT, "winsock_event signals");
307	for(i=startidx; i<numwait; i++) {
308		if(!base->waitfor[i])
309			continue; /* was deleted */
310		if(eventlist[i]->is_signal) {
311			eventlist[i]->just_checked = 0;
312			handle_signal(eventlist[i]);
313		}
314	}
315	/* early exit - do not process network, exit quickly */
316	if(base->need_to_exit)
317		return 0;
318
319	verbose(VERB_CLIENT, "winsock_event net");
320	for(i=startidx; i<numwait; i++) {
321		short bits = 0;
322		/* eventlist[i] fired */
323		/* see if eventlist[i] is still valid and just checked from
324		 * WSAWaitForEvents */
325		if(!base->waitfor[i])
326			continue; /* was deleted */
327		if(!eventlist[i]->just_checked)
328			continue; /* added by other callback */
329		if(eventlist[i]->is_signal)
330			continue; /* not a network event at all */
331		eventlist[i]->just_checked = 0;
332
333		if(WSAEnumNetworkEvents(eventlist[i]->ev_fd,
334			base->waitfor[i], /* reset the event handle */
335			/*NULL,*/ /* do not reset the event handle */
336			&netev) != 0) {
337			log_err("WSAEnumNetworkEvents failed: %s",
338				wsa_strerror(WSAGetLastError()));
339			return -1;
340		}
341		if((netev.lNetworkEvents & FD_READ)) {
342			if(netev.iErrorCode[FD_READ_BIT] != 0)
343				verbose(VERB_ALGO, "FD_READ_BIT error: %s",
344				wsa_strerror(netev.iErrorCode[FD_READ_BIT]));
345			bits |= EV_READ;
346		}
347		if((netev.lNetworkEvents & FD_WRITE)) {
348			if(netev.iErrorCode[FD_WRITE_BIT] != 0)
349				verbose(VERB_ALGO, "FD_WRITE_BIT error: %s",
350				wsa_strerror(netev.iErrorCode[FD_WRITE_BIT]));
351			bits |= EV_WRITE;
352		}
353		if((netev.lNetworkEvents & FD_CONNECT)) {
354			if(netev.iErrorCode[FD_CONNECT_BIT] != 0)
355				verbose(VERB_ALGO, "FD_CONNECT_BIT error: %s",
356				wsa_strerror(netev.iErrorCode[FD_CONNECT_BIT]));
357			bits |= EV_READ;
358			bits |= EV_WRITE;
359		}
360		if((netev.lNetworkEvents & FD_ACCEPT)) {
361			if(netev.iErrorCode[FD_ACCEPT_BIT] != 0)
362				verbose(VERB_ALGO, "FD_ACCEPT_BIT error: %s",
363				wsa_strerror(netev.iErrorCode[FD_ACCEPT_BIT]));
364			bits |= EV_READ;
365		}
366		if((netev.lNetworkEvents & FD_CLOSE)) {
367			if(netev.iErrorCode[FD_CLOSE_BIT] != 0)
368				verbose(VERB_ALGO, "FD_CLOSE_BIT error: %s",
369				wsa_strerror(netev.iErrorCode[FD_CLOSE_BIT]));
370			bits |= EV_READ;
371			bits |= EV_WRITE;
372		}
373		if(eventlist[i]->is_tcp && eventlist[i]->stick_events) {
374			verbose(VERB_ALGO, "winsock %d pass sticky %s%s",
375				eventlist[i]->ev_fd,
376				(eventlist[i]->old_events&EV_READ)?"EV_READ":"",
377				(eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":"");
378			bits |= eventlist[i]->old_events;
379		}
380		if(eventlist[i]->is_tcp && bits) {
381			eventlist[i]->old_events = bits;
382			eventlist[i]->stick_events = 1;
383			if((eventlist[i]->ev_events & bits)) {
384				newstickies = 1;
385			}
386			verbose(VERB_ALGO, "winsock %d store sticky %s%s",
387				eventlist[i]->ev_fd,
388				(eventlist[i]->old_events&EV_READ)?"EV_READ":"",
389				(eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":"");
390		}
391		if((bits & eventlist[i]->ev_events)) {
392			verbose(VERB_ALGO, "winsock event callback %p fd=%d "
393				"%s%s%s%s%s ; %s%s%s",
394				eventlist[i], eventlist[i]->ev_fd,
395				(netev.lNetworkEvents&FD_READ)?" FD_READ":"",
396				(netev.lNetworkEvents&FD_WRITE)?" FD_WRITE":"",
397				(netev.lNetworkEvents&FD_CONNECT)?
398					" FD_CONNECT":"",
399				(netev.lNetworkEvents&FD_ACCEPT)?
400					" FD_ACCEPT":"",
401				(netev.lNetworkEvents&FD_CLOSE)?" FD_CLOSE":"",
402				(bits&EV_READ)?" EV_READ":"",
403				(bits&EV_WRITE)?" EV_WRITE":"",
404				(bits&EV_TIMEOUT)?" EV_TIMEOUT":"");
405
406                        fptr_ok(fptr_whitelist_event(
407                                eventlist[i]->ev_callback));
408                        (*eventlist[i]->ev_callback)(eventlist[i]->ev_fd,
409                                bits & eventlist[i]->ev_events,
410				eventlist[i]->ev_arg);
411		}
412		if(eventlist[i]->is_tcp && bits)
413			verbose(VERB_ALGO, "winsock %d got sticky %s%s",
414				eventlist[i]->ev_fd,
415				(eventlist[i]->old_events&EV_READ)?"EV_READ":"",
416				(eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":"");
417	}
418	verbose(VERB_CLIENT, "winsock_event net");
419	if(base->tcp_reinvigorated) {
420		verbose(VERB_CLIENT, "winsock_event reinvigorated");
421		base->tcp_reinvigorated = 0;
422		newstickies = 1;
423	}
424	base->tcp_stickies = newstickies;
425	verbose(VERB_CLIENT, "winsock_event handle_select end");
426        return 0;
427}
428
429int event_base_dispatch(struct event_base *base)
430{
431        struct timeval wait;
432        if(settime(base) < 0)
433                return -1;
434        while(!base->need_to_exit)
435        {
436                /* see if timeouts need handling */
437                handle_timeouts(base, base->time_tv, &wait);
438                if(base->need_to_exit)
439                        return 0;
440                /* do select */
441                if(handle_select(base, &wait) < 0) {
442                        if(base->need_to_exit)
443                                return 0;
444                        return -1;
445                }
446        }
447        return 0;
448}
449
450int event_base_loopexit(struct event_base *base,
451	struct timeval * ATTR_UNUSED(tv))
452{
453	verbose(VERB_CLIENT, "winsock_event loopexit");
454        base->need_to_exit = 1;
455        return 0;
456}
457
458void event_base_free(struct event_base *base)
459{
460	verbose(VERB_CLIENT, "winsock_event event_base_free");
461        if(!base)
462                return;
463	free(base->items);
464        free(base->times);
465        free(base->signals);
466        free(base);
467}
468
469void event_set(struct event *ev, int fd, short bits,
470	void (*cb)(int, short, void *), void *arg)
471{
472        ev->node.key = ev;
473        ev->ev_fd = fd;
474        ev->ev_events = bits;
475        ev->ev_callback = cb;
476        fptr_ok(fptr_whitelist_event(ev->ev_callback));
477        ev->ev_arg = arg;
478	ev->just_checked = 0;
479        ev->added = 0;
480}
481
482int event_base_set(struct event_base *base, struct event *ev)
483{
484        ev->ev_base = base;
485	ev->old_events = 0;
486	ev->stick_events = 0;
487        ev->added = 0;
488        return 0;
489}
490
491int event_add(struct event *ev, struct timeval *tv)
492{
493	verbose(VERB_ALGO, "event_add %p added=%d fd=%d tv=" ARG_LL "d %s%s%s",
494		ev, ev->added, ev->ev_fd,
495		(tv?(long long)tv->tv_sec*1000+(long long)tv->tv_usec/1000:-1),
496		(ev->ev_events&EV_READ)?" EV_READ":"",
497		(ev->ev_events&EV_WRITE)?" EV_WRITE":"",
498		(ev->ev_events&EV_TIMEOUT)?" EV_TIMEOUT":"");
499        if(ev->added)
500                event_del(ev);
501	log_assert(ev->ev_fd==-1 || find_fd(ev->ev_base, ev->ev_fd) == -1);
502	ev->is_tcp = 0;
503	ev->is_signal = 0;
504	ev->just_checked = 0;
505
506        if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
507		BOOL b=0;
508		int t, l;
509		long events = 0;
510
511		if(ev->ev_base->max == ev->ev_base->cap)
512			return -1;
513		ev->idx = ev->ev_base->max++;
514		ev->ev_base->items[ev->idx] = ev;
515
516		if( (ev->ev_events&EV_READ) )
517			events |= FD_READ;
518		if( (ev->ev_events&EV_WRITE) )
519			events |= FD_WRITE;
520		l = sizeof(t);
521		if(getsockopt(ev->ev_fd, SOL_SOCKET, SO_TYPE,
522			(void*)&t, &l) != 0)
523			log_err("getsockopt(SO_TYPE) failed: %s",
524				wsa_strerror(WSAGetLastError()));
525		if(t == SOCK_STREAM) {
526			/* TCP socket */
527			ev->is_tcp = 1;
528			events |= FD_CLOSE;
529			if( (ev->ev_events&EV_WRITE) )
530				events |= FD_CONNECT;
531			l = sizeof(b);
532			if(getsockopt(ev->ev_fd, SOL_SOCKET, SO_ACCEPTCONN,
533				(void*)&b, &l) != 0)
534				log_err("getsockopt(SO_ACCEPTCONN) failed: %s",
535					wsa_strerror(WSAGetLastError()));
536			if(b) /* TCP accept socket */
537				events |= FD_ACCEPT;
538		}
539		ev->hEvent = WSACreateEvent();
540		if(ev->hEvent == WSA_INVALID_EVENT)
541			log_err("WSACreateEvent failed: %s",
542				wsa_strerror(WSAGetLastError()));
543		/* automatically sets fd to nonblocking mode.
544		 * nonblocking cannot be disabled, until wsaES(fd, NULL, 0) */
545		if(WSAEventSelect(ev->ev_fd, ev->hEvent, events) != 0) {
546			log_err("WSAEventSelect failed: %s",
547				wsa_strerror(WSAGetLastError()));
548		}
549		if(ev->is_tcp && ev->stick_events &&
550			(ev->ev_events & ev->old_events)) {
551			/* go to processing the sticky event right away */
552			ev->ev_base->tcp_reinvigorated = 1;
553		}
554	}
555
556	if(tv && (ev->ev_events&EV_TIMEOUT)) {
557#ifndef S_SPLINT_S
558                struct timeval *now = ev->ev_base->time_tv;
559                ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
560                ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
561                while(ev->ev_timeout.tv_usec >= 1000000) {
562                        ev->ev_timeout.tv_usec -= 1000000;
563                        ev->ev_timeout.tv_sec++;
564                }
565#endif
566                (void)rbtree_insert(ev->ev_base->times, &ev->node);
567        }
568        ev->added = 1;
569	return 0;
570}
571
572int event_del(struct event *ev)
573{
574	verbose(VERB_ALGO, "event_del %p added=%d fd=%d tv=" ARG_LL "d %s%s%s",
575		ev, ev->added, ev->ev_fd,
576		(ev->ev_events&EV_TIMEOUT)?(long long)ev->ev_timeout.tv_sec*1000+
577		(long long)ev->ev_timeout.tv_usec/1000:-1,
578		(ev->ev_events&EV_READ)?" EV_READ":"",
579		(ev->ev_events&EV_WRITE)?" EV_WRITE":"",
580		(ev->ev_events&EV_TIMEOUT)?" EV_TIMEOUT":"");
581	if(!ev->added)
582		return 0;
583	log_assert(ev->added);
584        if((ev->ev_events&EV_TIMEOUT))
585                (void)rbtree_delete(ev->ev_base->times, &ev->node);
586        if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
587		log_assert(ev->ev_base->max > 0);
588		/* remove item and compact the list */
589		ev->ev_base->items[ev->idx] =
590			ev->ev_base->items[ev->ev_base->max-1];
591		ev->ev_base->items[ev->ev_base->max-1] = NULL;
592		ev->ev_base->max--;
593		if(ev->idx < ev->ev_base->max)
594			ev->ev_base->items[ev->idx]->idx = ev->idx;
595		zero_waitfor(ev->ev_base->waitfor, ev->hEvent);
596
597		if(WSAEventSelect(ev->ev_fd, ev->hEvent, 0) != 0)
598			log_err("WSAEventSelect(disable) failed: %s",
599				wsa_strerror(WSAGetLastError()));
600		if(!WSACloseEvent(ev->hEvent))
601			log_err("WSACloseEvent failed: %s",
602				wsa_strerror(WSAGetLastError()));
603	}
604	ev->just_checked = 0;
605        ev->added = 0;
606        return 0;
607}
608
609/** which base gets to handle signals */
610static struct event_base* signal_base = NULL;
611/** signal handler */
612static RETSIGTYPE sigh(int sig)
613{
614        struct event* ev;
615        if(!signal_base || sig < 0 || sig >= MAX_SIG)
616                return;
617        ev = signal_base->signals[sig];
618        if(!ev)
619                return;
620        fptr_ok(fptr_whitelist_event(ev->ev_callback));
621        (*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
622}
623
624int signal_add(struct event *ev, struct timeval * ATTR_UNUSED(tv))
625{
626        if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
627                return -1;
628        signal_base = ev->ev_base;
629        ev->ev_base->signals[ev->ev_fd] = ev;
630        ev->added = 1;
631        if(signal(ev->ev_fd, sigh) == SIG_ERR) {
632                return -1;
633        }
634        return 0;
635}
636
637int signal_del(struct event *ev)
638{
639        if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
640                return -1;
641        ev->ev_base->signals[ev->ev_fd] = NULL;
642        ev->added = 0;
643        return 0;
644}
645
646void winsock_tcp_wouldblock(struct event* ev, int eventbits)
647{
648	verbose(VERB_ALGO, "winsock: tcp wouldblock %s",
649		eventbits==EV_READ?"EV_READ":"EV_WRITE");
650	ev->old_events &= (~eventbits);
651	if(ev->old_events == 0)
652		ev->stick_events = 0;
653		/* in case this is the last sticky event, we could
654		 * possibly run an empty handler loop to reset the base
655		 * tcp_stickies variable
656		 */
657}
658
659int winsock_register_wsaevent(struct event_base* base, struct event* ev,
660	WSAEVENT wsaevent, void (*cb)(int, short, void*), void* arg)
661{
662	if(base->max == base->cap)
663		return 0;
664	memset(ev, 0, sizeof(*ev));
665	ev->ev_fd = -1;
666	ev->ev_events = EV_READ;
667	ev->ev_callback = cb;
668	ev->ev_arg = arg;
669	ev->is_signal = 1;
670	ev->hEvent = wsaevent;
671	ev->added = 1;
672	ev->ev_base = base;
673	ev->idx = ev->ev_base->max++;
674	ev->ev_base->items[ev->idx] = ev;
675	return 1;
676}
677
678void winsock_unregister_wsaevent(struct event* ev)
679{
680	if(!ev || !ev->added) return;
681	log_assert(ev->added && ev->ev_base->max > 0)
682	/* remove item and compact the list */
683	ev->ev_base->items[ev->idx] = ev->ev_base->items[ev->ev_base->max-1];
684	ev->ev_base->items[ev->ev_base->max-1] = NULL;
685	ev->ev_base->max--;
686	if(ev->idx < ev->ev_base->max)
687		ev->ev_base->items[ev->idx]->idx = ev->idx;
688	ev->added = 0;
689}
690
691#else /* USE_WINSOCK */
692/** symbol so this codefile defines symbols. pleasing ranlib on OSX 10.5 */
693int winsock_unused_symbol = 1;
694#endif /* USE_WINSOCK */
695