4 * DEBUG: section 05 Socket Functions
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
35 * This is a very simple driver for Solaris /dev/poll.
37 * The updates are batched, one trip through the comm loop.
38 * (like libevent.) We keep a pointer into the structs so we
39 * can zero out an entry in the poll list if its active.
41 * Ported by Peter Payne from Squid 2.7.STABLE9 comm_devpoll.c
42 * on August 11, 2010 at 3pm (GMT+0100 Europe/London).
44 * Last modified 2010-10-08
48 * There are several poll types in Squid, ALL of which are compiled and linked
49 * in. Thus conditional compile-time flags are used to prevent the different
50 * modules from creating several versions of the same function simultaneously.
57 #include "comm/Loops.h"
59 #include "mgr/Registration.h"
60 #include "profiler/Profiler.h"
61 #include "SquidTime.h"
62 #include "StatCounters.h"
66 #if HAVE_SYS_DEVPOLL_H
67 /* Solaris /dev/poll support, see "man -s 7D poll" */
68 #include <sys/devpoll.h>
71 #define DEBUG_DEVPOLL 0
73 /* OPEN_MAX is defined in <limits.h>, presumably included by sys/devpoll.h */
74 #define DEVPOLL_UPDATESIZE OPEN_MAX
75 #define DEVPOLL_QUERYSIZE OPEN_MAX
78 typedef short pollfd_events_t
; /* type of pollfd.events from sys/poll.h */
81 /** \brief Current state */
82 struct _devpoll_state
{
83 pollfd_events_t state
; /**< current known state of file handle */
86 /** \brief Update list
88 * This structure contains an array of settings to send to the /dev/poll
89 * interface. Rather than send changes to /dev/poll one at a time they
90 * are pushed onto this array (updating cur to indicate how many of the
91 * pfds structure elements have been set) until it is full before it
92 * is written out the API.
95 struct pollfd
*pfds
; /**< ptr to array of struct pollfd config elements */
96 int cur
; /**< index of last written element of array, or -1 if none */
97 int size
; /**< maximum number of elements in array */
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 */
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 */
110 static void commDevPollRegisterWithCacheManager(void);
113 /* PRIVATE FUNCTIONS */
114 /** \brief Write batched file descriptor event changes to poll device
116 * Writes out the static array of file descriptor event changes to the
117 * poll device. This is done only when necessary (i.e. just before
118 * the poll device is queried during the select call, and whenever
119 * the number of changes to store in the array exceeds the size of the
123 comm_flush_updates(void)
126 if (devpoll_update
.cur
== -1)
127 return; /* array of changes to make is empty */
131 DEBUG_DEVPOLL
? 0 : 8,
132 HERE
<< (devpoll_update
.cur
+ 1) << " fds queued"
136 devpoll_fd
, /* open handle to /dev/poll */
137 devpoll_update
.pfds
, /* pointer to array of struct pollfd */
138 (devpoll_update
.cur
+ 1) * sizeof(struct pollfd
) /* bytes to process */
141 assert(static_cast<size_t>(i
) == (sizeof(struct pollfd
) * (devpoll_update
.cur
+ 1)));
142 devpoll_update
.cur
= -1; /* reset size of array, no elements remain */
145 /** \brief Register change in desired polling state for file descriptor
147 * Prevents unnecessary calls to the /dev/poll API by queueing changes
148 * in the devpoll_update array. If the array fills up the comm_flush_updates
149 * function is called.
151 * @param fd file descriptor to register change with
152 * @param events events to register (usually POLLIN, POLLOUT, or POLLREMOVE)
155 comm_update_fd(int fd
, int events
)
159 DEBUG_DEVPOLL
? 0 : 8,
160 HERE
<< "FD " << fd
<< ", events=" << events
163 /* Is the array already full and in need of flushing? */
164 if (devpoll_update
.cur
!= -1 && (devpoll_update
.cur
== devpoll_update
.size
))
165 comm_flush_updates();
167 /* Push new event onto array */
168 ++ devpoll_update
.cur
;
169 devpoll_update
.pfds
[devpoll_update
.cur
].fd
= fd
;
170 devpoll_update
.pfds
[devpoll_update
.cur
].events
= events
;
171 devpoll_update
.pfds
[devpoll_update
.cur
].revents
= 0;
175 static void commIncomingStats(StoreEntry
*sentry
)
177 storeAppendPrintf(sentry
, "Total number of devpoll loops: %ld\n", statCounter
.select_loops
);
178 storeAppendPrintf(sentry
, "Histogram of returned filedescriptors\n");
179 statCounter
.select_fds_hist
.dump(sentry
, statHistIntDumper
);
184 commDevPollRegisterWithCacheManager(void)
187 "comm_devpoll_incoming",
188 "comm_incoming() stats",
196 /* PUBLIC FUNCTIONS */
198 /** \brief Initialise /dev/poll support
200 * Allocates memory, opens /dev/poll device handle.
203 Comm::SelectLoopInit(void)
205 /* allocate memory first before attempting to open poll device */
206 /* This tracks the FD devpoll offset+state */
207 devpoll_state
= (struct _devpoll_state
*)xcalloc(
208 SQUID_MAXFD
, sizeof(struct _devpoll_state
)
211 /* And this is the stuff we use to read events */
212 do_poll
.dp_fds
= (struct pollfd
*)xcalloc(
213 DEVPOLL_QUERYSIZE
, sizeof(struct pollfd
)
215 dpoll_nfds
= DEVPOLL_QUERYSIZE
;
217 devpoll_update
.pfds
= (struct pollfd
*)xcalloc(
218 DEVPOLL_UPDATESIZE
, sizeof(struct pollfd
)
220 devpoll_update
.cur
= -1;
221 devpoll_update
.size
= DEVPOLL_UPDATESIZE
;
223 /* attempt to open /dev/poll device */
224 devpoll_fd
= open("/dev/poll", O_RDWR
);
226 fatalf("comm_select_init: can't open /dev/poll: %s\n", xstrerror());
228 fd_open(devpoll_fd
, FD_UNKNOWN
, "devpoll ctl");
230 commDevPollRegisterWithCacheManager();
233 /** \brief Set polling state of file descriptor and callback functions
235 * Sets requested polling state for given file handle along with
236 * desired callback function in the event the request event triggers.
238 * Note that setting a polling state with a NULL callback function will
239 * clear the polling for that event on that file descriptor.
241 * @param fd file descriptor to change
242 * @param type may be COMM_SELECT_READ (input) or COMM_SELECT_WRITE (output)
243 * @param handler callback function, or NULL to stop type of polling
244 * @param client_data pointer to be provided to call back function
245 * @param timeout if non-zero then timeout relative to now
248 Comm::SetSelect(int fd
, unsigned int type
, PF
* handler
, void *client_data
, time_t timeout
)
251 debugs(5, 5, HERE
<< "FD " << fd
<< ", type=" << type
<<
252 ", handler=" << handler
<< ", client_data=" << client_data
<<
253 ", timeout=" << timeout
);
255 /* POLLIN/POLLOUT are defined in <sys/poll.h> */
256 fde
*F
= &fd_table
[fd
];
257 if (!F
->flags
.open
) {
258 /* remove from poll set */
259 comm_update_fd( fd
, POLLREMOVE
);
260 devpoll_state
[fd
].state
= 0;
264 pollfd_events_t state_old
= devpoll_state
[fd
].state
;
265 pollfd_events_t state_new
= 0; /* new state (derive from old state) */
267 if ( type
& COMM_SELECT_READ
) {
268 if ( handler
!= NULL
) {
269 /* we want to POLLIN */
272 ; /* we want to clear POLLIN because handler is NULL */
275 F
->read_handler
= handler
;
276 F
->read_data
= client_data
;
277 } else if ( state_old
& POLLIN
) {
278 /* we're not changing reading state so take from existing */
282 if ( type
& COMM_SELECT_WRITE
) {
283 if ( handler
!= NULL
) {
284 /* we want to POLLOUT */
285 state_new
|= POLLOUT
;
287 ; /* we want to clear POLLOUT because handler is NULL */
290 F
->write_handler
= handler
;
291 F
->write_data
= client_data
;
292 } else if ( state_old
& POLLOUT
) {
293 /* we're not changing writing state so take from existing */
294 state_new
|= POLLOUT
;
297 if ( pollfd_events_t bits_changed
= (state_old
^ state_new
) ) {
298 /* something has changed, update /dev/poll of what to listen for */
300 /* did any bits clear? (in which case a poll remove is necessary) */
301 if ( bits_changed
& state_old
) {
302 comm_update_fd( fd
, POLLREMOVE
);
303 /* existing state cleared, so update with all required events */
305 comm_update_fd( fd
, state_new
);
307 /* only update with new required event */
308 if ( pollfd_events_t newly_set_only
= (bits_changed
& state_new
) )
309 comm_update_fd( fd
, newly_set_only
);
312 devpoll_state
[fd
].state
= state_new
;
316 F
->timeout
= squid_curtime
+ timeout
;
320 /** \brief Clear polling of file handle (both read and write)
322 * @param fd file descriptor to clear polling on
325 Comm::ResetSelect(int fd
)
327 SetSelect(fd
, COMM_SELECT_WRITE
, NULL
, NULL
, 0);
328 SetSelect(fd
, COMM_SELECT_READ
, NULL
, NULL
, 0);
332 /** \brief Do poll and trigger callback functions as appropriate
334 * Check all connections for new connections and input data that is to be
335 * processed. Also check for connections with data queued and whether we can
338 * Called to do the new-style IO, courtesy of of squid (like most of this
339 * new IO code). This routine handles the stuff we've hidden in
340 * comm_setselect and fd_table[] and calls callbacks for IO ready
343 * @param msec milliseconds to poll for (limited by max_poll_time)
346 Comm::DoSelect(int msec
)
352 PROF_start(comm_check_incoming
);
354 if (msec
> max_poll_time
)
355 msec
= max_poll_time
;
358 do_poll
.dp_timeout
= msec
;
359 do_poll
.dp_nfds
= dpoll_nfds
;
361 comm_flush_updates(); /* ensure latest changes are sent to /dev/poll */
363 num
= ioctl(devpoll_fd
, DP_POLL
, &do_poll
);
364 ++ statCounter
.select_loops
;
367 break; /* no error, skip out of loop */
369 if (ignoreErrno(errno
))
370 break; /* error is one we may ignore, skip out of loop */
372 /* error during poll */
374 PROF_stop(comm_check_incoming
);
378 PROF_stop(comm_check_incoming
);
381 statCounter
.select_fds_hist
.count(num
);
384 return COMM_TIMEOUT
; /* no error */
386 PROF_start(comm_handle_ready_fd
);
388 for (i
= 0; i
< num
; ++i
) {
389 int fd
= (int)do_poll
.dp_fds
[i
].fd
;
393 DEBUG_DEVPOLL
? 0 : 8,
394 HERE
<< "got FD " << fd
395 << ",events=" << std::hex
<< do_poll
.dp_fds
[i
].revents
396 << ",monitoring=" << devpoll_state
[fd
].state
397 << ",F->read_handler=" << F
->read_handler
398 << ",F->write_handler=" << F
->write_handler
402 if (do_poll
.dp_fds
[i
].revents
& (POLLERR
| POLLHUP
| POLLNVAL
)) {
405 DEBUG_DEVPOLL
? 0 : 8,
406 HERE
<< "devpoll event error: fd " << fd
411 /* check if file descriptor has data to read */
412 if (do_poll
.dp_fds
[i
].revents
& POLLIN
|| F
->flags
.read_pending
) {
413 if ( (hdl
= F
->read_handler
) != NULL
) {
416 DEBUG_DEVPOLL
? 0 : 8,
417 HERE
<< "Calling read handler on FD " << fd
419 PROF_start(comm_read_handler
);
420 F
->flags
.read_pending
= 0;
421 F
->read_handler
= NULL
;
422 hdl(fd
, F
->read_data
);
423 PROF_stop(comm_read_handler
);
424 ++ statCounter
.select_fds
;
428 DEBUG_DEVPOLL
? 0 : 8,
429 HERE
<< "no read handler for FD " << fd
431 // remove interest since no handler exist for this event.
432 SetSelect(fd
, COMM_SELECT_READ
, NULL
, NULL
, 0);
436 /* check if file descriptor is ready to write */
437 if (do_poll
.dp_fds
[i
].revents
& POLLOUT
) {
438 if ((hdl
= F
->write_handler
) != NULL
) {
441 DEBUG_DEVPOLL
? 0 : 8,
442 HERE
<< "Calling write handler on FD " << fd
444 PROF_start(comm_write_handler
);
445 F
->write_handler
= NULL
;
446 hdl(fd
, F
->write_data
);
447 PROF_stop(comm_write_handler
);
448 ++ statCounter
.select_fds
;
452 DEBUG_DEVPOLL
? 0 : 8,
453 HERE
<< "no write handler for FD " << fd
455 // remove interest since no handler exist for this event.
456 SetSelect(fd
, COMM_SELECT_WRITE
, NULL
, NULL
, 0);
461 PROF_stop(comm_handle_ready_fd
);
466 Comm::QuickPollRequired(void)
471 #endif /* USE_DEVPOLL */