Phil
Phil

Reputation: 6162

call boost signals2 slot on a separate thread

How can one call a boost signals2 slot on a separate thread without blocking the thread that is emitting the signal? I would like to get the functionality of the Qt5 QObject::connect with the QueuedConnection argument; for Qt5 details see Threads and QObjects. That is, I need to be able to emit a boost signals2 signal with no possiblity of blocking the thread that is emitting the signal; the slots that are connected are called from a separate "event" thread.

I like the functionality of the Qt5 API, but I cannot have QObject as a base class and the additional MOC machinery in my API and implementation. Boost signals2 is thread safe in its connection handling, but it is not multithreaded (really I am just looking for non-blocking) in its calls to connected slots.

I believe a combination of boost signals2 and asio as an event loop could do what I need, but I am unsure how to implement this. Plus, I am sure other people have had similar needs, so I am looking for ideas and suggestions on how to achieve this.

Thanks!

Upvotes: 2

Views: 1553

Answers (1)

Elad Maimoni
Elad Maimoni

Reputation: 4575

boost::signals2::signal invoke slots sequentially on the thread that emits the signal. If you wish to achieve a non blocking behavior you would simply have to emit the signal on a different thread yourself.

From what I understand, Qt QueuedConnection not only means the slot is invoked asynchronously, but on the receiver specific thread event loop. This is different from invoking the slot on a arbitrary thread. Your intent is not 100% clear to me.

Assuming you don't care on which thread the slot is invoked, so long as it does not block, you could wrap your signal in a small object that posts to a different thread.

A simple example, using boost::asio:

struct async_signal : std::enable_shared_from_this<async_signal>
{
    boost::signals2::signal<void(void)> signal;
    boost::asio::io_context             executor;

    void emit_async()
    {
        boost::asio::post(excutor, [self_weak = weak_from_this()]()
            {
               if (auto self = self_weak.lock())
               {
                  signal();
               }
            });
    }
}

Now the slots will be invoked sequentially on whichever thread(s) that call io_context::run(). This is usually a thread pool of some sort such as boost::asio::thread_pool;

If you are wondering about the std::enable_shared_from_this part, this is for thread safety.

You could of course make this class templated and well encapsulated, but I think its easier to understand this way.

Upvotes: 4

Related Questions