]> jfr.im git - irc/rqf/shadowircd.git/blob - libcharybdis/poll.c
dlink -> rb_dlink
[irc/rqf/shadowircd.git] / libcharybdis / poll.c
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * s_bsd_poll.c: POSIX poll() 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: poll.c 3528 2007-07-07 08:08:23Z nenolod $
26 */
27
28 #include "config.h"
29 #include "stdinc.h"
30 #include <sys/poll.h>
31
32 #include "libcharybdis.h"
33
34 /* I hate linux -- adrian */
35 #ifndef POLLRDNORM
36 #define POLLRDNORM POLLIN
37 #endif
38 #ifndef POLLWRNORM
39 #define POLLWRNORM POLLOUT
40 #endif
41
42 struct _pollfd_list
43 {
44 struct pollfd *pollfds;
45 int maxindex; /* highest FD number */
46 int allocated;
47 };
48
49 typedef struct _pollfd_list pollfd_list_t;
50
51 pollfd_list_t pollfd_list;
52 static void poll_update_pollfds(int, short, PF *);
53 static unsigned long last_count = 0;
54 static unsigned long empty_count = 0;
55
56 /*
57 * init_netio
58 *
59 * This is a needed exported function which will be called to initialise
60 * the network loop code.
61 */
62 void
63 init_netio(void)
64 {
65 int fd;
66 int maxconn = comm_get_maxconnections();
67
68 pollfd_list.pollfds = calloc(sizeof(struct pollfd), maxconn);
69
70 for (fd = 0; fd < maxconn; fd++)
71 pollfd_list.pollfds[fd].fd = -1;
72
73 pollfd_list.maxindex = 0;
74 pollfd_list.allocated = maxconn;
75 }
76
77 static inline void
78 resize_poll_array(int fd)
79 {
80 int i, old_value = pollfd_list.allocated;
81
82 if (fd < pollfd_list.allocated)
83 return;
84
85 pollfd_list.allocated += 1024;
86 pollfd_list.pollfds = MyRealloc(pollfd_list.pollfds, pollfd_list.allocated * sizeof(struct pollfd));
87
88 /* because realloced memory can contain junk, we have to zero it out. */
89 memset(&pollfd_list.pollfds[old_value+1], 0, sizeof(struct pollfd) * 1024);
90
91 for (i = old_value + 1; i <= pollfd_list.allocated; i++)
92 pollfd_list.pollfds[i].fd = -1;
93 }
94
95 /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
96 /* Private functions */
97
98 /*
99 * find a spare slot in the fd list. We can optimise this out later!
100 * -- adrian
101 */
102 static inline int
103 poll_findslot(void)
104 {
105 int i;
106 for (i = 0; i < pollfd_list.allocated; i++)
107 {
108 if(pollfd_list.pollfds[i].fd == -1)
109 {
110 /* MATCH!!#$*&$ */
111 return i;
112 }
113 }
114
115 s_assert(1 == 0);
116 /* NOTREACHED */
117 return -1;
118 }
119
120 /*
121 * set and clear entries in the pollfds[] array.
122 */
123 static void
124 poll_update_pollfds(int fd, short event, PF * handler)
125 {
126 fde_t *F = comm_locate_fd(fd);
127 int comm_index;
128
129 resize_poll_array(fd);
130
131 if (F == NULL)
132 F = comm_add_fd(fd);
133
134 if(F->comm_index < 0)
135 F->comm_index = poll_findslot();
136
137 comm_index = F->comm_index;
138
139 /* Update the events */
140 if(handler)
141 {
142 F->list = FDLIST_IDLECLIENT;
143 pollfd_list.pollfds[comm_index].events |= event;
144 pollfd_list.pollfds[comm_index].fd = fd;
145 /* update maxindex here */
146 if(comm_index > pollfd_list.maxindex)
147 pollfd_list.maxindex = comm_index;
148 }
149 else
150 {
151 if(comm_index >= 0)
152 {
153 pollfd_list.pollfds[comm_index].events &= ~event;
154 if(pollfd_list.pollfds[comm_index].events == 0)
155 {
156 pollfd_list.pollfds[comm_index].fd = -1;
157 pollfd_list.pollfds[comm_index].revents = 0;
158 F->comm_index = -1;
159 F->list = FDLIST_NONE;
160
161 /* update pollfd_list.maxindex here */
162 if(comm_index == pollfd_list.maxindex)
163 while (pollfd_list.maxindex >= 0 &&
164 pollfd_list.pollfds[pollfd_list.maxindex].fd == -1)
165 pollfd_list.maxindex--;
166 }
167 }
168 }
169 }
170
171
172 /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
173 /* Public functions */
174
175 /*
176 * comm_setselect
177 *
178 * This is a needed exported function which will be called to register
179 * and deregister interest in a pending IO state for a given FD.
180 */
181 void
182 comm_setselect(int fd, fdlist_t list, unsigned int type, PF * handler,
183 void *client_data, time_t timeout)
184 {
185 fde_t *F = comm_locate_fd(fd);
186 s_assert(fd >= 0);
187 s_assert(F->flags.open);
188
189 if(type & COMM_SELECT_READ)
190 {
191 F->read_handler = handler;
192 F->read_data = client_data;
193 poll_update_pollfds(fd, POLLRDNORM, handler);
194 }
195 if(type & COMM_SELECT_WRITE)
196 {
197 F->write_handler = handler;
198 F->write_data = client_data;
199 poll_update_pollfds(fd, POLLWRNORM, handler);
200 }
201 if(timeout)
202 F->timeout = CurrentTime + (timeout / 1000);
203 }
204
205 static void
206 irc_sleep(unsigned long useconds)
207 {
208 #ifdef HAVE_NANOSLEEP
209 struct timespec t;
210 t.tv_sec = useconds / (unsigned long) 1000000;
211 t.tv_nsec = (useconds % (unsigned long) 1000000) * 1000;
212 nanosleep(&t, (struct timespec *) NULL);
213 #else
214 struct timeval t;
215 t.tv_sec = 0;
216 t.tv_usec = useconds;
217 select(0, NULL, NULL, NULL, &t);
218 #endif
219 return;
220 }
221
222 /* int comm_select_fdlist(unsigned long delay)
223 * Input: The maximum time to delay.
224 * Output: Returns -1 on error, 0 on success.
225 * Side-effects: Deregisters future interest in IO and calls the handlers
226 * if an event occurs for an FD.
227 * Comments: Check all connections for new connections and input data
228 * that is to be processed. Also check for connections with data queued
229 * and whether we can write it out.
230 * Called to do the new-style IO, courtesy of squid (like most of this
231 * new IO code). This routine handles the stuff we've hidden in
232 * comm_setselect and fd_table[] and calls callbacks for IO ready
233 * events.
234 */
235 int
236 comm_select(unsigned long delay)
237 {
238 int num;
239 int fd;
240 int ci;
241 unsigned long ndelay;
242 PF *hdl;
243
244 if(last_count > 0)
245 {
246 empty_count = 0;
247 ndelay = 0;
248 }
249 else {
250 ndelay = ++empty_count * 15000 ;
251 if(ndelay > delay * 1000)
252 ndelay = delay * 1000;
253 }
254
255 for (;;)
256 {
257 /* XXX kill that +1 later ! -- adrian */
258 if(ndelay > 0)
259 irc_sleep(ndelay);
260 last_count = num = poll(pollfd_list.pollfds, pollfd_list.maxindex + 1, 0);
261 if(num >= 0)
262 break;
263 if(ignoreErrno(errno))
264 continue;
265 /* error! */
266 set_time();
267 return -1;
268 /* NOTREACHED */
269 }
270
271 /* update current time again, eww.. */
272 set_time();
273
274 if(num == 0)
275 return 0;
276 /* XXX we *could* optimise by falling out after doing num fds ... */
277 for (ci = 0; ci < pollfd_list.maxindex + 1; ci++)
278 {
279 fde_t *F;
280 int revents;
281 if(((revents = pollfd_list.pollfds[ci].revents) == 0) ||
282 (pollfd_list.pollfds[ci].fd) == -1)
283 continue;
284 fd = pollfd_list.pollfds[ci].fd;
285 F = comm_locate_fd(fd);
286 if(revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR))
287 {
288 hdl = F->read_handler;
289 F->read_handler = NULL;
290 poll_update_pollfds(fd, POLLRDNORM, NULL);
291 if(hdl)
292 hdl(fd, F->read_data);
293 }
294 if(revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR))
295 {
296 hdl = F->write_handler;
297 F->write_handler = NULL;
298 poll_update_pollfds(fd, POLLWRNORM, NULL);
299 if(hdl)
300 hdl(fd, F->write_data);
301 }
302 }
303 return 0;
304 }
305