I would like to handle SIGINT signal from the kernel in order to call a function that grecefully shutdown my process.
Here's a working example, a thread is waiting the signal and then call the function handle_stop:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
void handle_stop(const boost::system::error_code& error, int signal_number) {
std::cout<<"Executing Safe Shutdown"<<std::cout;
exit(0);
}
int main() {
std::cout<<"Init"<<std::cout;
boost::asio::io_service signalService;
boost::asio::signal_set signals(signalService, SIGINT, SIGTERM, SIGQUIT);
signals.async_wait(handle_stop);
boost::thread signalThread(boost::bind(&boost::asio::io_service::run, &signalService));
std::cout<<"Starting programm"<<std::cout;
while (true) {
std::cout<<"Waiting ctl-c"<<std::cout;
sleep(1);
}
}
My goal is pack inside a class the thread and the function to call for shut the process down.
Here's an non working attempt, the process shutdown immediately without wait for the signal.
What is wrong?
#include <atomic>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
class Shutdown {
public:
Shutdown():is_signal_received_ (false) {
//signals_::remove();
std::cout<<"constructor"<<std::endl;
}
~Shutdown() {
}
void init() {
std::cout<<"Init "<<std::endl;
boost::asio::signal_set signals(signalService_, SIGINT, SIGTERM, SIGQUIT);
signals.async_wait(boost::bind(boost::mem_fn(&Shutdown::handleStop), this, _1, _2));
boost::thread signalThread(boost::bind(&boost::asio::io_service::run, &signalService_));
std::cout<<"Init Completed"<<std::endl;
}
bool isSignalReceived() const {
return is_signal_received_;
}
private:
std::atomic<bool> is_signal_received_;
boost::asio::io_service signalService_;
void handleStop(const boost::system::error_code& error, int signal_number) {
is_signal_received_ = true;
myHandleStop(error, signal_number);
}
virtual void myHandleStop(const boost::system::error_code& error, int signal_number) {
}
};
class MyShutdown: public Shutdown {
private:
void myHandleStop(const boost::system::error_code& error, int signal_number) {
std::cout<<"Executing Safe Shutdown"<<std::cout;
exit(0);
}
};
int main() {
MyShutdown safeShutdown;
safeShutdown.init();
while (true) {
std::cout<<"Waiting ctl-c"<<std::cout;
sleep(1);
}
}
Here's the command for compiling:
g++ -o main main.cpp -lpthread -l boost_thread -l boost_system --std=c++11
Your issue is that your signal_set goes out of scope and is destroyed at the end of Shutdown::init. When that happens, the async_wait is cancelled. signalThread also goes out of scope at the same time without being either detached or joined. Those both need to be class members so that they stay alive until a signal can be handled:
#include <atomic>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
class Shutdown {
public:
Shutdown()
: is_signal_received_ (false),
signalService_(),
signals_(signalService_, SIGINT, SIGTERM, SIGQUIT)
{
std::cout<<"constructor"<<std::endl;
}
~Shutdown()
{
signals_.cancel();
signalService_.stop();
signalThread_.join();
}
void init() {
std::cout<<"Init "<<std::endl;
signals_.async_wait(boost::bind(&Shutdown::handleStop, this, _1, _2));
signalThread_ = boost::thread(boost::bind(&boost::asio::io_service::run, &signalService_));
std::cout<<"Init Completed"<<std::endl;
}
bool isSignalReceived() const {
return is_signal_received_;
}
private:
std::atomic<bool> is_signal_received_;
boost::asio::io_service signalService_;
boost::thread signalThread_;
boost::asio::signal_set signals_;
void handleStop(const boost::system::error_code& error, int signal_number) {
is_signal_received_ = true;
myHandleStop(error, signal_number);
}
virtual void myHandleStop(const boost::system::error_code& error, int signal_number) {
}
};
class MyShutdown: public Shutdown {
private:
void myHandleStop(const boost::system::error_code& error, int signal_number) {
std::cout<<"Executing Safe Shutdown"<<std::endl;
exit(0);
}
};
int main() {
MyShutdown safeShutdown;
safeShutdown.init();
while (true) {
std::cout<<"Waiting ctl-c"<<std::endl;
sleep(10);
}
}
I've also added calls to shut down the io_service and signal_set and to wait for the thread to terminate in ~Shutdown.
Related
I'm working on a timer class to perform operations on a different thread, sample code below is a copy from another SO question HERE
#include <thread>
#include <chrono>
#include <functional>
class Timer
{
public:
~Timer();
Timer() noexcept;
typedef std::chrono::milliseconds Interval;
typedef std::function<void(void)> Timeout;
public:
void start(const Interval& interval, const Timeout& timeout);
void stop();
private:
std::thread mThread; /** Timer thread */
bool mRunning = false; /** Timer status */
};
Implementation with a comment where the problem will occur:
Timer::~Timer()
{
}
Timer::Timer() noexcept
{
}
void Timer::start(const Interval& interval, const Timeout& timeout)
{
mRunning = true;
mThread = std::thread([&]()
{
while (mRunning == true)
{
std::this_thread::sleep_for(interval);
// std::abort will be called here
timeout();
}
});
}
void Timer::stop()
{
mRunning = false;
mThread.join();
}
Sample to test the timer:
#include <iostream>
int main()
{
Timer tm;
tm.start(std::chrono::milliseconds(1000), []
{
std::cout << "Hello!" << std::endl;
});
std::this_thread::sleep_for(std::chrono::seconds(4));
tm.stop();
}
I'm not able to understand why std::abort is called while executing the std::function within lambda and how do I resolve this?
Arguments to your start function are passed by reference. In your lambda, you capture them by reference. By the time you come around calling that lambda, everything you've captured is destroyed, thus you're causing undefined behavior.
Additionally, make sure to either use atomic<bool> instead of a regular bool:
#include <thread>
#include <chrono>
#include <functional>
#include <cstdio>
#include <atomic>
class Timer {
public:
~Timer() {
if (mRunning) {
stop();
}
}
typedef std::chrono::milliseconds Interval;
typedef std::function<void(void)> Timeout;
void start(const Interval &interval, const Timeout &timeout) {
mRunning = true;
mThread = std::thread([this, interval, timeout] {
while (mRunning) {
std::this_thread::sleep_for(interval);
timeout();
}
});
}
void stop() {
mRunning = false;
mThread.join();
}
private:
std::thread mThread{};
std::atomic_bool mRunning{};
};
int main() {
Timer tm;
tm.start(std::chrono::milliseconds(1000), [] {
std::puts("Hello!");
});
std::this_thread::sleep_for(std::chrono::seconds(4));
}
P.S. you might want to look into coroutines depending on where this idea is going.
Background
I am trying to stop periodic tasks when user interrupts process with SIGINT. I have based my periodic task scheduler on this answer.
To accomplish this I tried passing PeriodicScheduler instance pointer to my InterruptHandler and calling ps->stop().
Periodic Task Scheduler header:
#ifndef __PERIODICSCHEDULER_H__
#define __PERIODICSCHEDULER_H__
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>
namespace APP{
class PeriodicTask : boost::noncopyable {
public:
typedef std::function<void()> handler_fn;
PeriodicTask(boost::asio::io_service& ioService
, std::string const& name
, int interval
, handler_fn task);
void execute(boost::system::error_code const& e);
void start();
private:
void start_wait();
boost::asio::io_service& ioService;
boost::asio::deadline_timer timer;
handler_fn task;
std::string name;
int interval;
}; /* class PeriodicTask */
class PeriodicScheduler : boost::noncopyable
{
public:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
void run();
void stop();
void addTask(std::string const& name
, PeriodicTask::handler_fn const& task
, int interval);
private:
boost::asio::io_service io_service;
std::vector<std::unique_ptr<PeriodicTask>> tasks;
}; /* PeriodicScheduler */
} /* namespace Resto */
#endif /* __PERIODICSCHEDULER_H__ */
Periodic Task Scheduler source:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>
#include "periodicScheduler.h"
APP::PeriodicTask::PeriodicTask(boost::asio::io_service& ioService
, std::string const& name
, int interval
, handler_fn task)
: ioService(ioService)
, interval(interval)
, task(task)
, name(name)
, timer(ioService){
// Schedule start to be ran by the io_service
ioService.post(boost::bind(&PeriodicTask::start, this));
}
void APP::PeriodicTask::execute(boost::system::error_code const& e){
if (e != boost::asio::error::operation_aborted) {
task();
timer.expires_at(timer.expires_at() + boost::posix_time::seconds(interval));
start_wait();
}
}
void APP::PeriodicTask::start(){
// Uncomment if you want to call the handler on startup (i.e. at time 0)
// task();
timer.expires_from_now(boost::posix_time::seconds(interval));
start_wait();
}
void APP::PeriodicTask::start_wait(){
timer.async_wait(boost::bind(&PeriodicTask::execute
, this
, boost::asio::placeholders::error));
}
void APP::PeriodicScheduler::run(){
io_service.run();
}
void APP::PeriodicScheduler::stop(){
io_service.stop();
}
void APP::PeriodicScheduler::addTask(std::string const& name
, PeriodicTask::handler_fn const& task
, int interval){
tasks.push_back(make_unique<PeriodicTask>(std::ref(io_service)
, name, interval, task));
}
The following is InterruptHandler:
#include <csignal>
#include <condition_variable>
#include <mutex>
#include <iostream>
#include <boost/asio.hpp>
#include "periodicScheduler.h"
static std::condition_variable _condition;
static std::mutex _mutex;
namespace APP {
class InterruptHandler {
public:
static void hookSIGINT() {
signal(SIGINT, handleUserInterrupt);
}
static void handleUserInterrupt(int signal){
if (signal == SIGINT) {
std::cout << "SIGINT trapped ..." << '\n';
_condition.notify_one();
}
}
static void waitForUserInterrupt(APP::PeriodicScheduler *ps) {
std::unique_lock<std::mutex> lock { _mutex };
_condition.wait(lock);
ps->stop();
std::cout << "user has signaled to interrup program..." << '\n';
lock.unlock();
}
};
}
My main()
int main(int ac, const char * av[]) {
InterruptHandler::hookSIGINT();
APP::PeriodicScheduler ps;
APP::WorkerClass wc;
// WorkerClass::someTask and WorkerClass:someOtherTask are dummy functions only with sleep(5); inside them
ps.addTask("someTask", boost::bind( &APP::WorkerClass::someTask, wc ), 60);
ps.addTask("someOtherTask", boost::bind( &APP::WorkerClass::someOtherTask, wc ), 60);
ps.run();
InterruptHandler::waitForUserInterrupt(&ps);
return 0;
}
Issue
After running my app in terminal I pressed CTRL+C to trigger interrupt. I can see SIGINT trapped ... in the terminal but, application continues to run.
If I comment out ps.run(); statement, upon pressing CTRL+C I can see SIGINT trapped ... followed by user has signaled to interrup program... and application exits.
Questions
Is my approach correct? How can I effectively stop scheduled tasks and exit application?
Did I miss something?
By all means, I'd suggest using signal_set https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/signal_set.html
Here are some examples: https://stackoverflow.com/search?q=user%3A85371+signal_set
The best part is that is insulates you from some platform specific things and removes common pitfalls related to writing async-safe handlers.
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();
}
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).
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...