Make executors shallow-const.
diff --git a/asio/include/asio/executor.hpp b/asio/include/asio/executor.hpp index 3efbda6..d1d6fd3 100644 --- a/asio/include/asio/executor.hpp +++ b/asio/include/asio/executor.hpp
@@ -126,19 +126,19 @@ } /// Obtain the underlying execution context. - execution_context& context() ASIO_NOEXCEPT + execution_context& context() const ASIO_NOEXCEPT { return get_impl()->context(); } /// Inform the executor that it has some outstanding work to do. - void on_work_started() ASIO_NOEXCEPT + void on_work_started() const ASIO_NOEXCEPT { get_impl()->on_work_started(); } /// Inform the executor that some work is no longer outstanding. - void on_work_finished() ASIO_NOEXCEPT + void on_work_finished() const ASIO_NOEXCEPT { get_impl()->on_work_finished(); } @@ -157,7 +157,7 @@ * internal storage needed for function invocation. */ template <typename Function, typename Allocator> - void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a); + void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; /// Request the executor to invoke the given function object. /** @@ -173,7 +173,7 @@ * internal storage needed for function invocation. */ template <typename Function, typename Allocator> - void post(ASIO_MOVE_ARG(Function) f, const Allocator& a); + void post(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; /// Request the executor to invoke the given function object. /** @@ -189,7 +189,7 @@ * internal storage needed for function invocation. */ template <typename Function, typename Allocator> - void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a); + void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; struct unspecified_bool_type_t {}; typedef void (*unspecified_bool_type)(unspecified_bool_type_t); @@ -301,7 +301,7 @@ }; // Helper function to check and return the implementation pointer. - impl_base* get_impl() + impl_base* get_impl() const { if (!impl_) {
diff --git a/asio/include/asio/impl/executor.hpp b/asio/include/asio/impl/executor.hpp index 8aa9079..7c0766e 100644 --- a/asio/include/asio/impl/executor.hpp +++ b/asio/include/asio/impl/executor.hpp
@@ -344,7 +344,8 @@ } template <typename Function, typename Allocator> -void executor::dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a) +void executor::dispatch(ASIO_MOVE_ARG(Function) f, + const Allocator& a) const { impl_base* i = get_impl(); if (i->fast_dispatch_) @@ -354,13 +355,15 @@ } template <typename Function, typename Allocator> -void executor::post(ASIO_MOVE_ARG(Function) f, const Allocator& a) +void executor::post(ASIO_MOVE_ARG(Function) f, + const Allocator& a) const { get_impl()->post(function(ASIO_MOVE_CAST(Function)(f), a)); } template <typename Function, typename Allocator> -void executor::defer(ASIO_MOVE_ARG(Function) f, const Allocator& a) +void executor::defer(ASIO_MOVE_ARG(Function) f, + const Allocator& a) const { get_impl()->defer(function(ASIO_MOVE_CAST(Function)(f), a)); }
diff --git a/asio/include/asio/impl/io_context.hpp b/asio/include/asio/impl/io_context.hpp index 9045440..b10491f 100644 --- a/asio/include/asio/impl/io_context.hpp +++ b/asio/include/asio/impl/io_context.hpp
@@ -148,24 +148,26 @@ #endif // !defined(ASIO_NO_DEPRECATED) inline io_context& -io_context::executor_type::context() ASIO_NOEXCEPT +io_context::executor_type::context() const ASIO_NOEXCEPT { return io_context_; } -inline void io_context::executor_type::on_work_started() ASIO_NOEXCEPT +inline void +io_context::executor_type::on_work_started() const ASIO_NOEXCEPT { io_context_.impl_.work_started(); } -inline void io_context::executor_type::on_work_finished() ASIO_NOEXCEPT +inline void +io_context::executor_type::on_work_finished() const ASIO_NOEXCEPT { io_context_.impl_.work_finished(); } template <typename Function, typename Allocator> void io_context::executor_type::dispatch( - ASIO_MOVE_ARG(Function) f, const Allocator& a) + ASIO_MOVE_ARG(Function) f, const Allocator& a) const { // Make a local, non-const copy of the function. typedef typename decay<Function>::type function_type; @@ -198,7 +200,7 @@ template <typename Function, typename Allocator> void io_context::executor_type::post( - ASIO_MOVE_ARG(Function) f, const Allocator& a) + ASIO_MOVE_ARG(Function) f, const Allocator& a) const { // Make a local, non-const copy of the function. typedef typename decay<Function>::type function_type; @@ -223,7 +225,7 @@ template <typename Function, typename Allocator> void io_context::executor_type::defer( - ASIO_MOVE_ARG(Function) f, const Allocator& a) + ASIO_MOVE_ARG(Function) f, const Allocator& a) const { // Make a local, non-const copy of the function. typedef typename decay<Function>::type function_type;
diff --git a/asio/include/asio/impl/system_executor.hpp b/asio/include/asio/impl/system_executor.hpp index bcf429b..29f6a0a 100644 --- a/asio/include/asio/impl/system_executor.hpp +++ b/asio/include/asio/impl/system_executor.hpp
@@ -25,14 +25,14 @@ namespace asio { -inline execution_context& system_executor::context() ASIO_NOEXCEPT +inline execution_context& system_executor::context() const ASIO_NOEXCEPT { return detail::global<context_impl>(); } template <typename Function, typename Allocator> void system_executor::dispatch( - ASIO_MOVE_ARG(Function) f, const Allocator&) + ASIO_MOVE_ARG(Function) f, const Allocator&) const { typename decay<Function>::type tmp(ASIO_MOVE_CAST(Function)(f)); asio_handler_invoke_helpers::invoke(tmp, tmp); @@ -40,7 +40,7 @@ template <typename Function, typename Allocator> void system_executor::post( - ASIO_MOVE_ARG(Function) f, const Allocator& a) + ASIO_MOVE_ARG(Function) f, const Allocator& a) const { context_impl& ctx = detail::global<context_impl>(); @@ -66,7 +66,7 @@ template <typename Function, typename Allocator> void system_executor::defer( - ASIO_MOVE_ARG(Function) f, const Allocator& a) + ASIO_MOVE_ARG(Function) f, const Allocator& a) const { context_impl& ctx = detail::global<context_impl>();
diff --git a/asio/include/asio/impl/thread_pool.hpp b/asio/include/asio/impl/thread_pool.hpp index 7e9b6c4..7aeee4e 100644 --- a/asio/include/asio/impl/thread_pool.hpp +++ b/asio/include/asio/impl/thread_pool.hpp
@@ -32,24 +32,26 @@ } inline thread_pool& -thread_pool::executor_type::context() ASIO_NOEXCEPT +thread_pool::executor_type::context() const ASIO_NOEXCEPT { return pool_; } -inline void thread_pool::executor_type::on_work_started() ASIO_NOEXCEPT +inline void +thread_pool::executor_type::on_work_started() const ASIO_NOEXCEPT { pool_.scheduler_.work_started(); } -inline void thread_pool::executor_type::on_work_finished() ASIO_NOEXCEPT +inline void thread_pool::executor_type::on_work_finished() +const ASIO_NOEXCEPT { pool_.scheduler_.work_finished(); } template <typename Function, typename Allocator> void thread_pool::executor_type::dispatch( - ASIO_MOVE_ARG(Function) f, const Allocator& a) + ASIO_MOVE_ARG(Function) f, const Allocator& a) const { // Make a local, non-const copy of the function. typedef typename decay<Function>::type function_type; @@ -82,7 +84,7 @@ template <typename Function, typename Allocator> void thread_pool::executor_type::post( - ASIO_MOVE_ARG(Function) f, const Allocator& a) + ASIO_MOVE_ARG(Function) f, const Allocator& a) const { // Make a local, non-const copy of the function. typedef typename decay<Function>::type function_type; @@ -107,7 +109,7 @@ template <typename Function, typename Allocator> void thread_pool::executor_type::defer( - ASIO_MOVE_ARG(Function) f, const Allocator& a) + ASIO_MOVE_ARG(Function) f, const Allocator& a) const { // Make a local, non-const copy of the function. typedef typename decay<Function>::type function_type;
diff --git a/asio/include/asio/io_context.hpp b/asio/include/asio/io_context.hpp index 7fb49a9..96a0a33 100644 --- a/asio/include/asio/io_context.hpp +++ b/asio/include/asio/io_context.hpp
@@ -503,7 +503,7 @@ { public: /// Obtain the underlying execution context. - io_context& context() ASIO_NOEXCEPT; + io_context& context() const ASIO_NOEXCEPT; /// Inform the io_context that it has some outstanding work to do. /** @@ -511,7 +511,7 @@ * This ensures that the io_context's run() and run_one() functions do not * exit while the work is underway. */ - void on_work_started() ASIO_NOEXCEPT; + void on_work_started() const ASIO_NOEXCEPT; /// Inform the io_context that some work is no longer outstanding. /** @@ -519,7 +519,7 @@ * finished. Once the count of unfinished work reaches zero, the io_context * is stopped and the run() and run_one() functions may exit. */ - void on_work_finished() ASIO_NOEXCEPT; + void on_work_finished() const ASIO_NOEXCEPT; /// Request the io_context to invoke the given function object. /** @@ -536,7 +536,7 @@ * internal storage needed for function invocation. */ template <typename Function, typename Allocator> - void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a); + void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; /// Request the io_context to invoke the given function object. /** @@ -552,7 +552,7 @@ * internal storage needed for function invocation. */ template <typename Function, typename Allocator> - void post(ASIO_MOVE_ARG(Function) f, const Allocator& a); + void post(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; /// Request the io_context to invoke the given function object. /** @@ -572,7 +572,7 @@ * internal storage needed for function invocation. */ template <typename Function, typename Allocator> - void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a); + void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; /// Determine whether the io_context is running in the current thread. /**
diff --git a/asio/include/asio/system_executor.hpp b/asio/include/asio/system_executor.hpp index 07c7874..e227687 100644 --- a/asio/include/asio/system_executor.hpp +++ b/asio/include/asio/system_executor.hpp
@@ -36,13 +36,13 @@ { public: /// Obtain the underlying execution context. - execution_context& context() ASIO_NOEXCEPT; + execution_context& context() const ASIO_NOEXCEPT; /// Inform the executor that it has some outstanding work to do. /** * For the system executor, this is a no-op. */ - void on_work_started() ASIO_NOEXCEPT + void on_work_started() const ASIO_NOEXCEPT { } @@ -50,7 +50,7 @@ /** * For the system executor, this is a no-op. */ - void on_work_finished() ASIO_NOEXCEPT + void on_work_finished() const ASIO_NOEXCEPT { } @@ -67,7 +67,7 @@ * internal storage needed for function invocation. */ template <typename Function, typename Allocator> - void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a); + void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; /// Request the system executor to invoke the given function object. /** @@ -83,7 +83,7 @@ * internal storage needed for function invocation. */ template <typename Function, typename Allocator> - void post(ASIO_MOVE_ARG(Function) f, const Allocator& a); + void post(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; /// Request the system executor to invoke the given function object. /** @@ -99,7 +99,7 @@ * internal storage needed for function invocation. */ template <typename Function, typename Allocator> - void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a); + void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; /// Compare two executors for equality. /**
diff --git a/asio/include/asio/thread_pool.hpp b/asio/include/asio/thread_pool.hpp index 2a6b460..f3182bf 100644 --- a/asio/include/asio/thread_pool.hpp +++ b/asio/include/asio/thread_pool.hpp
@@ -83,7 +83,7 @@ { public: /// Obtain the underlying execution context. - thread_pool& context() ASIO_NOEXCEPT; + thread_pool& context() const ASIO_NOEXCEPT; /// Inform the thread pool that it has some outstanding work to do. /** @@ -91,7 +91,7 @@ * This ensures that the thread pool's join() function will not return while * the work is underway. */ - void on_work_started() ASIO_NOEXCEPT; + void on_work_started() const ASIO_NOEXCEPT; /// Inform the thread pool that some work is no longer outstanding. /** @@ -99,7 +99,7 @@ * finished. Once the count of unfinished work reaches zero, the thread * pool's join() function is permitted to exit. */ - void on_work_finished() ASIO_NOEXCEPT; + void on_work_finished() const ASIO_NOEXCEPT; /// Request the thread pool to invoke the given function object. /** @@ -116,7 +116,7 @@ * internal storage needed for function invocation. */ template <typename Function, typename Allocator> - void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a); + void dispatch(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; /// Request the thread pool to invoke the given function object. /** @@ -132,7 +132,7 @@ * internal storage needed for function invocation. */ template <typename Function, typename Allocator> - void post(ASIO_MOVE_ARG(Function) f, const Allocator& a); + void post(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; /// Request the thread pool to invoke the given function object. /** @@ -152,7 +152,7 @@ * internal storage needed for function invocation. */ template <typename Function, typename Allocator> - void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a); + void defer(ASIO_MOVE_ARG(Function) f, const Allocator& a) const; /// Determine whether the thread pool is running in the current thread. /**
diff --git a/asio/src/doc/requirements/Executor.qbk b/asio/src/doc/requirements/Executor.qbk index 9be992b..569410c 100644 --- a/asio/src/doc/requirements/Executor.qbk +++ b/asio/src/doc/requirements/Executor.qbk
@@ -8,25 +8,33 @@ [section:Executor1 Executor requirements] The library describes a standard set of requirements for ['executors]. A type -meeting Executor requirements shall embody a set of rules for determining how +meeting the `Executor` requirements embodies a set of rules for determining how submitted function objects are to be executed. -An executor type `X` shall satisfy the requirements of `CopyConstructible` (C++ -Std, [copyconstructible]) types. No constructor, comparison operator, copy -operation, move operation, swap operation, or member functions `context`, -`on_work_started` and `on_work_finished` on these types shall exit via an -exception. +A type `X` meets the `Executor` requirements if it satisfies the requirements of +`CopyConstructible` (C++Std [copyconstructible]) and `Destructible` (C++Std +[destructible]), as well as the additional requirements listed below. -The executor copy constructor, comparison operators, and member functions +No constructor, comparison operator, copy operation, move operation, swap +operation, or member functions `context`, `on_work_started`, and +`on_work_finished` on these types shall exit via an exception. + +The executor copy constructor, comparison operators, and other member functions defined in these requirements shall not introduce data races as a result of concurrent calls to those functions from different threads. -In the table below, `X` denotes an executor class, `x` denotes a value of type -`X&`, `x1` and `x2` denote values of type `const X&`, `x3` denotes a value of -type `X&&`, `f` denotes a `MoveConstructible` (C++ Std, [moveconstructible]) -function object callable with zero arguments, `a` denotes a value of type `A` -meeting `Allocator` requirements (C++ Std, [allocator.requirements]), `t` -denotes an object of type `T`, and `u` denotes an identifier. +Let `ctx` be the execution context returned by the executor's `context()` +member function. An executor becomes ['invalid] when the first call to +`ctx.shutdown()` returns. The effect of calling `on_work_started`, +`on_work_finished`, `dispatch`, `post`, or `defer` on an invalid executor is +undefined. [inline_note The copy constructor, comparison operators, and +`context()` member function continue to remain valid until `ctx` is destroyed.] + +In the table below, `x1` and `x2` denote (possibly const) values of type `X`, +`mx1` denotes an xvalue of type `X`, `f` denotes a `MoveConstructible` (C++Std +[moveconstructible]) function object callable with zero arguments, `a` denotes +a (possibly const) value of type `A` meeting the `Allocator` requirements +(C++Std [allocator.requirements]), and `u` denotes an identifier. [table Executor requirements [[expression] [type] [assertion/note\npre/post-conditions]] @@ -35,24 +43,25 @@ [] [Shall not exit via an exception.\n \n - post: `u == x1`] + post: `u == x1` and + `std::addressof(u.context()) == std::addressof(x1.context()).`] ] [ - [`X u(x3);`] + [`X u(mx1);`] [] [Shall not exit via an exception.\n \n - post: `u` equals the prior value of `x3`.] + post: `u` equals the prior value of `mx1` and + `std::addressof(u.context())` equals the prior value of + `std::addressof(mx1.context())`.] ] [ [`x1 == x2`] [`bool`] - [Shall not exit via an exception.\n - \n - Returns `true` only if `x1` and `x2` can be interchanged with identical + [ Returns `true` only if `x1` and `x2` can be interchanged with identical effects in any of the expressions defined in these type requirements. - Note: `false` does not necessarily imply that the effects are not - identical.\n + [inline_note Returning `false` does not necessarily imply that the effects + are not identical.]\n \n `operator==` shall be reflexive, symmetric, and transitive, and shall not exit via an exception.] @@ -60,71 +69,72 @@ [ [`x1 != x2`] [`bool`] + [Same as `!(x1 == x2)`.] + ] + [ + [`x1.context()`] + [`execution_context&`, or `E&` where `E` is a type that satifisfies the + [link requirements.execution_context `ExecutionContext`] requirements.] [Shall not exit via an exception.\n \n - Same as `!(x1 == x2)`.] + The comparison operators and member functions defined in these + requirements shall not alter the reference returned by this function.] ] [ - [`x.context()`] - [`execution_context&`, or a type that is convertible to `execution_context&`.] - [Shall not exit via an exception.] - ] - [ - [`x.on_work_started()`] + [`x1.on_work_started()`] [] [Shall not exit via an exception.] ] [ - [`x.on_work_finished()`] + [`x1.on_work_finished()`] [] [Shall not exit via an exception.\n \n - Requires: A preceding call `x1.on_work_started()` where `x == x1`.] + Precondition: A preceding call `x2.on_work_started()` where `x1 == x2`.] ] [ - [`x.dispatch(std::move(f),a)`] + [`x1.dispatch(std::move(f),a)`] [] - [Effects: Calls [^['DECAY_COPY]]`(forward<Func>(f))()` at most once. The - executor may invoke `f` in the current thread, prior to returning from - `dispatch`. The call to `DECAY_COPY()` is evaluated in the thread that - called `dispatch`.\n + [Effects: Creates an object `f1` initialized with + [^['DECAY_COPY]]`(forward<Func>(f))` (C++Std \[thread.decaycopy\]) in the + current thread of execution . Calls `f1()` at most once. The executor may + block forward progress of the caller until `f1()` finishes execution.\n \n - Executor implementations are encouraged to use the supplied allocator to - allocate any memory required to store the function object. The executor - shall deallocate all memory prior to invoking the function object.\n + Executor implementations should use the supplied allocator to allocate any + memory required to store the function object. Prior to invoking the + function object, the executor shall deallocate any memory allocated. + [inline_note Executors defined in this Technical Specification always use + the supplied allocator unless otherwise specified.]\n \n - Synchronization: The invocation of `dispatch` synchronizes with (C++ Std, - \[intro.multithread\]) the invocation of `f`.] + Synchronization: The invocation of `dispatch` synchronizes with (C++Std + \[intro.multithread\]) the invocation of `f1`.] ] [ - [`x.post(std::move(f),a)`] + [`x1.post(std::move(f),a)`\n + `x1.defer(std::move(f),a)`] [] - [Effects: Calls [^['DECAY_COPY]]`(forward<Func>(f))()` at most once. The - executor may not invoke `f` in the current thread, prior to returning from - `post`. The call to `DECAY_COPY()` is evaluated in the thread that called - `post`.\n + [Effects: Creates an object `f1` initialized with + [^['DECAY_COPY]]`(forward<Func>(f))` in the current thread of execution. + Calls `f1()` at most once. The executor shall not block forward progress + of the caller pending completion of `f1()`.\n \n - Executor implementations are encouraged to use the supplied allocator to - allocate any memory required to store the function object. The executor - shall deallocate all memory prior to invoking the function object.\n + Executor implementations should use the supplied allocator to allocate any + memory required to store the function object. Prior to invoking the + function object, the executor shall deallocate any memory allocated. + [inline_note Executors defined in this Technical Specification always use + the supplied allocator unless otherwise specified.]\n \n - Synchronization: The invocation of `post` synchronizes with (C++ Std, - \[intro.multithread\]) the invocation of `f`.] - ] - [ - [`x.defer(std::move(f),a)`] - [] - [Effects: Calls [^['DECAY_COPY]]`(forward<Func>(f))()` at most once. The - executor may not invoke `f` in the current thread, prior to returning from - `defer`. The call to `DECAY_COPY()` is evaluated in the thread that called - `defer`.\n + Synchronization: The invocation of `post` or `defer` synchronizes with + (C++Std \[intro.multithread\]) the invocation of `f1`.\n \n - Executor implementations are encouraged to use the supplied allocator to - allocate any memory required to store the function object. The executor - shall deallocate all memory prior to invoking the function object.\n - \n - Synchronization: The invocation of `defer` synchronizes with (C++ Std, - \[intro.multithread\]) the invocation of `f`.] + [inline_note Although the requirements placed on `defer` are identical to + `post`, the use of `post` conveys a preference that the caller ['does not] + block the first step of [^f1]'s progress, whereas `defer` conveys a + preference that the caller ['does] block the first step of [^f1]. One use + of `defer` is to convey the intention of the caller that [^f1] is a + continuation of the current call context. The executor may use this + information to optimize or otherwise adjust the way in which `f1` is + invoked.]] ] ]