]>
jfr.im git - irc/quakenet/snircd.git/blob - ircd/engine_kqueue.c
2 * IRC - Internet Relay Chat, ircd/engine_kqueue.c
3 * Copyright (C) 2001 Kevin L. Mitchell <klmitch@mit.edu>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 1, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * @brief FreeBSD kqueue()/kevent() event engine.
21 * @version $Id: engine_kqueue.c,v 1.8 2005/03/23 00:25:16 entrope Exp $
25 #include "ircd_events.h"
28 #include "ircd_alloc.h"
29 #include "ircd_features.h"
33 /* #include <assert.h> -- Now using assert in ircd_log.h */
36 #include <sys/types.h>
37 #include <sys/event.h>
38 #include <sys/socket.h>
43 #define KQUEUE_ERROR_THRESHOLD 20 /**< after 20 kqueue errors, restart */
44 #define ERROR_EXPIRE_TIME 3600 /**< expire errors after an hour */
46 /** Array of active Socket structures, indexed by file descriptor. */
47 static struct Socket
** sockList
;
48 /** Maximum file descriptor supported, plus one. */
49 static int kqueue_max
;
50 /** File descriptor for kqueue pseudo-file. */
53 /** Number of recent errors from kqueue. */
54 static int errors
= 0;
55 /** Periodic timer to forget errors. */
56 static struct Timer clear_error
;
58 /** Decrement the error count (once per hour).
59 * @param[in] ev Expired timer event (ignored).
62 error_clear(struct Event
* ev
)
64 if (!--errors
) /* remove timer when error count reaches 0 */
65 timer_del(ev_timer(ev
));
68 /** Initialize the kqueue engine.
69 * @param[in] max_sockets Maximum number of file descriptors to support.
70 * @return Non-zero on success, or zero on failure.
73 engine_init(int max_sockets
)
77 if ((kqueue_id
= kqueue()) < 0) { /* initialize... */
78 log_write(LS_SYSTEM
, L_WARNING
, 0,
79 "kqueue() engine cannot initialize: %m");
83 /* allocate necessary memory */
84 sockList
= (struct Socket
**) MyMalloc(sizeof(struct Socket
*) * max_sockets
);
86 /* initialize the data */
87 for (i
= 0; i
< max_sockets
; i
++)
90 kqueue_max
= max_sockets
; /* number of sockets allocated */
92 return 1; /* success! */
95 /** Add a signal to the event engine.
96 * @param[in] sig Signal to add to engine.
99 engine_signal(struct Signal
* sig
)
101 struct kevent sigevent
;
102 struct sigaction act
;
106 Debug((DEBUG_ENGINE
, "kqueue: Adding filter for signal %d [%p]",
107 sig_signal(sig
), sig
));
109 sigevent
.ident
= sig_signal(sig
); /* set up the kqueue event */
110 sigevent
.filter
= EVFILT_SIGNAL
; /* looking for signals... */
111 sigevent
.flags
= EV_ADD
| EV_ENABLE
; /* add and enable it */
114 sigevent
.udata
= sig
; /* store our user data */
116 if (kevent(kqueue_id
, &sigevent
, 1, 0, 0, 0) < 0) { /* add event */
117 log_write(LS_SYSTEM
, L_WARNING
, 0, "Unable to trap signal %d",
122 act
.sa_handler
= SIG_IGN
; /* ignore the signal */
124 sigemptyset(&act
.sa_mask
);
125 sigaction(sig_signal(sig
), &act
, 0);
128 /** Figure out what events go with a given state.
129 * @param[in] state %Socket state to consider.
130 * @param[in] events User-specified preferred event set.
131 * @return Actual set of preferred events.
134 state_to_events(enum SocketState state
, unsigned int events
)
137 case SS_CONNECTING
: /* connecting socket */
138 return SOCK_EVENT_WRITABLE
;
141 case SS_LISTENING
: /* listening socket */
142 case SS_NOTSOCK
: /* our signal socket--just in case */
143 return SOCK_EVENT_READABLE
;
146 case SS_CONNECTED
: case SS_DATAGRAM
: case SS_CONNECTDG
:
147 return events
; /* ordinary socket */
155 /** Activate kqueue filters as appropriate.
156 * @param[in] sock Socket structure to operate on.
157 * @param[in] clear Set of interest events to clear from socket.
158 * @param[in] set Set of interest events to set on socket.
161 set_or_clear(struct Socket
* sock
, unsigned int clear
, unsigned int set
)
164 struct kevent chglist
[2];
167 assert(-1 < s_fd(sock
));
169 if ((clear
^ set
) & SOCK_EVENT_READABLE
) { /* readable has changed */
170 chglist
[i
].ident
= s_fd(sock
); /* set up the change list */
171 chglist
[i
].filter
= EVFILT_READ
; /* readable filter */
172 chglist
[i
].flags
= EV_ADD
; /* adding it */
173 chglist
[i
].fflags
= 0;
175 chglist
[i
].udata
= 0; /* I love udata, but it can't really be used here */
177 if (set
& SOCK_EVENT_READABLE
) /* it's set */
178 chglist
[i
].flags
|= EV_ENABLE
;
180 chglist
[i
].flags
|= EV_DISABLE
;
182 i
++; /* advance to next element */
185 if ((clear
^ set
) & SOCK_EVENT_WRITABLE
) { /* writable has changed */
186 chglist
[i
].ident
= s_fd(sock
); /* set up the change list */
187 chglist
[i
].filter
= EVFILT_WRITE
; /* writable filter */
188 chglist
[i
].flags
= EV_ADD
; /* adding it */
189 chglist
[i
].fflags
= 0;
191 chglist
[i
].udata
= 0;
193 if (set
& SOCK_EVENT_WRITABLE
) /* it's set */
194 chglist
[i
].flags
|= EV_ENABLE
;
196 chglist
[i
].flags
|= EV_DISABLE
;
198 i
++; /* advance count... */
201 if (kevent(kqueue_id
, chglist
, i
, 0, 0, 0) < 0 && errno
!= EBADF
)
202 event_generate(ET_ERROR
, sock
, errno
); /* report error */
205 /** Add a socket to the event engine.
206 * @param[in] sock Socket to add to engine.
207 * @return Non-zero on success, or zero on error.
210 engine_add(struct Socket
* sock
)
213 assert(0 == sockList
[s_fd(sock
)]);
215 /* bounds-check... */
216 if (sock
->s_fd
>= kqueue_max
) {
217 log_write(LS_SYSTEM
, L_ERROR
, 0,
218 "Attempt to add socket %d (> %d) to event engine", s_fd(sock
),
223 sockList
[s_fd(sock
)] = sock
; /* add to list */
225 Debug((DEBUG_ENGINE
, "kqueue: Adding socket %d [%p], state %s, to engine",
226 s_fd(sock
), sock
, state_to_name(s_state(sock
))));
228 /* Add socket to queue */
229 set_or_clear(sock
, 0, state_to_events(s_state(sock
), s_events(sock
)));
231 return 1; /* success */
234 /** Handle state transition for a socket.
235 * @param[in] sock Socket changing state.
236 * @param[in] new_state New state for socket.
239 engine_state(struct Socket
* sock
, enum SocketState new_state
)
242 assert(sock
== sockList
[s_fd(sock
)]);
244 Debug((DEBUG_ENGINE
, "kqueue: Changing state for socket %p to %s", sock
,
245 state_to_name(new_state
)));
247 /* set the correct events */
249 state_to_events(s_state(sock
), s_events(sock
)), /* old state */
250 state_to_events(new_state
, s_events(sock
))); /* new state */
254 /** Handle change to preferred socket events.
255 * @param[in] sock Socket getting new interest list.
256 * @param[in] new_events New set of interesting events for socket.
259 engine_events(struct Socket
* sock
, unsigned int new_events
)
262 assert(sock
== sockList
[s_fd(sock
)]);
264 Debug((DEBUG_ENGINE
, "kqueue: Changing event mask for socket %p to [%s]",
265 sock
, sock_flags(new_events
)));
267 /* set the correct events */
269 state_to_events(s_state(sock
), s_events(sock
)), /* old events */
270 state_to_events(s_state(sock
), new_events
)); /* new events */
273 /** Remove a socket from the event engine.
274 * @param[in] sock Socket being destroyed.
277 engine_delete(struct Socket
* sock
)
279 struct kevent dellist
[2];
282 assert(sock
== sockList
[s_fd(sock
)]);
284 Debug((DEBUG_ENGINE
, "kqueue: Deleting socket %d [%p], state %s",
285 s_fd(sock
), sock
, state_to_name(s_state(sock
))));
287 dellist
[0].ident
= s_fd(sock
); /* set up the delete list */
288 dellist
[0].filter
= EVFILT_READ
; /* readable filter */
289 dellist
[0].flags
= EV_DELETE
; /* delete it */
290 dellist
[0].fflags
= 0;
292 dellist
[0].udata
= 0;
294 dellist
[1].ident
= s_fd(sock
);
295 dellist
[1].filter
= EVFILT_WRITE
; /* writable filter */
296 dellist
[1].flags
= EV_DELETE
; /* delete it */
297 dellist
[1].fflags
= 0;
299 dellist
[1].udata
= 0;
301 sockList
[s_fd(sock
)] = 0;
304 /** Run engine event loop.
305 * @param[in] gen Lists of generators of various types.
308 engine_loop(struct Generators
* gen
)
310 struct kevent
*events
;
313 struct timespec wait
;
319 if ((events_count
= feature_int(FEAT_POLLS_PER_LOOP
)) < 20)
321 events
= (struct kevent
*)MyMalloc(sizeof(struct kevent
) * events_count
);
324 if ((i
= feature_int(FEAT_POLLS_PER_LOOP
)) >= 20 && i
!= events_count
) {
325 events
= (struct kevent
*)MyRealloc(events
, sizeof(struct kevent
) * i
);
329 /* set up the sleep time */
330 wait
.tv_sec
= timer_next(gen
) ? (timer_next(gen
) - CurrentTime
) : -1;
333 Debug((DEBUG_INFO
, "kqueue: delay: %Tu (%Tu) %Tu", timer_next(gen
),
334 CurrentTime
, wait
.tv_sec
));
336 /* check for active events */
337 nevs
= kevent(kqueue_id
, 0, 0, events
, events_count
,
338 wait
.tv_sec
< 0 ? 0 : &wait
);
340 CurrentTime
= time(0); /* set current time... */
343 if (errno
!= EINTR
) { /* ignore kevent interrupts */
344 /* Log the kqueue error */
345 log_write(LS_SOCKET
, L_ERROR
, 0, "kevent() error: %m");
347 timer_add(timer_init(&clear_error
), error_clear
, 0, TT_PERIODIC
,
349 else if (errors
> KQUEUE_ERROR_THRESHOLD
) /* too many errors... */
350 server_restart("too many kevent errors");
352 /* old code did a sleep(1) here; with usage these days,
353 * that may be too expensive
358 for (i
= 0; i
< nevs
; i
++) {
359 if (events
[i
].filter
== EVFILT_SIGNAL
) {
360 /* it's a signal; deal appropriately */
361 event_generate(ET_SIGNAL
, events
[i
].udata
, events
[i
].ident
);
362 continue; /* skip socket processing loop */
365 assert(events
[i
].filter
== EVFILT_READ
||
366 events
[i
].filter
== EVFILT_WRITE
);
368 sock
= sockList
[events
[i
].ident
];
369 if (!sock
) /* slots may become empty while processing events */
372 assert(s_fd(sock
) == events
[i
].ident
);
374 gen_ref_inc(sock
); /* can't have it going away on us */
376 Debug((DEBUG_ENGINE
, "kqueue: Checking socket %p (fd %d) state %s, "
377 "events %s", sock
, s_fd(sock
), state_to_name(s_state(sock
)),
378 sock_flags(s_events(sock
))));
380 if (s_state(sock
) != SS_NOTSOCK
) {
381 errcode
= 0; /* check for errors on socket */
382 codesize
= sizeof(errcode
);
383 if (getsockopt(s_fd(sock
), SOL_SOCKET
, SO_ERROR
, &errcode
,
385 errcode
= errno
; /* work around Solaris implementation */
387 if (errcode
) { /* an error occurred; generate an event */
388 Debug((DEBUG_ENGINE
, "kqueue: Error %d on fd %d, socket %p", errcode
,
390 event_generate(ET_ERROR
, sock
, errcode
);
391 gen_ref_dec(sock
); /* careful not to leak reference counts */
396 switch (s_state(sock
)) {
398 if (events
[i
].filter
== EVFILT_WRITE
) { /* connection completed */
399 Debug((DEBUG_ENGINE
, "kqueue: Connection completed"));
400 event_generate(ET_CONNECT
, sock
, 0);
405 if (events
[i
].filter
== EVFILT_READ
) { /* connect. to be accept. */
406 Debug((DEBUG_ENGINE
, "kqueue: Ready for accept"));
407 event_generate(ET_ACCEPT
, sock
, 0);
411 case SS_NOTSOCK
: /* doing nothing socket-specific */
413 if (events
[i
].filter
== EVFILT_READ
) { /* data on socket */
414 Debug((DEBUG_ENGINE
, "kqueue: EOF or data to be read"));
415 event_generate(events
[i
].flags
& EV_EOF
? ET_EOF
: ET_READ
, sock
, 0);
417 if (events
[i
].filter
== EVFILT_WRITE
) { /* socket writable */
418 Debug((DEBUG_ENGINE
, "kqueue: Data can be written"));
419 event_generate(ET_WRITE
, sock
, 0);
423 case SS_DATAGRAM
: case SS_CONNECTDG
:
424 if (events
[i
].filter
== EVFILT_READ
) { /* socket readable */
425 Debug((DEBUG_ENGINE
, "kqueue: Datagram to be read"));
426 event_generate(ET_READ
, sock
, 0);
428 if (events
[i
].filter
== EVFILT_WRITE
) { /* socket writable */
429 Debug((DEBUG_ENGINE
, "kqueue: Datagram can be written"));
430 event_generate(ET_WRITE
, sock
, 0);
435 gen_ref_dec(sock
); /* we're done with it */
438 timer_run(); /* execute any pending timers */
442 /** Descriptor for kqueue() event engine. */
443 struct Engine engine_kqueue
= {
444 "kqueue()", /* Engine name */
445 engine_init
, /* Engine initialization function */
446 engine_signal
, /* Engine signal registration function */
447 engine_add
, /* Engine socket registration function */
448 engine_state
, /* Engine socket state change function */
449 engine_events
, /* Engine socket events mask function */
450 engine_delete
, /* Engine socket deletion function */
451 engine_loop
/* Core engine event loop */