void Socket::handleSendError(int ret, const char* context) { #if defined(_WIN32) const int mongo_errno = WSAGetLastError(); if ( mongo_errno == WSAETIMEDOUT && _timeout != 0 ) { #else const int mongo_errno = errno; if ( ( mongo_errno == EAGAIN || mongo_errno == EWOULDBLOCK ) && _timeout != 0 ) { #endif LOG(_logLevel) << "Socket " << context << " send() timed out " << remoteString() << endl; throw SocketException(SocketException::SEND_TIMEOUT , remoteString()); } else { LOG(_logLevel) << "Socket " << context << " send() " << errnoWithDescription(mongo_errno) << ' ' << remoteString() << endl; throw SocketException(SocketException::SEND_ERROR , remoteString()); } } void Socket::handleRecvError(int ret, int len) { if (ret == 0) { LOG(3) << "Socket recv() conn closed? " << remoteString() << endl; throw SocketException(SocketException::CLOSED , remoteString()); } // ret < 0 #if defined(_WIN32) int e = WSAGetLastError(); #else int e = errno; # if defined(EINTR) if (e == EINTR) { LOG(_logLevel) << "EINTR returned from recv(), retrying"; return; } # endif #endif #if defined(_WIN32) // Windows if ((e == EAGAIN || e == WSAETIMEDOUT) && _timeout > 0) { #else if (e == EAGAIN && _timeout > 0) { #endif // this is a timeout LOG(_logLevel) << "Socket recv() timeout " << remoteString() <<endl; throw SocketException(SocketException::RECV_TIMEOUT, remoteString()); } LOG(_logLevel) << "Socket recv() " << errnoWithDescription(e) << " " << remoteString() <<endl; throw SocketException(SocketException::RECV_ERROR , remoteString()); } void Socket::setTimeout( double secs ) { setSockTimeouts( _fd, secs ); } // TODO: allow modification? // // <positive value> : secs to wait between stillConnected checks // 0 : always check // -1 : never check const int Socket::errorPollIntervalSecs( 5 ); // Patch to allow better tolerance of flaky network connections that get broken // while we aren't looking. // TODO: Remove when better async changes come. // // isStillConnected() polls the socket at max every Socket::errorPollIntervalSecs to determine // if any disconnection-type events have happened on the socket. bool Socket::isStillConnected() { if (_fd == -1) { // According to the man page, poll will respond with POLLVNAL for invalid or // unopened descriptors, but it doesn't seem to be properly implemented in // some platforms - it can return 0 events and 0 for revent. Hence this workaround. return false; } if ( errorPollIntervalSecs < 0 ) return true; if ( ! isPollSupported() ) return true; // nothing we can do time_t now = time( 0 ); time_t idleTimeSecs = now - _lastValidityCheckAtSecs; // Only check once every 5 secs if ( idleTimeSecs < errorPollIntervalSecs ) return true; // Reset our timer, we're checking the connection _lastValidityCheckAtSecs = now; // It's been long enough, poll to see if our socket is still connected pollfd pollInfo; pollInfo.fd = _fd; // We only care about reading the EOF message on clean close (and errors) pollInfo.events = POLLIN; // Poll( info[], size, timeout ) - timeout == 0 => nonblocking int nEvents = socketPoll( &pollInfo, 1, 0 ); LOG( 2 ) << "polling for status of connection to " << remoteString() << ", " << ( nEvents == 0 ? "no events" : nEvents == -1 ? "error detected" : "event detected" ) << endl; if ( nEvents == 0 ) { // No events incoming, return still connected AFAWK return true; } else if ( nEvents < 0 ) { // Poll itself failed, this is weird, warn and log errno warning() << "Socket poll() failed during connectivity check" << " (idle " << idleTimeSecs << " secs," << " remote host " << remoteString() << ")" << causedBy(errnoWithDescription()) << endl; // Return true since it's not clear that we're disconnected. return true; } dassert( nEvents == 1 ); dassert( pollInfo.revents > 0 ); // Return false at this point, some event happened on the socket, but log what the // actual event was. if ( pollInfo.revents & POLLIN ) { // There shouldn't really be any data to recv here, so make sure this // is a clean hangup. const int testBufLength = 1024; char testBuf[testBufLength]; int recvd = ::recv( _fd, testBuf, testBufLength, portRecvFlags ); if ( recvd < 0 ) { // An error occurred during recv, warn and log errno warning() << "Socket recv() failed during connectivity check" << " (idle " << idleTimeSecs << " secs," << " remote host " << remoteString() << ")" << causedBy(errnoWithDescription()) << endl; } else if ( recvd > 0 ) { // We got nonzero data from this socket, very weird? // Log and warn at runtime, log and abort at devtime // TODO: Dump the data to the log somehow? error() << "Socket found pending " << recvd << " bytes of data during connectivity check" << " (idle " << idleTimeSecs << " secs," << " remote host " << remoteString() << ")" << endl; DEV { std::string hex = hexdump(testBuf, recvd); error() << "Hex dump of stale log data: " << hex << endl; } dassert( false ); } else { // recvd == 0, socket closed remotely, just return false LOG( 0 ) << "Socket closed remotely, no longer connected" << " (idle " << idleTimeSecs << " secs," << " remote host " << remoteString() << ")" << endl; } }
int socketPoll( pollfd* fdarray, unsigned long nfds, int timeout ) { fassert(17185, isPollSupported()); return wsaPollFunction(fdarray, nfds, timeout); }