0

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.

abhiarora
  • 9,743
  • 5
  • 32
  • 57

1 Answers1

2

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

rafix07
  • 20,001
  • 3
  • 20
  • 33
  • I just solved the problem with executor_work_guard. However, I also want my `thread1_loop()` to run if there is no handler to execute. Looks like your second solution will work better for me. Is there any method which will implicity call `restart`? – abhiarora Nov 17 '19 at 14:55
  • @abhiarora No, you have to call `restart` manually, every time `io_service` is stopped. From [reference](https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/io_context/run/overload1.html), the following sentence is key point *Subsequent calls to run(), run_one(), poll() or poll_one() will return immediately unless there is a prior call to restart()* . Consider catching exceptions which can be thrown by `run` function. If handler throws exception, `run` doesn't catch it. You need to do it by yourself, putting `run` invocation in try-catch block. – rafix07 Nov 17 '19 at 15:06