Search code examples
c++multithreadingc++11destructorblocking

Force threads to leave object before destruction


When using multithreading I often run into the following problem:

I've got an object, say a network receiver(but could be anything). And a function that gets the data. Now sometimes there simply is no data, and you want to let the thread wait to get it's data. A blocking call, very much like being used by Berkeley sockets and it's derived implementations.

The principle is simple:

Forgive my mediocre illustrational skills. I'm a programmer after all:)

Well now of course there is other ways of implementing this. But my usual implementation with C++11 is as follows:

  • Object A Calls the function in object B on a separate thread, that is dedicated to this task.
  • Object B Uses an std::condition_variable construction to block the thread until the data is actually acquired.
  • Object A Places the data in a queue, which is read out by the main thread.

Now my actual problem arises on the destruction of object B, if it has to be destructed before object A(returning a nullptr, or something similar on the blocking call). I really wouldn't know how to efficiently wrap up object B.

The main problem is that object B isn't aware of the thread, and a thread can only have one handle, which is inside object A.

Example:

Some code to illustrate my problem.

Say I've got this function in object B:

data* getData
{
    std::unique_lock<std::mutex>l_newDataWaiterLock(m_newDataWaiterMutex);
    m_newDataWaiter.wait(l_newDataMutex);
    if(!running)
       return nullptr
    else 
       return data;
}

and this destructor:

~ObjectB()
{
    m_running = false;
    m_newDataWaiter.notifyAll();
    //Point X
}

With these member variables:

std::condition_variable m_newDataWaiter;
std::atomic<bool> m_running;

We still have the problem that the destructor will have to wait at the indicated Point X, until all the other threads have received the notify, and returned a null.

Now I could cook something up using atomic counters, more std::condition_variables and mutexes. But I have a feeling there has to be a more elegant and reliable solution to this problem:). Since this solution would require a notify in every getData call for the entire lifecycle of object B.

Note: I'm using C++11, so I've used that to illustrate everything. And I'm hoping to solve it using that. Although this of course this is a more of a general concurrency problem.


Solution

  • How about managing object B with a std::shared_ptr and using std::weak_ptr to store the pointer in A?

    That is slightly more inefficient as each time that A wants to get access, it temporarily needs to get a std::shared_ptr through std::weak_ptr::lock() itself, but you can be sure there is no race condition anymore.