I have a boost::asio::io_service which is doing some work. Now I want to stop this service after a certain amount of time. My first approach was by using boost::thread(io_service.run()), but then I get errors.
Are there other ways to stop io_service?
Thank you!
You can use a deadline_timer.
You can also run the service on another thread like you tried:
boost::thread t = boost::thread(boost::bind(&boost::asio::io_service::run, boost::ref(io_service));
// sometime
io_service.stop(); // io_service is threadsafe
t.join();
Here's a deadline_timer example complete in C++03: Live On Coliru
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <iostream>
using namespace boost::asio;
using namespace boost;
io_service svc;
deadline_timer timer(svc);
void work()
{
this_thread::sleep_for(chrono::milliseconds(100));
std::cout << "Work done, rescheduling\n";
svc.post(work);
}
void expiration_handler(system::error_code ec)
{
if (ec != error::operation_aborted)
svc.stop();
}
int main()
{
svc.post(work);
timer.expires_from_now(posix_time::seconds(2));
timer.async_wait(expiration_handler);
svc.run();
}
This prints
Work done, rescheduling
until the deadline is reached after 2 seconds
std::this_thread::sleep_for(std::chrono::seconds(10));
io_service.stop();
Isn't it?
Use deadline_timer.
boost::asio::deadline_timer stop_timer(io_service);
...
// If require stopping
stop_timer.expires_from_now(boost::posix_time::seconds(10));
stop_timer.async_wait(
[&io_service](const boost::system::error_code &ec)
{
io_service.stop();
});
Related
I am new to Boost::asio and I am currently looking at io_context.
In the docs https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/io_context.html shown is the following example:
{
...
}
...
boost::asio::io_context io_context;
// Submit a function to the io_context.
boost::asio::post(io_context, my_task);
// Submit a lambda object to the io_context.
boost::asio::post(io_context,
[]()
{
...
});
// Run the io_context until it runs out of work.
io_context.run();
However, I would like to be able to post even after io_context.run() has been called.
Essentially, something like this:
#include <boost/asio.hpp>
int value = -1;
void my_task()
{
value = 42;
}
int main() {
boost::asio::io_context io_context;
// Submit a function to the io_context.
//boost::asio::post(io_context, my_task);
// Run the io_context until it runs out of work.
io_context.run();
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work(io_context.get_executor());
// Submit a lambda object to the io_context.
boost::asio::post(io_context,
[]()
{
my_task();
});
assert(value == 42);
}
After compiling the above with g++ -o a example.cpp -lboost_system -lpthread I am getting an assertion failure. What is the "right" way to accomplish this?
Either run the io_context on a separate thread, or, indeed use the execution context that already has that built in.
Using a manual thread
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
int value = -1;
void my_task() { value = 42; }
int main() {
boost::asio::io_context io_context;
auto work = make_work_guard(io_context);
std::thread thread([&] { io_context.run(); });
// submit to the io_context
post(io_context, my_task);
work.reset();
thread.join();
std::cout << value << "\n";
}
Prints
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
42
Using asio::thread_pool:
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
int value = -1;
void my_task() { value = 42; }
int main() {
boost::asio::thread_pool io(1);
post(io, my_task);
io.join();
std::cout << value << "\n";
}
As you can see it's basically the same, but
less code
less work (literally)
more correct (see e.g. Should the exception thrown by boost::asio::io_service::run() be caught?)
trivial to make multi-threaded thread_pool io(16); or just using the default thread_pool io;
io_context::run() blocks as long as there is work to do. In your first example, you could just wait until both the posted tasks are finished. After that, io_context::run() returns and you can submit new work and call run() again. If you don't want this sequential behavior but rather submit tasks while the io_context works on tasks, you need a second thread, that executes io_conext::run(). You can create one for example like this:
#include <thread>
//...
std::thread my_thread( [&](){ io_context.run(); } );
// post more work here
// wait for it to finish
my_thread.join();
Make sure that the io_context object lives longer than the thread, if you use the lamda capture by reference like in this example.
I wrote a little program to launch and record a stereo setup of cameras. I would like to record a sequence of 100ms. The thing is: I don't know how to time the functions with the highest possible precision. I found the header <unistd.h> which includes the function usleep which can pause the execution for a specified microsecond interval. So in my program I'm doing something like this:
left_camera.start_recording();
right_camera.start_recording();
usleep(100000);
left_camera.stop_recording();
right_camera.stop_recording();
Is there a better way to ensure precise timing between the two functions?
You can also use std::this_thread::sleep_for (C++11)
#include <chrono>
#include <thread>
int main()
{
std::this_thread::sleep_for(std::chrono::nanoseconds(500));
}
sleep is not better way to implement a timer. You can use asio asynchronous timer feature from c++ boost library. you can create the timer and this shall call register function after timer expires
#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
void func(const boost::system::error_code&)
{
std::cout << "in func" << std::endl;
}
int main()
{
boost::asio::io_service io;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(0.1));
t.async_wait(&func);
io.run();
return 0;
}
Link libboost_system library while compiling
For more information refer:
https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/tutorial.html
I am currently trying to create a server application using Boost::Asio that does two simple things:
Accept a client's incoming connection
Once the client has been accepted, start a boost::asio::deadline_timer which repeats itself
The following code shows my current attempt:
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
#include <WinSock2.h>
#include <Mswsock.h>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::asio;
using namespace boost::asio::ip;
void timerHandler(const boost::system::error_code& errorCode, deadline_timer* timer) {
timer->expires_at(timer->expires_at() + boost::posix_time::seconds(1));
timer->async_wait(boost::bind(timerHandler, _1, timer));
}
void acceptHandler(const boost::system::error_code &errorCode, io_service *ioService) {
deadline_timer timer(*ioService, boost::posix_time::seconds(1));
timer.async_wait(boost::bind(timerHandler, _1, &timer));
}
int main(int argc, char** argv) {
io_service ioService;
tcp::socket socket(ioService);
tcp::acceptor acceptor{ ioService, tcp::endpoint{ tcp::v4(), 12345 } };
acceptor.listen();
acceptor.async_accept(socket, boost::bind(acceptHandler, _1, &ioService));
ioService.run();
return EXIT_SUCCESS;
}
Problem:
The timer somehow does not work as expected in the acceptHandler. Somehow it gets cancelled twice, triggers an error on top of that and eventually crashes the entire application.
Handler Tracking Output:
#asio|1460922050.075890|0*1|socket#000000000015FAD0.async_accept
#asio|1460922051.153952|>1|ec=system:0
#asio|1460922051.153952|1*2|deadline_timer#000000000015F608.async_wait
#asio|1460922051.153952|1|deadline_timer#000000000015F608.cancel
#asio|1460922051.153952|<1|
#asio|1460922051.153952|>2|ec=system:995
#asio|1460922051.153952|2|deadline_timer#000000000015F608.cancel
Questions:
What causes the acceptHandler to cancel the deadline_timer in line 4 of the Handler Tracking output?
What casues the error 995 in line 6 of the Handler Tracking output? Error message is: The I/O operation has been aborted because of either a thread exit or an application request
What causes the timerHandler to cancel the deadline_timer in line 7 of the Handler Tracking output?
timer is allocated on the stack in the acceptHandler and is therefore not valid by the time timerHandler is called. You need to allocate the timer dynamically.
Also, you should check for error codes in both handlers. This is especially important when you want to end the program and cancel the timer.
I am currently trying to get the following application to work:
Await incoming client connection.
Start async. timer in another class.
While the timer runs repeatedly, do other stuff such as async_read and async_write.
Current source code:
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
#include <WinSock2.h>
#include <Mswsock.h>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include "TimerClass.hpp"
using namespace boost::asio;
using namespace boost::asio::ip;
TimerClass *timerClass;
void acceptHandler(const boost::system::error_code &errorCode, tcp::socket *socket) {
timerClass = new TimerClass(socket);
timerClass->startTimer();
while(true) {
// Do other suff such as async_write, ...
}
}
int main(int argc, char** argv) {
io_service ioService;
tcp::socket socket(ioService);
tcp::acceptor acceptor{ ioService, tcp::endpoint{ tcp::v4(), 12345 } };
acceptor.listen();
acceptor.async_accept(socket, boost::bind(acceptHandler, _1, &socket));
ioService.run();
return EXIT_SUCCESS;
}
TimerClass.hpp:
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::asio;
using namespace boost::posix_time;
class TimerClass {
public:
TimerClass(ip::tcp::socket *socket);
void startTimer();
void timerHandler(const boost::system::error_code& errorCode);
deadline_timer timer;
};
TimerClass.cpp:
#include <boost/bind.hpp>
#include "TimerClass.hpp"
TimerClass::TimerClass(ip::tcp::socket *socket) : timer(socket->get_io_service(), boost::posix_time::seconds(1)) {}
void TimerClass::startTimer() {
timer.async_wait(boost::bind(&TimerClass::timerHandler, this, boost::asio::placeholders::error));
}
void TimerClass::timerHandler(const boost::system::error_code& errorCode) {
timer.expires_at(timer.expires_at() + boost::posix_time::seconds(1));
timer.async_wait(boost::bind(&TimerClass::timerHandler, this, boost::asio::placeholders::error));
}
Handler Tracking Output:
#asio|1461070492.111630|0*1|socket#000000000021FBD0.async_accept
#asio|1461070498.527997|>1|ec=system:0
Questions:
Why won't it even call async_wait in startTimer? Debugging shows that startTimer gets called but I can't find anything in the Handler Tracking output. Why is that?
Am I correctly passing the socket to the TimerClass?
Without the infinite while(true) loop in the acceptHandler the acceptHandler returns but the application crashes before the io_service properly returns. How is that?
I compiled your code and it works for me (using boost version 1.54).
With your code I get the following output:
#asio|1461081908.437388|0*1|socket#003BFE2C.async_accept
#asio|1461081983.220840|>1|ec=system:0
#asio|1461081983.221817|1*2|deadline_timer#001C1318.async_wait
To make it run properly I had to remove the while(true) on your acceptHandler, obtaining the following output (added a std::cout inside the handler):
#asio|1461083707.104424|0*1|socket#0030FB6C.async_accept
#asio|1461083709.061824|>1|ec=system:0
#asio|1461083709.062803|1*2|deadline_timer#00641318.async_wait
#asio|1461083709.062803|<28158494073611763|
#asio|1461083710.064992|>2|ec=system:0
#asio|1461083710.064992|2|deadline_timer#00641318.cancel
#asio|1461083710.064992|2*3|deadline_timer#00641318.async_wait
TimerHandler executed...
#asio|1461083710.065971|<28169626628843099|
#asio|1461083711.065223|>3|ec=system:0
#asio|1461083711.065223|3|deadline_timer#00641318.cancel
#asio|1461083711.065223|3*4|deadline_timer#00641318.async_wait
TimerHandler executed...
I actually did this test using only the header TimerClass.hpp (defining the methods directly within it -I was lazy-) and it worked like a charm, the problem seems to be when using the .cpp file, that's why I asked if you were using include guards (not the issue though, already tested).
You should consider changing your design approach though, i.e. do not use blocking loops in you handlers, just call another asynchronous operation if needed (like async_read or async_write).
Take a look at this question and corresponding accepted answer for a nice server implementation idea. Or try to adapt some of the boost examples to your needs.
As per the segmentation fault you get when separating declaration from definition in the corresponding header and implementation files, you might want to check this other question.
I'm trying to asynchronously resolve a ftp host using Boost.Asio.
Here's what I've tried so far:
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
using boost::asio::ip::tcp;
class FtpSession {
public:
void Connect(std::string& host) {
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(host, "ftp");
resolver.async_resolve(query,
boost::bind(&FtpSession::OnResolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
}
private:
void OnResolve(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator) {
if (!err)
{
std::cout << "resolved!";
}
else
{
std::cout << "error.";
}
}
};
int main() {
FtpSession session;
std::string host("ftp.remotesensing.org");
session.Connect(host);
return 0;
}
But for some reason, when I execute it, it just doesn't print anything:
alon#alon-GA-73PVM-S2H:~/Desktop$ g++ -o test -lboost_system test.cc
alon#alon-GA-73PVM-S2H:~/Desktop$ ./test
alon#alon-GA-73PVM-S2H:~/Desktop$
No errors or warnings at the compilation though.
How can I fix this?
You need to call io_service.run() to actually do the work methinks. Think of the async_resolve as a request in a request queue - you need something (the io_service) to process the requests in the queue, and to do that, you actually need to run() it! In this case, it will see one request, execute it, call the handler and then exit.
Your io_service and ip::tcp::resolver object are going out of scope. Move both of them into members of the FtpSession class, then invoke io_service::run inside of main after session.Connect(host) to start the event loop.
I answered a similar question a few days ago that may help you as well.