Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 287
dario_ramos Avatar asked Jul 15 '11 16:07

dario_ramos


People also ask

What is concurrency in C?

Concurrency is the execution of the multiple instruction sequences at the same time. It happens in the operating system when there are several process threads running in parallel. The running process threads always communicate with each other through shared memory or message passing.

What are destructors in C?

A destructor is a member function that is invoked automatically when the object goes out of scope or is explicitly destroyed by a call to delete . A destructor has the same name as the class, preceded by a tilde ( ~ ). For example, the destructor for class String is declared: ~String() .

Does the C language provide destructors?

There is no such thing called 'constructors' and 'destructors' in C programming language or in structured languages, although there is no boundaries on defining such functions which act like them. You need to make functions which act like the constructors and destructors and then call them manually.

Can destructors cause deadlocks?

Destroying the dictionary triggers the destructor of the Worker , causing a deadlock more often than not.


2 Answers

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.

like image 197
bdonlan Avatar answered Oct 01 '22 10:10

bdonlan


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.

like image 27
Alok Save Avatar answered Oct 01 '22 10:10

Alok Save