#include "CommCalls.h"
#include "IPAddress.h"
#include "IPInterception.h"
+#include "DescriptorSet.h"
#if defined(_SQUID_CYGWIN_)
#include <sys/ioctl.h>
/* STATIC */
+static DescriptorSet *TheHalfClosed = NULL; /// the set of half-closed FDs
+static bool WillCheckHalfClosed = false; /// true if check is scheduled
+static EVH commHalfClosedCheck;
+static void commPlanHalfClosedCheck();
+
static comm_err_t commBind(int s, struct addrinfo &);
static void commSetReuseAddr(int);
static void commSetNoLinger(int);
// 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));
+ assert(fd_table[fd].halfClosedReader != NULL);
commStopHalfClosedMonitor(fd);
assert(!ccb->active());
}
startParams.fd = fd;
ScheduleCallHere(startCall);
+ // a half-closed fd may lack a reader, so we stop monitoring explicitly
+ if (commHasHalfClosedMonitor(fd))
+ commStopHalfClosedMonitor(fd);
commSetTimeout(fd, -1, NULL, NULL);
// notify read/write handlers
RESERVED_FD = XMIN(100, Squid_MaxFD / 4);
conn_close_pool = memPoolCreate("close_handler", sizeof(close_handler));
+
+ TheHalfClosed = new DescriptorSet;
}
void
comm_exit(void) {
+ delete TheHalfClosed;
+ TheHalfClosed = NULL;
+
safe_free(fd_table);
safe_free(fdd_table);
if (fdc_table) {
// will close the connection on read errors.
void
commStartHalfClosedMonitor(int fd) {
+ debugs(5, 5, HERE << "adding FD " << fd << " to " << *TheHalfClosed);
assert(isOpen(fd));
assert(!commHasHalfClosedMonitor(fd));
+ (void)TheHalfClosed->add(fd); // could also assert the result
+ commPlanHalfClosedCheck(); // may schedule check if we added the first FD
+}
+
+static
+void
+commPlanHalfClosedCheck()
+{
+ if (!WillCheckHalfClosed && !TheHalfClosed->empty()) {
+ eventAdd("commHalfClosedCheck", &commHalfClosedCheck, NULL, 1.0, 1);
+ WillCheckHalfClosed = true;
+ }
+}
+
+/// iterates over all descriptors that may need half-closed tests and
+/// calls comm_read for those that do; re-schedules the check if needed
+static
+void
+commHalfClosedCheck(void *) {
+ debugs(5, 5, HERE << "checking " << *TheHalfClosed);
+
+ typedef DescriptorSet::const_iterator DSCI;
+ const DSCI end = TheHalfClosed->end();
+ for (DSCI i = TheHalfClosed->begin(); i != end; ++i) {
+ const int fd = *i;
+ if (!fd_table[fd].halfClosedReader) { // not reading already
+ AsyncCall::Pointer call = commCbCall(5,4, "commHalfClosedReader",
+ CommIoCbPtrFun(&commHalfClosedReader, NULL));
+ comm_read(fd, NULL, 0, call);
+ fd_table[fd].halfClosedReader = call;
+ }
+ }
- AsyncCall::Pointer call = commCbCall(5,4, "commHalfClosedReader",
- CommIoCbPtrFun(&commHalfClosedReader, NULL));
- comm_read(fd, NULL, 0, call);
+ WillCheckHalfClosed = false; // as far as we know
+ commPlanHalfClosedCheck(); // may need to check again
}
/// 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<IoDialer*>(call->getDialer()))
- return d->handler == &commHalfClosedReader;
- }
- }
- return false;
+ return TheHalfClosed->has(fd);
}
/// stop waiting for possibly half-closed connection to close
static void
commStopHalfClosedMonitor(int const fd) {
- comm_read_cancel(fd, &commHalfClosedReader, NULL);
+ debugs(5, 5, HERE << "removing FD " << fd << " from " << *TheHalfClosed);
+
+ // cancel the read if one was scheduled
+ AsyncCall::Pointer reader = fd_table[fd].halfClosedReader;
+ if (reader != NULL)
+ comm_read_cancel(fd, reader);
+ fd_table[fd].halfClosedReader = NULL;
+
+ TheHalfClosed->del(fd);
}
/// I/O handler for the possibly half-closed connection monitoring code
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);
+ assert(commHasHalfClosedMonitor(fd)); // or we would have canceled the read
+
+ fd_table[fd].halfClosedReader = NULL; // done reading, for now
// nothing to do if fd is being closed
if (flag == COMM_ERR_CLOSING)
}
// continue waiting for close or error
- commStartHalfClosedMonitor(fd);
+ commPlanHalfClosedCheck(); // make sure this fd will be checked again
}