2
0
mirror of https://github.com/boostorg/asio.git synced 2026-01-27 18:42:07 +00:00

Merge asio from 'develop'.

This commit is contained in:
Christopher Kohlhoff
2020-06-24 11:46:26 +10:00
449 changed files with 52888 additions and 2901 deletions

View File

@@ -398,10 +398,15 @@ Boost.Asio's asynchronous operations.
[heading Handler Tracking]
This example shows how to implement custom handler tracking.
This example header file shows how to implement custom handler tracking.
* [@boost_asio/example/cpp11/handler_tracking/custom_tracking.hpp]
This example program shows how to include source location information in
the handler tracking output.
* [@boost_asio/example/cpp11/handler_tracking/async_tcp_echo_server.cpp]
[heading HTTP Server]

View File

@@ -44,14 +44,14 @@ below shows the new Networking TS interfaces and the facilities they replace:
ExecutionContext].]
]
[
[[link boost_asio.reference.dispatch `post`]]
[[link boost_asio.reference.post `post`]]
[[link boost_asio.reference.io_context.post `io_service::post`]]
[The `dispatch` free function can be used to submit functions to any [link
[The `post` free function can be used to submit functions to any [link
boost_asio.reference.Executor1 Executor] or [link boost_asio.reference.ExecutionContext
ExecutionContext].]
]
[
[[link boost_asio.reference.dispatch `defer`]]
[[link boost_asio.reference.defer `defer`]]
[[link boost_asio.reference.io_context.post `io_service::post`] when the [link
boost_asio.reference.asio_handler_is_continuation `asio_handler_is_continuation`]
hook returns true]

View File

@@ -14,12 +14,7 @@ boost_asio.reference.co_spawn `co_spawn()`] function. These facilities allow pro
to implement asynchronous logic in a synchronous manner, in conjunction with
the `co_await` keyword, as shown in the following example:
boost::asio::co_spawn(executor,
[socket = std::move(socket)]() mutable
{
return echo(std::move(socket));
},
boost::asio::detached);
boost::asio::co_spawn(executor, echo(std::move(socket)), boost::asio::detached);
// ...
@@ -46,10 +41,12 @@ execute. For example, a server's per-client object may consist of multiple
coroutines; they should all run on the same `strand` so that no explicit
synchronisation is required.
The second argument is a nullary function object that returns a [link
boost_asio.reference.awaitable `boost::asio::awaitable<R>`],
where `R` is the type of return value produced by the coroutine. In the above
example, the coroutine returns `void`.
The second argument is an [link boost_asio.reference.awaitable `awaitable<R>`],
that is the result of the coroutine's entry point function, and in the above
example is the result of the call to `echo`. (Alternatively, this argument can
be a function object that returns the [link boost_asio.reference.awaitable
`awaitable<R>`].) The template parameter `R` is the type of return value
produced by the coroutine. In the above example, the coroutine returns `void`.
The third argument is a completion token, and this is used by `co_spawn()` to
produce a completion handler with signature `void(std::exception_ptr, R)`. This

View File

@@ -18,30 +18,31 @@ asynchronous operations are chained together, or what the pending asynchronous
operations are. As an illustration, here is the output when you run the HTTP
Server example, handle a single request, then shut down via Ctrl+C:
@asio|1512254357.979980|0*1|signal_set@0x7ffeaaaa20d8.async_wait
@asio|1512254357.980127|0*2|socket@0x7ffeaaaa20f8.async_accept
@asio|1512254357.980150|.2|non_blocking_accept,ec=asio.system:11
@asio|1512254357.980162|0|resolver@0x7ffeaaaa1fd8.cancel
@asio|1512254368.457147|.2|non_blocking_accept,ec=system:0
@asio|1512254368.457193|>2|ec=system:0
@asio|1512254368.457219|2*3|socket@0x55cf39f0a238.async_receive
@asio|1512254368.457244|.3|non_blocking_recv,ec=system:0,bytes_transferred=141
@asio|1512254368.457275|2*4|socket@0x7ffeaaaa20f8.async_accept
@asio|1512254368.457293|.4|non_blocking_accept,ec=asio.system:11
@asio|1512254368.457301|<2|
@asio|1512254368.457310|>3|ec=system:0,bytes_transferred=141
@asio|1512254368.457441|3*5|socket@0x55cf39f0a238.async_send
@asio|1512254368.457502|.5|non_blocking_send,ec=system:0,bytes_transferred=156
@asio|1512254368.457511|<3|
@asio|1512254368.457519|>5|ec=system:0,bytes_transferred=156
@asio|1512254368.457544|5|socket@0x55cf39f0a238.close
@asio|1512254368.457559|<5|
@asio|1512254371.385106|>1|ec=system:0,signal_number=2
@asio|1512254371.385130|1|socket@0x7ffeaaaa20f8.close
@asio|1512254371.385163|<1|
@asio|1512254371.385175|>4|ec=asio.system:125
@asio|1512254371.385182|<4|
@asio|1512254371.385202|0|signal_set@0x7ffeaaaa20d8.cancel
@asio|1589424178.741850|0*1|signal_set@0x7ffee977d878.async_wait
@asio|1589424178.742593|0*2|socket@0x7ffee977d8a8.async_accept
@asio|1589424178.742619|.2|non_blocking_accept,ec=asio.system:11
@asio|1589424178.742625|0|resolver@0x7ffee977d760.cancel
@asio|1589424195.830382|.2|non_blocking_accept,ec=system:0
@asio|1589424195.830413|>2|ec=system:0
@asio|1589424195.830473|2*3|socket@0x7fa71d808230.async_receive
@asio|1589424195.830496|.3|non_blocking_recv,ec=system:0,bytes_transferred=151
@asio|1589424195.830503|2*4|socket@0x7ffee977d8a8.async_accept
@asio|1589424195.830507|.4|non_blocking_accept,ec=asio.system:11
@asio|1589424195.830510|<2|
@asio|1589424195.830529|>3|ec=system:0,bytes_transferred=151
@asio|1589424195.831143|3^5|in 'async_write' (./../../../boost/asio/impl/write.hpp:330)
@asio|1589424195.831143|3*5|socket@0x7fa71d808230.async_send
@asio|1589424195.831186|.5|non_blocking_send,ec=system:0,bytes_transferred=1090
@asio|1589424195.831194|<3|
@asio|1589424195.831218|>5|ec=system:0,bytes_transferred=1090
@asio|1589424195.831263|5|socket@0x7fa71d808230.close
@asio|1589424195.831298|<5|
@asio|1589424199.793770|>1|ec=system:0,signal_number=2
@asio|1589424199.793781|1|socket@0x7ffee977d8a8.close
@asio|1589424199.793809|<1|
@asio|1589424199.793840|>4|ec=asio.system:125
@asio|1589424199.793854|<4|
@asio|1589424199.793883|0|signal_set@0x7ffee977d878.cancel
Each line is of the form:
@@ -74,6 +75,13 @@ The `<action>` takes one of the following forms:
usually the case for any unfinished asynchronous operations when the
`io_context` is destroyed.]
]
[
[n^m]
[The handler number `n` is about to create a new asynchronous operation with
completion handler number `m`. The `<description>` contains source location
information to help identify where in the program the asynchronous operation
is being started.]
]
[
[n*m]
[The handler number `n` created a new asynchronous operation with completion
@@ -105,6 +113,53 @@ As shown above, Each handler is assigned a numeric identifier. Where the
handler tracking output shows a handler number of 0, it means that the action
was performed outside of any handler.
[heading Adding Location Information]
[c++]
Programs may augment the handler tracking output's location information by
using the macro `BOOST_ASIO_HANDLER_LOCATION` in the source code. For example:
#define HANDLER_LOCATION \
BOOST_ASIO_HANDLER_LOCATION((__FILE__, __LINE__, __func__))
// ...
void do_read()
{
HANDLER_LOCATION;
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length)
{
HANDLER_LOCATION;
if (!ec)
{
do_write(length);
}
});
}
[teletype]
With the additional location information available, the handler tracking output
may include a call stack of source locations:
@asio|1589423304.861944|>7|ec=system:0,bytes_transferred=5
@asio|1589423304.861952|7^8|in 'async_write' (./../../../boost/asio/impl/write.hpp:330)
@asio|1589423304.861952|7^8|called from 'do_write' (handler_tracking/async_tcp_echo_server.cpp:62)
@asio|1589423304.861952|7^8|called from 'operator()' (handler_tracking/async_tcp_echo_server.cpp:51)
@asio|1589423304.861952|7*8|socket@0x7ff61c008230.async_send
@asio|1589423304.861975|.8|non_blocking_send,ec=system:0,bytes_transferred=5
@asio|1589423304.861980|<7|
Furthermore, if `std::source_location` or `std::experimental::source_location`
are available, the [link boost_asio.reference.use_awaitable_t `use_awaitable_t`]
token (when default-constructed or used as a default completion token) will
also cause handler tracking to output a source location for each newly created
asynchronous operation. A `use_awaitable_t` object may also be explicitly
constructed with location information.
[heading Visual Representations]
The handler tracking output may be post-processed using the included
@@ -137,6 +192,12 @@ preprocessor macros:
[`BOOST_ASIO_HANDLER_TRACKING_INIT(args)`]
[An expression that is used to initialise the tracking mechanism.]
]
[
[`BOOST_ASIO_HANDLER_LOCATION(args)`]
[A variable declaration that is used to define a source code location.
`args` is a parenthesised function argument list containing the file
name, line number, and function name.]
]
[
[`BOOST_ASIO_HANDLER_CREATION(args)`]
[An expression that is called on creation of an asynchronous operation.
@@ -214,7 +275,7 @@ preprocessor macros:
[heading See Also]
[link boost_asio.examples.cpp11_examples.handler_tracking Custom handler tracking
example].
[link boost_asio.examples.cpp11_examples.handler_tracking Handler tracking
examples].
[endsect]

View File

@@ -9,6 +9,135 @@
-->
<informaltable frame="all">
<tgroup cols="4">
<colspec colname="a"/>
<colspec colname="b"/>
<colspec colname="c"/>
<colspec colname="d"/>
<thead>
<row>
<entry valign="center" namest="a" nameend="a">
<bridgehead renderas="sect2">Properties</bridgehead>
</entry>
<entry valign="center" namest="b" nameend="d">
<bridgehead renderas="sect2">Execution</bridgehead>
</entry>
</row>
</thead>
<tbody>
<row>
<entry valign="top">
<bridgehead renderas="sect3">Customisation Points</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="boost_asio.reference.prefer">prefer</link></member>
<member><link linkend="boost_asio.reference.query">query</link></member>
<member><link linkend="boost_asio.reference.require">require</link></member>
<member><link linkend="boost_asio.reference.require_concept">require_concept</link></member>
</simplelist>
<bridgehead renderas="sect3">Traits</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="boost_asio.reference.can_prefer">can_prefer</link></member>
<member><link linkend="boost_asio.reference.can_query">can_query</link></member>
<member><link linkend="boost_asio.reference.can_require">can_require</link></member>
<member><link linkend="boost_asio.reference.can_require_concept">can_require_concept</link></member>
<member><link linkend="boost_asio.reference.is_nothrow_prefer">is_nothrow_prefer</link></member>
<member><link linkend="boost_asio.reference.is_nothrow_query">is_nothrow_query</link></member>
<member><link linkend="boost_asio.reference.is_nothrow_require">is_nothrow_require</link></member>
<member><link linkend="boost_asio.reference.is_nothrow_require_concept">is_nothrow_require_concept</link></member>
<member><link linkend="boost_asio.reference.prefer_result_type">prefer_result_type</link></member>
<member><link linkend="boost_asio.reference.query_result_type">query_result_type</link></member>
<member><link linkend="boost_asio.reference.require_result_type">require_result_type</link></member>
<member><link linkend="boost_asio.reference.require_concept_result_type">require_concept_result_type</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="boost_asio.reference.Executor1">Executor</link></member>
</simplelist>
<bridgehead renderas="sect3">Customisation Points</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="boost_asio.reference.execution__execute">execution::execute</link></member>
</simplelist>
<bridgehead renderas="sect3">Class Templates</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="boost_asio.reference.execution__any_executor">execution::any_executor</link></member>
</simplelist>
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="boost_asio.reference.execution__bad_executor">execution::bad_executor</link></member>
<member><link linkend="boost_asio.reference.execution__invocable_archetype">execution::invocable_archetype</link></member>
</simplelist>
<bridgehead renderas="sect3">Traits</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="boost_asio.reference.execution__is_executor">execution::is_executor</link></member>
<member><link linkend="boost_asio.reference.execution__can_execute">execution::can_execute</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Properties</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="boost_asio.reference.execution__allocator_t">execution::allocator_t</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_t">execution::blocking_t</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_t__possibly_t">execution::blocking_t::possibly_t</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_t__always_t">execution::blocking_t::always_t</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_t__never_t">execution::blocking_t::never_t</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_adaptation_t">execution::blocking_adaptation_t</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_adaptation_t__disallowed_t">execution::blocking_adaptation_t::disallowed_t</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_adaptation_t__allowed_t">execution::blocking_adaptation_t::allowed_t</link></member>
<member><link linkend="boost_asio.reference.execution__bulk_guarantee_t">execution::bulk_guarantee_t</link></member>
<member><link linkend="boost_asio.reference.execution__bulk_guarantee_t__unsequenced_t">execution::bulk_guarantee_t::unsequenced_t</link></member>
<member><link linkend="boost_asio.reference.execution__bulk_guarantee_t__sequenced_t">execution::bulk_guarantee_t::sequenced_t</link></member>
<member><link linkend="boost_asio.reference.execution__bulk_guarantee_t__parallel_t">execution::bulk_guarantee_t::parallel_t</link></member>
<member><link linkend="boost_asio.reference.execution__context_t">execution::context_t</link></member>
<member><link linkend="boost_asio.reference.execution__context_as_t">execution::context_as_t</link></member>
<member><link linkend="boost_asio.reference.execution__mapping_t">execution::mapping_t</link></member>
<member><link linkend="boost_asio.reference.execution__mapping_t__thread_t">execution::mapping_t::thread_t</link></member>
<member><link linkend="boost_asio.reference.execution__mapping_t__new_thread_t">execution::mapping_t::new_thread_t</link></member>
<member><link linkend="boost_asio.reference.execution__mapping_t__other_t">execution::mapping_t::other_t</link></member>
<member><link linkend="boost_asio.reference.execution__occupancy_t">execution::occupancy_t</link></member>
<member><link linkend="boost_asio.reference.execution__outstanding_work_t">execution::outstanding_work_t</link></member>
<member><link linkend="boost_asio.reference.execution__outstanding_work_t__untracked_t">execution::outstanding_work_t::untracked_t</link></member>
<member><link linkend="boost_asio.reference.execution__outstanding_work_t__tracked_t">execution::outstanding_work_t::tracked_t</link></member>
<member><link linkend="boost_asio.reference.execution__prefer_only">execution::prefer_only</link></member>
<member><link linkend="boost_asio.reference.execution__relationship_t">execution::relationship_t</link></member>
<member><link linkend="boost_asio.reference.execution__relationship_t__fork_t">execution::relationship_t::fork_t</link></member>
<member><link linkend="boost_asio.reference.execution__relationship_t__continuation_t">execution::relationship_t::continuation_t</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Property Objects</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="boost_asio.reference.execution__allocator">execution::allocator</link></member>
<member><link linkend="boost_asio.reference.execution__blocking">execution::blocking</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_t.possibly">execution::blocking.possibly</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_t.always">execution::blocking.always</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_t.never">execution::blocking.never</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_adaptation">execution::blocking_adaptation</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_adaptation_t.disallowed">execution::blocking_adaptation.disallowed</link></member>
<member><link linkend="boost_asio.reference.execution__blocking_adaptation_t.allowed">execution::blocking_adaptation.allowed</link></member>
<member><link linkend="boost_asio.reference.execution__bulk_guarantee">execution::bulk_guarantee</link></member>
<member><link linkend="boost_asio.reference.execution__bulk_guarantee_t.unsequenced">execution::bulk_guarantee.unsequenced</link></member>
<member><link linkend="boost_asio.reference.execution__bulk_guarantee_t.sequenced">execution::bulk_guarantee.sequenced</link></member>
<member><link linkend="boost_asio.reference.execution__bulk_guarantee_t.parallel">execution::bulk_guarantee.parallel</link></member>
<member><link linkend="boost_asio.reference.execution__context">execution::context</link></member>
<member><link linkend="boost_asio.reference.execution__context_as">execution::context_as</link></member>
<member><link linkend="boost_asio.reference.execution__mapping">execution::mapping</link></member>
<member><link linkend="boost_asio.reference.execution__mapping_t.thread">execution::mapping.thread</link></member>
<member><link linkend="boost_asio.reference.execution__mapping_t.new_thread">execution::mapping.new_thread</link></member>
<member><link linkend="boost_asio.reference.execution__mapping_t.other">execution::mapping.other</link></member>
<member><link linkend="boost_asio.reference.execution__occupancy">execution::occupancy</link></member>
<member><link linkend="boost_asio.reference.execution__outstanding_work">execution::outstanding_work</link></member>
<member><link linkend="boost_asio.reference.execution__outstanding_work_t.untracked">execution::outstanding_work.untracked</link></member>
<member><link linkend="boost_asio.reference.execution__outstanding_work_t.tracked">execution::outstanding_work.tracked</link></member>
<member><link linkend="boost_asio.reference.execution__relationship">execution::relationship</link></member>
<member><link linkend="boost_asio.reference.execution__relationship_t.fork">execution::relationship.fork</link></member>
<member><link linkend="boost_asio.reference.execution__relationship_t.continuation">execution::relationship.continuation</link></member>
</simplelist>
</entry>
</row>
</tbody>
</tgroup>
<tgroup cols="4">
<colspec colname="a"/>
<colspec colname="b"/>
@@ -26,6 +155,7 @@
<entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="boost_asio.reference.any_io_executor">any_io_executor</link></member>
<member><link linkend="boost_asio.reference.bad_executor">bad_executor</link></member>
<member><link linkend="boost_asio.reference.coroutine">coroutine</link></member>
<member><link linkend="boost_asio.reference.detached_t">detached_t</link></member>
@@ -36,16 +166,18 @@
<member><link linkend="boost_asio.reference.executor_arg_t">executor_arg_t</link></member>
<member><link linkend="boost_asio.reference.invalid_service_owner">invalid_service_owner</link></member>
<member><link linkend="boost_asio.reference.io_context">io_context</link></member>
<member><link linkend="boost_asio.reference.io_context__executor_type">io_context::executor_type</link></member>
<member><link linkend="boost_asio.reference.io_context.executor_type">io_context::executor_type</link></member>
<member><link linkend="boost_asio.reference.io_context__service">io_context::service</link></member>
<member><link linkend="boost_asio.reference.io_context__strand">io_context::strand</link></member>
<member><link linkend="boost_asio.reference.io_context__work">io_context::work</link> (deprecated)</member>
<member><link linkend="boost_asio.reference.multiple_exceptions">multiple_exceptions</link></member>
<member><link linkend="boost_asio.reference.service_already_exists">service_already_exists</link></member>
<member><link linkend="boost_asio.reference.static_thread_pool">static_thread_pool</link></member>
<member><link linkend="boost_asio.reference.system_context">system_context</link></member>
<member><link linkend="boost_asio.reference.system_executor">system_executor</link></member>
<member><link linkend="boost_asio.reference.this_coro__executor_t">this_coro::executor_t</link></member>
<member><link linkend="boost_asio.reference.thread_pool">thread_pool</link></member>
<member><link linkend="boost_asio.reference.thread_pool__executor_type">thread_pool::executor_type</link></member>
<member><link linkend="boost_asio.reference.thread_pool.executor_type">thread_pool::executor_type</link></member>
<member><link linkend="boost_asio.reference.yield_context">yield_context</link></member>
</simplelist>
</entry>
@@ -53,9 +185,9 @@
<bridgehead renderas="sect3">Free Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="boost_asio.reference.execution_context.add_service">add_service</link></member>
<member><link linkend="boost_asio.reference.asio_handler_allocate">asio_handler_allocate</link></member>
<member><link linkend="boost_asio.reference.asio_handler_deallocate">asio_handler_deallocate</link></member>
<member><link linkend="boost_asio.reference.asio_handler_invoke">asio_handler_invoke</link></member>
<member><link linkend="boost_asio.reference.asio_handler_allocate">asio_handler_allocate</link> (deprecated)</member>
<member><link linkend="boost_asio.reference.asio_handler_deallocate">asio_handler_deallocate</link> (deprecated)</member>
<member><link linkend="boost_asio.reference.asio_handler_invoke">asio_handler_invoke</link> (deprecated)</member>
<member><link linkend="boost_asio.reference.asio_handler_is_continuation">asio_handler_is_continuation</link></member>
<member><link linkend="boost_asio.reference.async_compose">async_compose</link></member>
<member><link linkend="boost_asio.reference.async_initiate">async_initiate</link></member>
@@ -80,11 +212,14 @@
<member><link linkend="boost_asio.reference.async_completion">async_completion</link></member>
<member><link linkend="boost_asio.reference.awaitable">awaitable</link></member>
<member><link linkend="boost_asio.reference.basic_io_object">basic_io_object</link></member>
<member><link linkend="boost_asio.reference.basic_system_executor">basic_system_executor</link></member>
<member><link linkend="boost_asio.reference.basic_yield_context">basic_yield_context</link></member>
<member><link linkend="boost_asio.reference.executor_binder">executor_binder</link></member>
<member><link linkend="boost_asio.reference.executor_work_guard">executor_work_guard</link></member>
<member><link linkend="boost_asio.reference.io_context__basic_executor_type">io_context::basic_executor_type</link></member>
<member><link linkend="boost_asio.reference.redirect_error_t">redirect_error_t</link></member>
<member><link linkend="boost_asio.reference.strand">strand</link></member>
<member><link linkend="boost_asio.reference.thread_pool__basic_executor_type">thread_pool::basic_executor_type</link></member>
<member><link linkend="boost_asio.reference.use_awaitable_t">use_awaitable_t</link></member>
<member><link linkend="boost_asio.reference.use_future_t">use_future_t</link></member>
</simplelist>

View File

@@ -81,6 +81,7 @@ INPUT = ./../../../boost/asio.hpp \
./../../../boost/asio/posix \
./../../../boost/asio/ssl \
./../../../boost/asio/windows \
./../../../boost/asio/execution \
./noncopyable_dox.txt \
./std_exception_dox.txt
FILE_PATTERNS =
@@ -221,7 +222,8 @@ PREDEFINED = GENERATING_DOCUMENTATION \
BOOST_ASIO_MUTABLE_BUFFER=mutable_buffer \
BOOST_ASIO_SYNC_OP_VOID=void \
BOOST_ASIO_STRING_VIEW_PARAM=string_view \
BOOST_ASIO_UNUSED_VARIABLE=
BOOST_ASIO_UNUSED_VARIABLE= \
BOOST_ASIO_UNSPECIFIED(e)=unspecified
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@@ -110,7 +110,8 @@
not(contains(compoundname, 'std_allocator_void')) and
not(contains(compoundname, 'thread_function')) and
not(contains(compoundname, 'context_impl')) and
not(contains(compoundname, 'initiate_'))">
not(contains(compoundname, 'initiate_')) and
not(contains(compoundname, '_is_deprecated'))">
<xsl:call-template name="class"/>
</xsl:if>
</xsl:when>
@@ -124,7 +125,8 @@
not(contains(name, 'std_allocator_void')) and
not(contains(name, 'thread_function')) and
not(contains(name, 'io_context_impl')) and
not(contains(name, 'initiate_'))">
not(contains(name, 'initiate_')) and
not(contains(name, '_is_deprecated'))">
<xsl:call-template name="namespace-memberdef"/>
</xsl:if>
</xsl:otherwise>
@@ -133,6 +135,7 @@
<xsl:value-of select="$newline"/>
<xsl:text>[endsect]</xsl:text>
<xsl:value-of select="$newline"/>
</xsl:template>
@@ -214,7 +217,11 @@
<xsl:template name="make-id">
<xsl:param name="name"/>
<xsl:param name="static"/>
<xsl:choose>
<xsl:when test="$name='query' and $static='yes'">
<xsl:text>query__static</xsl:text>
</xsl:when>
<xsl:when test="contains($name, 'boost::system::')">
<xsl:call-template name="make-id">
<xsl:with-param name="name"
@@ -263,6 +270,12 @@
select="concat(substring-before($name, '&gt;'), '_gt_', substring-after($name, '&gt;'))"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($name, '&amp;')">
<xsl:call-template name="make-id">
<xsl:with-param name="name"
select="concat(substring-before($name, '&amp;'), '_amp_', substring-after($name, '&amp;'))"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($name, '[')">
<xsl:call-template name="make-id">
<xsl:with-param name="name"
@@ -774,6 +787,9 @@
<xsl:value-of select="$newline"/>
<xsl:text>['Convenience header: ]</xsl:text>
<xsl:choose>
<xsl:when test="contains($file, 'boost/asio/execution')">
<xsl:text>[^boost/asio/execution.hpp]</xsl:text>
</xsl:when>
<xsl:when test="contains($file, 'boost/asio/ssl')">
<xsl:text>[^boost/asio/ssl.hpp]</xsl:text>
</xsl:when>
@@ -909,6 +925,7 @@
<xsl:variable name="id">
<xsl:call-template name="make-id">
<xsl:with-param name="name" select="$name"/>
<xsl:with-param name="static" select="@static"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="doxygen-id">
@@ -958,6 +975,7 @@
<xsl:variable name="id">
<xsl:call-template name="make-id">
<xsl:with-param name="name" select="$name"/>
<xsl:with-param name="static" select="@static"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="doxygen-id">
@@ -1008,6 +1026,7 @@
<xsl:variable name="id">
<xsl:call-template name="make-id">
<xsl:with-param name="name" select="$name"/>
<xsl:with-param name="static" select="@static"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="doxygen-id">
@@ -1219,6 +1238,7 @@
<xsl:variable name="id">
<xsl:call-template name="make-id">
<xsl:with-param name="name" select="$name"/>
<xsl:with-param name="static" select="@static"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="doxygen-id">
@@ -1362,7 +1382,7 @@
</xsl:text>typedef <xsl:value-of select="type"/><xsl:text> </xsl:text><xsl:value-of select="name"/>;<xsl:text>
</xsl:text>
<xsl:if test="count(type/ref) &gt; 0 and not(contains(type, '*'))">
<xsl:if test="count(type/ref) &gt; 0 and not(contains(type, '*')) and not(contains(name, 'polymorphic_query_result_type'))">
<xsl:variable name="class-refid">
<xsl:choose>
<xsl:when test="type='basic_address_iterator&lt; address_v4 &gt;'">
@@ -1407,6 +1427,14 @@
<xsl:template name="variable">
<xsl:if test="contains(name, 'is_applicable_property_v')">
<xsl:text>
template &lt;typename T&gt;</xsl:text>
</xsl:if>
<xsl:if test="contains(name, 'context_as')">
<xsl:text>
template &lt;typename U&gt;</xsl:text>
</xsl:if>
<xsl:text>
</xsl:text><xsl:if test="@static='yes'">static </xsl:if><xsl:value-of
select="type"/><xsl:text> </xsl:text><xsl:value-of select="name"/>
@@ -1492,6 +1520,15 @@
<xsl:when test="declname = 'Args'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'AwaitableExecutor'">
<xsl:value-of select="concat('``[link boost_asio.reference.Executor1 ', declname, ']``')"/>
</xsl:when>
<xsl:when test="declname = 'Bits'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Blocking'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'BufferSequence'">
<xsl:value-of select="declname"/>
</xsl:when>
@@ -1579,6 +1616,12 @@
<xsl:when test="declname = 'OtherHandler'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'OtherSupportableProperties'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'OutstandingWork'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'PasswordCallback'">
<xsl:value-of select="declname"/>
</xsl:when>
@@ -1591,6 +1634,12 @@
<xsl:when test="declname = 'PointerToPodType'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Properties'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Property'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Protocol1'">
<xsl:value-of select="concat('``[link boost_asio.reference.Protocol ', declname, ']``')"/>
</xsl:when>
@@ -1600,6 +1649,9 @@
<xsl:when test="declname = 'RawSocketService1'">
<xsl:value-of select="concat('``[link boost_asio.reference.RawSocketService ', declname, ']``')"/>
</xsl:when>
<xsl:when test="declname = 'Relationship'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Rep'">
<xsl:value-of select="declname"/>
</xsl:when>
@@ -1624,6 +1676,9 @@
<xsl:when test="declname = 'StreamSocketService1'">
<xsl:value-of select="concat('``[link boost_asio.reference.StreamSocketService ', declname, ']``')"/>
</xsl:when>
<xsl:when test="declname = 'SupportableProperties'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'T'">
<xsl:value-of select="declname"/>
</xsl:when>

View File

@@ -7,6 +7,78 @@
[section:Executor1 Executor requirements]
[heading Standard executors]
An executor is defined by the following concept:
template<class E>
concept executor =
invocable<remove_cvref_t<F>&>
&& constructible_from<remove_cvref_t<F>, F>
&& move_constructible<remove_cvref_t<F>>
&& copy_constructible<E>
&& is_nothrow_copy_constructible_v<E>
&& equality_comparable<E>
&&
requires(const E& e, invocable_archetype f)
{
execution::execute(e, static_cast<invocable_archetype&&>(f));
};
Neither an executor's equality comparison nor `swap` operation shall exit via
an exception.
None of an executor type's copy constructor, destructor, equality comparison,
`swap` function, `execute` function, or associated `query` functions shall
introduce data races as a result of concurrent invocations of those functions
from different threads.
For any two (possibly const) values `x1` and `x2` of some executor type `X`,
`x1 == x2` shall return `true` only if `boost::asio::query(x1,p) == boost::asio::query(x2,p)`
for every property `p` where both `boost::asio::query(x1,p)` and `boost::asio::query(x2,p)`
are well-formed and result in a non-void type that is `equality_comparable`
(C++Std [equalitycomparable]). [inline_note The above requirements imply that `x1
== x2` returns `true` if `x1` and `x2` can be interchanged with identical
effects. An executor may conceptually contain additional properties which are
not exposed by a named property type that can be observed via `boost::asio::query`; in
this case, it is up to the concrete executor implementation to decide if these
properties affect equality. Returning `false` does not necessarily imply that
the effects are not identical.]
An executor type's destructor shall not block pending completion of the
submitted function objects. [inline_note The ability to wait for completion of
submitted function objects may be provided by the associated execution
context.]
In addition to the above requirements, types `E` and `F` model `executor_of`
only if they satisfy the requirements of the Table below.
Let:
* `e` denotes a (possibly const) executor object of type `E`,
* `cf` denotes the function object `DECAY_COPY(std::forward<F>(f))`
* `f` denotes a function of type `F&&` invocable as `cf()` and where
`decay_t<F>` models `move_constructible`.
The expression `execution::execute(e, f)`:
* Evaluates `DECAY_COPY(std::forward<F>(f))` on the calling thread to create
`cf` that will be invoked at most once by an execution agent.
* May block pending completion of this invocation. Synchronizes with
[intro.multithread] the invocation of `f`.
* Shall not propagate any exception thrown by the function object or any other
function submitted to the executor.
[inline_note The treatment of exceptions thrown by one-way submitted functions
is implementation-defined. The forward progress guarantee of the associated
execution agent(s) is implementation-defined.]
[heading Networking TS-style executors]
The library describes a standard set of requirements for ['executors]. A type
meeting the `Executor` requirements embodies a set of rules for determining how
submitted function objects are to be executed.

View File

@@ -1536,6 +1536,8 @@ We use an [link boost_asio.reference.ip__udp.resolver ip::udp::resolver] object
The [link boost_asio.reference.ip__basic_resolver.resolve ip::udp::resolver::resolve()] function is guaranteed to return at least one endpoint in the list if it does not fail. This means it is safe to dereference the return value directly.
Since UDP is datagram-oriented, we will not be using a stream socket. Create an [link boost_asio.reference.ip__udp.socket ip::udp::socket] and initiate contact with the remote endpoint.
``''''''`` udp::socket socket(io_context);
@@ -1544,26 +1546,32 @@ The [link boost_asio.reference.ip__basic_resolver.resolve ip::udp::resolver::res
``''''''`` boost::array<char, 1> send_buf = {{ 0 }};
``''''''`` socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint);
``''''''`` boost::array<char, 128> recv_buf;
``''''''`` udp::endpoint sender_endpoint;
Since UDP is datagram-oriented, we will not be using a stream socket. Create an [link boost_asio.reference.ip__udp.socket ip::udp::socket] and initiate contact with the remote endpoint.
Now we need to be ready to accept whatever the server sends back to us. The endpoint on our side that receives the server's response will be initialised by [link boost_asio.reference.basic_datagram_socket.receive_from ip::udp::socket::receive_from()].
``''''''`` boost::array<char, 128> recv_buf;
``''''''`` udp::endpoint sender_endpoint;
``''''''`` size_t len = socket.receive_from(
``''''''`` boost::asio::buffer(recv_buf), sender_endpoint);
``''''''`` std::cout.write(recv_buf.data(), len);
``''''''`` }
Finally, handle any exceptions that may have been thrown.
``''''''`` catch (std::exception& e)
``''''''`` {
``''''''`` std::cerr << e.what() << std::endl;
``''''''`` }
``''''''`` return 0;
``''''''``}
See the [link boost_asio.tutorial.tutdaytime4.src full source listing]
@@ -2378,4 +2386,4 @@ Return to [link boost_asio.tutorial.tutdaytime7 Daytime.7 - A combined TCP/UDP a
[endsect]
[endsect]
[endsect]

View File

@@ -66,6 +66,7 @@
<xsl:value-of select="$newline"/>
<xsl:text>[endsect]</xsl:text>
<xsl:value-of select="$newline"/>
</xsl:template>
@@ -121,6 +122,12 @@
select="concat(substring-before($name, '&gt;'), '_gt_', substring-after($name, '&gt;'))"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($name, '&amp;')">
<xsl:call-template name="make-id">
<xsl:with-param name="name"
select="concat(substring-before($name, '&amp;'), '_amp_', substring-after($name, '&amp;'))"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($name, '+')">
<xsl:call-template name="make-id">
<xsl:with-param name="name"

View File

@@ -29,7 +29,8 @@ io_context_pool::io_context_pool(std::size_t pool_size)
{
io_context_ptr io_context(new boost::asio::io_context);
io_contexts_.push_back(io_context);
work_.push_back(boost::asio::make_work_guard(*io_context));
work_.push_back(boost::asio::require(io_context->get_executor(),
boost::asio::execution::outstanding_work.tracked));
}
}

View File

@@ -39,14 +39,12 @@ public:
private:
typedef boost::shared_ptr<boost::asio::io_context> io_context_ptr;
typedef boost::asio::executor_work_guard<
boost::asio::io_context::executor_type> io_context_work;
/// The pool of io_contexts.
std::vector<io_context_ptr> io_contexts_;
/// The work that keeps the io_contexts running.
std::list<io_context_work> work_;
/// The work-tracking executors that keep the io_contexts running.
std::list<boost::asio::any_io_executor> work_;
/// The next io_context to use for a connection.
std::size_t next_io_context_;

View File

@@ -83,7 +83,7 @@ class connection
public:
typedef boost::shared_ptr<connection> pointer;
static pointer create(const boost::asio::executor& ex)
static pointer create(const boost::asio::any_io_executor& ex)
{
return pointer(new connection(ex));
}
@@ -102,7 +102,7 @@ public:
}
private:
connection(const boost::asio::executor& ex)
connection(const boost::asio::any_io_executor& ex)
: socket_(ex),
session_impl_(socket_),
read_in_progress_(false),

View File

@@ -52,12 +52,6 @@ public:
service_.destroy(impl_);
}
/// Get the io_context associated with the object.
boost::asio::io_context& get_io_context()
{
return service_.get_io_context();
}
/// Set the output file for all logger instances.
void use_file(const std::string& file)
{

View File

@@ -30,7 +30,9 @@ void read_handler(const boost::system::error_code& e,
}
else
{
services::logger logger(s->get_executor().context(), "read_handler");
boost::asio::execution_context& context = boost::asio::query(
s->get_executor(), boost::asio::execution::context);
services::logger logger(context, "read_handler");
std::string msg = "Read error: ";
msg += e.message();
@@ -40,7 +42,9 @@ void read_handler(const boost::system::error_code& e,
void connect_handler(const boost::system::error_code& e, tcp::socket* s)
{
services::logger logger(s->get_executor().context(), "connect_handler");
boost::asio::execution_context& context = boost::asio::query(
s->get_executor(), boost::asio::execution::context);
services::logger logger(context, "connect_handler");
if (!e)
{

View File

@@ -45,7 +45,8 @@ public:
logger_service(boost::asio::execution_context& context)
: boost::asio::execution_context::service(context),
work_io_context_(),
work_(boost::asio::make_work_guard(work_io_context_)),
work_(boost::asio::require(work_io_context_.get_executor(),
boost::asio::execution::outstanding_work.tracked)),
work_thread_(new boost::thread(
boost::bind(&boost::asio::io_context::run, &work_io_context_)))
{
@@ -56,7 +57,7 @@ public:
{
/// Indicate that we have finished with the private io_context. Its
/// io_context::run() function will exit once all other work has completed.
work_.reset();
work_ = boost::asio::any_io_executor();
if (work_thread_)
work_thread_->join();
}
@@ -128,11 +129,10 @@ private:
/// Private io_context used for performing logging operations.
boost::asio::io_context work_io_context_;
/// Work for the private io_context to perform. If we do not give the
/// io_context some work to do then the io_context::run() function will exit
/// immediately.
boost::asio::executor_work_guard<
boost::asio::io_context::executor_type> work_;
/// A work-tracking executor giving work for the private io_context to
/// perform. If we do not give the io_context some work to do then the
/// io_context::run() function will exit immediately.
boost::asio::any_io_executor work_;
/// Thread used for running the work io_context's run loop.
boost::scoped_ptr<boost::thread> work_thread_;

View File

@@ -98,7 +98,8 @@ public:
// use this function to run the io_context until the operation is complete.
return_type get()
{
boost::asio::io_context& io_context = socket_.get_executor().context();
boost::asio::io_context& io_context = boost::asio::query(
socket_.get_executor(), boost::asio::execution::context);
// Restart the io_context, as it may have been left in the "stopped" state
// by a previous operation.

View File

@@ -277,8 +277,6 @@ The boost::asio::ip::udp::resolver::resolve() function is guaranteed to return a
least one endpoint in the list if it does not fail. This means it is safe to
dereference the return value directly.
\until udp::endpoint
Since UDP is datagram-oriented, we will not be using a stream socket. Create an
boost::asio::ip::udp::socket and initiate contact with the remote endpoint.

View File

@@ -1,5 +1,5 @@
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/defer.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/system_executor.hpp>
@@ -10,8 +10,8 @@
#include <typeinfo>
#include <vector>
using boost::asio::any_io_executor;
using boost::asio::defer;
using boost::asio::executor;
using boost::asio::post;
using boost::asio::strand;
using boost::asio::system_executor;
@@ -106,7 +106,7 @@ public:
protected:
// Construct the actor to use the specified executor for all message handlers.
actor(executor e)
actor(any_io_executor e)
: executor_(std::move(e))
{
}
@@ -124,7 +124,7 @@ protected:
template <class Actor, class Message>
void deregister_handler(void (Actor::* mf)(Message, actor_address))
{
const std::type_info& id = typeid(message_handler<Message>);
const std::type_info& id = typeid(Message);
for (auto iter = handlers_.begin(); iter != handlers_.end(); ++iter)
{
if ((*iter)->message_id() == id)
@@ -171,7 +171,7 @@ private:
// All messages associated with a single actor object should be processed
// non-concurrently. We use a strand to ensure non-concurrent execution even
// if the underlying executor may use multiple threads.
strand<executor> executor_;
strand<any_io_executor> executor_;
std::vector<std::shared_ptr<message_handler_base>> handlers_;
};
@@ -221,7 +221,7 @@ using boost::asio::thread_pool;
class member : public actor
{
public:
explicit member(executor e)
explicit member(any_io_executor e)
: actor(std::move(e))
{
register_handler(&member::init_handler);

View File

@@ -1,9 +1,9 @@
#include <boost/asio/post.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/execution.hpp>
#include <boost/asio/static_thread_pool.hpp>
#include <iostream>
using boost::asio::post;
using boost::asio::thread_pool;
using boost::asio::static_thread_pool;
namespace execution = boost::asio::execution;
// Traditional active object pattern.
// Member functions do not block.
@@ -11,37 +11,43 @@ using boost::asio::thread_pool;
class bank_account
{
int balance_ = 0;
mutable thread_pool pool_{1};
mutable static_thread_pool pool_{1};
public:
void deposit(int amount)
{
post(pool_, [=]
{
balance_ += amount;
});
execution::execute(
pool_.executor(),
[this, amount]
{
balance_ += amount;
});
}
void withdraw(int amount)
{
post(pool_, [=]
{
if (balance_ >= amount)
balance_ -= amount;
});
execution::execute(
pool_.executor(),
[this, amount]
{
if (balance_ >= amount)
balance_ -= amount;
});
}
void print_balance() const
{
post(pool_, [=]
{
std::cout << "balance = " << balance_ << "\n";
});
execution::execute(
pool_.executor(),
[this]
{
std::cout << "balance = " << balance_ << "\n";
});
}
~bank_account()
{
pool_.join();
pool_.wait();
}
};

View File

@@ -1,11 +1,9 @@
#include <boost/asio/post.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/use_future.hpp>
#include <boost/asio/execution.hpp>
#include <boost/asio/static_thread_pool.hpp>
#include <iostream>
using boost::asio::post;
using boost::asio::thread_pool;
using boost::asio::use_future;
using boost::asio::static_thread_pool;
namespace execution = boost::asio::execution;
// Traditional active object pattern.
// Member functions block until operation is finished.
@@ -13,35 +11,43 @@ using boost::asio::use_future;
class bank_account
{
int balance_ = 0;
mutable thread_pool pool_{1};
mutable static_thread_pool pool_{1};
public:
void deposit(int amount)
{
post(pool_,
use_future([=]
execution::execute(
boost::asio::require(pool_.executor(),
execution::blocking.always),
[this, amount]
{
balance_ += amount;
})).get();
});
}
void withdraw(int amount)
{
post(pool_,
use_future([=]
execution::execute(
boost::asio::require(pool_.executor(),
execution::blocking.always),
[this, amount]
{
if (balance_ >= amount)
balance_ -= amount;
})).get();
});
}
int balance() const
{
return post(pool_,
use_future([=]
int result = 0;
execution::execute(
boost::asio::require(pool_.executor(),
execution::blocking.always),
[this, &result]
{
return balance_;
})).get();
result = balance_;
});
return result;
}
};

View File

@@ -1,6 +1,6 @@
#include <boost/asio/dispatch.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/execution.hpp>
#include <boost/asio/static_thread_pool.hpp>
#include <algorithm>
#include <condition_variable>
#include <memory>
#include <mutex>
@@ -8,14 +8,13 @@
#include <thread>
#include <numeric>
using boost::asio::dispatch;
using boost::asio::execution_context;
using boost::asio::thread_pool;
using boost::asio::static_thread_pool;
namespace execution = boost::asio::execution;
// A fixed-size thread pool used to implement fork/join semantics. Functions
// are scheduled using a simple FIFO queue. Implementing work stealing, or
// using a queue based on atomic operations, are left as tasks for the reader.
class fork_join_pool : public execution_context
class fork_join_pool
{
public:
// The constructor starts a thread pool with the specified number of threads.
@@ -23,7 +22,7 @@ public:
// Additional threads may temporarily be added to the pool if they join a
// fork_executor.
explicit fork_join_pool(
std::size_t thread_count = std::thread::hardware_concurrency() * 2)
std::size_t thread_count = std::max(std::thread::hardware_concurrency(), 1u) * 2)
: use_count_(1),
threads_(thread_count)
{
@@ -33,7 +32,9 @@ public:
// it is time to shut down, i.e. the use count is zero.
for (thread_count_ = 0; thread_count_ < thread_count; ++thread_count_)
{
dispatch(threads_, [&]
execution::execute(
threads_.executor(),
[this]
{
std::unique_lock<std::mutex> lock(mutex_);
while (use_count_ > 0)
@@ -45,7 +46,7 @@ public:
catch (...)
{
stop_threads();
threads_.join();
threads_.wait();
throw;
}
}
@@ -54,7 +55,7 @@ public:
~fork_join_pool()
{
stop_threads();
threads_.join();
threads_.wait();
}
private:
@@ -118,7 +119,7 @@ private:
// Dispatch a function, executing it immediately if the queue is already
// loaded. Otherwise adds the function to the queue and wakes a thread.
void do_dispatch(std::shared_ptr<function_base> p,
void do_execute(std::shared_ptr<function_base> p,
const std::shared_ptr<std::size_t>& work_count)
{
std::unique_lock<std::mutex> lock(mutex_);
@@ -136,16 +137,6 @@ private:
}
}
// Add a function to the queue and wake a thread.
void do_post(std::shared_ptr<function_base> p,
const std::shared_ptr<std::size_t>& work_count)
{
std::lock_guard<std::mutex> lock(mutex_);
queue_.push(p);
do_work_started(work_count);
condition_.notify_one();
}
// Ask all threads to shut down.
void stop_threads()
{
@@ -159,7 +150,7 @@ private:
std::queue<std::shared_ptr<function_base>> queue_;
std::size_t use_count_;
std::size_t thread_count_;
thread_pool threads_;
static_thread_pool threads_;
};
// A class that satisfies the Executor requirements. Every function or piece of
@@ -173,45 +164,16 @@ public:
{
}
fork_join_pool& context() const noexcept
fork_join_pool& query(execution::context_t) const noexcept
{
return context_;
}
void on_work_started() const noexcept
template <class Func>
void execute(Func f) const
{
std::lock_guard<std::mutex> lock(context_.mutex_);
context_.do_work_started(work_count_);
}
void on_work_finished() const noexcept
{
std::lock_guard<std::mutex> lock(context_.mutex_);
context_.do_work_finished(work_count_);
}
template <class Func, class Alloc>
void dispatch(Func&& f, const Alloc& a) const
{
auto p(std::allocate_shared<function<Func>>(
typename std::allocator_traits<Alloc>::template rebind_alloc<char>(a),
std::move(f), work_count_));
context_.do_dispatch(p, work_count_);
}
template <class Func, class Alloc>
void post(Func f, const Alloc& a) const
{
auto p(std::allocate_shared<function<Func>>(
typename std::allocator_traits<Alloc>::template rebind_alloc<char>(a),
std::move(f), work_count_));
context_.do_post(p, work_count_);
}
template <class Func, class Alloc>
void defer(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
auto p(std::make_shared<function<Func>>(std::move(f), work_count_));
context_.do_execute(p, work_count_);
}
friend bool operator==(const fork_executor& a,
@@ -290,8 +252,8 @@ void fork_join_sort(Iterator begin, Iterator end)
{
fork_executor fork(pool);
join_guard join(fork);
dispatch(fork, [=]{ fork_join_sort(begin, begin + n / 2); });
dispatch(fork, [=]{ fork_join_sort(begin + n / 2, end); });
execution::execute(fork, [=]{ fork_join_sort(begin, begin + n / 2); });
execution::execute(fork, [=]{ fork_join_sort(begin + n / 2, end); });
}
std::inplace_merge(begin, begin + n / 2, end);
}

View File

@@ -20,9 +20,10 @@ using boost::asio::post;
using boost::asio::system_executor;
using boost::asio::use_future;
using boost::asio::use_service;
namespace execution = boost::asio::execution;
// An executor that launches a new thread for each function submitted to it.
// This class satisfies the Executor requirements.
// This class satisfies the executor requirements.
class thread_executor
{
private:
@@ -55,40 +56,28 @@ private:
};
public:
execution_context& context() const noexcept
execution_context& query(execution::context_t) const
{
return system_executor().context();
return boost::asio::query(system_executor(), execution::context);
}
void on_work_started() const noexcept
execution::blocking_t query(execution::blocking_t) const
{
// This executor doesn't count work.
return execution::blocking.never;
}
void on_work_finished() const noexcept
thread_executor require(execution::blocking_t::never_t) const
{
// This executor doesn't count work.
return *this;
}
template <class Func, class Alloc>
void dispatch(Func&& f, const Alloc& a) const
template <class Func>
void execute(Func f) const
{
post(std::forward<Func>(f), a);
}
template <class Func, class Alloc>
void post(Func f, const Alloc&) const
{
thread_bag& bag = use_service<thread_bag>(context());
thread_bag& bag = use_service<thread_bag>(query(execution::context));
bag.add_thread(std::thread(std::move(f)));
}
template <class Func, class Alloc>
void defer(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
}
friend bool operator==(const thread_executor&,
const thread_executor&) noexcept
{
@@ -292,7 +281,7 @@ void writer(queue_back<std::string> in)
int main()
{
thread_pool pool;
thread_pool pool(1);
auto f = pipeline(reader, filter, bind_executor(pool, upper), writer);
f.wait();

View File

@@ -8,6 +8,7 @@
using boost::asio::dispatch;
using boost::asio::execution_context;
namespace execution = boost::asio::execution;
class priority_scheduler : public execution_context
{
@@ -21,47 +22,20 @@ public:
{
}
priority_scheduler& context() const noexcept
priority_scheduler& query(execution::context_t) const noexcept
{
return context_;
}
void on_work_started() const noexcept
template <class Func>
void execute(Func f) const
{
// This executor doesn't count work. Instead, the scheduler simply runs
// until explicitly stopped.
}
void on_work_finished() const noexcept
{
// This executor doesn't count work. Instead, the scheduler simply runs
// until explicitly stopped.
}
template <class Func, class Alloc>
void dispatch(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
}
template <class Func, class Alloc>
void post(Func f, const Alloc& a) const
{
auto p(std::allocate_shared<item<Func>>(
typename std::allocator_traits<
Alloc>::template rebind_alloc<char>(a),
priority_, std::move(f)));
auto p(std::make_shared<item<Func>>(priority_, std::move(f)));
std::lock_guard<std::mutex> lock(context_.mutex_);
context_.queue_.push(p);
context_.condition_.notify_one();
}
template <class Func, class Alloc>
void defer(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
}
friend bool operator==(const executor_type& a,
const executor_type& b) noexcept
{

View File

@@ -77,7 +77,8 @@ int main(int argc, char* argv[])
// We run the io_context off in its own thread so that it operates
// completely asynchronously with respect to the rest of the program.
boost::asio::io_context io_context;
auto work = boost::asio::make_work_guard(io_context);
auto work = boost::asio::require(io_context.get_executor(),
boost::asio::execution::outstanding_work.tracked);
std::thread thread([&io_context](){ io_context.run(); });
get_daytime(io_context, argv[1]);

View File

@@ -16,6 +16,13 @@
using boost::asio::ip::tcp;
// Define a helper macro to invoke BOOST_ASIO_HANDLER_LOCATION with the current
// file name, line number, and function name. For the function name, you might
// also consider using __PRETTY_FUNCTION__, BOOST_CURRENT_FUNCTION, or a hand-
// crafted name. For C++20 or later, you may also use std::source_location.
#define HANDLER_LOCATION \
BOOST_ASIO_HANDLER_LOCATION((__FILE__, __LINE__, __func__))
class session
: public std::enable_shared_from_this<session>
{
@@ -27,16 +34,22 @@ public:
void start()
{
HANDLER_LOCATION;
do_read();
}
private:
void do_read()
{
HANDLER_LOCATION;
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length)
{
HANDLER_LOCATION;
if (!ec)
{
do_write(length);
@@ -46,10 +59,14 @@ private:
void do_write(std::size_t length)
{
HANDLER_LOCATION;
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
HANDLER_LOCATION;
if (!ec)
{
do_read();
@@ -74,9 +91,13 @@ public:
private:
void do_accept()
{
HANDLER_LOCATION;
acceptor_.async_accept(
[this](boost::system::error_code ec, tcp::socket socket)
{
HANDLER_LOCATION;
if (!ec)
{
std::make_shared<session>(std::move(socket))->start();

View File

@@ -24,6 +24,9 @@
# define BOOST_ASIO_HANDLER_TRACKING_INIT \
::custom_tracking::init()
# define BOOST_ASIO_HANDLER_LOCATION(args) \
::custom_tracking::location args
# define BOOST_ASIO_HANDLER_CREATION(args) \
::custom_tracking::creation args
@@ -71,6 +74,13 @@ struct custom_tracking
{
}
// Record a source location.
static void location(const char* file_name,
int line, const char* function_name)
{
std::printf("At location %s:%d in %s\n", file_name, line, function_name);
}
// Record the creation of a tracked handler.
static void creation(boost::asio::execution_context& /*ctx*/,
tracked_handler& h, const char* object_type, void* /*object*/,

View File

@@ -97,7 +97,9 @@ struct async_write_message_initiation
// As our composed operation performs multiple underlying I/O operations,
// we should maintain a work object against the I/O executor. This tells
// the I/O executor that there is still more work to come in the future.
boost::asio::executor_work_guard<tcp::socket::executor_type> io_work_;
typename std::decay<decltype(boost::asio::prefer(
std::declval<tcp::socket::executor_type>(),
boost::asio::execution::outstanding_work.tracked))>::type io_work_;
// The user-supplied completion handler, called once only on completion
// of the entire composed operation.
@@ -134,9 +136,6 @@ struct async_write_message_initiation
// This point is reached only on completion of the entire composed
// operation.
// We no longer have any future work coming for the I/O executor.
io_work_.reset();
// Deallocate the encoded message before calling the user-supplied
// completion handler.
encoded_message_.reset();
@@ -187,7 +186,8 @@ struct async_write_message_initiation
socket, std::move(encoded_message),
repeat_count, std::move(delay_timer),
intermediate_completion_handler::starting,
boost::asio::make_work_guard(socket.get_executor()),
boost::asio::prefer(socket.get_executor(),
boost::asio::execution::outstanding_work.tracked),
std::forward<CompletionHandler>(completion_handler)});
}
};

View File

@@ -44,7 +44,8 @@ int main(int argc, char* argv[])
// Get an endpoint for the Boost website. This will be passed to the SOCKS
// 4 server. Explicitly specify IPv4 since SOCKS 4 does not support IPv6.
auto http_endpoint = *resolver.resolve(tcp::v4(), "www.boost.org", "http");
auto http_endpoint =
*resolver.resolve(tcp::v4(), "www.boost.org", "http").begin();
// Send the request to the SOCKS 4 server.
socks4::request socks_request(

View File

@@ -19,8 +19,8 @@ using boost::asio::ip::tcp;
class session : public std::enable_shared_from_this<session>
{
public:
session(tcp::socket socket, boost::asio::ssl::context& context)
: socket_(std::move(socket), context)
session(boost::asio::ssl::stream<tcp::socket> socket)
: socket_(std::move(socket))
{
}
@@ -106,7 +106,9 @@ private:
{
if (!error)
{
std::make_shared<session>(std::move(socket), context_)->start();
std::make_shared<session>(
boost::asio::ssl::stream<tcp::socket>(
std::move(socket), context_))->start();
}
do_accept();

View File

@@ -98,7 +98,8 @@ public:
// use this function to run the io_context until the operation is complete.
return_type get()
{
boost::asio::io_context& io_context = socket_.get_executor().context();
boost::asio::io_context& io_context = boost::asio::query(
socket_.get_executor(), boost::asio::execution::context);
// Restart the io_context, as it may have been left in the "stopped" state
// by a previous operation.

View File

@@ -1,4 +1,8 @@
#include <boost/asio/ts/executor.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/defer.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/system_executor.hpp>
#include <condition_variable>
#include <deque>
#include <memory>
@@ -6,8 +10,8 @@
#include <typeinfo>
#include <vector>
using boost::asio::any_io_executor;
using boost::asio::defer;
using boost::asio::executor;
using boost::asio::post;
using boost::asio::strand;
using boost::asio::system_executor;
@@ -94,7 +98,7 @@ public:
{
// Execute the message handler in the context of the target's executor.
post(to->executor_,
[=, msg=std::move(msg)]
[=, msg=std::move(msg)]() mutable
{
to->call_handler(std::move(msg), from);
});
@@ -102,7 +106,7 @@ public:
protected:
// Construct the actor to use the specified executor for all message handlers.
actor(executor e)
actor(any_io_executor e)
: executor_(std::move(e))
{
}
@@ -166,7 +170,7 @@ private:
// All messages associated with a single actor object should be processed
// non-concurrently. We use a strand to ensure non-concurrent execution even
// if the underlying executor may use multiple threads.
strand<executor> executor_;
strand<any_io_executor> executor_;
std::vector<std::shared_ptr<message_handler_base>> handlers_;
};
@@ -216,7 +220,7 @@ using boost::asio::thread_pool;
class member : public actor
{
public:
explicit member(executor e)
explicit member(any_io_executor e)
: actor(std::move(e))
{
register_handler(&member::init_handler);

View File

@@ -1,29 +1,35 @@
#include <boost/asio/ts/executor.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/execution.hpp>
#include <boost/asio/static_thread_pool.hpp>
#include <iostream>
#include <string>
using boost::asio::bind_executor;
using boost::asio::dispatch;
using boost::asio::make_work_guard;
using boost::asio::post;
using boost::asio::thread_pool;
using boost::asio::get_associated_executor;
using boost::asio::static_thread_pool;
namespace execution = boost::asio::execution;
// A function to asynchronously read a single line from an input stream.
template <class Handler>
void async_getline(std::istream& is, Handler handler)
template <class IoExecutor, class Handler>
void async_getline(IoExecutor io_ex, std::istream& is, Handler handler)
{
// Create executor_work for the handler's associated executor.
auto work = make_work_guard(handler);
// Track work for the handler's associated executor.
auto work_ex = boost::asio::prefer(
get_associated_executor(handler, io_ex),
execution::outstanding_work.tracked);
// Post a function object to do the work asynchronously.
post([&is, work, handler=std::move(handler)]() mutable
execution::execute(
boost::asio::require(io_ex, execution::blocking.never),
[&is, work_ex, handler=std::move(handler)]() mutable
{
std::string line;
std::getline(is, line);
// Pass the result to the handler, via the associated executor.
dispatch(work.get_executor(),
execution::execute(
boost::asio::prefer(work_ex, execution::blocking.possibly),
[line=std::move(line), handler=std::move(handler)]() mutable
{
handler(std::move(line));
@@ -33,15 +39,18 @@ void async_getline(std::istream& is, Handler handler)
int main()
{
thread_pool pool;
static_thread_pool io_pool(1);
static_thread_pool completion_pool(1);
std::cout << "Enter a line: ";
async_getline(std::cin,
bind_executor(pool, [](std::string line)
async_getline(io_pool.executor(), std::cin,
bind_executor(completion_pool.executor(),
[](std::string line)
{
std::cout << "Line: " << line << "\n";
}));
pool.join();
io_pool.wait();
completion_pool.wait();
}

View File

@@ -1,30 +1,35 @@
#include <boost/asio/ts/executor.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/execution.hpp>
#include <boost/asio/static_thread_pool.hpp>
#include <iostream>
#include <string>
using boost::asio::bind_executor;
using boost::asio::dispatch;
using boost::asio::get_associated_executor;
using boost::asio::make_work_guard;
using boost::asio::post;
using boost::asio::thread_pool;
using boost::asio::static_thread_pool;
namespace execution = boost::asio::execution;
// A function to asynchronously read a single line from an input stream.
template <class Handler>
void async_getline(std::istream& is, Handler handler)
template <class IoExecutor, class Handler>
void async_getline(IoExecutor io_ex, std::istream& is, Handler handler)
{
// Create executor_work for the handler's associated executor.
auto work = make_work_guard(handler);
// Track work for the handler's associated executor.
auto work_ex = boost::asio::prefer(
get_associated_executor(handler, io_ex),
execution::outstanding_work.tracked);
// Post a function object to do the work asynchronously.
post([&is, work, handler=std::move(handler)]() mutable
execution::execute(
boost::asio::require(io_ex, execution::blocking.never),
[&is, work_ex, handler=std::move(handler)]() mutable
{
std::string line;
std::getline(is, line);
// Pass the result to the handler, via the associated executor.
dispatch(work.get_executor(),
execution::execute(
boost::asio::prefer(work_ex, execution::blocking.possibly),
[line=std::move(line), handler=std::move(handler)]() mutable
{
handler(std::move(line));
@@ -33,36 +38,44 @@ void async_getline(std::istream& is, Handler handler)
}
// A function to asynchronously read multiple lines from an input stream.
template <class Handler>
void async_getlines(std::istream& is, std::string init, Handler handler)
template <class IoExecutor, class Handler>
void async_getlines(IoExecutor io_ex, std::istream& is, std::string init, Handler handler)
{
// Get the final handler's associated executor.
auto ex = get_associated_executor(handler);
// Track work for the I/O executor.
auto io_work_ex = boost::asio::prefer(io_ex,
execution::outstanding_work.tracked);
// Track work for the handler's associated executor.
auto handler_work_ex = boost::asio::prefer(
get_associated_executor(handler, io_ex),
execution::outstanding_work.tracked);
// Use the associated executor for each operation in the composition.
async_getline(is,
bind_executor(ex,
[&is, lines=std::move(init), handler=std::move(handler)]
async_getline(io_work_ex, is,
bind_executor(handler_work_ex,
[io_work_ex, &is, lines=std::move(init), handler=std::move(handler)]
(std::string line) mutable
{
if (line.empty())
handler(lines);
else
async_getlines(is, lines + line + "\n", std::move(handler));
async_getlines(io_work_ex, is, lines + line + "\n", std::move(handler));
}));
}
int main()
{
thread_pool pool;
static_thread_pool io_pool(1);
static_thread_pool completion_pool(1);
std::cout << "Enter text, terminating with a blank line:\n";
async_getlines(std::cin, "",
bind_executor(pool, [](std::string lines)
async_getlines(io_pool.executor(), std::cin, "",
bind_executor(completion_pool.executor(), [](std::string lines)
{
std::cout << "Lines:\n" << lines << "\n";
}));
pool.join();
io_pool.wait();
completion_pool.wait();
}

View File

@@ -1,9 +1,9 @@
#include <boost/asio/ts/executor.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/execution.hpp>
#include <boost/asio/static_thread_pool.hpp>
#include <iostream>
using boost::asio::post;
using boost::asio::thread_pool;
using boost::asio::static_thread_pool;
namespace execution = boost::asio::execution;
// Traditional active object pattern.
// Member functions do not block.
@@ -11,37 +11,43 @@ using boost::asio::thread_pool;
class bank_account
{
int balance_ = 0;
mutable thread_pool pool_{1};
mutable static_thread_pool pool_{1};
public:
void deposit(int amount)
{
post(pool_, [=]
{
balance_ += amount;
});
execution::execute(
pool_.executor(),
[this, amount]
{
balance_ += amount;
});
}
void withdraw(int amount)
{
post(pool_, [=]
{
if (balance_ >= amount)
balance_ -= amount;
});
execution::execute(
pool_.executor(),
[this, amount]
{
if (balance_ >= amount)
balance_ -= amount;
});
}
void print_balance() const
{
post(pool_, [=]
{
std::cout << "balance = " << balance_ << "\n";
});
execution::execute(
pool_.executor(),
[this]
{
std::cout << "balance = " << balance_ << "\n";
});
}
~bank_account()
{
pool_.join();
pool_.wait();
}
};

View File

@@ -1,10 +1,9 @@
#include <boost/asio/ts/executor.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/execution.hpp>
#include <boost/asio/static_thread_pool.hpp>
#include <iostream>
using boost::asio::post;
using boost::asio::thread_pool;
using boost::asio::use_future;
using boost::asio::static_thread_pool;
namespace execution = boost::asio::execution;
// Traditional active object pattern.
// Member functions block until operation is finished.
@@ -12,35 +11,43 @@ using boost::asio::use_future;
class bank_account
{
int balance_ = 0;
mutable thread_pool pool_{1};
mutable static_thread_pool pool_{1};
public:
void deposit(int amount)
{
post(pool_,
use_future([=]
execution::execute(
boost::asio::require(pool_.executor(),
execution::blocking.always),
[this, amount]
{
balance_ += amount;
})).get();
});
}
void withdraw(int amount)
{
post(pool_,
use_future([=]
execution::execute(
boost::asio::require(pool_.executor(),
execution::blocking.always),
[this, amount]
{
if (balance_ >= amount)
balance_ -= amount;
})).get();
});
}
int balance() const
{
return post(pool_,
use_future([=]
int result = 0;
execution::execute(
boost::asio::require(pool_.executor(),
execution::blocking.always),
[this, &result]
{
return balance_;
})).get();
result = balance_;
});
return result;
}
};

View File

@@ -1,5 +1,6 @@
#include <boost/asio/ts/executor.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/execution.hpp>
#include <boost/asio/static_thread_pool.hpp>
#include <algorithm>
#include <condition_variable>
#include <memory>
#include <mutex>
@@ -7,14 +8,13 @@
#include <thread>
#include <numeric>
using boost::asio::dispatch;
using boost::asio::execution_context;
using boost::asio::thread_pool;
using boost::asio::static_thread_pool;
namespace execution = boost::asio::execution;
// A fixed-size thread pool used to implement fork/join semantics. Functions
// are scheduled using a simple FIFO queue. Implementing work stealing, or
// using a queue based on atomic operations, are left as tasks for the reader.
class fork_join_pool : public execution_context
class fork_join_pool
{
public:
// The constructor starts a thread pool with the specified number of threads.
@@ -22,7 +22,7 @@ public:
// Additional threads may temporarily be added to the pool if they join a
// fork_executor.
explicit fork_join_pool(
std::size_t thread_count = std::thread::hardware_concurrency() * 2)
std::size_t thread_count = std::max(std::thread::hardware_concurrency(), 1u) * 2)
: use_count_(1),
threads_(thread_count)
{
@@ -32,7 +32,9 @@ public:
// it is time to shut down, i.e. the use count is zero.
for (thread_count_ = 0; thread_count_ < thread_count; ++thread_count_)
{
dispatch(threads_, [&]
execution::execute(
threads_.executor(),
[this]
{
std::unique_lock<std::mutex> lock(mutex_);
while (use_count_ > 0)
@@ -44,7 +46,7 @@ public:
catch (...)
{
stop_threads();
threads_.join();
threads_.wait();
throw;
}
}
@@ -53,7 +55,7 @@ public:
~fork_join_pool()
{
stop_threads();
threads_.join();
threads_.wait();
}
private:
@@ -117,7 +119,7 @@ private:
// Dispatch a function, executing it immediately if the queue is already
// loaded. Otherwise adds the function to the queue and wakes a thread.
void do_dispatch(std::shared_ptr<function_base> p,
void do_execute(std::shared_ptr<function_base> p,
const std::shared_ptr<std::size_t>& work_count)
{
std::unique_lock<std::mutex> lock(mutex_);
@@ -135,16 +137,6 @@ private:
}
}
// Add a function to the queue and wake a thread.
void do_post(std::shared_ptr<function_base> p,
const std::shared_ptr<std::size_t>& work_count)
{
std::lock_guard<std::mutex> lock(mutex_);
queue_.push(p);
do_work_started(work_count);
condition_.notify_one();
}
// Ask all threads to shut down.
void stop_threads()
{
@@ -158,7 +150,7 @@ private:
std::queue<std::shared_ptr<function_base>> queue_;
std::size_t use_count_;
std::size_t thread_count_;
thread_pool threads_;
static_thread_pool threads_;
};
// A class that satisfies the Executor requirements. Every function or piece of
@@ -172,45 +164,16 @@ public:
{
}
fork_join_pool& context() const noexcept
fork_join_pool& query(execution::context_t) const noexcept
{
return context_;
}
void on_work_started() const noexcept
template <class Func>
void execute(Func f) const
{
std::lock_guard<std::mutex> lock(context_.mutex_);
context_.do_work_started(work_count_);
}
void on_work_finished() const noexcept
{
std::lock_guard<std::mutex> lock(context_.mutex_);
context_.do_work_finished(work_count_);
}
template <class Func, class Alloc>
void dispatch(Func&& f, const Alloc& a) const
{
auto p(std::allocate_shared<function<Func>>(
typename std::allocator_traits<Alloc>::template rebind_alloc<char>(a),
std::move(f), work_count_));
context_.do_dispatch(p, work_count_);
}
template <class Func, class Alloc>
void post(Func f, const Alloc& a) const
{
auto p(std::allocate_shared<function<Func>>(
typename std::allocator_traits<Alloc>::template rebind_alloc<char>(a),
std::move(f), work_count_));
context_.do_post(p, work_count_);
}
template <class Func, class Alloc>
void defer(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
auto p(std::make_shared<function<Func>>(std::move(f), work_count_));
context_.do_execute(p, work_count_);
}
friend bool operator==(const fork_executor& a,
@@ -289,8 +252,8 @@ void fork_join_sort(Iterator begin, Iterator end)
{
fork_executor fork(pool);
join_guard join(fork);
dispatch(fork, [=]{ fork_join_sort(begin, begin + n / 2); });
dispatch(fork, [=]{ fork_join_sort(begin + n / 2, end); });
execution::execute(fork, [=]{ fork_join_sort(begin, begin + n / 2); });
execution::execute(fork, [=]{ fork_join_sort(begin + n / 2, end); });
}
std::inplace_merge(begin, begin + n / 2, end);
}

View File

@@ -1,4 +1,6 @@
#include <boost/asio/ts/executor.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/execution.hpp>
#include <condition_variable>
#include <future>
#include <memory>
@@ -8,29 +10,19 @@
#include <vector>
#include <cctype>
using boost::asio::execution_context;
using boost::asio::executor_binder;
using boost::asio::get_associated_executor;
using boost::asio::post;
using boost::asio::system_executor;
using boost::asio::use_future;
using boost::asio::use_service;
namespace execution = boost::asio::execution;
// An executor that launches a new thread for each function submitted to it.
// This class satisfies the Executor requirements.
// This class satisfies the executor requirements.
class thread_executor
{
private:
// Service to track all threads started through a thread_executor.
class thread_bag : public execution_context::service
// Singleton execution context that manages threads launched by the new_thread_executor.
class thread_bag
{
public:
typedef thread_bag key_type;
explicit thread_bag(execution_context& ctx)
: execution_context::service(ctx)
{
}
friend class thread_executor;
void add_thread(std::thread&& t)
{
@@ -38,8 +30,9 @@ private:
threads_.push_back(std::move(t));
}
private:
virtual void shutdown()
thread_bag() = default;
~thread_bag()
{
for (auto& t : threads_)
t.join();
@@ -50,40 +43,24 @@ private:
};
public:
execution_context& context() const noexcept
static thread_bag& query(execution::context_t)
{
return system_executor().context();
static thread_bag threads;
return threads;
}
void on_work_started() const noexcept
static constexpr auto query(execution::blocking_t)
{
// This executor doesn't count work.
return execution::blocking.never;
}
void on_work_finished() const noexcept
template <class Func>
void execute(Func f) const
{
// This executor doesn't count work.
}
template <class Func, class Alloc>
void dispatch(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
}
template <class Func, class Alloc>
void post(Func f, const Alloc&) const
{
thread_bag& bag = use_service<thread_bag>(context());
thread_bag& bag = query(execution::context);
bag.add_thread(std::thread(std::move(f)));
}
template <class Func, class Alloc>
void defer(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
}
friend bool operator==(const thread_executor&,
const thread_executor&) noexcept
{
@@ -186,7 +163,16 @@ std::future<void> pipeline(queue_back<T> in, F f)
// Run the function, and as we're the last stage return a future so that the
// caller can wait for the pipeline to finish.
return post(ex, use_future([in, f = std::move(f)]() mutable { f(in); }));
std::packaged_task<void()> task(
[in, f = std::move(f)]() mutable
{
f(in);
});
std::future<void> fut = task.get_future();
execution::execute(
boost::asio::require(ex, execution::blocking.never),
std::move(task));
return fut;
}
// Launch an intermediate stage in a pipeline.
@@ -205,7 +191,9 @@ std::future<void> pipeline(queue_back<T> in, F f, Tail... t)
auto ex = get_associated_executor(f, thread_executor());
// Run the function.
post(ex, [in, out, f = std::move(f)]() mutable
execution::execute(
boost::asio::require(ex, execution::blocking.never),
[in, out, f = std::move(f)]() mutable
{
f(in, out);
out.stop();
@@ -231,7 +219,9 @@ std::future<void> pipeline(F f, Tail... t)
auto ex = get_associated_executor(f, thread_executor());
// Run the function.
post(ex, [out, f = std::move(f)]() mutable
execution::execute(
boost::asio::require(ex, execution::blocking.never),
[out, f = std::move(f)]() mutable
{
f(out);
out.stop();
@@ -243,12 +233,12 @@ std::future<void> pipeline(F f, Tail... t)
//------------------------------------------------------------------------------
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/static_thread_pool.hpp>
#include <iostream>
#include <string>
using boost::asio::bind_executor;
using boost::asio::thread_pool;
using boost::asio::static_thread_pool;
void reader(queue_front<std::string> out)
{
@@ -287,8 +277,8 @@ void writer(queue_back<std::string> in)
int main()
{
thread_pool pool;
static_thread_pool pool(1);
auto f = pipeline(reader, filter, bind_executor(pool, upper), writer);
auto f = pipeline(reader, filter, bind_executor(pool.executor(), upper), writer);
f.wait();
}

View File

@@ -1,66 +1,74 @@
#include <boost/asio/ts/executor.hpp>
#include <boost/asio/execution.hpp>
#include <condition_variable>
#include <iostream>
#include <memory>
#include <mutex>
#include <queue>
using boost::asio::dispatch;
using boost::asio::execution_context;
namespace execution = boost::asio::execution;
class priority_scheduler : public execution_context
namespace custom_props {
struct priority
{
template <typename T>
static constexpr bool is_applicable_property_v =
execution::is_executor<T>::value;
static constexpr bool is_requirable = true;
static constexpr bool is_preferable = true;
using polymorphic_query_result_type = int;
int value() const { return value_; }
int value_ = 1;
};
constexpr priority low_priority{0};
constexpr priority normal_priority{1};
constexpr priority high_priority{2};
} // namespace custom_props
class priority_scheduler
{
public:
// A class that satisfies the Executor requirements.
class executor_type
{
public:
executor_type(priority_scheduler& ctx, int pri) noexcept
: context_(ctx), priority_(pri)
executor_type(priority_scheduler& ctx) noexcept
: context_(ctx), priority_(custom_props::normal_priority.value())
{
}
priority_scheduler& context() const noexcept
priority_scheduler& query(execution::context_t) const noexcept
{
return context_;
}
void on_work_started() const noexcept
int query(custom_props::priority) const noexcept
{
// This executor doesn't count work. Instead, the scheduler simply runs
// until explicitly stopped.
return priority_;
}
void on_work_finished() const noexcept
executor_type require(custom_props::priority pri) const
{
// This executor doesn't count work. Instead, the scheduler simply runs
// until explicitly stopped.
executor_type new_ex(*this);
new_ex.priority_ = pri.value();
return new_ex;
}
template <class Func, class Alloc>
void dispatch(Func&& f, const Alloc& a) const
template <class Func>
void execute(Func f) const
{
post(std::forward<Func>(f), a);
}
template <class Func, class Alloc>
void post(Func f, const Alloc& a) const
{
auto p(std::allocate_shared<item<Func>>(
typename std::allocator_traits<
Alloc>::template rebind_alloc<char>(a),
priority_, std::move(f)));
auto p(std::make_shared<item<Func>>(priority_, std::move(f)));
std::lock_guard<std::mutex> lock(context_.mutex_);
context_.queue_.push(p);
context_.condition_.notify_one();
}
template <class Func, class Alloc>
void defer(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
}
friend bool operator==(const executor_type& a,
const executor_type& b) noexcept
{
@@ -78,15 +86,9 @@ public:
int priority_;
};
~priority_scheduler() noexcept
executor_type executor() noexcept
{
shutdown();
destroy();
}
executor_type get_executor(int pri = 0) noexcept
{
return executor_type(*const_cast<priority_scheduler*>(this), pri);
return executor_type(*const_cast<priority_scheduler*>(this));
}
void run()
@@ -158,16 +160,22 @@ private:
int main()
{
priority_scheduler sched;
auto low = sched.get_executor(0);
auto med = sched.get_executor(1);
auto high = sched.get_executor(2);
dispatch(low, []{ std::cout << "1\n"; });
dispatch(low, []{ std::cout << "11\n"; });
dispatch(med, []{ std::cout << "2\n"; });
dispatch(med, []{ std::cout << "22\n"; });
dispatch(high, []{ std::cout << "3\n"; });
dispatch(high, []{ std::cout << "33\n"; });
dispatch(high, []{ std::cout << "333\n"; });
dispatch(sched.get_executor(-1), [&]{ sched.stop(); });
auto ex = sched.executor();
auto prefer_low = boost::asio::prefer(ex, custom_props::low_priority);
auto low = boost::asio::require(ex, custom_props::low_priority);
auto med = boost::asio::require(ex, custom_props::normal_priority);
auto high = boost::asio::require(ex, custom_props::high_priority);
execution::any_executor<custom_props::priority> poly_high(high);
execution::execute(prefer_low, []{ std::cout << "1\n"; });
execution::execute(low, []{ std::cout << "11\n"; });
execution::execute(low, []{ std::cout << "111\n"; });
execution::execute(med, []{ std::cout << "2\n"; });
execution::execute(med, []{ std::cout << "22\n"; });
execution::execute(high, []{ std::cout << "3\n"; });
execution::execute(high, []{ std::cout << "33\n"; });
execution::execute(high, []{ std::cout << "333\n"; });
execution::execute(poly_high, []{ std::cout << "3333\n"; });
execution::execute(boost::asio::require(ex, custom_props::priority{-1}), [&]{ sched.stop(); });
sched.run();
std::cout << "polymorphic query result = " << boost::asio::query(poly_high, custom_props::priority{}) << "\n";
}

View File

@@ -108,7 +108,9 @@ auto async_write_messages(tcp::socket& socket,
// As our composed operation performs multiple underlying I/O operations,
// we should maintain a work object against the I/O executor. This tells
// the I/O executor that there is still more work to come in the future.
boost::asio::executor_work_guard<tcp::socket::executor_type> io_work_;
typename std::decay<decltype(boost::asio::prefer(
std::declval<tcp::socket::executor_type>(),
boost::asio::execution::outstanding_work.tracked))>::type io_work_;
// The user-supplied completion handler, called once only on completion
// of the entire composed operation.
@@ -145,9 +147,6 @@ auto async_write_messages(tcp::socket& socket,
// This point is reached only on completion of the entire composed
// operation.
// We no longer have any future work coming for the I/O executor.
io_work_.reset();
// Deallocate the encoded message before calling the user-supplied
// completion handler.
encoded_message_.reset();
@@ -198,7 +197,8 @@ auto async_write_messages(tcp::socket& socket,
socket, std::move(encoded_message),
repeat_count, std::move(delay_timer),
intermediate_completion_handler::starting,
boost::asio::make_work_guard(socket.get_executor()),
boost::asio::prefer(socket.get_executor(),
boost::asio::execution::outstanding_work.tracked),
std::forward<decltype(completion_handler)>(completion_handler)});
};

View File

@@ -204,10 +204,7 @@ int main(int argc, char* argv[])
{
unsigned short port = std::atoi(argv[i]);
co_spawn(io_context,
[&io_context, port]
{
return listener(tcp::acceptor(io_context, {tcp::v4(), port}));
},
listener(tcp::acceptor(io_context, {tcp::v4(), port})),
detached);
}

View File

@@ -23,6 +23,11 @@ using boost::asio::detached;
using boost::asio::use_awaitable;
namespace this_coro = boost::asio::this_coro;
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
# define use_awaitable \
boost::asio::use_awaitable_t(__FILE__, __LINE__, __PRETTY_FUNCTION__)
#endif
awaitable<void> echo(tcp::socket socket)
{
try
@@ -47,12 +52,7 @@ awaitable<void> listener()
for (;;)
{
tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
co_spawn(executor,
[socket = std::move(socket)]() mutable
{
return echo(std::move(socket));
},
detached);
co_spawn(executor, echo(std::move(socket)), detached);
}
}
@@ -65,7 +65,7 @@ int main()
boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);
signals.async_wait([&](auto, auto){ io_context.stop(); });
co_spawn(io_context, listener, detached);
co_spawn(io_context, listener(), detached);
io_context.run();
}

View File

@@ -49,12 +49,7 @@ awaitable<void> listener()
for (;;)
{
auto socket = co_await acceptor.async_accept();
co_spawn(executor,
[socket = std::move(socket)]() mutable
{
return echo(std::move(socket));
},
detached);
co_spawn(executor, echo(std::move(socket)), detached);
}
}
@@ -67,7 +62,7 @@ int main()
boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);
signals.async_wait([&](auto, auto){ io_context.stop(); });
co_spawn(io_context, listener, detached);
co_spawn(io_context, listener(), detached);
io_context.run();
}

View File

@@ -91,12 +91,7 @@ int main()
signals.async_wait([&](auto, auto){ io_context.stop(); });
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
co_spawn(io_context,
[acceptor = std::move(acceptor)]() mutable
{
return listener(std::move(acceptor));
},
detached);
co_spawn(io_context, listener(std::move(acceptor)), detached);
io_context.run();
}

View File

@@ -56,12 +56,7 @@ awaitable<void> listener()
for (;;)
{
tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
co_spawn(executor,
[socket = std::move(socket)]() mutable
{
return echo(std::move(socket));
},
detached);
co_spawn(executor, echo(std::move(socket)), detached);
}
}
@@ -74,7 +69,7 @@ int main()
boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);
signals.async_wait([&](auto, auto){ io_context.stop(); });
co_spawn(io_context, listener, detached);
co_spawn(io_context, listener(), detached);
io_context.run();
}

View File

@@ -54,6 +54,22 @@
#include <boost/asio/detached.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/execution.hpp>
#include <boost/asio/execution/allocator.hpp>
#include <boost/asio/execution/any_executor.hpp>
#include <boost/asio/execution/blocking.hpp>
#include <boost/asio/execution/blocking_adaptation.hpp>
#include <boost/asio/execution/bulk_guarantee.hpp>
#include <boost/asio/execution/context.hpp>
#include <boost/asio/execution/context_as.hpp>
#include <boost/asio/execution/execute.hpp>
#include <boost/asio/execution/executor.hpp>
#include <boost/asio/execution/invocable_archetype.hpp>
#include <boost/asio/execution/mapping.hpp>
#include <boost/asio/execution/occupancy.hpp>
#include <boost/asio/execution/outstanding_work.hpp>
#include <boost/asio/execution/prefer_only.hpp>
#include <boost/asio/execution/relationship.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/executor_work_guard.hpp>
@@ -94,6 +110,7 @@
#include <boost/asio/ip/udp.hpp>
#include <boost/asio/ip/unicast.hpp>
#include <boost/asio/ip/v6_only.hpp>
#include <boost/asio/is_applicable_property.hpp>
#include <boost/asio/is_executor.hpp>
#include <boost/asio/is_read_buffered.hpp>
#include <boost/asio/is_write_buffered.hpp>
@@ -101,6 +118,7 @@
#include <boost/asio/local/connect_pair.hpp>
#include <boost/asio/local/datagram_protocol.hpp>
#include <boost/asio/local/stream_protocol.hpp>
#include <boost/asio/multiple_exceptions.hpp>
#include <boost/asio/packaged_task.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/asio/posix/basic_descriptor.hpp>
@@ -109,14 +127,19 @@
#include <boost/asio/posix/descriptor_base.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/prefer.hpp>
#include <boost/asio/query.hpp>
#include <boost/asio/read.hpp>
#include <boost/asio/read_at.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/redirect_error.hpp>
#include <boost/asio/require.hpp>
#include <boost/asio/require_concept.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/asio/serial_port_base.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/asio/socket_base.hpp>
#include <boost/asio/static_thread_pool.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/streambuf.hpp>

View File

@@ -0,0 +1,73 @@
//
// any_io_executor.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_ASIO_ANY_IO_EXECUTOR_HPP
#define BOOST_ASIO_ANY_IO_EXECUTOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#if defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
# include <boost/asio/executor.hpp>
#else // defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
# include <boost/asio/execution.hpp>
# include <boost/asio/execution_context.hpp>
#endif // defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
#if defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
typedef executor any_io_executor;
#else // defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
/// Polymorphic executor type for use with I/O objects.
/**
* The @c any_io_executor type is a polymorphic executor that supports the set
* of properties required by I/O objects. It is defined as the
* execution::any_executor class template parameterised as follows:
* @code execution::any_executor<
* execution::context_as_t<execution_context&>,
* execution::blocking_t::never_t,
* execution::prefer_only<execution::blocking_t::possibly_t>,
* execution::prefer_only<execution::outstanding_work_t::tracked_t>,
* execution::prefer_only<execution::outstanding_work_t::untracked_t>,
* execution::prefer_only<execution::relationship_t::fork_t>,
* execution::prefer_only<execution::relationship_t::continuation_t>
* > @endcode
*/
#if defined(GENERATING_DOCUMENTATION)
typedef execution::any_executor<...> any_io_executor;
#else // defined(GENERATING_DOCUMENTATION)
typedef execution::any_executor<
execution::context_as_t<execution_context&>,
execution::blocking_t::never_t,
execution::prefer_only<execution::blocking_t::possibly_t>,
execution::prefer_only<execution::outstanding_work_t::tracked_t>,
execution::prefer_only<execution::outstanding_work_t::untracked_t>,
execution::prefer_only<execution::relationship_t::fork_t>,
execution::prefer_only<execution::relationship_t::continuation_t>
> any_io_executor;
#endif // defined(GENERATING_DOCUMENTATION)
#endif // defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_ANY_IO_EXECUTOR_HPP

View File

@@ -17,6 +17,7 @@
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/execution/executor.hpp>
#include <boost/asio/is_executor.hpp>
#include <boost/asio/system_executor.hpp>
@@ -114,8 +115,9 @@ get_associated_executor(const T& t) BOOST_ASIO_NOEXCEPT
template <typename T, typename Executor>
inline typename associated_executor<T, Executor>::type
get_associated_executor(const T& t, const Executor& ex,
typename enable_if<is_executor<
Executor>::value>::type* = 0) BOOST_ASIO_NOEXCEPT
typename enable_if<
is_executor<Executor>::value || execution::is_executor<Executor>::value
>::type* = 0) BOOST_ASIO_NOEXCEPT
{
return associated_executor<T, Executor>::get(t, ex);
}

View File

@@ -19,8 +19,13 @@
#if defined(BOOST_ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
#include <experimental/coroutine>
#include <boost/asio/executor.hpp>
#if defined(BOOST_ASIO_HAS_STD_COROUTINE)
# include <coroutine>
#else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
# include <experimental/coroutine>
#endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/detail/push_options.hpp>
@@ -28,8 +33,13 @@ namespace boost {
namespace asio {
namespace detail {
#if defined(BOOST_ASIO_HAS_STD_COROUTINE)
using std::coroutine_handle;
using std::suspend_always;
#else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
using std::experimental::coroutine_handle;
using std::experimental::suspend_always;
#endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
template <typename> class awaitable_thread;
template <typename, typename> class awaitable_frame;
@@ -37,7 +47,7 @@ template <typename, typename> class awaitable_frame;
} // namespace detail
/// The return type of a coroutine or asynchronous operation.
template <typename T, typename Executor = executor>
template <typename T, typename Executor = any_io_executor>
class awaitable
{
public:

View File

@@ -33,7 +33,7 @@ namespace asio {
#define BOOST_ASIO_BASIC_DATAGRAM_SOCKET_FWD_DECL
// Forward declaration with defaulted arguments.
template <typename Protocol, typename Executor = executor>
template <typename Protocol, typename Executor = any_io_executor>
class basic_datagram_socket;
#endif // !defined(BOOST_ASIO_BASIC_DATAGRAM_SOCKET_FWD_DECL)
@@ -1094,7 +1094,7 @@ private:
detail::non_const_lvalue<WriteHandler> handler2(handler);
self_->impl_.get_service().async_send(
self_->impl_.get_implementation(), buffers, flags,
handler2.value, self_->impl_.get_implementation_executor());
handler2.value, self_->impl_.get_executor());
}
private:
@@ -1127,8 +1127,8 @@ private:
detail::non_const_lvalue<WriteHandler> handler2(handler);
self_->impl_.get_service().async_send_to(
self_->impl_.get_implementation(), buffers, destination, flags,
handler2.value, self_->impl_.get_implementation_executor());
self_->impl_.get_implementation(), buffers, destination,
flags, handler2.value, self_->impl_.get_executor());
}
private:
@@ -1162,7 +1162,7 @@ private:
detail::non_const_lvalue<ReadHandler> handler2(handler);
self_->impl_.get_service().async_receive(
self_->impl_.get_implementation(), buffers, flags,
handler2.value, self_->impl_.get_implementation_executor());
handler2.value, self_->impl_.get_executor());
}
private:
@@ -1195,8 +1195,8 @@ private:
detail::non_const_lvalue<ReadHandler> handler2(handler);
self_->impl_.get_service().async_receive_from(
self_->impl_.get_implementation(), buffers, *sender_endpoint, flags,
handler2.value, self_->impl_.get_implementation_executor());
self_->impl_.get_implementation(), buffers, *sender_endpoint,
flags, handler2.value, self_->impl_.get_executor());
}
private:

View File

@@ -21,6 +21,7 @@
|| defined(GENERATING_DOCUMENTATION)
#include <cstddef>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/detail/deadline_timer_service.hpp>
#include <boost/asio/detail/handler_type_requirements.hpp>
#include <boost/asio/detail/io_object_impl.hpp>
@@ -28,7 +29,6 @@
#include <boost/asio/detail/throw_error.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/time_traits.hpp>
#include <boost/asio/detail/push_options.hpp>
@@ -126,7 +126,7 @@ namespace asio {
*/
template <typename Time,
typename TimeTraits = boost::asio::time_traits<Time>,
typename Executor = executor>
typename Executor = any_io_executor>
class basic_deadline_timer
{
public:
@@ -672,8 +672,8 @@ private:
detail::non_const_lvalue<WaitHandler> handler2(handler);
self_->impl_.get_service().async_wait(
self_->impl_.get_implementation(), handler2.value,
self_->impl_.get_implementation_executor());
self_->impl_.get_implementation(),
handler2.value, self_->impl_.get_executor());
}
private:

View File

@@ -33,7 +33,7 @@ namespace asio {
#define BOOST_ASIO_BASIC_RAW_SOCKET_FWD_DECL
// Forward declaration with defaulted arguments.
template <typename Protocol, typename Executor = executor>
template <typename Protocol, typename Executor = any_io_executor>
class basic_raw_socket;
#endif // !defined(BOOST_ASIO_BASIC_RAW_SOCKET_FWD_DECL)
@@ -1086,7 +1086,7 @@ private:
detail::non_const_lvalue<WriteHandler> handler2(handler);
self_->impl_.get_service().async_send(
self_->impl_.get_implementation(), buffers, flags,
handler2.value, self_->impl_.get_implementation_executor());
handler2.value, self_->impl_.get_executor());
}
private:
@@ -1119,8 +1119,8 @@ private:
detail::non_const_lvalue<WriteHandler> handler2(handler);
self_->impl_.get_service().async_send_to(
self_->impl_.get_implementation(), buffers, destination, flags,
handler2.value, self_->impl_.get_implementation_executor());
self_->impl_.get_implementation(), buffers, destination,
flags, handler2.value, self_->impl_.get_executor());
}
private:
@@ -1154,7 +1154,7 @@ private:
detail::non_const_lvalue<ReadHandler> handler2(handler);
self_->impl_.get_service().async_receive(
self_->impl_.get_implementation(), buffers, flags,
handler2.value, self_->impl_.get_implementation_executor());
handler2.value, self_->impl_.get_executor());
}
private:
@@ -1187,8 +1187,8 @@ private:
detail::non_const_lvalue<ReadHandler> handler2(handler);
self_->impl_.get_service().async_receive_from(
self_->impl_.get_implementation(), buffers, *sender_endpoint, flags,
handler2.value, self_->impl_.get_implementation_executor());
self_->impl_.get_implementation(), buffers, *sender_endpoint,
flags, handler2.value, self_->impl_.get_executor());
}
private:

View File

@@ -31,7 +31,7 @@ namespace asio {
#define BOOST_ASIO_BASIC_SEQ_PACKET_SOCKET_FWD_DECL
// Forward declaration with defaulted arguments.
template <typename Protocol, typename Executor = executor>
template <typename Protocol, typename Executor = any_io_executor>
class basic_seq_packet_socket;
#endif // !defined(BOOST_ASIO_BASIC_SEQ_PACKET_SOCKET_FWD_DECL)
@@ -707,7 +707,7 @@ private:
detail::non_const_lvalue<WriteHandler> handler2(handler);
self_->impl_.get_service().async_send(
self_->impl_.get_implementation(), buffers, flags,
handler2.value, self_->impl_.get_implementation_executor());
handler2.value, self_->impl_.get_executor());
}
private:
@@ -741,8 +741,8 @@ private:
detail::non_const_lvalue<ReadHandler> handler2(handler);
self_->impl_.get_service().async_receive_with_flags(
self_->impl_.get_implementation(), buffers, in_flags, *out_flags,
handler2.value, self_->impl_.get_implementation_executor());
self_->impl_.get_implementation(), buffers, in_flags,
*out_flags, handler2.value, self_->impl_.get_executor());
}
private:

View File

@@ -22,6 +22,7 @@
|| defined(GENERATING_DOCUMENTATION)
#include <string>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/detail/handler_type_requirements.hpp>
#include <boost/asio/detail/io_object_impl.hpp>
@@ -30,7 +31,6 @@
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/serial_port_base.hpp>
#if defined(BOOST_ASIO_HAS_IOCP)
# include <boost/asio/detail/win_iocp_serial_port_service.hpp>
@@ -56,7 +56,7 @@ namespace asio {
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*/
template <typename Executor = executor>
template <typename Executor = any_io_executor>
class basic_serial_port
: public serial_port_base
{
@@ -850,8 +850,8 @@ private:
detail::non_const_lvalue<WriteHandler> handler2(handler);
self_->impl_.get_service().async_write_some(
self_->impl_.get_implementation(), buffers, handler2.value,
self_->impl_.get_implementation_executor());
self_->impl_.get_implementation(), buffers,
handler2.value, self_->impl_.get_executor());
}
private:
@@ -883,8 +883,8 @@ private:
detail::non_const_lvalue<ReadHandler> handler2(handler);
self_->impl_.get_service().async_read_some(
self_->impl_.get_implementation(), buffers, handler2.value,
self_->impl_.get_implementation_executor());
self_->impl_.get_implementation(), buffers,
handler2.value, self_->impl_.get_executor());
}
private:

View File

@@ -17,6 +17,7 @@
#include <boost/asio/detail/config.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/detail/handler_type_requirements.hpp>
#include <boost/asio/detail/io_object_impl.hpp>
@@ -26,7 +27,6 @@
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/executor.hpp>
namespace boost {
namespace asio {
@@ -91,7 +91,7 @@ namespace asio {
* that any signals registered using signal_set objects are unblocked in at
* least one thread.
*/
template <typename Executor = executor>
template <typename Executor = any_io_executor>
class basic_signal_set
{
public:
@@ -553,8 +553,8 @@ private:
detail::non_const_lvalue<SignalHandler> handler2(handler);
self_->impl_.get_service().async_wait(
self_->impl_.get_implementation(), handler2.value,
self_->impl_.get_implementation_executor());
self_->impl_.get_implementation(),
handler2.value, self_->impl_.get_executor());
}
private:

View File

@@ -15,6 +15,7 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/detail/config.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/detail/handler_type_requirements.hpp>
@@ -24,7 +25,6 @@
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/socket_base.hpp>
@@ -49,7 +49,7 @@ namespace asio {
#define BOOST_ASIO_BASIC_SOCKET_FWD_DECL
// Forward declaration with defaulted arguments.
template <typename Protocol, typename Executor = executor>
template <typename Protocol, typename Executor = any_io_executor>
class basic_socket;
#endif // !defined(BOOST_ASIO_BASIC_SOCKET_FWD_DECL)
@@ -1847,7 +1847,7 @@ private:
detail::non_const_lvalue<ConnectHandler> handler2(handler);
self_->impl_.get_service().async_connect(
self_->impl_.get_implementation(), peer_endpoint,
handler2.value, self_->impl_.get_implementation_executor());
handler2.value, self_->impl_.get_executor());
}
}
@@ -1879,8 +1879,8 @@ private:
detail::non_const_lvalue<WaitHandler> handler2(handler);
self_->impl_.get_service().async_wait(
self_->impl_.get_implementation(), w, handler2.value,
self_->impl_.get_implementation_executor());
self_->impl_.get_implementation(), w,
handler2.value, self_->impl_.get_executor());
}
private:

View File

@@ -16,6 +16,7 @@
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/basic_socket.hpp>
#include <boost/asio/detail/handler_type_requirements.hpp>
#include <boost/asio/detail/io_object_impl.hpp>
@@ -24,7 +25,6 @@
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/socket_base.hpp>
#if defined(BOOST_ASIO_WINDOWS_RUNTIME)
@@ -48,7 +48,7 @@ namespace asio {
#define BOOST_ASIO_BASIC_SOCKET_ACCEPTOR_FWD_DECL
// Forward declaration with defaulted arguments.
template <typename Protocol, typename Executor = executor>
template <typename Protocol, typename Executor = any_io_executor>
class basic_socket_acceptor;
#endif // !defined(BOOST_ASIO_BASIC_SOCKET_ACCEPTOR_FWD_DECL)
@@ -2401,8 +2401,8 @@ private:
detail::non_const_lvalue<WaitHandler> handler2(handler);
self_->impl_.get_service().async_wait(
self_->impl_.get_implementation(), w, handler2.value,
self_->impl_.get_implementation_executor());
self_->impl_.get_implementation(), w,
handler2.value, self_->impl_.get_executor());
}
private:
@@ -2436,7 +2436,7 @@ private:
detail::non_const_lvalue<AcceptHandler> handler2(handler);
self_->impl_.get_service().async_accept(
self_->impl_.get_implementation(), *peer, peer_endpoint,
handler2.value, self_->impl_.get_implementation_executor());
handler2.value, self_->impl_.get_executor());
}
private:
@@ -2470,7 +2470,7 @@ private:
detail::non_const_lvalue<MoveAcceptHandler> handler2(handler);
self_->impl_.get_service().async_move_accept(
self_->impl_.get_implementation(), peer_ex, peer_endpoint,
handler2.value, self_->impl_.get_implementation_executor());
handler2.value, self_->impl_.get_executor());
}
private:

View File

@@ -33,7 +33,7 @@ namespace asio {
#define BOOST_ASIO_BASIC_STREAM_SOCKET_FWD_DECL
// Forward declaration with defaulted arguments.
template <typename Protocol, typename Executor = executor>
template <typename Protocol, typename Executor = any_io_executor>
class basic_stream_socket;
#endif // !defined(BOOST_ASIO_BASIC_STREAM_SOCKET_FWD_DECL)
@@ -1001,7 +1001,7 @@ private:
detail::non_const_lvalue<WriteHandler> handler2(handler);
self_->impl_.get_service().async_send(
self_->impl_.get_implementation(), buffers, flags,
handler2.value, self_->impl_.get_implementation_executor());
handler2.value, self_->impl_.get_executor());
}
private:
@@ -1035,7 +1035,7 @@ private:
detail::non_const_lvalue<ReadHandler> handler2(handler);
self_->impl_.get_service().async_receive(
self_->impl_.get_implementation(), buffers, flags,
handler2.value, self_->impl_.get_implementation_executor());
handler2.value, self_->impl_.get_executor());
}
private:

View File

@@ -17,6 +17,7 @@
#include <boost/asio/detail/config.hpp>
#include <cstddef>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/detail/chrono_time_traits.hpp>
#include <boost/asio/detail/deadline_timer_service.hpp>
#include <boost/asio/detail/handler_type_requirements.hpp>
@@ -24,7 +25,6 @@
#include <boost/asio/detail/non_const_lvalue.hpp>
#include <boost/asio/detail/throw_error.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/wait_traits.hpp>
#if defined(BOOST_ASIO_HAS_MOVE)
@@ -42,7 +42,7 @@ namespace asio {
// Forward declaration with defaulted arguments.
template <typename Clock,
typename WaitTraits = boost::asio::wait_traits<Clock>,
typename Executor = executor>
typename Executor = any_io_executor>
class basic_waitable_timer;
#endif // !defined(BOOST_ASIO_BASIC_WAITABLE_TIMER_FWD_DECL)
@@ -319,6 +319,54 @@ public:
impl_ = std::move(other.impl_);
return *this;
}
// All timers have access to each other's implementations.
template <typename Clock1, typename WaitTraits1, typename Executor1>
friend class basic_waitable_timer;
/// Move-construct a basic_waitable_timer from another.
/**
* This constructor moves a timer from one object to another.
*
* @param other The other basic_waitable_timer object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_waitable_timer(const executor_type&)
* constructor.
*/
template <typename Executor1>
basic_waitable_timer(
basic_waitable_timer<Clock, WaitTraits, Executor1>&& other,
typename enable_if<
is_convertible<Executor1, Executor>::value
>::type* = 0)
: impl_(std::move(other.impl_))
{
}
/// Move-assign a basic_waitable_timer from another.
/**
* This assignment operator moves a timer from one object to another. Cancels
* any outstanding asynchronous operations associated with the target object.
*
* @param other The other basic_waitable_timer object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_waitable_timer(const executor_type&)
* constructor.
*/
template <typename Executor1>
typename enable_if<
is_convertible<Executor1, Executor>::value,
basic_waitable_timer&
>::type operator=(basic_waitable_timer<Clock, WaitTraits, Executor1>&& other)
{
basic_waitable_timer tmp(std::move(other));
impl_ = std::move(tmp.impl_);
return *this;
}
#endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// Destroys the timer.
@@ -743,8 +791,8 @@ private:
detail::non_const_lvalue<WaitHandler> handler2(handler);
self_->impl_.get_service().async_wait(
self_->impl_.get_implementation(), handler2.value,
self_->impl_.get_implementation_executor());
self_->impl_.get_implementation(),
handler2.value, self_->impl_.get_executor());
}
private:

View File

@@ -21,6 +21,7 @@
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/execution/executor.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/is_executor.hpp>
#include <boost/asio/uses_executor.hpp>
@@ -155,16 +156,14 @@ struct executor_binder_argument_type<R(&)(A1, A2)>
typedef A2 second_argument_type;
};
// Helper to:
// - Apply the empty base optimisation to the executor.
// - Perform uses_executor construction of the target type, if required.
// Helper to perform uses_executor construction of the target type, if
// required.
template <typename T, typename Executor, bool UsesExecutor>
class executor_binder_base;
template <typename T, typename Executor>
class executor_binder_base<T, Executor, true>
: protected Executor
{
protected:
template <typename E, typename U>
@@ -496,7 +495,9 @@ private:
template <typename Executor, typename T>
inline executor_binder<typename decay<T>::type, Executor>
bind_executor(const Executor& ex, BOOST_ASIO_MOVE_ARG(T) t,
typename enable_if<is_executor<Executor>::value>::type* = 0)
typename enable_if<
is_executor<Executor>::value || execution::is_executor<Executor>::value
>::type* = 0)
{
return executor_binder<typename decay<T>::type, Executor>(
executor_arg_t(), ex, BOOST_ASIO_MOVE_CAST(T)(t));

View File

@@ -50,11 +50,13 @@
#if defined(BOOST_ASIO_HAS_BOOST_WORKAROUND)
# include <boost/detail/workaround.hpp>
# if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) \
|| BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590))
# if !defined(__clang__)
# if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582))
# define BOOST_ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND
# endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582))
# elif BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590))
# define BOOST_ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND
# endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582))
// || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590))
# endif // BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590))
#endif // defined(BOOST_ASIO_HAS_BOOST_WORKAROUND)
#if defined(BOOST_ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND)

View File

@@ -20,6 +20,7 @@
#if defined(BOOST_ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
#include <boost/asio/awaitable.hpp>
#include <boost/asio/execution/executor.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/is_executor.hpp>
@@ -46,13 +47,321 @@ struct awaitable_signature<awaitable<void, Executor>>
} // namespace detail
/// Spawn a new thread of execution.
/// Spawn a new coroutined-based thread of execution.
/**
* The entry point function object @c f must have the signature:
* @param ex The executor that will be used to schedule the new thread of
* execution.
*
* @code awaitable<void, E> f(); @endcode
* @param a The boost::asio::awaitable object that is the result of calling the
* coroutine's entry point function.
*
* where @c E is convertible from @c Executor.
* @param token The completion token that will handle the notification that
* the thread of execution has completed. The function signature of the
* completion handler must be:
* @code void handler(std::exception_ptr, T); @endcode
*
* @par Example
* @code
* boost::asio::awaitable<std::size_t> echo(tcp::socket socket)
* {
* std::size_t bytes_transferred = 0;
*
* try
* {
* char data[1024];
* for (;;)
* {
* std::size_t n = co_await socket.async_read_some(
* boost::asio::buffer(data), boost::asio::use_awaitable);
*
* co_await boost::asio::async_write(socket,
* boost::asio::buffer(data, n), boost::asio::use_awaitable);
*
* bytes_transferred += n;
* }
* }
* catch (const std::exception&)
* {
* }
*
* co_return bytes_transferred;
* }
*
* // ...
*
* boost::asio::co_spawn(my_executor,
* echo(std::move(my_tcp_socket)),
* [](std::exception_ptr e, std::size_t n)
* {
* std::cout << "transferred " << n << "\n";
* });
* @endcode
*/
template <typename Executor, typename T, typename AwaitableExecutor,
BOOST_ASIO_COMPLETION_TOKEN_FOR(
void(std::exception_ptr, T)) CompletionToken
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)>
inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken, void(std::exception_ptr, T))
co_spawn(const Executor& ex, awaitable<T, AwaitableExecutor> a,
CompletionToken&& token
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor),
typename enable_if<
(is_executor<Executor>::value || execution::is_executor<Executor>::value)
&& is_convertible<Executor, AwaitableExecutor>::value
>::type* = 0);
/// Spawn a new coroutined-based thread of execution.
/**
* @param ex The executor that will be used to schedule the new thread of
* execution.
*
* @param a The boost::asio::awaitable object that is the result of calling the
* coroutine's entry point function.
*
* @param token The completion token that will handle the notification that
* the thread of execution has completed. The function signature of the
* completion handler must be:
* @code void handler(std::exception_ptr); @endcode
*
* @par Example
* @code
* boost::asio::awaitable<void> echo(tcp::socket socket)
* {
* try
* {
* char data[1024];
* for (;;)
* {
* std::size_t n = co_await socket.async_read_some(
* boost::asio::buffer(data), boost::asio::use_awaitable);
*
* co_await boost::asio::async_write(socket,
* boost::asio::buffer(data, n), boost::asio::use_awaitable);
* }
* }
* catch (const std::exception& e)
* {
* std::cerr << "Exception: " << e.what() << "\n";
* }
* }
*
* // ...
*
* boost::asio::co_spawn(my_executor,
* echo(std::move(my_tcp_socket)),
* boost::asio::detached);
* @endcode
*/
template <typename Executor, typename AwaitableExecutor,
BOOST_ASIO_COMPLETION_TOKEN_FOR(
void(std::exception_ptr)) CompletionToken
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)>
inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken, void(std::exception_ptr))
co_spawn(const Executor& ex, awaitable<void, AwaitableExecutor> a,
CompletionToken&& token
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor),
typename enable_if<
(is_executor<Executor>::value || execution::is_executor<Executor>::value)
&& is_convertible<Executor, AwaitableExecutor>::value
>::type* = 0);
/// Spawn a new coroutined-based thread of execution.
/**
* @param ctx An execution context that will provide the executor to be used to
* schedule the new thread of execution.
*
* @param a The boost::asio::awaitable object that is the result of calling the
* coroutine's entry point function.
*
* @param token The completion token that will handle the notification that
* the thread of execution has completed. The function signature of the
* completion handler must be:
* @code void handler(std::exception_ptr); @endcode
*
* @par Example
* @code
* boost::asio::awaitable<std::size_t> echo(tcp::socket socket)
* {
* std::size_t bytes_transferred = 0;
*
* try
* {
* char data[1024];
* for (;;)
* {
* std::size_t n = co_await socket.async_read_some(
* boost::asio::buffer(data), boost::asio::use_awaitable);
*
* co_await boost::asio::async_write(socket,
* boost::asio::buffer(data, n), boost::asio::use_awaitable);
*
* bytes_transferred += n;
* }
* }
* catch (const std::exception&)
* {
* }
*
* co_return bytes_transferred;
* }
*
* // ...
*
* boost::asio::co_spawn(my_io_context,
* echo(std::move(my_tcp_socket)),
* [](std::exception_ptr e, std::size_t n)
* {
* std::cout << "transferred " << n << "\n";
* });
* @endcode
*/
template <typename ExecutionContext, typename T, typename AwaitableExecutor,
BOOST_ASIO_COMPLETION_TOKEN_FOR(
void(std::exception_ptr, T)) CompletionToken
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(
typename ExecutionContext::executor_type)>
inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken, void(std::exception_ptr, T))
co_spawn(ExecutionContext& ctx, awaitable<T, AwaitableExecutor> a,
CompletionToken&& token
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(
typename ExecutionContext::executor_type),
typename enable_if<
is_convertible<ExecutionContext&, execution_context&>::value
&& is_convertible<typename ExecutionContext::executor_type,
AwaitableExecutor>::value
>::type* = 0);
/// Spawn a new coroutined-based thread of execution.
/**
* @param ctx An execution context that will provide the executor to be used to
* schedule the new thread of execution.
*
* @param a The boost::asio::awaitable object that is the result of calling the
* coroutine's entry point function.
*
* @param token The completion token that will handle the notification that
* the thread of execution has completed. The function signature of the
* completion handler must be:
* @code void handler(std::exception_ptr); @endcode
*
* @par Example
* @code
* boost::asio::awaitable<void> echo(tcp::socket socket)
* {
* try
* {
* char data[1024];
* for (;;)
* {
* std::size_t n = co_await socket.async_read_some(
* boost::asio::buffer(data), boost::asio::use_awaitable);
*
* co_await boost::asio::async_write(socket,
* boost::asio::buffer(data, n), boost::asio::use_awaitable);
* }
* }
* catch (const std::exception& e)
* {
* std::cerr << "Exception: " << e.what() << "\n";
* }
* }
*
* // ...
*
* boost::asio::co_spawn(my_io_context,
* echo(std::move(my_tcp_socket)),
* boost::asio::detached);
* @endcode
*/
template <typename ExecutionContext, typename AwaitableExecutor,
BOOST_ASIO_COMPLETION_TOKEN_FOR(
void(std::exception_ptr)) CompletionToken
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(
typename ExecutionContext::executor_type)>
inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken, void(std::exception_ptr))
co_spawn(ExecutionContext& ctx, awaitable<void, AwaitableExecutor> a,
CompletionToken&& token
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(
typename ExecutionContext::executor_type),
typename enable_if<
is_convertible<ExecutionContext&, execution_context&>::value
&& is_convertible<typename ExecutionContext::executor_type,
AwaitableExecutor>::value
>::type* = 0);
/// Spawn a new coroutined-based thread of execution.
/**
* @param ex The executor that will be used to schedule the new thread of
* execution.
*
* @param f A nullary function object with a return type of the form
* @c boost::asio::awaitable<R,E> that will be used as the coroutine's entry
* point.
*
* @param token The completion token that will handle the notification that the
* thread of execution has completed. If @c R is @c void, the function
* signature of the completion handler must be:
*
* @code void handler(std::exception_ptr); @endcode
* Otherwise, the function signature of the completion handler must be:
* @code void handler(std::exception_ptr, R); @endcode
*
*
* @par Example
* @code
* boost::asio::awaitable<std::size_t> echo(tcp::socket socket)
* {
* std::size_t bytes_transferred = 0;
*
* try
* {
* char data[1024];
* for (;;)
* {
* std::size_t n = co_await socket.async_read_some(
* boost::asio::buffer(data), boost::asio::use_awaitable);
*
* co_await boost::asio::async_write(socket,
* boost::asio::buffer(data, n), boost::asio::use_awaitable);
*
* bytes_transferred += n;
* }
* }
* catch (const std::exception&)
* {
* }
*
* co_return bytes_transferred;
* }
*
* // ...
*
* boost::asio::co_spawn(my_executor,
* [socket = std::move(my_tcp_socket)]() mutable
* -> boost::asio::awaitable<void>
* {
* try
* {
* char data[1024];
* for (;;)
* {
* std::size_t n = co_await socket.async_read_some(
* boost::asio::buffer(data), boost::asio::use_awaitable);
*
* co_await boost::asio::async_write(socket,
* boost::asio::buffer(data, n), boost::asio::use_awaitable);
* }
* }
* catch (const std::exception& e)
* {
* std::cerr << "Exception: " << e.what() << "\n";
* }
* }, boost::asio::detached);
* @endcode
*/
template <typename Executor, typename F,
BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature<
@@ -64,16 +373,78 @@ co_spawn(const Executor& ex, F&& f,
CompletionToken&& token
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor),
typename enable_if<
is_executor<Executor>::value
is_executor<Executor>::value || execution::is_executor<Executor>::value
>::type* = 0);
/// Spawn a new thread of execution.
/// Spawn a new coroutined-based thread of execution.
/**
* The entry point function object @c f must have the signature:
* @param ctx An execution context that will provide the executor to be used to
* schedule the new thread of execution.
*
* @code awaitable<void, E> f(); @endcode
* @param f A nullary function object with a return type of the form
* @c boost::asio::awaitable<R,E> that will be used as the coroutine's entry
* point.
*
* where @c E is convertible from @c ExecutionContext::executor_type.
* @param token The completion token that will handle the notification that the
* thread of execution has completed. If @c R is @c void, the function
* signature of the completion handler must be:
*
* @code void handler(std::exception_ptr); @endcode
* Otherwise, the function signature of the completion handler must be:
* @code void handler(std::exception_ptr, R); @endcode
*
*
* @par Example
* @code
* boost::asio::awaitable<std::size_t> echo(tcp::socket socket)
* {
* std::size_t bytes_transferred = 0;
*
* try
* {
* char data[1024];
* for (;;)
* {
* std::size_t n = co_await socket.async_read_some(
* boost::asio::buffer(data), boost::asio::use_awaitable);
*
* co_await boost::asio::async_write(socket,
* boost::asio::buffer(data, n), boost::asio::use_awaitable);
*
* bytes_transferred += n;
* }
* }
* catch (const std::exception&)
* {
* }
*
* co_return bytes_transferred;
* }
*
* // ...
*
* boost::asio::co_spawn(my_io_context,
* [socket = std::move(my_tcp_socket)]() mutable
* -> boost::asio::awaitable<void>
* {
* try
* {
* char data[1024];
* for (;;)
* {
* std::size_t n = co_await socket.async_read_some(
* boost::asio::buffer(data), boost::asio::use_awaitable);
*
* co_await boost::asio::async_write(socket,
* boost::asio::buffer(data, n), boost::asio::use_awaitable);
* }
* }
* catch (const std::exception& e)
* {
* std::cerr << "Exception: " << e.what() << "\n";
* }
* }, boost::asio::detached);
* @endcode
*/
template <typename ExecutionContext, typename F,
BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature<

View File

@@ -19,6 +19,7 @@
#include <boost/asio/async_result.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/execution/executor.hpp>
#include <boost/asio/is_executor.hpp>
#include <boost/asio/detail/push_options.hpp>
@@ -101,7 +102,9 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void()) defer(
const Executor& ex,
BOOST_ASIO_MOVE_ARG(CompletionToken) token
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor),
typename enable_if<is_executor<Executor>::value>::type* = 0);
typename enable_if<
execution::is_executor<Executor>::value || is_executor<Executor>::value
>::type* = 0);
/// Submits a completion token or function object for execution.
/**

View File

@@ -17,6 +17,7 @@
#include <boost/asio/detail/config.hpp>
#include <memory>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/push_options.hpp>
@@ -42,6 +43,55 @@ public:
BOOST_ASIO_CONSTEXPR detached_t()
{
}
/// Adapts an executor to add the @c detached_t completion token as the
/// default.
template <typename InnerExecutor>
struct executor_with_default : InnerExecutor
{
/// Specify @c detached_t as the default completion token type.
typedef detached_t default_completion_token_type;
/// Construct the adapted executor from the inner executor type.
executor_with_default(const InnerExecutor& ex) BOOST_ASIO_NOEXCEPT
: InnerExecutor(ex)
{
}
/// Convert the specified executor to the inner executor type, then use
/// that to construct the adapted executor.
template <typename OtherExecutor>
executor_with_default(const OtherExecutor& ex,
typename enable_if<
is_convertible<OtherExecutor, InnerExecutor>::value
>::type* = 0) BOOST_ASIO_NOEXCEPT
: InnerExecutor(ex)
{
}
};
/// Type alias to adapt an I/O object to use @c detached_t as its
/// default completion token type.
#if defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES) \
|| defined(GENERATING_DOCUMENTATION)
template <typename T>
using as_default_on_t = typename T::template rebind_executor<
executor_with_default<typename T::executor_type> >::other;
#endif // defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES)
// || defined(GENERATING_DOCUMENTATION)
/// Function helper to adapt an I/O object to use @c detached_t as its
/// default completion token type.
template <typename T>
static typename decay<T>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other
as_default_on(BOOST_ASIO_MOVE_ARG(T) object)
{
return typename decay<T>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other(BOOST_ASIO_MOVE_CAST(T)(object));
}
};
/// A special value, similar to std::nothrow.

View File

@@ -29,4 +29,12 @@
# define BOOST_ASIO_ASSERT(expr) assert(expr)
#endif // defined(BOOST_ASIO_HAS_BOOST_ASSERT)
#if defined(BOOST_ASIO_HAS_STATIC_ASSERT)
# define BOOST_ASIO_STATIC_ASSERT(c, n, m) static_assert((c), m)
#else // defined(BOOST_ASIO_HAS_STATIC_ASSERT)
# define BOOST_ASIO_STATIC_ASSERT(c, n, m) \
typedef char static_assert_ ## n \
[(c) ? 1 : -1] BOOST_ASIO_UNUSED_TYPEDEF
#endif // defined(BOOST_ASIO_HAS_STATIC_ASSERT)
#endif // BOOST_ASIO_DETAIL_ASSERT_HPP

View File

@@ -32,12 +32,31 @@ namespace detail {
#if !defined(BOOST_ASIO_HAS_THREADS)
typedef long atomic_count;
inline void increment(atomic_count& a, long b) { a += b; }
inline void ref_count_up(atomic_count& a) { ++a; }
inline bool ref_count_down(atomic_count& a) { return --a == 0; }
#elif defined(BOOST_ASIO_HAS_STD_ATOMIC)
typedef std::atomic<long> atomic_count;
inline void increment(atomic_count& a, long b) { a += b; }
inline void ref_count_up(atomic_count& a)
{
a.fetch_add(1, std::memory_order_relaxed);
}
inline bool ref_count_down(atomic_count& a)
{
if (a.fetch_sub(1, std::memory_order_release) == 1)
{
std::atomic_thread_fence(std::memory_order_acquire);
return true;
}
return false;
}
#else // defined(BOOST_ASIO_HAS_STD_ATOMIC)
typedef boost::detail::atomic_count atomic_count;
inline void increment(atomic_count& a, long b) { while (b > 0) ++a, --b; }
inline void ref_count_up(atomic_count& a) { ++a; }
inline bool ref_count_down(atomic_count& a) { return --a == 0; }
#endif // defined(BOOST_ASIO_HAS_STD_ATOMIC)
} // namespace detail

View File

@@ -76,19 +76,29 @@ public:
};
template <typename Handler, typename Arg1>
inline void* asio_handler_allocate(std::size_t size,
inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,
binder1<Handler, Arg1>* this_handler)
{
#if defined(BOOST_ASIO_NO_DEPRECATED)
boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
return asio_handler_allocate_is_no_longer_used();
#else // defined(BOOST_ASIO_NO_DEPRECATED)
return boost_asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void* pointer, std::size_t size,
binder1<Handler, Arg1>* this_handler)
{
boost_asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_deallocate_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1>
@@ -100,19 +110,27 @@ inline bool asio_handler_is_continuation(
}
template <typename Function, typename Handler, typename Arg1>
inline void asio_handler_invoke(Function& function,
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(Function& function,
binder1<Handler, Arg1>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Function, typename Handler, typename Arg1>
inline void asio_handler_invoke(const Function& function,
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(const Function& function,
binder1<Handler, Arg1>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1>
@@ -177,19 +195,29 @@ public:
};
template <typename Handler, typename Arg1, typename Arg2>
inline void* asio_handler_allocate(std::size_t size,
inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,
binder2<Handler, Arg1, Arg2>* this_handler)
{
#if defined(BOOST_ASIO_NO_DEPRECATED)
boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
return asio_handler_allocate_is_no_longer_used();
#else // defined(BOOST_ASIO_NO_DEPRECATED)
return boost_asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1, typename Arg2>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void* pointer, std::size_t size,
binder2<Handler, Arg1, Arg2>* this_handler)
{
boost_asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_deallocate_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1, typename Arg2>
@@ -201,19 +229,27 @@ inline bool asio_handler_is_continuation(
}
template <typename Function, typename Handler, typename Arg1, typename Arg2>
inline void asio_handler_invoke(Function& function,
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(Function& function,
binder2<Handler, Arg1, Arg2>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Function, typename Handler, typename Arg1, typename Arg2>
inline void asio_handler_invoke(const Function& function,
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(const Function& function,
binder2<Handler, Arg1, Arg2>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1, typename Arg2>
@@ -284,19 +320,29 @@ public:
};
template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
inline void* asio_handler_allocate(std::size_t size,
inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,
binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
{
#if defined(BOOST_ASIO_NO_DEPRECATED)
boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
return asio_handler_allocate_is_no_longer_used();
#else // defined(BOOST_ASIO_NO_DEPRECATED)
return boost_asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void* pointer, std::size_t size,
binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
{
boost_asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_deallocate_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
@@ -309,20 +355,28 @@ inline bool asio_handler_is_continuation(
template <typename Function, typename Handler,
typename Arg1, typename Arg2, typename Arg3>
inline void asio_handler_invoke(Function& function,
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(Function& function,
binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Function, typename Handler,
typename Arg1, typename Arg2, typename Arg3>
inline void asio_handler_invoke(const Function& function,
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(const Function& function,
binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
@@ -402,20 +456,30 @@ public:
template <typename Handler, typename Arg1,
typename Arg2, typename Arg3, typename Arg4>
inline void* asio_handler_allocate(std::size_t size,
inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,
binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
{
#if defined(BOOST_ASIO_NO_DEPRECATED)
boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
return asio_handler_allocate_is_no_longer_used();
#else // defined(BOOST_ASIO_NO_DEPRECATED)
return boost_asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1,
typename Arg2, typename Arg3, typename Arg4>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void* pointer, std::size_t size,
binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
{
boost_asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_deallocate_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1,
@@ -429,20 +493,28 @@ inline bool asio_handler_is_continuation(
template <typename Function, typename Handler, typename Arg1,
typename Arg2, typename Arg3, typename Arg4>
inline void asio_handler_invoke(Function& function,
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(Function& function,
binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Function, typename Handler, typename Arg1,
typename Arg2, typename Arg3, typename Arg4>
inline void asio_handler_invoke(const Function& function,
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(const Function& function,
binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1,
@@ -528,20 +600,30 @@ public:
template <typename Handler, typename Arg1, typename Arg2,
typename Arg3, typename Arg4, typename Arg5>
inline void* asio_handler_allocate(std::size_t size,
inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,
binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
{
#if defined(BOOST_ASIO_NO_DEPRECATED)
boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
return asio_handler_allocate_is_no_longer_used();
#else // defined(BOOST_ASIO_NO_DEPRECATED)
return boost_asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1, typename Arg2,
typename Arg3, typename Arg4, typename Arg5>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void* pointer, std::size_t size,
binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
{
boost_asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_deallocate_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1, typename Arg2,
@@ -555,20 +637,28 @@ inline bool asio_handler_is_continuation(
template <typename Function, typename Handler, typename Arg1,
typename Arg2, typename Arg3, typename Arg4, typename Arg5>
inline void asio_handler_invoke(Function& function,
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(Function& function,
binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Function, typename Handler, typename Arg1,
typename Arg2, typename Arg3, typename Arg4, typename Arg5>
inline void asio_handler_invoke(const Function& function,
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(const Function& function,
binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1, typename Arg2,
@@ -611,19 +701,29 @@ public:
};
template <typename Handler, typename Arg1>
inline void* asio_handler_allocate(std::size_t size,
inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,
move_binder1<Handler, Arg1>* this_handler)
{
#if defined(BOOST_ASIO_NO_DEPRECATED)
boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
return asio_handler_allocate_is_no_longer_used();
#else // defined(BOOST_ASIO_NO_DEPRECATED)
return boost_asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void* pointer, std::size_t size,
move_binder1<Handler, Arg1>* this_handler)
{
boost_asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_deallocate_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1>
@@ -635,11 +735,15 @@ inline bool asio_handler_is_continuation(
}
template <typename Function, typename Handler, typename Arg1>
inline void asio_handler_invoke(BOOST_ASIO_MOVE_ARG(Function) function,
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(BOOST_ASIO_MOVE_ARG(Function) function,
move_binder1<Handler, Arg1>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
BOOST_ASIO_MOVE_CAST(Function)(function), this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1, typename Arg2>
@@ -674,19 +778,29 @@ public:
};
template <typename Handler, typename Arg1, typename Arg2>
inline void* asio_handler_allocate(std::size_t size,
inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,
move_binder2<Handler, Arg1, Arg2>* this_handler)
{
#if defined(BOOST_ASIO_NO_DEPRECATED)
boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
return asio_handler_allocate_is_no_longer_used();
#else // defined(BOOST_ASIO_NO_DEPRECATED)
return boost_asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1, typename Arg2>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void* pointer, std::size_t size,
move_binder2<Handler, Arg1, Arg2>* this_handler)
{
boost_asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_deallocate_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler, typename Arg1, typename Arg2>
@@ -698,11 +812,15 @@ inline bool asio_handler_is_continuation(
}
template <typename Function, typename Handler, typename Arg1, typename Arg2>
inline void asio_handler_invoke(BOOST_ASIO_MOVE_ARG(Function) function,
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(BOOST_ASIO_MOVE_ARG(Function) function,
move_binder2<Handler, Arg1, Arg2>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
BOOST_ASIO_MOVE_CAST(Function)(function), this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
#endif // defined(BOOST_ASIO_HAS_MOVE)

View File

@@ -0,0 +1,109 @@
//
// detail/blocking_executor_op.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_ASIO_DETAIL_BLOCKING_EXECUTOR_OP_HPP
#define BOOST_ASIO_DETAIL_BLOCKING_EXECUTOR_OP_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/event.hpp>
#include <boost/asio/detail/fenced_block.hpp>
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <boost/asio/detail/mutex.hpp>
#include <boost/asio/detail/scheduler_operation.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace detail {
template <typename Operation = scheduler_operation>
class blocking_executor_op_base : public Operation
{
public:
blocking_executor_op_base(typename Operation::func_type complete_func)
: Operation(complete_func),
is_complete_(false)
{
}
void wait()
{
boost::asio::detail::mutex::scoped_lock lock(mutex_);
while (!is_complete_)
event_.wait(lock);
}
protected:
struct do_complete_cleanup
{
~do_complete_cleanup()
{
boost::asio::detail::mutex::scoped_lock lock(op_->mutex_);
op_->is_complete_ = true;
op_->event_.unlock_and_signal_one_for_destruction(lock);
}
blocking_executor_op_base* op_;
};
private:
boost::asio::detail::mutex mutex_;
boost::asio::detail::event event_;
bool is_complete_;
};
template <typename Handler, typename Operation = scheduler_operation>
class blocking_executor_op : public blocking_executor_op_base<Operation>
{
public:
blocking_executor_op(Handler& h)
: blocking_executor_op_base<Operation>(&blocking_executor_op::do_complete),
handler_(h)
{
}
static void do_complete(void* owner, Operation* base,
const boost::system::error_code& /*ec*/,
std::size_t /*bytes_transferred*/)
{
blocking_executor_op* o(static_cast<blocking_executor_op*>(base));
typename blocking_executor_op_base<Operation>::do_complete_cleanup
on_exit = { o };
(void)on_exit;
BOOST_ASIO_HANDLER_COMPLETION((*o));
// Make the upcall if required.
if (owner)
{
fenced_block b(fenced_block::half);
BOOST_ASIO_HANDLER_INVOCATION_BEGIN(());
boost_asio_handler_invoke_helpers::invoke(o->handler_, o->handler_);
BOOST_ASIO_HANDLER_INVOCATION_END;
}
}
private:
Handler& handler_;
};
} // namespace detail
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_DETAIL_BLOCKING_EXECUTOR_OP_HPP

View File

@@ -105,6 +105,8 @@ class buffer_sequence_adapter
: buffer_sequence_adapter_base
{
public:
enum { is_single_buffer = false };
explicit buffer_sequence_adapter(const Buffers& buffer_sequence)
: count_(0), total_buffer_size_(0)
{
@@ -154,6 +156,16 @@ public:
boost::asio::buffer_sequence_end(buffer_sequence));
}
enum { linearisation_storage_size = 8192 };
static Buffer linearise(const Buffers& buffer_sequence,
const boost::asio::mutable_buffer& storage)
{
return buffer_sequence_adapter::linearise(
boost::asio::buffer_sequence_begin(buffer_sequence),
boost::asio::buffer_sequence_end(buffer_sequence), storage);
}
private:
template <typename Iterator>
void init(Iterator begin, Iterator end)
@@ -202,6 +214,30 @@ private:
return Buffer();
}
template <typename Iterator>
static Buffer linearise(Iterator begin, Iterator end,
const boost::asio::mutable_buffer& storage)
{
boost::asio::mutable_buffer unused_storage = storage;
Iterator iter = begin;
while (iter != end && unused_storage.size() != 0)
{
Buffer buffer(*iter);
++iter;
if (buffer.size() == 0)
continue;
if (unused_storage.size() == storage.size())
{
if (iter == end)
return buffer;
if (buffer.size() >= unused_storage.size())
return buffer;
}
unused_storage += boost::asio::buffer_copy(unused_storage, buffer);
}
return Buffer(storage.data(), storage.size() - unused_storage.size());
}
native_buffer_type buffers_[max_buffers];
std::size_t count_;
std::size_t total_buffer_size_;
@@ -212,6 +248,8 @@ class buffer_sequence_adapter<Buffer, boost::asio::mutable_buffer>
: buffer_sequence_adapter_base
{
public:
enum { is_single_buffer = true };
explicit buffer_sequence_adapter(
const boost::asio::mutable_buffer& buffer_sequence)
{
@@ -254,6 +292,14 @@ public:
return Buffer(buffer_sequence);
}
enum { linearisation_storage_size = 1 };
static Buffer linearise(const boost::asio::mutable_buffer& buffer_sequence,
const Buffer&)
{
return Buffer(buffer_sequence);
}
private:
native_buffer_type buffer_;
std::size_t total_buffer_size_;
@@ -264,6 +310,8 @@ class buffer_sequence_adapter<Buffer, boost::asio::const_buffer>
: buffer_sequence_adapter_base
{
public:
enum { is_single_buffer = true };
explicit buffer_sequence_adapter(
const boost::asio::const_buffer& buffer_sequence)
{
@@ -306,6 +354,14 @@ public:
return Buffer(buffer_sequence);
}
enum { linearisation_storage_size = 1 };
static Buffer linearise(const boost::asio::const_buffer& buffer_sequence,
const Buffer&)
{
return Buffer(buffer_sequence);
}
private:
native_buffer_type buffer_;
std::size_t total_buffer_size_;
@@ -318,6 +374,8 @@ class buffer_sequence_adapter<Buffer, boost::asio::mutable_buffers_1>
: buffer_sequence_adapter_base
{
public:
enum { is_single_buffer = true };
explicit buffer_sequence_adapter(
const boost::asio::mutable_buffers_1& buffer_sequence)
{
@@ -360,6 +418,14 @@ public:
return Buffer(buffer_sequence);
}
enum { linearisation_storage_size = 1 };
static Buffer linearise(const boost::asio::mutable_buffers_1& buffer_sequence,
const Buffer&)
{
return Buffer(buffer_sequence);
}
private:
native_buffer_type buffer_;
std::size_t total_buffer_size_;
@@ -370,6 +436,8 @@ class buffer_sequence_adapter<Buffer, boost::asio::const_buffers_1>
: buffer_sequence_adapter_base
{
public:
enum { is_single_buffer = true };
explicit buffer_sequence_adapter(
const boost::asio::const_buffers_1& buffer_sequence)
{
@@ -412,6 +480,14 @@ public:
return Buffer(buffer_sequence);
}
enum { linearisation_storage_size = 1 };
static Buffer linearise(const boost::asio::const_buffers_1& buffer_sequence,
const Buffer&)
{
return Buffer(buffer_sequence);
}
private:
native_buffer_type buffer_;
std::size_t total_buffer_size_;
@@ -424,6 +500,8 @@ class buffer_sequence_adapter<Buffer, boost::array<Elem, 2> >
: buffer_sequence_adapter_base
{
public:
enum { is_single_buffer = false };
explicit buffer_sequence_adapter(
const boost::array<Elem, 2>& buffer_sequence)
{
@@ -469,6 +547,19 @@ public:
? buffer_sequence[0] : buffer_sequence[1]);
}
enum { linearisation_storage_size = 8192 };
static Buffer linearise(const boost::array<Elem, 2>& buffer_sequence,
const boost::asio::mutable_buffer& storage)
{
if (buffer_sequence[0].size() == 0)
return Buffer(buffer_sequence[1]);
if (buffer_sequence[1].size() == 0)
return Buffer(buffer_sequence[0]);
return Buffer(storage.data(),
boost::asio::buffer_copy(storage, buffer_sequence));
}
private:
native_buffer_type buffers_[2];
std::size_t total_buffer_size_;
@@ -481,6 +572,8 @@ class buffer_sequence_adapter<Buffer, std::array<Elem, 2> >
: buffer_sequence_adapter_base
{
public:
enum { is_single_buffer = false };
explicit buffer_sequence_adapter(
const std::array<Elem, 2>& buffer_sequence)
{
@@ -526,6 +619,19 @@ public:
? buffer_sequence[0] : buffer_sequence[1]);
}
enum { linearisation_storage_size = 8192 };
static Buffer linearise(const std::array<Elem, 2>& buffer_sequence,
const boost::asio::mutable_buffer& storage)
{
if (buffer_sequence[0].size() == 0)
return Buffer(buffer_sequence[1]);
if (buffer_sequence[1].size() == 0)
return Buffer(buffer_sequence[0]);
return Buffer(storage.data(),
boost::asio::buffer_copy(storage, buffer_sequence));
}
private:
native_buffer_type buffers_[2];
std::size_t total_buffer_size_;

View File

@@ -28,17 +28,17 @@ namespace boost {
namespace asio {
namespace detail {
template <typename Handler>
template <typename Handler, typename IoExecutor>
class completion_handler : public operation
{
public:
BOOST_ASIO_DEFINE_HANDLER_PTR(completion_handler);
completion_handler(Handler& h)
completion_handler(Handler& h, const IoExecutor& io_ex)
: operation(&completion_handler::do_complete),
handler_(BOOST_ASIO_MOVE_CAST(Handler)(h))
handler_(BOOST_ASIO_MOVE_CAST(Handler)(h)),
work_(handler_, io_ex)
{
handler_work<Handler>::start(handler_);
}
static void do_complete(void* owner, operation* base,
@@ -48,10 +48,14 @@ public:
// Take ownership of the handler object.
completion_handler* h(static_cast<completion_handler*>(base));
ptr p = { boost::asio::detail::addressof(h->handler_), h, h };
handler_work<Handler> w(h->handler_);
BOOST_ASIO_HANDLER_COMPLETION((*h));
// Take ownership of the operation's outstanding work.
handler_work<Handler, IoExecutor> w(
BOOST_ASIO_MOVE_CAST2(handler_work<Handler, IoExecutor>)(
h->work_));
// Make a copy of the handler so that the memory can be deallocated before
// the upcall is made. Even if we're not about to make an upcall, a
// sub-object of the handler may be the true owner of the memory associated
@@ -74,6 +78,7 @@ public:
private:
Handler handler_;
handler_work<Handler, IoExecutor> work_;
};
} // namespace detail

View File

@@ -65,6 +65,14 @@ public:
event_.unlock_and_signal_one(lock);
}
// Unlock the mutex and signal one waiter who may destroy us.
void unlock_and_signal_one_for_destruction(
conditionally_enabled_mutex::scoped_lock& lock)
{
if (lock.mutex_.enabled_)
event_.unlock_and_signal_one(lock);
}
// If there's a waiter, unlock the mutex and signal it.
bool maybe_unlock_and_signal_one(
conditionally_enabled_mutex::scoped_lock& lock)

View File

@@ -61,6 +61,9 @@
# define BOOST_ASIO_DECL
#endif // !defined(BOOST_ASIO_DECL)
// Helper macro for documentation.
#define BOOST_ASIO_UNSPECIFIED(e) e
// Microsoft Visual C++ detection.
#if !defined(BOOST_ASIO_MSVC)
# if defined(BOOST_ASIO_HAS_BOOST_CONFIG) && defined(BOOST_MSVC)
@@ -180,6 +183,13 @@
# endif // defined(BOOST_ASIO_MSVC)
# endif // !defined(BOOST_ASIO_DISABLE_VARIADIC_TEMPLATES)
#endif // !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
#if !defined(BOOST_ASIO_ELLIPSIS)
# if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
# define BOOST_ASIO_ELLIPSIS ...
# else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
# define BOOST_ASIO_ELLIPSIS
# endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
#endif // !defined(BOOST_ASIO_ELLIPSIS)
// Support deleted functions on compilers known to allow it.
#if !defined(BOOST_ASIO_DELETED)
@@ -234,39 +244,87 @@
# define BOOST_ASIO_CONSTEXPR
# endif // defined(BOOST_ASIO_HAS_CONSTEXPR)
#endif // !defined(BOOST_ASIO_CONSTEXPR)
#if !defined(BOOST_ASIO_STATIC_CONSTEXPR)
# if defined(BOOST_ASIO_HAS_CONSTEXPR)
# define BOOST_ASIO_STATIC_CONSTEXPR(type, assignment) \
static constexpr type assignment
# else // defined(BOOST_ASIO_HAS_CONSTEXPR)
# define BOOST_ASIO_STATIC_CONSTEXPR(type, assignment) \
static const type assignment
# endif // defined(BOOST_ASIO_HAS_CONSTEXPR)
#endif // !defined(BOOST_ASIO_STATIC_CONSTEXPR)
#if !defined(BOOST_ASIO_STATIC_CONSTEXPR_DEFAULT_INIT)
# if defined(BOOST_ASIO_HAS_CONSTEXPR)
# if defined(__GNUC__)
# if (__GNUC__ >= 8)
# define BOOST_ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(type, name) \
static constexpr const type name{}
# else // (__GNUC__ >= 8)
# define BOOST_ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(type, name) \
static const type name
# endif // (__GNUC__ >= 8)
# else // defined(__GNUC__)
# define BOOST_ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(type, name) \
static constexpr const type name{}
# endif // defined(__GNUC__)
# else // defined(BOOST_ASIO_HAS_CONSTEXPR)
# define BOOST_ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(type, name) \
static const type name
# endif // defined(BOOST_ASIO_HAS_CONSTEXPR)
#endif // !defined(BOOST_ASIO_STATIC_CONSTEXPR_DEFAULT_INIT)
// Support noexcept on compilers known to allow it.
#if !defined(BOOST_ASIO_NOEXCEPT)
#if !defined(BOOST_ASIO_HAS_NOEXCEPT)
# if !defined(BOOST_ASIO_DISABLE_NOEXCEPT)
# if defined(BOOST_ASIO_HAS_BOOST_CONFIG) && (BOOST_VERSION >= 105300)
# if !defined(BOOST_NO_NOEXCEPT)
# define BOOST_ASIO_HAS_NOEXCEPT 1
# endif // !defined(BOOST_NO_NOEXCEPT)
# define BOOST_ASIO_NOEXCEPT BOOST_NOEXCEPT
# define BOOST_ASIO_NOEXCEPT_OR_NOTHROW BOOST_NOEXCEPT_OR_NOTHROW
# define BOOST_ASIO_NOEXCEPT_IF(c) BOOST_NOEXCEPT_IF(c)
# elif defined(__clang__)
# if __has_feature(__cxx_noexcept__)
# define BOOST_ASIO_NOEXCEPT noexcept(true)
# define BOOST_ASIO_NOEXCEPT_OR_NOTHROW noexcept(true)
# define BOOST_ASIO_HAS_NOEXCEPT 1
# endif // __has_feature(__cxx_noexcept__)
# elif defined(__GNUC__)
# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__)
# define BOOST_ASIO_NOEXCEPT noexcept(true)
# define BOOST_ASIO_NOEXCEPT_OR_NOTHROW noexcept(true)
# define BOOST_ASIO_HAS_NOEXCEPT 1
# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__)
# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
# elif defined(BOOST_ASIO_MSVC)
# if (_MSC_VER >= 1900)
# define BOOST_ASIO_NOEXCEPT noexcept(true)
# define BOOST_ASIO_NOEXCEPT_OR_NOTHROW noexcept(true)
# define BOOST_ASIO_HAS_NOEXCEPT 1
# endif // (_MSC_VER >= 1900)
# endif // defined(BOOST_ASIO_MSVC)
# endif // !defined(BOOST_ASIO_DISABLE_NOEXCEPT)
# if !defined(BOOST_ASIO_NOEXCEPT)
# define BOOST_ASIO_NOEXCEPT
# endif // !defined(BOOST_ASIO_NOEXCEPT)
# if !defined(BOOST_ASIO_NOEXCEPT_OR_NOTHROW)
# define BOOST_ASIO_NOEXCEPT_OR_NOTHROW throw()
# endif // !defined(BOOST_ASIO_NOEXCEPT_OR_NOTHROW)
#endif // !defined(BOOST_ASIO_HAS_NOEXCEPT)
#if !defined(BOOST_ASIO_NOEXCEPT)
# if defined(BOOST_ASIO_HAS_NOEXCEPT)
# define BOOST_ASIO_NOEXCEPT noexcept(true)
# else // defined(BOOST_ASIO_HAS_NOEXCEPT)
# define BOOST_ASIO_NOEXCEPT
# endif // defined(BOOST_ASIO_HAS_NOEXCEPT)
#endif // !defined(BOOST_ASIO_NOEXCEPT)
#if !defined(BOOST_ASIO_NOEXCEPT_OR_NOTHROW)
# if defined(BOOST_ASIO_HAS_NOEXCEPT)
# define BOOST_ASIO_NOEXCEPT_OR_NOTHROW noexcept(true)
# else // defined(BOOST_ASIO_HAS_NOEXCEPT)
# define BOOST_ASIO_NOEXCEPT_OR_NOTHROW throw()
# endif // defined(BOOST_ASIO_HAS_NOEXCEPT)
#endif // !defined(BOOST_ASIO_NOEXCEPT_OR_NOTHROW)
#if !defined(BOOST_ASIO_NOEXCEPT_IF)
# if defined(BOOST_ASIO_HAS_NOEXCEPT)
# define BOOST_ASIO_NOEXCEPT_IF(c) noexcept(c)
# else // defined(BOOST_ASIO_HAS_NOEXCEPT)
# define BOOST_ASIO_NOEXCEPT_IF(c)
# endif // defined(BOOST_ASIO_HAS_NOEXCEPT)
#endif // !defined(BOOST_ASIO_NOEXCEPT_IF)
// Support automatic type deduction on compilers known to support it.
#if !defined(BOOST_ASIO_HAS_DECLTYPE)
@@ -354,6 +412,113 @@
# endif // !defined(BOOST_ASIO_DISABLE_CONCEPTS)
#endif // !defined(BOOST_ASIO_HAS_CONCEPTS)
// Support template variables on compilers known to allow it.
#if !defined(BOOST_ASIO_HAS_VARIABLE_TEMPLATES)
# if !defined(BOOST_ASIO_DISABLE_VARIABLE_TEMPLATES)
# if defined(__clang__)
# if (__cplusplus >= 201402)
# if __has_feature(__cxx_variable_templates__)
# define BOOST_ASIO_HAS_VARIABLE_TEMPLATES 1
# endif // __has_feature(__cxx_variable_templates__)
# endif // (__cplusplus >= 201703)
# endif // defined(__clang__)
# if defined(__GNUC__)
# if (__GNUC__ >= 5)
# if (__cplusplus >= 201402)
# define BOOST_ASIO_HAS_VARIABLE_TEMPLATES 1
# endif // (__cplusplus >= 201402)
# endif // (__GNUC__ >= 5)
# endif // defined(__GNUC__)
# if defined(BOOST_ASIO_MSVC)
# if (_MSC_VER >= 1901)
# define BOOST_ASIO_HAS_VARIABLE_TEMPLATES 1
# endif // (_MSC_VER >= 1901)
# endif // defined(BOOST_ASIO_MSVC)
# endif // !defined(BOOST_ASIO_DISABLE_VARIABLE_TEMPLATES)
#endif // !defined(BOOST_ASIO_HAS_VARIABLE_TEMPLATES)
// Support SFINAEd template variables on compilers known to allow it.
#if !defined(BOOST_ASIO_HAS_SFINAE_VARIABLE_TEMPLATES)
# if !defined(BOOST_ASIO_DISABLE_SFINAE_VARIABLE_TEMPLATES)
# if defined(__clang__)
# if (__cplusplus >= 201703)
# if __has_feature(__cxx_variable_templates__)
# define BOOST_ASIO_HAS_SFINAE_VARIABLE_TEMPLATES 1
# endif // __has_feature(__cxx_variable_templates__)
# endif // (__cplusplus >= 201703)
# endif // defined(__clang__)
# if defined(__GNUC__)
# if (__GNUC__ >= 7)
# if (__cplusplus >= 201402)
# define BOOST_ASIO_HAS_SFINAE_VARIABLE_TEMPLATES 1
# endif // (__cplusplus >= 201402)
# endif // (__GNUC__ >= 7)
# endif // defined(__GNUC__)
# if defined(BOOST_ASIO_MSVC)
# if (_MSC_VER >= 1901)
# define BOOST_ASIO_HAS_SFINAE_VARIABLE_TEMPLATES 1
# endif // (_MSC_VER >= 1901)
# endif // defined(BOOST_ASIO_MSVC)
# endif // !defined(BOOST_ASIO_DISABLE_SFINAE_VARIABLE_TEMPLATES)
#endif // !defined(BOOST_ASIO_HAS_SFINAE_VARIABLE_TEMPLATES)
// Support SFINAE use of constant expressions on compilers known to allow it.
#if !defined(BOOST_ASIO_HAS_CONSTANT_EXPRESSION_SFINAE)
# if !defined(BOOST_ASIO_DISABLE_CONSTANT_EXPRESSION_SFINAE)
# if defined(__clang__)
# if (__cplusplus >= 201402)
# define BOOST_ASIO_HAS_CONSTANT_EXPRESSION_SFINAE 1
# endif // (__cplusplus >= 201703)
# endif // defined(__clang__)
# if defined(__GNUC__)
# if (__GNUC__ >= 7)
# if (__cplusplus >= 201402)
# define BOOST_ASIO_HAS_CONSTANT_EXPRESSION_SFINAE 1
# endif // (__cplusplus >= 201402)
# endif // (__GNUC__ >= 7)
# endif // defined(__GNUC__)
# if defined(BOOST_ASIO_MSVC)
# if (_MSC_VER >= 1901)
# define BOOST_ASIO_HAS_CONSTANT_EXPRESSION_SFINAE 1
# endif // (_MSC_VER >= 1901)
# endif // defined(BOOST_ASIO_MSVC)
# endif // !defined(BOOST_ASIO_DISABLE_CONSTANT_EXPRESSION_SFINAE)
#endif // !defined(BOOST_ASIO_HAS_CONSTANT_EXPRESSION_SFINAE)
// Enable workarounds for lack of working expression SFINAE.
#if !defined(BOOST_ASIO_HAS_WORKING_EXPRESSION_SFINAE)
# if !defined(BOOST_ASIO_DISABLE_WORKING_EXPRESSION_SFINAE)
# if !defined(BOOST_ASIO_MSVC)
# if (__cplusplus >= 201103)
# define BOOST_ASIO_HAS_WORKING_EXPRESSION_SFINAE 1
# endif // (__cplusplus >= 201103)
# endif // defined(BOOST_ASIO_MSVC)
# endif // !defined(BOOST_ASIO_DISABLE_WORKING_EXPRESSION_SFINAE)
#endif // !defined(BOOST_ASIO_HAS_WORKING_EXPRESSION_SFINAE)
// Support static_assert on compilers known to allow it.
#if !defined(BOOST_ASIO_HAS_STATIC_ASSERT)
# if !defined(BOOST_ASIO_DISABLE_STATIC_ASSERT)
# if defined(__clang__)
# if __has_feature(__cxx_static_assert__)
# define BOOST_ASIO_HAS_STATIC_ASSERT 1
# endif // __has_feature(__cxx_static_assert__)
# endif // defined(__clang__)
# if defined(__GNUC__)
# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__)
# define BOOST_ASIO_HAS_STATIC_ASSERT 1
# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__)
# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
# endif // defined(__GNUC__)
# if defined(BOOST_ASIO_MSVC)
# if (_MSC_VER >= 1900)
# define BOOST_ASIO_HAS_STATIC_ASSERT 1
# endif // (_MSC_VER >= 1900)
# endif // defined(BOOST_ASIO_MSVC)
# endif // !defined(BOOST_ASIO_DISABLE_STATIC_ASSERT)
#endif // !defined(BOOST_ASIO_HAS_STATIC_ASSERT)
// Standard library support for system errors.
# if !defined(BOOST_ASIO_DISABLE_STD_SYSTEM_ERROR)
# if defined(__clang__)
@@ -927,6 +1092,61 @@
# endif // !defined(BOOST_ASIO_DISABLE_STD_INVOKE_RESULT)
#endif // !defined(BOOST_ASIO_HAS_STD_INVOKE_RESULT)
// Standard library support for std::exception_ptr and std::current_exception.
#if !defined(BOOST_ASIO_HAS_STD_EXCEPTION_PTR)
# if !defined(BOOST_ASIO_DISABLE_STD_EXCEPTION_PTR)
# if defined(__clang__)
# if defined(BOOST_ASIO_HAS_CLANG_LIBCXX)
# define BOOST_ASIO_HAS_STD_EXCEPTION_PTR 1
# elif (__cplusplus >= 201103)
# define BOOST_ASIO_HAS_STD_EXCEPTION_PTR 1
# endif // (__cplusplus >= 201103)
# elif defined(__GNUC__)
# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__)
# define BOOST_ASIO_HAS_STD_EXCEPTION_PTR 1
# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__)
# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
# endif // defined(__GNUC__)
# if defined(BOOST_ASIO_MSVC)
# if (_MSC_VER >= 1800)
# define BOOST_ASIO_HAS_STD_EXCEPTION_PTR 1
# endif // (_MSC_VER >= 1800)
# endif // defined(BOOST_ASIO_MSVC)
# endif // !defined(BOOST_ASIO_DISABLE_STD_EXCEPTION_PTR)
#endif // !defined(BOOST_ASIO_HAS_STD_EXCEPTION_PTR)
// Standard library support for std::source_location.
#if !defined(BOOST_ASIO_HAS_STD_SOURCE_LOCATION)
# if !defined(BOOST_ASIO_DISABLE_STD_SOURCE_LOCATION)
// ...
# endif // !defined(BOOST_ASIO_DISABLE_STD_SOURCE_LOCATION)
#endif // !defined(BOOST_ASIO_HAS_STD_SOURCE_LOCATION)
// Standard library support for std::experimental::source_location.
#if !defined(BOOST_ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION)
# if !defined(BOOST_ASIO_DISABLE_STD_EXPERIMENTAL_SOURCE_LOCATION)
# if defined(__GNUC__)
# if (__cplusplus >= 201709)
# if __has_include(<experimental/source_location>)
# define BOOST_ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION 1
# endif // __has_include(<experimental/source_location>)
# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4)
# endif // defined(__GNUC__)
# endif // !defined(BOOST_ASIO_DISABLE_STD_EXPERIMENTAL_SOURCE_LOCATION)
#endif // !defined(BOOST_ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION)
// Standard library has a source_location that we can use.
#if !defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
# if !defined(BOOST_ASIO_DISABLE_SOURCE_LOCATION)
# if defined(BOOST_ASIO_HAS_STD_SOURCE_LOCATION)
# define BOOST_ASIO_HAS_SOURCE_LOCATION 1
# elif defined(BOOST_ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION)
# define BOOST_ASIO_HAS_SOURCE_LOCATION 1
# endif // defined(BOOST_ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION)
# endif // !defined(BOOST_ASIO_DISABLE_SOURCE_LOCATION)
#endif // !defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
// Windows App target. Windows but with a limited API.
#if !defined(BOOST_ASIO_WINDOWS_APP)
# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0603)
@@ -970,17 +1190,17 @@
// Windows: target OS version.
#if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS)
# if defined(_MSC_VER) || defined(__BORLANDC__)
# if defined(_MSC_VER) || (defined(__BORLANDC__) && !defined(__clang__))
# pragma message( \
"Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:\n"\
"- add -D_WIN32_WINNT=0x0601 to the compiler command line; or\n"\
"- add _WIN32_WINNT=0x0601 to your project's Preprocessor Definitions.\n"\
"Assuming _WIN32_WINNT=0x0601 (i.e. Windows 7 target).")
# else // defined(_MSC_VER) || defined(__BORLANDC__)
# else // defined(_MSC_VER) || (defined(__BORLANDC__) && !defined(__clang__))
# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately.
# warning For example, add -D_WIN32_WINNT=0x0601 to the compiler command line.
# warning Assuming _WIN32_WINNT=0x0601 (i.e. Windows 7 target).
# endif // defined(_MSC_VER) || defined(__BORLANDC__)
# endif // defined(_MSC_VER) || (defined(__BORLANDC__) && !defined(__clang__))
# define _WIN32_WINNT 0x0601
# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS)
# if defined(_MSC_VER)
@@ -1379,9 +1599,9 @@
# if (__GNUC__ >= 3)
# define BOOST_ASIO_HAS_HANDLER_HOOKS 1
# endif // (__GNUC__ >= 3)
# elif !defined(__BORLANDC__)
# elif !defined(__BORLANDC__) || defined(__clang__)
# define BOOST_ASIO_HAS_HANDLER_HOOKS 1
# endif // !defined(__BORLANDC__)
# endif // !defined(__BORLANDC__) || defined(__clang__)
# endif // !defined(BOOST_ASIO_DISABLE_HANDLER_HOOKS)
#endif // !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
@@ -1460,7 +1680,7 @@
# define BOOST_ASIO_UNUSED_VARIABLE
#endif // !defined(BOOST_ASIO_UNUSED_VARIABLE)
// Support co_await on compilers known to allow it.
// Support the co_await keyword on compilers known to allow it.
#if !defined(BOOST_ASIO_HAS_CO_AWAIT)
# if !defined(BOOST_ASIO_DISABLE_CO_AWAIT)
# if defined(BOOST_ASIO_MSVC)
@@ -1470,14 +1690,33 @@
# endif // defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
# endif // (_MSC_FULL_VER >= 190023506)
# endif // defined(BOOST_ASIO_MSVC)
# if defined(__clang__)
# if (__cplusplus >= 201703) && (__cpp_coroutines >= 201703)
# if __has_include(<experimental/coroutine>)
# define BOOST_ASIO_HAS_CO_AWAIT 1
# endif // __has_include(<experimental/coroutine>)
# endif // (__cplusplus >= 201703) && (__cpp_coroutines >= 201703)
# elif defined(__GNUC__)
# if (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902)
# if __has_include(<coroutine>)
# define BOOST_ASIO_HAS_CO_AWAIT 1
# endif // __has_include(<coroutine>)
# endif // (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902)
# endif // defined(__GNUC__)
# endif // !defined(BOOST_ASIO_DISABLE_CO_AWAIT)
# if defined(__clang__)
# if (__cplusplus >= 201703) && (__cpp_coroutines >= 201703)
# if __has_include(<experimental/coroutine>)
# define BOOST_ASIO_HAS_CO_AWAIT 1
# endif // __has_include(<experimental/coroutine>)
# endif // (__cplusplus >= 201703) && (__cpp_coroutines >= 201703)
# endif // defined(__clang__)
#endif // !defined(BOOST_ASIO_HAS_CO_AWAIT)
// Standard library support for coroutines.
#if !defined(BOOST_ASIO_HAS_STD_COROUTINE)
# if !defined(BOOST_ASIO_DISABLE_STD_COROUTINE)
# if defined(__GNUC__)
# if (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902)
# if __has_include(<coroutine>)
# define BOOST_ASIO_HAS_STD_COROUTINE 1
# endif // __has_include(<coroutine>)
# endif // (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902)
# endif // defined(__GNUC__)
# endif // !defined(BOOST_ASIO_DISABLE_STD_COROUTINE)
#endif // !defined(BOOST_ASIO_HAS_STD_COROUTINE)
#endif // BOOST_ASIO_DETAIL_CONFIG_HPP

View File

@@ -98,7 +98,7 @@ public:
cancel(impl, ec);
}
// Move-construct a new serial port implementation.
// Move-construct a new timer implementation.
void move_construct(implementation_type& impl,
implementation_type& other_impl)
{
@@ -111,7 +111,7 @@ public:
other_impl.might_have_pending_waits = false;
}
// Move-assign from another serial port implementation.
// Move-assign from another timer implementation.
void move_assign(implementation_type& impl,
deadline_timer_service& other_service,
implementation_type& other_impl)
@@ -130,6 +130,21 @@ public:
other_impl.might_have_pending_waits = false;
}
// Move-construct a new timer implementation.
void converting_move_construct(implementation_type& impl,
deadline_timer_service&, implementation_type& other_impl)
{
move_construct(impl, other_impl);
}
// Move-assign from another timer implementation.
void converting_move_assign(implementation_type& impl,
deadline_timer_service& other_service,
implementation_type& other_impl)
{
move_assign(impl, other_service, other_impl);
}
// Cancel any asynchronous wait operations associated with the timer.
std::size_t cancel(implementation_type& impl, boost::system::error_code& ec)
{

View File

@@ -51,13 +51,18 @@ enum
typedef unsigned char state_type;
template <typename ReturnType>
inline ReturnType error_wrapper(ReturnType return_value,
boost::system::error_code& ec)
inline void get_last_error(
boost::system::error_code& ec, bool is_error_condition)
{
ec = boost::system::error_code(errno,
boost::asio::error::get_system_category());
return return_value;
if (!is_error_condition)
{
ec.assign(0, ec.category());
}
else
{
ec = boost::system::error_code(errno,
boost::asio::error::get_system_category());
}
}
BOOST_ASIO_DECL int open(const char* path, int flags,
@@ -77,17 +82,30 @@ typedef iovec buf;
BOOST_ASIO_DECL std::size_t sync_read(int d, state_type state, buf* bufs,
std::size_t count, bool all_empty, boost::system::error_code& ec);
BOOST_ASIO_DECL std::size_t sync_read1(int d, state_type state, void* data,
std::size_t size, boost::system::error_code& ec);
BOOST_ASIO_DECL bool non_blocking_read(int d, buf* bufs, std::size_t count,
boost::system::error_code& ec, std::size_t& bytes_transferred);
BOOST_ASIO_DECL bool non_blocking_read1(int d, void* data, std::size_t size,
boost::system::error_code& ec, std::size_t& bytes_transferred);
BOOST_ASIO_DECL std::size_t sync_write(int d, state_type state,
const buf* bufs, std::size_t count, bool all_empty,
boost::system::error_code& ec);
BOOST_ASIO_DECL std::size_t sync_write1(int d, state_type state,
const void* data, std::size_t size, boost::system::error_code& ec);
BOOST_ASIO_DECL bool non_blocking_write(int d,
const buf* bufs, std::size_t count,
boost::system::error_code& ec, std::size_t& bytes_transferred);
BOOST_ASIO_DECL bool non_blocking_write1(int d,
const void* data, std::size_t size,
boost::system::error_code& ec, std::size_t& bytes_transferred);
BOOST_ASIO_DECL int ioctl(int d, state_type& state, long cmd,
ioctl_arg_type* arg, boost::system::error_code& ec);

View File

@@ -37,9 +37,11 @@ template <typename MutableBufferSequence>
class descriptor_read_op_base : public reactor_op
{
public:
descriptor_read_op_base(int descriptor,
const MutableBufferSequence& buffers, func_type complete_func)
: reactor_op(&descriptor_read_op_base::do_perform, complete_func),
descriptor_read_op_base(const boost::system::error_code& success_ec,
int descriptor, const MutableBufferSequence& buffers,
func_type complete_func)
: reactor_op(success_ec,
&descriptor_read_op_base::do_perform, complete_func),
descriptor_(descriptor),
buffers_(buffers)
{
@@ -49,12 +51,24 @@ public:
{
descriptor_read_op_base* o(static_cast<descriptor_read_op_base*>(base));
buffer_sequence_adapter<boost::asio::mutable_buffer,
MutableBufferSequence> bufs(o->buffers_);
typedef buffer_sequence_adapter<boost::asio::mutable_buffer,
MutableBufferSequence> bufs_type;
status result = descriptor_ops::non_blocking_read(o->descriptor_,
bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_)
? done : not_done;
status result;
if (bufs_type::is_single_buffer)
{
result = descriptor_ops::non_blocking_read1(o->descriptor_,
bufs_type::first(o->buffers_).data(),
bufs_type::first(o->buffers_).size(),
o->ec_, o->bytes_transferred_) ? done : not_done;
}
else
{
bufs_type bufs(o->buffers_);
result = descriptor_ops::non_blocking_read(o->descriptor_,
bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_)
? done : not_done;
}
BOOST_ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_read",
o->ec_, o->bytes_transferred_));
@@ -74,14 +88,14 @@ class descriptor_read_op
public:
BOOST_ASIO_DEFINE_HANDLER_PTR(descriptor_read_op);
descriptor_read_op(int descriptor, const MutableBufferSequence& buffers,
descriptor_read_op(const boost::system::error_code& success_ec,
int descriptor, const MutableBufferSequence& buffers,
Handler& handler, const IoExecutor& io_ex)
: descriptor_read_op_base<MutableBufferSequence>(
: descriptor_read_op_base<MutableBufferSequence>(success_ec,
descriptor, buffers, &descriptor_read_op::do_complete),
handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler)),
io_executor_(io_ex)
work_(handler_, io_ex)
{
handler_work<Handler, IoExecutor>::start(handler_, io_executor_);
}
static void do_complete(void* owner, operation* base,
@@ -91,10 +105,14 @@ public:
// Take ownership of the handler object.
descriptor_read_op* o(static_cast<descriptor_read_op*>(base));
ptr p = { boost::asio::detail::addressof(o->handler_), o, o };
handler_work<Handler, IoExecutor> w(o->handler_, o->io_executor_);
BOOST_ASIO_HANDLER_COMPLETION((*o));
// Take ownership of the operation's outstanding work.
handler_work<Handler, IoExecutor> w(
BOOST_ASIO_MOVE_CAST2(handler_work<Handler, IoExecutor>)(
o->work_));
// Make a copy of the handler so that the memory can be deallocated before
// the upcall is made. Even if we're not about to make an upcall, a
// sub-object of the handler may be the true owner of the memory associated
@@ -118,7 +136,7 @@ public:
private:
Handler handler_;
IoExecutor io_executor_;
handler_work<Handler, IoExecutor> work_;
};
} // namespace detail

View File

@@ -37,9 +37,11 @@ template <typename ConstBufferSequence>
class descriptor_write_op_base : public reactor_op
{
public:
descriptor_write_op_base(int descriptor,
const ConstBufferSequence& buffers, func_type complete_func)
: reactor_op(&descriptor_write_op_base::do_perform, complete_func),
descriptor_write_op_base(const boost::system::error_code& success_ec,
int descriptor, const ConstBufferSequence& buffers,
func_type complete_func)
: reactor_op(success_ec,
&descriptor_write_op_base::do_perform, complete_func),
descriptor_(descriptor),
buffers_(buffers)
{
@@ -49,12 +51,24 @@ public:
{
descriptor_write_op_base* o(static_cast<descriptor_write_op_base*>(base));
buffer_sequence_adapter<boost::asio::const_buffer,
ConstBufferSequence> bufs(o->buffers_);
typedef buffer_sequence_adapter<boost::asio::const_buffer,
ConstBufferSequence> bufs_type;
status result = descriptor_ops::non_blocking_write(o->descriptor_,
bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_)
? done : not_done;
status result;
if (bufs_type::is_single_buffer)
{
result = descriptor_ops::non_blocking_write1(o->descriptor_,
bufs_type::first(o->buffers_).data(),
bufs_type::first(o->buffers_).size(),
o->ec_, o->bytes_transferred_) ? done : not_done;
}
else
{
bufs_type bufs(o->buffers_);
result = descriptor_ops::non_blocking_write(o->descriptor_,
bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_)
? done : not_done;
}
BOOST_ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_write",
o->ec_, o->bytes_transferred_));
@@ -74,14 +88,14 @@ class descriptor_write_op
public:
BOOST_ASIO_DEFINE_HANDLER_PTR(descriptor_write_op);
descriptor_write_op(int descriptor, const ConstBufferSequence& buffers,
descriptor_write_op(const boost::system::error_code& success_ec,
int descriptor, const ConstBufferSequence& buffers,
Handler& handler, const IoExecutor& io_ex)
: descriptor_write_op_base<ConstBufferSequence>(
: descriptor_write_op_base<ConstBufferSequence>(success_ec,
descriptor, buffers, &descriptor_write_op::do_complete),
handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler)),
io_executor_(io_ex)
work_(handler_, io_ex)
{
handler_work<Handler, IoExecutor>::start(handler_, io_executor_);
}
static void do_complete(void* owner, operation* base,
@@ -91,10 +105,14 @@ public:
// Take ownership of the handler object.
descriptor_write_op* o(static_cast<descriptor_write_op*>(base));
ptr p = { boost::asio::detail::addressof(o->handler_), o, o };
handler_work<Handler, IoExecutor> w(o->handler_, o->io_executor_);
BOOST_ASIO_HANDLER_COMPLETION((*o));
// Take ownership of the operation's outstanding work.
handler_work<Handler, IoExecutor> w(
BOOST_ASIO_MOVE_CAST2(handler_work<Handler, IoExecutor>)(
o->work_));
// Make a copy of the handler so that the memory can be deallocated before
// the upcall is made. Even if we're not about to make an upcall, a
// sub-object of the handler may be the true owner of the memory associated
@@ -118,7 +136,7 @@ public:
private:
Handler handler_;
IoExecutor io_executor_;
handler_work<Handler, IoExecutor> work_;
};
} // namespace detail

View File

@@ -17,6 +17,7 @@
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/asio/detail/memory.hpp>
#include <boost/asio/detail/push_options.hpp>
@@ -24,57 +25,80 @@ namespace boost {
namespace asio {
namespace detail {
class executor_function_base
#if defined(BOOST_ASIO_HAS_MOVE)
// Lightweight, move-only function object wrapper.
class executor_function
{
public:
void complete()
template <typename F, typename Alloc>
explicit executor_function(F f, const Alloc& a)
{
func_(this, true);
// Allocate and construct an object to wrap the function.
typedef impl<F, Alloc> impl_type;
typename impl_type::ptr p = {
detail::addressof(a), impl_type::ptr::allocate(a), 0 };
impl_ = new (p.v) impl_type(BOOST_ASIO_MOVE_CAST(F)(f), a);
p.v = 0;
}
void destroy()
executor_function(executor_function&& other) BOOST_ASIO_NOEXCEPT
: impl_(other.impl_)
{
func_(this, false);
other.impl_ = 0;
}
protected:
typedef void (*func_type)(executor_function_base*, bool);
executor_function_base(func_type func)
: func_(func)
~executor_function()
{
if (impl_)
impl_->complete_(impl_, false);
}
// Prevents deletion through this type.
~executor_function_base()
void operator()()
{
if (impl_)
{
impl_base* i = impl_;
impl_ = 0;
i->complete_(i, true);
}
}
private:
func_type func_;
};
template <typename Function, typename Alloc>
class executor_function : public executor_function_base
{
public:
BOOST_ASIO_DEFINE_TAGGED_HANDLER_ALLOCATOR_PTR(
thread_info_base::executor_function_tag, executor_function);
template <typename F>
executor_function(BOOST_ASIO_MOVE_ARG(F) f, const Alloc& allocator)
: executor_function_base(&executor_function::do_complete),
function_(BOOST_ASIO_MOVE_CAST(F)(f)),
allocator_(allocator)
// Base class for polymorphic function implementations.
struct impl_base
{
}
void (*complete_)(impl_base*, bool);
};
static void do_complete(executor_function_base* base, bool call)
// Polymorphic function implementation.
template <typename Function, typename Alloc>
struct impl : impl_base
{
BOOST_ASIO_DEFINE_TAGGED_HANDLER_ALLOCATOR_PTR(
thread_info_base::executor_function_tag, impl);
template <typename F>
impl(BOOST_ASIO_MOVE_ARG(F) f, const Alloc& a)
: function_(BOOST_ASIO_MOVE_CAST(F)(f)),
allocator_(a)
{
complete_ = &executor_function::complete<Function, Alloc>;
}
Function function_;
Alloc allocator_;
};
// Helper to complete function invocation.
template <typename Function, typename Alloc>
static void complete(impl_base* base, bool call)
{
// Take ownership of the function object.
executor_function* o(static_cast<executor_function*>(base));
Alloc allocator(o->allocator_);
ptr p = { detail::addressof(allocator), o, o };
impl<Function, Alloc>* i(static_cast<impl<Function, Alloc>*>(base));
Alloc allocator(i->allocator_);
typename impl<Function, Alloc>::ptr p = {
detail::addressof(allocator), i, i };
// Make a copy of the function so that the memory can be deallocated before
// the upcall is made. Even if we're not about to make an upcall, a
@@ -82,7 +106,7 @@ public:
// associated with the function. Consequently, a local copy of the function
// is required to ensure that any owning sub-object remains valid until
// after we have deallocated the memory here.
Function function(BOOST_ASIO_MOVE_CAST(Function)(o->function_));
Function function(BOOST_ASIO_MOVE_CAST(Function)(i->function_));
p.reset();
// Make the upcall if required.
@@ -92,9 +116,84 @@ public:
}
}
impl_base* impl_;
};
#else // defined(BOOST_ASIO_HAS_MOVE)
// Not so lightweight, copyable function object wrapper.
class executor_function
{
public:
template <typename F, typename Alloc>
explicit executor_function(const F& f, const Alloc&)
: impl_(new impl<typename decay<F>::type>(f))
{
}
void operator()()
{
impl_->complete_(impl_.get());
}
private:
Function function_;
Alloc allocator_;
// Base class for polymorphic function implementations.
struct impl_base
{
void (*complete_)(impl_base*);
};
// Polymorphic function implementation.
template <typename F>
struct impl : impl_base
{
impl(const F& f)
: function_(f)
{
complete_ = &executor_function::complete<F>;
}
F function_;
};
// Helper to complete function invocation.
template <typename F>
static void complete(impl_base* i)
{
static_cast<impl<F>*>(i)->function_();
}
shared_ptr<impl_base> impl_;
};
#endif // defined(BOOST_ASIO_HAS_MOVE)
// Lightweight, non-owning, copyable function object wrapper.
class executor_function_view
{
public:
template <typename F>
explicit executor_function_view(F& f) BOOST_ASIO_NOEXCEPT
: complete_(&executor_function_view::complete<F>),
function_(&f)
{
}
void operator()()
{
complete_(function_);
}
private:
// Helper to complete function invocation.
template <typename F>
static void complete(void* f)
{
(*static_cast<F*>(f))();
}
void (*complete_)(void*);
void* function_;
};
} // namespace detail

View File

@@ -19,6 +19,7 @@
#include <boost/asio/detail/memory.hpp>
#include <boost/asio/detail/noncopyable.hpp>
#include <boost/asio/detail/recycling_allocator.hpp>
#include <boost/asio/detail/thread_info_base.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
@@ -29,11 +30,41 @@
// boost_asio_handler_alloc_helpers namespace is defined here for that purpose.
namespace boost_asio_handler_alloc_helpers {
#if defined(BOOST_ASIO_NO_DEPRECATED)
template <typename Handler>
inline void error_if_hooks_are_defined(Handler& h)
{
using boost::asio::asio_handler_allocate;
// If you get an error here it is because some of your handlers still
// overload asio_handler_allocate, but this hook is no longer used.
(void)static_cast<boost::asio::asio_handler_allocate_is_no_longer_used>(
asio_handler_allocate(static_cast<std::size_t>(0),
boost::asio::detail::addressof(h)));
using boost::asio::asio_handler_deallocate;
// If you get an error here it is because some of your handlers still
// overload asio_handler_deallocate, but this hook is no longer used.
(void)static_cast<boost::asio::asio_handler_deallocate_is_no_longer_used>(
asio_handler_deallocate(static_cast<void*>(0),
static_cast<std::size_t>(0), boost::asio::detail::addressof(h)));
}
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
template <typename Handler>
inline void* allocate(std::size_t s, Handler& h)
{
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
return ::operator new(s);
#elif defined(BOOST_ASIO_NO_DEPRECATED)
// The asio_handler_allocate hook is no longer used to obtain memory.
(void)&error_if_hooks_are_defined<Handler>;
(void)h;
#if !defined(BOOST_ASIO_DISABLE_SMALL_BLOCK_RECYCLING)
return boost::asio::detail::thread_info_base::allocate(
boost::asio::detail::thread_context::thread_call_stack::top(), s);
#else // !defined(BOOST_ASIO_DISABLE_SMALL_BLOCK_RECYCLING)
return ::operator new(size);
#endif // !defined(BOOST_ASIO_DISABLE_SMALL_BLOCK_RECYCLING)
#else
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(s, boost::asio::detail::addressof(h));
@@ -45,6 +76,17 @@ inline void deallocate(void* p, std::size_t s, Handler& h)
{
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
::operator delete(p);
#elif defined(BOOST_ASIO_NO_DEPRECATED)
// The asio_handler_allocate hook is no longer used to obtain memory.
(void)&error_if_hooks_are_defined<Handler>;
(void)h;
#if !defined(BOOST_ASIO_DISABLE_SMALL_BLOCK_RECYCLING)
boost::asio::detail::thread_info_base::deallocate(
boost::asio::detail::thread_context::thread_call_stack::top(), p, s);
#else // !defined(BOOST_ASIO_DISABLE_SMALL_BLOCK_RECYCLING)
(void)s;
::operator delete(p);
#endif // !defined(BOOST_ASIO_DISABLE_SMALL_BLOCK_RECYCLING)
#else
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(p, s, boost::asio::detail::addressof(h));

View File

@@ -26,12 +26,29 @@
// namespace is defined here for that purpose.
namespace boost_asio_handler_invoke_helpers {
#if defined(BOOST_ASIO_NO_DEPRECATED)
template <typename Function, typename Context>
inline void error_if_hook_is_defined(Function& function, Context& context)
{
using boost::asio::asio_handler_invoke;
// If you get an error here it is because some of your handlers still
// overload asio_handler_invoke, but this hook is no longer used.
(void)static_cast<boost::asio::asio_handler_invoke_is_no_longer_used>(
asio_handler_invoke(function, boost::asio::detail::addressof(context)));
}
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
template <typename Function, typename Context>
inline void invoke(Function& function, Context& context)
{
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
Function tmp(function);
tmp();
#elif defined(BOOST_ASIO_NO_DEPRECATED)
// The asio_handler_invoke hook is no longer used to invoke the function.
(void)&error_if_hook_is_defined<Function, Context>;
(void)context;
function();
#else
using boost::asio::asio_handler_invoke;
asio_handler_invoke(function, boost::asio::detail::addressof(context));
@@ -44,6 +61,12 @@ inline void invoke(const Function& function, Context& context)
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
Function tmp(function);
tmp();
#elif defined(BOOST_ASIO_NO_DEPRECATED)
// The asio_handler_invoke hook is no longer used to invoke the function.
(void)&error_if_hook_is_defined<const Function, Context>;
(void)context;
Function tmp(function);
tmp();
#else
using boost::asio::asio_handler_invoke;
asio_handler_invoke(function, boost::asio::detail::addressof(context));

View File

@@ -90,6 +90,28 @@ public:
// Initialise the tracking system.
BOOST_ASIO_DECL static void init();
class location
{
public:
// Constructor adds a location to the stack.
BOOST_ASIO_DECL explicit location(const char* file,
int line, const char* func);
// Destructor removes a location from the stack.
BOOST_ASIO_DECL ~location();
private:
// Disallow copying and assignment.
location(const location&) BOOST_ASIO_DELETED;
location& operator=(const location&) BOOST_ASIO_DELETED;
friend class handler_tracking;
const char* file_;
int line_;
const char* func_;
location* next_;
};
// Record the creation of a tracked handler.
BOOST_ASIO_DECL static void creation(
execution_context& context, tracked_handler& h,
@@ -178,6 +200,9 @@ private:
# define BOOST_ASIO_HANDLER_TRACKING_INIT \
boost::asio::detail::handler_tracking::init()
# define BOOST_ASIO_HANDLER_LOCATION(args) \
boost::asio::detail::handler_tracking::location tracked_location args
# define BOOST_ASIO_HANDLER_CREATION(args) \
boost::asio::detail::handler_tracking::creation args
@@ -214,6 +239,7 @@ private:
# define BOOST_ASIO_INHERIT_TRACKED_HANDLER
# define BOOST_ASIO_ALSO_INHERIT_TRACKED_HANDLER
# define BOOST_ASIO_HANDLER_TRACKING_INIT (void)0
# define BOOST_ASIO_HANDLER_LOCATION(loc) (void)0
# define BOOST_ASIO_HANDLER_CREATION(args) (void)0
# define BOOST_ASIO_HANDLER_COMPLETION(args) (void)0
# define BOOST_ASIO_HANDLER_INVOCATION_BEGIN(args) (void)0

View File

@@ -18,92 +18,277 @@
#include <boost/asio/detail/config.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/execution/allocator.hpp>
#include <boost/asio/execution/blocking.hpp>
#include <boost/asio/execution/execute.hpp>
#include <boost/asio/execution/executor.hpp>
#include <boost/asio/execution/outstanding_work.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/prefer.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
class executor;
class io_context;
namespace detail {
// A helper class template to allow completion handlers to be dispatched
// through either the new executors framework or the old invocaton hook. The
// primary template uses the new executors framework.
template <typename Handler,
typename IoExecutor = system_executor, typename HandlerExecutor
= typename associated_executor<Handler, IoExecutor>::type>
class handler_work
template <typename Executor, typename CandidateExecutor = void,
typename IoContext = io_context,
typename PolymorphicExecutor = executor, typename = void>
class handler_work_base
{
public:
explicit handler_work(Handler& handler) BOOST_ASIO_NOEXCEPT
: io_executor_(),
executor_(boost::asio::get_associated_executor(handler, io_executor_))
explicit handler_work_base(const Executor& ex) BOOST_ASIO_NOEXCEPT
: executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
{
}
handler_work(Handler& handler, const IoExecutor& io_ex) BOOST_ASIO_NOEXCEPT
: io_executor_(io_ex),
executor_(boost::asio::get_associated_executor(handler, io_executor_))
template <typename OtherExecutor>
handler_work_base(const Executor& ex,
const OtherExecutor&) BOOST_ASIO_NOEXCEPT
: executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
{
}
static void start(Handler& handler) BOOST_ASIO_NOEXCEPT
handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT
: executor_(other.executor_)
{
HandlerExecutor ex(boost::asio::get_associated_executor(handler));
ex.on_work_started();
}
static void start(Handler& handler,
const IoExecutor& io_ex) BOOST_ASIO_NOEXCEPT
#if defined(BOOST_ASIO_HAS_MOVE)
handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT
: executor_(BOOST_ASIO_MOVE_CAST(executor_type)(other.executor_))
{
HandlerExecutor ex(boost::asio::get_associated_executor(handler, io_ex));
ex.on_work_started();
io_ex.on_work_started();
}
#endif // defined(BOOST_ASIO_HAS_MOVE)
bool owns_work() const BOOST_ASIO_NOEXCEPT
{
return true;
}
~handler_work()
template <typename Function, typename Handler>
void dispatch(Function& function, Handler& handler)
{
io_executor_.on_work_finished();
executor_.on_work_finished();
execution::execute(
boost::asio::prefer(executor_,
execution::blocking.possibly,
execution::allocator((get_associated_allocator)(handler))),
BOOST_ASIO_MOVE_CAST(Function)(function));
}
template <typename Function>
void complete(Function& function, Handler& handler)
private:
typedef typename decay<
typename prefer_result_type<Executor,
execution::outstanding_work_t::tracked_t
>::type
>::type executor_type;
executor_type executor_;
};
template <typename Executor, typename CandidateExecutor,
typename IoContext, typename PolymorphicExecutor>
class handler_work_base<Executor, CandidateExecutor,
IoContext, PolymorphicExecutor,
typename enable_if<
!execution::is_executor<Executor>::value
&& (!is_same<Executor, PolymorphicExecutor>::value
|| !is_same<CandidateExecutor, void>::value)
>::type>
{
public:
explicit handler_work_base(const Executor& ex) BOOST_ASIO_NOEXCEPT
: executor_(ex),
owns_work_(true)
{
executor_.on_work_started();
}
handler_work_base(const Executor& ex,
const Executor& candidate) BOOST_ASIO_NOEXCEPT
: executor_(ex),
owns_work_(ex != candidate)
{
if (owns_work_)
executor_.on_work_started();
}
template <typename OtherExecutor>
handler_work_base(const Executor& ex,
const OtherExecutor&) BOOST_ASIO_NOEXCEPT
: executor_(ex),
owns_work_(true)
{
executor_.on_work_started();
}
handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT
: executor_(other.executor_),
owns_work_(other.owns_work_)
{
if (owns_work_)
executor_.on_work_started();
}
#if defined(BOOST_ASIO_HAS_MOVE)
handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT
: executor_(BOOST_ASIO_MOVE_CAST(Executor)(other.executor_)),
owns_work_(other.owns_work_)
{
other.owns_work_ = false;
}
#endif // defined(BOOST_ASIO_HAS_MOVE)
~handler_work_base()
{
if (owns_work_)
executor_.on_work_finished();
}
bool owns_work() const BOOST_ASIO_NOEXCEPT
{
return owns_work_;
}
template <typename Function, typename Handler>
void dispatch(Function& function, Handler& handler)
{
executor_.dispatch(BOOST_ASIO_MOVE_CAST(Function)(function),
boost::asio::get_associated_allocator(handler));
}
private:
// Disallow copying and assignment.
handler_work(const handler_work&);
handler_work& operator=(const handler_work&);
IoExecutor io_executor_;
HandlerExecutor executor_;
Executor executor_;
bool owns_work_;
};
// This specialisation dispatches a handler through the old invocation hook.
// The specialisation is not strictly required for correctness, as the
// system_executor will dispatch through the hook anyway. However, by doing
// this we avoid an extra copy of the handler.
template <typename Handler>
class handler_work<Handler, system_executor, system_executor>
template <typename Executor, typename IoContext, typename PolymorphicExecutor>
class handler_work_base<Executor, void, IoContext, PolymorphicExecutor,
typename enable_if<
is_same<
Executor,
typename IoContext::executor_type
>::value
>::type>
{
public:
explicit handler_work(Handler&) BOOST_ASIO_NOEXCEPT {}
static void start(Handler&) BOOST_ASIO_NOEXCEPT {}
~handler_work() {}
explicit handler_work_base(const Executor&)
{
}
bool owns_work() const BOOST_ASIO_NOEXCEPT
{
return false;
}
};
template <typename Executor, typename IoContext>
class handler_work_base<Executor, void, IoContext, Executor>
{
public:
explicit handler_work_base(const Executor& ex) BOOST_ASIO_NOEXCEPT
#if !defined(BOOST_ASIO_NO_TYPEID)
: executor_(
ex.target_type() == typeid(typename IoContext::executor_type)
? Executor() : ex)
#else // !defined(BOOST_ASIO_NO_TYPEID)
: executor_(ex)
#endif // !defined(BOOST_ASIO_NO_TYPEID)
{
if (executor_)
executor_.on_work_started();
}
handler_work_base(const Executor& ex,
const Executor& candidate) BOOST_ASIO_NOEXCEPT
: executor_(ex != candidate ? ex : Executor())
{
if (executor_)
executor_.on_work_started();
}
template <typename OtherExecutor>
handler_work_base(const Executor& ex,
const OtherExecutor&) BOOST_ASIO_NOEXCEPT
: executor_(ex)
{
executor_.on_work_started();
}
handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT
: executor_(other.executor_)
{
if (executor_)
executor_.on_work_started();
}
#if defined(BOOST_ASIO_HAS_MOVE)
handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT
: executor_(BOOST_ASIO_MOVE_CAST(Executor)(other.executor_))
{
}
#endif // defined(BOOST_ASIO_HAS_MOVE)
~handler_work_base()
{
if (executor_)
executor_.on_work_finished();
}
bool owns_work() const BOOST_ASIO_NOEXCEPT
{
return !!executor_;
}
template <typename Function, typename Handler>
void dispatch(Function& function, Handler& handler)
{
executor_.dispatch(BOOST_ASIO_MOVE_CAST(Function)(function),
boost::asio::get_associated_allocator(handler));
}
private:
Executor executor_;
};
template <typename Handler, typename IoExecutor,
typename HandlerExecutor =
typename associated_executor<Handler, IoExecutor>::type>
class handler_work :
handler_work_base<IoExecutor>,
handler_work_base<HandlerExecutor, IoExecutor>
{
public:
handler_work(Handler& handler, const IoExecutor& io_ex) BOOST_ASIO_NOEXCEPT
: handler_work_base<IoExecutor>(io_ex),
handler_work_base<HandlerExecutor, IoExecutor>(
boost::asio::get_associated_executor(handler, io_ex), io_ex)
{
}
template <typename Function>
void complete(Function& function, Handler& handler)
{
boost_asio_handler_invoke_helpers::invoke(function, handler);
if (!handler_work_base<IoExecutor>::owns_work()
&& !handler_work_base<HandlerExecutor, IoExecutor>::owns_work())
{
// When using a native implementation, I/O completion handlers are
// already dispatched according to the execution context's executor's
// rules. We can call the function directly.
boost_asio_handler_invoke_helpers::invoke(function, handler);
}
else
{
handler_work_base<HandlerExecutor,
IoExecutor>::dispatch(function, handler);
}
}
private:
// Disallow copying and assignment.
handler_work(const handler_work&);
handler_work& operator=(const handler_work&);
};
} // namespace detail

View File

@@ -33,10 +33,8 @@ namespace descriptor_ops {
int open(const char* path, int flags, boost::system::error_code& ec)
{
errno = 0;
int result = error_wrapper(::open(path, flags), ec);
if (result >= 0)
ec = boost::system::error_code();
int result = ::open(path, flags);
get_last_error(ec, result < 0);
return result;
}
@@ -45,8 +43,8 @@ int close(int d, state_type& state, boost::system::error_code& ec)
int result = 0;
if (d != -1)
{
errno = 0;
result = error_wrapper(::close(d), ec);
result = ::close(d);
get_last_error(ec, result < 0);
if (result != 0
&& (ec == boost::asio::error::would_block
@@ -68,13 +66,11 @@ int close(int d, state_type& state, boost::system::error_code& ec)
#endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__)
state &= ~non_blocking;
errno = 0;
result = error_wrapper(::close(d), ec);
result = ::close(d);
get_last_error(ec, result < 0);
}
}
if (result == 0)
ec = boost::system::error_code();
return result;
}
@@ -87,23 +83,23 @@ bool set_user_non_blocking(int d, state_type& state,
return false;
}
errno = 0;
#if defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__)
int result = error_wrapper(::fcntl(d, F_GETFL, 0), ec);
int result = ::fcntl(d, F_GETFL, 0);
get_last_error(ec, result < 0);
if (result >= 0)
{
errno = 0;
int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK));
result = error_wrapper(::fcntl(d, F_SETFL, flag), ec);
result = ::fcntl(d, F_SETFL, flag);
get_last_error(ec, result < 0);
}
#else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__)
ioctl_arg_type arg = (value ? 1 : 0);
int result = error_wrapper(::ioctl(d, FIONBIO, &arg), ec);
int result = ::ioctl(d, FIONBIO, &arg);
get_last_error(ec, result < 0);
#endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__)
if (result >= 0)
{
ec = boost::system::error_code();
if (value)
state |= user_set_non_blocking;
else
@@ -137,23 +133,23 @@ bool set_internal_non_blocking(int d, state_type& state,
return false;
}
errno = 0;
#if defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__)
int result = error_wrapper(::fcntl(d, F_GETFL, 0), ec);
int result = ::fcntl(d, F_GETFL, 0);
get_last_error(ec, result < 0);
if (result >= 0)
{
errno = 0;
int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK));
result = error_wrapper(::fcntl(d, F_SETFL, flag), ec);
result = ::fcntl(d, F_SETFL, flag);
get_last_error(ec, result < 0);
}
#else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__)
ioctl_arg_type arg = (value ? 1 : 0);
int result = error_wrapper(::ioctl(d, FIONBIO, &arg), ec);
int result = ::ioctl(d, FIONBIO, &arg);
get_last_error(ec, result < 0);
#endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__)
if (result >= 0)
{
ec = boost::system::error_code();
if (value)
state |= internal_non_blocking;
else
@@ -176,7 +172,7 @@ std::size_t sync_read(int d, state_type state, buf* bufs,
// A request to read 0 bytes on a stream is a no-op.
if (all_empty)
{
ec = boost::system::error_code();
ec.assign(0, ec.category());
return 0;
}
@@ -184,9 +180,54 @@ std::size_t sync_read(int d, state_type state, buf* bufs,
for (;;)
{
// Try to complete the operation without blocking.
errno = 0;
signed_size_type bytes = error_wrapper(::readv(
d, bufs, static_cast<int>(count)), ec);
signed_size_type bytes = ::readv(d, bufs, static_cast<int>(count));
get_last_error(ec, bytes < 0);
// Check if operation succeeded.
if (bytes > 0)
return bytes;
// Check for EOF.
if (bytes == 0)
{
ec = boost::asio::error::eof;
return 0;
}
// Operation failed.
if ((state & user_set_non_blocking)
|| (ec != boost::asio::error::would_block
&& ec != boost::asio::error::try_again))
return 0;
// Wait for descriptor to become ready.
if (descriptor_ops::poll_read(d, 0, ec) < 0)
return 0;
}
}
std::size_t sync_read1(int d, state_type state, void* data,
std::size_t size, boost::system::error_code& ec)
{
if (d == -1)
{
ec = boost::asio::error::bad_descriptor;
return 0;
}
// A request to read 0 bytes on a stream is a no-op.
if (size == 0)
{
ec.assign(0, ec.category());
return 0;
}
// Read some data.
for (;;)
{
// Try to complete the operation without blocking.
signed_size_type bytes = ::read(d, data, size);
get_last_error(ec, bytes < 0);
// Check if operation succeeded.
if (bytes > 0)
@@ -217,9 +258,8 @@ bool non_blocking_read(int d, buf* bufs, std::size_t count,
for (;;)
{
// Read some data.
errno = 0;
signed_size_type bytes = error_wrapper(::readv(
d, bufs, static_cast<int>(count)), ec);
signed_size_type bytes = ::readv(d, bufs, static_cast<int>(count));
get_last_error(ec, bytes < 0);
// Check for end of stream.
if (bytes == 0)
@@ -228,6 +268,13 @@ bool non_blocking_read(int d, buf* bufs, std::size_t count,
return true;
}
// Check if operation succeeded.
if (bytes > 0)
{
bytes_transferred = bytes;
return true;
}
// Retry operation if interrupted by signal.
if (ec == boost::asio::error::interrupted)
continue;
@@ -237,15 +284,46 @@ bool non_blocking_read(int d, buf* bufs, std::size_t count,
|| ec == boost::asio::error::try_again)
return false;
// Operation is complete.
// Operation failed.
bytes_transferred = 0;
return true;
}
}
bool non_blocking_read1(int d, void* data, std::size_t size,
boost::system::error_code& ec, std::size_t& bytes_transferred)
{
for (;;)
{
// Read some data.
signed_size_type bytes = ::read(d, data, size);
get_last_error(ec, bytes < 0);
// Check for end of stream.
if (bytes == 0)
{
ec = boost::asio::error::eof;
return true;
}
// Check if operation succeeded.
if (bytes > 0)
{
ec = boost::system::error_code();
bytes_transferred = bytes;
return true;
}
else
bytes_transferred = 0;
// Retry operation if interrupted by signal.
if (ec == boost::asio::error::interrupted)
continue;
// Check if we need to run the operation again.
if (ec == boost::asio::error::would_block
|| ec == boost::asio::error::try_again)
return false;
// Operation failed.
bytes_transferred = 0;
return true;
}
}
@@ -262,7 +340,7 @@ std::size_t sync_write(int d, state_type state, const buf* bufs,
// A request to write 0 bytes on a stream is a no-op.
if (all_empty)
{
ec = boost::system::error_code();
ec.assign(0, ec.category());
return 0;
}
@@ -270,9 +348,47 @@ std::size_t sync_write(int d, state_type state, const buf* bufs,
for (;;)
{
// Try to complete the operation without blocking.
errno = 0;
signed_size_type bytes = error_wrapper(::writev(
d, bufs, static_cast<int>(count)), ec);
signed_size_type bytes = ::writev(d, bufs, static_cast<int>(count));
get_last_error(ec, bytes < 0);
// Check if operation succeeded.
if (bytes > 0)
return bytes;
// Operation failed.
if ((state & user_set_non_blocking)
|| (ec != boost::asio::error::would_block
&& ec != boost::asio::error::try_again))
return 0;
// Wait for descriptor to become ready.
if (descriptor_ops::poll_write(d, 0, ec) < 0)
return 0;
}
}
std::size_t sync_write1(int d, state_type state, const void* data,
std::size_t size, boost::system::error_code& ec)
{
if (d == -1)
{
ec = boost::asio::error::bad_descriptor;
return 0;
}
// A request to write 0 bytes on a stream is a no-op.
if (size == 0)
{
ec.assign(0, ec.category());
return 0;
}
// Write some data.
for (;;)
{
// Try to complete the operation without blocking.
signed_size_type bytes = ::write(d, data, size);
get_last_error(ec, bytes < 0);
// Check if operation succeeded.
if (bytes > 0)
@@ -296,9 +412,15 @@ bool non_blocking_write(int d, const buf* bufs, std::size_t count,
for (;;)
{
// Write some data.
errno = 0;
signed_size_type bytes = error_wrapper(::writev(
d, bufs, static_cast<int>(count)), ec);
signed_size_type bytes = ::writev(d, bufs, static_cast<int>(count));
get_last_error(ec, bytes < 0);
// Check if operation succeeded.
if (bytes >= 0)
{
bytes_transferred = bytes;
return true;
}
// Retry operation if interrupted by signal.
if (ec == boost::asio::error::interrupted)
@@ -309,15 +431,39 @@ bool non_blocking_write(int d, const buf* bufs, std::size_t count,
|| ec == boost::asio::error::try_again)
return false;
// Operation is complete.
// Operation failed.
bytes_transferred = 0;
return true;
}
}
bool non_blocking_write1(int d, const void* data, std::size_t size,
boost::system::error_code& ec, std::size_t& bytes_transferred)
{
for (;;)
{
// Write some data.
signed_size_type bytes = ::write(d, data, size);
get_last_error(ec, bytes < 0);
// Check if operation succeeded.
if (bytes >= 0)
{
ec = boost::system::error_code();
bytes_transferred = bytes;
return true;
}
else
bytes_transferred = 0;
// Retry operation if interrupted by signal.
if (ec == boost::asio::error::interrupted)
continue;
// Check if we need to run the operation again.
if (ec == boost::asio::error::would_block
|| ec == boost::asio::error::try_again)
return false;
// Operation failed.
bytes_transferred = 0;
return true;
}
}
@@ -331,13 +477,11 @@ int ioctl(int d, state_type& state, long cmd,
return -1;
}
errno = 0;
int result = error_wrapper(::ioctl(d, cmd, arg), ec);
int result = ::ioctl(d, cmd, arg);
get_last_error(ec, result < 0);
if (result >= 0)
{
ec = boost::system::error_code();
// When updating the non-blocking mode we always perform the ioctl syscall,
// even if the flags would otherwise indicate that the descriptor is
// already in the correct state. This ensures that the underlying
@@ -371,10 +515,8 @@ int fcntl(int d, int cmd, boost::system::error_code& ec)
return -1;
}
errno = 0;
int result = error_wrapper(::fcntl(d, cmd), ec);
if (result != -1)
ec = boost::system::error_code();
int result = ::fcntl(d, cmd);
get_last_error(ec, result < 0);
return result;
}
@@ -386,10 +528,8 @@ int fcntl(int d, int cmd, long arg, boost::system::error_code& ec)
return -1;
}
errno = 0;
int result = error_wrapper(::fcntl(d, cmd, arg), ec);
if (result != -1)
ec = boost::system::error_code();
int result = ::fcntl(d, cmd, arg);
get_last_error(ec, result < 0);
return result;
}
@@ -406,13 +546,11 @@ int poll_read(int d, state_type state, boost::system::error_code& ec)
fds.events = POLLIN;
fds.revents = 0;
int timeout = (state & user_set_non_blocking) ? 0 : -1;
errno = 0;
int result = error_wrapper(::poll(&fds, 1, timeout), ec);
int result = ::poll(&fds, 1, timeout);
get_last_error(ec, result < 0);
if (result == 0)
ec = (state & user_set_non_blocking)
? boost::asio::error::would_block : boost::system::error_code();
else if (result > 0)
ec = boost::system::error_code();
if (state & user_set_non_blocking)
ec = boost::asio::error::would_block;
return result;
}
@@ -429,13 +567,11 @@ int poll_write(int d, state_type state, boost::system::error_code& ec)
fds.events = POLLOUT;
fds.revents = 0;
int timeout = (state & user_set_non_blocking) ? 0 : -1;
errno = 0;
int result = error_wrapper(::poll(&fds, 1, timeout), ec);
int result = ::poll(&fds, 1, timeout);
get_last_error(ec, result < 0);
if (result == 0)
ec = (state & user_set_non_blocking)
? boost::asio::error::would_block : boost::system::error_code();
else if (result > 0)
ec = boost::system::error_code();
if (state & user_set_non_blocking)
ec = boost::asio::error::would_block;
return result;
}
@@ -452,13 +588,11 @@ int poll_error(int d, state_type state, boost::system::error_code& ec)
fds.events = POLLPRI | POLLERR | POLLHUP;
fds.revents = 0;
int timeout = (state & user_set_non_blocking) ? 0 : -1;
errno = 0;
int result = error_wrapper(::poll(&fds, 1, timeout), ec);
int result = ::poll(&fds, 1, timeout);
get_last_error(ec, result < 0);
if (result == 0)
ec = (state & user_set_non_blocking)
? boost::asio::error::would_block : boost::system::error_code();
else if (result > 0)
ec = boost::system::error_code();
if (state & user_set_non_blocking)
ec = boost::asio::error::would_block;
return result;
}

View File

@@ -74,11 +74,12 @@ struct handler_tracking::tracking_state
static_mutex mutex_;
uint64_t next_id_;
tss_ptr<completion>* current_completion_;
tss_ptr<location>* current_location_;
};
handler_tracking::tracking_state* handler_tracking::get_state()
{
static tracking_state state = { BOOST_ASIO_STATIC_MUTEX_INIT, 1, 0 };
static tracking_state state = { BOOST_ASIO_STATIC_MUTEX_INIT, 1, 0, 0 };
return &state;
}
@@ -91,6 +92,25 @@ void handler_tracking::init()
static_mutex::scoped_lock lock(state->mutex_);
if (state->current_completion_ == 0)
state->current_completion_ = new tss_ptr<completion>;
if (state->current_location_ == 0)
state->current_location_ = new tss_ptr<location>;
}
handler_tracking::location::location(
const char* file, int line, const char* func)
: file_(file),
line_(line),
func_(func),
next_(*get_state()->current_location_)
{
if (file_)
*get_state()->current_location_ = this;
}
handler_tracking::location::~location()
{
if (file_)
*get_state()->current_location_ = next_;
}
void handler_tracking::creation(execution_context&,
@@ -110,6 +130,24 @@ void handler_tracking::creation(execution_context&,
if (completion* current_completion = *state->current_completion_)
current_id = current_completion->id_;
for (location* current_location = *state->current_location_;
current_location; current_location = current_location->next_)
{
write_line(
#if defined(BOOST_ASIO_WINDOWS)
"@asio|%I64u.%06I64u|%I64u^%I64u|%s%s%.80s%s(%.80s:%d)\n",
#else // defined(BOOST_ASIO_WINDOWS)
"@asio|%llu.%06llu|%llu^%llu|%s%s%.80s%s(%.80s:%d)\n",
#endif // defined(BOOST_ASIO_WINDOWS)
timestamp.seconds, timestamp.microseconds,
current_id, h.id_,
current_location == *state->current_location_ ? "in " : "called from ",
current_location->func_ ? "'" : "",
current_location->func_ ? current_location->func_ : "",
current_location->func_ ? "' " : "",
current_location->file_, current_location->line_);
}
write_line(
#if defined(BOOST_ASIO_WINDOWS)
"@asio|%I64u.%06I64u|%I64u*%I64u|%.20s@%p.%.50s\n",

View File

@@ -70,8 +70,8 @@ boost::system::error_code reactive_serial_port_service::open(
// Set up default serial port options.
termios ios;
errno = 0;
s = descriptor_ops::error_wrapper(::tcgetattr(fd, &ios), ec);
s = ::tcgetattr(fd, &ios);
descriptor_ops::get_last_error(ec, s < 0);
if (s >= 0)
{
#if defined(_BSD_SOURCE) || defined(_DEFAULT_SOURCE)
@@ -86,8 +86,8 @@ boost::system::error_code reactive_serial_port_service::open(
#endif
ios.c_iflag |= IGNPAR;
ios.c_cflag |= CREAD | CLOCAL;
errno = 0;
s = descriptor_ops::error_wrapper(::tcsetattr(fd, TCSANOW, &ios), ec);
s = ::tcsetattr(fd, TCSANOW, &ios);
descriptor_ops::get_last_error(ec, s < 0);
}
if (s < 0)
{
@@ -112,18 +112,16 @@ boost::system::error_code reactive_serial_port_service::do_set_option(
const void* option, boost::system::error_code& ec)
{
termios ios;
errno = 0;
descriptor_ops::error_wrapper(::tcgetattr(
descriptor_service_.native_handle(impl), &ios), ec);
if (ec)
int s = ::tcgetattr(descriptor_service_.native_handle(impl), &ios);
descriptor_ops::get_last_error(ec, s < 0);
if (s < 0)
return ec;
if (store(option, ios, ec))
return ec;
errno = 0;
descriptor_ops::error_wrapper(::tcsetattr(
descriptor_service_.native_handle(impl), TCSANOW, &ios), ec);
s = ::tcsetattr(descriptor_service_.native_handle(impl), TCSANOW, &ios);
descriptor_ops::get_last_error(ec, s < 0);
return ec;
}
@@ -133,10 +131,9 @@ boost::system::error_code reactive_serial_port_service::do_get_option(
void* option, boost::system::error_code& ec) const
{
termios ios;
errno = 0;
descriptor_ops::error_wrapper(::tcgetattr(
descriptor_service_.native_handle(impl), &ios), ec);
if (ec)
int s = ::tcgetattr(descriptor_service_.native_handle(impl), &ios);
descriptor_ops::get_last_error(ec, s < 0);
if (s < 0)
return ec;
return load(option, ios, ec);

View File

@@ -136,6 +136,10 @@ scheduler::~scheduler()
{
if (thread_)
{
mutex::scoped_lock lock(mutex_);
shutdown_ = true;
stop_all_threads(lock);
lock.unlock();
thread_->join();
delete thread_;
}
@@ -321,6 +325,12 @@ void scheduler::compensating_work_started()
++static_cast<thread_info*>(this_thread)->private_outstanding_work;
}
void scheduler::capture_current_exception()
{
if (thread_info_base* this_thread = thread_call_stack::contains(this))
this_thread->capture_current_exception();
}
void scheduler::post_immediate_completion(
scheduler::operation* op, bool is_continuation)
{
@@ -445,6 +455,7 @@ std::size_t scheduler::do_run_one(mutex::scoped_lock& lock,
// Complete the operation. May throw an exception. Deletes the object.
o->complete(this, ec, task_result);
this_thread.rethrow_pending_exception();
return 1;
}
@@ -525,6 +536,7 @@ std::size_t scheduler::do_wait_one(mutex::scoped_lock& lock,
// Complete the operation. May throw an exception. Deletes the object.
o->complete(this, ec, task_result);
this_thread.rethrow_pending_exception();
return 1;
}
@@ -579,6 +591,7 @@ std::size_t scheduler::do_poll_one(mutex::scoped_lock& lock,
// Complete the operation. May throw an exception. Deletes the object.
o->complete(this, ec, task_result);
this_thread.rethrow_pending_exception();
return 1;
}

View File

@@ -90,7 +90,8 @@ class signal_set_service::pipe_read_op : public reactor_op
{
public:
pipe_read_op()
: reactor_op(&pipe_read_op::do_perform, pipe_read_op::do_complete)
: reactor_op(boost::system::error_code(),
&pipe_read_op::do_perform, pipe_read_op::do_complete)
{
}

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,9 @@
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <boost/asio/detail/recycling_allocator.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/defer.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/detail/push_options.hpp>
@@ -27,8 +30,137 @@ namespace boost {
namespace asio {
namespace detail {
template <typename F, typename Allocator>
class strand_executor_service::allocator_binder
{
public:
typedef Allocator allocator_type;
allocator_binder(BOOST_ASIO_MOVE_ARG(F) f, const Allocator& a)
: f_(BOOST_ASIO_MOVE_CAST(F)(f)),
allocator_(a)
{
}
allocator_binder(const allocator_binder& other)
: f_(other.f_),
allocator_(other.allocator_)
{
}
#if defined(BOOST_ASIO_HAS_MOVE)
allocator_binder(allocator_binder&& other)
: f_(BOOST_ASIO_MOVE_CAST(F)(other.f_)),
allocator_(BOOST_ASIO_MOVE_CAST(allocator_type)(other.allocator_))
{
}
#endif // defined(BOOST_ASIO_HAS_MOVE)
allocator_type get_allocator() const BOOST_ASIO_NOEXCEPT
{
return allocator_;
}
void operator()()
{
f_();
}
private:
F f_;
allocator_type allocator_;
};
template <typename Executor>
class strand_executor_service::invoker
class strand_executor_service::invoker<Executor,
typename enable_if<
execution::is_executor<Executor>::value
>::type>
{
public:
invoker(const implementation_type& impl, Executor& ex)
: impl_(impl),
executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
{
}
invoker(const invoker& other)
: impl_(other.impl_),
executor_(other.executor_)
{
}
#if defined(BOOST_ASIO_HAS_MOVE)
invoker(invoker&& other)
: impl_(BOOST_ASIO_MOVE_CAST(implementation_type)(other.impl_)),
executor_(BOOST_ASIO_MOVE_CAST(executor_type)(other.executor_))
{
}
#endif // defined(BOOST_ASIO_HAS_MOVE)
struct on_invoker_exit
{
invoker* this_;
~on_invoker_exit()
{
this_->impl_->mutex_->lock();
this_->impl_->ready_queue_.push(this_->impl_->waiting_queue_);
bool more_handlers = this_->impl_->locked_ =
!this_->impl_->ready_queue_.empty();
this_->impl_->mutex_->unlock();
if (more_handlers)
{
recycling_allocator<void> allocator;
execution::execute(
boost::asio::prefer(
boost::asio::require(this_->executor_,
execution::blocking.never),
execution::allocator(allocator)),
BOOST_ASIO_MOVE_CAST(invoker)(*this_));
}
}
};
void operator()()
{
// Indicate that this strand is executing on the current thread.
call_stack<strand_impl>::context ctx(impl_.get());
// Ensure the next handler, if any, is scheduled on block exit.
on_invoker_exit on_exit = { this };
(void)on_exit;
// Run all ready handlers. No lock is required since the ready queue is
// accessed only within the strand.
boost::system::error_code ec;
while (scheduler_operation* o = impl_->ready_queue_.front())
{
impl_->ready_queue_.pop();
o->complete(impl_.get(), ec, 0);
}
}
private:
typedef typename decay<
typename prefer_result_type<
Executor,
execution::outstanding_work_t::tracked_t
>::type
>::type executor_type;
implementation_type impl_;
executor_type executor_;
};
#if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
template <typename Executor>
class strand_executor_service::invoker<Executor,
typename enable_if<
!execution::is_executor<Executor>::value
>::type>
{
public:
invoker(const implementation_type& impl, Executor& ex)
@@ -96,6 +228,68 @@ private:
executor_work_guard<Executor> work_;
};
#endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
template <typename Executor, typename Function>
inline void strand_executor_service::execute(const implementation_type& impl,
Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function,
typename enable_if<
can_query<Executor, execution::allocator_t<void> >::value
>::type*)
{
return strand_executor_service::execute(impl, ex,
BOOST_ASIO_MOVE_CAST(Function)(function),
boost::asio::query(ex, execution::allocator));
}
template <typename Executor, typename Function>
inline void strand_executor_service::execute(const implementation_type& impl,
Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function,
typename enable_if<
!can_query<Executor, execution::allocator_t<void> >::value
>::type*)
{
return strand_executor_service::execute(impl, ex,
BOOST_ASIO_MOVE_CAST(Function)(function),
std::allocator<void>());
}
template <typename Executor, typename Function, typename Allocator>
void strand_executor_service::execute(const implementation_type& impl,
Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a)
{
typedef typename decay<Function>::type function_type;
// If the executor is not never-blocking, and we are already in the strand,
// then the function can run immediately.
if (boost::asio::query(ex, execution::blocking) != execution::blocking.never
&& call_stack<strand_impl>::contains(impl.get()))
{
// Make a local, non-const copy of the function.
function_type tmp(BOOST_ASIO_MOVE_CAST(Function)(function));
fenced_block b(fenced_block::full);
boost_asio_handler_invoke_helpers::invoke(tmp, tmp);
return;
}
// Allocate and construct an operation to wrap the function.
typedef executor_op<function_type, Allocator> op;
typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(function), a);
BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
"strand_executor", impl.get(), 0, "execute"));
// Add the function to the strand and schedule the strand if required.
bool first = enqueue(impl, p.p);
p.v = p.p = 0;
if (first)
{
execution::execute(ex, invoker<Executor>(impl, ex));
}
}
template <typename Executor, typename Function, typename Allocator>
void strand_executor_service::dispatch(const implementation_type& impl,
Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a)
@@ -125,7 +319,11 @@ void strand_executor_service::dispatch(const implementation_type& impl,
bool first = enqueue(impl, p.p);
p.v = p.p = 0;
if (first)
ex.dispatch(invoker<Executor>(impl, ex), a);
{
boost::asio::dispatch(ex,
allocator_binder<invoker<Executor>, Allocator>(
invoker<Executor>(impl, ex), a));
}
}
// Request invocation of the given function and return immediately.
@@ -147,7 +345,11 @@ void strand_executor_service::post(const implementation_type& impl,
bool first = enqueue(impl, p.p);
p.v = p.p = 0;
if (first)
ex.post(invoker<Executor>(impl, ex), a);
{
boost::asio::post(ex,
allocator_binder<invoker<Executor>, Allocator>(
invoker<Executor>(impl, ex), a));
}
}
// Request invocation of the given function and return immediately.
@@ -169,7 +371,11 @@ void strand_executor_service::defer(const implementation_type& impl,
bool first = enqueue(impl, p.p);
p.v = p.p = 0;
if (first)
ex.defer(invoker<Executor>(impl, ex), a);
{
boost::asio::defer(ex,
allocator_binder<invoker<Executor>, Allocator>(
invoker<Executor>(impl, ex), a));
}
}
} // namespace detail

View File

@@ -36,7 +36,7 @@ inline strand_service::strand_impl::strand_impl()
struct strand_service::on_dispatch_exit
{
io_context_impl* io_context_;
io_context_impl* io_context_impl_;
strand_impl* impl_;
~on_dispatch_exit()
@@ -47,7 +47,7 @@ struct strand_service::on_dispatch_exit
impl_->mutex_.unlock();
if (more_handlers)
io_context_->post_immediate_completion(impl_, false);
io_context_impl_->post_immediate_completion(impl_, false);
}
};
@@ -64,10 +64,10 @@ void strand_service::dispatch(strand_service::implementation_type& impl,
}
// Allocate and construct an operation to wrap the handler.
typedef completion_handler<Handler> op;
typedef completion_handler<Handler, io_context::executor_type> op;
typename op::ptr p = { boost::asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(handler);
p.p = new (p.v) op(handler, io_context_.get_executor());
BOOST_ASIO_HANDLER_CREATION((this->context(),
*p.p, "strand", impl, 0, "dispatch"));
@@ -82,11 +82,10 @@ void strand_service::dispatch(strand_service::implementation_type& impl,
call_stack<strand_impl>::context ctx(impl);
// Ensure the next handler, if any, is scheduled on block exit.
on_dispatch_exit on_exit = { &io_context_, impl };
on_dispatch_exit on_exit = { &io_context_impl_, impl };
(void)on_exit;
completion_handler<Handler>::do_complete(
&io_context_, o, boost::system::error_code(), 0);
op::do_complete(&io_context_impl_, o, boost::system::error_code(), 0);
}
}
@@ -99,10 +98,10 @@ void strand_service::post(strand_service::implementation_type& impl,
boost_asio_handler_cont_helpers::is_continuation(handler);
// Allocate and construct an operation to wrap the handler.
typedef completion_handler<Handler> op;
typedef completion_handler<Handler, io_context::executor_type> op;
typename op::ptr p = { boost::asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(handler);
p.p = new (p.v) op(handler, io_context_.get_executor());
BOOST_ASIO_HANDLER_CREATION((this->context(),
*p.p, "strand", impl, 0, "post"));

View File

@@ -44,7 +44,8 @@ struct strand_service::on_do_complete_exit
strand_service::strand_service(boost::asio::io_context& io_context)
: boost::asio::detail::service_base<strand_service>(io_context),
io_context_(boost::asio::use_service<io_context_impl>(io_context)),
io_context_(io_context),
io_context_impl_(boost::asio::use_service<io_context_impl>(io_context)),
mutex_(),
salt_(0)
{
@@ -95,7 +96,7 @@ bool strand_service::do_dispatch(implementation_type& impl, operation* op)
{
// If we are running inside the io_context, and no other handler already
// holds the strand lock, then the handler can run immediately.
bool can_dispatch = io_context_.can_dispatch();
bool can_dispatch = io_context_impl_.can_dispatch();
impl->mutex_.lock();
if (can_dispatch && !impl->locked_)
{
@@ -118,7 +119,7 @@ bool strand_service::do_dispatch(implementation_type& impl, operation* op)
impl->locked_ = true;
impl->mutex_.unlock();
impl->ready_queue_.push(op);
io_context_.post_immediate_completion(impl, false);
io_context_impl_.post_immediate_completion(impl, false);
}
return false;
@@ -141,7 +142,7 @@ void strand_service::do_post(implementation_type& impl,
impl->locked_ = true;
impl->mutex_.unlock();
impl->ready_queue_.push(op);
io_context_.post_immediate_completion(impl, is_continuation);
io_context_impl_.post_immediate_completion(impl, is_continuation);
}
}

View File

@@ -114,6 +114,7 @@ win_iocp_io_context::~win_iocp_io_context()
{
if (thread_.get())
{
stop();
thread_->join();
thread_.reset();
}
@@ -200,7 +201,7 @@ size_t win_iocp_io_context::run(boost::system::error_code& ec)
thread_call_stack::context ctx(this, this_thread);
size_t n = 0;
while (do_one(INFINITE, ec))
while (do_one(INFINITE, this_thread, ec))
if (n != (std::numeric_limits<size_t>::max)())
++n;
return n;
@@ -218,7 +219,7 @@ size_t win_iocp_io_context::run_one(boost::system::error_code& ec)
win_iocp_thread_info this_thread;
thread_call_stack::context ctx(this, this_thread);
return do_one(INFINITE, ec);
return do_one(INFINITE, this_thread, ec);
}
size_t win_iocp_io_context::wait_one(long usec, boost::system::error_code& ec)
@@ -233,7 +234,7 @@ size_t win_iocp_io_context::wait_one(long usec, boost::system::error_code& ec)
win_iocp_thread_info this_thread;
thread_call_stack::context ctx(this, this_thread);
return do_one(usec < 0 ? INFINITE : ((usec - 1) / 1000 + 1), ec);
return do_one(usec < 0 ? INFINITE : ((usec - 1) / 1000 + 1), this_thread, ec);
}
size_t win_iocp_io_context::poll(boost::system::error_code& ec)
@@ -249,7 +250,7 @@ size_t win_iocp_io_context::poll(boost::system::error_code& ec)
thread_call_stack::context ctx(this, this_thread);
size_t n = 0;
while (do_one(0, ec))
while (do_one(0, this_thread, ec))
if (n != (std::numeric_limits<size_t>::max)())
++n;
return n;
@@ -267,7 +268,7 @@ size_t win_iocp_io_context::poll_one(boost::system::error_code& ec)
win_iocp_thread_info this_thread;
thread_call_stack::context ctx(this, this_thread);
return do_one(0, ec);
return do_one(0, this_thread, ec);
}
void win_iocp_io_context::stop()
@@ -287,6 +288,12 @@ void win_iocp_io_context::stop()
}
}
void win_iocp_io_context::capture_current_exception()
{
if (thread_info_base* this_thread = thread_call_stack::contains(this))
this_thread->capture_current_exception();
}
void win_iocp_io_context::post_deferred_completion(win_iocp_operation* op)
{
// Flag the operation as ready.
@@ -396,7 +403,8 @@ void win_iocp_io_context::on_completion(win_iocp_operation* op,
}
}
size_t win_iocp_io_context::do_one(DWORD msec, boost::system::error_code& ec)
size_t win_iocp_io_context::do_one(DWORD msec,
win_iocp_thread_info& this_thread, boost::system::error_code& ec)
{
for (;;)
{
@@ -458,6 +466,7 @@ size_t win_iocp_io_context::do_one(DWORD msec, boost::system::error_code& ec)
(void)on_exit;
op->complete(this, result_ec, bytes_transferred);
this_thread.rethrow_pending_exception();
ec = boost::system::error_code();
return 1;
}

View File

@@ -1,169 +0,0 @@
//
// io_object_executor.hpp
// ~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_ASIO_DETAIL_IO_OBJECT_EXECUTOR_HPP
#define BOOST_ASIO_DETAIL_IO_OBJECT_EXECUTOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace detail {
// Wrap the (potentially polymorphic) executor so that we can bypass it when
// dispatching on a target executor that has a native I/O implementation.
template <typename Executor>
class io_object_executor
{
public:
io_object_executor(const Executor& ex,
bool native_implementation) BOOST_ASIO_NOEXCEPT
: executor_(ex),
has_native_impl_(native_implementation)
{
}
io_object_executor(const io_object_executor& other) BOOST_ASIO_NOEXCEPT
: executor_(other.executor_),
has_native_impl_(other.has_native_impl_)
{
}
template <typename Executor1>
io_object_executor(
const io_object_executor<Executor1>& other) BOOST_ASIO_NOEXCEPT
: executor_(other.inner_executor()),
has_native_impl_(other.has_native_implementation())
{
}
#if defined(BOOST_ASIO_HAS_MOVE)
io_object_executor(io_object_executor&& other) BOOST_ASIO_NOEXCEPT
: executor_(BOOST_ASIO_MOVE_CAST(Executor)(other.executor_)),
has_native_impl_(other.has_native_impl_)
{
}
#endif // defined(BOOST_ASIO_HAS_MOVE)
const Executor& inner_executor() const BOOST_ASIO_NOEXCEPT
{
return executor_;
}
bool has_native_implementation() const BOOST_ASIO_NOEXCEPT
{
return has_native_impl_;
}
execution_context& context() const BOOST_ASIO_NOEXCEPT
{
return executor_.context();
}
void on_work_started() const BOOST_ASIO_NOEXCEPT
{
if (is_same<Executor, io_context::executor_type>::value
|| has_native_impl_)
{
// When using a native implementation, work is already counted by the
// execution context.
}
else
{
executor_.on_work_started();
}
}
void on_work_finished() const BOOST_ASIO_NOEXCEPT
{
if (is_same<Executor, io_context::executor_type>::value
|| has_native_impl_)
{
// When using a native implementation, work is already counted by the
// execution context.
}
else
{
executor_.on_work_finished();
}
}
template <typename F, typename A>
void dispatch(BOOST_ASIO_MOVE_ARG(F) f, const A& a) const
{
if (is_same<Executor, io_context::executor_type>::value
|| has_native_impl_)
{
// When using a native implementation, I/O completion handlers are
// already dispatched according to the execution context's executor's
// rules. We can call the function directly.
#if defined(BOOST_ASIO_HAS_MOVE)
if (is_same<F, typename decay<F>::type>::value)
{
boost_asio_handler_invoke_helpers::invoke(f, f);
return;
}
#endif // defined(BOOST_ASIO_HAS_MOVE)
typename decay<F>::type function(BOOST_ASIO_MOVE_CAST(F)(f));
boost_asio_handler_invoke_helpers::invoke(function, function);
}
else
{
executor_.dispatch(BOOST_ASIO_MOVE_CAST(F)(f), a);
}
}
template <typename F, typename A>
void post(BOOST_ASIO_MOVE_ARG(F) f, const A& a) const
{
executor_.post(BOOST_ASIO_MOVE_CAST(F)(f), a);
}
template <typename F, typename A>
void defer(BOOST_ASIO_MOVE_ARG(F) f, const A& a) const
{
executor_.defer(BOOST_ASIO_MOVE_CAST(F)(f), a);
}
friend bool operator==(const io_object_executor& a,
const io_object_executor& b) BOOST_ASIO_NOEXCEPT
{
return a.executor_ == b.executor_
&& a.has_native_impl_ == b.has_native_impl_;
}
friend bool operator!=(const io_object_executor& a,
const io_object_executor& b) BOOST_ASIO_NOEXCEPT
{
return a.executor_ != b.executor_
|| a.has_native_impl_ != b.has_native_impl_;
}
private:
Executor executor_;
const bool has_native_impl_;
};
} // namespace detail
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_DETAIL_IO_OBJECT_EXECUTOR_HPP

View File

@@ -17,42 +17,18 @@
#include <new>
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/io_object_executor.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/execution/executor.hpp>
#include <boost/asio/execution/context.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/query.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
class executor;
namespace detail {
inline bool is_native_io_executor(const io_context::executor_type&)
{
return true;
}
template <typename Executor>
inline bool is_native_io_executor(const Executor&,
typename enable_if<!is_same<Executor, executor>::value>::type* = 0)
{
return false;
}
template <typename Executor>
inline bool is_native_io_executor(const Executor& ex,
typename enable_if<is_same<Executor, executor>::value>::type* = 0)
{
#if !defined (BOOST_ASIO_NO_TYPEID)
return ex.target_type() == typeid(io_context::executor_type);
#else // !defined (BOOST_ASIO_NO_TYPEID)
return false;
#endif // !defined (BOOST_ASIO_NO_TYPEID)
}
template <typename IoObjectService,
typename Executor = io_context::executor_type>
class io_object_impl
@@ -67,13 +43,11 @@ public:
// The type of the executor associated with the object.
typedef Executor executor_type;
// The type of executor to be used when implementing asynchronous operations.
typedef io_object_executor<Executor> implementation_executor_type;
// Construct an I/O object using an executor.
explicit io_object_impl(const executor_type& ex)
: service_(&boost::asio::use_service<IoObjectService>(ex.context())),
implementation_executor_(ex, (is_native_io_executor)(ex))
: service_(&boost::asio::use_service<IoObjectService>(
io_object_impl::get_context(ex))),
executor_(ex)
{
service_->construct(implementation_);
}
@@ -84,8 +58,7 @@ public:
typename enable_if<is_convertible<
ExecutionContext&, execution_context&>::value>::type* = 0)
: service_(&boost::asio::use_service<IoObjectService>(context)),
implementation_executor_(context.get_executor(),
is_same<ExecutionContext, io_context>::value)
executor_(context.get_executor())
{
service_->construct(implementation_);
}
@@ -94,7 +67,7 @@ public:
// Move-construct an I/O object.
io_object_impl(io_object_impl&& other)
: service_(&other.get_service()),
implementation_executor_(other.get_implementation_executor())
executor_(other.get_executor())
{
service_->move_construct(implementation_, other.implementation_);
}
@@ -103,8 +76,8 @@ public:
template <typename IoObjectService1, typename Executor1>
io_object_impl(io_object_impl<IoObjectService1, Executor1>&& other)
: service_(&boost::asio::use_service<IoObjectService>(
other.get_implementation_executor().context())),
implementation_executor_(other.get_implementation_executor())
io_object_impl::get_context(other.get_executor()))),
executor_(other.get_executor())
{
service_->converting_move_construct(implementation_,
other.get_service(), other.get_implementation());
@@ -125,9 +98,9 @@ public:
{
service_->move_assign(implementation_,
*other.service_, other.implementation_);
implementation_executor_.~implementation_executor_type();
new (&implementation_executor_) implementation_executor_type(
std::move(other.implementation_executor_));
executor_.~executor_type();
new (&executor_) executor_type(
std::move(other.executor_));
service_ = other.service_;
}
return *this;
@@ -135,16 +108,9 @@ public:
#endif // defined(BOOST_ASIO_HAS_MOVE)
// Get the executor associated with the object.
executor_type get_executor() BOOST_ASIO_NOEXCEPT
const executor_type& get_executor() BOOST_ASIO_NOEXCEPT
{
return implementation_executor_.inner_executor();
}
// Get the executor to be used when implementing asynchronous operations.
const implementation_executor_type& get_implementation_executor()
BOOST_ASIO_NOEXCEPT
{
return implementation_executor_;
return executor_;
}
// Get the service associated with the I/O object.
@@ -172,6 +138,22 @@ public:
}
private:
// Helper function to get an executor's context.
template <typename T>
static execution_context& get_context(const T& t,
typename enable_if<execution::is_executor<T>::value>::type* = 0)
{
return boost::asio::query(t, execution::context);
}
// Helper function to get an executor's context.
template <typename T>
static execution_context& get_context(const T& t,
typename enable_if<!execution::is_executor<T>::value>::type* = 0)
{
return t.context();
}
// Disallow copying and copy assignment.
io_object_impl(const io_object_impl&);
io_object_impl& operator=(const io_object_impl&);
@@ -183,7 +165,7 @@ private:
implementation_type implementation_;
// The associated executor.
implementation_executor_type implementation_executor_;
executor_type executor_;
};
} // namespace detail

View File

@@ -19,6 +19,7 @@
#include <memory>
#if !defined(BOOST_ASIO_HAS_STD_SHARED_PTR)
# include <boost/make_shared.hpp>
# include <boost/shared_ptr.hpp>
# include <boost/weak_ptr.hpp>
#endif // !defined(BOOST_ASIO_HAS_STD_SHARED_PTR)
@@ -32,9 +33,11 @@ namespace asio {
namespace detail {
#if defined(BOOST_ASIO_HAS_STD_SHARED_PTR)
using std::make_shared;
using std::shared_ptr;
using std::weak_ptr;
#else // defined(BOOST_ASIO_HAS_STD_SHARED_PTR)
using boost::make_shared;
using boost::shared_ptr;
using boost::weak_ptr;
#endif // defined(BOOST_ASIO_HAS_STD_SHARED_PTR)

View File

@@ -56,6 +56,12 @@ public:
{
}
// Unlock the mutex and signal one waiter who may destroy us.
template <typename Lock>
void unlock_and_signal_one_for_destruction(Lock&)
{
}
// If there's a waiter, unlock the mutex and signal it.
template <typename Lock>
bool maybe_unlock_and_signal_one(Lock&)

View File

@@ -93,7 +93,7 @@
// Greenhills C++
#elif defined(__BORLANDC__)
#elif defined(__BORLANDC__) && !defined(__clang__)
// Borland C++

View File

@@ -71,6 +71,18 @@ public:
::pthread_cond_signal(&cond_); // Ignore EINVAL.
}
// Unlock the mutex and signal one waiter who may destroy us.
template <typename Lock>
void unlock_and_signal_one_for_destruction(Lock& lock)
{
BOOST_ASIO_ASSERT(lock.locked());
state_ |= 1;
bool have_waiters = (state_ > 1);
if (have_waiters)
::pthread_cond_signal(&cond_); // Ignore EINVAL.
lock.unlock();
}
// If there's a waiter, unlock the mutex and signal it.
template <typename Lock>
bool maybe_unlock_and_signal_one(Lock& lock)

View File

@@ -102,7 +102,7 @@
// Greenhills C++
#elif defined(__BORLANDC__)
#elif defined(__BORLANDC__) && !defined(__clang__)
// Borland C++

View File

@@ -202,7 +202,7 @@ public:
typedef reactive_wait_op<Handler, IoExecutor> op;
typename op::ptr p = { boost::asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(handler, io_ex);
p.p = new (p.v) op(success_ec_, handler, io_ex);
BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor",
&impl, impl.descriptor_, "async_wait"));
@@ -235,11 +235,22 @@ public:
size_t write_some(implementation_type& impl,
const ConstBufferSequence& buffers, boost::system::error_code& ec)
{
buffer_sequence_adapter<boost::asio::const_buffer,
ConstBufferSequence> bufs(buffers);
typedef buffer_sequence_adapter<boost::asio::const_buffer,
ConstBufferSequence> bufs_type;
return descriptor_ops::sync_write(impl.descriptor_, impl.state_,
bufs.buffers(), bufs.count(), bufs.all_empty(), ec);
if (bufs_type::is_single_buffer)
{
return descriptor_ops::sync_write1(impl.descriptor_,
impl.state_, bufs_type::first(buffers).data(),
bufs_type::first(buffers).size(), ec);
}
else
{
bufs_type bufs(buffers);
return descriptor_ops::sync_write(impl.descriptor_, impl.state_,
bufs.buffers(), bufs.count(), bufs.all_empty(), ec);
}
}
// Wait until data can be written without blocking.
@@ -266,7 +277,7 @@ public:
typedef descriptor_write_op<ConstBufferSequence, Handler, IoExecutor> op;
typename op::ptr p = { boost::asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(impl.descriptor_, buffers, handler, io_ex);
p.p = new (p.v) op(success_ec_, impl.descriptor_, buffers, handler, io_ex);
BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor",
&impl, impl.descriptor_, "async_write_some"));
@@ -289,7 +300,7 @@ public:
typedef reactive_null_buffers_op<Handler, IoExecutor> op;
typename op::ptr p = { boost::asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(handler, io_ex);
p.p = new (p.v) op(success_ec_, handler, io_ex);
BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor",
&impl, impl.descriptor_, "async_write_some(null_buffers)"));
@@ -303,11 +314,22 @@ public:
size_t read_some(implementation_type& impl,
const MutableBufferSequence& buffers, boost::system::error_code& ec)
{
buffer_sequence_adapter<boost::asio::mutable_buffer,
MutableBufferSequence> bufs(buffers);
typedef buffer_sequence_adapter<boost::asio::mutable_buffer,
MutableBufferSequence> bufs_type;
return descriptor_ops::sync_read(impl.descriptor_, impl.state_,
bufs.buffers(), bufs.count(), bufs.all_empty(), ec);
if (bufs_type::is_single_buffer)
{
return descriptor_ops::sync_read1(impl.descriptor_,
impl.state_, bufs_type::first(buffers).data(),
bufs_type::first(buffers).size(), ec);
}
else
{
bufs_type bufs(buffers);
return descriptor_ops::sync_read(impl.descriptor_, impl.state_,
bufs.buffers(), bufs.count(), bufs.all_empty(), ec);
}
}
// Wait until data can be read without blocking.
@@ -335,7 +357,7 @@ public:
typedef descriptor_read_op<MutableBufferSequence, Handler, IoExecutor> op;
typename op::ptr p = { boost::asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(impl.descriptor_, buffers, handler, io_ex);
p.p = new (p.v) op(success_ec_, impl.descriptor_, buffers, handler, io_ex);
BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor",
&impl, impl.descriptor_, "async_read_some"));
@@ -358,7 +380,7 @@ public:
typedef reactive_null_buffers_op<Handler, IoExecutor> op;
typename op::ptr p = { boost::asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(handler, io_ex);
p.p = new (p.v) op(success_ec_, handler, io_ex);
BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor",
&impl, impl.descriptor_, "async_read_some(null_buffers)"));
@@ -374,6 +396,9 @@ private:
// The selector that performs event demultiplexing for the service.
reactor& reactor_;
// Cached success value to avoid accessing category singleton.
const boost::system::error_code success_ec_;
};
} // namespace detail

Some files were not shown because too many files have changed in this diff Show More