# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: rousskov@measurement-factory.com-20080911055832-\ # fmdvvk86w4yn4k9n # target_branch: file:///home/rousskov/programs/bazaar/repos/squid\ # /trunk/ # testament_sha1: 1a67b106ff4f18cdaefda11eb2dfb0cb199c2aaf # timestamp: 2008-09-11 00:27:40 -0600 # message: Cleanup, the Comm part # base_revision_id: amosjeffries@squid-cache.org-20080910015634-\ # 2xvc2fymhl885rm6 # # Begin patch === modified file 'src/CommCalls.cc' --- src/CommCalls.cc 2008-02-13 05:58:29 +0000 +++ src/CommCalls.cc 2008-09-11 05:58:32 +0000 @@ -1,4 +1,5 @@ #include "squid.h" +#include "fde.h" #include "CommCalls.h" /* CommCommonCbParams */ @@ -63,6 +64,19 @@ } void +CommIoCbParams::syncWithComm() +{ + // change parameters if the call was scheduled before comm_close but + // is being fired after comm_close + if (fd >= 0 && fd_table[fd].closing() && flag != COMM_ERR_CLOSING) { + debugs(5, 3, HERE << "converting late call to COMM_ERR_CLOSING: FD " << fd); + flag = COMM_ERR_CLOSING; + size = 0; + } +} + + +void CommIoCbParams::print(std::ostream &os) const { CommCommonCbParams::print(os); === modified file 'src/CommCalls.h' --- src/CommCalls.h 2008-02-13 05:58:29 +0000 +++ src/CommCalls.h 2008-09-11 05:58:32 +0000 @@ -41,6 +41,9 @@ CommCommonCbParams(const CommCommonCbParams ¶ms); ~CommCommonCbParams(); + /// last chance to adjust based on the current Comm state + void syncWithComm() {} // not virtual because fire() knows dialer type + void print(std::ostream &os) const; public: @@ -78,6 +81,7 @@ CommIoCbParams(void *aData); void print(std::ostream &os) const; + void syncWithComm(); public: char *buf; @@ -137,7 +141,7 @@ Method method; protected: - virtual void doDial() { (object->*method)(this->params); } + virtual void doDial() { this->params.syncWithComm(); (object->*method)(this->params); } }; @@ -285,6 +289,7 @@ void CommCbFunPtrCallT::fire() { + dialer.params.syncWithComm(); dialer.dial(); } === modified file 'src/comm.cc' --- src/comm.cc 2008-09-03 10:39:15 +0000 +++ src/comm.cc 2008-09-11 05:58:32 +0000 @@ -67,6 +67,10 @@ IOCB_WRITE } iocb_type; +static void commStopHalfClosedMonitor(int fd); +static IOCB commHalfClosedReader; + + struct comm_io_callback_t { iocb_type type; int fd; @@ -176,8 +180,6 @@ assert(ccb->active()); ccb->xerrno = 0; -// delete ccb->callback; - ccb->callback = NULL; ccb->callback = NULL; } @@ -334,20 +336,25 @@ void comm_read(int fd, char *buf, int size, AsyncCall::Pointer &callback) { - /* Make sure we're not reading anything and we're not closing */ + debugs(5, 5, "comm_read, queueing read for FD " << fd << "; asynCall " << callback); + + /* Make sure we are open and not closing */ assert(isOpen(fd)); - assert(!fd_table[fd].flags.closing_); - // XXX: If we already called commio_finish_callback, the new callback - // we are setting here would apply to the next connection with the same FD. - assert(!fd_table[fd].flags.close_request); + assert(!fd_table[fd].closing()); + comm_io_callback_t *ccb = COMMIO_FD_READCB(fd); - debugs(5, 4, "comm_read, queueing read for FD " << fd); + // Make sure we are either not reading or just passively monitoring. + // Active/passive conflicts are OK and simply cancel passive monitoring. + if (ccb->active()) { + // if the assertion below fails, we have an active comm_read conflict + assert(commHasHalfClosedMonitor(fd)); + commStopHalfClosedMonitor(fd); + assert(!ccb->active()); + } /* Queue the read */ - /* XXX ugly */ - commio_set_callback(fd, IOCB_READ, COMMIO_FD_READCB(fd), - callback, (char *)buf, NULL, size); - commSetSelect(fd, COMM_SELECT_READ, commHandleRead, COMMIO_FD_READCB(fd), 0); + commio_set_callback(fd, IOCB_READ, ccb, callback, (char *)buf, NULL, size); + commSetSelect(fd, COMM_SELECT_READ, commHandleRead, ccb, 0); } /** @@ -434,6 +441,8 @@ return; } + call->cancel("old comm_read_cancel"); + typedef CommIoCbParams Params; const Params ¶ms = GetCommParams(cb->callback); @@ -466,7 +475,7 @@ } AsyncCall::Pointer call = cb->callback; - assert(call != NULL); // XXX: should never fails (active() checks for callback==NULL) + assert(call != NULL); // XXX: should never fail (active() checks for callback==NULL) /* Ok, we can be reasonably sure we won't lose any data here! */ assert(call == callback); @@ -1274,6 +1283,10 @@ } +/* Squid seems to be working fine without this code. With this code, + * we leak memory on many connect requests because of EINPROGRESS. + * If you find that this code is needed, please file a bug report. */ +#if 0 #ifdef _SQUID_LINUX_ /* 2007-11-27: * Linux Debian replaces our allocated AI pointer with garbage when @@ -1285,6 +1298,7 @@ AI = NULL; } #endif +#endif address.FreeAddrInfo(AI); @@ -1500,10 +1514,6 @@ void comm_close_start(int fd, void *data) { - fde *F = &fd_table[fd]; - - F->flags.closing_ = 1; - #if USE_SSL if (F->ssl) @@ -1530,9 +1540,6 @@ close(fd); - if (AbortChecker::Instance().isMonitoring(fd)) - AbortChecker::Instance().stopMonitoring(fd); - fdc_table[fd] = AcceptFD(fd); statCounter.syscalls.sock.closes++; @@ -1557,26 +1564,20 @@ void _comm_close(int fd, char const *file, int line) { - fde *F = NULL; - - debugs(5, 5, "comm_close: FD " << fd); + debugs(5, 3, "comm_close: start closing FD " << fd); assert(fd >= 0); assert(fd < Squid_MaxFD); - F = &fd_table[fd]; + + fde *F = &fd_table[fd]; fdd_table[fd].close_file = file; fdd_table[fd].close_line = line; - if(F->flags.close_request) - return; - - if (F->flags.closing_) + if (F->closing()) return; if (shutting_down && (!F->flags.open || F->type == FD_FILE)) return; - assert(F->flags.open); - /* The following fails because ipc.c is doing calls to pipe() to create sockets! */ assert(isOpen(fd)); @@ -1595,7 +1596,7 @@ commSetTimeout(fd, -1, NULL, NULL); - /* new-style read/write handler stuff */ + // notify read/write handlers if (commio_has_callback(fd, IOCB_WRITE, COMMIO_FD_WRITECB(fd))) { commio_finish_callback(fd, COMMIO_FD_WRITECB(fd), COMM_ERR_CLOSING, errno); } @@ -1603,7 +1604,7 @@ commio_finish_callback(fd, COMMIO_FD_READCB(fd), COMM_ERR_CLOSING, errno); } - /* Do callbacks for read/accept routines, if any */ + // notify accept handlers fdc_table[fd].notify(-1, COMM_ERR_CLOSING, 0, ConnectionDetail()); commCallCloseHandlers(fd); @@ -1618,6 +1619,8 @@ CommCloseCbPtrFun(comm_close_complete, NULL)); Params &completeParams = GetCommParams(completeCall); completeParams.fd = fd; + // must use async call to wait for all callbacks + // scheduled before comm_close() to finish ScheduleCallHere(completeCall); PROF_stop(comm_close); @@ -2022,20 +2025,18 @@ void comm_write(int fd, const char *buf, int size, AsyncCall::Pointer &callback, FREE * free_func) { - assert(!fd_table[fd].flags.closing_); - - debugs(5, 5, "comm_write: FD " << fd << ": sz " << size << ": asynCall " << callback << "."); - - if (commio_has_callback(fd, IOCB_WRITE, COMMIO_FD_WRITECB(fd))) { - /* This means that the write has been scheduled, but has not - * triggered yet - */ - fatalf ("comm_write: fd %d: pending callback!\n", fd); - } - - commio_set_callback(fd, IOCB_WRITE, COMMIO_FD_WRITECB(fd), - callback, (char *)buf, free_func, size); - commSetSelect(fd, COMM_SELECT_WRITE, commHandleWrite, COMMIO_FD_WRITECB(fd), 0); + debugs(5, 5, "comm_write: FD " << fd << ": sz " << size << ": asynCall " << callback); + + /* Make sure we are open, not closing, and not writing */ + assert(isOpen(fd)); + assert(!fd_table[fd].closing()); + comm_io_callback_t *ccb = COMMIO_FD_WRITECB(fd); + assert(!ccb->active()); + + /* Queue the write */ + commio_set_callback(fd, IOCB_WRITE, ccb, callback, + (char *)buf, free_func, size); + commSetSelect(fd, COMM_SELECT_WRITE, commHandleWrite, ccb, 0); } @@ -2393,130 +2394,65 @@ comm_accept (temp.acceptFD, temp.theFunction, temp.theData); } -void -commMarkHalfClosed(int fd) { - assert (isOpen(fd)); - AbortChecker::Instance().monitor(fd); -} - -int commIsHalfClosed(int fd) { - assert (isOpen(fd)); - - return AbortChecker::Instance().isMonitoring(fd); -} - -void -commCheckHalfClosed(void *data) { - AbortChecker::Instance().doIOLoop(); - eventAdd("commCheckHalfClosed", commCheckHalfClosed, NULL, 1.0, false); -} - -AbortChecker &AbortChecker::Instance() {return Instance_;} - -AbortChecker AbortChecker::Instance_; - -void -AbortChecker::AbortCheckReader(int fd, char *, size_t size, comm_err_t flag, int xerrno, void *data) { - assert (size == 0); - /* sketch: - * if the read is ok and 0, the conn is still open. - * if the read is a fail, close the conn - */ - - if (flag != COMM_OK && flag != COMM_ERR_CLOSING) { - debugs(5, 3, "AbortChecker::AbortCheckReader: FD " << fd << " aborted"); +/// Start waiting for a possibly half-closed connection to close +// by scheduling a read callback to a monitoring handler that +// will close the connection on read errors. +void +commStartHalfClosedMonitor(int fd) { + assert(isOpen(fd)); + assert(!commHasHalfClosedMonitor(fd)); + + AsyncCall::Pointer call = commCbCall(5,4, "commHalfClosedReader", + CommIoCbPtrFun(&commHalfClosedReader, NULL)); + comm_read(fd, NULL, 0, call); +} + +/// checks whether we are waiting for possibly half-closed connection to close +// We are monitoring if the read handler for the fd is the monitoring handler. +bool +commHasHalfClosedMonitor(int fd) { + assert(isOpen(fd)); + + if (const comm_io_callback_t *cb = COMMIO_FD_READCB(fd)) { + AsyncCall::Pointer call = cb->callback; + if (call != NULL) { + // check whether the callback has the right type (it should) + // and uses commHalfClosedReader as the address to call back + typedef CommIoCbPtrFun IoDialer; + if (IoDialer *d = dynamic_cast(call->getDialer())) + return d->handler == &commHalfClosedReader; + } + } + return false; +} + +/// stop waiting for possibly half-closed connection to close +static void +commStopHalfClosedMonitor(int const fd) { + comm_read_cancel(fd, &commHalfClosedReader, NULL); +} + +/// I/O handler for the possibly half-closed connection monitoring code +static void +commHalfClosedReader(int fd, char *, size_t size, comm_err_t flag, int, void *) { + // there cannot be more data coming in on half-closed connections + assert(size == 0); + + // nothing to do if fd is being closed + if (flag == COMM_ERR_CLOSING) + return; + + // if read failed, close the connection + if (flag != COMM_OK) { + debugs(5, 3, "commHalfClosedReader: closing FD " << fd); comm_close(fd); + return; } -} - -void -AbortChecker::monitor(int fd) { - assert (!contains(fd)); - - add - (fd); - - debugs(5, 3, "AbortChecker::monitor: monitoring half closed FD " << fd << " for aborts"); -} - -void -AbortChecker::stopMonitoring (int fd) { - assert (contains (fd)); - - remove - (fd); - - debugs(5, 3, "AbortChecker::stopMonitoring: stopped monitoring half closed FD " << fd << " for aborts"); -} - -#include "splay.h" -void -AbortChecker::doIOLoop() { - fds->walk(RemoveCheck, this); - fds->walk(AddCheck, this); -} - -void -AbortChecker::AddCheck (int const &fd, void *data) { - AbortChecker *me = (AbortChecker *)data; - me->addCheck(fd); -} - -void -AbortChecker::RemoveCheck (int const &fd, void *data) { - AbortChecker *me = (AbortChecker *)data; - me->removeCheck(fd); -} - - -int -AbortChecker::IntCompare (int const &lhs, int const &rhs) { - return lhs - rhs; -} - -bool -AbortChecker::isMonitoring(int fd) const { - return contains(fd); -} - -bool -AbortChecker::contains (int const fd) const { - fds = fds->splay(fd, IntCompare); - - if (splayLastResult != 0) - return false; - - return true; -} - -void - -AbortChecker::remove - (int const fd) { - - fds = fds->remove - (fd, IntCompare); -} - -void - -AbortChecker::add - (int const fd) { - fds = fds->insert (fd, IntCompare); -} - -void -AbortChecker::addCheck (int const fd) { - /* assert comm_is_open (fd); */ - comm_read(fd, NULL, 0, AbortCheckReader, NULL); -} - -void -AbortChecker::removeCheck (int const fd) { - /* - comm_read_cancel(fd, AbortCheckReader, NULL); - */ -} + + // continue waiting for close or error + commStartHalfClosedMonitor(fd); +} + CommRead::CommRead() : fd(-1), buf(NULL), len(0), callback(NULL) {} === modified file 'src/comm.h' --- src/comm.h 2008-05-08 03:47:53 +0000 +++ src/comm.h 2008-09-11 05:58:32 +0000 @@ -101,11 +101,19 @@ extern int comm_udp_recvfrom(int fd, void *buf, size_t len, int flags, IPAddress &from); extern int comm_udp_recv(int fd, void *buf, size_t len, int flags); extern ssize_t comm_udp_send(int s, const void *buf, size_t len, int flags); -extern void commMarkHalfClosed(int); -extern int commIsHalfClosed(int); -extern void commCheckHalfClosed(void *); extern bool comm_has_incomplete_write(int); +/** The read channel has closed and the caller does not expect more data + * but needs to detect connection aborts. The current detection method uses + * 0-length reads: We read until the error occurs or the writer closes + * the connection. If there is a read error, we close the connection. + */ +extern void commStartHalfClosedMonitor(int fd); +extern bool commHasHalfClosedMonitor(int fd); +// XXX: remove these wrappers which minimize client_side.cc changes in a commit +inline void commMarkHalfClosed(int fd) { commStartHalfClosedMonitor(fd); } +inline bool commIsHalfClosed(int fd) { return commHasHalfClosedMonitor(fd); } + /* Not sure where these should live yet */ class Acceptor @@ -133,53 +141,6 @@ Vector deferred; }; -/* App layer doesn't want any more data from the socket, as the read channel is - * closed, but we need to detect aborts, so this lets us do so. - */ - -class AbortChecker -{ - -public: - static AbortChecker &Instance(); - /* the current method of checking, is via a 0 length read every second. - * if nothing is returned by the next IO loop, we let it be. - * If an error occurs, we close the conn. - * Note that some tcp environments may allow direct polling for the socket status - * and this could be adjusted to use that method for the test. (in which case - * the singleton should be refactored to have the tcp engine register the - * instance with it). - */ - static IOCB AbortCheckReader; - - bool isMonitoring(int fd) const; - void monitor (int); - void stopMonitoring (int); - void doIOLoop(); - -private: - static AbortChecker Instance_; - static void AddCheck (int const &, void *); - static int IntCompare (int const &, int const &); - static void RemoveCheck (int const &, void *); - AbortChecker() : fds (NULL), checking (false), lastCheck (0){} - - mutable SplayNode *fds; - bool checking; - time_t lastCheck; - bool contains (int const) const; - - void remove - (int const); - - void add - (int const); - - void addCheck (int const); - - void removeCheck (int const); -}; - /* A comm engine that calls comm_select */ class CommSelectEngine : public AsyncEngine === modified file 'src/disk.cc' --- src/disk.cc 2008-07-11 15:15:40 +0000 +++ src/disk.cc 2008-09-11 05:58:32 +0000 @@ -133,8 +133,6 @@ */ assert(F->write_handler == NULL); - F->flags.closing_ = 1; - #if CALL_FSYNC_BEFORE_CLOSE fsync(fd); === modified file 'src/fde.h' --- src/fde.h 2008-09-06 05:15:20 +0000 +++ src/fde.h 2008-09-11 05:58:32 +0000 @@ -45,14 +45,8 @@ public: fde() { clear(); }; - /** - * Return true if the the comm_close for this fd called. - * Two flags used for the filedescriptor closing procedure: - * - The flag flags.close_request which set when the comm_close called - * - The flag flags.closing which scheduled to be set just before the - * comm_close handlers called. - */ - bool closing() {return flags.closing_ || flags.close_request;} + /// True if comm_close for this fd has been called + bool closing() { return flags.close_request; } /* NOTE: memset is used on fdes today. 20030715 RBC */ static void DumpStats (StoreEntry *); @@ -74,9 +68,8 @@ struct { unsigned int open:1; - unsigned int close_request:1; + unsigned int close_request:1; // file_ or comm_close has been called unsigned int write_daemon:1; - unsigned int closing_:1; unsigned int socket_eof:1; unsigned int nolinger:1; unsigned int nonblocking:1; # Begin bundle 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 uxpDbB9jffEfx/+LuSKcKEgc/yC4AA==