Squid Web Cache master
Loading...
Searching...
No Matches
ModPoll.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9/* DEBUG: section 05 Socket Functions */
10
11#include "squid.h"
12
13#if USE_POLL
14#include "anyp/PortCfg.h"
15#include "comm/Connection.h"
16#include "comm/Loops.h"
17#include "fd.h"
18#include "fde.h"
19#include "globals.h"
20#include "ICP.h"
21#include "mgr/Registration.h"
22#include "SquidConfig.h"
23#include "StatCounters.h"
24#include "Store.h"
25
26#include <cerrno>
27#if HAVE_POLL_H
28#include <poll.h>
29#endif
30
31/* Needed for poll() on Linux at least */
32#if USE_POLL
33#ifndef POLLRDNORM
34#define POLLRDNORM POLLIN
35#endif
36#ifndef POLLWRNORM
37#define POLLWRNORM POLLOUT
38#endif
39#endif
40
41static int MAX_POLL_TIME = 1000; /* see also Comm::QuickPollRequired() */
42
43#ifndef howmany
44#define howmany(x, y) (((x)+((y)-1))/(y))
45#endif
46#ifndef NBBY
47#define NBBY 8
48#endif
49#define FD_MASK_BYTES sizeof(fd_mask)
50#define FD_MASK_BITS (FD_MASK_BYTES*NBBY)
51
52/* STATIC */
53static int fdIsTcpListen(int fd);
54static int fdIsUdpListen(int fd);
55static int fdIsDns(int fd);
57static int comm_check_incoming_poll_handlers(int nfds, int *fds);
58static void comm_poll_dns_incoming(void);
59
60void
61Comm::SetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout)
62{
63 fde *F = &fd_table[fd];
64 assert(fd >= 0);
65 assert(F->flags.open || (!handler && !client_data && !timeout));
66 debugs(5, 5, "FD " << fd << ", type=" << type <<
67 ", handler=" << handler << ", client_data=" << client_data <<
68 ", timeout=" << timeout);
69
70 if (type & COMM_SELECT_READ) {
71 F->read_handler = handler;
72 F->read_data = client_data;
73 }
74
75 if (type & COMM_SELECT_WRITE) {
76 F->write_handler = handler;
77 F->write_data = client_data;
78 }
79
80 if (timeout)
81 F->timeout = squid_curtime + timeout;
82}
83
84static int
86{
87 if (icpIncomingConn != nullptr && icpIncomingConn->fd == fd)
88 return 1;
89
90 if (icpOutgoingConn != nullptr && icpOutgoingConn->fd == fd)
91 return 1;
92
93 return 0;
94}
95
96static int
97fdIsDns(int fd)
98{
99 if (fd == DnsSocketA)
100 return 1;
101
102 if (fd == DnsSocketB)
103 return 1;
104
105 return 0;
106}
107
108static int
110{
111 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
112 if (s->listenConn != nullptr && s->listenConn->fd == fd)
113 return 1;
114 }
115
116 return 0;
117}
118
119static int
121{
122 int i;
123 int fd;
124 PF *hdl = nullptr;
125 int npfds;
126
127 struct pollfd pfds[3 + MAXTCPLISTENPORTS];
129
130 for (i = npfds = 0; i < nfds; ++i) {
131 int events;
132 fd = fds[i];
133 events = 0;
134
135 if (fd_table[fd].read_handler)
136 events |= POLLRDNORM;
137
138 if (fd_table[fd].write_handler)
139 events |= POLLWRNORM;
140
141 if (events) {
142 pfds[npfds].fd = fd;
143 pfds[npfds].events = events;
144 pfds[npfds].revents = 0;
145 ++npfds;
146 }
147 }
148
149 if (!nfds)
150 return -1;
151
154
155 if (poll(pfds, npfds, 0) < 1)
157
158 for (i = 0; i < npfds; ++i) {
159 int revents;
160
161 if (((revents = pfds[i].revents) == 0) || ((fd = pfds[i].fd) == -1))
162 continue;
163
164 if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) {
165 if ((hdl = fd_table[fd].read_handler)) {
166 fd_table[fd].read_handler = nullptr;
167 hdl(fd, fd_table[fd].read_data);
168 } else if (pfds[i].events & POLLRDNORM)
169 debugs(5, DBG_IMPORTANT, "comm_poll_incoming: FD " << fd << " NULL read handler");
170 }
171
172 if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) {
173 if ((hdl = fd_table[fd].write_handler)) {
174 fd_table[fd].write_handler = nullptr;
175 hdl(fd, fd_table[fd].write_data);
176 } else if (pfds[i].events & POLLWRNORM)
177 debugs(5, DBG_IMPORTANT, "comm_poll_incoming: FD " << fd << " NULL write_handler");
178 }
179 }
180
182}
183
184static void
186{
187 int nfds = 0;
188 int fds[2];
189
191 fds[nfds] = icpIncomingConn->fd;
192 ++nfds;
193 }
194
196 fds[nfds] = icpOutgoingConn->fd;
197 ++nfds;
198 }
199
201 auto n = comm_check_incoming_poll_handlers(nfds, fds);
203 }
204}
205
206static void
208{
209 int nfds = 0;
210 int fds[MAXTCPLISTENPORTS];
211
212 // XXX: only poll sockets that won't be deferred. But how do we identify them?
213
214 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
215 if (Comm::IsConnOpen(s->listenConn)) {
216 fds[nfds] = s->listenConn->fd;
217 ++nfds;
218 }
219 }
220
222 auto n = comm_check_incoming_poll_handlers(nfds, fds);
224 }
225}
226
227/* poll all sockets; call handlers for those that are ready. */
229Comm::DoSelect(int msec)
230{
231 struct pollfd pfds[SQUID_MAXFD];
232
233 PF *hdl = nullptr;
234 int fd;
235 int maxfd;
236 unsigned long nfds;
237 unsigned long npending;
238 int num;
239 int calldns = 0, calludp = 0, calltcp = 0;
240 double timeout = current_dtime + (msec / 1000.0);
241
242 do {
243 double start;
245 start = current_dtime;
246
249
252
255
256 calldns = calludp = calltcp = 0;
257
258 nfds = 0;
259
260 npending = 0;
261
262 maxfd = Biggest_FD + 1;
263
264 for (int i = 0; i < maxfd; ++i) {
265 int events;
266 events = 0;
267 /* Check each open socket for a handler. */
268
269 if (fd_table[i].read_handler)
270 events |= POLLRDNORM;
271
272 if (fd_table[i].write_handler)
273 events |= POLLWRNORM;
274
275 if (events) {
276 pfds[nfds].fd = i;
277 pfds[nfds].events = events;
278 pfds[nfds].revents = 0;
279 ++nfds;
280
281 if ((events & POLLRDNORM) && fd_table[i].flags.read_pending)
282 ++npending;
283 }
284 }
285
286 if (npending)
287 msec = 0;
288
289 if (msec > MAX_POLL_TIME)
290 msec = MAX_POLL_TIME;
291
292 /* nothing to do
293 *
294 * Note that this will only ever trigger when there are no log files
295 * and stdout/err/in are all closed too.
296 */
297 if (nfds == 0 && npending == 0) {
298 if (shutting_down)
299 return Comm::SHUTDOWN;
300 else
301 return Comm::IDLE;
302 }
303
304 for (;;) {
306 num = poll(pfds, nfds, msec);
307 int xerrno = errno;
309
310 if (num >= 0 || npending > 0)
311 break;
312
313 if (ignoreErrno(xerrno))
314 continue;
315
316 debugs(5, DBG_CRITICAL, MYNAME << "poll failure: " << xstrerr(xerrno));
317
318 assert(xerrno != EINVAL);
319
320 return Comm::COMM_ERROR;
321
322 /* NOTREACHED */
323 }
324
326
327 debugs(5, num ? 5 : 8, "comm_poll: " << num << "+" << npending << " FDs ready");
329
330 if (num == 0 && npending == 0)
331 continue;
332
333 /* scan each socket but the accept socket. Poll this
334 * more frequently to minimize losses due to the 5 connect
335 * limit in SunOS */
336
337 for (size_t loopIndex = 0; loopIndex < nfds; ++loopIndex) {
338 fde *F;
339 int revents = pfds[loopIndex].revents;
340 fd = pfds[loopIndex].fd;
341
342 if (fd == -1)
343 continue;
344
345 if (fd_table[fd].flags.read_pending)
346 revents |= POLLIN;
347
348 if (revents == 0)
349 continue;
350
351 if (fdIsUdpListen(fd)) {
352 calludp = 1;
353 continue;
354 }
355
356 if (fdIsDns(fd)) {
357 calldns = 1;
358 continue;
359 }
360
361 if (fdIsTcpListen(fd)) {
362 calltcp = 1;
363 continue;
364 }
365
366 F = &fd_table[fd];
367
368 if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) {
369 debugs(5, 6, "comm_poll: FD " << fd << " ready for reading");
370
371 if ((hdl = F->read_handler)) {
372 F->read_handler = nullptr;
373 hdl(fd, F->read_data);
375
378
381
384 }
385 }
386
387 if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) {
388 debugs(5, 6, "comm_poll: FD " << fd << " ready for writing");
389
390 if ((hdl = F->write_handler)) {
391 F->write_handler = nullptr;
392 hdl(fd, F->write_data);
394
397
400
403 }
404 }
405
406 if (revents & POLLNVAL) {
408 debugs(5, DBG_CRITICAL, "WARNING: FD " << fd << " has handlers, but it's invalid.");
409 debugs(5, DBG_CRITICAL, "FD " << fd << " is a " << fdTypeStr[F->type]);
410 debugs(5, DBG_CRITICAL, "--> " << F->desc);
411 debugs(5, DBG_CRITICAL, "tmout:" << F->timeoutHandler << "read:" <<
412 F->read_handler << " write:" << F->write_handler);
413
414 for (ch = F->closeHandler; ch != nullptr; ch = ch->Next())
415 debugs(5, DBG_CRITICAL, " close handler: " << ch);
416
417 if (F->closeHandler != nullptr) {
419 } else if (F->timeoutHandler != nullptr) {
420 debugs(5, DBG_CRITICAL, "comm_poll: Calling Timeout Handler");
422 }
423
424 F->closeHandler = nullptr;
425 F->timeoutHandler = nullptr;
426 F->read_handler = nullptr;
427 F->write_handler = nullptr;
428
429 if (F->flags.open)
430 fd_close(fd);
431 }
432 }
433
434 if (calludp)
436
437 if (calldns)
439
440 if (calltcp)
442
444
446
447 return Comm::OK;
448 } while (timeout > current_dtime);
449
450 debugs(5, 8, "comm_poll: time out: " << squid_curtime << ".");
451
452 return Comm::TIMEOUT;
453}
454
455static void
457{
458 int nfds = 0;
459 int fds[2];
460
461 if (DnsSocketA >= 0) {
462 fds[nfds] = DnsSocketA;
463 ++nfds;
464 }
465
466 if (DnsSocketB >= 0) {
467 fds[nfds] = DnsSocketB;
468 ++nfds;
469 }
470
472 auto n = comm_check_incoming_poll_handlers(nfds, fds);
474 }
475}
476
477static void
479{
480 Mgr::RegisterAction("comm_poll_incoming",
481 "comm_incoming() stats",
482 commIncomingStats, 0, 1);
483}
484
485void
487{
489}
490
491static void
493{
494 storeAppendPrintf(sentry, "Current incoming_udp_interval: %d\n",
496 storeAppendPrintf(sentry, "Current incoming_dns_interval: %d\n",
498 storeAppendPrintf(sentry, "Current incoming_tcp_interval: %d\n",
500 storeAppendPrintf(sentry, "\n");
501 storeAppendPrintf(sentry, "Histogram of events per incoming socket type\n");
502 storeAppendPrintf(sentry, "ICP Messages handled per comm_poll_udp_incoming() call:\n");
504 storeAppendPrintf(sentry, "DNS Messages handled per comm_poll_dns_incoming() call:\n");
506 storeAppendPrintf(sentry, "HTTP Messages handled per comm_poll_tcp_incoming() call:\n");
508}
509
510/* Called by async-io or diskd to speed up the polling */
511void
513{
514 MAX_POLL_TIME = 10;
515}
516
517#endif /* USE_POLL */
518
#define ScheduleCallHere(call)
Definition AsyncCall.h:166
struct pollfd * pfds
Definition ModDevPoll.cc:68
static int fdIsDns(int fd)
Definition ModPoll.cc:97
static void commPollRegisterWithCacheManager(void)
Definition ModPoll.cc:478
static int comm_check_incoming_poll_handlers(int nfds, int *fds)
Definition ModPoll.cc:120
static int MAX_POLL_TIME
Definition ModPoll.cc:41
static int fdIsTcpListen(int fd)
Definition ModPoll.cc:109
#define POLLRDNORM
Definition ModPoll.cc:34
#define POLLWRNORM
Definition ModPoll.cc:37
static void comm_poll_dns_incoming(void)
Definition ModPoll.cc:456
static void comm_poll_tcp_incoming(void)
Definition ModPoll.cc:207
static OBJH commIncomingStats
Definition ModPoll.cc:56
static int fdIsUdpListen(int fd)
Definition ModPoll.cc:85
static void comm_poll_udp_incoming(void)
Definition ModPoll.cc:185
time_t squid_curtime
AnyP::PortCfgPointer HttpPortList
list of Squid http(s)_port configured
Definition PortCfg.cc:22
#define MAXTCPLISTENPORTS
Definition PortCfg.h:86
class SquidConfig Config
StatCounters statCounter
StatHistBinDumper statHistIntDumper
Definition StatHist.h:119
#define assert(EX)
Definition assert.h:17
#define SQUID_MAXFD
Definition autoconf.h:1442
AsyncCall::Pointer & Next()
Definition AsyncCall.h:66
bool check()
Definition Incoming.h:86
bool startPolling(int n)
Definition Incoming.h:68
void finishPolling(int, SquidConfig::CommIncoming::Measure &)
Definition Incoming.cc:15
StatHist history
Definition Incoming.h:104
static const int Factor
Definition Incoming.h:50
struct SquidConfig::CommIncoming comm_incoming
double select_time
StatHist select_fds_hist
Comm::Incoming comm_udp
unsigned long int select_loops
struct StatCounters::@112 syscalls
Comm::Incoming comm_dns
Comm::Incoming comm_tcp
void count(double val)
Definition StatHist.cc:55
void dump(StoreEntry *sentry, StatHistBinDumper *bd) const
Definition StatHist.cc:171
Definition fde.h:52
unsigned int type
Definition fde.h:105
char desc[FD_DESC_SZ]
Definition fde.h:115
AsyncCall::Pointer timeoutHandler
Definition fde.h:153
PF * read_handler
Definition fde.h:149
void * write_data
Definition fde.h:152
struct fde::_fde_flags flags
time_t timeout
Definition fde.h:154
AsyncCall::Pointer closeHandler
Definition fde.h:157
void * read_data
Definition fde.h:150
PF * write_handler
Definition fde.h:151
void PF(int, void *)
Definition forward.h:18
void fd_close(const int fd)
Definition minimal.cc:21
void commCallCloseHandlers(int fd)
Definition comm.cc:744
int ignoreErrno(int ierrno)
Definition comm.cc:1407
#define MYNAME
Definition Stream.h:219
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
#define COMM_SELECT_READ
Definition defines.h:24
#define COMM_SELECT_WRITE
Definition defines.h:25
const char * fdTypeStr[]
Definition fd.cc:34
#define fd_table
Definition fde.h:189
int DnsSocketB
int DnsSocketA
int shutting_down
int Biggest_FD
int incoming_sockets_accepted
Comm::ConnectionPointer icpOutgoingConn
Definition icp_v2.cc:101
Comm::ConnectionPointer icpIncomingConn
Definition icp_v2.cc:99
void OBJH(StoreEntry *)
Definition forward.h:44
void QuickPollRequired(void)
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition Connection.cc:27
Flag
Definition Flag.h:15
@ SHUTDOWN
Definition Flag.h:19
@ OK
Definition Flag.h:16
@ IDLE
Definition Flag.h:20
@ TIMEOUT
Definition Flag.h:18
@ COMM_ERROR
Definition Flag.h:17
Comm::Flag DoSelect(int)
Do poll and trigger callback functions as appropriate.
void SelectLoopInit(void)
Initialize the module on Squid startup.
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
void RegisterAction(char const *action, char const *desc, OBJH *handler, Protected, Atomic, Format)
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition store.cc:855
struct SquidConfig::CommIncoming::Measure udp
struct SquidConfig::CommIncoming::Measure dns
struct SquidConfig::CommIncoming::Measure tcp
bool open
Definition fde.h:118
double current_dtime
the current UNIX time in seconds (with microsecond precision)
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
const char * xstrerr(int error)
Definition xstrerror.cc:83