]> jfr.im git - irc/rqf/shadowircd.git/blob - libcharybdis/kqueue.c
[svn] - remove the rest of the MAX_CLIENTS stuff. change kqueue implementation accord...
[irc/rqf/shadowircd.git] / libcharybdis / kqueue.c
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * kqueue.c: FreeBSD kqueue compatible network routines.
4 *
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2001 Adrian Chadd <adrian@creative.net.au>
8 * Copyright (C) 2002-2005 ircd-ratbox development team
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 * USA
24 *
25 * $Id: kqueue.c 3356 2007-04-03 09:31:11Z nenolod $
26 */
27
28 #include "stdinc.h"
29 #include <sys/event.h>
30
31 #include "libcharybdis.h"
32
33 /* jlemon goofed up and didn't add EV_SET until fbsd 4.3 */
34
35 #ifndef EV_SET
36 #define EV_SET(kevp, a, b, c, d, e, f) do { \
37 (kevp)->ident = (a); \
38 (kevp)->filter = (b); \
39 (kevp)->flags = (c); \
40 (kevp)->fflags = (d); \
41 (kevp)->data = (e); \
42 (kevp)->udata = (f); \
43 } while(0)
44 #endif
45
46 static void kq_update_events(fde_t *, short, PF *);
47 static int kq;
48 static struct timespec zero_timespec;
49
50 static struct kevent *kqlst; /* kevent buffer */
51 static int kqmax; /* max structs to buffer */
52 static int kqoff; /* offset into the buffer */
53
54
55 /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
56 /* Private functions */
57
58 void
59 kq_update_events(fde_t * F, short filter, PF * handler)
60 {
61 PF *cur_handler;
62 int kep_flags;
63
64 switch (filter)
65 {
66 case EVFILT_READ:
67 cur_handler = F->read_handler;
68 break;
69 case EVFILT_WRITE:
70 cur_handler = F->write_handler;
71 break;
72 default:
73 /* XXX bad! -- adrian */
74 return;
75 break;
76 }
77
78 if((cur_handler == NULL && handler != NULL) || (cur_handler != NULL && handler == NULL))
79 {
80 struct kevent *kep;
81
82 kep = kqlst + kqoff;
83
84 if(handler != NULL)
85 {
86 if(filter == EVFILT_WRITE)
87 kep_flags = (EV_ADD | EV_ENABLE | EV_ONESHOT);
88 else
89 kep_flags = (EV_ADD | EV_ENABLE);
90 }
91 else
92 {
93 /* lets definately not poll stuff that isn't real --
94 * some kqueue implementations hate doing this... and
95 * it's intended to delete AND disable at the same time.
96 *
97 * don't believe me? read kevent(4). --nenolod
98 */
99 kep_flags = (EV_DELETE | EV_DISABLE);
100 }
101
102 EV_SET(kep, (uintptr_t) F->fd, filter, kep_flags, 0, 0, (void *) F);
103
104 if(++kqoff == kqmax)
105 {
106 int ret;
107
108 ret = kevent(kq, kqlst, kqoff, NULL, 0, &zero_timespec);
109 /* jdc -- someone needs to do error checking... */
110 if(ret == -1)
111 {
112 libcharybdis_log("kq_update_events(): kevent(): %s", strerror(errno));
113 return;
114 }
115 kqoff = 0;
116 }
117 }
118 }
119
120
121
122 /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
123 /* Public functions */
124
125
126 /*
127 * init_netio
128 *
129 * This is a needed exported function which will be called to initialise
130 * the network loop code.
131 */
132 void
133 init_netio(void)
134 {
135 kq = kqueue();
136 if(kq < 0)
137 {
138 libcharybdis_log("init_netio: Couldn't open kqueue fd!\n");
139 exit(115); /* Whee! */
140 }
141 kqmax = getdtablesize();
142 kqlst = MyMalloc(sizeof(struct kevent) * kqmax);
143 zero_timespec.tv_sec = 0;
144 zero_timespec.tv_nsec = 0;
145 }
146
147 /*
148 * comm_setselect
149 *
150 * This is a needed exported function which will be called to register
151 * and deregister interest in a pending IO state for a given FD.
152 */
153 void
154 comm_setselect(int fd, fdlist_t list, unsigned int type, PF * handler,
155 void *client_data, time_t timeout)
156 {
157 fde_t *F = comm_locate_fd(fd);
158 s_assert(fd >= 0);
159 s_assert(F->flags.open);
160
161 /* Update the list, even though we're not using it .. */
162 F->list = list;
163
164 if(type & COMM_SELECT_READ)
165 {
166 kq_update_events(F, EVFILT_READ, handler);
167 F->read_handler = handler;
168 F->read_data = client_data;
169 }
170 if(type & COMM_SELECT_WRITE)
171 {
172 kq_update_events(F, EVFILT_WRITE, handler);
173 F->write_handler = handler;
174 F->write_data = client_data;
175 }
176 if(timeout)
177 F->timeout = CurrentTime + (timeout / 1000);
178
179 }
180
181 /*
182 * Check all connections for new connections and input data that is to be
183 * processed. Also check for connections with data queued and whether we can
184 * write it out.
185 */
186
187 /*
188 * comm_select
189 *
190 * Called to do the new-style IO, courtesy of squid (like most of this
191 * new IO code). This routine handles the stuff we've hidden in
192 * comm_setselect and fd_table[] and calls callbacks for IO ready
193 * events.
194 */
195
196 int
197 comm_select(unsigned long delay)
198 {
199 int num, i;
200 static struct kevent *ke = NULL;
201 struct timespec poll_time;
202
203 /* allocate ke if it has not been allocated already */
204 if (ke == NULL)
205 ke = MyMalloc(sizeof(struct kevent) * comm_get_maxconnections());
206
207 /*
208 * remember we are doing NANOseconds here, not micro/milli. God knows
209 * why jlemon used a timespec, but hey, he wrote the interface, not I
210 * -- Adrian
211 */
212
213 poll_time.tv_sec = delay / 1000;
214
215 poll_time.tv_nsec = (delay % 1000) * 1000000;
216
217 for (;;)
218 {
219 num = kevent(kq, kqlst, kqoff, ke, KE_LENGTH, &poll_time);
220 kqoff = 0;
221
222 if(num >= 0)
223 break;
224
225 if(ignoreErrno(errno))
226 break;
227
228 set_time();
229
230 return COMM_ERROR;
231
232 /* NOTREACHED */
233 }
234
235 set_time();
236
237 if(num == 0)
238 return COMM_OK; /* No error.. */
239
240 for (i = 0; i < num; i++)
241 {
242 int fd = (int) ke[i].ident;
243 PF *hdl = NULL;
244 fde_t *F = comm_locate_fd(fd);
245
246 if(ke[i].flags & EV_ERROR)
247 {
248 errno = (int) ke[i].data;
249 /* XXX error == bad! -- adrian */
250 continue; /* XXX! */
251 }
252 if (F == NULL)
253 {
254 /* XXX this is because of our "queueing" of
255 * kqueue changes so we may get ones for fds
256 * we have already closed? -- jilles */
257 continue;
258 }
259
260 switch (ke[i].filter)
261 {
262
263 case EVFILT_READ:
264
265 if((hdl = F->read_handler) != NULL)
266 {
267 F->read_handler = NULL;
268 hdl(fd, F->read_data);
269 }
270
271 break;
272
273 case EVFILT_WRITE:
274
275 if((hdl = F->write_handler) != NULL)
276 {
277 F->write_handler = NULL;
278 hdl(fd, F->write_data);
279 }
280 break;
281
282 default:
283 /* Bad! -- adrian */
284 break;
285 }
286 }
287 return COMM_OK;
288 }
289