shmil_y
shmil_y

Reputation: 23

c++ saving bound object and using it after asio

I'm trying to save the result of bind to std:function, then pass it as parameter to another function, and store it as data member. Then I use asio async_wait, but when i return from the wait, and try to operate the function i saved i get segmentation fault. any Idea why?

#include <memory>
#include <iostream>
#include <asio/io_service.hpp>
#include <functional>
#include <asio/deadline_timer.hpp>

using namespace std;

typedef std::function<void (const std::error_code& error)> TM_callback;

class Timer {
public:
    Timer(asio::io_service& io_service) :_timer(io_service) {}

    void start(TM_callback cb) {
        _cb = cb;
      _timer.expires_from_now(boost::posix_time::milliseconds(1000));
        TM_callback timeoutFunc = std::bind(&Timer::onTimeout, this, std::placeholders::_1);
        _timer.async_wait(timeoutFunc);
    }

private:
    void onTimeout(const std::error_code& error) {
        (_cb)(error); // <-- here i get segmentation fault
    }

    TM_callback _cb;
    asio::deadline_timer _timer;
};

class COL {
public:
    COL(asio::io_service& io_service): _inTimer(io_service){}
void startInTimer() {
    TM_callback cb = std::bind(&COL::onInTimeout, this, std::placeholders::_1);
    _inTimer.start(cb);
}

private:
void onInTimeout(const std::error_code& error) {cout<<error.message();}
    Timer _inTimer;
};

int main()
{
    asio::io_service io_service;
    COL col(io_service);
    col.startInTimer();

    return 0;
}

Upvotes: 1

Views: 126

Answers (1)

sehe
sehe

Reputation: 393467

Ok, the most likely problem is in the code you don't show. As you can see @m.s. didn't "imagine" your problem. He forgot the io_service::run() too:

int main() {
    asio::io_service io_service;
    COL col(io_service);
    col.startInTimer();
    io_service.run();
}

Still no problem. Live On Coliru

The problem starts when inTimer is not guaranteed to live until the completion handler is executed:

int main() {
    asio::io_service io_service;
    {
        COL col(io_service);
        col.startInTimer();
    }
    io_service.run();
}

Now you have Undefined Behaviour: Live On Coliru

Solution

The easiest solution is to make the COL (what is that?) object live long enough. The more structural/idiomatic way would to let the bind keep the Timer object alive, e.g. using a shared_ptr:

Live On Coliru

#include <iostream>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
#include <boost/asio.hpp>

using namespace std;

typedef std::function<void(const boost::system::error_code &error)> TM_callback;

namespace asio = boost::asio;

class Timer : public boost::enable_shared_from_this<Timer> {
  public:
    Timer(asio::io_service &io_service) : _timer(io_service) {}

    void start(TM_callback cb) {
        _cb = cb;
        _timer.expires_from_now(boost::posix_time::milliseconds(1000));
        TM_callback timeoutFunc = boost::bind(&Timer::onTimeout, shared_from_this(), boost::asio::placeholders::error);
        _timer.async_wait(timeoutFunc);
    }

  private:
    void onTimeout(const boost::system::error_code &error) {
        (_cb)(error);
    }

    TM_callback _cb;
    asio::deadline_timer _timer;
};

class COL : public boost::enable_shared_from_this<COL> {
  public:
    COL(asio::io_service &io_service) : _svc(io_service) {}

    void startInTimer() {
        TM_callback cb = boost::bind(&COL::onInTimeout, shared_from_this(), boost::asio::placeholders::error);

        boost::shared_ptr<Timer> _inTimer = boost::make_shared<Timer>(_svc);
        _inTimer->start(cb);
    }

  private:
    void onInTimeout(const boost::system::error_code &error) { cout << error.message(); }
    asio::io_service& _svc;
};

int main() {
    asio::io_service io_service;
    {
        boost::make_shared<COL>(io_service)->startInTimer();
    }
    io_service.run();
}

Note that this subtly also fixes the problem that more than one timer couldn't be in flight at a give time (scheduling a new timer would cancel the pending one).

Upvotes: 1

Related Questions