2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 05 Socket Functions */
12 * This is a very simple driver for Solaris /dev/poll.
14 * The updates are batched, one trip through the comm loop.
15 * (like libevent.) We keep a pointer into the structs so we
16 * can zero out an entry in the poll list if its active.
18 * Ported by Peter Payne from Squid 2.7.STABLE9 comm_devpoll.c
19 * on August 11, 2010 at 3pm (GMT+0100 Europe/London).
21 * Last modified 2010-10-08
25 * There are several poll types in Squid, ALL of which are compiled and linked
26 * in. Thus conditional compile-time flags are used to prevent the different
27 * modules from creating several versions of the same function simultaneously.
34 #include "comm/Loops.h"
37 #include "mgr/Registration.h"
38 #include "StatCounters.h"
44 #if HAVE_SYS_DEVPOLL_H
45 /* Solaris /dev/poll support, see "man -s 7D poll" */
46 #include <sys/devpoll.h>
49 #define DEBUG_DEVPOLL 0
51 // OPEN_MAX is defined in <climits>
52 #define DEVPOLL_UPDATESIZE OPEN_MAX
53 #define DEVPOLL_QUERYSIZE OPEN_MAX
56 typedef short pollfd_events_t
; /* type of pollfd.events from sys/poll.h */
59 /** \brief Current state */
60 struct _devpoll_state
{
61 pollfd_events_t state
; /**< current known state of file handle */
64 /** \brief Update list
66 * This structure contains an array of settings to send to the /dev/poll
67 * interface. Rather than send changes to /dev/poll one at a time they
68 * are pushed onto this array (updating cur to indicate how many of the
69 * pfds structure elements have been set) until it is full before it
70 * is written out the API.
73 struct pollfd
*pfds
; /**< ptr to array of struct pollfd config elements */
74 int cur
; /**< index of last written element of array, or -1 if none */
75 int size
; /**< maximum number of elements in array */
78 /* STATIC VARIABLES */
79 static int devpoll_fd
; /**< handle to /dev/poll device */
80 static int max_poll_time
= 1000; /**< maximum milliseconds to spend in poll */
82 static struct _devpoll_state
*devpoll_state
; /**< array of socket states */
83 static struct dvpoll do_poll
; /**< data struct for storing poll results */
84 static int dpoll_nfds
; /**< maximum number of poll results */
87 static void commDevPollRegisterWithCacheManager(void);
89 /* PRIVATE FUNCTIONS */
90 /** \brief Write batched file descriptor event changes to poll device
92 * Writes out the static array of file descriptor event changes to the
93 * poll device. This is done only when necessary (i.e. just before
94 * the poll device is queried during the select call, and whenever
95 * the number of changes to store in the array exceeds the size of the
99 comm_flush_updates(void)
102 if (devpoll_update
.cur
== -1)
103 return; /* array of changes to make is empty */
107 DEBUG_DEVPOLL
? 0 : 8,
108 (devpoll_update
.cur
+ 1) << " fds queued"
112 devpoll_fd
, /* open handle to /dev/poll */
113 devpoll_update
.pfds
, /* pointer to array of struct pollfd */
114 (devpoll_update
.cur
+ 1) * sizeof(struct pollfd
) /* bytes to process */
117 assert(static_cast<size_t>(i
) == (sizeof(struct pollfd
) * (devpoll_update
.cur
+ 1)));
118 devpoll_update
.cur
= -1; /* reset size of array, no elements remain */
121 /** \brief Register change in desired polling state for file descriptor
123 * Prevents unnecessary calls to the /dev/poll API by queueing changes
124 * in the devpoll_update array. If the array fills up the comm_flush_updates
125 * function is called.
127 * @param fd file descriptor to register change with
128 * @param events events to register (usually POLLIN, POLLOUT, or POLLREMOVE)
131 comm_update_fd(int fd
, int events
)
135 DEBUG_DEVPOLL
? 0 : 8,
136 "FD " << fd
<< ", events=" << events
139 /* Is the array already full and in need of flushing? */
140 if (devpoll_update
.cur
!= -1 && (devpoll_update
.cur
== devpoll_update
.size
))
141 comm_flush_updates();
143 /* Push new event onto array */
144 ++ devpoll_update
.cur
;
145 devpoll_update
.pfds
[devpoll_update
.cur
].fd
= fd
;
146 devpoll_update
.pfds
[devpoll_update
.cur
].events
= events
;
147 devpoll_update
.pfds
[devpoll_update
.cur
].revents
= 0;
150 static void commIncomingStats(StoreEntry
*sentry
)
152 storeAppendPrintf(sentry
, "Total number of devpoll loops: %ld\n", statCounter
.select_loops
);
153 storeAppendPrintf(sentry
, "Histogram of returned filedescriptors\n");
154 statCounter
.select_fds_hist
.dump(sentry
, statHistIntDumper
);
158 commDevPollRegisterWithCacheManager(void)
161 "comm_devpoll_incoming",
162 "comm_incoming() stats",
169 /* PUBLIC FUNCTIONS */
171 /** \brief Initialise /dev/poll support
173 * Allocates memory, opens /dev/poll device handle.
176 Comm::SelectLoopInit(void)
178 /* allocate memory first before attempting to open poll device */
179 /* This tracks the FD devpoll offset+state */
180 devpoll_state
= (struct _devpoll_state
*)xcalloc(
181 SQUID_MAXFD
, sizeof(struct _devpoll_state
)
184 /* And this is the stuff we use to read events */
185 do_poll
.dp_fds
= (struct pollfd
*)xcalloc(
186 DEVPOLL_QUERYSIZE
, sizeof(struct pollfd
)
188 dpoll_nfds
= DEVPOLL_QUERYSIZE
;
190 devpoll_update
.pfds
= (struct pollfd
*)xcalloc(
191 DEVPOLL_UPDATESIZE
, sizeof(struct pollfd
)
193 devpoll_update
.cur
= -1;
194 devpoll_update
.size
= DEVPOLL_UPDATESIZE
;
196 /* attempt to open /dev/poll device */
197 devpoll_fd
= open("/dev/poll", O_RDWR
);
198 if (devpoll_fd
< 0) {
200 fatalf("comm_select_init: can't open /dev/poll: %s\n", xstrerr(xerrno
));
203 fd_open(devpoll_fd
, FD_UNKNOWN
, "devpoll ctl");
205 commDevPollRegisterWithCacheManager();
208 /** \brief Set polling state of file descriptor and callback functions
210 * Sets requested polling state for given file handle along with
211 * desired callback function in the event the request event triggers.
213 * Note that setting a polling state with a NULL callback function will
214 * clear the polling for that event on that file descriptor.
216 * @param fd file descriptor to change
217 * @param type may be COMM_SELECT_READ (input) or COMM_SELECT_WRITE (output)
218 * @param handler callback function, or NULL to stop type of polling
219 * @param client_data pointer to be provided to call back function
220 * @param timeout if non-zero then timeout relative to now
223 Comm::SetSelect(int fd
, unsigned int type
, PF
* handler
, void *client_data
, time_t timeout
)
226 debugs(5, 5, "FD " << fd
<< ", type=" << type
<<
227 ", handler=" << handler
<< ", client_data=" << client_data
<<
228 ", timeout=" << timeout
);
230 /* POLLIN/POLLOUT are defined in <sys/poll.h> */
231 fde
*F
= &fd_table
[fd
];
232 if (!F
->flags
.open
) {
233 /* remove from poll set */
234 comm_update_fd( fd
, POLLREMOVE
);
235 devpoll_state
[fd
].state
= 0;
239 pollfd_events_t state_old
= devpoll_state
[fd
].state
;
240 pollfd_events_t state_new
= 0; /* new state (derive from old state) */
242 if ( type
& COMM_SELECT_READ
) {
243 if ( handler
!= NULL
) {
244 // Hack to keep the events flowing if there is data immediately ready
245 if (F
->flags
.read_pending
)
246 state_new
|= POLLOUT
;
247 /* we want to POLLIN */
250 ; /* we want to clear POLLIN because handler is NULL */
253 F
->read_handler
= handler
;
254 F
->read_data
= client_data
;
255 } else if ( state_old
& POLLIN
) {
256 /* we're not changing reading state so take from existing */
260 if ( type
& COMM_SELECT_WRITE
) {
261 if ( handler
!= NULL
) {
262 /* we want to POLLOUT */
263 state_new
|= POLLOUT
;
265 ; /* we want to clear POLLOUT because handler is NULL */
268 F
->write_handler
= handler
;
269 F
->write_data
= client_data
;
270 } else if ( state_old
& POLLOUT
) {
271 /* we're not changing writing state so take from existing */
272 state_new
|= POLLOUT
;
275 if ( pollfd_events_t bits_changed
= (state_old
^ state_new
) ) {
276 /* something has changed, update /dev/poll of what to listen for */
278 /* did any bits clear? (in which case a poll remove is necessary) */
279 if ( bits_changed
& state_old
) {
280 comm_update_fd( fd
, POLLREMOVE
);
281 /* existing state cleared, so update with all required events */
283 comm_update_fd( fd
, state_new
);
285 /* only update with new required event */
286 if ( pollfd_events_t newly_set_only
= (bits_changed
& state_new
) )
287 comm_update_fd( fd
, newly_set_only
);
290 devpoll_state
[fd
].state
= state_new
;
294 F
->timeout
= squid_curtime
+ timeout
;
297 /** \brief Do poll and trigger callback functions as appropriate
299 * Check all connections for new connections and input data that is to be
300 * processed. Also check for connections with data queued and whether we can
303 * Called to do the new-style IO, courtesy of of squid (like most of this
304 * new IO code). This routine handles the stuff we've hidden in
305 * comm_setselect and fd_table[] and calls callbacks for IO ready
308 * @param msec milliseconds to poll for (limited by max_poll_time)
311 Comm::DoSelect(int msec
)
317 if (msec
> max_poll_time
)
318 msec
= max_poll_time
;
321 do_poll
.dp_timeout
= msec
;
322 do_poll
.dp_nfds
= dpoll_nfds
;
324 comm_flush_updates(); /* ensure latest changes are sent to /dev/poll */
326 num
= ioctl(devpoll_fd
, DP_POLL
, &do_poll
);
327 ++ statCounter
.select_loops
;
330 break; /* no error, skip out of loop */
332 if (ignoreErrno(errno
))
333 break; /* error is one we may ignore, skip out of loop */
335 /* error during poll */
337 return Comm::COMM_ERROR
;
342 statCounter
.select_fds_hist
.count(num
);
345 return Comm::TIMEOUT
; /* no error */
347 for (i
= 0; i
< num
; ++i
) {
348 int fd
= (int)do_poll
.dp_fds
[i
].fd
;
352 DEBUG_DEVPOLL
? 0 : 8,
354 << ",events=" << std::hex
<< do_poll
.dp_fds
[i
].revents
355 << ",monitoring=" << devpoll_state
[fd
].state
356 << ",F->read_handler=" << F
->read_handler
357 << ",F->write_handler=" << F
->write_handler
361 if (do_poll
.dp_fds
[i
].revents
& (POLLERR
| POLLHUP
| POLLNVAL
)) {
362 debugs(5, DEBUG_DEVPOLL
? 0 : 8,
363 "ERROR: devpoll event failure: fd " << fd
368 /* check if file descriptor has data to read */
369 if (do_poll
.dp_fds
[i
].revents
& POLLIN
|| F
->flags
.read_pending
) {
370 if ( (hdl
= F
->read_handler
) != NULL
) {
373 DEBUG_DEVPOLL
? 0 : 8,
374 "Calling read handler on FD " << fd
376 F
->read_handler
= nullptr;
377 hdl(fd
, F
->read_data
);
378 ++ statCounter
.select_fds
;
382 DEBUG_DEVPOLL
? 0 : 8,
383 "no read handler for FD " << fd
385 // remove interest since no handler exist for this event.
386 SetSelect(fd
, COMM_SELECT_READ
, nullptr, nullptr, 0);
390 /* check if file descriptor is ready to write */
391 if (do_poll
.dp_fds
[i
].revents
& POLLOUT
) {
392 if ((hdl
= F
->write_handler
) != NULL
) {
395 DEBUG_DEVPOLL
? 0 : 8,
396 "Calling write handler on FD " << fd
398 F
->write_handler
= nullptr;
399 hdl(fd
, F
->write_data
);
400 ++ statCounter
.select_fds
;
404 DEBUG_DEVPOLL
? 0 : 8,
405 "no write handler for FD " << fd
407 // remove interest since no handler exist for this event.
408 SetSelect(fd
, COMM_SELECT_WRITE
, nullptr, nullptr, 0);
417 Comm::QuickPollRequired(void)
422 #endif /* USE_DEVPOLL */