Use atomic flag to start / stop listening thread

This commit is contained in:
Benau 2018-02-17 11:40:48 +08:00
parent cfeadf335c
commit 372753f505
2 changed files with 41 additions and 64 deletions

View File

@ -43,8 +43,7 @@
# include <errno.h>
# include <sys/socket.h>
#endif
#include <pthread.h>
#include <signal.h>
#include <functional>
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
// ----------------------------------------------------------------------------

View File

@ -38,7 +38,8 @@
#define WIN32_LEAN_AND_MEAN
#include <enet/enet.h>
#include <pthread.h>
#include <atomic>
#include <thread>
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<NetworkPlayerProfile*> 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; }