Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In which order should I send callback() & notify waiters?

I have a class through which I offer some services asynchronously(the same calls can also be made synchronously). When requested an object of this class(say operator) starts the operation in a different thread. Other objects can register to the notifications of operator object so that when the operation ends OperationEnded() method is called on this objects. Other objects may also wait completion of this operation by calling Wait() on the operator object.

The code at the end of the operation is pretty much as follows:

_opEndedMutex.lock();
_thereIsOngoingOp = false;
_opEndedCondition.notify_all();
_opEndedMutex.unlock();

//no more call after notification
m_spNotificationManager->OperationEnded();

and the wait() function is as follows:

boost::unique_lock<boost::mutex> lock(_opEndedMutex);
while(_thereIsOngoingOp)
{
    _opEndedCondition.wait(_opEndedMutex);
}

The problem is about resource management. This is a C++ class so when the end of operation is detected the user of this class may delete the operator object(the destructor waits completion if there is any active operation). The end of the operation may either be detected by waiting or accepting notification. So if I call _opEndedCondition.notify_all() first and the user deletes the operator object it may crash while trying to call OperationEnded() because m_spNotificationManager is deleted. And if choose to call OperationEnded() first and the user deletes the operator object during this call then it may crash while trying to reach _opEndedMutex, _thereIsOngoingOp and _opEndedCondition.

The first thing that comes my to mind as a solution is to protect the two calls with a new mutex. This did not seem pretty because I cannot foresee what may happen if a new mutex is introduced and if in an OperationEnded() notification the user syncronously starts a new operation. I am also not sure how to use a new mutex in the wait() method.

note1: This API is both used in our own company's and other companies' applications. So I cannot get rid of either of the synchronization mechanisms.

note2: I've modified the original code, variable and method names so there may be typos, but the idea should be clear.

edit1:

The operator objects stay in a shared library generated through a factory and then exposed to the outside world through an interface. so typical lifetime of an operator object would be as :

IOperator * op = factory:getNewOperator();
//perform operations with op
op->Release() //this one goes and deletes the op object

Also note that operator objects are reusable. Client code can make a new operator object use it several times and delete it at the end.

like image 800
Mert Avatar asked Feb 19 '14 11:02

Mert


1 Answers

You have a few solutions to this:

  • You can specialize your operation object from std::enable_shared_from_this. This would mean that in the client code, you no longer delete the object, but set a std::shared_ptr to nullptr, and don't care when the object is actually deleted.

  • You can implement limited garbage collection, on a timer delay: When you receive the OperationEnded() notification in client code, you take the pointer and place it on a queue, along with a timestamp for when the object was added. The queue would then have an active object that wakes on a timer, takes the current time, and if the timestamp is (let's say) at least five seconds older than the current time, deletes it.

  • You can allocate and de-allocate the operation class using an object pool. When an object is no longer used, it is not actually deleted, but placed as a free (recycled) object in the pool. The object would actually be deleted when the pool is destroyed, after you finish your processing.

  • You can create a life-time-manager for your object, that would delete your operation object and then send the notification.

  • You can make your 'OperationEnded' function call delete this; at the end; Then, you would implement client code to simply set the pointer to NULL when receiving the notification; Such a solution is brittle though and it's possible that with your implementation, this just shifts the problem to a different piece of code.

  • Finally, you can implement a custom combination of all these.

like image 179
utnapistim Avatar answered Sep 27 '22 22:09

utnapistim