I want to be able to post group of handlers to boost::asio::io_service and then run all of them. When all handlers finished, I want to add a new group of them and run() again. And repeat this forever in one thread.
But I have a problem that after the first run() call, next posted jobs are ignored.
Here is a small example (coliru):
#include <iostream>
#include <boost/asio.hpp>
int main()
{
boost::asio::io_service io;
io.post([]{ std::cout << "Hello";});
io.run();
io.post([]{ std::cout << ", World!" << std::endl; });
io.run();
}
It will print "Hello" message only and then successfully exit.
Why this example does not print "Hello, World!"?
Boost version: 1.71.0
You have to call restart:
A normal exit from the run() function implies that the io_context
object is stopped (the stopped() function returns true). Subsequent
calls to run(), run_one(), poll() or poll_one() will return
immediately unless there is a prior call to restart().
io.post([]{ std::cout << "Hello";});
io.run();
io.post([]{ std::cout << ", World!" << std::endl; });
io.restart(); // just here
io.run();
Related
I have written a small test program to understand the signal and slot mechanism provided by boost and their behavior when posted in different thread. I want to have slot's being called in different threads but the output of my program shows slots are not being called in different thread from which signal was emitted.
#include <iostream>
#include <boost/thread.hpp>
#include <boost/chrono.hpp>
#include <boost/random.hpp>
#include <boost/signals2.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/signals2/signal.hpp>
boost::signals2::signal<void (int)> randomNumberSig;
// ---------------- Thread 1 ----------------
boost::asio::io_service thread1_serv;
void handle_rnd_1(int number)
{
std::cout << "Thread1: " << boost::this_thread::get_id() << " & Number is " << number << std::endl;
}
void thread1_init(void)
{
std::cout << "Thread 1 Init" << std::endl;
boost::asio::io_service::work work (thread1_serv);
randomNumberSig.connect([] (int num) -> void {
std::cout << "Slot called from main thread" << std::endl;
thread1_serv.post(boost::bind(handle_rnd_1, num));
});
}
void thread1_loop(void)
{
}
void thread1(void)
{
thread1_init();
while (true) {
thread1_serv.run();
thread1_loop();
}
}
int main(int argc, char *argv[])
{
std::cout << "Starting the Program" << std::endl;
boost::thread t1(&thread1);
while (1) {
int num = 2;
std::cout << "Thread " << boost::this_thread::get_id() << " & Number: " << num << std::endl;
randomNumberSig(num);
boost::this_thread::sleep_for(boost::chrono::seconds(num));
}
return 0;
}
The output of the program is:
Starting the Program
Thread 7fae3a2ba3c0 & Number: 2
Thread 1 Init
Thread 7fae3a2ba3c0 & Number: 2
Slot called from main thread
Thread 7fae3a2ba3c0 & Number: 2
Slot called from main thread
Thread 7fae3a2ba3c0 & Number: 2
Slot called from main thread
I suspect post() method of the io_service is not working properly or I have missed something in initializing the io_service.
You don't handle invocation of run function properly.
You used work to prevent run from ending when there is no work to do.
But your work is local inside thread1_init so when this function ends, work
is destroyed and io_service::run exits when there are no handlers to be called.
After run finished, io_service is marked as stopped, and you need to call restart before
calling run (as subsequent invocation).
If you don't call restart, run returns immediately without processing any handlers - that is why you don't see them.
So first solution is to create work whose lifetime is the same as io_service (just use global variable - ugly):
boost::asio::io_service thread1_serv;
boost::asio::io_service::work work(thread1_serv);
Another solution, don't use work, just call restart before run:
thread1_init();
while (true) {
thread1_serv.restart();
thread1_serv.run();
thread1_loop();
}
Wandbox test
I try to run an async network thread using boost::asio and boost::thread.
But the async_accept returns immediately with error code 125 - operation canceled...
attached I a minimal sample of the Problem:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
class Server{
public:
Server()
{ }
void listen(unsigned int port)
{
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port);
boost::asio::ip::tcp::acceptor acceptor(m_io_service, endpoint);
std::cout << "Waiting for incomming connection on port: " << port << std::endl;
acceptor.async_accept(*m_stream.rdbuf(), boost::bind( &Server::handleAccept, this, boost::asio::placeholders::error, boost::ref( acceptor ) ) );
m_listenThread = new boost::thread(boost::bind(&boost::asio::io_service::run, &m_io_service));
}
void stop()
{
m_listenThread->join();
}
private:
void handleAccept(const boost::system::error_code& error, boost::asio::ip::tcp::acceptor& acceptor)
{
std::cout << "receiverd incomming connection" << std::endl;
if(error)
std::cout << "ERROR: " << error.message() << "(" << error.value() << ")" << std::endl;
}
boost::asio::io_service m_io_service;
boost::asio::ip::tcp::iostream m_stream;
boost::thread* m_listenThread;
};
int main(int argc, char *argv[])
{
Server server;
server.listen(10000);
while(1);
}
acceptor::async_accept returns immediately, scheduling a call of the handler when either there is an error or a connection is accepted (1)
the listen() function is returning, which is causing the destruction of the acceptor (2)
When an acceptor (or socket, or deadline_timer) is destroyed, all pending handlers are scheduled on the io_service with an error code of asio::error::operation_aborted. This is to satisfy the postconditions of the async_ functions (i.e., "the handler will be called exactly once, as if by io_service.post()") (3)
Therefore, at point (2), your handler is being scheduled - just before the code returns to the main loop.
To fix:
ensure that the acceptor survives until the handler has been called. This is standard practice in asio async programming. The examples on the boost website will help you to make sense of the (sparse) asio documentation.
Don't lose hope. It took me a long time to learn how to use asio properly, and to realise how fantastically powerful it is.
So I was experimenting with the deadline_timer class and wrote the code below to see if I could have on deadline_timer with multiple async_wait operations that would execute at different times.
Below I create a deadline timer in the main function all the way at the bottom and initially set it to expire after 3 seconds. Then I call an async_wait operation and pass the first print function as the handler. I then use the expires_from_now operation to set the time of expiration for what I intended to only affect the second async_wait call which has print2 as a handler. The output from running this is below the code.
This is test1.cpp
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/bind.hpp>
#include <time.h>
#include <sys/time.h>
double get_wall_time(){
struct timeval time;
if (gettimeofday(&time,NULL)){
// Handle error
return 0;
}
return (double)time.tv_sec + (double)time.tv_usec * .000001;
}
void print(double start, const boost::system::error_code& /*e*/)
{
std::cout << "Hello, world!" << std::endl;
std::cout << get_wall_time() - start << std::endl;
}
void print2(double start, const boost::system::error_code& /*e*/)
{
std::cout << "SECOND Hello, world!" << std::endl;
std::cout << get_wall_time() - start << std::endl;
}
int main(int argc, char* argv[])
{
boost::asio::io_service io;
boost::asio::deadline_timer timer(io, boost::posix_time::seconds(3));
auto start = get_wall_time();
timer.async_wait(boost::bind(print, start, boost::asio::placeholders::error));
timer.expires_from_now(boost::posix_time::seconds(20));
timer.async_wait(boost::bind(print2, start, boost::asio::placeholders::error));
io.run();
return 0;
}
Here is the output
Hello, world!
0.000774145
SECOND Hello, world!
20.0085
This is the output after commenting out the second async_wait with the expiration modification.
Hello, world!
3.00079
As you can see the first handler executes instantly when I intended for it to execute after 3 seconds. The second handler correctly executes after 20 seconds. Is there any way I could get the behavior I intended with a deadline_timer without having to create a bunch of them?
A timer must have only one outstanding async_wait at a time. IIRC, issuing another implicitly cancels the first one (which will fire it's handler with an error code) as if you called cancel() followed by async_wait().
If you want to respond to 2 timer events, you have 2 choices. Either have 2 timers, or set the timeout and issue the second async_wait in the handler of the first.
I was learning boost asio documentation.I came across this deadline_timer example.
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
/*This timer example shows a timer that fires once every second.*/
void print(const boost::system::error_code& e, boost::asio::deadline_timer* t, int* count)
{
if (*count < 5)
{
std::cout << *count << std::endl;
++(*count);
t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
t->async_wait(boost::bind(print,boost::asio::placeholders::error, t, count));
}
}
int main()
{
boost::asio::io_service io;
int count = 0;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(10));
auto myfunc = boost::bind(print, boost::asio::placeholders::error, &t ,&count);
t.async_wait(myfunc);
std::cout << "async wait " << std::endl;
io.run();
std::cout << "Just called io.run() " << std::endl;
std::cout << "Final count is " << count << std::endl;
return 0;
}
The async_wait() function seems to be blocking (i.e waiting for the 10 second timer to expire)
The output from the above program is as follows.
async wait
0
1
2
3
4
Just called io.run()
Final count is 5
I would expect an async_wait() to create a separate thread and wait for the timer to expire there meanwhile executing the main thread.
i.e I would expect the program to print
Just called io.run()
Final count is 5
while waiting for the timer to expire.? Is my understanding wrong?
This is my understanding of async_wait(). This implementation looks more like a blocking wait. Is my understanding wrong? What am I missing?
The io.run(); statement is the key to explaining the difference between the output you're getting and the output you're expecting.
In the ASIO framework, any asynchronous commands need to have a dedicated thread to run the callbacks upon. But because ASIO is relatively low-level, it expects you to provide the thread yourself.
As a result, what you're doing when you call io.run(); within the main thread is to specify to the framework that you intend to run all asynchronous commands on the main thread. That's acceptable, but that also means that the program will block on io.run();.
If you intend the commands to run on a separate thread, you'll have to write something like this:
std::thread run_thread([&]() {
io.run();
});
std::cout << "Just called io.run() " << std::endl;
std::cout << "Final count is " << count << std::endl;
run_thread.join();
return 0;
The async_wait function isn't blocking, run is. That's run's job. If you don't want a thread to block in the io_service's processing loop, don't have that thread call run.
The async_wait function doesn't create any threads. That would make it expensive and make it much harder to control the number of threads servicing the io_service.
Your expectation is unreasonable because returning from main terminates the process. So who or what would wait for the timer?
I have a program and would like to stop it by sending SIGINT for writing some data to a file instead of exiting immediately. However, if the user of the program sends SIGINT again, then the program should quit immediately and forget about writing data to a file.
For portability reason I would like to use boost::asio for this purpose.
My initial (simplified) approach (see below) did not work. Is this not possible or am I missing something?
The handler seems to be called only once (printing out the message) and the program always stops when the loop has reached the max iteration number.
void handler(
const boost::system::error_code& error,
int signal_number) {
if (!error) {
static bool first = true;
if(first) {
std::cout << " A signal(SIGINT) occurred." << std::endl;
// do something like writing data to a file
first = false;
}
else {
std::cout << " A signal(SIGINT) occurred, exiting...." << std::endl;
exit(0);
}
}
}
int main() {
// Construct a signal set registered for process termination.
boost::asio::io_service io;
boost::asio::signal_set signals(io, SIGINT);
// Start an asynchronous wait for one of the signals to occur.
signals.async_wait(handler);
io.run();
size_t i;
for(i=0;i<std::numeric_limits<size_t>::max();++i){
// time stepping loop, do some computations
}
std::cout << i << std::endl;
return 0;
}
When your first event is handled, you don't post any new work on the service object, so it exits.
This means that then (after the ioservice exited) the tight loop is started. This may not be what you expected.
If you want to listen for SIGINT again, you have to wait for the signal set again from the handler:
#include <boost/asio.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/bind.hpp>
#include <boost/atomic.hpp>
#include <iostream>
void handler(boost::asio::signal_set& this_, boost::system::error_code error, int signal_number) {
if (!error) {
static boost::atomic_bool first(true);
if(first) {
// do something like writing data to a file
std::cout << " A signal(SIGINT) occurred." << std::endl;
first = false;
this_.async_wait(boost::bind(handler, boost::ref(this_), _1, _2));
}
else {
std::cout << " A second signal(SIGINT) occurred, exiting...." << std::endl;
exit(1);
}
}
}
int main() {
// Construct a signal set registered for process termination.
boost::asio::io_service io;
boost::asio::signal_set signals(io, SIGINT);
// Start an asynchronous wait for one of the signals to occur.
signals.async_wait(boost::bind(handler, boost::ref(signals), _1, _2));
io.run();
return 2;
}
As you can see I bound the signal_set& reference to the handler in order to be able to async_wait on it after receiving the first signal. Also, as a matter of principle, I made first an atomic (although that's not necessary until you run the io_service on multiple threads).
Did you actually wish to run the io_service in the background? In that case, make it look like so:
signals.async_wait(boost::bind(handler, boost::ref(signals), _1, _2));
boost::thread(boost::bind(&boost::asio::io_service::run, boost::ref(io))).detach();
while (true)
{
std::cout << "Some work on the main thread...\n";
boost::this_thread::sleep_for(boost::chrono::seconds(1));
}
With typical output:
Some work on the main thread...
Some work on the main thread...
Some work on the main thread...
^CSome work on the main thread...
A signal(SIGINT) occurred.
Some work on the main thread...
Some work on the main thread...
^CSome work on the main thread...
A second signal(SIGINT) occurred, exiting....