I am in doubt with using objects from multiple threads.
First of all it is no problem to protect the object against simultaneous access with std::lock_guard
or others.
The next step is to declare the objects with volatile.
class A
{
public: int val;
};
volatile A a;
But doing this ends up in having a lot of new functions with type qualifiers volatile
...
int GetVal() volatile;
void SetVal() volatile;
...
OK, all this works fine.
But how to access stl members, e.g. std::vector
or std::map.
If I want to have a volatile std::vector<int>
I run in lots of errors while stl do not define any volatile method.
From this point I searched the net and found a lot of "tricks". Mostly with the same idea in the background: Having a mutex for concurrency protection and moving the volatile
away with a const_cast
to make the standard interface available.
As a sample of this implementations:
template <typename T>
class PseudoPtr {
public:
PseudoPtr(volatile T& obj, mutex& mtx)
: guard(mtx),
pObj_(const_cast<T*>(&obj))
{ }
~PseudoPtr() { }
// Pointer behaviour
T& operator*() { return *pObj_; }
T* operator->() { return pObj_; }
private:
my_guard<mutex> guard;
T* pObj_;
PseudoPtr(const PseudoPtr&) = delete;
PseudoPtr& operator=(PseudoPtr &) = delete;
};
Having my example class from above without the volatile
qualifiers:
class A
{
...
int GetVal();
};
But if I use this in the following way:
volatile A a;
mutex mu;
...
for (;;)
{
PseudoPtr ptr(a, mu); //starts locking the object
if (ptr->GetVal())
{
... do something ...
}
}
I will never see any changes in the object a, because the compiler could optimize
the access away, because the volatile object was const_cast
and so the optimizer did not
know anything about the volatile behavior.
For all my own classes this is not a problem, while I could write all the volatile
methods. But if I want to use a stl container, there is no way to have volatile instances from them and using it though a const_cast
.
OK, the actual compiler is not so hard optimizing (gcc 4.6.1), but is there a guarantee that the compiler never do this optimization? const_cast
will break the volatile
and do not const_cast
on volatile
stl objects will not work, while stl containers have no volatile methods at all.
As I think, all the 'tricks' found in the web are buggy, because they all ignore the optimizer.
This runs in my questions:
- I am wrong with my interpretation of volatile and optimizers?
- Is there a "standard" working solution for using stl conatiners thread safe?
---------------------- after a few hours of reading ------------------------
First of all, yes, pthread_mutex_lock should do the following things: * makes access to memory atomic (that is the only thing I know before) * guarantee that memory becomes visible to all other threads
OK, the second was new to me! And this results in the next question: How the trick works?
The library which provides the mutex semantic must have a chance to tell * the compiler, to stop out of order execution * writes down all cached (register optimized) variables to hardware * tell the hardware to synchronize all hardware caches etc.
OK, fine! But how that works: Implementation specific to gcc: There exists a macro called barrier() which is something like
asm volatile("" ::: "memory");
The pthread library for gcc is included to glibc/nptl and each function which guarantee the visibility of data to other threads simply calls the barrier macro or call the inline assembler directly or do the things with something similar.
If there is no misunderstanding again, that's the simple thing behind the curtain.
What I have learned: volatile in combination with mutex locking is not sense full in any case.
I write that answer in hope that others run in that mystery becomes also a bit less addled.
If there are again mistakes and misunderstandings?!: Let me know!
Thanks for all your answers!
(Sorry for editing my post to add my own conclusion, but I could not add an own answer to my post)