Reputation: 133
What happens when a deadline timer is destroyed during a context switch when one thread was already inside the timeout handler?
For example, if during execution of TimerCallback a context switch to another thread deleted ScheduledCommand?
ScheduledCommand::ScheduledCommand(
const boost::shared_ptr<CommandInterface> command,
const boost::posix_time::time_duration timeTillExecution):
mTimer(TheCommandTimerThread::instance()->IoService(), timeTillExecution),
mCommand(command)
{
mTimer.async_wait(boost::bind(&ScheduledCommand::TimerCallback,
this,
boost::asio::placeholders::error));
}
ScheduledCommand::~ScheduledCommand()
{
Cancel();
}
void ScheduledCommand::TimerCallback(const boost::system::error_code& error)
{
if (!error)
{
assert(mCommand);
mCommand->Execute();
}
}
The above code had a segmentation fault at mCommand->Execute(). GDB analysis showed that mCommand was invalid. Probably deleted from another thread. Thanks.
Edit:
Why doesn't the following change fix this problem?
ScheduledCommand::Cancel()
{
if (mTimer.cancel() == 0)
{
mTimer.wait()
}
}
Upvotes: 2
Views: 1565
Reputation: 21123
If, in a multithreaded environment, you delete an object in one thread, while using it another, you can get a crash, exactly as you are seeing.
When using boost::asio, if you cancel the timer, you still have to let it execute the handler (which will get an error that the operation was canceled) before you can safely destroy the object. Depending on the rest of your setup, there are a handful of approaches. Manual use of synchronization primatives, use of boost::asio::strand
to limit some interactions to a single thread, or use of boost::shared_ptr
to ensure that key objects remain viable until they are no longer referenced.
Without more information, it's difficult to tell you the best option.
Upvotes: 1