Writing a thread safe optimized Datastore class - c++

I am trying to write a thread safe datastore class.
This class object is shared with between many threads in Generator and Consumer, where the class members can be set or get.
By calling setDatastore() the object is set for usage at different threads.
Below is my code,
#ifndef IF_DATA_STORE_H
#define IF_DATA_STORE_H
#include <mutex>
#include <shared_mutex>
#include <memory>
class DataType1{public:int value;};
class DataType2{public:int value;};
class DataStore
{
public:
DataStore(): _member1(), _member2(){}
~DataStore(){}
// for member1
void setMember1(const DataType1& val)
{
std::unique_lock lock(_mtx1); // no one can read/write!
_member1 = val;
}
const DataType1& getMember1() const
{
std::shared_lock lock(_mtx1); // multiple threads can read!
return _member1;
}
// for member2
void setMember2(const DataType2& val)
{
std::unique_lock lock(_mtx2); // no one can read/write!
_member2 = val;
}
const DataType2& getMember2() const
{
std::shared_lock lock(_mtx2); // multiple threads can read!
return _member2;
}
private:
mutable std::shared_mutex _mtx1;
mutable std::shared_mutex _mtx2;
DataType1 _member1;
DataType2 _member2;
// different other member!
};
// now see where data is generated/consumed!
class Generator
{
public:
void start(){/* start thread!*/}
void setDataStore(std::shared_ptr<DataStore> store)
{
_store = store;
}
void threadRoutine() //this is called from different thread and updating values
{
// some code...
{
_data.value = 10; // keep a local updated copy of data!
_store->setMember1(_data);
}
}
private:
std::shared_ptr<DataStore> _store;
DataType1 _data;
};
class Consumer
{
public:
void start(){/* start thread!*/}
void setDataStore(std::shared_ptr<DataStore> store)
{
_store = store;
}
void threadRoutine() // running a check on datastore every 1sec
{
// some code...
auto val = _store->getMember1();
// do something..
}
private:
std::shared_ptr<DataStore> _store;
};
// fianlly start all!
int main()
{
// somewhere in main thread
std::shared_ptr<DataStore> store;
Consumer c; Generator g;
c.setDataStore(store); c.start();
g.setDataStore(store); g.start();
}
#endif
Questions:
Is there any other way than creating multiple shared mutex for each member?
In Generator.threadRoutine() if I keep a local copy of DataType1 does this cause high memory issues (I see high cpu and memory) when this block called frequently, don't if this is the root cause of it.
Any other better way suggested?

Related

Synchronous thread safe APIs for exposing cached data

We offer a package which interfaces with a bunch of other packages who's APIs are not thread safe. Our package's API is entirely message based and therefore asynchronous to allow thread safety for users of our package. Our package therefore wraps a bunch of non-thread safe packages and offers a thread-safe API. That means that users of our package can interface with our package from any thread.
We would like to add synchronous APIs to our package while maintaining thread safety. I've done some research and have come up with two possible patterns to do this which I will share below. I'm not entirely happy with either approach so am wondering if the community may have more suggestions for patterns we can use. Note that the code below is for design and illustration purposes (c++ pseudocode) so is not meant to compile.
Approach 1 - Package users dependency inject data access classes to our package. We access these classes using run time type inference.
// Define an interface class for all data accessor classes
class DataAccessor
{
}
// Some random data
class FooData
{
int foo;
int bar;
}
// A concrete data accessor
class FooDataAccessor : public DataAccessor
{
public:
FooData getFooData()
{
FooData fooDataCopy;
{
//Locks cachedFooData in this scope
ScopedCriticalSection _(dataCriticalSection);
fooDataCopy.foo = cachedFooData.foo;
fooDataCopy.bar = cachedFooData.bar;
}
return fooDataCopy;
}
void setFooData(FooData& fooData)
{
//Locks cachedFooData in this scope
ScopedCriticalSection _(dataCriticalSection);
cachedFooData.foo = dooData.foo;
cachedFooData.bar = dooData.bar;
}
private:
FooData cachedFooData;
CriticalSection dataCriticalSection; //Use this for locking cachedFooData to set the data.
}
class OurPackage
{
OurPackage(std::vector<DataAccessor*>); //constructor which is injected the data accessors so that our package customers can own their lifecycle.
}
// How package users would inject the data accessors into our package, then later access the data
int main()
{
std::vector<DataAccessor*> dataAccessors;
//The package customer now populates the data Accessors with the instances they need.
dataAccessors.push_back(new FooDataAccessor());
auto package = new OurPackage(dataAccessors);
// How package users access the data, assume FooDataAccessor was at the front
auto fooAccessor = dataAccessors.front();
if (fooAccessor)
{
FooData data = fooAccessor->getFooData();
}
}
// In OurPackage, set the actual data in caches
for (DataAccessor* dataAccessor : dataAccessors)
{
//Use RTTI to find the instance we want
if (auto dataAccessorTypeWeWant = dynamic_cast<DataAccessorTypeWeWant*>(dataAccessor) != nullptr)
{
//Set the data on dataAccessorTypeWeWant
//For example, set FooData
FooData fooData;
fooData.foo = 1;
fooData.bar = 2;
dataAccessorTypeWeWant.setFooData(fooData);
break;
}
}
2 - Use a singleton pattern instead
If the data access caches are singletons instead, package users don't have to manage the lifecycle of these classes and don't need to worry about passing pointers to instances of the data access caches around their application. This has all the pitfalls of singletons though.
Whatever patter that you chose you should use an Atomic Type that is founded into the library <atomic>, functionality available since C++11. Whit this type you can create threadsafe variables, for example:
// Some random data
class FooData
{
std::atomic<int> foo;
std::atomic<int> bar;
}
I share you a description of this library from CPlusPlus:
Atomic types are types that encapsulate a value whose access is guaranteed to not cause data races and can be used to synchronize memory accesses among different threads.
This is a sample. If copy by value is what you need.
#include <vector>
#include <iostream>
#include <thread>
#include <atomic>
#include <mutex>
// Undefine to see data corruptions
#define USING_LOCK
std::atomic<int> atomic_i;
class FooData {
public:
FooData() :foo(atomic_i.fetch_add(1, std::memory_order_relaxed)), bar(foo) {}
~FooData() { if (foo != bar) { std::cout << "Data corrupted!\n"; } }
private:
int foo;
int bar;
};
class FooDataAccessor {
public:
FooData getFooData() {
#ifdef USING_LOCK
std::lock_guard<std::mutex> l(_lock);
#endif // USING_LOCK
return cachedFooData;
}
void setFooData(const FooData& fooData) {
#ifdef USING_LOCK
std::lock_guard<std::mutex> l(_lock);
#endif // USING_LOCK
cachedFooData = fooData;
}
private:
FooData cachedFooData;
#ifdef USING_LOCK
std::mutex _lock;
#endif // USING_LOCK
};
void f(FooDataAccessor* accessor) {
for (size_t i = 0; i < 1000; i++) {
accessor->getFooData();
accessor->setFooData(FooData());
}
}
int main() {
FooDataAccessor accessor;
std::vector<std::thread> v;
for (size_t i = 0; i < 5; i++) {
v.emplace_back(f, &accessor);
}
for (auto& t : v) {
t.join();
}
}

Mutex inside templated struct causes segmentation fault

For exchanging data between classes, I use a kind of "main-hub-class", from which each other class can access the data.
Now, to make this thread-safe I came up with a templated struct that holds a variable and a boost::shared_mutex for that variable:
class DataExchange {
[...]
template <typename T>
struct ShareDataEntry {
T value;
boost::shared_mutex _mutex;
};
SharedDataEntry<int> ultraSonicValue;
[...]
}
In the .cpp I am trying to use that like this:
void DataExchange::setUltrasSonicValue(int _value) {
boost::unique_lock<boost::shared_mutex> lock ( ultraSonicValue._mutex ); // <-- this segfaults
ultraSonicValue.value = _value;
lock.unlock();
}
From gdb, I get the error
__GI____pthread_mutex_lock (mutex=0x58) at pthread_mutex_lock.c:66
66 pthread_mutex_lock.c: No such file or directory
What am I doing wrong? My guess is that the mutex isn't initialized? But how (and where) would I do that?
EDIT
Updated code sample, now showing everything I use, also with a test for the problem I described:
DataExchange.hpp:
#pragma once
#include <boost/thread.hpp>
class DataExchange {
private:
DataExchange();
DataExchange(DataExchange const&) {};
DataExchange& operator=(DataExchangeconst&) { return *instance; };
static DataExchange* instance;
template <typename T>
struct ShareDataEntry {
T value;
boost::shared_mutex _mutex;
};
// simple int with extra mutex
int testIntOne;
boost::shared_mutex testIntOne_M;
// int in my struct
SharedDataEntry<int> testIntTwo;
public:
static DataExchange* getInstance();
~DataExchange() { delete instance; };
void setTestIntOne(int _tmp);
int getTestIntOne();
void setTestIntTwo(int _tmp);
int getTestIntTwo();
}
DataExchange.cpp:
#include "infrastructure/DataExchange.hpp"
DataExchange* DataExchange::instance = NULL;
DataExchange::DataExchange() {};
DataExchange* DataExchange::getInstance() {
if (instance == NULL) instance = new DataExchange;
return instance;
}
void DataExchange::setTestIntOne(int _tmp) {
boost::unique_lock<boost::shared_mutex> lock ( testIntOne_M ); // this is now where the segfault occurs
testIntOne = _tmp;
lock.unlock();
}
int DataExchange::getTestIntOne() {
boost::shared_lock<boost::shared_mutex> lock ( testIntOne_M );
return testIntOne;
}
void DataExchange::setTestIntTwo(int _tmp) {
boost::unique_lock<boost::shared_mutex> lock ( testIntTwo._mutex );
testIntTwo.value = _tmp;
lock.unlock();
}
int DataExchange::getTestIntTwo() {
boost::shared_lock<boost::shared_mutex> lock ( testIntTwo._mutex );
return testIntTwo.value;
}
main.cpp:
#inlcude "infarstructure/DataExchange.hpp"
int main(int argc, char *argv[]) {
DataExchange* dataExchange = DataExchange::getInstance();
// this line segfaults already, altough I was pretty sure it worked before
dataExchange->setTestIntOne(5);
cout << dataExchange->getTestIntOne() << "\n";
dataExchange->setTestIntTwo(-5);
cout << dataExchange->getTestIntTwo() << "\n";
return 0;
}
Does it segfault because the mutex wasn't initialized?
Also, I am very sure it worked earlier, at least the first way (without the struct).
Second Edit:
Alright, everything is working fine now. It was a stupid mistake on my part. Both approaches work flawlessly - as long as one initializes the DataExchange object.

RAII thread safe getter

Most of the times I see in the code some variant of this kind of implementation for a thread safe getter method:
class A
{
public:
inline Resource getResource() const
{
Lock lock(m_mutex);
return m_resource;
}
private:
Resource m_resource;
Mutex m_mutex;
};
Assuming that the class Resource can't be copied, or that the copy operation has a too high computational cost, is there a way in C++ to avoid the returning copy but still using a RAII style locking mechanism?
I haven't tried it, but something like this should work:
#include <iostream>
#include <mutex>
using namespace std;
typedef std::mutex Mutex;
typedef std::unique_lock<Mutex> Lock;
struct Resource {
void doSomething() {printf("Resource::doSomething()\n"); }
};
template<typename MutexType, typename ResourceType>
class LockedResource
{
public:
LockedResource(MutexType& mutex, ResourceType& resource) : m_mutexLocker(mutex), m_pResource(&resource) {}
LockedResource(MutexType& mutex, ResourceType* resource) : m_mutexLocker(mutex), m_pResource(resource) {}
LockedResource(LockedResource&&) = default;
LockedResource(const LockedResource&) = delete;
LockedResource& operator=(const LockedResource&) = delete;
ResourceType* operator->()
{
return m_pResource;
}
private:
Lock m_mutexLocker;
ResourceType* m_pResource;
};
class A
{
public:
inline LockedResource<Mutex, Resource> getResource()
{
return LockedResource<Mutex, Resource>(m_mutex, &m_resource);
}
private:
Resource m_resource;
Mutex m_mutex;
};
int main()
{
A a;
{ //Lock scope for multiple calls
auto r = a.getResource();
r->doSomething();
r->doSomething();
// The next line will block forever as the lock is still in use
//auto dead = a.getResource();
} // r will be destroyed here and unlock
a.getResource()->doSomething();
return 0;
}
But be careful, as the lifetime of the accessed Resource depends on the lifetime of the owner (A)
Example on Godbolt: Link
P1144 reduces the generated assembly quite nicely so that you can see where the lock is locked and unlocked.
How about returning an accessor object that provides a thread-safe interface to the Resource class and/or keeps some lock?
class ResourceGuard {
private:
Resource *resource;
public:
void thread_safe_method() {
resource->lock_and_do_stuff();
}
}
This will be cleared in a RAII fashion, releasing any locks if needed. If you need locking it should be done in the the Resource class.
Of course you have to take care of the lifespan of Resource. A very simple way would be to use a std::shard_ptr. A weak_ptr might fit as well.
another way to achieve the same thing. This is the implementation of a mutable version. the const accessor is just as trivial.
#include <iostream>
#include <mutex>
struct Resource
{
};
struct locked_resource_view
{
locked_resource_view(std::unique_lock<std::mutex> lck, Resource& r)
: _lock(std::move(lck))
, _resource(r)
{}
void unlock() {
_lock.unlock();
}
Resource& get() {
return _resource;
}
private:
std::unique_lock<std::mutex> _lock;
Resource& _resource;
};
class A
{
public:
inline locked_resource_view getResource()
{
return {
std::unique_lock<std::mutex>(m_mutex),
m_resource
};
}
private:
Resource m_resource;
mutable std::mutex m_mutex;
};
using namespace std;
auto main() -> int
{
A a;
auto r = a.getResource();
// do something with r.get()
return 0;
}

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.

Possibility of segmentation fault with mutex in class with multithreading

With multiple threads (std::async) sharing an instance of the following class through a shared_ptr, is it possible to get a segmentation fault in this part of the code? If my understanding of std::mutex is correct, mutex.lock() causes all other threads trying to call mutex.lock() to block until mutex.unlock() is called, thus access to the vector should happen purely sequentially. Am I missing something here? If not, is there a better way of designing such a class (maybe with a std::atomic_flag)?
#include <mutex>
#include <vector>
class Foo
{
private:
std::mutex mutex;
std::vector<int> values;
public:
Foo();
void add(const int);
int get();
};
Foo::Foo() : mutex(), values() {}
void Foo::add(const int value)
{
mutex.lock();
values.push_back(value);
mutex.unlock();
}
int Foo::get()
{
mutex.lock();
int value;
if ( values.size() > 0 )
{
value = values.back();
values.pop_back();
}
else
{
value = 0;
}
mutex.unlock();
return value;
}
Disclaimer: The default value of 0 in get() is intended as it has a special meaning in the rest of the code.
Update: The above code is exactly as I use it, except for the typo push_Back of course.
Other than not using RAII to acquire the lock and using size() > 0 instead of !empty(), the code looks fine. This is exactly how a mutex is meant to be used and this is the quintessential example of how and where you need a mutex.
As Andy Prowl pointed out, instances can't be copy constructed or copy assigned.
Here is the "improved" version:
#include <mutex>
#include <vector>
class Foo {
private:
std::mutex mutex;
typedef std::lock_guard<std::mutex> lock;
std::vector<int> values;
public:
Foo();
void add(int);
int get();
};
Foo::Foo() : mutex(), values() {}
void Foo::add(int value) {
lock _(mutex);
values.push_back(value);
}
int Foo::get() {
lock _(mutex);
int value = 0;
if ( !values.empty() )
{
value = values.back();
values.pop_back();
}
return value;
}
with RAII for acquiring the mutex etc.