Replace thread sleep with condition variables

Second patch in the improve-shutdown series. This replaces blocking
sleeps with the use of condition variables for signaling thread
shutdown. The details are:

(1) Create CSimpleCondition class to provide a very basic (simple)
    condition variable that can be used in situations where external
    mutex control is not required, i.e., the class contains both a
    managed mutex and condition variable. Instances of this class
    can be used for basic signaling, and waiters can specify user
    defined predicates.

(2) Replace instances of large sleeps in worker threads with use of
    CSimpleCondition. This allows for very quick response times from
    worker threads when a shutdown has been initiated.

(3) Change stop thread booleans to atomic_bool.

(4) Fixes small whitespace discprencies.
pull/232/head
Mark Landis (N6AZX) 3 years ago
parent 76497403e0
commit 9a37f2c680

@ -97,7 +97,7 @@ protected:
CPacketQueue m_LocalQueue; CPacketQueue m_LocalQueue;
// thread // thread
bool m_bStopThread; std::atomic_bool m_bStopThread;
std::thread *m_pThread; std::thread *m_pThread;
CTimePoint m_TimeoutTimer; CTimePoint m_TimeoutTimer;
CTimePoint m_StatsTimer; CTimePoint m_StatsTimer;

@ -29,6 +29,8 @@
#include "cdmriddirfile.h" #include "cdmriddirfile.h"
#include "cdmriddirhttp.h" #include "cdmriddirhttp.h"
#include <chrono>
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// constructor & destructor // constructor & destructor
@ -42,6 +44,7 @@ CDmridDir::~CDmridDir()
{ {
// kill threads // kill threads
m_bStopThread = true; m_bStopThread = true;
m_cv.signal();
if ( m_pThread != NULL ) if ( m_pThread != NULL )
{ {
m_pThread->join(); m_pThread->join();
@ -55,8 +58,8 @@ CDmridDir::~CDmridDir()
bool CDmridDir::Init(void) bool CDmridDir::Init(void)
{ {
// load content // load content
Reload(); Reload();
// reset stop flag // reset stop flag
m_bStopThread = false; m_bStopThread = false;
@ -70,6 +73,7 @@ bool CDmridDir::Init(void)
void CDmridDir::Close(void) void CDmridDir::Close(void)
{ {
m_bStopThread = true; m_bStopThread = true;
m_cv.signal();
if ( m_pThread != NULL ) if ( m_pThread != NULL )
{ {
m_pThread->join(); m_pThread->join();
@ -83,15 +87,13 @@ void CDmridDir::Close(void)
void CDmridDir::Thread(CDmridDir *This) void CDmridDir::Thread(CDmridDir *This)
{ {
while ( !This->m_bStopThread ) std::chrono::minutes timeout(DMRIDDB_REFRESH_RATE);
while (!This->m_cv.wait(timeout, [&]{return This->m_bStopThread==true;}))
{ {
// Wait 30 seconds
CTimePoint::TaskSleepFor(DMRIDDB_REFRESH_RATE * 60000);
// have lists files changed ? // have lists files changed ?
if ( This->NeedReload() ) if ( This->NeedReload() )
{ {
This->Reload(); This->Reload();
} }
} }
} }

@ -31,6 +31,7 @@
#include <netdb.h> #include <netdb.h>
#include "cbuffer.h" #include "cbuffer.h"
#include "ccallsign.h" #include "ccallsign.h"
#include "csimplecondition.h"
// compare function for std::map::find // compare function for std::map::find
@ -84,9 +85,10 @@ protected:
// Lock() // Lock()
std::mutex m_Mutex; std::mutex m_Mutex;
CSimpleCondition m_cv;
// thread // thread
bool m_bStopThread; std::atomic_bool m_bStopThread;
std::thread *m_pThread; std::thread *m_pThread;
}; };

@ -80,8 +80,8 @@ bool CG3Protocol::Init(void)
{ {
// start helper threads // start helper threads
m_pPresenceThread = new std::thread(PresenceThread, this); m_pPresenceThread = new std::thread(PresenceThread, this);
m_pPresenceThread = new std::thread(ConfigThread, this); m_pConfigThread = new std::thread(ConfigThread, this);
m_pPresenceThread = new std::thread(IcmpThread, this); m_pIcmpThread = new std::thread(IcmpThread, this);
} }
#endif #endif
@ -94,6 +94,7 @@ bool CG3Protocol::Init(void)
void CG3Protocol::Close(void) void CG3Protocol::Close(void)
{ {
m_bStopThread = true;
if (m_pPresenceThread != NULL) if (m_pPresenceThread != NULL)
{ {
m_pPresenceThread->join(); m_pPresenceThread->join();

@ -64,7 +64,13 @@ class CG3Protocol : public CProtocol
{ {
public: public:
// constructor // constructor
CG3Protocol() : m_GwAddress(0u), m_Modules("*"), m_LastModTime(0) {}; CG3Protocol() :
m_pPresenceThread(nullptr),
m_pConfigThread(nullptr),
m_pIcmpThread(nullptr),
m_GwAddress(0u),
m_Modules("*"),
m_LastModTime(0) {}
// destructor // destructor
virtual ~CG3Protocol() {}; virtual ~CG3Protocol() {};

@ -25,6 +25,9 @@
#include "main.h" #include "main.h"
#include "ctimepoint.h" #include "ctimepoint.h"
#include "cgatekeeper.h" #include "cgatekeeper.h"
#include "csimplecondition.h"
#include <chrono>
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -47,6 +50,7 @@ CGateKeeper::~CGateKeeper()
{ {
// kill threads // kill threads
m_bStopThread = true; m_bStopThread = true;
m_cv.signal();
if ( m_pThread != NULL ) if ( m_pThread != NULL )
{ {
m_pThread->join(); m_pThread->join();
@ -78,6 +82,7 @@ bool CGateKeeper::Init(void)
void CGateKeeper::Close(void) void CGateKeeper::Close(void)
{ {
m_bStopThread = true; m_bStopThread = true;
m_cv.signal();
if ( m_pThread != NULL ) if ( m_pThread != NULL )
{ {
m_pThread->join(); m_pThread->join();
@ -179,11 +184,9 @@ bool CGateKeeper::MayTransmit(const CCallsign &callsign, const CIp &ip, int prot
void CGateKeeper::Thread(CGateKeeper *This) void CGateKeeper::Thread(CGateKeeper *This)
{ {
while ( !This->m_bStopThread ) std::chrono::seconds timeout(30);
while (!This->m_cv.wait(timeout, [&]{return This->m_bStopThread==true;}))
{ {
// Wait 30 seconds
CTimePoint::TaskSleepFor(30000);
// have lists files changed ? // have lists files changed ?
if ( This->m_NodeWhiteList.NeedReload() ) if ( This->m_NodeWhiteList.NeedReload() )
{ {

@ -30,6 +30,7 @@
#include "cip.h" #include "cip.h"
#include "ccallsignlist.h" #include "ccallsignlist.h"
#include "cpeercallsignlist.h" #include "cpeercallsignlist.h"
#include "csimplecondition.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// class // class
@ -71,8 +72,10 @@ protected:
CPeerCallsignList m_PeerList; CPeerCallsignList m_PeerList;
// thread // thread
bool m_bStopThread; CSimpleCondition m_cv;
std::atomic_bool m_bStopThread;
std::thread *m_pThread; std::thread *m_pThread;
}; };

@ -132,7 +132,7 @@ protected:
CPacketQueue m_Queue; CPacketQueue m_Queue;
// thread // thread
bool m_bStopThread; std::atomic_bool m_bStopThread;
std::thread *m_pThread; std::thread *m_pThread;
// identity // identity

@ -32,6 +32,8 @@
#include "cysfnodedirfile.h" #include "cysfnodedirfile.h"
#include "cysfnodedirhttp.h" #include "cysfnodedirhttp.h"
#include <chrono>
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// constructor // constructor
@ -72,6 +74,7 @@ CReflector::CReflector(const CCallsign &callsign)
CReflector::~CReflector() CReflector::~CReflector()
{ {
m_bStopThreads = true; m_bStopThreads = true;
m_cv.signal();
if ( m_XmlReportThread != NULL ) if ( m_XmlReportThread != NULL )
{ {
m_XmlReportThread->join(); m_XmlReportThread->join();
@ -146,6 +149,7 @@ void CReflector::Stop(void)
{ {
// stop & delete all threads // stop & delete all threads
m_bStopThreads = true; m_bStopThreads = true;
m_cv.signal();
// stop & delete report threads // stop & delete report threads
if ( m_XmlReportThread != NULL ) if ( m_XmlReportThread != NULL )
@ -388,6 +392,7 @@ void CReflector::RouterThread(CReflector *This, CPacketStream *streamIn)
void CReflector::XmlReportThread(CReflector *This) void CReflector::XmlReportThread(CReflector *This)
{ {
const std::chrono::minutes timeout(XML_UPDATE_PERIOD);
while ( !This->m_bStopThreads ) while ( !This->m_bStopThreads )
{ {
// report to xml file // report to xml file
@ -408,8 +413,7 @@ void CReflector::XmlReportThread(CReflector *This)
} }
#endif #endif
// and wait a bit This->m_cv.wait(timeout, [&]{return This->m_bStopThreads==true;});
CTimePoint::TaskSleepFor(XML_UPDATE_PERIOD * 1000);
} }
} }
@ -493,7 +497,8 @@ void CReflector::JsonReportThread(CReflector *This)
case NOTIFICATION_NONE: case NOTIFICATION_NONE:
default: default:
// nothing to do, just sleep a bit // nothing to do, just sleep a bit
CTimePoint::TaskSleepFor(250); std::chrono::milliseconds timeout(250);
This->m_cv.wait(timeout, [&]{return This->m_bStopThreads==true;});
break; break;
} }
} }

@ -31,6 +31,8 @@
#include "cprotocols.h" #include "cprotocols.h"
#include "cpacketstream.h" #include "cpacketstream.h"
#include "cnotificationqueue.h" #include "cnotificationqueue.h"
#include "cysfnodedir.h"
#include "csimplecondition.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -137,7 +139,8 @@ protected:
std::array<CPacketStream, NB_OF_MODULES> m_Streams; std::array<CPacketStream, NB_OF_MODULES> m_Streams;
// threads // threads
bool m_bStopThreads; CSimpleCondition m_cv;
std::atomic_bool m_bStopThreads;
std::array<std::thread *, NB_OF_MODULES> m_RouterThreads; std::array<std::thread *, NB_OF_MODULES> m_RouterThreads;
std::thread *m_XmlReportThread; std::thread *m_XmlReportThread;
std::thread *m_JsonReportThread; std::thread *m_JsonReportThread;

@ -0,0 +1,41 @@
#pragma once
#include <condition_variable>
#include <mutex>
class CSimpleCondition final
{
public:
CSimpleCondition() : m_Mutex(), m_Condition() {}
CSimpleCondition(const CSimpleCondition&) = delete;
CSimpleCondition& operator=(const CSimpleCondition&) = delete;
CSimpleCondition(CSimpleCondition&&) = delete;
~CSimpleCondition() {};
// Wait up to @duration to be signaled, or until @predicate is true.
// Returns result of predicate after timing out or being signaled.
template<typename Duration, typename Predicate>
bool wait(Duration, Predicate);
// Signal waiters. If @all is true, all waiters will be woken up.
void signal(bool all=true)
{
if (all)
m_Condition.notify_all();
else
m_Condition.notify_one();
}
private:
std::mutex m_Mutex;
std::condition_variable m_Condition;
};
// Note: @timeout is a relative duration, e.g., "30s".
template<typename Duration, typename Predicate>
bool CSimpleCondition::wait(Duration timeout, Predicate predicate)
{
std::unique_lock<std::mutex> lock(m_Mutex);
auto bound = std::chrono::system_clock::now() + timeout;
return m_Condition.wait_until(lock, bound, predicate);
}

@ -92,7 +92,7 @@ protected:
uint16 m_PortOpenStream; uint16 m_PortOpenStream;
// thread // thread
bool m_bStopThread; std::atomic_bool m_bStopThread;
std::thread *m_pThread; std::thread *m_pThread;
// socket // socket

@ -86,7 +86,7 @@ protected:
CWiresxPacketQueue m_PacketQueue; CWiresxPacketQueue m_PacketQueue;
// thread // thread
bool m_bStopThread; std::atomic_bool m_bStopThread;
std::thread *m_pThread; std::thread *m_pThread;
}; };

@ -26,7 +26,7 @@
#include "main.h" #include "main.h"
#include "creflector.h" #include "creflector.h"
#include "cysfnodedir.h" #include "cysfnodedir.h"
#include <chrono>
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// constructor & destructor // constructor & destructor
@ -41,6 +41,7 @@ CYsfNodeDir::~CYsfNodeDir()
{ {
// kill threads // kill threads
m_bStopThread = true; m_bStopThread = true;
m_cv.signal();
if ( m_pThread != NULL ) if ( m_pThread != NULL )
{ {
m_pThread->join(); m_pThread->join();
@ -69,6 +70,7 @@ bool CYsfNodeDir::Init(void)
void CYsfNodeDir::Close(void) void CYsfNodeDir::Close(void)
{ {
m_bStopThread = true; m_bStopThread = true;
m_cv.signal();
if ( m_pThread != NULL ) if ( m_pThread != NULL )
{ {
m_pThread->join(); m_pThread->join();
@ -82,11 +84,9 @@ void CYsfNodeDir::Close(void)
void CYsfNodeDir::Thread(CYsfNodeDir *This) void CYsfNodeDir::Thread(CYsfNodeDir *This)
{ {
while ( !This->m_bStopThread ) const std::chrono::minutes timeout(YSFNODEDB_REFRESH_RATE);
while (!This->m_cv.wait(timeout, [&]{return This->m_bStopThread==true;}))
{ {
// Wait 30 seconds
CTimePoint::TaskSleepFor(YSFNODEDB_REFRESH_RATE * 60000);
// have lists files changed ? // have lists files changed ?
if ( This->NeedReload() ) if ( This->NeedReload() )
{ {

@ -32,6 +32,7 @@
#include "cbuffer.h" #include "cbuffer.h"
#include "ccallsign.h" #include "ccallsign.h"
#include "cysfnode.h" #include "cysfnode.h"
#include "csimplecondition.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// define // define
@ -86,7 +87,8 @@ protected:
std::mutex m_Mutex; std::mutex m_Mutex;
// thread // thread
bool m_bStopThread; CSimpleCondition m_cv;
std::atomic_bool m_bStopThread;
std::thread *m_pThread; std::thread *m_pThread;
}; };

@ -53,7 +53,7 @@ static int wait_for_termination()
sigaddset(&waitset, SIGHUP); sigaddset(&waitset, SIGHUP);
pthread_sigmask(SIG_BLOCK, &waitset, nullptr); pthread_sigmask(SIG_BLOCK, &waitset, nullptr);
// Now wait for termination signal // Wait for a termination signal
int result = -1; int result = -1;
while (result < 0) while (result < 0)
{ {
@ -156,7 +156,7 @@ int main(int argc, const char * argv[])
// and wait for end // and wait for end
wait_for_termination(); wait_for_termination();
g_Reflector->Stop(); g_Reflector.Stop();
std::cout << "Reflector stopped" << std::endl; std::cout << "Reflector stopped" << std::endl;
// done // done

Loading…
Cancel
Save

Powered by TurnKey Linux.