diff --git a/Socket.cpp b/Socket.cpp index 5a89c1b..2167ed6 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -272,6 +272,26 @@ namespace core // the confirmed real damage (a 55GB, then 12.9GB debug log) // this caused before being noticed. coreutils::Log(coreutils::LOG_EXCEPT) << "Socket " << getDescriptor() << ": " << consecutiveWriteStalls << " consecutive writes made zero progress - giving up and shutting down."; + // shutdown() alone only sets shutDown=true and expects the + // fifo to drain normally before the connection actually + // closes (see eventReceived()'s keepAlive: "!shutDown || + // (fifo.size() > 0 && !readHangup)") - exactly backwards + // for THIS path, since the whole reason we're here is that + // the front of the fifo can never be sent. Leaving it + // queued meant keepAlive stayed true forever, resetSocket() + // kept re-arming EPOLLWRNORM because fifo.size() > 0, the + // very next write attempt hit this identical stall + // immediately, and shutdown() got called again - forever, + // at whatever rate the kernel could fire EPOLLWRNORM. + // Confirmed live: 2.4 million+ repeats of this exact log + // line for one socket in under 4 milliseconds, pegging a + // worker thread at 100% CPU indefinitely (a real game + // server, brought down by nothing more than an ordinary + // port-scanner connection). Clearing the undeliverable data + // here lets keepAlive correctly evaluate to just !shutDown + // (now false) on the very next cycle, so the connection + // actually tears down instead of spinning forever. + std::queue().swap(fifo); shutdown("stuck write retry loop - no progress after repeated attempts"); } }