From 22c13147384c1b804f7ee5209e16b36fd1b86295 Mon Sep 17 00:00:00 2001 From: athile Date: Tue, 6 Jul 2010 10:03:49 -0700 Subject: [PATCH] Potential fix for inability to cancel Boost.Asio blocking accept on Linux --- components/commandserver/server.cpp | 46 ++++++++++++++++++++++++----- components/commandserver/server.hpp | 1 + 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/components/commandserver/server.cpp b/components/commandserver/server.cpp index c8bf34bfa0..a634242423 100755 --- a/components/commandserver/server.cpp +++ b/components/commandserver/server.cpp @@ -1,10 +1,8 @@ - #include "server.hpp" +#include "libs/platform/strings.h" using boost::asio::ip::tcp; -#include - // // Namespace for containing implementation details that the // rest of OpenMW doesn't need to worry about @@ -105,7 +103,7 @@ namespace OMW { namespace CommandServer { namespace Detail { bDone = true; } else - throw str_exception("Unexpected header!"); + throw std::runtime_error("Unexpected header!"); } else bDone = true; @@ -120,12 +118,12 @@ namespace OMW { namespace CommandServer { using namespace Detail; Server::Server (Deque* pCommandQueue, const int port) - : mAcceptor (mIOService, tcp::endpoint(tcp::v4(), port)) + : mPort (port) + , mAcceptor (mIOService, tcp::endpoint(tcp::v4(), mPort)) , mbStopping (false) , mpCommandQueue (pCommandQueue) { } - void Server::start() { @@ -133,8 +131,41 @@ namespace OMW { namespace CommandServer { mpThread = new boost::thread(boost::bind(&Server::threadMain, this)); } + // + // Helper function - see Server::stop() + // + static void connectAndDisconnect (int port) + { + char portString[64]; + snprintf(portString, 64, "%d", port); + + boost::asio::io_service ioService; + tcp::resolver resolver(ioService); + tcp::resolver::query query("localhost", portString); + tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); + tcp::resolver::iterator end; + + tcp::socket socket(ioService); + boost::system::error_code error = boost::asio::error::host_not_found; + while (error && endpoint_iterator != end) + { + socket.close(); + socket.connect(*endpoint_iterator++, error); + } + + socket.close(); + ioService.stop(); + } + void Server::stop() { + // Boost.Asio doesn't have a way to cancel the blocking accept() call + // in the listening thread. Therefore, set an internal flag to let + // the server know it's stopping and then unblock it via a do-nothing + // connect/disconnect. + mbStopping = true; + connectAndDisconnect(mPort); + // (1) Stop accepting new connections // (2) Wait for the listener thread to finish mAcceptor.close(); @@ -144,7 +175,6 @@ namespace OMW { namespace CommandServer { // open connections { boost::mutex::scoped_lock lock(mConnectionsMutex); - mbStopping = true; for (ConnectionSet::iterator it = mConnections.begin(); it != mConnections.end(); ++it) @@ -186,7 +216,7 @@ namespace OMW { namespace CommandServer { std::auto_ptr spConnection(new Connection(mAcceptor.io_service(), this)); boost::system::error_code ec; mAcceptor.accept(spConnection->socket(), ec); - if (!ec) + if (!ec && !mbStopping) { boost::mutex::scoped_lock lock(mConnectionsMutex); mConnections.insert(spConnection.get()); diff --git a/components/commandserver/server.hpp b/components/commandserver/server.hpp index b53e0f23a9..3d7665a147 100755 --- a/components/commandserver/server.hpp +++ b/components/commandserver/server.hpp @@ -45,6 +45,7 @@ namespace OMW { namespace CommandServer void threadMain(); // Objects used to set up the listening server + int mPort; boost::asio::io_service mIOService; boost::asio::ip::tcp::acceptor mAcceptor; boost::thread* mpThread;