Qpid Proton C++ API  0.18.0
Multithreading

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 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.