I have a code where I maintain a QList of pointers on a QThread derived class objects.
Simplified example:
main.cpp
#include <QCoreApplication>
#include <QDebug>
#include "myplugin.h"
int main()
{
int loopcount = 0;
myPlugin* plug = new myPlugin();
// Initialization with 2 threads
plug->init(2);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
while(true){
loopcount++;
// a little sleep
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
if(loopcount == 1) {
// simulate detach() condition
plug->genDetach();
}
// check plugin status
bool status = plug->canBeStopped();
// code using status...
// read progress
float prog = plug->getProgress();
// code using progress
}
}
Here is the plugin class:
myplugin.h
#ifndef MYPLUGIN_H
#define MYPLUGIN_H
#include "mythreadedclass.h"
class myPlugin
{
public:
myPlugin();
void init(int nbthreads);
bool canBeStopped();
float getProgress();
void genDetach();
int m_nbthreads;
bool m_detach;
private:
QList <myThreadedClass*> myThreadsList;
};
#endif // MYPLUGIN_H
myplugin.cpp
#include "myplugin.h"
#include <QDebug>
myPlugin::myPlugin()
{
m_nbthreads = 0;
m_detach = false;
}
void myPlugin::genDetach() {
// set a flag to trig the detach()
m_detach = true;
}
void myPlugin::init(int nbthreads) {
m_nbthreads = nbthreads;
myThreadedClass* newthread;
// Create as much threads as required
// and store pointers in a QList
for(int i=0; i<nbthreads;i++){
newthread = new myThreadedClass();
myThreadsList.append(newthread);
newthread->setId(i);
// start the thread
// (QThread::start() calls run()
newthread->start();
}
}
bool myPlugin::canBeStopped() {
bool bRet = true;
QList<myThreadedClass*> tmpThreadList;
// simulate unidentified event leading to a detach() in next range-loop in the real code
if(m_detach){
// This line increments reference count and lead to detach()
// in for range-loop begin() iterator
tmpThreadList = myThreadsList; // QList container copy (on write)
}
// Plugin can be stopped if all his threads can be stopped
// using a range-loop, begin() operator performs a detach()
// if another reference on the QList exists
// detach() will detect that count>1 and make a deep-copy
// of what? (the whole Qlist? the QList item? What if items are pointers?)
// How can m_progress become uninitialized???
// loop shall be changed to for (const auto &i : list) { ... } to avoid detach
for (myThreadedClass * threadInstance: myThreadsList){
// query the thread
bRet &= threadInstance->canBeStopped();
}
// on return, tmpThreadList destructor is called
// can this be a problem?
return bRet;
}
float myPlugin::getProgress() {
float threadProgress = 0;
int threadcount = myThreadsList.count();
myThreadedClass * threadInstance = nullptr;
for (myThreadedClass * threadIter: myThreadsList){
// here getProgress randomly crashes with
// "access to uninitialized value" m_progress after detach
// NOT REPRODUCED WITH THIS EXAMPLE
threadProgress = threadIter->getProgress();
}
return 0;
}
mythreadedclass.h
#ifndef MYTHREADEDCLASS_H
#define MYTHREADEDCLASS_H
#include <QThread>
class myThreadedClass : public QThread
{
public:
// Constructor
myThreadedClass();
void run(); // re-implemented; called by QThread::start()
void setId(int idnum);
bool canBeStopped();
float getProgress() const;
bool running;
int m_id;
int m_loop;
protected:
float m_progress;
};
#endif // MYTHREADEDCLASS_H
mythreadedclass.cpp
#include "mythreadedclass.h"
#include <QDebug>
// Constructor
myThreadedClass::myThreadedClass()
{
running = false;
m_loop = 1;
m_progress = 0; // initialized in the constructor
}
void myThreadedClass::setId(int idnum) {
qDebug()<<"myThreadClass::setId thread id"<<idnum;
m_id = idnum;
}
void myThreadedClass::run()
{
running = true;
while(running) {
qDebug()<<"myThreadClass::run thread id"<<m_id
<<" loop "<< m_loop++;
// Here a lot of processing code...
// modifying m_progress at the end
sleep(2);
}
}
bool myThreadedClass::canBeStopped() {
// Here code setting running value...
return (running == false);
}
float myThreadedClass::getProgress() const
{
// How can the data be uninitialized???
return m_progress; // return protected data value
}
Regularly, I parse this QList to access each of my objects.
The code can run with no problem for hours...
But randomly, the programm crashes on the second loop (in getProgress), with a message saying that I try to access to uninitialized data in myThreadedClass::getProgress().
The data in question is a m_progress, a protected member initialized in the constructor.
(I have no idea if it is linked to the fact it is protected...)
I have catched the segfault with valgrind and gdb, and it appears that because I use a for range-loop to parse the QList (in canBeStopped()), a detach() occurs.
valgrind output:
==4518== Use of uninitialised value of size 8
==4518== at 0xB6C7608: xxx::getProgress() const (xxx.cpp:42)
...
...
==4518== Uninitialised value was created by a heap allocation
==4518== at 0x4A06A2E: malloc (vg_replace_malloc.c:270)
==4518== by 0x300AC8DB4C: QListData::detach3() (in /usr/lib64/libQtCore.so.4.6.2)
==4518== by 0xB6C27FC: QList<myThreadedClass*>::detach_helper() (qlist.h:604)
==4518== by 0xB6C110B: QList<myThreadedClass*>::detach() (in xxxxxxxx.so)
==4518== by 0xB6BF1C1: QList<myThreadedClass*>::begin() (qlist.h:248)
==4518== by 0xB6BD49B: xxx::canBeStopped() (xxx.cpp:691)
(I have to analyze parts of my code to identify the other reference to my object that lead to the detach, but I can simulate it within the example.)
So my question:
How can the detach() in the 1st loop make the m_progress variable become uninitialized in the 2nd loop???
could the deep-copy occuring with the detach be incomplete?
And for my information, can someone explain what is exacly duplicated during detach(), when the QList only stores pointers to objects?
How can I trace or identify the original and the copy, print their address or something?
Context: QT4.8.7 SL6 or Centos8.3 or Ubuntu
I have a struct instance that gets used by multiple threads. Each thread contains an unknown amount of function calls that alter the struct member variable.
I have a dedicated function that tries to "reserve" the struct instance for the current thread and I would like to ensure no other thread can reserve the instance till the original thread allows it.
Mutexes come to mind as those can be used to guard resources, but I only know of std::lock_guard that are in the scope of a single function, but do not add protection for all function calls in between lock and unlock.
Is it possible to protect a resource like that, when I know it will always call reserve and release in that order?
Snippet that explains it better:
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
struct information_t {
std::mutex mtx;
int importantValue = 0;
// These should only be callable from the thread that currently holds the mutex
void incrementIt() { importantValue++; }
void decrementIt() { importantValue--; }
void reset() { importantValue = 0; }
} protectedResource; // We only have one instance of this that we need to work with
// Free the resource so other threads can reserve and use it
void release()
{
std::cout << "Result: " << protectedResource.importantValue << '\n';
protectedResource.reset();
protectedResource.mtx.unlock(); // Will this work? Can I guarantee the mtx is locked?
}
// Supposed to make sure no other thread can reserve or use it now anymore!
void reserve()
{
protectedResource.mtx.lock();
}
int main()
{
std::thread threads[3];
threads[0] = std::thread([]
{
reserve();
protectedResource.incrementIt();
protectedResource.incrementIt();
release();
});
threads[1] = std::thread([]
{
reserve();
// do nothing
release();
});
threads[2] = std::thread([]
{
reserve();
protectedResource.decrementIt();
release();
});
for (auto& th : threads) th.join();
return 0;
}
My suggestion per comment:
A better idiom might be a monitor which keeps the lock of your resource and provides access to the owner. To obtain a resource, the reserve() could return such monitor object (something like a proxy to access the contents of the resource). Any competing access to reserve() would block now (as the mutex is locked). When the resource owning thread is done, it just destroys the monitor object which in turn unlocks the resource. (This allows to apply RAII to all this which makes your code safe and maintainable.)
I modified OPs code to sketch how this could look like:
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
class information_t {
private:
std::mutex mtx;
int importantValue = 0;
public:
class Monitor {
private:
information_t& resource;
std::lock_guard<std::mutex> lock;
friend class information_t; // to allow access to constructor.
private:
Monitor(information_t& resource):
resource(resource), lock(resource.mtx)
{ }
public:
~Monitor()
{
std::cout << "Result: " << resource.importantValue << '\n';
resource.reset();
}
Monitor(const Monitor&) = delete; // copying prohibited
Monitor& operator=(const Monitor&) = delete; // copy assign prohibited
public:
// exposed resource API for monitor owner:
void incrementIt() { resource.incrementIt(); }
void decrementIt() { resource.decrementIt(); }
void reset() { resource.reset(); }
};
friend class Monitor; // to allow access to private members
public:
Monitor aquire() { return Monitor(*this); }
private:
// These should only be callable from the thread that currently holds the mutex
// Hence, they are private and accessible through a monitor instance only
void incrementIt() { importantValue++; }
void decrementIt() { importantValue--; }
void reset() { importantValue = 0; }
} protectedResource; // We only have one instance of this that we need to work with
#if 0 // OBSOLETE
// Free the resource so other threads can reserve and use it
void release()
{
protectedResource.reset();
protectedResource.mtx.unlock(); // Will this work? Can I guarantee the mtx is locked?
}
#endif // 0
// Supposed to make sure no other thread can reserve or use it now anymore!
information_t::Monitor reserve()
{
return protectedResource.aquire();
}
using MyResource = information_t::Monitor;
int main()
{
std::thread threads[3];
threads[0]
= std::thread([]
{
MyResource protectedResource = reserve();
protectedResource.incrementIt();
protectedResource.incrementIt();
// scope end releases protectedResource
});
threads[1]
= std::thread([]
{
try {
MyResource protectedResource = reserve();
throw "Haha!";
protectedResource.incrementIt();
// scope end releases protectedResource
} catch(...) { }
});
threads[2]
= std::thread([]
{
MyResource protectedResource = reserve();
protectedResource.decrementIt();
// scope end releases protectedResource
});
for (auto& th : threads) th.join();
return 0;
}
Output:
Result: 2
Result: -1
Result: 0
Live Demo on coliru
Is it possible to protect a resource like that, when I know it will always call reserve and release in that order?
It's not anymore necessary to be concerned about this. The correct usage is burnt in:
To get access to the resource, you need a monitor.
If you get it you are the exclusive owner of the resource.
If you exit the scope (where you stored the monitor as local variable) the monitor is destroyed and thus the locked resource auto-released.
The latter will happen even for unexpected bail-outs (in the MCVE the throw "Haha!";).
Furthermore, I made the following functions private:
information_t::increment()
information_t::decrement()
information_t::reset()
So, no unauthorized access is possible. To use them properly, an information_t::Monitor instance must be acquired. It provides public wrappers to those functions which can be used in the scope where the monitor resides i.e. by the owner thread only.
Here is a simplified observer pattern:
one creator creates a profile when it starts and "destroy" it when it is done.
zero, one or more observers try to "look at" the profile at any time.
To implement it, the trick is that observers shall refcnt profile, so the last observer (or creator) can safely destroy it.
I can do it without shared_ptr/weak_ptr, but I wonder if using them can avoid re-inventing wheels.
Here is my code:
#include <iostream>
#include <memory>
#include <thread>
#include <cassert>
volatile bool playing = true;
class Profile {
public:
int a_;
Profile(int v) {a_ = v;}
};
std::shared_ptr<Profile> g_profile{ nullptr };
void observer() {
do {
// observe profile if I can
std::weak_ptr<Profile> weak = g_profile;
if (auto prof = weak.lock()) {
auto a = prof->a_;
// if prof is stable, I shall see the same a_
assert(a == prof->a_);
}
else {
std::cout << ".";
}
} while (playing);
}
void creator() {
do {
// create profile when I start
g_profile.reset(new Profile(std::rand()));
std::weak_ptr<Profile> weak = g_profile;
assert(weak.lock() != nullptr);
// doing some work ...
// destroy profile when I am done
g_profile.reset();
} while (playing);
}
void timer() {
std::this_thread::sleep_for(std::chrono::seconds(10));
playing = false;
}
int main() {
std::thread cr{ creator };
std::thread ob{ observer };
std::thread tm{ timer };
cr.join();ob.join();tm.join();
// no memory leak
}
But the program crashes either at
std::weak_ptr<Profile> weak = g_profile or assert(a == prof->a_). So here are my questions:
do you have a pointer implementing observer pattern (or variant) with shared_ptr/weak_ptr?
what's wrong with the above code? Can you make it right?
You have undefined bahavior when one thread reads from the shared pointer g_profile (observer) while the other thread writes to it (when creator calls std::shared_ptr::reset)
If you want to use the shared_ptr from two threads you'll have to use a lock or atomic_shared_ptr.
Also volatile does not guarantee any synchronization as it does in java. See this answer.
Problem
How do you initialize an object inside a RAII scope, and use it outside of that scope?
Background
I have a global lock which can be called with lock() and unlock().
I have a type, LockedObject, which can only be initialized when the global lock is locked.
I have a function, use_locked(LockedObject &locked_object), which needs to be called with the global lock unlocked.
The usage scenario is
lock();
LockedObject locked_object;
unlock();
use_locked(locked_object);
RAII
For various reasons, I moved to a RAII encapsulation of the global lock. I would like to use this everywhere, primarily as creating LockedObject can fail with exceptions.
The problem is that
{
GlobalLock global_lock;
LockedObject locked_object;
}
use_locked(locked_object);
fails, as locked_object is created in the inner scope.
Examples
Set-up (mostly not important):
#include <assert.h>
#include <iostream>
bool locked = false;
void lock() {
assert(!locked);
locked = true;
}
void unlock() {
assert(locked);
locked = false;
}
class LockedObject {
public:
LockedObject(int i) {
assert(locked);
std::cout << "Initialized: " << i << std::endl;
}
};
void use_locked(LockedObject locked_object) {
assert(!locked);
}
class GlobalLock {
public:
GlobalLock() {
lock();
}
~GlobalLock() {
unlock();
}
};
Original, non RAII method:
void manual() {
lock();
LockedObject locked_object(123);
unlock();
use_locked(locked_object);
}
Broken RAII methods:
/*
void raii_broken_scoping() {
{
GlobalLock global_lock;
// Initialized in the wrong scope
LockedObject locked_object(123);
}
use_locked(locked_object);
}
*/
/*
void raii_broken_initialization() {
// No empty initialization
// Alternatively, empty initialization requires lock
LockedObject locked_object;
{
GlobalLock global_lock;
locked_object = LockedObject(123);
}
use_locked(locked_object);
}
*/
And a main function:
int main(int, char **) {
manual();
// raii_broken_scoping();
// raii_broken_initialization;
}
For what it's worth, in Python I would do:
with GlobalLock():
locked_object = LockedObject(123)
I want the equivalent of that. I mention my current solution in an answer, but it feels clumsy.
The specific (but simplified) code to be executed follows. With my current lambda-based call:
boost::python::api::object wrapped_object = [&c_object] () {
GIL lock_gil;
return boost::python::api::object(boost::ref(c_object));
} ();
auto thread = std::thread(use_wrapped_object, c_object);
with
class GIL {
public:
GIL();
~GIL();
private:
GIL(const GIL&);
PyGILState_STATE gilstate;
};
GIL::GIL() {
gilstate = PyGILState_Ensure();
}
GIL::~GIL() {
PyGILState_Release(gilstate);
}
boost::python::api::objects must be created with the GIL and the thread must be created without the GIL. The PyGILState struct and function calls are all given to me by CPython's C API, so I can only wrap them.
Allocate your object on the heap and use some pointers:
std::unique_ptr<LockedObject> locked_object;
{
GlobalLock global_lock;
locked_object.reset(new LockedObject());
}
use_locked(locked_object);
Here is a complete list of options from my perspective. optional would be what I would do:
The proposed post-C++1y optional would solve your problem, as it lets you construct data after declaration, as would heap based unique_ptr solutions. Roll your own, or steal ot from boost
A 'run at end of scope' RAII function storer (with 'commit') can also make this code less crazy, as can letting your locks be manually disengaged within their scope.
template<class F>
struct run_at_end_of_scope {
F f;
bool Skip;
void commit(){ if (!Skip) f(); Skip = true; }
void skip() { Skip = true; }
~run_at_end_of_scope(){commit();}
};
template<class F>
run_at_end_of_scope<F> at_end(F&&f){ return {std::forward<F>(f), false}; }
then:
auto later = at_end([&]{ /*code*/ });
and you can later.commit(); or later.skip(); to run the code earlier or skip running it.
Making your RAII locking classes have move constructors would let you do construction in another scope, and return via move (possibly elided).
LockedObject make_LockedObject(){
GlobalLock lock;
return {};
}
My current solution is to use an anonymous function:
void raii_return() {
LockedObject locked_object = [&] () {
GlobalLock global_lock;
return LockedObject(123);
} ();
use_locked(locked_object);
}
The advantage of this approach is that it avoids pointers and thanks to copy elision it should be quite fast.
One downside is that LockedObjects don't necessarily support copying (use_locked would in that case take a reference).
I am trying to design a multithreaded event system in C++. In it, the objects may be located in different threads and every object should be able to queue events for other threads. Each thread has its own event queue and event dispatcher, as well as an event loop. It should be possible to change the thread affinity of the objects.
Let's say we have two threads: A and B, and an object myobj, which belongs to B. Obviously, A needs a pointer to myobj in order to be able to send events to it. A doesn't have any pointer to B, but it needs some way to get a reference to it in order to be able to lock the event queue and add the event to it.
I could store a pointer to B in myobj, but then I obviously need to protect myobj. If I place a mutex in myobj, myobj could be destructed while the mutex is being locked, thus causing a segmentation fault.
I could also use a global table where I associate each object with its corresponding thread. However, this would consume a lot of memory and cause any thread that wants to send an event to block until A has finish
ed.
What is the most efficient safe strategy to implement this? Is there perhaps some kind of design pattern for this?
Thanks in advance.
I've implemented a thread wrapper base class ThreadEventComponent for sending and processing events between instances of itself. Each ThreadEventComponent has it's own event queue that is automatically locked internally whenever used. The events themselves are negotiated by a static map of type map<EventKey, vector<ThreadEventComponent*>> that is also automatically locked whenever used. As you can see, multiple ThreadEventComponent derived instances can subscribe to the same event. Each event sent with SendEvent(Event*) is copied per instance to insure that multiple threads aren't fighting over the same data held within the event.
Admittedly, this is not the most efficient strategy, opposed to sharing memory. There are optimizations to be made regarding the addEvent(Event&)method. With drawbacks aside, it does work well for configuring a thread to do some operation outside of the main thread.
Both MainLoop() and ProcessEvent(Event*) are virtual functions to be implemented by the derived class. ProcessEvent(Event*) is called whenever an event is available in the queue. After that, MainLoop() is called regardless of the event queue state. MainLoop() is where you should tell your thread to sleep and where any other operations such as file reading/writing or network reading/writing should go.
The following code is something I've been working on for my own person use to get my head wrapped around threading in C++. This code has never been reviewed, so I'd love to hear any suggestions you have. I am aware of two elements that are less than desirable in this code sample. 1) I'm using new at run-time, the drawback being that finding memory takes time, but this can be mitigated by creating a memory buffer to construct new events over in the ThreadEventComponent base class. 2)Event casting to TEvent<T> can cause run-time errors if not implemented correctly in ProcessEvent. I'm not sure what the best solution for this is.
Note: I have EventKey implemented as a string, but you can change it to whatever type you wish as long as it has a default value along with the equality and assignment operators available.
Event.h
#include <string>
using namespace std;
typedef string EventKey;
class Event
{
public:
Event()
: mKey()
{
}
Event(EventKey key)
: mKey(key)
{
}
Event(const Event& e)
: mKey(e.mKey)
{
}
virtual ~Event()
{
}
EventKey GetKey()
{
return mKey;
}
protected:
EventKey mKey;
};
template<class T>
class TEvent : public Event
{
public:
TEvent()
: Event()
{
}
TEvent(EventKey type, T& object)
: Event(type), mObject(object)
{
}
TEvent(const TEvent<T>& e)
: Event(e.mKey), mObject(e.mObject)
{
}
virtual ~TEvent()
{
}
T& GetObject()
{
return mObject;
}
private:
T mObject;
};
ThreadEventComponent.h
#include "Event.h"
#include <thread>
#include <atomic>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <mutex>
#include <assert.h>
class ThreadEventComponent
{
public:
ThreadEventComponent();
~ThreadEventComponent();
void Start(bool detached = false);
void Stop();
void ForceStop();
void WaitToFinish();
virtual void Init() = 0;
virtual void MainLoop() = 0;
virtual void ProcessEvent(Event* incoming) = 0;
template<class T>
void SendEvent(TEvent<T>& e)
{
sEventListLocker.lock();
EventKey key = e.GetKey();
for (unsigned int i = 0; i < sEventList[key].size(); i++)
{
assert(sEventList[key][i] != nullptr);
sEventList[key][i]->addEvent<T>(e);
}
sEventListLocker.unlock();
}
void SendEvent(Event& e);
void Subscribe(EventKey key);
void Unsubscribe(EventKey key);
protected:
template<class T>
void addEvent(TEvent<T>& e)
{
mQueueLocker.lock();
// The event gets copied per thread
mEventQueue.push(new TEvent<T>(e));
mQueueLocker.unlock();
}
void addEvent(Event& e);
thread mThread;
atomic<bool> mShouldExit;
private:
void threadLoop();
queue<Event*> mEventQueue;
mutex mQueueLocker;
typedef map<EventKey, vector<ThreadEventComponent*>> EventMap;
static EventMap sEventList;
static mutex sEventListLocker;
};
ThreadEventComponent.cpp
#include "ThreadEventComponent.h"
ThreadEventComponent::EventMap ThreadEventComponent::sEventList = ThreadEventComponent::EventMap();
std::mutex ThreadEventComponent::sEventListLocker;
ThreadEventComponent::ThreadEventComponent()
{
mShouldExit = false;
}
ThreadEventComponent::~ThreadEventComponent()
{
}
void ThreadEventComponent::Start(bool detached)
{
mShouldExit = false;
mThread = thread(&ThreadEventComponent::threadLoop, this);
if (detached)
mThread.detach();
}
void ThreadEventComponent::Stop()
{
mShouldExit = true;
}
void ThreadEventComponent::ForceStop()
{
mQueueLocker.lock();
while (!mEventQueue.empty())
{
delete mEventQueue.front();
mEventQueue.pop();
}
mQueueLocker.unlock();
mShouldExit = true;
}
void ThreadEventComponent::WaitToFinish()
{
if(mThread.joinable())
mThread.join();
}
void ThreadEventComponent::SendEvent(Event& e)
{
sEventListLocker.lock();
EventKey key = e.GetKey();
for (unsigned int i = 0; i < sEventList[key].size(); i++)
{
assert(sEventList[key][i] != nullptr);
sEventList[key][i]->addEvent(e);
}
sEventListLocker.unlock();
}
void ThreadEventComponent::Subscribe(EventKey key)
{
sEventListLocker.lock();
if (find(sEventList[key].begin(), sEventList[key].end(), this) == sEventList[key].end())
{
sEventList[key].push_back(this);
}
sEventListLocker.unlock();
}
void ThreadEventComponent::Unsubscribe(EventKey key)
{
sEventListLocker.lock();
// Finds event listener of correct type
EventMap::iterator mapIt = sEventList.find(key);
assert(mapIt != sEventList.end());
// Finds the pointer to itself
std::vector<ThreadEventComponent*>::iterator elIt =
std::find(mapIt->second.begin(), mapIt->second.end(), this);
assert(elIt != mapIt->second.end());
// Removes it from the event list
mapIt->second.erase(elIt);
sEventListLocker.unlock();
}
void ThreadEventComponent::addEvent(Event& e)
{
mQueueLocker.lock();
// The event gets copied per thread
mEventQueue.push(new Event(e));
mQueueLocker.unlock();
}
void ThreadEventComponent::threadLoop()
{
Init();
bool shouldExit = false;
while (!shouldExit)
{
if (mQueueLocker.try_lock())
{
if (mEventQueue.empty())
{
mQueueLocker.unlock();
if(mShouldExit)
shouldExit = true;
}
else
{
Event* e = mEventQueue.front();
mEventQueue.pop();
mQueueLocker.unlock();
ProcessEvent(e);
delete e;
}
}
MainLoop();
}
}
Example Class - A.h
#include "ThreadEventComponent.h"
class A : public ThreadEventComponent
{
public:
A() : ThreadEventComponent()
{
}
void Init()
{
Subscribe("a stop");
Subscribe("a");
}
void MainLoop()
{
this_thread::sleep_for(50ms);
}
void ProcessEvent(Event* incoming)
{
if (incoming->GetKey() == "a")
{
auto e = static_cast<TEvent<vector<int>>*>(incoming);
mData = e->GetObject();
for (unsigned int i = 0; i < mData.size(); i++)
{
mData[i] = sqrt(mData[i]);
}
SendEvent(TEvent<vector<int>>("a done", mData));
}
else if(incoming->GetKey() == "a stop")
{
StopWhenDone();
}
}
private:
vector<int> mData;
};
Example Class - B.h
#include "ThreadEventComponent.h"
int compare(const void * a, const void * b)
{
return (*(int*)a - *(int*)b);
}
class B : public ThreadEventComponent
{
public:
B() : ThreadEventComponent()
{
}
void Init()
{
Subscribe("b stop");
Subscribe("b");
}
void MainLoop()
{
this_thread::sleep_for(50ms);
}
void ProcessEvent(Event* incoming)
{
if (incoming->GetKey() == "b")
{
auto e = static_cast<TEvent<vector<int>>*>(incoming);
mData = e->GetObject();
qsort(&mData[0], mData.size(), sizeof(int), compare);
SendEvent(TEvent<vector<int>>("b done", mData));
}
else if (incoming->GetKey() == "b stop")
{
StopWhenDone();
}
}
private:
vector<int> mData;
};
Test Example - main.cpp
#include <iostream>
#include <random>
#include "A.h"
#include "B.h"
class Master : public ThreadEventComponent
{
public:
Master() : ThreadEventComponent()
{
}
void Init()
{
Subscribe("a done");
Subscribe("b done");
}
void MainLoop()
{
this_thread::sleep_for(50ms);
}
void ProcessEvent(Event* incoming)
{
if (incoming->GetKey() == "a done")
{
TEvent<vector<int>>* e = static_cast<TEvent<vector<int>>*>(incoming);
cout << "A finished" << endl;
mDataSetA = e->GetObject();
for (unsigned int i = 0; i < mDataSetA.size(); i++)
{
cout << mDataSetA[i] << " ";
}
cout << endl << endl;
}
else if (incoming->GetKey() == "b done")
{
TEvent<vector<int>>* e = static_cast<TEvent<vector<int>>*>(incoming);
cout << "B finished" << endl;
mDataSetB = e->GetObject();
for (unsigned int i = 0; i < mDataSetB.size(); i++)
{
cout << mDataSetB[i] << " ";
}
cout << endl << endl;
}
}
private:
vector<int> mDataSetA;
vector<int> mDataSetB;
};
int main()
{
srand(time(0));
A a;
B b;
a.Start();
b.Start();
vector<int> data;
for (int i = 0; i < 100; i++)
{
data.push_back(rand() % 100);
}
Master master;
master.Start();
master.SendEvent(TEvent<vector<int>>("a", data));
master.SendEvent(TEvent<vector<int>>("b", data));
master.SendEvent(TEvent<vector<int>>("a", data));
master.SendEvent(TEvent<vector<int>>("b", data));
master.SendEvent(Event("a stop"));
master.SendEvent(Event("b stop"));
a.WaitToFinish();
b.WaitToFinish();
// cin.get();
master.StopWhenDone();
master.WaitToFinish();
return EXIT_SUCCESS;
}
I have not used it myself, but Boost.Signals2 claims to be thread-safe.
The primary motivation for Boost.Signals2 is to provide a version of the original Boost.Signals library which can be used safely in a multi-threaded environment.
Of course, using this would make your project depend on boost, which might not be in your interest.
[edit] It seems slots are executed in the emitting thread (no queue), so this might not be what you had in mind after all.
I'd consider making the thread part of classes to encapsulate them. That way you can easily design your interfaces around the thread loops (provided as member functions of these classes) and have defined entry points to send data to the thread loop (e.g. using a std::queue protected with a mutex).
I don't know if this is a designated, well known design pattern, but that's what I'm using for my all day productive code at work, and I (and my colleagues) feel and experience pretty good with it.
I'll try to give you a point:
class A {
public:
A() {}
bool start();
bool stop();
bool terminate() const;
void terminate(bool value);
int data() const;
void data(int value);
private:
std::thread thread_;
void threadLoop();
bool terminate_;
mutable std::mutex internalDataGuard_;
int data_;
};
bool A::start() {
thread_ = std::thread(std::bind(this,threadLoop));
return true;
}
bool A::stop() {
terminate(true);
thread_.join();
return true;
}
bool A::terminate() const {
std::lock_guard<std::mutex> lock(internalDataGuard_);
return terminate_;
}
void A::terminate(bool value) {
std::lock_guard<std::mutex> lock(internalDataGuard_);
terminate_ = value;
}
int A::data() const {
std::lock_guard<std::mutex> lock(internalDataGuard_);
return data_;
}
void A::data(int value) {
std::lock_guard<std::mutex> lock(internalDataGuard_);
data_ = value;
// Notify thread loop about data changes
}
void A::threadLoop() {
while(!terminate())
{
// Wait (blocking) for data changes
}
}
To setup signalling of data changes there are several choices and (OS) constraints:
The simplest thing you could use to wake up the thread loop to process changed/new data is a semaphore. In c++11 the nearest approx for a semaphore is a condition variable. Advanced versions of the pthreads API also provide condition variable support. Anyway since only one thread should be waiting there, and no kind of event broadcasing is necessary, it should be easy to implement with simple locking mechanisms.
If you have the choice to use an advanced OS, you might prefer implementing event signalling using s.th. like poll(), which provides lock-free implementation at the user space.
Some frameworks like boost, Qt, Platinum C++, and others also support event handling by signal/slot abstractions, you might have a look at their documentation and implementation to get a grip what's necessary/state of the art.
Obviously, A needs a pointer to myobj in order to be able to send
events to it.
I question the above assumption -- To me, allowing thread A to have a pointer to an object that is controlled/owned/accessed by thread B is kind of asking for trouble... in particular, some code running in thread A might be tempted later on to use that pointer to directly call methods on myobj, causing race conditions and discord; or B might delete myobj, at which point A is holding a dangling-pointer and is thereby in a precarious state.
If I was designing the system, I would try to do it in such a way that cross-thread messaging was done without requiring pointers-to-objects-in-other-threads, for the reasons you mention -- they are unsafe, in particular such a pointer might become a dangling-pointer at any time.
So then the question becomes, how do I send a message to an object in another thread, if I don't have a pointer to that object?
One way would be to give each object a unique ID by which it can be specified. This ID could be an integer (either hard-coded or dynamically assigned using an atomic counter or similar), or perhaps a short string if you wanted it to be more easily human-readable.
Then instead of the code in thread A sending the message directly to myobj, it would send a message to thread B, and the message would include a field indicating the ID of the object that is intended to receive the message.
When thread B's event loop receives the message, it would use the included ID value to look up the appropriate object (using an efficient key-value lookup mechanism such as std::unordered_map) and call the appropriate method on that object. If the object had already been destroyed, then the key-value lookup would fail (because you'd have a mechanism to make sure that the object removed itself from its thread's object-map as part of its destructor), and thus trying to send a message to a destroyed-object would fail cleanly (as opposed to invoking undefined behavior).
Note that this approach does mean that thread A's code has to know which thread myobj is owned by, in order to know which thread to send the message to. Typically thread A would need to know that anyway, but if you're going for a design that abstracts away even the knowledge about which thread a given object is running in, you could include an owner-thread-ID as part of the object-ID, so that your postMessage() method could examine the destination-object-ID to figure out which thread to send the message to.