Assume I have shared_ptr<T> a and two threads running concurrently where one does:
a.reset();
and another does:
auto b = a;
if the operations are atomic, then I either end up with two empty shared_ptrs or a being empty and b pointing to what was pointed to by a. I am fine with either outcome, however, due to the interleaving of the instructions, these operations might not be atomic. Is there any way I can assure that?
To be more precise I only need a.reset() to be atomic.
UPD: as pointed out in the comments my question is silly if I don't get more specific. It is possible to achieve atomicity with a mutex. However, I wonder if, on the implementation level of shared_ptr, things are already taken care of. From cppreference.com, copy assignment and copy constructors are thread-safe. So auto b = a is alright to run without a lock. However, from this it's unclear if a.reset() is also thread-safe.
UPD1: it would be great if there is some document that specifies which methods of shared_ptr are thread-safe. From cppreference:
If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur
It is unclear to me which of the methods are non-const.
Let the other thread use a weak_ptr. The lock() operation on weak pointer is documented to be atomic.
Create:
std::shared_ptr<A> a = std::make_shared<A>();
std::weak_ptr<A> a_weak = std::weak_ptr<A>(a);
Thread 1:
a.reset();
Thread 2:
b = a_weak.get();
if (b != nullptr)
{
...
}
std::shared_ptr<T> is what some call a "thread-compatible" class, meaning that as long as each instance of a std::shared_ptr<T> can only have one thread calling its member functions at a given point in time, such member function invocations do not cause race conditions, even if multiple threads are accessing shared_ptrs that share ownership with each other.
std::shared_ptr<T> is not a thread-safe class; it is not safe for one thread to call a non-const method of an std::shared_ptr<T> instance while another thread is also accessing the same instance. If you need potentially concurrent reads and writes to not race, then synchronize them using a mutex.
Related
I am learning about C++ threading. I have a class called Shape which has member variables called mWidth and mHeight. There's a member function called Shape::setWH(int newWidth,int newHeight) which modifies mWidth and mHeight. Multiple threads can perform this operation on same object right? so I can say its not thread safe and use std::mutex to lock the operation.
If a class has bunch of member functions which modifies member variables. Can we say that none of them are thread safe?
Yes. If not stated otherwise you have to assume that methods are not thread-safe. Making each and every single method thread safe would be no good, because the caller needs to be in control of synchronization when they want more coarse grained synchronization (eg lock a mutex for many calls rather than every single call).
Types have to be assumed to be not thread-safe unless their methods are explicitly stated to be thread safe. Baking in synchronisation into each single method would be inefficient, because that would mean that the caller has no control over how to use them in a thread safe way.
Suppose the caller writes a tight loop, then they want to lock a mutex only once not in every iteration:
std::lock_guard<std::mutex> lock(mymutex);
for (int i=0;i <10000; ++i) {
foo.setSomething(i);
}
Or suppose a user is completely sure that no synchronization is needed, then locking a mutex anyhow is unecessary overhead.
It is completely ok to not provide the thread-safety you are worried about. As long as you do not claim that something would be thread-safe the caller should be aware that it isnt and that they need to use synchronization. What you should care about is that const should not just be logical constness but actual constness. For example:
struct foo {
int get42() const {
++calls;
return 42;
}
private:
mutable int calls = 0;
};
In a single-threaded context this is completely fine. From outside there is no way to see that calling foo::get42 changed internal state. The method does not modify "logical state", it does however modify the binary state of the object. Avoid that and document when a non-const method is thread-safe. Then the caller should be ready to use your type in a thread-safe way.
I have a double free bug using std::shared_ptr and trying to get know why. I am using shared_ptr in multithread environment , one thread sometimes replaces some element in a global array
std::shared_ptr<Bucket> globalTable[100]; // global elements storage
using:
globalTable[idx].reset(newBucket);
and the other thread reads this table sometimes using :
std::shared_ptr<Bucket> bkt(globalTable[pIdx]);
// do calculations with bkt-> items
After this I am receiving double-free error, and AddressSanitizer says that the second code tries to free an object that was destroyed by the first one . How it is possible ? As I know shared_ptr must be completly thread safe.
Reset does not guarantee you thread saefty.
Assignments and reference counting are thread safe as explained here
To satisfy thread safety requirements, the reference counters are
typically incremented using an equivalent of std::atomic::fetch_add
with std::memory_order_relaxed (decrementing requires stronger
ordering to safely destroy the control block).
If multiple threads access same shared_ptr you can have a race condition.
If multiple threads of execution access the same shared_ptr without
synchronization and any of those accesses uses a non-const member
function of shared_ptr then a data race will occur; the shared_ptr
overloads of atomic functions can be used to prevent the data race.
Your function reset is non const so falls on that category. You need to use mutex or another synchronization mechanism.
http://en.cppreference.com/w/cpp/memory/shared_ptr
Not all operations on a std::shared_ptr are thread-safe.
Specifically, the reference-counts are managed atomically, but it's your responsibility to make sure the std::shared_ptr instance you access is not concurrently modified.
You fail that responsibility, resulting in a data-race and the expected undefined behavior, manifesting as a double-free in your case.
Consider the following:
// these services are running on different threads that are started a long time ago
std::vector<io_service&> io_services;
struct A {
std::unique_ptr<Object> u;
} a;
io_services[0].post([&io_services, &a] {
std::unique_ptr<Object> o{new Object};
a.u = std::move(o);
io_services[1].post([&a] {
// as far as I know changes to `u` isn't guaranteed to be seen in this thread
a.u->...;
});
});
The actual code passes a struct to a bunch of different boost::asio::io_service objects and each field of struct is filled by a different service object (the struct is never accessed from different io_service objects/threads at the same time, it is passed between the services by reference until the process is done).
As far as I know I always need some kind of explicit synchronization/memory flushing when I pass anything between threads even if there is no read/write race (as in simultaneous access). What is the way of correctly doing it in this case?
Note that Object does not belong to me and it is not trivially copy-able or movable. I could use a std::atomic<Object*> (if I am not wrong) but I would rather use the smart pointer. Is there a way to do that?
Edit:
It seems like std::atomic_thread_fence is the tool for the job but I cannot really wrap the 'memory model' concepts to safely code.
My understanding is that the following lines are needed for this code to work correctly. Is it really the case?
// these services are running on different threads that are started a long time ago
std::vector<io_service&> io_services;
struct A {
std::unique_ptr<Object> u;
} a;
io_services[0].post([&io_services, &a] {
std::unique_ptr<Object> o{new Object};
a.u = std::move(o);
std::atomic_thread_fence(std::memory_order_release);
io_services[1].post([&a] {
std::atomic_thread_fence(std::memory_order_acquire);
a.u->...;
});
});
Synchronisation is only needed when there would be a data race without it. A data race is defined as unsequenced access by different threads.
You have no such unsequenced access. The t.join() guarantees that all statements that follow are sequenced strictly after all statements that run as part of t. So no synchronisation is required.
ELABORATION: (To explain why thread::join has the above claimed properties) First, description of thread::join from standard [thread.thread.member]:
void join();
Requires: joinable() is true.
Effects: Blocks until
the thread represented by *this has completed.
Synchronization: The
completion of the thread represented by *this synchronizes with (1.10)
the corresponding successful join() return.
a). The above shows that join() provides synchronisation (specifically: the completion of the thread represented by *this synchronises with the outer thread calling join()). Next [intro.multithread]:
An evaluation A inter-thread happens before an evaluation B if
(13.1) — A synchronizes with B, or ...
Which shows that, because of a), we have that the completion of t inter-thread happens before the return of the join() call.
Finally, [intro.multithread]:
Two actions are potentially concurrent if
(23.1) — they are performed
by different threads, or
(23.2) — they are unsequenced, and at least
one is performed by a signal handler.
The execution of a program
contains a data race if it contains two potentially concurrent
conflicting actions, at least one of which is not atomic, and neither
happens before the other ...
Above the required conditions for a data race are described. The situation with t.join() does not meet these conditions because, as shown, the completion of t does in fact happen-before the return of join().
So there is no data race, and all data accesses are guaranteed well-defined behaviour.
(I'd like to remark that you appear to have changed your question in some significant way since #Smeeheey answered it; essentially, he answered your originally-worded question but cannot get credit for it since you asked two different questions. This is poor form – in the future, please just post a new question so the original answerer can get credit as due.)
If multiple threads read/write a variable, even if you know said variable is accessed in a defined sequence, you must still inform the compiler of that. The correct way to do this necessarily involves synchronization, atomics, or something documented to perform one of the prior itself (such as std::thread::join). Presuming the synchronization route is both obvious in implementation and undesirable..:
Addressing this with atomics may simply consist of std::atomic_thread_fence; however, an acquire fence in C++ cannot synchronize-with a release fence alone – an actual atomic object must be modified. Consequently, if you want to use fences alone you'll need to specify std::memory_order_seq_cst; that done, your code will work as shown otherwise.
If you want to stick with release/acquire semantics, fortunately even the simplest atomic will do – std::atomic_flag:
std::vector<io_service&> io_services;
struct A {
std::unique_ptr<Object> u;
} a;
std::atomic_flag a_initialized = ATOMIC_FLAG_INIT;
io_services[0].post([&io_services, &a, &a_initialized] {
std::unique_ptr<Object> o{new Object};
a_initialized.clear(std::memory_order_release); // initiates release sequence (RS)
a.u = std::move(o);
a_initialized.test_and_set(std::memory_order_relaxed); // continues RS
io_services[1].post([&a, &a_initialized] {
while (!a_initialized.test_and_set(std::memory_order_acquire)) ; // completes RS
a.u->...;
});
});
For information on release sequences, see here.
I am working on a set that is frequently read but rarely written.
class A {
boost::shared_ptr<std::set<int> > _mySet;
public:
void add(int v) {
boost::shared_ptr<std::set<int> > tmpSet(new std::set<int>(*_mySet));
tmpSet->insert(v); // insert to tmpSet
_mySet = tmpSet; // swap _mySet
}
void check(int v) {
boost::shared_ptr<std::set<int> > theSet = _mySet;
if (theSet->find(v) != theSet->end()) {
// do something irrelevant
}
}
};
In the class, add() is only called by one thread and check() is called by many threads. check() does not care whether _mySet is the latest or not. Is the class thread-safe? Is it possible that the thread executing check() would observe swap _mySet happening before insert to tmpSet?
This is an interesting use of shared_ptr to implement thread safety.
Whether it is OK depends on the thread-safety guarantees of
boost::shared_ptr. In particular, does it establish some sort of
fence or membar, so that you are guaranteed that all of the writes in
the constructor and insert functions of set occur before any
modification of the pointer value becomes visible.
I can find no thread safety guarantees whatsoever in the Boost
documentation of smart pointers. This surprizes me, as I was sure that
there was some. But a quick look at the sources for 1.47.0 show none,
and that any use of boost::shared_ptr in a threaded environment will
fail. (Could someone please point me to what I'm missing. I can't
believe that boost::shared_ptr has ignored threading.)
Anyway, there are three possibilities: you can't use the shared pointer
in a threaded environment (which seems to be the case), the shared
pointer ensures its own internal consistency in a threaded environment,
but doesn't establish ordering with regards to other objects, or the
shared pointer establishes full ordering. Only in the last case will
your code be safe as is. In the first case, you'll need some form of
lock around everything, and in the second, you'll need some sort of
fences or membar to ensure that the necessary writes are actually done
before publishing the new version, and that they will be seen before
trying to read it.
You do need synchronization, it is not thread safe. Generally it doesn't matter, even something as simple as shared += value; is not thread safe.
look here for example with regards to thread safety of shared_ptr: Is boost shared_ptr <XXX> thread safe?
I would also question your allocation/swapping in add() and use of shared_ptr in check()
update:
I went back and re-rad dox for shared_ptr ... It is most likely thread-safe in your particular since the reference counting for shared_ptr is thread-safe. However you are doing (IMHO) unnecessary complexity by not using read/write lock.
Eventually this code should be thread safe:
atomic_store(&_my_set,tmpSet);
and
theSet = atomic_load(&_mySet);
(instead of simple assignments)
But I don't know the current status of atomicity support for shared_ptr.
Note, that adding atomicity to shared_ptr in lock-free manner is really dificult thing; so even atomicity is implemented it may relay on mutexes or usermode spinlocks and, therefore, may sometimes suffer from performance issues
Edit: Perhaps, volatile qualifier for _my_set member variable should also be added.. but I'm not sure that it is strictly required by semantics of atomic operations
I have a question about boost::shared_ptr<T>.
There are lots of thread.
using namespace boost;
class CResource
{
// xxxxxx
}
class CResourceBase
{
public:
void SetResource(shared_ptr<CResource> res)
{
m_Res = res;
}
shared_ptr<CResource> GetResource()
{
return m_Res;
}
private:
shared_ptr<CResource> m_Res;
}
CResourceBase base;
//----------------------------------------------
// Thread_A:
while (true)
{
//...
shared_ptr<CResource> nowResource = base.GetResource();
nowResource.doSomeThing();
//...
}
// Thread_B:
shared_ptr<CResource> nowResource;
base.SetResource(nowResource);
//...
Q1
If Thread_A do not care the nowResource is the newest, will this part of code have problem?
I mean when Thread_B do not SetResource() completely, Thread_A get a wrong smart point by GetResource()?
Q2
What does thread-safe mean?
If I do not care about whether the resource is newest, will the shared_ptr<CResource> nowResource crash the program when the nowResource is released or will the problem destroy the shared_ptr<CResource>?
boost::shared_ptr<> offers a certain level of thread safety. The reference count is manipulated in a thread safe manner (unless you configure boost to disable threading support).
So you can copy a shared_ptr around and the ref_count is maintained correctly. What you cannot do safely in multiple threads is modify the actual shared_ptr object instance itself from multiple threads (such as calling reset() on it from multiple threads). So your usage is not safe - you're modifying the actual shared_ptr instance in multiple threads - you'll need to have your own protection.
In my code, shared_ptr's are generally locals or parameters passed by value, so there's no issue. Getting them from one thread to another I generally use a thread-safe queue.
Of course none of this addresses the thread safety of accessing the object pointed to by the shared_ptr - that's also up to you.
From the boost documentation:
shared_ptr objects offer the same
level of thread safety as built-in
types. A shared_ptr instance can be
"read" (accessed using only const
operations) simultaneously by multiple
threads. Different shared_ptr
instances can be "written to"
(accessed using mutable operations
such as operator= or reset)
simultaneously by multiple threads
(even when these instances are copies,
and share the same reference count
underneath.)
Any other simultaneous accesses result in undefined behavior.
So your usage is not safe, since it uses simultaneous read and write of m_res. Example 3 in the boost documentation also illustrates this.
You should use a separate mutex that guards the access to m_res in SetResource/GetResource.
Well, tr1::shared_ptr (which is based on boost) documentation tells a different story, which implies that resource management is thread safe, whereas access to the resource is not.
"...
Thread Safety
C++0x-only features are: rvalue-ref/move support, allocator support, aliasing constructor, make_shared & allocate_shared. Additionally, the constructors taking auto_ptr parameters are deprecated in C++0x mode.
The Thread Safety section of the Boost shared_ptr documentation says "shared_ptr objects offer the same level of thread safety as built-in types." The implementation must ensure that concurrent updates to separate shared_ptr instances are correct even when those instances share a reference count e.g.
shared_ptr a(new A);
shared_ptr b(a);
// Thread 1 // Thread 2
a.reset(); b.reset();
The dynamically-allocated object must be destroyed by exactly one of the threads. Weak references make things even more interesting. The shared state used to implement shared_ptr must be transparent to the user and invariants must be preserved at all times. The key pieces of shared state are the strong and weak reference counts. Updates to these need to be atomic and visible to all threads to ensure correct cleanup of the managed resource (which is, after all, shared_ptr's job!) On multi-processor systems memory synchronisation may be needed so that reference-count updates and the destruction of the managed resource are race-free.
..."
see
http://gcc.gnu.org/onlinedocs/libstdc++/manual/memory.html#std.util.memory.shared_ptr
m_Res is not threadsafe ,because it simultaneous read/write,
you need boost::atomic_store/load function to protects it.
//--- Example 3 ---
// thread A
p = p3; // reads p3, writes p
// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
Add, your class has a Cyclic-references condition; the shared_ptr<CResource> m_Res can't be a member of CResourceBase. You can use weak_ptr instead.