]> git.ipfire.org Git - thirdparty/squid.git/blob - src/comm/ModDevPoll.cc
Cleanup: un-wrap C++ header includes
[thirdparty/squid.git] / src / comm / ModDevPoll.cc
1 /*
2 * DEBUG: section 05 Socket Functions
3 *
4 * SQUID Web Proxy Cache http://www.squid-cache.org/
5 * ----------------------------------------------------------
6 *
7 * Squid is the result of efforts by numerous individuals from
8 * the Internet community; see the CONTRIBUTORS file for full
9 * details. Many organizations have provided support for Squid's
10 * development; see the SPONSORS file for full details. Squid is
11 * Copyrighted (C) 2001 by the Regents of the University of
12 * California; see the COPYRIGHT file for full details. Squid
13 * incorporates software developed and/or copyrighted by other
14 * sources; see the CREDITS file for full details.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
29 *
30 */
31
32 /*
33 * This is a very simple driver for Solaris /dev/poll.
34 *
35 * The updates are batched, one trip through the comm loop.
36 * (like libevent.) We keep a pointer into the structs so we
37 * can zero out an entry in the poll list if its active.
38 *
39 * Ported by Peter Payne from Squid 2.7.STABLE9 comm_devpoll.c
40 * on August 11, 2010 at 3pm (GMT+0100 Europe/London).
41 *
42 * Last modified 2010-10-08
43 */
44
45 /*
46 * There are several poll types in Squid, ALL of which are compiled and linked
47 * in. Thus conditional compile-time flags are used to prevent the different
48 * modules from creating several versions of the same function simultaneously.
49 */
50
51 #include "squid.h"
52
53 #if USE_DEVPOLL
54
55 #include "comm/Loops.h"
56 #include "fd.h"
57 #include "fde.h"
58 #include "mgr/Registration.h"
59 #include "profiler/Profiler.h"
60 #include "SquidTime.h"
61 #include "StatCounters.h"
62 #include "StatHist.h"
63 #include "Store.h"
64
65 #include <cerrno>
66 #include <climits>
67 #if HAVE_SYS_DEVPOLL_H
68 /* Solaris /dev/poll support, see "man -s 7D poll" */
69 #include <sys/devpoll.h>
70 #endif
71
72 #define DEBUG_DEVPOLL 0
73
74 // OPEN_MAX is defined in <climits>
75 #define DEVPOLL_UPDATESIZE OPEN_MAX
76 #define DEVPOLL_QUERYSIZE OPEN_MAX
77
78 /* TYPEDEFS */
79 typedef short pollfd_events_t; /* type of pollfd.events from sys/poll.h */
80
81 /* STRUCTURES */
82 /** \brief Current state */
83 struct _devpoll_state {
84 pollfd_events_t state; /**< current known state of file handle */
85 };
86
87 /** \brief Update list
88 *
89 * This structure contains an array of settings to send to the /dev/poll
90 * interface. Rather than send changes to /dev/poll one at a time they
91 * are pushed onto this array (updating cur to indicate how many of the
92 * pfds structure elements have been set) until it is full before it
93 * is written out the API.
94 */
95 static struct {
96 struct pollfd *pfds; /**< ptr to array of struct pollfd config elements */
97 int cur; /**< index of last written element of array, or -1 if none */
98 int size; /**< maximum number of elements in array */
99 } devpoll_update;
100
101 /* STATIC VARIABLES */
102 static int devpoll_fd; /**< handle to /dev/poll device */
103 static int max_poll_time = 1000; /**< maximum milliseconds to spend in poll */
104
105 static struct _devpoll_state *devpoll_state; /**< array of socket states */
106 static struct dvpoll do_poll; /**< data struct for storing poll results */
107 static int dpoll_nfds; /**< maximum number of poll results */
108
109 /* PROTOTYPES */
110 static void commDevPollRegisterWithCacheManager(void);
111
112 /* PRIVATE FUNCTIONS */
113 /** \brief Write batched file descriptor event changes to poll device
114 *
115 * Writes out the static array of file descriptor event changes to the
116 * poll device. This is done only when necessary (i.e. just before
117 * the poll device is queried during the select call, and whenever
118 * the number of changes to store in the array exceeds the size of the
119 * array).
120 */
121 static void
122 comm_flush_updates(void)
123 {
124 int i;
125 if (devpoll_update.cur == -1)
126 return; /* array of changes to make is empty */
127
128 debugs(
129 5,
130 DEBUG_DEVPOLL ? 0 : 8,
131 HERE << (devpoll_update.cur + 1) << " fds queued"
132 );
133
134 i = write(
135 devpoll_fd, /* open handle to /dev/poll */
136 devpoll_update.pfds, /* pointer to array of struct pollfd */
137 (devpoll_update.cur + 1) * sizeof(struct pollfd) /* bytes to process */
138 );
139 assert(i > 0);
140 assert(static_cast<size_t>(i) == (sizeof(struct pollfd) * (devpoll_update.cur + 1)));
141 devpoll_update.cur = -1; /* reset size of array, no elements remain */
142 }
143
144 /** \brief Register change in desired polling state for file descriptor
145 *
146 * Prevents unnecessary calls to the /dev/poll API by queueing changes
147 * in the devpoll_update array. If the array fills up the comm_flush_updates
148 * function is called.
149 *
150 * @param fd file descriptor to register change with
151 * @param events events to register (usually POLLIN, POLLOUT, or POLLREMOVE)
152 */
153 static void
154 comm_update_fd(int fd, int events)
155 {
156 debugs(
157 5,
158 DEBUG_DEVPOLL ? 0 : 8,
159 HERE << "FD " << fd << ", events=" << events
160 );
161
162 /* Is the array already full and in need of flushing? */
163 if (devpoll_update.cur != -1 && (devpoll_update.cur == devpoll_update.size))
164 comm_flush_updates();
165
166 /* Push new event onto array */
167 ++ devpoll_update.cur;
168 devpoll_update.pfds[devpoll_update.cur].fd = fd;
169 devpoll_update.pfds[devpoll_update.cur].events = events;
170 devpoll_update.pfds[devpoll_update.cur].revents = 0;
171 }
172
173 static void commIncomingStats(StoreEntry *sentry)
174 {
175 storeAppendPrintf(sentry, "Total number of devpoll loops: %ld\n", statCounter.select_loops);
176 storeAppendPrintf(sentry, "Histogram of returned filedescriptors\n");
177 statCounter.select_fds_hist.dump(sentry, statHistIntDumper);
178 }
179
180 static void
181 commDevPollRegisterWithCacheManager(void)
182 {
183 Mgr::RegisterAction(
184 "comm_devpoll_incoming",
185 "comm_incoming() stats",
186 commIncomingStats,
187 0,
188 1
189 );
190 }
191
192 /* PUBLIC FUNCTIONS */
193
194 /** \brief Initialise /dev/poll support
195 *
196 * Allocates memory, opens /dev/poll device handle.
197 */
198 void
199 Comm::SelectLoopInit(void)
200 {
201 /* allocate memory first before attempting to open poll device */
202 /* This tracks the FD devpoll offset+state */
203 devpoll_state = (struct _devpoll_state *)xcalloc(
204 SQUID_MAXFD, sizeof(struct _devpoll_state)
205 );
206
207 /* And this is the stuff we use to read events */
208 do_poll.dp_fds = (struct pollfd *)xcalloc(
209 DEVPOLL_QUERYSIZE, sizeof(struct pollfd)
210 );
211 dpoll_nfds = DEVPOLL_QUERYSIZE;
212
213 devpoll_update.pfds = (struct pollfd *)xcalloc(
214 DEVPOLL_UPDATESIZE, sizeof(struct pollfd)
215 );
216 devpoll_update.cur = -1;
217 devpoll_update.size = DEVPOLL_UPDATESIZE;
218
219 /* attempt to open /dev/poll device */
220 devpoll_fd = open("/dev/poll", O_RDWR);
221 if (devpoll_fd < 0)
222 fatalf("comm_select_init: can't open /dev/poll: %s\n", xstrerror());
223
224 fd_open(devpoll_fd, FD_UNKNOWN, "devpoll ctl");
225
226 commDevPollRegisterWithCacheManager();
227 }
228
229 /** \brief Set polling state of file descriptor and callback functions
230 *
231 * Sets requested polling state for given file handle along with
232 * desired callback function in the event the request event triggers.
233 *
234 * Note that setting a polling state with a NULL callback function will
235 * clear the polling for that event on that file descriptor.
236 *
237 * @param fd file descriptor to change
238 * @param type may be COMM_SELECT_READ (input) or COMM_SELECT_WRITE (output)
239 * @param handler callback function, or NULL to stop type of polling
240 * @param client_data pointer to be provided to call back function
241 * @param timeout if non-zero then timeout relative to now
242 */
243 void
244 Comm::SetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout)
245 {
246 assert(fd >= 0);
247 debugs(5, 5, HERE << "FD " << fd << ", type=" << type <<
248 ", handler=" << handler << ", client_data=" << client_data <<
249 ", timeout=" << timeout);
250
251 /* POLLIN/POLLOUT are defined in <sys/poll.h> */
252 fde *F = &fd_table[fd];
253 if (!F->flags.open) {
254 /* remove from poll set */
255 comm_update_fd( fd, POLLREMOVE );
256 devpoll_state[fd].state = 0;
257 return;
258 }
259
260 pollfd_events_t state_old = devpoll_state[fd].state;
261 pollfd_events_t state_new = 0; /* new state (derive from old state) */
262
263 if ( type & COMM_SELECT_READ ) {
264 if ( handler != NULL ) {
265 /* we want to POLLIN */
266 state_new |= POLLIN;
267 } else {
268 ; /* we want to clear POLLIN because handler is NULL */
269 }
270
271 F->read_handler = handler;
272 F->read_data = client_data;
273 } else if ( state_old & POLLIN ) {
274 /* we're not changing reading state so take from existing */
275 state_new |= POLLIN;
276 }
277
278 if ( type & COMM_SELECT_WRITE ) {
279 if ( handler != NULL ) {
280 /* we want to POLLOUT */
281 state_new |= POLLOUT;
282 } else {
283 ; /* we want to clear POLLOUT because handler is NULL */
284 }
285
286 F->write_handler = handler;
287 F->write_data = client_data;
288 } else if ( state_old & POLLOUT ) {
289 /* we're not changing writing state so take from existing */
290 state_new |= POLLOUT;
291 }
292
293 if ( pollfd_events_t bits_changed = (state_old ^ state_new) ) {
294 /* something has changed, update /dev/poll of what to listen for */
295
296 /* did any bits clear? (in which case a poll remove is necessary) */
297 if ( bits_changed & state_old ) {
298 comm_update_fd( fd, POLLREMOVE );
299 /* existing state cleared, so update with all required events */
300 if ( state_new )
301 comm_update_fd( fd, state_new );
302 } else {
303 /* only update with new required event */
304 if ( pollfd_events_t newly_set_only = (bits_changed & state_new) )
305 comm_update_fd( fd, newly_set_only );
306 }
307
308 devpoll_state[fd].state = state_new;
309 }
310
311 if (timeout)
312 F->timeout = squid_curtime + timeout;
313 }
314
315 /** \brief Clear polling of file handle (both read and write)
316 *
317 * @param fd file descriptor to clear polling on
318 */
319 void
320 Comm::ResetSelect(int fd)
321 {
322 SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
323 SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
324 }
325
326 /** \brief Do poll and trigger callback functions as appropriate
327 *
328 * Check all connections for new connections and input data that is to be
329 * processed. Also check for connections with data queued and whether we can
330 * write it out.
331 *
332 * Called to do the new-style IO, courtesy of of squid (like most of this
333 * new IO code). This routine handles the stuff we've hidden in
334 * comm_setselect and fd_table[] and calls callbacks for IO ready
335 * events.
336 *
337 * @param msec milliseconds to poll for (limited by max_poll_time)
338 */
339 comm_err_t
340 Comm::DoSelect(int msec)
341 {
342 int num, i;
343 fde *F;
344 PF *hdl;
345
346 PROF_start(comm_check_incoming);
347
348 if (msec > max_poll_time)
349 msec = max_poll_time;
350
351 for (;;) {
352 do_poll.dp_timeout = msec;
353 do_poll.dp_nfds = dpoll_nfds;
354
355 comm_flush_updates(); /* ensure latest changes are sent to /dev/poll */
356
357 num = ioctl(devpoll_fd, DP_POLL, &do_poll);
358 ++ statCounter.select_loops;
359
360 if (num >= 0)
361 break; /* no error, skip out of loop */
362
363 if (ignoreErrno(errno))
364 break; /* error is one we may ignore, skip out of loop */
365
366 /* error during poll */
367 getCurrentTime();
368 PROF_stop(comm_check_incoming);
369 return COMM_ERROR;
370 }
371
372 PROF_stop(comm_check_incoming);
373 getCurrentTime();
374
375 statCounter.select_fds_hist.count(num);
376
377 if (num == 0)
378 return COMM_TIMEOUT; /* no error */
379
380 PROF_start(comm_handle_ready_fd);
381
382 for (i = 0; i < num; ++i) {
383 int fd = (int)do_poll.dp_fds[i].fd;
384 F = &fd_table[fd];
385 debugs(
386 5,
387 DEBUG_DEVPOLL ? 0 : 8,
388 HERE << "got FD " << fd
389 << ",events=" << std::hex << do_poll.dp_fds[i].revents
390 << ",monitoring=" << devpoll_state[fd].state
391 << ",F->read_handler=" << F->read_handler
392 << ",F->write_handler=" << F->write_handler
393 );
394
395 /* handle errors */
396 if (do_poll.dp_fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
397 debugs(
398 5,
399 DEBUG_DEVPOLL ? 0 : 8,
400 HERE << "devpoll event error: fd " << fd
401 );
402 continue;
403 }
404
405 /* check if file descriptor has data to read */
406 if (do_poll.dp_fds[i].revents & POLLIN || F->flags.read_pending) {
407 if ( (hdl = F->read_handler) != NULL ) {
408 debugs(
409 5,
410 DEBUG_DEVPOLL ? 0 : 8,
411 HERE << "Calling read handler on FD " << fd
412 );
413 PROF_start(comm_read_handler);
414 F->flags.read_pending = 0;
415 F->read_handler = NULL;
416 hdl(fd, F->read_data);
417 PROF_stop(comm_read_handler);
418 ++ statCounter.select_fds;
419 } else {
420 debugs(
421 5,
422 DEBUG_DEVPOLL ? 0 : 8,
423 HERE << "no read handler for FD " << fd
424 );
425 // remove interest since no handler exist for this event.
426 SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
427 }
428 }
429
430 /* check if file descriptor is ready to write */
431 if (do_poll.dp_fds[i].revents & POLLOUT) {
432 if ((hdl = F->write_handler) != NULL) {
433 debugs(
434 5,
435 DEBUG_DEVPOLL ? 0 : 8,
436 HERE << "Calling write handler on FD " << fd
437 );
438 PROF_start(comm_write_handler);
439 F->write_handler = NULL;
440 hdl(fd, F->write_data);
441 PROF_stop(comm_write_handler);
442 ++ statCounter.select_fds;
443 } else {
444 debugs(
445 5,
446 DEBUG_DEVPOLL ? 0 : 8,
447 HERE << "no write handler for FD " << fd
448 );
449 // remove interest since no handler exist for this event.
450 SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
451 }
452 }
453 }
454
455 PROF_stop(comm_handle_ready_fd);
456 return COMM_OK;
457 }
458
459 void
460 Comm::QuickPollRequired(void)
461 {
462 max_poll_time = 10;
463 }
464
465 #endif /* USE_DEVPOLL */