From 372753f505d4cf8e5e2da5403889979f4b56a7ec Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 17 Feb 2018 11:40:48 +0800 Subject: [PATCH] Use atomic flag to start / stop listening thread --- src/network/stk_host.cpp | 70 ++++++++++------------------------------ src/network/stk_host.hpp | 35 +++++++++++++------- 2 files changed, 41 insertions(+), 64 deletions(-) diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 85c71a053..3bf92ed4f 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -43,8 +43,7 @@ # include # include #endif -#include -#include +#include STKHost *STKHost::m_stk_host = NULL; bool STKHost::m_enable_console = false; @@ -303,12 +302,12 @@ void STKHost::init() m_shutdown = false; m_network = NULL; m_lan_network = NULL; - m_listening_thread = NULL; m_game_setup = NULL; m_is_registered = false; m_error_message = ""; - pthread_mutex_init(&m_exit_mutex, NULL); + m_exit_flag.clear(); + m_exit_flag.test_and_set(); // Start with initialising ENet // ============================ @@ -355,18 +354,6 @@ STKHost::~STKHost() delete m_network; } // ~STKHost -//----------------------------------------------------------------------------- -/** Requests that the network infrastructure is to be shut down. This function - * is called from a thread, but the actual shutdown needs to be done from - * the main thread to avoid race conditions (e.g. ProtocolManager might still - * access data structures when the main thread tests if STKHost exist (which - * it does, but ProtocolManager might be shut down already. - */ -void STKHost::requestShutdown() -{ - m_shutdown = true; -} // requestExit - //----------------------------------------------------------------------------- /** Called from the main thread when the network infrastructure is to be shut * down. @@ -462,9 +449,9 @@ bool STKHost::connect(const TransportAddress& address) */ void STKHost::startListening() { - pthread_mutex_lock(&m_exit_mutex); // will let the update function start - m_listening_thread = new pthread_t; - pthread_create(m_listening_thread, NULL, &STKHost::mainLoop, this); + m_exit_flag.clear(); + m_exit_flag.test_and_set(); + m_listening_thread = std::thread(std::bind(&STKHost::mainLoop, this)); } // startListening // ---------------------------------------------------------------------------- @@ -473,29 +460,11 @@ void STKHost::startListening() */ void STKHost::stopListening() { - if (m_listening_thread) - { - // This will stop the update function on its next update - pthread_mutex_unlock(&m_exit_mutex); - pthread_join(*m_listening_thread, NULL); // wait for the thread to end - } + m_exit_flag.clear(); + if (m_listening_thread.joinable()) + m_listening_thread.join(); } // stopListening -// --------------------------------------------------------------------------- -/** \brief Returns true when the thread should stop listening. - */ -int STKHost::mustStopListening() -{ - switch (pthread_mutex_trylock(&m_exit_mutex)) { - case 0: /* if we got the lock, unlock and return 1 (true) */ - pthread_mutex_unlock(&m_exit_mutex); - return 1; - case EBUSY: /* return 0 (false) if the mutex was locked */ - return 0; - } - return 1; -} // mustStopListening - // ---------------------------------------------------------------------------- /** Returns true if this client instance is allowed to control the server. * A client can authorise itself by providing the server's password. It is @@ -520,26 +489,25 @@ bool STKHost::isAuthorisedToControl() const * event and passes it to the Network Manager. * \param self : used to pass the ENet host to the function. */ -void* STKHost::mainLoop(void* self) +void STKHost::mainLoop() { VS::setThreadName("STKHost"); ENetEvent event; - STKHost* myself = (STKHost*)(self); - ENetHost* host = myself->m_network->getENetHost(); + ENetHost* host = m_network->getENetHost(); if(NetworkConfig::get()->isServer() && (NetworkConfig::get()->isLAN() || NetworkConfig::get()->isPublicServer()) ) { TransportAddress address(0, NetworkConfig::get()->getServerDiscoveryPort()); ENetAddress eaddr = address.toEnetAddress(); - myself->m_lan_network = new Network(1, 1, 0, 0, &eaddr); + m_lan_network = new Network(1, 1, 0, 0, &eaddr); } - while (!myself->mustStopListening()) + while (m_exit_flag.test_and_set()) { - if(myself->m_lan_network) + if(m_lan_network) { - myself->handleDirectSocketRequest(); + handleDirectSocketRequest(); } // if discovery host while (enet_host_service(host, &event, 20) != 0) @@ -561,7 +529,7 @@ void* STKHost::mainLoop(void* self) if (stk_event->getType() == EVENT_TYPE_CONNECTED) { Log::info("STKHost", "A client has just connected. There are " - "now %lu peers.", myself->m_peers.size()); + "now %lu peers.", m_peers.size()); Log::debug("STKHost", "Addresses are : %lx, %lx", stk_event->getPeer(), peer); } // EVENT_TYPE_CONNECTED @@ -588,12 +556,8 @@ void* STKHost::mainLoop(void* self) pm->propagateEvent(stk_event); } // while enet_host_service - } // while !mustStopListening - - free(myself->m_listening_thread); - myself->m_listening_thread = NULL; + } // while m_exit_flag.test_and_set() Log::info("STKHost", "Listening has been stopped"); - return NULL; } // mainLoop // ---------------------------------------------------------------------------- diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index 92d109732..ec9620b80 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -38,7 +38,8 @@ #define WIN32_LEAN_AND_MEAN #include -#include +#include +#include class GameSetup; class NetworkConsole; @@ -88,14 +89,14 @@ private: GameSetup* m_game_setup; /** Id of thread listening to enet events. */ - pthread_t* m_listening_thread; + std::thread m_listening_thread; /** Flag which is set from the protocol manager thread which * triggers a shutdown of the STKHost (and the Protocolmanager). */ - bool m_shutdown; + std::atomic_bool m_shutdown; - /** Mutex used to stop this thread. */ - pthread_mutex_t m_exit_mutex; + /** Atomic flag used to stop this thread. */ + std::atomic_flag m_exit_flag = ATOMIC_FLAG_INIT; /** If this is a server, it indicates if this server is registered * with the stk server. */ @@ -111,6 +112,9 @@ private: void init(); void handleDirectSocketRequest(); + // ------------------------------------------------------------------------ + void mainLoop(); + public: /** If a network console should be started. Note that the console can cause * a crash in release mode on windows (see #1529). */ @@ -139,15 +143,25 @@ public: // ------------------------------------------------------------------------ /** Checks if the STKHost has been created. */ static bool existHost() { return m_stk_host != NULL; } - // ------------------------------------------------------------------------ - - static void* mainLoop(void* self); virtual GameSetup* setupNewGame(); void abort(); void deleteAllPeers(); bool connect(const TransportAddress& peer); - void requestShutdown(); + + //------------------------------------------------------------------------- + /** Requests that the network infrastructure is to be shut down. This + * function is called from a thread, but the actual shutdown needs to be + * done from the main thread to avoid race conditions (e.g. + * ProtocolManager might still access data structures when the main thread + * tests if STKHost exist (which it does, but ProtocolManager might be + * shut down already. + */ + void requestShutdown() + { + m_shutdown.store(true); + } // requestExit + //------------------------------------------------------------------------- void shutdown(); void sendPacketExcept(STKPeer* peer, @@ -164,7 +178,6 @@ public: STKPeer *getPeer(ENetPeer *enet_peer); STKPeer *getServerPeerForClient() const; std::vector getMyPlayerProfiles(); - int mustStopListening(); uint16_t getPort() const; void setErrorMessage(const irr::core::stringw &message); bool isAuthorisedToControl() const; @@ -174,7 +187,7 @@ public: // -------------------------------------------------------------------- /** Returns true if a shutdown of the network infrastructure was * requested. */ - bool requestedShutdown() const { return m_shutdown; } + bool requestedShutdown() const { return m_shutdown.load(); } // -------------------------------------------------------------------- /** Returns the current game setup. */ GameSetup* getGameSetup() { return m_game_setup; }