]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/comm_select.cc
4 * $Id: comm_select.cc,v 1.5 1998/08/14 09:22:33 wessels Exp $
6 * DEBUG: section 5 Socket Functions
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
39 #define MAX_POLL_TIME 10
41 #define MAX_POLL_TIME 1000
46 static int examine_select(fd_set
*, fd_set
*);
48 static int fdIsHttp(int fd
);
49 static int fdIsIcp(int fd
);
50 static int commDeferRead(int fd
);
51 static void checkTimeouts(void);
52 static OBJH commIncomingStats
;
54 static int comm_check_incoming_poll_handlers(int nfds
, int *fds
);
56 static int comm_check_incoming_select_handlers(int nfds
, int *fds
);
59 static struct timeval zero_tv
;
62 * Automatic tuning for incoming requests:
64 * INCOMING sockets are the ICP and HTTP ports. We need to check these
65 * fairly regularly, but how often? When the load increases, we
66 * want to check the incoming sockets more often. If we have a lot
67 * of incoming ICP, then we need to check these sockets more than
68 * if we just have HTTP.
70 * The variables 'incoming_icp_interval' and 'incoming_http_interval'
71 * determine how many normal I/O events to process before checking
72 * incoming sockets again. Note we store the incoming_interval
73 * multipled by a factor of (2^INCOMING_FACTOR) to have some
74 * pseudo-floating point precision.
76 * The variable 'icp_io_events' and 'http_io_events' counts how many normal
77 * I/O events have been processed since the last check on the incoming
78 * sockets. When io_events > incoming_interval, its time to check incoming
81 * Every time we check incoming sockets, we count how many new messages
82 * or connections were processed. This is used to adjust the
83 * incoming_interval for the next iteration. The new incoming_interval
84 * is calculated as the current incoming_interval plus what we would
85 * like to see as an average number of events minus the number of
86 * events just processed.
88 * incoming_interval = incoming_interval + 1 - number_of_events_processed
90 * There are separate incoming_interval counters for both HTTP and ICP events
92 * You can see the current values of the incoming_interval's, as well as
93 * a histogram of 'incoming_events' by asking the cache manager
94 * for 'comm_incoming', e.g.:
96 * % ./client mgr:comm_incoming
100 * - We have MAX_INCOMING_INTEGER as a magic upper limit on
101 * incoming_interval for both types of sockets. At the
102 * largest value the cache will effectively be idling.
104 * - The higher the INCOMING_FACTOR, the slower the algorithm will
105 * respond to load spikes/increases/decreases in demand. A value
106 * between 3 and 8 is recommended.
109 #define MAX_INCOMING_INTEGER 256
110 #define INCOMING_FACTOR 5
111 #define MAX_INCOMING_INTERVAL (MAX_INCOMING_INTEGER << INCOMING_FACTOR)
112 static int icp_io_events
= 0;
113 static int http_io_events
= 0;
114 static int incoming_icp_interval
= 16 << INCOMING_FACTOR
;
115 static int incoming_http_interval
= 16 << INCOMING_FACTOR
;
116 #define commCheckICPIncoming (++icp_io_events > (incoming_icp_interval>> INCOMING_FACTOR))
117 #define commCheckHTTPIncoming (++http_io_events > (incoming_http_interval>> INCOMING_FACTOR))
120 commDeferRead(int fd
)
122 fde
*F
= &fd_table
[fd
];
123 if (F
->defer_check
== NULL
)
125 return F
->defer_check(fd
, F
->defer_data
);
131 if (fd
== theInIcpConnection
)
133 if (fd
== theOutIcpConnection
)
142 for (j
= 0; j
< NHttpSockets
; j
++) {
143 if (fd
== HttpSockets
[j
])
151 comm_check_incoming_poll_handlers(int nfds
, int *fds
)
158 struct pollfd pfds
[3 + MAXHTTPPORTS
];
159 for (i
= npfds
= 0; i
< nfds
; i
++) {
163 if (fd_table
[fd
].read_handler
)
164 events
|= POLLRDNORM
;
165 if (fd_table
[fd
].write_handler
)
166 events
|= POLLWRNORM
;
169 pfds
[npfds
].events
= events
;
170 pfds
[npfds
].revents
= 0;
176 #if !ALARM_UPDATES_TIME
179 if (poll(pfds
, npfds
, 0) < 1)
181 for (i
= 0; i
< npfds
; i
++) {
183 if (((revents
= pfds
[i
].revents
) == 0) || ((fd
= pfds
[i
].fd
) == -1))
185 if (revents
& (POLLRDNORM
| POLLIN
| POLLHUP
| POLLERR
)) {
186 if ((hdl
= fd_table
[fd
].read_handler
)) {
187 fd_table
[fd
].read_handler
= NULL
;
190 debug(5, 1) ("comm_poll_incoming: NULL read handler\n");
192 if (revents
& (POLLWRNORM
| POLLOUT
| POLLHUP
| POLLERR
)) {
193 if ((hdl
= fd_table
[fd
].write_handler
)) {
194 fd_table
[fd
].write_handler
= NULL
;
197 debug(5, 1) ("comm_poll_incoming: NULL write handler\n");
204 comm_poll_icp_incoming(void)
210 if (theInIcpConnection
>= 0)
211 fds
[nfds
++] = theInIcpConnection
;
212 if (theInIcpConnection
!= theOutIcpConnection
)
213 if (theOutIcpConnection
>= 0)
214 fds
[nfds
++] = theOutIcpConnection
;
217 nevents
= comm_check_incoming_poll_handlers(nfds
, fds
);
218 incoming_icp_interval
= incoming_icp_interval
+ 1 - nevents
;
219 if (incoming_icp_interval
< 0)
220 incoming_icp_interval
= 0;
221 if (incoming_icp_interval
> MAX_INCOMING_INTERVAL
)
222 incoming_icp_interval
= MAX_INCOMING_INTERVAL
;
223 if (nevents
> INCOMING_ICP_MAX
)
224 nevents
= INCOMING_ICP_MAX
;
225 statHistCount(&Counter
.comm_icp_incoming
, nevents
);
229 comm_poll_http_incoming(void)
232 int fds
[MAXHTTPPORTS
];
236 for (j
= 0; j
< NHttpSockets
; j
++) {
237 if (HttpSockets
[j
] < 0)
239 if (commDeferRead(HttpSockets
[j
]))
241 fds
[nfds
++] = HttpSockets
[j
];
243 nevents
= comm_check_incoming_poll_handlers(nfds
, fds
);
244 incoming_http_interval
= incoming_http_interval
+ 1 - nevents
;
245 if (incoming_http_interval
< 0)
246 incoming_http_interval
= 0;
247 if (incoming_http_interval
> MAX_INCOMING_INTERVAL
)
248 incoming_http_interval
= MAX_INCOMING_INTERVAL
;
249 if (nevents
> INCOMING_HTTP_MAX
)
250 nevents
= INCOMING_HTTP_MAX
;
251 statHistCount(&Counter
.comm_http_incoming
, nevents
);
254 /* poll all sockets; call handlers for those that are ready. */
258 struct pollfd pfds
[SQUID_MAXFD
];
265 int callicp
= 0, callhttp
= 0;
266 static time_t last_timeout
= 0;
267 double timeout
= current_dtime
+ (msec
/ 1000.0);
269 #if !ALARM_UPDATES_TIME
275 if (commCheckICPIncoming
)
276 comm_poll_icp_incoming();
277 if (commCheckHTTPIncoming
)
278 comm_poll_http_incoming();
280 if (squid_curtime
> delay_pools_last_update
) {
281 delayPoolsUpdate(squid_curtime
- delay_pools_last_update
);
282 delay_pools_last_update
= squid_curtime
;
285 callicp
= callhttp
= 0;
287 maxfd
= Biggest_FD
+ 1;
288 for (i
= 0; i
< maxfd
; i
++) {
291 /* Check each open socket for a handler. */
292 if (fd_table
[i
].read_handler
&& !commDeferRead(i
))
293 events
|= POLLRDNORM
;
294 if (fd_table
[i
].write_handler
)
295 events
|= POLLWRNORM
;
298 pfds
[nfds
].events
= events
;
299 pfds
[nfds
].revents
= 0;
304 assert(shutting_down
);
305 return COMM_SHUTDOWN
;
307 if (msec
> MAX_POLL_TIME
)
308 msec
= MAX_POLL_TIME
;
310 num
= poll(pfds
, nfds
, msec
);
311 Counter
.select_loops
++;
314 if (ignoreErrno(errno
))
316 debug(5, 0) ("comm_poll: poll failure: %s\n", xstrerror());
317 assert(errno
!= EINVAL
);
321 debug(5, num
? 5 : 8) ("comm_poll: %d sockets ready\n", num
);
322 /* Check timeout handlers ONCE each second. */
323 if (squid_curtime
> last_timeout
) {
324 last_timeout
= squid_curtime
;
329 /* scan each socket but the accept socket. Poll this
330 * more frequently to minimize losses due to the 5 connect
332 for (i
= 0; i
< nfds
; i
++) {
334 if (((revents
= pfds
[i
].revents
) == 0) || ((fd
= pfds
[i
].fd
) == -1))
344 if (revents
& (POLLRDNORM
| POLLIN
| POLLHUP
| POLLERR
)) {
345 debug(5, 6) ("comm_poll: FD %d ready for reading\n", fd
);
346 if ((hdl
= fd_table
[fd
].read_handler
)) {
347 fd_table
[fd
].read_handler
= NULL
;
348 hdl(fd
, fd_table
[fd
].read_data
);
350 if (commCheckICPIncoming
)
351 comm_poll_icp_incoming();
352 if (commCheckHTTPIncoming
)
353 comm_poll_http_incoming();
355 if (revents
& (POLLWRNORM
| POLLOUT
| POLLHUP
| POLLERR
)) {
356 debug(5, 5) ("comm_poll: FD %d ready for writing\n", fd
);
357 if ((hdl
= fd_table
[fd
].write_handler
)) {
358 fd_table
[fd
].write_handler
= NULL
;
359 hdl(fd
, fd_table
[fd
].write_data
);
361 if (commCheckICPIncoming
)
362 comm_poll_icp_incoming();
363 if (commCheckHTTPIncoming
)
364 comm_poll_http_incoming();
366 if (revents
& POLLNVAL
) {
368 fde
*F
= &fd_table
[fd
];
369 debug(5, 0) ("WARNING: FD %d has handlers, but it's invalid.\n", fd
);
370 debug(5, 0) ("FD %d is a %s\n", fd
, fdTypeStr
[fd_table
[fd
].type
]);
371 debug(5, 0) ("--> %s\n", fd_table
[fd
].desc
);
372 debug(5, 0) ("tmout:%p read:%p write:%p\n",
376 for (ch
= F
->close_handler
; ch
; ch
= ch
->next
)
377 debug(5, 0) (" close handler: %p\n", ch
->handler
);
378 if (F
->close_handler
) {
379 commCallCloseHandlers(fd
);
380 } else if (F
->timeout_handler
) {
381 debug(5, 0) ("comm_poll: Calling Timeout Handler\n");
382 F
->timeout_handler(fd
, F
->timeout_data
);
384 F
->close_handler
= NULL
;
385 F
->timeout_handler
= NULL
;
386 F
->read_handler
= NULL
;
387 F
->write_handler
= NULL
;
393 comm_poll_icp_incoming();
395 comm_poll_http_incoming();
397 } while (timeout
> current_dtime
);
398 debug(5, 8) ("comm_poll: time out: %d.\n", squid_curtime
);
405 comm_check_incoming_select_handlers(int nfds
, int *fds
)
415 FD_ZERO(&write_mask
);
416 for (i
= 0; i
< nfds
; i
++) {
418 if (fd_table
[fd
].read_handler
) {
419 FD_SET(fd
, &read_mask
);
423 if (fd_table
[fd
].write_handler
) {
424 FD_SET(fd
, &write_mask
);
431 #if !ALARM_UPDATES_TIME
434 if (select(maxfd
, &read_mask
, &write_mask
, NULL
, &zero_tv
) < 1)
436 for (i
= 0; i
< nfds
; i
++) {
438 if (FD_ISSET(fd
, &read_mask
)) {
439 if ((hdl
= fd_table
[fd
].read_handler
) != NULL
) {
440 fd_table
[fd
].read_handler
= NULL
;
443 debug(5, 1) ("comm_select_incoming: NULL read handler\n");
446 if (FD_ISSET(fd
, &write_mask
)) {
447 if ((hdl
= fd_table
[fd
].write_handler
) != NULL
) {
448 fd_table
[fd
].write_handler
= NULL
;
451 debug(5, 1) ("comm_select_incoming: NULL write handler\n");
459 comm_select_icp_incoming(void)
465 if (theInIcpConnection
>= 0)
466 fds
[nfds
++] = theInIcpConnection
;
467 if (theInIcpConnection
!= theOutIcpConnection
)
468 if (theOutIcpConnection
>= 0)
469 fds
[nfds
++] = theOutIcpConnection
;
472 nevents
= comm_check_incoming_select_handlers(nfds
, fds
);
473 incoming_icp_interval
= incoming_icp_interval
+ 1 - nevents
;
474 if (incoming_icp_interval
< 0)
475 incoming_icp_interval
= 0;
476 if (incoming_icp_interval
> MAX_INCOMING_INTERVAL
)
477 incoming_icp_interval
= MAX_INCOMING_INTERVAL
;
478 if (nevents
> INCOMING_ICP_MAX
)
479 nevents
= INCOMING_ICP_MAX
;
480 statHistCount(&Counter
.comm_icp_incoming
, nevents
);
484 comm_select_http_incoming(void)
487 int fds
[MAXHTTPPORTS
];
491 for (j
= 0; j
< NHttpSockets
; j
++) {
492 if (HttpSockets
[j
] < 0)
494 if (commDeferRead(HttpSockets
[j
]))
496 fds
[nfds
++] = HttpSockets
[j
];
498 nevents
= comm_check_incoming_select_handlers(nfds
, fds
);
499 incoming_http_interval
= incoming_http_interval
+ 1 - nevents
;
500 if (incoming_http_interval
< 0)
501 incoming_http_interval
= 0;
502 if (incoming_http_interval
> MAX_INCOMING_INTERVAL
)
503 incoming_http_interval
= MAX_INCOMING_INTERVAL
;
504 if (nevents
> INCOMING_HTTP_MAX
)
505 nevents
= INCOMING_HTTP_MAX
;
506 statHistCount(&Counter
.comm_http_incoming
, nevents
);
509 /* Select on all sockets; call handlers for those that are ready. */
511 comm_select(int msec
)
521 int callicp
= 0, callhttp
= 0;
522 static time_t last_timeout
= 0;
523 struct timeval poll_time
;
524 double timeout
= current_dtime
+ (msec
/ 1000.0);
526 #if !ALARM_UPDATES_TIME
534 if (commCheckICPIncoming
)
535 comm_select_icp_incoming();
536 if (commCheckHTTPIncoming
)
537 comm_select_http_incoming();
539 if (squid_curtime
> delay_pools_last_update
) {
540 delayPoolsUpdate(squid_curtime
- delay_pools_last_update
);
541 delay_pools_last_update
= squid_curtime
;
544 callicp
= callhttp
= 0;
546 maxfd
= Biggest_FD
+ 1;
547 for (i
= 0; i
< maxfd
; i
++) {
548 /* Check each open socket for a handler. */
549 if (fd_table
[i
].read_handler
&& !commDeferRead(i
)) {
553 if (fd_table
[i
].write_handler
) {
555 FD_SET(i
, &writefds
);
559 assert(shutting_down
);
560 return COMM_SHUTDOWN
;
562 if (msec
> MAX_POLL_TIME
)
563 msec
= MAX_POLL_TIME
;
565 poll_time
.tv_sec
= msec
/ 1000;
566 poll_time
.tv_usec
= (msec
% 1000) * 1000;
567 num
= select(maxfd
, &readfds
, &writefds
, NULL
, &poll_time
);
568 Counter
.select_loops
++;
571 if (ignoreErrno(errno
))
573 debug(50, 0) ("comm_select: select failure: %s\n",
575 examine_select(&readfds
, &writefds
);
581 debug(5, num
? 5 : 8) ("comm_select: %d sockets ready at %d\n",
582 num
, (int) squid_curtime
);
583 /* Check lifetime and timeout handlers ONCE each second.
584 * Replaces brain-dead check every time through the loop! */
585 if (squid_curtime
> last_timeout
) {
586 last_timeout
= squid_curtime
;
591 /* scan each socket but the accept socket. Poll this
592 * more frequently to minimize losses due to the 5 connect
594 for (fd
= 0; fd
< maxfd
; fd
++) {
595 if (!FD_ISSET(fd
, &readfds
) && !FD_ISSET(fd
, &writefds
))
605 if (FD_ISSET(fd
, &readfds
)) {
606 debug(5, 6) ("comm_select: FD %d ready for reading\n", fd
);
607 if (fd_table
[fd
].read_handler
) {
608 hdl
= fd_table
[fd
].read_handler
;
609 fd_table
[fd
].read_handler
= NULL
;
610 hdl(fd
, fd_table
[fd
].read_data
);
612 if (commCheckICPIncoming
)
613 comm_select_icp_incoming();
614 if (commCheckHTTPIncoming
)
615 comm_select_http_incoming();
617 if (FD_ISSET(fd
, &writefds
)) {
618 debug(5, 5) ("comm_select: FD %d ready for writing\n", fd
);
619 if (fd_table
[fd
].write_handler
) {
620 hdl
= fd_table
[fd
].write_handler
;
621 fd_table
[fd
].write_handler
= NULL
;
622 hdl(fd
, fd_table
[fd
].write_data
);
624 if (commCheckICPIncoming
)
625 comm_select_icp_incoming();
626 if (commCheckHTTPIncoming
)
627 comm_select_http_incoming();
631 comm_select_icp_incoming();
633 comm_select_http_incoming();
635 } while (timeout
> current_dtime
);
636 debug(5, 8) ("comm_select: time out: %d\n", (int) squid_curtime
);
642 comm_select_init(void)
646 cachemgrRegister("comm_incoming",
647 "comm_incoming() stats",
648 commIncomingStats
, 0, 1);
653 * examine_select - debug routine.
655 * I spend the day chasing this core dump that occurs when both the client
656 * and the server side of a cache fetch simultaneoulsy abort the
657 * connection. While I haven't really studied the code to figure out how
658 * it happens, the snippet below may prevent the cache from exitting:
660 * Call this from where the select loop fails.
663 examine_select(fd_set
* readfds
, fd_set
* writefds
)
670 close_handler
*ch
= NULL
;
672 debug(5, 0) ("examine_select: Examining open file descriptors...\n");
673 for (fd
= 0; fd
< Squid_MaxFD
; fd
++) {
676 tv
.tv_sec
= tv
.tv_usec
= 0;
677 if (FD_ISSET(fd
, readfds
))
679 else if (FD_ISSET(fd
, writefds
))
680 FD_SET(fd
, &write_x
);
683 num
= select(Squid_MaxFD
, &read_x
, &write_x
, NULL
, &tv
);
685 debug(5, 5) ("FD %d is valid.\n", fd
);
689 debug(5, 0) ("FD %d: %s\n", fd
, xstrerror());
690 debug(5, 0) ("WARNING: FD %d has handlers, but it's invalid.\n", fd
);
691 debug(5, 0) ("FD %d is a %s called '%s'\n",
693 fdTypeStr
[fd_table
[fd
].type
],
695 debug(5, 0) ("tmout:%p read:%p write:%p\n",
699 for (ch
= F
->close_handler
; ch
; ch
= ch
->next
)
700 debug(5, 0) (" close handler: %p\n", ch
->handler
);
701 if (F
->close_handler
) {
702 commCallCloseHandlers(fd
);
703 } else if (F
->timeout_handler
) {
704 debug(5, 0) ("examine_select: Calling Timeout Handler\n");
705 F
->timeout_handler(fd
, F
->timeout_data
);
707 F
->close_handler
= NULL
;
708 F
->timeout_handler
= NULL
;
709 F
->read_handler
= NULL
;
710 F
->write_handler
= NULL
;
712 FD_CLR(fd
, writefds
);
724 for (fd
= 0; fd
<= Biggest_FD
; fd
++) {
726 if (F
->open
!= FD_OPEN
)
730 if (F
->timeout
> squid_curtime
)
732 debug(5, 5) ("checkTimeouts: FD %d Expired\n", fd
);
733 if (F
->timeout_handler
) {
734 debug(5, 5) ("checkTimeouts: FD %d: Call timeout handler\n", fd
);
735 callback
= F
->timeout_handler
;
736 F
->timeout_handler
= NULL
;
737 callback(fd
, F
->timeout_data
);
739 debug(5, 5) ("checkTimeouts: FD %d: Forcing comm_close()\n", fd
);
746 commIncomingStats(StoreEntry
* sentry
)
748 StatCounters
*f
= &Counter
;
749 storeAppendPrintf(sentry
, "Current incoming_icp_interval: %d\n",
750 incoming_icp_interval
>> INCOMING_FACTOR
);
751 storeAppendPrintf(sentry
, "Current incoming_http_interval: %d\n",
752 incoming_http_interval
>> INCOMING_FACTOR
);
753 storeAppendPrintf(sentry
, "\n");
754 storeAppendPrintf(sentry
, "Histogram of events per incoming socket type\n");
756 storeAppendPrintf(sentry
, "ICP Messages handled per comm_poll_icp_incoming() call:\n");
758 storeAppendPrintf(sentry
, "ICP Messages handled per comm_select_icp_incoming() call:\n");
760 statHistDump(&f
->comm_icp_incoming
, sentry
, statHistIntDumper
);
762 storeAppendPrintf(sentry
, "HTTP Messages handled per comm_poll_http_incoming() call:\n");
764 storeAppendPrintf(sentry
, "HTTP Messages handled per comm_select_http_incoming() call:\n");
766 statHistDump(&f
->comm_http_incoming
, sentry
, statHistIntDumper
);