Loading...
Searching...
No Matches
Multi-threading

Full multithreading support is available with C++11 and later. Limited multithreading is possible with older versions of C++. See the last section of this page for more information.

`proton::container` handles multiple connections concurrently in a thread pool, created using `proton::container::run()`. As AMQP events occur on a connection the container calls `proton::messaging_handler` event callbacks. The calls for each connection are serialized - callbacks for the same connection are never made concurrently.

You assign a handler to a connection in `proton::container::connect()` or `proton::listen_handler::on_accept()` with `proton::connection_options::handler()`. We recommend you create a separate handler for each connection. That means the handler doesn't need locks or other synchronization to protect it against concurrent use by Proton threads. If you use the handler concurrently from non-Proton threads then you will need synchronization.

The examples multithreaded_client.cpp and multithreaded_client_flow_control.cpp illustrate these points.

# Thread-safety rules

`proton::container` is thread-safe with C++11 or greater. An application thread can open (or listen for) new connections at any time. The container uses threads that call `proton::container::run()` to handle network IO and call user-defined `proton::messaging_handler` callbacks.

`proton::container` ensures that calls to event callbacks for each connection instance are serialized (not called concurrently), but callbacks for different connections can be safely executed in parallel.

`proton::connection` and related objects (`proton::session`, `proton::sender`, `proton::receiver`, `proton::delivery`) are not thread-safe and are subject to the following rules.

1. They can only be used from a `proton::messaging_handler` event callback called by Proton or a `proton::work_queue` function (more below).
2. You cannot use objects belonging to one connection from a callback for another connection. We recommend a single handler instance per connection to avoid confusion.
3. You can store Proton objects in member variables for use in a later callback, provided you respect rule two.

`proton::message` is a value type with the same threading constraints as a standard C++ built-in type. It cannot be concurrently modified.

# Work queues

`proton::work_queue` provides a safe way to communicate between different connection handlers or between non-Proton threads and connection handlers.

- Each connection has an associated `proton::work_queue`.
- The work queue is thread-safe (C++11 or greater). Any thread can add work.
- Work is a `std::function`, and bound arguments will be called like an event callback.

When the work function is called by Proton, it will be serialized safely so that you can treat the work function like an event callback and safely access the handler and Proton objects stored on it.

The examples multithreaded_client.cpp and multithreaded_client_flow_control.cpp show how you can send and receive messages from non-Proton threads using work queues.

# The wake primitive

`proton::connection::wake()` allows any thread to "wake up" a connection by generating an `on_connection_wake()` callback. This is the only thread-safe `proton::connection` function.

This is a lightweight, low-level primitive for signaling between threads.

- It does not carry any code or data (unlike `proton::work_queue`).
- Multiple calls to `wake()` can be "coalesced" into a single `on_connection_wake()`.
- Calls to `on_connection_wake()` can occur without any call to `connection::wake()`. Proton uses wake internally.

The semantics of `wake()` are similar to `std::condition_variable::notify_one`. There will be a wakeup, but there must be some shared application state to determine why the wakeup occurred and what, if anything, to do about it.

Work queues are easier to use in many instances, but `wake()` may be useful if you already have your own external thread-safe queues and just need an efficient way to wake a connection to check them for data.

# Using older versions of C++

Before C++11 there was no standard support for threading in C++. You can use Proton with threads but with the following limitations.

- The container will not create threads, it will only use the single thread that calls `proton::container::run()`.
- None of the Proton library classes are thread-safe, including `proton::container` and `proton::work_queue`. You need an external lock to use `proton::container` in multiple threads.

The only exception is `proton::connection::wake()`, it is thread-safe even in older C++.

You can implement your own `proton::container` using your own threading library, or ignore the container and embed the lower-level `proton::io::connection_driver` in an external poller. These approaches still use the same `proton::messaging_handler` callbacks, so you can reuse most of your application code. Note that this is an advanced undertaking. There are a few pointers in IO integration.