c++ saving bound object and using it after asio - c++

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;
}

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).

Related

shared_from_this throws bad_weak_ptr with boost::asio

First, I have read all related questions listed.
They say, "you must have an existing shared_ptr to this before you can use shared_from_this." As far as I can see, there is no way I am violating that condition. I create the instance of Foo as a shared_ptr and enforced that it is always created as a shared_ptr. I then, stored the shared_ptr in a collection. Yet, I still get the bad_weak_ptr exception when shared_from_this is called.
#pragma once
#include <memory>
#include <vector>
//--------------------------------------------------------------------
class Foo : std::enable_shared_from_this<Foo>
{
public:
typedef std::shared_ptr<Foo> SharedPtr;
// Ensure all instances are created as shared_ptr in order to fulfill requirements for shared_from_this
static Foo::SharedPtr Create()
{
return Foo::SharedPtr(new Foo());
};
Foo(const Foo &) = delete;
Foo(Foo &&) = delete;
Foo & operator = (const Foo &) = delete;
Foo & operator = (Foo &&) = delete;
~Foo() {};
// We have to defer the start until we are fully constructed because we share_from_this()
void Start()
{
DoStuff();
}
private:
Foo() {}
void DoStuff()
{
auto self(shared_from_this());
}
};
//--------------------------------------------------------------------
int main()
{
std::vector<Foo::SharedPtr> foos;
Foo::SharedPtr foo = Foo::Create();
foos.emplace_back(foo);
foo->Start();
return 0;
}
You must inherit enable_shared_from_this with public specifier according to
Publicly inheriting from std::enable_shared_from_this provides the type T with a member function shared_from_this.
from http://en.cppreference.com/w/cpp/memory/enable_shared_from_this.
So write
class Foo : public std::enable_shared_from_this<Foo>
First off, you start the threads before ever posting work, so the io_service::run() is prone to complete before DoAccept is actually done.
Next, the base class must be PUBLIC for enable_shared_from_this to work:
class Connection : public std::enable_shared_from_this<Connection> {
Working self-contained code:
#include <iostream>
#include <mutex>
namespace SomeNamespace{
struct Logger {
enum { LOGGER_SEVERITY_INFO };
void Log(std::string const& msg, std::string const& file, unsigned line, int level) const {
static std::mutex mx;
std::lock_guard<std::mutex> lk(mx);
std::cout << file << ":" << line << " level:" << level << " " << msg << "\n";
}
template <typename... Args>
void LogF(std::string const& msg, Args const&... args) const {
static std::mutex mx;
std::lock_guard<std::mutex> lk(mx);
static char buf[2048];
snprintf(buf, sizeof(buf)-1, msg.c_str(), args...);
std::cout << buf << "\n";
}
static Logger &GetInstance() {
static Logger This;
return This;
}
};
} // namespace Somenamespace
#include <boost/asio.hpp>
#include <atomic>
#include <condition_variable>
#include <memory>
//--------------------------------------------------------------------
class ConnectionManager;
//--------------------------------------------------------------------
class Connection : public std::enable_shared_from_this<Connection> {
public:
typedef std::shared_ptr<Connection> SharedPtr;
// Ensure all instances are created as shared_ptr in order to fulfill requirements for shared_from_this
static Connection::SharedPtr Create(ConnectionManager *connectionManager, boost::asio::ip::tcp::socket &socket);
Connection(const Connection &) = delete;
Connection(Connection &&) = delete;
Connection &operator=(const Connection &) = delete;
Connection &operator=(Connection &&) = delete;
~Connection();
// We have to defer the start until we are fully constructed because we share_from_this()
void Start();
void Stop();
void Send(const std::vector<char> &data);
private:
ConnectionManager *m_owner;
boost::asio::ip::tcp::socket m_socket;
std::atomic<bool> m_stopped;
boost::asio::streambuf m_receiveBuffer;
mutable std::mutex m_sendMutex;
std::shared_ptr<std::vector<boost::asio::const_buffer> > m_sendBuffers;
bool m_sending;
std::vector<char> m_allReadData; // for testing
Connection(ConnectionManager *connectionManager, boost::asio::ip::tcp::socket socket);
void DoReceive();
void DoSend();
};
//--------------------------------------------------------------------
//#include "Connection.h"
//#include "ConnectionManager.h"
//**ConnectionManager.h **
//#pragma once
//#include "Connection.h"
// Boost Includes
#include <boost/asio.hpp>
// Standard Includes
#include <thread>
#include <vector>
//--------------------------------------------------------------------
class ConnectionManager {
public:
ConnectionManager(unsigned port, size_t numThreads);
ConnectionManager(const ConnectionManager &) = delete;
ConnectionManager(ConnectionManager &&) = delete;
ConnectionManager &operator=(const ConnectionManager &) = delete;
ConnectionManager &operator=(ConnectionManager &&) = delete;
~ConnectionManager();
void Start();
void Stop();
void OnConnectionClosed(Connection::SharedPtr connection);
protected:
boost::asio::io_service m_io_service;
boost::asio::ip::tcp::acceptor m_acceptor;
boost::asio::ip::tcp::socket m_listenSocket;
std::vector<std::thread> m_threads;
mutable std::mutex m_connectionsMutex;
std::vector<Connection::SharedPtr> m_connections;
void IoServiceThreadProc();
void DoAccept();
};
//--------------------------------------------------------------------
#include <boost/bind.hpp>
#include <algorithm>
//--------------------------------------------------------------------
Connection::SharedPtr Connection::Create(ConnectionManager *connectionManager, boost::asio::ip::tcp::socket &socket) {
return Connection::SharedPtr(new Connection(connectionManager, std::move(socket)));
}
//--------------------------------------------------------------------
Connection::Connection(ConnectionManager *connectionManager, boost::asio::ip::tcp::socket socket)
: m_owner(connectionManager), m_socket(std::move(socket)), m_stopped(false), m_receiveBuffer(), m_sendMutex(),
m_sendBuffers(), m_sending(false), m_allReadData() {}
//--------------------------------------------------------------------
Connection::~Connection() {
// Boost uses RAII, so we don't have anything to do. Let thier destructors take care of business
}
//--------------------------------------------------------------------
void Connection::Start() { DoReceive(); }
//--------------------------------------------------------------------
void Connection::Stop() {
// The entire connection class is only kept alive, because it is a shared pointer and always has a ref count
// as a consequence of the outstanding async receive call that gets posted every time we receive.
// Once we stop posting another receive in the receive handler and once our owner release any references to
// us, we will get destroyed.
m_stopped = true;
m_owner->OnConnectionClosed(shared_from_this());
}
//--------------------------------------------------------------------
void Connection::Send(const std::vector<char> &data) {
std::lock_guard<std::mutex> lock(m_sendMutex);
// If the send buffers do not exist, then create them
if (!m_sendBuffers) {
m_sendBuffers = std::make_shared<std::vector<boost::asio::const_buffer> >();
}
// Copy the data to be sent to the send buffers
m_sendBuffers->emplace_back(boost::asio::buffer(data));
DoSend();
}
//--------------------------------------------------------------------
void Connection::DoSend() {
// According to the boost documentation, we cannot issue an async_write while one is already outstanding
//
// If that is the case, it is OK, because we've added the data to be sent to a new set of buffers back in
// the Send method. Notice how the original buffer is moved, so therefore will be null below and how Send
// will create new buffers and accumulate data to be sent until we complete in the lamda
//
// When we complete in the lamda, if we have any new data to be sent, we call DoSend once again.
//
// It is important though, that DoSend is only called from the lambda below and the Send method.
if (!m_sending && m_sendBuffers) {
m_sending = true;
auto copy = std::move(m_sendBuffers);
auto self(shared_from_this());
boost::asio::async_write(m_socket, *copy,
[self, copy](const boost::system::error_code &errorCode, size_t bytes_transferred) {
std::lock_guard<std::mutex> lock(self->m_sendMutex);
self->m_sending = false;
if (errorCode) {
// An error occurred
return;
}
self->DoSend();
});
}
}
//--------------------------------------------------------------------
void Connection::DoReceive() {
SomeNamespace::Logger::GetInstance().Log(__PRETTY_FUNCTION__, __FILE__, __LINE__, SomeNamespace::Logger::LOGGER_SEVERITY_INFO);
auto self(shared_from_this()); // ***EXCEPTION HERE****
boost::asio::async_read_until(m_socket, m_receiveBuffer, '#',
[self](const boost::system::error_code &errorCode, size_t bytesRead) {
if (errorCode) {
// Notify our masters that we are ready to be destroyed
self->m_owner->OnConnectionClosed(self);
// An error occured
return;
}
// Grab the read data
std::istream stream(&self->m_receiveBuffer);
std::string data;
std::getline(stream, data, '#');
// Issue the next receive
if (!self->m_stopped) {
self->DoReceive();
}
});
}
//--------------------------------------------------------------------
//**ConnectionManager.cpp **
//#include "ConnectionManager.h"
//#include "Logger.h"
#include <boost/bind.hpp>
#include <system_error>
//------------------------------------------------------------------------------
ConnectionManager::ConnectionManager(unsigned port, size_t numThreads)
: m_io_service(), m_acceptor(m_io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
m_listenSocket(m_io_service), m_threads(numThreads) {}
//------------------------------------------------------------------------------
ConnectionManager::~ConnectionManager() { Stop(); }
//------------------------------------------------------------------------------
void ConnectionManager::Start() {
if (m_io_service.stopped()) {
m_io_service.reset();
}
DoAccept();
for (auto &thread : m_threads) {
if (!thread.joinable()) {
thread = std::thread(&ConnectionManager::IoServiceThreadProc, this);
}
}
}
//------------------------------------------------------------------------------
void ConnectionManager::Stop() {
{
std::lock_guard<std::mutex> lock(m_connectionsMutex);
m_connections.clear();
}
// TODO - Will the stopping of the io_service be enough to kill all the connections and ultimately have them get
// destroyed?
// Because remember they have outstanding ref count to thier shared_ptr in the async handlers
m_io_service.stop();
for (auto &thread : m_threads) {
if (thread.joinable()) {
thread.join();
}
}
}
//------------------------------------------------------------------------------
void ConnectionManager::IoServiceThreadProc() {
try {
// Log that we are starting the io_service thread
{
const std::string msg("io_service socket thread starting.");
SomeNamespace::Logger::GetInstance().Log(msg, __FILE__, __LINE__,
SomeNamespace::Logger::LOGGER_SEVERITY_INFO);
}
// Run the asynchronous callbacks from the socket on this thread
// Until the io_service is stopped from another thread
m_io_service.run();
} catch (std::system_error &e) {
SomeNamespace::Logger::GetInstance().LogF("System error caught in io_service socket thread. Error Code: %d", e.code().value());
} catch (std::exception &e) {
SomeNamespace::Logger::GetInstance().LogF("Standard exception caught in io_service socket thread. Exception: %s", e.what());
} catch (...) {
SomeNamespace::Logger::GetInstance().LogF("Unhandled exception caught in io_service socket thread.");
}
SomeNamespace::Logger::GetInstance().LogF("io_service socket thread exiting.");
}
//------------------------------------------------------------------------------
void ConnectionManager::DoAccept() {
SomeNamespace::Logger::GetInstance().Log(__PRETTY_FUNCTION__, __FILE__, __LINE__, SomeNamespace::Logger::LOGGER_SEVERITY_INFO);
m_acceptor.async_accept(m_listenSocket, [this](const boost::system::error_code errorCode) {
if (errorCode) {
return;
}
{
// Create the connection from the connected socket
Connection::SharedPtr connection = Connection::Create(this, m_listenSocket);
{
std::lock_guard<std::mutex> lock(m_connectionsMutex);
m_connections.push_back(connection);
connection->Start();
}
}
DoAccept();
});
}
//------------------------------------------------------------------------------
void ConnectionManager::OnConnectionClosed(Connection::SharedPtr connection) {
std::lock_guard<std::mutex> lock(m_connectionsMutex);
auto itConnection = std::find(m_connections.begin(), m_connections.end(), connection);
if (itConnection != m_connections.end()) {
m_connections.erase(itConnection);
}
}
//------------------------------------------------------------------------------
//**main.cpp**
//#include "ConnectionManager.h"
#include <cstring>
#include <iostream>
#include <string>
int main() {
ConnectionManager connectionManager(4000, 2);
connectionManager.Start();
std::this_thread::sleep_for(std::chrono::minutes(1));
connectionManager.Stop();
}

boost ASIO server segmentation fault

I created a server using Boost ASIO. It builds fine but as soon as I run it, it gives segmentation fault. Can't really figure out this behaviour.
Also, I read that this may be due to me not initialising the io_service object explicitly. If, that's the case then how do I modify this code so that I don't have to pass io_service object from outside the class.
Below is my code:
#include <iostream>
#include <string>
#include <memory>
#include <array>
#include <boost/asio.hpp>
using namespace boost::asio;
//Connection Class
class Connection : public std::enable_shared_from_this<Connection>{
ip::tcp::socket m_socket;
std::array<char, 2056> m_acceptMessage;
std::string m_acceptMessageWrapper;
std::string m_buffer;
public:
Connection(io_service& ioService): m_socket(ioService) { }
virtual ~Connection() { }
static std::shared_ptr<Connection> create(io_service& ioService){
return std::shared_ptr<Connection>(new Connection(ioService));
}
std::string& receiveMessage() {
size_t len = boost::asio::read(m_socket, boost::asio::buffer(m_acceptMessage));
m_acceptMessageWrapper = std::string(m_acceptMessage.begin(), m_acceptMessage.begin() + len);
return m_acceptMessageWrapper;
}
void sendMessage(const std::string& message) {
boost::asio::write(m_socket, boost::asio::buffer(message));
}
ip::tcp::socket& getSocket(){
return m_socket;
}
};
//Server Class
class Server {
ip::tcp::acceptor m_acceptor;
io_service m_ioService ;
public:
Server(int port):
m_acceptor(m_ioService, ip::tcp::endpoint(ip::tcp::v4(), port)){ }
virtual ~Server() { }
std::shared_ptr<Connection> createConnection(){
std::shared_ptr<Connection> newConnection = Connection::create(m_ioService);
m_acceptor.accept(newConnection->getSocket());
return newConnection;
}
void runService() {
m_ioService.run();
}
};
int main(int argc, char* argv[]) {
Server s(5000);
auto c1 = s.createConnection();
//do soething
s.runService();
return 0;
}
You are facing initialisation order issues. In your class Server, you have declared m_acceptor before m_ioService and using the uninitialized io_service object to construct the acceptor.
Just reorder the declarations inside the class. Surprisingly clang did not give any warning for this.
class Server {
io_service m_ioService ;
ip::tcp::acceptor m_acceptor;
public:
Server(int port):
m_acceptor(m_ioService, ip::tcp::endpoint(ip::tcp::v4(), port)){ }
virtual ~Server() { }
std::shared_ptr<Connection> createConnection(){
std::shared_ptr<Connection> newConnection = Connection::create(m_ioService);
m_acceptor.accept(newConnection->getSocket());
return newConnection;
}
void runService() {
m_ioService.run();
}
};

Using boost::asio::io_service as class member field

I have class where I use boost asio library:
Header:
class TestIOService {
public:
void makeConnection();
static TestIOService getInst();
private:
TestIOService(std::string address);
std::string address;
// boost::asio::io_service service;
};
Impl:
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/udp.hpp>
#include "TestIOService.h"
void TestIOService::makeConnection() {
boost::asio::io_service service;
boost::asio::ip::udp::socket socket(service);
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string("192.168.1.2"), 1234);
socket.connect(endpoint);
socket.close();
}
TestIOService::TestIOService(std::string address) : address(address) { }
TestIOService TestIOService::getInst() {
return TestIOService("192.168.1.2");
}
And main:
int main(void)
{
TestIOService service = TestIOService::getInst();
service.makeConnection();
}
When I have service defined in makeConnection method with this line:
boost::asio::io_service service;
there is no problem, but when I have it as class field member(commented out in code) I get this error:
note: ‘TestIOService::TestIOService(TestIOService&&)’ is implicitly
deleted because the default definition would be ill-formed:
class TestIOService {
io_service is not copyable.
You can make it shared quickly by wrapping it in shared_ptr<io_service>, but you should really reconsider the design first.
If your class needs to be copyable, it would logically not contain the io_service object
E.g. the following sample does create two instances of the test class not sharing a connection:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
class TestIOService {
public:
void makeConnection();
static TestIOService getInst();
private:
TestIOService(std::string address);
std::string address;
boost::shared_ptr<boost::asio::ip::udp::socket> socket;
boost::shared_ptr<boost::asio::io_service> service;
};
void TestIOService::makeConnection() {
using namespace boost::asio;
service = boost::make_shared<io_service>();
socket = boost::make_shared<ip::udp::socket>(*service);
socket->connect({ip::address::from_string("192.168.1.2"), 1234 });
//socket->close();
}
TestIOService::TestIOService(std::string address)
: address(address) { }
TestIOService TestIOService::getInst() {
return TestIOService("192.168.1.2");
}
int main() {
auto test1 = TestIOService::getInst();
auto test2 = TestIOService::getInst();
}

Extension of boost::asio hangs after being interrupted

Boris' article shows us how to create extension of boost::asio. I try to add signal_set and async_wait on registered signals. Then the program hangs until a second SIGINT is triggered. Though, I would like to finish it properly within one signal only.
Here is my code. I test it with gcc-4.6.3 and boost-1.52.0 on Ubuntu.
To compile -
gcc -I/boost_inc -L/boot_lib main.cpp -lpthread -lboost_system -lboost_thread
#include <boost/asio.hpp>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <cstddef>
template <typename Service>
class basic_timer
: public boost::asio::basic_io_object<Service>
{
public:
explicit basic_timer(boost::asio::io_service &io_service)
: boost::asio::basic_io_object<Service>(io_service)
{}
void wait(std::size_t seconds)
{ return this->service.wait(this->implementation, seconds); }
template <typename Handler>
void async_wait(std::size_t seconds, Handler handler)
{ this->service.async_wait(this->implementation, seconds, handler); }
};
class timer_impl;
template <typename TimerImplementation = timer_impl>
class basic_timer_service
: public boost::asio::io_service::service
{
public:
static boost::asio::io_service::id id;
explicit basic_timer_service(boost::asio::io_service &io_service)
: boost::asio::io_service::service(io_service),
async_work_(new boost::asio::io_service::work(async_io_service_)),
async_thread_(
boost::bind(&boost::asio::io_service::run, &async_io_service_))
{}
~basic_timer_service()
{
async_work_.reset();
async_io_service_.stop();
async_thread_.join(); // program is blocked here until the second
// signal is triggerd
async_io_service_.reset();
}
typedef boost::shared_ptr<TimerImplementation> implementation_type;
void construct(implementation_type &impl)
{
impl.reset(new TimerImplementation());
}
void destroy(implementation_type &impl)
{
impl->destroy();
impl.reset();
}
void wait(implementation_type &impl, std::size_t seconds)
{
boost::system::error_code ec;
impl->wait(seconds, ec);
boost::asio::detail::throw_error(ec);
}
template <typename Handler>
class wait_operation
{
public:
wait_operation(
implementation_type &impl,
boost::asio::io_service &io_service,
std::size_t seconds, Handler handler)
: impl_(impl),
io_service_(io_service),
work_(io_service),
seconds_(seconds),
handler_(handler)
{}
void operator()() const
{
implementation_type impl = impl_.lock();
if (!io_service_.stopped() && impl)
{
boost::system::error_code ec;
impl->wait(seconds_, ec);
this->io_service_.post(
boost::asio::detail::bind_handler(handler_, ec));
}
else
{
this->io_service_.post(
boost::asio::detail::bind_handler(
handler_, boost::asio::error::operation_aborted));
}
}
private:
boost::weak_ptr<TimerImplementation> impl_;
boost::asio::io_service &io_service_;
boost::asio::io_service::work work_;
std::size_t seconds_;
Handler handler_;
};
template <typename Handler>
void async_wait(
implementation_type &impl,
std::size_t seconds, Handler handler)
{
this->async_io_service_.post(
wait_operation<Handler>(
impl, this->get_io_service(), seconds, handler));
}
private:
void shutdown_service()
{}
boost::asio::io_service async_io_service_;
boost::scoped_ptr<boost::asio::io_service::work> async_work_;
boost::thread async_thread_;
};
class timer_impl
{
public:
timer_impl()
{}
~timer_impl()
{}
void destroy()
{}
void wait(std::size_t seconds, boost::system::error_code &ec)
{
sleep(seconds);
ec = boost::system::error_code();
}
};
typedef basic_timer<basic_timer_service<> > timer;
template <typename TimerImplementation>
boost::asio::io_service::id basic_timer_service<TimerImplementation>::id;
void wait_handler(const boost::system::error_code &ec)
{
std::cout << "5 s." << std::endl;
}
int main()
{
{
boost::asio::io_service io_service;
boost::asio::signal_set signals(io_service);
timer t(io_service);
signals.add(SIGINT);
signals.async_wait(
boost::bind(&boost::asio::io_service::stop, &io_service));
t.async_wait(2, wait_handler);
std:: cout << "async called\n" ;
io_service.run();
}
{ // this block will not be executed
boost::asio::io_service io_service;
timer t(io_service);
t.async_wait(2, wait_handler);
std:: cout << "async called\n" ;
io_service.run();
}
return 0;
}
After tried an example offered by the author of asio, I confronted the same behavior. Consequently, I dig into the library source and found that the source use io_service_impl's interfaces rather than ones of io_service. Furthermore, an operation functor posted to the io_service_impl is different from ones invoked by the io_service. Altogether, I decided to rewrite the timer example according to the internal interfaces of asio.
I hereby present the rewritten timer example.
#include <boost/asio.hpp>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <cstddef>
#define get_service_impl(X) \
ba::use_service<bad::io_service_impl>(X)
namespace ba = boost::asio;
namespace bad = boost::asio::detail;
// Nothing changed
template <typename Service>
class basic_timer
: public boost::asio::basic_io_object<Service>
{
public:
explicit basic_timer(boost::asio::io_service &io_service)
: boost::asio::basic_io_object<Service>(io_service)
{}
void wait(std::size_t seconds)
{ return this->service.wait(this->implementation, seconds); }
template <typename Handler>
void async_wait(std::size_t seconds, Handler handler)
{ this->service.async_wait(this->implementation, seconds, handler); }
};
// Nothing changed
class timer_impl
{
public:
void wait(std::size_t seconds, boost::system::error_code &ec)
{
sleep(seconds);
ec = boost::system::error_code();
}
};
// ----- Change a lot! --------
class basic_timer_service
: public boost::asio::io_service::service
{
public:
typedef boost::asio::detail::socket_ops::shared_cancel_token_type
implementation_type;
static boost::asio::io_service::id id;
explicit basic_timer_service(boost::asio::io_service &io_service)
: boost::asio::io_service::service(io_service),
io_service_impl_(get_service_impl(io_service)),
work_io_service_( new boost::asio::io_service ),
work_io_service_impl_(get_service_impl(*work_io_service_)),
work_(new ba::io_service::work(*work_io_service_)),
work_thread_() // do not create thread here
{}
~basic_timer_service()
{ shutdown_service(); }
void construct(implementation_type &impl)
{ impl.reset(new timer_impl()); }
void cancel(implementation_type &impl)
{
impl.reset((void*)0, boost::asio::detail::socket_ops::noop_deleter());
}
void destroy(implementation_type &impl)
{ impl.reset(); }
void shutdown_service()
{
work_.reset();
if(work_io_service_.get()){
work_io_service_->stop();
if (work_thread_.get()){
work_thread_->join();
work_thread_.reset();
}
}
work_io_service_.reset();
}
void wait(implementation_type &impl, std::size_t seconds)
{
boost::system::error_code ec;
// XXX I not sure this is safe
timer_impl *impl_ptr = static_cast<timer_impl*>(impl.get());
impl_ptr->wait(seconds, ec);
boost::asio::detail::throw_error(ec);
}
template <typename Handler>
class wait_operation
: public boost::asio::detail::operation
{
public:
BOOST_ASIO_DEFINE_HANDLER_PTR(wait_operation);
// namespace ba = boost::asio
// namespace bad = boost::asio::detail
wait_operation(
bad::socket_ops::weak_cancel_token_type cancel_token,
std::size_t seconds,
bad::io_service_impl& ios,
Handler handler)
: bad::operation(&wait_operation::do_complete),
cancel_token_(cancel_token),
seconds_(seconds),
io_service_impl_(ios),
handler_(handler)
{}
static void do_complete(
bad::io_service_impl *owner,
bad::operation *base,
boost::system::error_code const & /* ec */ ,
std::size_t /* byte_transferred */ )
{
wait_operation *o(static_cast<wait_operation*>(base));
ptr p = { boost::addressof(o->handler_), o, o};
// Distinguish between main io_service and private io_service
if(owner && owner != &o->io_service_impl_)
{ // private io_service
// Start blocking call
bad::socket_ops::shared_cancel_token_type lock =
o->cancel_token_.lock();
if(!lock){
o->ec_ = boost::system::error_code(
ba::error::operation_aborted,
boost::system::system_category());
}else{
timer_impl *impl = static_cast<timer_impl*>(lock.get());
impl->wait(o->seconds_, o->ec_);
}
// End of blocking call
o->io_service_impl_.post_deferred_completion(o);
p.v = p.p = 0;
}else{ // main io_service
bad::binder1<Handler, boost::system::error_code>
handler(o->handler_, o->ec_);
p.h = boost::addressof(handler.handler_);
p.reset();
if(owner){
bad::fenced_block b(bad::fenced_block::half);
boost_asio_handler_invoke_helpers::invoke(
handler, handler.handler_);
}
}
}
private:
bad::socket_ops::weak_cancel_token_type cancel_token_;
std::size_t seconds_;
bad::io_service_impl &io_service_impl_;
Handler handler_;
boost::system::error_code ec_;
};
template <typename Handler>
void async_wait(
implementation_type &impl,
std::size_t seconds, Handler handler)
{
typedef wait_operation<Handler> op;
typename op::ptr p = {
boost::addressof(handler),
boost_asio_handler_alloc_helpers::allocate(
sizeof(op), handler), 0};
p.p = new (p.v) op(impl, seconds, io_service_impl_, handler);
start_op(p.p);
p.v = p.p = 0;
}
protected:
// Functor for runing background thread
class work_io_service_runner
{
public:
work_io_service_runner(ba::io_service &io_service)
: io_service_(io_service) {}
void operator()(){ io_service_.run(); }
private:
ba::io_service &io_service_;
};
void start_op(bad::operation* op)
{
start_work_thread();
io_service_impl_.work_started();
work_io_service_impl_.post_immediate_completion(op);
}
void start_work_thread()
{
bad::mutex::scoped_lock lock(mutex_);
if (!work_thread_.get())
{
work_thread_.reset(new bad::thread(
work_io_service_runner(*work_io_service_)));
}
}
bad::io_service_impl& io_service_impl_;
private:
bad::mutex mutex_;
boost::scoped_ptr<ba::io_service> work_io_service_;
bad::io_service_impl &work_io_service_impl_;
boost::scoped_ptr<ba::io_service::work> work_;
boost::scoped_ptr<bad::thread> work_thread_;
};
boost::asio::io_service::id basic_timer_service::id;
typedef basic_timer<basic_timer_service> timer;
void wait_handler(const boost::system::error_code &ec)
{
if(!ec)
std::cout << "wait_handler is called\n" ;
else
std::cerr << "Error: " << ec.message() << "\n";
}
int main()
{
{
boost::asio::io_service io_service;
boost::asio::signal_set signals(io_service);
timer t(io_service);
signals.add(SIGINT);
signals.async_wait(
boost::bind(&boost::asio::io_service::stop, &io_service));
t.async_wait(2, wait_handler);
std:: cout << "async called\n" ;
io_service.run();
std:: cout << "exit loop\n";
}
{
boost::asio::io_service io_service;
timer t(io_service);
t.async_wait(2, wait_handler);
std:: cout << "async called\n" ;
io_service.run();
}
return 0;
}
To compile
gcc -I/boost_inc -L/boot_lib main.cpp -lpthread -lboost_system -lboost_thread
The new timer works fine. Still I would like to know how to write a non-intrusive extension of asio.

asio periodic timer

I want to implement a java-like Timer by asio's timer, it used to execute code periodically.
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
void print()
{
std::cout << "Hello, world!\n";
}
class WorldTimer
{
public:
boost::posix_time::ptime now()
{
return _timer.expires_at();
}
void update()
{
_f();
_timer.expires_at(_timer.expires_at() + boost::posix_time::milliseconds(1000));
_timer.async_wait(boost::bind(&WorldTimer::update, this));
}
WorldTimer(boost::asio::io_service& io, void (*f)()) : _f(f), _timer(io){}
private:
void (*_f)();
boost::asio::deadline_timer _timer;
};
int main() {
boost::asio::io_service io;
WorldTimer timer(io, print);
timer.update();
io.run();
return 0;
}
Program only output Hello, world! once and pending there. asio doc has a example, it works but I can not figure out what's the difference.
Yeah... timer has not been init a expire time, this is revised version:
class WorldTimer
{
public:
boost::posix_time::ptime now()
{
return _timer.expires_at();
}
WorldTimer(boost::asio::io_service& io, void (*f)()) : _f(f), _timer(io, boost::posix_time::microseconds(0))
{
_timer.async_wait(boost::bind(&WorldTimer::update, this));
}
private:
void (*_f)();
boost::asio::deadline_timer _timer;
void update()
{
_f();
_timer.expires_at(_timer.expires_at() + boost::posix_time::milliseconds(1000));
_timer.async_wait(boost::bind(&WorldTimer::update, this));
}
};
int main() {
boost::asio::io_service io;
WorldTimer timer(io, print);
io.run();
return 0;
}
Your deadline timer constructor is different from the one in the example. You need to explicitly set the expiry time.
The example code uses the other constructor which sets a particular expiry time relative to now.
So the print-out you are seeing is related to your call to update, which calls
_timer.expires_at(_timer.expires_at() + boost::posix_time::milliseconds(1000));
and _timer.expires_at() has not been set yet...