In Boost.Asio, various objects
such as sockets and timers offer the ability to terminate all ongoing asynchronous
operations globally through their close
or cancel
member functions.
Beyond this global cancellation capability, Boost.Asio
also provides mechanisms for cancelling specific asynchronous operations
on an individual basis. This individual per-operation cancellation is enabled
by specifying that a completion handler has an associated cancellation slot.
If the asynchronous operation supports cancellation, then it will install
a cancellation handler into the associated slot. The cancellation handler
will be invoked when the user emits a cancellation signal into the cancellation
slot. See Per-Operation
Cancellation for more information.
The utility of per-operation cancellation becomes particularly evident when
using boost::asio::experimental::parallel_group
to launch work performed
in parallel and wait for one or all of the operations to complete. For instance,
in a parallel group awaiting the completion of any one operation among several,
completing one operation will trigger individual cancellation of all the
remaining operations. The same concept applies to the awaitable
operator ||
,
which runs two awaitables in parallel, waiting for one to complete and subsequently
cancelling the remaining one. See Co-ordinating
Parallel Operations for more information.
Within the
, every asynchronous
function is designed to support individual per-operation cancellation. This
allows for associating of a cancellation slot with any mqtt_client
async_xxx
function call, enabling the emission of a cancellation signal as needed.
The impact of emitting a cancellation signal varies depending on the signal
type (terminal, total, partial) and the operation being cancelled. Detailed
descriptions of how cancellation signals affect each async_xxx
function are provided in the Per-Operation Cancellation
paragraph in their respective sections of the
reference
documentation.
mqtt_client
This example illustrates associating a cancellation slot with a mqtt_client::async_publish
operation and emitting a terminal cancellation signal. Executing this sequence
effectively results in the immediate cancellation of the entire client operation,
mirroring the outcome of invoking mqtt_client::cancel
directly.
If a total or partial cancellation signal were issued instead of a terminal
one, the implications would be less severe. In such cases, the cancellation
would specifically target resending the PUBLISH
packet, preventing it from
being retransmitted should the client reconnect during the ongoing operation.
async_mqtt5::mqtt_client<boost::asio::ip::tcp::socket> client(ioc); client.brokers("<your-mqtt-broker>", 1883) .async_run(boost::asio::detached); boost::asio::cancellation_signal signal; client.async_publish<async_mqtt5::qos_e::at_least_once>( "<topic>", "Hello world!", async_mqtt5::retain_e::no, async_mqtt5::publish_props {}, boost::asio::bind_cancellation_slot( signal.slot(), [&client](async_mqtt5::error_code ec, async_mqtt5::reason_code rc, async_mqtt5::puback_props props ) { std::cout << ec.message() << std::endl; } ) ); signal.emit(boost::asio::cancellation_type_t::terminal);
As a result of supporting per-operation cancellation, all the asynchronous
functions with the
can be used
in mqtt_client
parallel_group
or with
awaitable operator
||
. This feature is especially beneficial
for executing operations that require a timeout mechanism.
Below are two examples illustrating how to implement a timeout:
In the context of
, the handling
of cancellation signals varies across different asynchronous operations.
Except for mqtt_client
mqtt_client::async_receive
,
all other async_xxx
operations
respond to a terminal cancellation signal by invoking mqtt_client::cancel
.
These operations will halt the resending of certain packets for total and
partial cancellation signals.
It is worth noting that cancelling an async_xxx
operation during an ongoing protocol exchange is not implemented because
of a design decision to prevent protocol breaches. For example, if mqtt_client::async_publish
with QoS 2 is in the middle of a communication with the Broker [6] and an attempt to cancel it is made, it could lead to a protocol
violation. For instance, if the operation is cancelled after a PUBREC
packet has been received
from the Broker but before sending the PUBREL
packet, that would breach
the MQTT protocol by failing to send a necessary packet and leave the connection
with the Broker in an invalid state.
Therefore, the design of
's cancellation
strategy carefully avoids these pitfalls to ensure continuous protocol
compliance.
mqtt_client