0

I am getting acquainted with boost thread and signals. I am thus implementing this simple example, I only post the cpp file of an example class implementing a thread capable of executing a method when a Signal1 is fired. The signal is defined within a Package1Signals singleton (excuse me for these names, they have been generated from a model)

Class1.hpp

#ifndef CLASS1_HEADER
#define CLASS1_HEADER
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/asio/signal_set.hpp>
#include "Package1.hpp"
#include <boost/thread.hpp>


class Class1{
private:
    boost::asio::io_service service;
public:
    boost::thread  thread;
    Class1();
    void classifierBehavior();
    void Reception1(Signal1 signal1);
    void Reception1Behavior();
};


#endif

Class1.cpp

#include "Class1.hpp"
#include <boost/thread.hpp>
#include <boost/date_time.hpp>
#include "Package1.hpp"
#include "Package2.hpp"
#include "Package1.hpp"

Class1::Class1(){
 //boost::thread thread(boost::bind(&Class1::classifierBehavior,boost::ref(*this)));
 //thread.join();
 thread = boost::thread(&Class1::classifierBehavior,this);
};

void Class1::classifierBehavior(){
  Package1Signals::getInstance()->signal1.connect(boost::bind(&Class1::Reception1, 
  this,_1));
  service.run();

};

 void Class1::Reception1(Signal1 signal1){
std::cout<<"Signal received\n";
service.post(boost::bind(&Class1::Reception1Behavior, this));
 }

 void Class1::Reception1Behavior(){
std::cout<<"Behavior executed\n";
 }

Package1.hpp

#ifndef PACKAGE1_HEADER
#define PACKAGE1_HEADER
#include <boost/signal.hpp>

struct Signal1{   };

class Package1Signals{
private:
    Package1Signals();
    static Package1Signals * instance;
public:
    boost::signal<void(Signal1)> signal1;
    static Package1Signals * getInstance();
 };
 #endif

Package1.cpp

 #include "Package1.hpp"
 Package1Signals * Package1Signals::instance = NULL;
 Package1Signals::Package1Signals(){}
 Package1Signals * Package1Signals::getInstance(){
    if(!instance){
            instance = new Package1Signals();
        }
    return instance;
 }

here is the code which executes it

int main() {
    Class1 test;
    Package1Signals::getInstance()->signal1();
    test.thread.join();
    int k =0;
    std::cin>>k;
    return 0;
 }

I can see the thread runs, the signal is intercepted but the posted handler is not executed. What am I doing wrong?

Andrea Sindico
  • 7,358
  • 6
  • 47
  • 84

2 Answers2

3

I can see the thread runs, the signal is intercepted

Are you sure? I don't think the signal is event sent.

but the posted handler is not executed. What am I doing wrong?

Your constructor creates a new thread, then waits for it to finish, so the constructor doesn't return until the new thread exits. That means this line in main won't execute until the receiver thread exits:

 Package1Signals::getInstance()->signal1();

Maybe you want the boost::thread to be a member of your class, so it has the same lifetime as your class, instead of being a local variable in the constructor.

For style considerations, you don't need to use boost::ref(*this) you can just pass this, and you don't need to use boost:bind to create the thread, read the docs which tell you that constructing a thread with multiple arguments is equivalent to passing those arguments to bind and constructing the thread with the result i.e. you can just say

thread(&Class1::classifierBehavior, this);

Which is much simpler and easier to read!

Update: Now you've fixed the constructor so it doesn't block you have a race condition between connecting the receiver to the signal, which happens in the new thread, and publishing the signal, which happens in the main thread, as soon as the constructor finishes. If the new thread takes a few milliseconds to start executing then it will be too late and miss the signal, because in the main thread the constructor will have finished and the signal will have been emitted already.

Try connecting the receiver before starting the new thread:

Class1::Class1(){
  Package1Signals::getInstance()->signal1.connect(boost::bind(&Class1::Reception1,   this,_1));
 thread = boost::thread(&Class1::classifierBehavior,this);
};

void Class1::classifierBehavior(){
  service.run();
};

This way the receiver is connected before the signal is emitted.

I think the io_service will still get the event even if the event gets posted before the service is running, but you might want to check that, otherwise there's another race condition there.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • Thanks I have changed the code following your suggestions. However I guess the join in the constructor was not the only problem as it seems now neither the signal is received by the object. – Andrea Sindico Jul 10 '12 at 21:59
  • You'll have to come up with a [simple self-contained complete example](http://sscce.org) then, showing the definitions of `Class1` and `Package1Signals`. Also, don't forget to call `join()` _somewhere_, maybe in the `Class1::~Class1()` destructor, otherwise the main thread will just return from `main` and the other thread will be killed, maybe before handling the signal. – Jonathan Wakely Jul 10 '12 at 22:22
  • I can't open your link :(. I have posted the whole code. The signal shall not be handled by the thread but by the instance (the object containing the reference to the thread) which will then post a task to the thread. However I can't either see the cout in the signal reception. Plus I have added the join call in the main function before the cin statement. The strange thing is that I can actually input an integer from the console though I should not as the main should be blocked waiting for the thread to finish its tasks (and the thread should be in the service.run loop) – Andrea Sindico Jul 11 '12 at 07:17
  • You connect to the signal in the new thread, so there's a race condition between sending the signal and connecting to receive it. What probably happens is the signal gets sent before the receiver is connected. Try making the `connect` call before starting the new thread. – Jonathan Wakely Jul 11 '12 at 07:34
  • It works and thanks to your kind explanation I have understood why. – Andrea Sindico Jul 11 '12 at 10:19
1

The thread.join(); statement basically waits till the thread exits. So what essentially happens is your constructor doesn't get completed until the thread exits, which is equivalent to running the thread code in your constructor.

The fix is to create the thread and keep the handle of it in the class. DON'T call thread.join(); till you want to wait for the thread to finish and join the main thread.

If your thread is created to be run by service, you need to run the service in order for your thread to start working. So you would have to do service.run(); in your constructor and not inside the function that is being run by the thread.

Also, if I remember it correctly, service can only be run in the same thread where it was created.

Vite Falcon
  • 6,575
  • 3
  • 30
  • 48
  • Thanks, see the correction and comment to the Jonathan answer – Andrea Sindico Jul 10 '12 at 22:02
  • I have changed it the way you suggested but it does not work. However I guess it is conceptually wrong as the service.run should actually be executed in the scope of the thread that will execute the service tasks. See this http://stackoverflow.com/questions/5050588/how-in-boost-send-a-signal-in-a-thread-and-have-the-corresponding-slot-executed – Andrea Sindico Jul 11 '12 at 06:10