--------------------- PatchSet 5319 Date: 2002/10/09 08:12:15 Author: adri Branch: commloops Tag: (none) Log: * add in a routine to check whether a read is pending * add in a routine to cancel a pending read, assuming that one isn't queued * modify the pconn code to use the new io framework via a hack. The main issue is that we need to have a read registered to check for close/EOF or something being read - but when the socket is handed over to forward.c it needs to not have a read handler. I can't think of a clean way of doing this. Members: src/comm.c:1.21.4.22->1.21.4.23 src/comm.h:1.1.2.2->1.1.2.3 src/pconn.c:1.6.32.2->1.6.32.3 Index: squid/src/comm.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/comm.c,v retrieving revision 1.21.4.22 retrieving revision 1.21.4.23 diff -u -r1.21.4.22 -r1.21.4.23 --- squid/src/comm.c 8 Oct 2002 15:03:38 -0000 1.21.4.22 +++ squid/src/comm.c 9 Oct 2002 08:12:15 -0000 1.21.4.23 @@ -1,6 +1,6 @@ /* - * $Id: comm.c,v 1.21.4.22 2002/10/08 15:03:38 rbcollins Exp $ + * $Id: comm.c,v 1.21.4.23 2002/10/09 08:12:15 adri Exp $ * * DEBUG: section 5 Socket Functions * AUTHOR: Harvest Derived @@ -444,6 +444,81 @@ } +/* + * Empty the read buffers + * + * This is a magical routine that empties the read buffers. + * Under some platforms (Linux) if a buffer has data in it before + * you call close(), the socket will hang and take quite a while + * to timeout. + */ +static void +comm_empty_os_read_buffers(int fd) +{ +#ifdef _SQUID_LINUX_ + /* prevent those nasty RST packets */ + char buf[SQUID_TCP_SO_RCVBUF]; + while (FD_READ_METHOD(fd, buf, SQUID_TCP_SO_RCVBUF) > 0); +#endif +} + + +/* + * Return whether a file descriptor has any pending read request callbacks + * + * Assumptions: the fd is open (ie, its not closing) + */ +int +comm_has_pending_read(int fd) +{ + dlink_node *node; + CommCallbackData *cd; + + assert(fd_table[fd].flags.open == 1); + assert(fdc_table[fd].active == 1); + + /* + * XXX I don't like having to walk the list! + * Instead, if this routine is called often enough, we should + * also maintain a linked list of _read_ events - we can just + * check if the list head a HEAD.. + * - adrian + */ + node = fdc_table[fd].CommCallbackList.head; + while (node != NULL) { + cd = node->data; + if (cd->type == COMM_CB_READ) + return 1; + node = node->next; + } + + /* Not found */ + return 0; +} + +/* + * Cancel a pending read. Assert that we have the right parameters, + * and that there are no pending read events! + */ +void +comm_read_cancel(int fd, IOCB *callback, void *data) +{ + assert(fd_table[fd].flags.open == 1); + assert(fdc_table[fd].active == 1); + + assert(fdc_table[fd].read.handler == callback); + assert(fdc_table[fd].read.handler_data == data); + + assert(!comm_has_pending_read(fd)); + + /* Ok, we can be reasonably sure we won't lose any data here! */ + + /* Delete the callback */ + fdc_table[fd].read.handler = NULL; + fdc_table[fd].read.handler_data = NULL; +} + + /* Older stuff */ static void @@ -1075,6 +1150,7 @@ } #endif fd_close(fd); /* update fdstat */ + comm_empty_os_read_buffers(fd); close(fd); fdc_table[fd].active = 0; bzero(&fdc_table[fd], sizeof(fdc_t)); Index: squid/src/comm.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/Attic/comm.h,v retrieving revision 1.1.2.2 retrieving revision 1.1.2.3 diff -u -r1.1.2.2 -r1.1.2.3 --- squid/src/comm.h 5 Oct 2002 11:14:26 -0000 1.1.2.2 +++ squid/src/comm.h 9 Oct 2002 08:12:16 -0000 1.1.2.3 @@ -6,4 +6,7 @@ /* fill sb with up to length data from fd */ extern void comm_fill_immediate(int fd, StoreIOBuffer sb, IOFCB *callback, void *data); +extern int comm_has_pending_read(int fd); +extern void comm_read_cancel(int fd, IOCB *callback, void *data); + #endif Index: squid/src/pconn.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/pconn.c,v retrieving revision 1.6.32.2 retrieving revision 1.6.32.3 diff -u -r1.6.32.2 -r1.6.32.3 --- squid/src/pconn.c 5 Oct 2002 12:30:42 -0000 1.6.32.2 +++ squid/src/pconn.c 9 Oct 2002 08:12:17 -0000 1.6.32.3 @@ -1,6 +1,6 @@ /* - * $Id: pconn.c,v 1.6.32.2 2002/10/05 12:30:42 rbcollins Exp $ + * $Id: pconn.c,v 1.6.32.3 2002/10/09 08:12:17 adri Exp $ * * DEBUG: section 48 Persistent Connections * AUTHOR: Duane Wessels @@ -34,12 +34,15 @@ */ #include "squid.h" +#include "StoreIOBuffer.h" +#include "comm.h" struct _pconn { hash_link hash; /* must be first */ int *fds; int nfds_alloc; int nfds; + char buf[BUFSIZ]; }; typedef struct _pconn pconn; @@ -48,7 +51,7 @@ int client_pconn_hist[PCONN_HIST_SZ]; int server_pconn_hist[PCONN_HIST_SZ]; -static PF pconnRead; +static IOCB pconnRead; static PF pconnTimeout; static const char *pconnKey(const char *host, u_short port); static hash_table *table = NULL; @@ -142,15 +145,11 @@ } static void -pconnRead(int fd, void *data) +pconnRead(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data) { - LOCAL_ARRAY(char, buf, 256); struct _pconn *p = data; - int n; assert(table != NULL); - statCounter.syscalls.sock.reads++; - n = FD_READ_METHOD(fd, buf, 256); - debug(48, 3) ("pconnRead: %d bytes from FD %d, %s\n", n, fd, + debug(48, 3) ("pconnRead: %d bytes from FD %d, %s\n", (int)len, fd, hashKeyStr(&p->hash)); pconnRemoveFD(p, fd); comm_close(fd); @@ -238,32 +237,52 @@ xfree(old); } p->fds[p->nfds++] = fd; - commSetSelect(fd, COMM_SELECT_READ, pconnRead, p, 0); + comm_read(fd, p->buf, BUFSIZ, pconnRead, p); commSetTimeout(fd, Config.Timeout.pconn, pconnTimeout, p); snprintf(desc, FD_DESC_SZ, "%s idle connection", host); fd_note(fd, desc); debug(48, 3) ("pconnPush: pushed FD %d for %s\n", fd, key); } + +/* + * return a pconn fd for host:port, or -1 if none are available + * + * XXX this routine isn't terribly efficient - if there's a pending + * read event (which signifies the fd will close in the next IO loop!) + * we ignore the FD and move onto the next one. This means, as an example, + * if we have a lot of FDs open to a very popular server and we get a bunch + * of requests JUST as they timeout (say, it shuts down) we'll be wasting + * quite a bit of CPU. Just keep it in mind. + */ int pconnPop(const char *host, u_short port) { struct _pconn *p; hash_link *hptr; int fd = -1; + int i; LOCAL_ARRAY(char, key, SQUIDHOSTNAMELEN + 10); assert(table != NULL); strcpy(key, pconnKey(host, port)); hptr = hash_lookup(table, key); + if (hptr != NULL) { p = (struct _pconn *) hptr; assert(p->nfds > 0); - fd = p->fds[0]; - pconnRemoveFD(p, fd); - commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); - commSetTimeout(fd, -1, NULL, NULL); + for (i = 0; i < p->nfds; i++) { + fd = p->fds[0]; + /* If there are pending reads - we're about to close it, so don't issue it! */ + if (!comm_has_pending_read(fd)) { + pconnRemoveFD(p, fd); + comm_read_cancel(fd, pconnRead, p); + commSetTimeout(fd, -1, NULL, NULL); + return fd; + } + } } - return fd; + /* Nothing (valid!) found */ + return -1; } void