mirror of
https://github.com/boostorg/mqtt5.git
synced 2025-07-29 12:07:43 +02:00
Add a chapter on asio compliance - executors
Summary: related to T12804 Reviewers: ivica Reviewed By: ivica Subscribers: miljen, iljazovic Differential Revision: https://repo.mireo.local/D28496
This commit is contained in:
@ -116,6 +116,7 @@
|
||||
[include 04_maintaining_a_stable_connection.qbk]
|
||||
[include 05_optimising_communication.qbk]
|
||||
[include 06_disconnecting_the_client.qbk]
|
||||
[include 07_asio_compliance.qbk]
|
||||
|
||||
[include 10_examples.qbk]
|
||||
|
||||
|
14
doc/qbk/07_asio_compliance.qbk
Normal file
14
doc/qbk/07_asio_compliance.qbk
Normal file
@ -0,0 +1,14 @@
|
||||
[section:asio_compliance Compliance with Boost.Asio]
|
||||
|
||||
Every asynchronous operation in __Asio__ has associated characteristics that specify their behaviour.
|
||||
|
||||
* An *allocator* determines how the asynchronous operations allocate memory resources.
|
||||
* A *cancellation slot* determines how the asynchronous operations support cancellation.
|
||||
* An *executor* determines the queuing and execution strategy for completion handlers.
|
||||
|
||||
This section expands further into the roles of allocators, cancellation slots, and [link async_mqtt5.asio_compliance.executors Executors],
|
||||
highlighting their integration and usage within the __Client__.
|
||||
|
||||
[include 08_executors.qbk]
|
||||
|
||||
[endsect] [/asio_compliance]
|
141
doc/qbk/08_executors.qbk
Normal file
141
doc/qbk/08_executors.qbk
Normal file
@ -0,0 +1,141 @@
|
||||
[section:executors Executors]
|
||||
|
||||
Every asynchronous operation has an associated executor that determines how the completion handlers are queued and run.
|
||||
Asynchronous operations use the associated executor to track the existence of the work of asynchronous tasks,
|
||||
schedule the completion handlers for execution,
|
||||
and prevent re-entrant execution of the completion handlers to avoid recursion and potential stack overflow issues.
|
||||
|
||||
Every asynchronous operation within the __Client__ is defined as a composed operation.
|
||||
This implies that each `async_xxx` operation is a sequence consisting of an initiating function,
|
||||
a series of intermediate asynchronous operations, and a final completion handler.
|
||||
|
||||
Upon creating an instance of the __Client__, it is necessary to provide an executor or an __ExecutionContext__.
|
||||
The specified executor (or __ExecutionContext__'s executor) will become the default executor associated with the __Client__
|
||||
and will be used for the execution of all the intermediate operations and a final completion handler for all asynchronous operations
|
||||
that have not bound an executor.
|
||||
If an executor is bound to an asynchronous operation, that executor will be used instead.
|
||||
|
||||
In this context, the [refmem mqtt_client async_run] operation is particularly important.
|
||||
It starts the __Client__, which initiates a series of internal asynchronous operations, all of which need an executor.
|
||||
If the [refmem mqtt_client async_run] is called with a completion handler that has an associated executor,
|
||||
then all the internal asynchronous operations will also be associated with the same executor.
|
||||
Otherwise, the default executor from the __Client__'s construction will be used instead.
|
||||
|
||||
[note
|
||||
If the [refmem mqtt_client async_run]'s completion handler has an associated executor,
|
||||
*the associated executor will become the new default associated executor* instead of the executor provided in the constructor.
|
||||
]
|
||||
|
||||
[important
|
||||
The same executor *must* execute [refmem mqtt_client async_run] and all the subsequent async_xxx operations.
|
||||
]
|
||||
|
||||
The following examples will demonstrate the previously described interactions.
|
||||
|
||||
[heading Example 1: using the constructor's executor as the default associated executor]
|
||||
|
||||
In this code snippet, the __Client__ is constructed with a strand
|
||||
without explicitly associating an executor with the completion handler of [refmem mqtt_client async_run].
|
||||
Consequently, the __Client__ adopts the strand as its new default executor,
|
||||
which is used to execute the [refmem mqtt_client async_publish] operation.
|
||||
|
||||
```
|
||||
int main() {
|
||||
boost::asio::io_context ioc;
|
||||
|
||||
// Construct the Client with a strand.
|
||||
auto strand = boost::asio::make_strand(ioc.get_executor());
|
||||
async_mqtt5::mqtt_client<boost::asio::ip::tcp::socket> client(strand);
|
||||
|
||||
client.brokers("<your-mqtt-broker>", 1883)
|
||||
.async_run(boost::asio::detached);
|
||||
|
||||
// This asynchronous operation will use the default associated executor,
|
||||
// which is the strand with which the Client is constructed.
|
||||
client.async_publish<async_mqtt5::qos_e::at_most_once>(
|
||||
"<topic>", "Hello world!",
|
||||
async_mqtt5::retain_e::no, async_mqtt5::publish_props {},
|
||||
[&client, &strand](async_mqtt5::error_code /* ec */) {
|
||||
assert(strand.running_in_this_thread());
|
||||
client.cancel();
|
||||
}
|
||||
);
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
[heading Example 2: binding an executor to async_run's handler overrides the default associated executor]
|
||||
|
||||
In this code snippet, the __Client__ is constructed with __IOC__'s executor
|
||||
with explicitly associating an executor (strand) with the completion handler of [refmem mqtt_client async_run].
|
||||
Consequently, the __Client__ adopts the strand as its new default executor,
|
||||
which is used to execute the [refmem mqtt_client async_publish] operation.
|
||||
|
||||
```
|
||||
int main() {
|
||||
boost::asio::io_context ioc;
|
||||
|
||||
// Create the Client with io_context's executor.
|
||||
async_mqtt5::mqtt_client<boost::asio::ip::tcp::socket> client(ioc.get_executor());
|
||||
|
||||
auto strand = boost::asio::make_strand(ioc.get_executor());
|
||||
client.brokers("<your-mqtt-broker>", 1883)
|
||||
// Bind the strand to async_run's completion handler.
|
||||
// Strand is now the default associated executor.
|
||||
.async_run(boost::asio::bind_executor(strand, boost::asio::detached));
|
||||
|
||||
client.async_publish<async_mqtt5::qos_e::at_most_once>(
|
||||
"<topic>", "Hello world!",
|
||||
async_mqtt5::retain_e::no, async_mqtt5::publish_props {},
|
||||
[&client, &strand](async_mqtt5::error_code /* ec */) {
|
||||
assert(strand.running_in_this_thread());
|
||||
client.cancel();
|
||||
}
|
||||
);
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
```
|
||||
|
||||
[heading Example 3: binding an executor to a async_xxx call]
|
||||
|
||||
In this code snippet, the __Client__ is constructed with __IOC__'s executor
|
||||
without explicitly associating an executor with the completion handler of [refmem mqtt_client async_run].
|
||||
The [refmem mqtt_client async_publish] operation bound the strand as the associated executor.
|
||||
Therefore, all the intermediate operations and the final completion handler will be executed
|
||||
on the strand.
|
||||
|
||||
[warning
|
||||
This example only serves as a demonstration and should *not* be used.
|
||||
It is *not* recommended that [refmem mqtt_client async_run] and other async_xxx operations execute on different executors!
|
||||
]
|
||||
|
||||
```
|
||||
int main() {
|
||||
boost::asio::io_context ioc;
|
||||
|
||||
async_mqtt5::mqtt_client<boost::asio::ip::tcp::socket> client(ioc.get_executor());
|
||||
|
||||
auto strand = boost::asio::make_strand(ioc.get_executor());
|
||||
client.brokers("<your-mqtt-broker>", 1883)
|
||||
.async_run(boost::asio::detached);
|
||||
|
||||
client.async_publish<async_mqtt5::qos_e::at_most_once>(
|
||||
"<topic>", "Hello world!",
|
||||
async_mqtt5::retain_e::no, async_mqtt5::publish_props {},
|
||||
boost::asio::bind_executor(
|
||||
strand,
|
||||
[&client, &strand](async_mqtt5::error_code /* ec */) {
|
||||
assert(strand.running_in_this_thread());
|
||||
client.cancel();
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
```
|
||||
|
||||
[endsect] [/executors]
|
Reference in New Issue
Block a user