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