#include #include #include "LogFile.h" #include "IPollSet.h" #include "MultiCast.h" #include "NewConnections.h" ///////////////////////////////////////////////////////////////////// // NewConnections ///////////////////////////////////////////////////////////////////// void NewConnections::updateMultiCastInfo(int port) { static std::string portInfo; MultiCast::getInstance().serialize([port]() { MultiCast &multiCast = MultiCast::getInstance(); if (portInfo.empty()) // First port added. multiCast.addInfo("HOST", getShortHostName()); else portInfo += '_'; portInfo += ntoa(port); multiCast.addInfo("PORT", portInfo); }); } void NewConnections::threadFunction() { IPollSet pollSet; pollSet.addForRead(_mainSocket); pollSet.addForRead(_timeToShutDown.getWaitHandle()); while (true) { pollSet.poll(); if (_timeToShutDown.isSet()) { LogFile::primary().quoteAndSend("Graceful shutdown of new socket thread.\n"); break; } if (pollSet.woken().count(_mainSocket)) { SocketInfo *newSocket = new SocketInfo(_mainSocket); SocketInfo::Valid valid = newSocket->getValid(); if (valid == SocketInfo::valid) { if (_showInLog) { LogFile::primary().quoteAndSend("New_socket", newSocket->remoteAddr(), newSocket); } _listener->listenToNewSocket(newSocket); } else { delete(newSocket); if (valid != SocketInfo::invalidRetry) { LogFile::primary(). quoteAndSend("Unable to accept() socket. " "Aborting from new socket thread."); LogFile::primary().scheduleShutdown(); break; } } } } close(_mainSocket); _mainSocket = -1; } NewConnections::NewConnections(NewSocketListener *listener, int port, bool showInLog) : ThreadClass("NewConnections"), _listener(listener), _mainSocket(-1), _showInLog(showInLog), _success(false) { _mainSocket = socket(PF_INET, SOCK_STREAM, 0); if (_mainSocket == -1) { perror("socket()"); return; } // http://hea-www.harvard.edu/~fine/Tech/addrinuse.html int on=1; setsockopt(_mainSocket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); struct sockaddr_in my_addr; my_addr.sin_family = AF_INET; my_addr.sin_port = htons(port); my_addr.sin_addr.s_addr = INADDR_ANY; const int64_t startTime = getMicroTime(); const int64_t retryTime = 2000000; const int64_t endTime = startTime + retryTime; int bindFailCount = 0; while (true) { int bind_result = bind(_mainSocket, (sockaddr*)&my_addr, sizeof(my_addr)); if (!bind_result) // Success! break; if ((errno == EADDRINUSE) && (getMicroTime() < endTime)) { // Sometimes it takes longer than other times for a dead process to give // up the socket. SO_REUSEADDR helps a lot, but we still have issues. // Some programs like ax_alert_server run in a loop anyway so this is // just one more error and you'd never even noticed that that the server // restarted twice in a row when you only tried to restart it once. // Other programs, like tikiller, only restart when you tell them too. // For those programs I added a small delay between when the script calls // killall and when the script starts the next incarnation of the // program. The problem is that I never know how long to wait. 0.2 // seconds is good for some programs. tikiller seems to need more time // sometimes. // // Now I'm taking a more targeted approach. We will keep trying to call // bind() in a tight loop. We will still give up after a certain amount // of time. But we only wait exactly as much time as required. The // old solution, where the wait was in the script, would always wait for // some fixed amount of time. if (!bindFailCount) // First failure. LogFile::primary().quoteAndSend(TclList()<<"Bind failed" <<"retry time"<<(retryTime/1000000.0) <<"port"<