Destructor in a multithreaded environment? - c++

I was wondering what would happen in such a class:
class MyClass
{
private:
std::vector<int> iVector;
void Worker()
{
//Lots of stuff done with iVector
//adding, removing elements, etc.
}
}
Let's say I create a thread (invoked by one of the class member functions) that uses iVector and modifies it. Besides this worker, none of the other member functions of the class reads or modifies this std::vector.
Everything seems fine as worker thread is the only one using iVector.
But what would happen when one instance of the object is destroyed? Even if the object is destroyed after the worker thread is finished, destructor for iVector would be invoked from the main thread. Would this lead to undefined behavior?
Thanks!

Firstly I would suggest running a std::join (or your library equivalent) on the thread in the destructor. This will ensure the thread is properly finished and synchronized before the vector destructor runs. This is important as the vector's lifetime must exceed the thread using it.
The C++11 standard and presumably later ones state in 30.3.1.5:
5: Synchronization: The completion of the thread represented by *this
synchronizes with (1.10) the corresponding successful join() return. [
Note: Operations on *this are not synchronized. โ€” end note ]
Now we have to examine 1.10 for more detail, first this section states:
3: The value of an object visible to a thread T at a particular point
is the initial value of the object, a value assigned to the object by
T, or a value assigned to the object by another thread, according to
the rules below.
Honestly, this is difficult to parse, it does not specify exactly what kind of synchronization join offers and seems to imply it synchronizes only the thread itself and not data it has accessed. Therefore I would go the safe route and run atomic_thread_fence(memory_order_acquire) after join in the main thread, and atomic_thread_fence(memory_order_release) in the child thread just before it finishes which should guarantee full happens before semantics and no UB.

If an execution thread is using the ivector class member, and another thread destroys the object with this class member, the continued use of the ivector class member results in undefined behavior.
Even if the object is destroyed after the worker thread is finished,
destructor for iVector would be invoked from the main thread. Would
this lead to undefined behavior?
No. As you've described, this situation is not undefined behavior. The C++ standard does not require an object to be destroyed by the same execution thread that created the object. It's fine for one execution thread to grow, resize the vector, then go away or stop using the vector, and then a different execution thread destroy the entire object.

Related

Joinable thread in C++

http://www.cplusplus.com/reference/thread/thread/joinable/
A thread object is joinable if it represents a thread of execution.
A thread object is not joinable in any of these cases:
if it was **default-constructed**.
if it has been **moved from** (either constructing another thread object, or assigning to it).
if either of its members join or detach has been called.
What is the meaning of default constructed, here?
W.R.T moved from - When we have to put threads in a vector, we may create thread objects outside and then move them in the vector. Is this one of the cases which this moved from is referring to?
After detach has been called once, can we never join it again?
What is the meaning of default constructed, here?
It means a std::thread which was constructed with no arguments, and therefore does not represent a thread (i.e. it is not "running"). See docs: https://en.cppreference.com/w/cpp/thread/thread/thread
W.R.T moved from - When we have to put threads in a vector, we may create thread objects outside and then move them in the vector. Is this one of the cases which this moved from is referring to?
Yes, you cannot join a thread after moving it into the vector. But you can of course join the new thread object inside the vector (where it was "moved to").
After detach has been called once, can we never join it again?
That's right, you cannot join a thread which has already been joined or detached.

C++ Multithreading: Do I need mutex for constructor and destructor?

For a object that will be accessed by multiple threads, do I need to have a mutex for its constructor and destructor?
My guess is I need a mutex in the destructor, since it is possible to have one thread accessing the object while another thread deleting the object.
On the other hand, I cannot think of a reason why we need a mutex in the constructor, since there cannot be other threads accessing the object before it is fully constructed.
You are unable to share object before it is constructed. If it is not shared, then only one thread uses that. Conclusion there is no need to synchronize anything in constructor (unless you are spawning a new thread).
Now destructor is called only when all strong references are ending their lifetimes. This means that when destructor is executed last strong reference is just clean up. Again this means only one thread is using object so there is no point of synchronizing.
If for some magic reason you will have race condition in constructor or destructor the bug must be in an owner of the object.
The only synchronization I can imagine has sense in destructor is joining threads futures (spawned by this object) or fulfilling promises.

What happens if one deletes an object with a running pthread?

I have an object that uses pthreads. Its constructor creates several threads.
The class's destructor calls pthread_join on all these threads.
What would happen during a delete, if it didn't? I.e., what happens to a non-terminated thread, if the delete operator suddenly tries to deallocate the object?
Thanks in advance for all replies. :)
With the join, your destructor will block until all of the joined threads have exited.
pthread_join
The pthread_join() function waits for the thread specified by thread
to terminate. If that thread has already terminated, then
pthread_join() returns immediately. The thread specified by thread
must be joinable.
If you don't join with the threads, they will continue to run. This can have various consequences depending on if you invoke undefined behavior (eg, try to reference the deleted object after the destructor exits from one of the still running threads).
Deleting the pthread_t objects could have consequences depending on your system (I don't know every implementation of pthreads), but in general they're only references to the underlying system object representing the thread. So you will lose your reference to that thread, and potentially be unable to join with it later, but the thread would continue to run.

C++11 Multithreading : State of thread after execution

What is the state of thread after it completes its execution.?
Is it destroyed immediately after its execution or is it destroyed with parent thread.?
The std::thread object is different than a underlying thread of control (although they should map 1-on-1).
This separation is really important and it implies that std::thread and thread of control can have different life duration. For example, if you create your std::thread on the stack, you really need to call thread::detach before your object go destroyed (if you don't destructor will call terminate ). Also, as Grizzly pointed out, you can call .join() before your object destruction which will block until the execution of the thread has finished.
This also answers your question - std::thread object is not destroyed after the thread is finished - it is behaving as every other C++ object - it will be destroyed when it goes out of the scope (or gets deleted).

C++: Concurrency and destructors

Suppose you have an object which can be accesed by many threads. A critical section is used to protect the sensitive areas. But what about the destructor? Even if I enter a critical section as soon as I enter the destructor, once the destructor has been called, is the object already invalidated?
My train of thought: Say I enter the destructor, and I have to wait on the critical section because some other thread is still using it. Once he is done, I can finish destroying the object. Does this make sense?
In general, you should not destroy an object until you know that no other thread is using it. Period.
Consider this scenario, based on your 'train of thought':
Thread A: Get object X reference
Thread A: Lock object X
Thread B: Get object X reference
Thread B: Block on object X lock
Thread A: Unlock object X
Thread B: Lock object X; unlock object X; destroy object X
Now consider what happens if the timing is slightly different:
Thread A: Get object X reference
Thread B: Get object X reference
Thread B: Lock object X; unlock object X; destroy object X
Thread A: Lock object X - crash
In short, object destruction must be synchronized somewhere other than the object itself. One common option is to use reference counting. Thread A will take a lock on the object reference itself, preventing the reference from being removed and the object being destroyed, until it manages to increment the reference count (keeping the object alive). Then thread B merely clears the reference and decrements the reference count. You can't predict which thread will actually call the destructor, but it will be safe either way.
The reference counting model can be implemented easily by using boost::shared_ptr or std::shared_ptr; the destructor will not run unless all shared_ptrs in all threads have been destroyed (or made to point elsewhere), so at the moment of destruction you know that the only pointer to the object remaining is the this pointer of the destructor itself.
Note that when using shared_ptr, it's important to prevent the original object reference from changing until you can capture a copy of it. Eg:
std::shared_ptr<SomeObject> objref;
Mutex objlock;
void ok1() {
objlock.lock();
objref->dosomething(); // ok; reference is locked
objlock.unlock();
}
void ok2() {
std::shared_ptr<SomeObject> localref;
objlock.lock();
localref = objref;
objlock.unlock();
localref->dosomething(); // ok; local reference
}
void notok1() {
objref->dosomething(); // not ok; reference may be modified
}
void notok2() {
std::shared_ptr<SomeObject> localref = objref; // not ok; objref may be modified
localref->dosomething();
}
Note that simultaneous reads on a shared_ptr is safe, so you can choose to use a read-write lock if it makes sense for your application.
If a object is in use then you should make sure that the destructor of the object is not being called before the use of the object ends. If this is the behavior you have then its a potential problem and it really needs to be fixed.
You should make sure that if one thread is destroying your objects then another thread should not be calling functions on that object or the first thread should wait till second thread completes the function calling.
Yes, even destructors might need critical sections to protect updating some global data which is not related to the class itself.
It's possible that while one thread is waiting for CS in destructor the other is destroying the object and if CS belongs to object it will be destroyed as well. So that's not a good design.
You absolutely, positively need to make sure your object lifetime is less than the consumer threads, or you are in for some serious headaches. Either:
Make the consumers of the object children so it's impossible for them to exist outside of your object, or
use message passing/broker.
If you go the latter route, I highly recommend 0mq http://www.zeromq.org/.
Yes while you are in destructor, the object is already invalidated.
I used Destroy() method that enters critical section and then destroys it self.
Lifetime of object is over before destructor is called?
Yes, it is fine to do that. If a class supports such use, clients don't need to synchronize destruction; i.e. they don't need to make sure that all other methods on the object have finished before invoking the destructor.
I would recommend that clients not assume they can do this unless it is explicitly documented. Clients do have this burden, by default, with standard library objects in particular(ยง17.6.4.10/2).
There are cases where it is fine, though; std::condition_variable's destructor, for example, specifically allows ongoing condition_variable::wait() method invocations when ~condition_variable() starts. It only requires that clients not initiate calls to wait() after ~condition_variable() starts.
It might be cleaner to require that the client synchronize access to the destructor โ€“ and constructor for that matter โ€“ like most of the rest of the standard library does. I would recommend doing that if feasible.
However, there are certain patterns where it might make sense to relieve clients of the burden of fully synchronizing destruction. condition_variable's overall pattern seems like one: consider use of an object that handles possibly long-running requests. The user does the following:
Construct the object
Cause the object to receive requests from other threads.
Cause the object to stop receiving requests: at this point, some outstanding requests might be ongoing, but no new ones can be invoked.
Destroy the object. The destructor will block until all requests are done, otherwise the ongoing requests might have a bad time.
An alternative would be to require that clients do need to synchronize access. You could imagine step 3.5 above where the client calls a shutdown() method on the object that does the blocking, after which it is safe for the client to destroy the object. However, this design has some downsides; it complicates the API and introduces an additional state for the object of shutdown-but-valid.
Consider instead perhaps getting step (3) to block until all requests are done. There are tradeoffs...