Trying to access an object that is being destroyed - c++

I have an object which contains a thread which indirectly accesses this object like so:
#include <iostream>
#include <thread>
#include <atomic>
class A;
class Manager
{
public:
Manager(void) = default;
void StartA(void)
{
a = std::make_unique<A>(*this);
}
void StopA(void)
{
a = nullptr;
}
A& GetA(void)
{
return *a;
}
private:
std::unique_ptr<A> a;
};
class A
{
public:
A(Manager& manager)
: manager{manager},
shouldwork{true},
thread{[&]{ this->Run(); }}
{
}
~A(void)
{
shouldwork = false;
thread.join();
}
private:
Manager& manager;
std::atomic<bool> shouldwork;
std::thread thread;
void Run(void)
{
while (shouldwork)
{
// Here goes a lot of code which calls manager.GetA().
auto& a = manager.GetA();
}
}
};
int main(int argc, char* argv[])
try
{
Manager man;
man.StartA();
man.StopA();
}
catch (std::exception& e)
{
std::cerr << "Exception caught: " << e.what() << '\n';
}
catch (...)
{
std::cerr << "Unknown exception.\n";
}
The problem is that when one thread calls Manager::StopA and enters destructor of A, the thread inside A segfaults at Manager::GetA. How can I fix this?

In StopA() you set a = nullptr;, this in turn destroys the a object and all further access to its members result in undefined behaviour (a likely cause the segmentation fault).
Simply moving the a = nullptr; to the destructor of the Manager could resolve this problem. Even better, allow the RAII mechanism of the std::unique_ptr to destroy the a object when the destructor of the Manager runs (i.e. remove the line of code completely).
With active object implementations, careful control of the member variables is important, especially the "stop variable/control" (here the shouldwork = false;). Allow the manager to access the variable directly or via a method to stop the active object before its destruction.
Some of the code here looks out of place or obscure, e.g. a = std::make_unique<A>(*this);. A redesign could help simplify some of the code. The Manager class could be removed.
class A
{
public:
A(): shouldwork{true}, thread{[&]{ this->Run(); }}
{
}
void StopA()
{
shouldwork = false;
thread.join();
}
private:
std::atomic<bool> shouldwork;
std::thread thread;
void Run(void)
{
while (shouldwork)
{
// code...
}
}
};
The code is modelled along the lines of std::thread, were the stopping of the tread is more controlled before an attempt is made to join it. The destructor is left empty in this case, to mimic the termination (calling std::terminate) result, as is the case with the standard thread library. Threads must be explicitly joined (or detached) before destruction.
Re-introducing the Manager, the code could look as follows;
class A
{
public:
A() : shouldwork{true}, thread{[&]{ this->Run(); }} {}
void StopA() { shouldwork = false; thread.join(); }
private:
void Run();
std::atomic<bool> shouldwork;
std::thread thread;
};
class Manager
{
public:
Manager() = default;
void StartA(void)
{
a = std::make_unique<A>();
}
void StopA(void)
{
a->StopA();
}
A& GetA(void)
{
return *a;
}
private:
std::unique_ptr<A> a;
};
void A::Run()
{
while (shouldwork)
{
// Here goes a lot of code which calls manager.GetA().
auto& a = manager.GetA();
}
}
And your main remains as it is.

Related

Singleton pattern in C++ for a DI container

I am trying to create a DI container in C++ (for studying purposes). I know about boost DI container option, but I just want to have fun writing one by myself.
I would like that the created container only had one instance per object "registered", so I should apply the Singleton design pattern.
But, what would be the best (idiomatic) way to implement the Singleton Pattern as an in C++20 or, at least, in modern C++ and why?
Do you mean something like this, using meyer's singleton.
(https://www.modernescpp.com/index.php/thread-safe-initialization-of-a-singleton)
I never use singletons that need to be created with new, since their destructor never gets called. With this pattern the destructors do get called when the program terminates.
#include <iostream>
//-----------------------------------------------------------------------------
// create an abstract baseclass (closest thing C++ has to an interface)
struct data_itf
{
virtual int get_value1() const = 0;
virtual ~data_itf() = default;
protected:
data_itf() = default;
};
//-----------------------------------------------------------------------------
// two injectable instance types
struct test_data_container :
public data_itf
{
int get_value1() const override
{
return 0;
}
~test_data_container()
{
std::cout << "test_data_container deleted";
}
};
struct production_data_container :
public data_itf
{
int get_value1() const override
{
return 42;
}
~production_data_container()
{
std::cout << "production_data_container deleted";
}
};
//-----------------------------------------------------------------------------
// meyers threadsafe singleton to get to instances implementing
// interface to be injected.
//
data_itf& get_test_data()
{
static test_data_container test_data;
return test_data;
}
data_itf& get_production_data()
{
static production_data_container production_data;
return production_data;
}
//-----------------------------------------------------------------------------
// object that needs data
class my_object_t
{
public:
explicit my_object_t(const data_itf& data) :
m_data{ data }
{
}
~my_object_t()
{
std::cout << "my_object deleted";
}
void function()
{
std::cout << m_data.get_value1() << "\n";
}
private:
const data_itf& m_data;
};
//-----------------------------------------------------------------------------
int main()
{
auto& data = get_production_data();
my_object_t object{ data };
object.function();
return 0;
}

Ensuring mutex lock is called before unlock

Was trying to execute a piece of code only when mutex is locked. Methods in class mainclass does the task only when the mutex is locked. To avoid creating undefined behaviour when the mutex is unlocked without locking ( considering case where locking may fail), lock and unlock is handled with constructor and destructor of mutexclass.
I want to check if mutex is locked before executing the code from mainclass methods calling1() and calling2(). As constructor won't return values, want to know alternate methods of doing this.
#include <iostream>
using namespace std;
class mutexclass
{
public:
pthread_mutex_t *mutex;
bool a = true;
mutexclass(pthread_mutex_t* inmutex)
{ mutex= inmutex;
int err=pthread_mutex_lock(mutex);
cout<<"automutex constructor";
if(err != 0)
a = false;
};
~mutexclass()
{
if(a)
{
pthread_mutex_unlock(mutex);
cout<<"automutex destructor";
}
else
cout<<"a is not true";
};
};
class mainclass{
public:
mainclass();
~mainclass();
pthread_mutex_t lock;
void calling1();
void calling2();
};
mainclass::mainclass()
{
pthread_mutex_init(&lock,NULL);
}
mainclass::~mainclass()
{
pthread_mutex_destroy(&lock);
}
void mainclass::calling1()
{
mutexclass m = mutexclass(&lock);
//call cout only if mutex lock is successful
cout<<"calling1";
}
void mainclass::calling2()
{
mutexclass m = mutexclass(&lock);
cout<<"calling2";
}
int main() {
mainclass s;
s.calling1();
s.calling2();
return 0;
}
You should really use std::mutex and std::lock_guard, and in general the standard C++ threading support. But if for some reason you can't (maybe this is a learning exercise?) then I recommend preventing mutexclass from ever being in a state where the mutex is not locked. That should be an error, so throw an exception if the lock can't be acquired.
It's also important to prevent instances from being copied. Otherwise, the same mutex will be unlocked multiple times in the destructor of each copy. To prevent that, delete the copy constructor and assignment operator.
#include <system_error>
class mutexclass
{
public:
explicit mutexclass(pthread_mutex_t* inmutex)
: mutex(inmutex)
{
int err = pthread_mutex_lock(mutex);
if (err != 0) {
throw std::system_error(err, std::generic_category(),
"pthread mutex lock acquisition failure");
}
}
~mutexclass()
{
pthread_mutex_unlock(mutex);
}
mutexclass(const mutexclass&) = delete;
mutexclass& operator=(const mutexclass&) = delete;
private:
pthread_mutex_t* mutex;
};
Now you can implement the calling* functions like this:
void mainclass::calling1()
{
try {
mutexclass m(&lock);
cout << "calling1\n";
} catch (const std::exception& e) {
// Could not lock.
}
}
You set a boolean to indicate whether the mutex is locked or not. You can also check that value outside the class:
class mutexclass
{
public:
mutexclass(pthread_mutex_t* inmutex)
{
mutex = inmutex;
int err = pthread_mutex_lock(mutex);
if(err == 0) locked_ = true;
}
~mutexclass()
{
if(locked_) {
pthread_mutex_unlock(mutex);
}
}
bool locked() const { return locked_; }
private:
pthread_mutex_t *mutex;
bool locked_ = false;
};
void mainclass::calling1()
{
mutexclass m = mutexclass(&lock);
//call cout only if mutex lock is successful
if (m.locked()) {
cout<<"calling1";
}
}
You could also use std::mutex, which throws an exception on errors.

Creating a callback with std::function as class-member

I have designed a simple callback-keyListener-"Interface" with the help of a pure virtual function. Also I used a shared_ptr, to express the ownership and to be sure, that the listener is always available in the handler. That works like a charme, but now I want to implement the same functionality with the help of std::function, because with std::function I am able to use lambdas/functors and I do not need to derive from some "interface"-classes.
I tried to implement the std::function-variant in the second example and it seems to work, but I have two questions related to example 2:
Why does this example still work, although the listener is out of scope? (It seems, that we are working with a copy of the listener instead of the origin listener?)
How can I modify the second example, to achieve the same functionality like in the first example (working on the origin listener)? (member-ptr to std::function seems not to work! How can we handle here the case, when the listener is going out of scope before the handler? )
Example 1: With a virtual function
#include <memory>
struct KeyListenerInterface
{
virtual ~KeyListenerInterface(){}
virtual void keyPressed(int k) = 0;
};
struct KeyListenerA : public KeyListenerInterface
{
void virtual keyPressed(int k) override {}
};
struct KeyHandler
{
std::shared_ptr<KeyListenerInterface> m_sptrkeyListener;
void registerKeyListener(std::shared_ptr<KeyListenerInterface> sptrkeyListener)
{
m_sptrkeyListener = sptrkeyListener;
}
void pressKey() { m_sptrkeyListener->keyPressed(42); }
};
int main()
{
KeyHandler oKeyHandler;
{
auto sptrKeyListener = std::make_shared<KeyListenerA>();
oKeyHandler.registerKeyListener(sptrKeyListener);
}
oKeyHandler.pressKey();
}
Example 2: With std::function
#include <functional>
#include <memory>
struct KeyListenerA
{
void operator()(int k) {}
};
struct KeyHandler
{
std::function<void(int)> m_funcKeyListener;
void registerKeyListener(const std::function<void(int)> &funcKeyListener)
{
m_funcKeyListener = funcKeyListener;
}
void pressKey() { m_funcKeyListener(42); }
};
int main()
{
KeyHandler oKeyHandler;
{
KeyListenerA keyListener;
oKeyHandler.registerKeyListener(keyListener);
}
oKeyHandler.pressKey();
}
std::function<Sig> implements value semantic callbacks.
This means it copies what you put into it.
In C++, things that can be copied or moved should, well, behave a lot like the original. The thing you are copying or moving can carry with it references or pointers to an extrenal resource, and everything should work fine.
How exactly to adapt to value semantics depends on what state you want in your KeyListener; in your case, there is no state, and copies of no state are all the same.
I'll assume we want to care about the state it stores:
struct KeyListenerA {
int* last_pressed = 0;
void operator()(int k) {if (last_pressed) *last_pressed = k;}
};
struct KeyHandler {
std::function<void(int)> m_funcKeyListener;
void registerKeyListener(std::function<void(int)> funcKeyListener) {
m_funcKeyListener = std::move(funcKeyListener);
}
void pressKey() { m_funcKeyListener(42); }
};
int main() {
KeyHandler oKeyHandler;
int last_pressed = -1;
{
KeyListenerA keyListener{&last_pressed};
oKeyHandler.registerKeyListener(keyListener);
}
oKeyHandler.pressKey();
std::cout << last_pressed << "\n"; // prints 42
}
or
{
oKeyHandler.registerKeyListener([&last_pressed](int k){last_pressed=k;});
}
here we store a reference or pointer to the state in the callable. This gets copied around, and when invoked the right action occurs.
The problem I have with listeners is the doulbe lifetime issue; a listener link is only valid as long as both the broadcaster and reciever exist.
To this end, I use something like this:
using token = std::shared_ptr<void>;
template<class...Message>
struct broadcaster {
using reciever = std::function< void(Message...) >;
token attach( reciever r ) {
return attach(std::make_shared<reciever>(std::move(r)));
}
token attach( std::shared_ptr<reciever> r ) {
auto l = lock();
targets.push_back(r);
return r;
}
void operator()( Message... msg ) {
decltype(targets) tmp;
{
// do a pass that filters out expired targets,
// so we don't leave zombie targets around forever.
auto l = lock();
targets.erase(
std::remove_if( begin(targets), end(targets),
[](auto&& ptr){ return ptr.expired(); }
),
end(targets)
);
tmp = targets; // copy the targets to a local array
}
for (auto&& wpf:tmp) {
auto spf = wpf.lock();
// If in another thread, someone makes the token invalid
// while it still exists, we can do an invalid call here:
if (spf) (*spf)(msg...);
// (There is no safe way around this issue; to fix it, you
// have to either restrict which threads invalidation occurs
// in, or use the shared_ptr `attach` and ensure that final
// destruction doesn't occur until shared ptr is actually
// destroyed. Aliasing constructor may help here.)
}
}
private:
std::mutex m;
auto lock() { return std::unique_lock<std::mutex>(m); }
std::vector< std::weak_ptr<reciever> > targets;
};
which converts your code to:
struct KeyHandler {
broadcaster<int> KeyPressed;
};
int main() {
KeyHandler oKeyHandler;
int last_pressed = -1;
token listen;
{
listen = oKeyHandler.KeyPressed.attach([&last_pressed](int k){last_pressed=k;});
}
oKeyHandler.KeyPressed(42);
std::cout << last_pressed << "\n"; // prints 42
listen = {}; // detach
oKeyHandler.KeyPressed(13);
std::cout << last_pressed << "\n"; // still prints 42
}

std::shared_ptr becomes invalid when passing as an argument by value

Dear people of stackoverflow.
I have a class, named FcgiManager, that is intended to handle a proper ammount of fastcgi workers.
Currently it has a simple method to add a new worker to list:
bool FcgiManager::AddWorker() {
workers_.push_back(std::make_unique<FcgiWorker>(server_));
return true;
}
The "workers_" field is:
private:
// cut
std::list<std::unique_ptr<FcgiWorker>> workers_;
The program exits with "bad weak pointer" exception.
When I trace it i see that from this method it does reache FcgiWorkes constructor, here it is:
FcgiWorker::FcgiWorker(std::shared_ptr<Dolly> server) :
running_{false}, pool_{std::make_unique<FcgiRequestPool>()}, server_{server} {
// actual code is not important, because execution doesn't get here.
}
And i see, that the "server" argument shared_ptr has some martian values in it's structure, like count = 1443278, weak = -1.
But in the AddWorker() method, just before entering FcgiWorker constructor the "server_" field has appropriate values.
I have even added an "IsAlive()" method to server and tried calling server_->IsAlive() from AddWorker() and yes, it's alive.
I also tried to plainly create a worker like this:
Any advice appreciated.
bool FcgiManager::AddWorker() {
FcgiWorker test(server_);
return true;
}
To check if I am doing something wrong with make_unique, but the result was the same.
Any advice appreciated.
Update-1:
FcgiWorker::server_ is:
std::shared_ptr<Dolly> server_;
it is initialized in the FcgiWorker constructor's initialization list, see constructor code above.
Update-2
FcgiManager::server_ is:
std::shared_ptr<Dolly> server_;
Here is how it is created in main.cpp:
auto server = std::make_shared<Dolly>();
server->BindFcgiManagerToServer();
server->Run();
server.cpp:
Dolly::Dolly() : start_time_{std::time(nullptr)} {
fcgi_manager_ = std::make_unique<FcgiManager>();
}
void Dolly::BindFcgiManagerToServer() {
fcgi_manager_->set_server(self());
}
server.h:
class Dolly : public std::enable_shared_from_this<Dolly> {
private:
// cut
inline std::shared_ptr<Dolly> self() { return shared_from_this(); }
Update-3 & Update-4 & 5
Created MCVE, and, oh well - it compiles and does not throw. So i guess i'll have to solve my problem myself now :)
Thanks everyone!
(MCVE updated twice, since it was not complete the first time)
#include <memory>
#include <list>
#include <iostream>
class Dolly;
class FcgiRequestPool {
};
class FcgiWorker {
public:
FcgiWorker(std::shared_ptr<Dolly> server) :
pool_{std::make_unique<FcgiRequestPool>()}, server_{server} {
std::cout << "worker created" << std::endl << std::flush;
}
private:
std::shared_ptr<FcgiRequestPool> pool_;
std::shared_ptr<Dolly> server_;
};
class FcgiManager {
public:
inline void set_server(std::shared_ptr<Dolly> server) { server_ = server; }
bool InitWorkers() {
auto workers_num = 4;
auto created_workers_num = 0;
try {
for (created_workers_num = 0; created_workers_num < workers_num; created_workers_num++) {
AddWorker();
}
} catch (std::exception& e) {
std::cout << e.what() << std::endl << std::flush;
}
return true;
}
void Run() {
InitWorkers();
}
private:
bool AddWorker() {
workers_.push_back(std::make_unique<FcgiWorker>(server_));
return true;
}
std::list<std::unique_ptr<FcgiWorker>> workers_;
std::shared_ptr<Dolly> server_;
};
class Dolly : public std::enable_shared_from_this<Dolly> {
public:
Dolly() {
fcgi_manager_ = std::make_unique<FcgiManager>();
}
void BindFcgiManagerToServer() {
fcgi_manager_->set_server(self());
}
void Run() {
fcgi_manager_->Run();
}
private:
std::unique_ptr<FcgiManager> fcgi_manager_;
std::shared_ptr<Dolly> self() {
return shared_from_this();
}
};
int main() {
auto server = std::make_shared<Dolly>();
server->BindFcgiManagerToServer();
server->Run();
return 0;
}
Resolved
After creating MCVE and comparing it step-by-step with the actual program, I found out, that the shared_ptr exception was generated here:
pool_{std::make_unique<FcgiRequestPool>()}
And has no relation to the whole classes creation logic.
I am marking the answer with MCVE advice as valid, because it is actually a great advice and it did help me.
The root cause: I was creating a unique_ptr to a class, which was derived from std::enable_shared_from_this.

Multithreaded event system

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.