Use C++11 threads instead of custom pthread wrappers.

Signal handling of CThread was handled in previous commit.
This commit is contained in:
Alexey Sokolov
2015-10-31 22:08:23 +00:00
parent dfa94d6bc1
commit 9c48119897
3 changed files with 16 additions and 170 deletions
+5 -162
View File
@@ -29,179 +29,27 @@
#include <cstring>
#include <list>
#include <pthread.h>
#include <mutex>
#include <condition_variable>
/**
* This class represents a non-recursive mutex. Only a single thread may own the
* mutex at any point in time.
*/
class CMutex {
public:
friend class CConditionVariable;
CMutex() : m_mutex() {
int i = pthread_mutex_init(&m_mutex, nullptr);
if (i) {
CUtils::PrintError("Can't initialize mutex: " + CString(strerror(errno)));
exit(1);
}
}
~CMutex() {
int i = pthread_mutex_destroy(&m_mutex);
if (i) {
CUtils::PrintError("Can't destroy mutex: " + CString(strerror(errno)));
exit(1);
}
}
void lock() {
int i = pthread_mutex_lock(&m_mutex);
if (i) {
CUtils::PrintError("Can't lock mutex: " + CString(strerror(errno)));
exit(1);
}
}
void unlock() {
int i = pthread_mutex_unlock(&m_mutex);
if (i) {
CUtils::PrintError("Can't unlock mutex: " + CString(strerror(errno)));
exit(1);
}
}
private:
// Undefined copy constructor and assignment operator
CMutex(const CMutex&);
CMutex& operator=(const CMutex&);
pthread_mutex_t m_mutex;
};
using CMutex = std::mutex;
/**
* A mutex locker should always be used as an automatic variable. This
* class makes sure that the mutex is unlocked when this class is destructed.
* For example, this makes it easier to make code exception-safe.
*/
class CMutexLocker {
public:
CMutexLocker(CMutex& mutex, bool initiallyLocked = true)
: m_mutex(mutex), m_locked(false) {
if (initiallyLocked)
lock();
}
~CMutexLocker() {
if (m_locked)
unlock();
}
void lock() {
assert(!m_locked);
m_mutex.lock();
m_locked = true;
}
void unlock() {
assert(m_locked);
m_locked = false;
m_mutex.unlock();
}
private:
// Undefined copy constructor and assignment operator
CMutexLocker(const CMutexLocker&);
CMutexLocker& operator=(const CMutexLocker&);
CMutex &m_mutex;
bool m_locked;
};
using CMutexLocker = std::unique_lock<std::mutex>;
/**
* A condition variable makes it possible for threads to wait until some
* condition is reached at which point the thread can wake up again.
*/
class CConditionVariable {
public:
CConditionVariable() : m_cond() {
int i = pthread_cond_init(&m_cond, nullptr);
if (i) {
CUtils::PrintError("Can't initialize condition variable: "
+ CString(strerror(errno)));
exit(1);
}
}
~CConditionVariable() {
int i = pthread_cond_destroy(&m_cond);
if (i) {
CUtils::PrintError("Can't destroy condition variable: "
+ CString(strerror(errno)));
exit(1);
}
}
void wait(CMutex& mutex) {
int i = pthread_cond_wait(&m_cond, &mutex.m_mutex);
if (i) {
CUtils::PrintError("Can't wait on condition variable: "
+ CString(strerror(errno)));
exit(1);
}
}
void signal() {
int i = pthread_cond_signal(&m_cond);
if (i) {
CUtils::PrintError("Can't signal condition variable: "
+ CString(strerror(errno)));
exit(1);
}
}
void broadcast() {
int i = pthread_cond_broadcast(&m_cond);
if (i) {
CUtils::PrintError("Can't broadcast condition variable: "
+ CString(strerror(errno)));
exit(1);
}
}
private:
// Undefined copy constructor and assignment operator
CConditionVariable(const CConditionVariable&);
CConditionVariable& operator=(const CConditionVariable&);
pthread_cond_t m_cond;
};
class CThread {
public:
typedef void *threadRoutine(void *);
static void startThread(threadRoutine *func, void *arg) {
pthread_t thr;
sigset_t old_sigmask, sigmask;
/* Block all signals. The thread will inherit our signal mask
* and thus won't ever try to handle signals.
*/
int i = sigfillset(&sigmask);
i |= pthread_sigmask(SIG_SETMASK, &sigmask, &old_sigmask);
i |= pthread_create(&thr, nullptr, func, arg);
i |= pthread_sigmask(SIG_SETMASK, &old_sigmask, nullptr);
i |= pthread_detach(thr);
if (i) {
CUtils::PrintError("Can't start new thread: "
+ CString(strerror(errno)));
exit(1);
}
}
private:
// Undefined constructor
CThread();
};
using CConditionVariable = std::condition_variable_any;
/**
* A job is a task which should run without blocking the main thread. You do
@@ -292,11 +140,6 @@ private:
void finishJob(CJob *) const;
void threadFunc();
static void *threadPoolFunc(void *arg) {
CThreadPool &pool = *reinterpret_cast<CThreadPool *>(arg);
pool.threadFunc();
return nullptr;
}
// mutex protecting all of these members
CMutex m_mutex;
+8 -5
View File
@@ -20,6 +20,7 @@
#include <znc/ZNCDebug.h>
#include <algorithm>
#include <thread>
/* Just an arbitrary limit for the number of idle threads */
static const size_t MAX_IDLE_THREADS = 3;
@@ -49,7 +50,7 @@ void CThreadPool::jobDone(CJob* job) {
if (oldState == CJob::CANCELLED) {
// Signal the main thread that cancellation is done
m_cancellationCond.signal();
m_cancellationCond.notify_one();
return;
}
@@ -88,7 +89,7 @@ CThreadPool::~CThreadPool() {
m_done = true;
while (m_num_threads > 0) {
m_cond.broadcast();
m_cond.notify_all();
m_exit_cond.wait(m_mutex);
}
}
@@ -133,7 +134,7 @@ void CThreadPool::threadFunc() {
m_num_idle--;
if (m_num_threads == 0 && m_done)
m_exit_cond.signal();
m_exit_cond.notify_one();
}
void CThreadPool::addJob(CJob *job) {
@@ -142,7 +143,7 @@ void CThreadPool::addJob(CJob *job) {
// Do we already have a thread which can handle this job?
if (m_num_idle > 0) {
m_cond.signal();
m_cond.notify_one();
return;
}
@@ -153,7 +154,9 @@ void CThreadPool::addJob(CJob *job) {
// Start a new thread for our pool
m_num_threads++;
CThread::startThread(threadPoolFunc, this);
std::thread([this]() {
threadFunc();
}).detach();
}
void CThreadPool::cancelJob(CJob *job) {
+3 -3
View File
@@ -40,14 +40,14 @@ public:
// and signal it to exit
m_bThreadDone = true;
m_CV.broadcast();
m_CV.notify_all();
}
virtual void runThread() {
CMutexLocker locker(m_Mutex);
// We are running
m_bThreadReady = true;
m_CV.broadcast();
m_CV.notify_all();
// wait for our exit signal
while (!m_bThreadDone)
@@ -97,7 +97,7 @@ public:
m_Mutex.lock();
// We are running, tell the main thread
m_bThreadReady = true;
m_CVThreadReady.broadcast();
m_CVThreadReady.notify_all();
// Have to unlock here so that wait() can get the mutex
m_Mutex.unlock();