c++multithreadingboostboost-asio

Crash when calling run on boost::asio::io_service


I am trying to write a relatively simple class 'CallbackTimer' that takes an amount of time and a function, and after the amount of time elapses the function is called. This can be repeated a few times if necessary.

It all works as intended, however I am occasionally getting a crash when I call start a couple of times before the timer has finished, but I can't see where the problem is. I assume it's because I'm not mixing my threads and the io_service correctly, but I'm not sure. Any help or pointers would be appreciated.

The class is as follows

CallbackTimer::CallbackTimer(const std::function<void()>& callback) :
  callback_(callback),
  currentRepeats_(0),
  //io_(boost::asio::io_service()),
  timer_(new boost::asio::deadline_timer(io_)),
  strand_(io_),
  thread_(nullptr)
{
}

CallbackTimer::~CallbackTimer()
{
  cancel();
}

void CallbackTimer::start(size_t intervalMillis, int repeats)
{
  if (thread_)
  {
    cancel();
  }

  requestedRepeats_ = repeats;
  intervalMillis_ = intervalMillis;
  currentRepeats_ = 0;
  runTimer();

  thread_.reset(new std::thread([this]()
  {
    this->io_.run();
  }));

  thread_->detach();
}

void CallbackTimer::cancel()
{
  timer_->cancel();

  if (thread_ && thread_->joinable())
  {
    thread_->join();
  }

  io_.reset();
}

void CallbackTimer::runCallback(const boost::system::error_code& e)
{
  if (e == boost::asio::error::operation_aborted)
  {
    io_.stop();
    return;
  }

  currentRepeats_++;
  callback_();

  if (currentRepeats_ >= requestedRepeats_)
  {
    io_.stop();
    return;
  }

  runTimer();
}

void CallbackTimer::runTimer()
{
  timer_->expires_from_now(boost::posix_time::millisec(intervalMillis_));
  timer_->async_wait(strand_.wrap(std::bind(&CallbackTimer::runCallback, this, std::placeholders::_1)));
}

Solution

  • At quick glance, there are two potential problems:

    Both problems result from no synchronization with the thread running the io_service. The thread is detached after its creation in CallbackTimer::start(), and the the if-statement within CallbackTimer::cancel() will always be false, causing no synchronization to occur. This allows for the following cases:

    To resolve this, consider no longer detaching from thread_ so that synchronization may occur.

    For further debugging, a stack trace or an mcve could help in identifying the exact source of the crash.