Doc. no:  D????  (as of commit 730249b70aee6867e05296030cb5dd93482fa509)
Date:     2014-??-??
Reply-To: Christopher Kohlhoff <chris@kohlhoff.com>

Executors Proposal

Table of Contents

1. Overview
2. Motivation and Scope
2.1. Reference Implementation
2.2. Related Work
3. Impact On the Standard
4. Design Decisions
5. Proposed Text for the Standard
5.1. Header <experimental/type_traits> synopsis
5.2. Class template handler_type
5.2.1. handler_type members
5.3. Class template async_result
5.3.1. async_result members
5.4. Class template async_completion
5.4.1. async_completion members
5.5. Header <experimental/memory> synopsis
5.6. Class template associated_allocator
5.6.1. associated_allocator members
5.7. Function get_associated_allocator
5.8. Header <experimental/executor> synopsis
5.9. Requirements
5.9.1. Executor requirements
5.9.2. Service requirements
5.10. Class execution_context
5.10.1. execution_context constructor
5.10.2. execution_context destructor
5.10.3. execution_context operations
5.10.4. execution_context protected operations
5.10.5. execution_context globals
5.11. Class execution_context::service
5.11.1. execution_context::service constructors
5.11.2. execution_context::service observers
5.11.3. execution_context::service operations
5.12. Class template is_executor
5.13. Executor argument tag
5.14. uses_executor
5.14.1. uses_executor trait
5.14.2. uses-executor construction
5.15. Class template associated_executor
5.15.1. associated_executor members
5.16. Function get_associated_executor
5.17. Class template executor_wrapper
5.17.1. executor_wrapper constructors
5.17.2. executor_wrapper access
5.17.3. executor_wrapper invocation
5.17.4. Class template specialization async_result
5.17.5. Class template specialization associated_allocator
5.17.6. Class template specialization associated_executor
5.18. Function wrap
5.19. Class template executor_work
5.19.1. executor_work constructors
5.19.2. executor_work destructor
5.19.3. executor_work observers
5.19.4. executor_work modifiers
5.20. Function make_work
5.21. Class system_executor
5.21.1. system_executor operations
5.21.2. system_executor comparisons
5.22. Class bad_executor
5.22.1. bad_executor constructor
5.23. Class executor
5.23.1. executor constructors
5.23.2. executor assignment
5.23.3. executor destructor
5.23.4. executor modifiers
5.23.5. executor operations
5.23.6. executor capacity
5.23.7. executor target access
5.23.8. executor comparisons
5.23.9. executor specialized algorithms
5.24. Function dispatch
5.25. Function post
5.26. Function defer
5.27. Header <experimental/strand> synopsis
5.28. Class template strand
5.28.1. strand constructors
5.28.2. strand assignment
5.28.3. strand destructor
5.28.4. strand operations
5.28.5. strand comparisons
5.29. Header <experimental/timer> synopsis
5.30. Function dispatch_at
5.31. Function post_at
5.32. Function defer_at
5.33. Function dispatch_after
5.34. Function post_after
5.35. Function defer_after
5.36. Header <experimental/future> synopsis
5.37. Class template specialization async_result for packaged_task
5.38. Class template packaged_handler
5.38.1. packaged_handler constructors
5.38.2. packaged_handler operations
5.38.3. Class template specialization async_result
5.39. Class template packaged_token
5.39.1. packaged_token constructors
5.39.2. packaged_token operations
5.40. Function package
5.41. Header <experimental/thread_pool> synopsis
5.42. Class thread_pool
5.42.1. thread_pool constructors/destructor
5.42.2. thread_pool members
5.43. Class thread_pool::executor_type
5.43.1. thread_pool::executor_type constructors
5.43.2. thread_pool::executor_type assignment
5.43.3. thread_pool::executor_type operations
5.43.4. thread_pool::executor_type comparisons
5.44. Header <experimental/loop_scheduler> synopsis
5.45. Class loop_scheduler
5.45.1. loop_scheduler constructors/destructor
5.45.2. loop_scheduler members
5.46. Class loop_scheduler::executor_type
5.46.1. loop_scheduler::executor_type constructors
5.46.2. loop_scheduler::executor_type assignment
5.46.3. loop_scheduler::executor_type operations
5.46.4. loop_scheduler::executor_type comparisons

1. Overview

TBD.

2. Motivation and Scope

TBD.

2.1. Reference Implementation

TBD.

2.2. Related Work

TBD.

3. Impact On the Standard

This is a pure library proposal. It does not add any new language features, nor does it alter any existing standard library headers.

This library can be implemented using compilers that conform to the C++14 standard. An implementation of this library requires operating system-specific functions that lie outside the C++14 standard.

4. Design Decisions

TBD.

5. Proposed Text for the Standard

5.1. Header <experimental/type_traits> synopsis

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class CompletionToken, class Signature, class = void>
        struct handler_type;

      template<class CompletionToken, class Signature>
        using handler_type_t =
          typename handler_type<CompletionToken, Signature>::type;

      template<class Handler> class async_result;

      template<class CompletionToken, class Signature>
        struct async_completion;

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

5.2. Class template handler_type

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class CompletionToken, class Signature, class = void>
      struct handler_type
      {
        typedef see below type;
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

Template parameter CompletionToken specifies the model used to obtain the result of the asynchronous operation. Template parameter Signature is the call signature (C++ Std, [func.def]) for the handler type invoked on completion of the asynchronous operation.

A program may specialize this trait if the CompletionToken template parameter in the specialization is a user-defined type.

Specializations of handler_type shall define a nested handler type type that satisfies the MoveConstructible requirements, and objects of type type shall be constructible from an lvalue or rvalue of the type specified by the CompletionToken template parameter.

5.2.1. handler_type members

typedef see below type;

Type: CompletionToken if CompletionToken and decay_t<CompletionToken> are the same type; otherwise, handler_type_t<decay_t<CompletionToken>, Signature>.

5.3. Class template async_result

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class Handler>
      class async_result
      {
      public:
        typedef void type;

        explicit async_result(Handler&);
        async_result(const async_result&) = delete;
        async_result& operator=(const async_result&) = delete;

        type get();
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

Template argument Handler is a handler type produced by handler_type_t<T, S> for some completion token type T and call signature S.

A program may specialize this template if the Handler template parameter in the specialization is a user-defined type.

Specializations of async_result shall satisfy the Destructible requirements (C++ Std, [destructible]) in addition to the requirements in the table below. In this table, R is a specialization of async_result for the template parameter Handler; r is a modifiable lvalue of type R; and h is a modifiable lvalue of type Handler.

Table 1. async_result specialization requirements

Expression

Return type

Note

R::type

void; or a type satisfying MoveConstructible requirements (C++ Std, [moveconstructible])

R r(h);

r.get()

R::type

The get() member function shall be used only as a return expression.


5.3.1. async_result members

explicit async_result(Handler&);

Effects: Does nothing.

type get();

Effects: Does nothing.

5.4. Class template async_completion

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class CompletionToken, class Signature>
      struct async_completion
      {
        typedef handler_type_t<CompletionToken, Signature> handler_type;

        explicit async_completion(remove_reference_t<CompletionToken>& t);
        async_completion(const async_completion&) = delete;
        async_completion& operator=(const async_completion&) = delete;

        see below handler;
        async_result<handler_type> result;
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

Template parameter CompletionToken specifies the model used to obtain the result of the asynchronous operation. Template parameter Signature is the call signature (C++ Std, [func.def]) for the handler type invoked on completion of the asynchronous operation.

5.4.1. async_completion members

explicit async_completion(remove_reference_t<CompletionToken>& t);

Effects: If CompletionToken and handler_type are the same type, binds handler to t; otherwise, initializes handler with the result of forward<CompletionToken>(t). Initializes result with handler.

see below handler;

Type: handler_type& if CompletionToken and handler_type are the same type; otherwise, handler_type.

5.5. Header <experimental/memory> synopsis

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class T, class Alloc = allocator<void>>
        struct associated_allocator;

      template<class T, class Alloc = allocator<void>>
        using associated_allocator_t = typename associated_allocator<T, Alloc>::type;

      // get_associated_allocator:

      template<class T>
        associated_allocator_t<T> get_associated_allocator(const T& t);
      template<class T, class Alloc>
        associated_allocator_t<T, Alloc>
          get_associated_allocator(const T& t, const Alloc& a);

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

5.6. Class template associated_allocator

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class T, class Alloc = allocator<void>>
      struct associated_allocator
      {
        typedef see below type;

        static type get(const T& t, const Alloc& a = Alloc()) noexcept;
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

A program may specialize this traits type if the T template parameter in the specialization is a user-defined type. The template parameter Alloc shall be a type meeting Allocator requirements (C++ Std, [allocator.requirements]).

Specializations of associated_allocator shall satisfy the requirements in the table below. In this table, X is a specialization of associated_allocator for the template parameter T; t is a const reference to an object of type T; and a is an object of type Alloc.

Table 2. associated_allocator specialization requirements

Expression

Return type

Note

typename X::type

A type meeting Allocator requirements (C++ Std, [allocator.requirements]).

X::get(t)

X::type

Shall not exit via an exception.
Equivalent to X::get(t, Alloc()).

X::get(t, a)

X::type

Shall not exit via an exception.


5.6.1. associated_allocator members

typedef see below type;

Type: If T has a nested type allocator_type, typename T::allocator_type. Otherwise Alloc.

type get(const T& t, const Alloc& a = Alloc()) noexcept;

Returns: If T has a nested type allocator_type, t.get_allocator(). Otherwise a.

5.7. Function get_associated_allocator

template<class T>
  associated_allocator_t<T> get_associated_allocator(const T& t);

Returns: associated_allocator<T>::get(t).

template<class T, class Alloc>
  associated_allocator_t<T, Alloc>
    get_associated_allocator(const T& t, const Alloc& a);

Returns: associated_allocator<T, Alloc>::get(t, a).

5.8. Header <experimental/executor> synopsis

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      enum class fork_event {
        prepare,
        parent,
        child
      };

      class execution_context;

      class service_already_exists;

      template<class Service> Service& use_service(execution_context& ctx);
      template<class Service, class... Args> Service&
        make_service(execution_context& ctx, Args&&... args);
      template<class Service> bool has_service(execution_context& ctx) noexcept;

      template<class T> struct is_executor : false_type {};

      struct executor_arg_t { };
      constexpr executor_arg_t executor_arg = executor_arg_t();

      template<class T, class Executor> struct uses_executor;

      template<class T, class Executor = system_executor>
        struct associated_executor;

      template<class T, class Executor = system_executor>
        using associated_executor_t = typename associated_executor<T, Executor>::type;

      // get_associated_executor:

      template<class T>
        associated_executor_t<T> get_associated_executor(const T& t);
      template<class T, class Executor>
        associated_executor_t<T, Executor>
          get_associated_executor(const T& t, const Executor& ex);
      template<class T, class ExecutionContext>
        associated_executor_t<T, typename ExecutionContext::executor_type>
          get_associated_executor(const T& t, ExecutionContext& ctx);

      template<class T, class Executor> struct executor_wrapper;

      template<class T, class Executor, class Signature>
        struct handler_type<executor_wrapper<T, Executor>, Signature>;

      template<class T, class Executor>
        class async_result<executor_wrapper<T, Executor>>;

      template<class T, class Executor, class Allocator>
        struct associated_allocator<executor_wrapper<T, Executor>, Allocator>;

      template<class T, class Executor, class Executor1>
        struct associated_executor<executor_wrapper<T, Executor>, Executor1>;

      // wrap:

      template<class Executor, class T>
        executor_wrapper<decay_t<T>, Executor> wrap(const Executor& ex, T&& t);
      template<class ExecutionContext, class T>
        executor_wrapper<decay_t<T>, typename ExecutionContext::executor_type>
          wrap(ExecutionContext& ctx, T&& t);

      template<class T, class Executor> struct executor_work;

      // make_work:

      template<class Executor>
        executor_work<Executor>
          make_work(const Executor& ex);
      template<class ExecutionContext>
        executor_work<typename ExecutionContext::executor_type>
          make_work(ExecutionContext& ctx);
      template<class T>
        executor_work<associated_executor_t<T>>
          make_work(const T& t);
      template<class T, class Executor>
        executor_work<associated_executor_t<T, Executor>>
          make_work(const T& t, const Executor& ex);
      template<class T, class ExecutionContext>
        executor_work<associated_executor_t<T, typename ExecutionContext::executor_type>>
          make_work(const T& t, ExecutionContext& ctx);

      class system_executor;

      bool operator==(const system_executor&, const system_executor&);
      bool operator!=(const system_executor&, const system_executor&);

      template<> struct is_executor<system_executor> : true_type {};

      class bad_executor;

      class executor;

      template <> struct is_executor<executor> : true_type {};

      bool operator==(const executor& a, const executor& b) noexcept;
      bool operator==(const executor& e, nullptr_t) noexcept;
      bool operator==(nullptr_t, const executor& e) noexcept;
      bool operator!=(const executor& a, const executor& b) noexcept;
      bool operator!=(const executor& e, nullptr_t) noexcept;
      bool operator!=(nullptr_t, const executor& e) noexcept;

      // dispatch:

      template<class CompletionToken>
        auto dispatch(CompletionToken&& token);
      template<class Executor, class CompletionToken>
        auto dispatch(const Executor& ex, CompletionToken&& token);
      template<class ExecutionContext, class CompletionToken>
        auto dispatch(ExecutionContext& ctx, CompletionToken&& token);

      // post:

      template<class CompletionToken>
        auto post(CompletionToken&& token);
      template<class Executor, class CompletionToken>
        auto post(const Executor& ex, CompletionToken&& token);
      template<class ExecutionContext, class CompletionToken>
        auto post(ExecutionContext& ctx, CompletionToken&& token);

      // defer:

      template<class CompletionToken>
        auto defer(CompletionToken&& token);
      template<class Executor, class CompletionToken>
        auto defer(const Executor& ex, CompletionToken&& token);
      template<class ExecutionContext, class CompletionToken>
        auto defer(ExecutionContext& ctx, CompletionToken&& token);

    } // inline namespace concurrency_v1
  } // namespace experimental

  template<class Alloc>
    struct uses_allocator<std::experimental::concurrency_v1::executor, Alloc>
      : true_type {};

} // namespace std

5.9. Requirements

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

The executor copy constructor, comparison operators, and 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.

Table 3. Executor requirements

expression

type

assertion/note
pre/post-conditions

X u(x1);

Shall not exit via an exception.

post: u == x1

X u(x3);

Shall not exit via an exception.

post: u equals the prior value of x3.

x1 == x2

bool

Shall not exit via an exception.

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. —end note]

operator== shall be reflexive, symmetric, and transitive, and shall not exit via an exception.

x1 != x2

bool

Shall not exit via an exception.

Same as !(x1 == x2).

x.context()

execution_context&, or a type that is convertible to execution_context&.

Shall not exit via an exception.

x.on_work_started()

Shall not exit via an exception.

x.on_work_finished()

Shall not exit via an exception.

Requires: A preceding call x1.on_work_started() where x == x1.

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

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.

Synchronization: The invocation of dispatch synchronizes with (C++ Std, [intro.multithread]) the invocation of f.

x.post(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.

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.

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.

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.

Synchronization: The invocation of defer synchronizes with (C++ Std, [intro.multithread]) the invocation of f.


5.9.2. Service requirements

A class is a service if it is publicly derived from another service, or if it is a class publicly derived from execution_context::service.

A service may contain a publicly-accessible nested typedef named key_type. If the nested typedef key_type exists, the service class shall be the same type as key_type, or otherwise publicly and unambiguously derived from key_type.

All services define a one-argument constructor that takes a reference to the execution_context object that owns the service. This constructor is explicit, preventing its participation in automatic conversions.

A service may provide additional constructors with two or more arguments, where the first argument is a reference to the execution_context object that owns the service. [Note: These constructors may be called by the make_service function. —end note]

[Example:

class my_service : public execution_context::service
{
public:
  typedef my_service key_type;
  explicit my_service(execution_context& ctx);
private:
  virtual void shutdown_service();
  ...
};

end example]

A service's shutdown_service member function must cause all copies of user-defined function objects that are held by the service to be destroyed.

5.10. Class execution_context

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      class execution_context
      {
      public:
        class service;

        // construct / copy / destroy:

        execution_context();
        execution_context(const execution_context&) = delete;
        execution_context& operator=(const execution_context&) = delete;
        virtual ~execution_context();

        // execution context operations:

        void notify_fork(fork_event e);

      protected:

        // execution context protected operations:

        void shutdown_context();
        void destroy_context();
      };

      // service access:
      template<class Service> Service& use_service(execution_context& ctx);
      template<class Service, class... Args> Service&
        make_service(execution_context& ctx, Args&&... args);
      template<class Service> bool has_service(execution_context& ctx) noexcept;
      class service_already_exists : public logic_error { ... };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

Class execution_context implements an extensible, type-safe, polymorphic set of services, indexed by service type.

Access to the services of an execution_context is via three function templates, use_service<>, make_service<> and has_service<>.

In a call to use_service<Service>(), the type argument chooses a service, making available all members of the named type. If the service is not present in an execution_context, an object of type Service is created and added to the execution_context. A C++ program can check if an execution_context implements a particular service with the function template has_service<Service>().

Service objects may be explicitly added to an execution_context using the function template make_service<Service>(). If the service is already present, the service_already_exists exception is thrown.

Once a service reference is obtained from an execution_context object by calling use_service<>, that reference remains usable until a call to destroy_context().

5.10.1. execution_context constructor

execution_context();

Effects: Creates an object of class execution_context.

5.10.2. execution_context destructor

~execution_context();

Effects: Destroys an object of class execution_context. Performs shutdown_context() followed by destroy_context().

5.10.3. execution_context operations

void notify_fork(fork_event e);

Effects: For each service object svc in the set:
— If e == fork_event::prepare, performs svc->notify_fork(e) in reverse order of the beginning of service object lifetime (C++ Std, [basic.life]).
— Otherwise, performs svc->notify_fork(e) in order of the beginning of service object lifetime.

5.10.4. execution_context protected operations

void shutdown_context();

Effects: For each service object svc in the execution_context set, in reverse order of the beginning of service object lifetime (C++ Std, [basic.life]), performs svc->shutdown_service().

[Note: shutdown_context is an idempotent operation. —end note]

void destroy_context();

Effects: Destroys each service object in the execution_context set, in reverse order of the beginning of service object lifetime (C++ Std, [basic.life]).

[Note: destroy_context is an idempotent operation. —end note]

5.10.5. execution_context globals

The functions use_service, make_service and has_service shall not introduce data races as a result of concurrent calls to those functions from different threads.

template<class Service> Service& use_service(execution_context& ctx);

Let Key be Service::key_type if the nested typedef Service::key_type exists; otherwise, let Key be Service.

Requires: Service is a service class that is publicly and unambiguously derived from execution_context::service. If the nested typedef Service::key_type exists, Service is the same type as Service::key_type, or Service is publicly and unambiguously derived from Service::key_type, and Service::key_type is publicly and unambiguously derived from execution_context::service.

Effects: If an object of type Key does not already exist in the execution_context set identified by ctx, creates an object of type Service, initializing it with Service(ctx), and adds it to the set.

Returns: A reference to the corresponding service of ctx.

Notes: The reference returned remains valid until a call to destroy_context.

template<class Service, class... Args> Service&
  make_service(execution_context& ctx, Args&&... args);

Let Key be Service::key_type if the nested typedef Service::key_type exists; otherwise, let Key be Service.

Requires: Service is a service class that is publicly and unambiguously derived from execution_context::service. If the nested typedef Service::key_type exists, Service is the same type as Service::key_type, or Service is publicly and unambiguously derived from Service::key_type, and Service::key_type is publicly and unambiguously derived from execution_context::service. A service object of type Key does not already exist in the execution_context set identified by ctx.

Effects: Creates an object of type Service, initializing it with Service(ctx, forward<Args>(args)...), and adds it to the execution_context set identified by ctx.

Throws: service_already_exists if a corresponding service object of type Key is already present in the set.

Notes: The reference returned remains valid until a call to destroy_context.

template<class Service> bool has_service(execution_context& ctx) noexcept;

Let Key be Service::key_type if the nested typedef Service::key_type exists; otherwise, let Key be Service.

Requires: Service is a service class that is publicly and unambiguously derived from execution_context::service. If the nested typedef Service::key_type exists, Service is the same type as Service::key_type, or Service is publicly and unambiguously derived from Service::key_type, and Service::key_type is publicly and unambiguously derived from execution_context::service.

Returns: If an object of type Key is present in ctx, true; otherwise, false.

5.11. Class execution_context::service

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      class execution_context::service
      {
      protected:
        // construct / copy / destroy:

        service(execution_context& owner);
        service(const service&) = delete;
        service& operator=(const service&) = delete;
        virtual ~service();

        // service observers:

        execution_context& context() noexcept;

      private:
        friend class execution_context; // exposition only

        // service operations:

        virtual void shutdown_service() = 0;
        virtual void notify_fork(fork_event e);

        execution_context& context_; // exposition only
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

5.11.1. execution_context::service constructors

service(execution_context& owner);

Postconditions: &context_ == &owner.

5.11.2. execution_context::service observers

execution_context& context() noexcept;

Returns: context_.

5.11.3. execution_context::service operations

void notify_fork(fork_event e);

Effects: Does nothing.

5.12. Class template is_executor

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class T> struct is_executor : false_type {};

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

is_executor can be used to detect executor types satisfying the Executor type requirements.

Instantiations of the is_executor template shall meet the UnaryTypeTrait requirements (C++ Std, [meta.rqmts]). A program may specialize this template for a user-defined type T to have a BaseCharacteristic of integral_constant<int, N> with N > 0 to indicate that T should be treated as an executor type.

5.13. Executor argument tag

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      struct executor_arg_t { };
      constexpr executor_arg_t executor_arg = executor_arg_t();

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

The executor_arg_t struct is an empty structure type used as a unique type to disambiguate constructor and function overloading. Specifically, types may have constructors with executor_arg_t as the first argument, immediately followed by an argument of a type that satisfies the Executor requirements.

5.14. uses_executor

5.14.1. uses_executor trait

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class T, class Executor> struct uses_executor;

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

Remark: Detects whether T has a nested executor_type that is convertible from Executor. Meets the BinaryTypeTrait requirements (C++ Std, [meta.rqmts]). The implementation shall provide a definition that is derived from false_type. A program may specialize this template to derive from true_type for a user-defined type T that does not have a nested executor_type but nonetheless can be constructed with an executor where either:

— the first argument of a constructor has type executor_type and the second argument has type Executor; or

— the last argument of a constructor has type Executor.

5.14.2. uses-executor construction

Uses-executor construction with executor Executor refers to the construction of an object obj of type T, using constructor arguments v1, v2, ..., vN of types V1, V2, ..., VN, respectively, and an executor ex of type Executor, according to the following rules:

— if uses_executor<T, Executor>::value is false and is_constructible<T, V1, V2, ..., VN>::value is true, then obj is initialized as obj(v1, v2, ..., vN);

— otherwise, if uses_executor<T, Executor>::value is true and is_constructible<T, executor_arg_t, Executor, V1, V2, ..., VN>::value is true, then obj is initialized as obj(executor_arg, ex, v1, v2, ..., vN);

— otherwise, if uses_executor<T, Executor>::value is true and is_constructible<T, V1, V2, ..., VN, Executor>::value is true, then obj is initialized as obj(v1, v2, ..., vN, ex);

— otherwise, the request for uses-executor construction is ill-formed. [Note: An error will result if uses_executor<T, Executor>::value is true but the specific constructor does not take an executor. This definition prevents a silent failure to pass the executor to an element. —end note]

5.15. Class template associated_executor

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class T, class Executor = system_executor>
      struct associated_executor
      {
        typedef see below type;

        static type get(const T& t, const Executor& e = Executor()) noexcept;
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

A program may specialize this traits type if the T template parameter in the specialization is a user-defined type. The template parameter Executor shall be a type meeting Executor requirements.

Specializations of associated_executor shall satisfy the requirements in the table below. In this table, X is a specialization of associated_executor for the template parameter T; t is a const reference to an object of type T; and e is an object of type Executor.

Table 4. associated_executor specialization requirements

Expression

Return type

Note

typename X::type

A type meeting Executor requirements.

X::get(t)

X::type

Shall not exit via an exception.
Equivalent to X::get(t, Executor()).

X::get(t, e)

X::type

Shall not exit via an exception.


5.15.1. associated_executor members

typedef see below type;

Type: If T has a nested type executor_type, typename T::executor_type. Otherwise Executor.

type get(const T& t, const Executor& e = Executor()) noexcept;

Returns: If T has a nested type executor_type, t.get_executor(). Otherwise e.

5.16. Function get_associated_executor

template<class T>
  associated_executor_t<T> get_associated_executor(const T& t);

Returns: associated_executor<T>::get(t).

template<class T, class Executor>
  associated_executor_t<T, Executor>
    get_associated_executor(const T& t, const Executor& ex);

Returns: associated_executor<T, Executor>::get(t, ex).

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class T, class ExecutionContext>
  associated_executor_t<T, typename ExecutionContext::executor_type>
    get_associated_executor(const T& t, ExecutionContext& ctx);

Returns: associated_executor<T, typename ExecutionContext::executor_type>::get(t, ctx.get_executor()).

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

5.17. Class template executor_wrapper

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class T, class Executor>
      class executor_wrapper
      {
      public:
        // types:

        typedef T wrapped_type;
        typedef Executor executor_type;
        typedef see below result_type;          // not always defined
        typedef see below argument_type;        // not always defined
        typedef see below first_argument_type;  // not always defined
        typedef see below second_argument_type; // not always defined

        // construct / copy / destroy:

        executor_wrapper(T t, const Executor& ex);
        executor_wrapper(const executor_wrapper& other) = default;
        executor_wrapper(executor_wrapper&& other) = default;
        template<class U, class OtherExecutor>
          executor_wrapper(const executor_wrapper<U, OtherExecutor>& other);
        template<class U, class OtherExecutor>
          executor_wrapper(executor_wrapper<U, OtherExecutor>&& other);
        template<class U, class OtherExecutor>
          executor_wrapper(executor_arg_t, const Executor& ex,
            const executor_wrapper<U, OtherExecutor>& other);
        template<class U, class OtherExecutor>
          executor_wrapper(executor_arg_t, const Executor& ex,
            executor_wrapper<U, OtherExecutor>&& other);

        ~executor_wrapper();

        // executor wrapper access:

        T& unwrap() noexcept;
        const T& unwrap() const noexcept;
        executor_type get_executor() const noexcept;

        // executor wrapper invocation:

        template<class... Args>
          result_of_t<cv T&(Args&&...)> operator()(Args&&... args) cv;

      private:
        Executor ex_; // exposition only
        T wrapped_; // exposition only
      };

      template<class T, class Executor, class Signature>
      struct handler_type<executor_wrapper<T, Executor>, Signature>
      {
        typedef executor_wrapper<handler_type_t<T, Signature>, Executor> type;
      };

      template<class T, class Executor>
        class async_result<executor_wrapper<T, Executor>>;

      template<class T, class Executor, class Alloc>
        struct associated_allocator<executor_wrapper<T, Executor>, Alloc>;

      template<class T, class Executor, class Executor1>
        struct associated_executor<executor_wrapper<T, Executor>, Executor1>;

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

executor_wrapper<T, Executor> is a wrapper around an object or function of type T, and an executor object of type Executor satisfying Executor requirements.

executor_wrapper<T, Executor> has a weak result type (C++ Std, [func.require]). If T is a function type, result_type shall be a synonym for the return type of T.

The template instantiation executor_wrapper<T, Executor> shall define a nested type named argument_type as a synonym for T1 only if the type T is any of the following:

— a function type or a pointer to function type taking one argument of type T1

— a pointer to member function R T0::f cv (where cv represents the member function’s cv-qualifiers); the type T1 is cv T0*

— a class type with a member type argument_type; the type T1 is T::argument_type.

The template instantiation executor_wrapper<T, Executor> shall define two nested types named first_argument_type and second_argument_type as synonyms for T1 and T2, respectively, only if the type T is any of the following:

— a function type or a pointer to function type taking two arguments of types T1 and T2

— a pointer to member function R T0::f(T2) cv (where cv represents the member function’s cv-qualifiers); the type T1 is cv T0*

— a class type with member types first_argument_type and second_argument_type; the type T1 is T::first_argument_type. and the type T2 is T::second_argument_type.

5.17.1. executor_wrapper constructors

executor_wrapper(T t, const Executor& ex);

Effects: Constructs an object of type executor_wrapper<T, Executor>. Initializes ex_ with the value ex. If uses_executor<T, Executor>::value is true, performs uses-executor construction to initialize wrapped_ with wrapped_(executor_arg, ex_, std::move(t)); otherwise, initializes wrapped_ with wrapped_(std::move(t)).

template<class U, class OtherExecutor>
  executor_wrapper(const executor_wrapper<U, OtherExecutor>& other);

Requires: U is T or convertible to T. OtherExecutor is Executor or convertible to Executor.

Effects: Constructs an object of type executor_wrapper<T, Executor>. Initializes ex_ with other.get_executor(). If uses_executor<T, Executor>::value is true, performs uses-executor construction to initialize wrapped_ with wrapped_(executor_arg, ex_, other.unwrap()); otherwise, initializes wrapped_ with wrapped_(other.unwrap()).

template<class U, class OtherExecutor>
  executor_wrapper(executor_wrapper<U, OtherExecutor>&& other);

Requires: U is T or convertible to T. OtherExecutor is Executor or convertible to Executor.

Effects: Constructs an object of type executor_wrapper<T, Executor>. Initializes ex_ with other.get_executor(). If uses_executor<T, Executor>::value is true, performs uses-executor construction to initialize wrapped_ with wrapped_(executor_arg, ex_, std::move(other.unwrap())); otherwise, initializes wrapped_ with wrapped_(std::move(other.unwrap())).

template<class U, class OtherExecutor>
  executor_wrapper(executor_arg_t, const Executor& ex,
    const executor_wrapper<U, OtherExecutor>& other);

Requires: U is T or convertible to T.

Effects: Constructs an object of type executor_wrapper<T, Executor>. Initializes ex_ with ex. If uses_executor<T, Executor>::value is true, performs uses-executor construction to initialize wrapped_ with wrapped_(executor_arg, ex_, other.unwrap()); otherwise, initializes wrapped_ with wrapped_(other.unwrap()).

template<class U, class OtherExecutor>
  executor_wrapper(executor_arg_t, const Executor& ex,
    executor_wrapper<U, OtherExecutor>&& other);

Requires: U is T or convertible to T.

Effects: Constructs an object of type executor_wrapper<T, Executor>. Initializes ex_ with ex. If uses_executor<T, Executor>::value is true, performs uses-executor construction to initialize wrapped_ with wrapped_(executor_arg, ex_, std::move(other.unwrap())); otherwise, initializes wrapped_ with wrapped_(std::move(other.unwrap())).

5.17.2. executor_wrapper access

T& unwrap() noexcept;
const T& unwrap() const noexcept;

Returns: wrapped_.

executor_type get_executor() const noexcept;

Returns: executor_.

5.17.3. executor_wrapper invocation

template<class... Args>
  result_of_t<cv T&(Args&&...)> operator()(Args&&... args) cv;

Returns: INVOKE(unwrap(), forward<Args>(args)...) (C++ Std, [func.require]).

Remarks: operator() is described for exposition only. Implementations are not required to provide an actual executor_wrapper::operator(). Implementations are permitted to support executor_wrapper function invocation through multiple overloaded operators or through other means.

5.17.4. Class template specialization async_result

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class T, class Executor>
      class async_result<executor_wrapper<T, Executor>>
      {
      public:
        typedef typename async_result<T>::type type;

        explicit async_result(executor_wrapper<T, Executor>& wrapper);
        async_result(const async_result&) = delete;
        async_result& operator=(const async_result&) = delete;

        type get();

      private:
        async_result<T> wrapped_; // exposition only
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

The implementation shall provide a specialization of async_result that meets the async_result specialization requirements.

explicit async_result(executor_wrapper<T, Executor>& wrapper);

Effects: Initializes wrapped_ with wrapped_(wrapper.unwrap()).

type get();

Returns: wrapped_.get().

5.17.5. Class template specialization associated_allocator

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class T, class Executor, class Alloc>
        struct associated_allocator<executor_wrapper<T, Executor>, Alloc>
      {
        typedef associated_allocator_t<T, Alloc> type;

        static type get(const executor_wrapper<T, Executor>& w,
                        const Alloc& a = Alloc()) noexcept;
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

The implementation shall provide a specialization of associated_allocator that meets the associated_allocator specialization requirements.

static type get(const executor_wrapper<T, Executor>& w,
                const Alloc& a = Alloc()) noexcept;

Returns: associated_allocator<T, Alloc>::get(w.unwrap(), a).

5.17.6. Class template specialization associated_executor

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class T, class Executor, class Executor1>
        struct associated_executor<executor_wrapper<T, Executor>, Executor1>
      {
        typedef Executor type;

        static type get(const executor_wrapper<T, Executor>& w,
                        const Executor1& e = Executor1()) noexcept;
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

The implementation shall provide a specialization of associated_executor that meets the associated_executor specialization requirements.

static type get(const executor_wrapper<T, Executor>& w,
                const Executor1& e = Executor1()) noexcept;

Returns: w.get_executor().

5.18. Function wrap

template<class Executor, class T>
  executor_wrapper<decay_t<T>, Executor> wrap(const Executor& ex, T&& t);

Returns: executor_wrapper<decay_t<T>, Executor>(forward<T>(t), ex).

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class ExecutionContext, class CompletionToken>
  executor_wrapper<decay_t<T>, typename ExecutionContext::executor_type>
    wrap(ExecutionContext& ctx, T&& t);

Returns: executor_wrapper<decay_t<T>, typename ExecutionContext::executor_type>(forward<T>(t), ctx.get_executor()).

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

5.19. Class template executor_work

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class Executor>
      class executor_work
      {
      public:
        // types:

        typedef Executor executor_type;

        // construct / copy / destroy:

        explicit executor_work(const executor_type& ex) noexcept;
        executor_work(const executor_work& other) noexcept;
        executor_work(executor_work&& other) noexcept;

        executor_work operator=(const executor_type&) = delete;

        ~executor_work();

        // executor work observers:

        executor_type get_executor() const noexcept;
        bool owns_work() const noexcept;

        // executor work modifiers:

        void reset() noexcept;

      private:
        Executor ex_; // exposition only
        bool owns_; // exposition only
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

5.19.1. executor_work constructors

explicit executor_work(const executor_type& ex) noexcept;

Effects: Constructs an object of class executor_work, initializing ex_ with ex, and then performing ex_.on_work_started().

Postconditions: ex == ex_ and owns_ == true.

executor_work(const executor_work& other) noexcept;

Effects: Constructs an object of class executor_work, initializing ex_ with other.ex. If other.owns_ == true, performs ex_.on_work_started().

Postconditions: ex == other.ex_ and owns_ == other.owns_.

executor_work(executor_work&& other) noexcept;

Effects: Constructs an object of class executor_work, initializing ex_ with other.ex and owns_ with other.owns_.

Postconditions: ex is equal to the prior value of other.ex_, owns_ is equal to the prior value of other.owns_, and other.owns_ == false.

5.19.2. executor_work destructor

Effects: If owns_ is true, performs ex.on_work_finished().

5.19.3. executor_work observers

executor_type get_executor() const noexcept;

Returns: ex_.

bool owns_work() const noexcept;

Returns: owns_.

5.19.4. executor_work modifiers

void reset() noexcept;

Effects: If owns_ is true, performs ex.on_work_finished().

Postconditions: owns_ == false.

5.20. Function make_work

template<class Executor>
  executor_work<Executor>
    make_work(const Executor& ex);

Returns: executor_work<Executor>(ex).

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class ExecutionContext>
  executor_work<typename ExecutionContext::executor_type>
    make_work(ExecutionContext& ctx);

Returns: An object of type executor_work<typename ExecutionContext::executor_type> initialized with the result of ctx.get_executor().

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

template<class T>
  executor_work<associated_executor_t<T>>
    make_work(const T& t);

Returns: An object of type executor_work<associated_executor_t<T>> initialized with the result of associated_executor<T>::get(t).

Remarks: This function shall not participate in overload resolution unless is_executor<T>::value is false and is_convertible<T&, execution_context&>::value is false.

template<class T, class Executor>
  executor_work<associated_executor_t<T, Executor>>
    make_work(const T& t, const Executor& ex);

Returns: An object of type executor_work<associated_executor_t<T, Executor>> initialized with the result of associated_executor<T, Executor>::get(t, ex).

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class T, class ExecutionContext>
  executor_work<associated_executor_t<T, typename ExecutionContext::executor_type>>
    make_work(const T& t, ExecutionContext& ctx);

Returns: An object of type executor_work<associated_executor_t<T, typename ExecutionContext::executor_type>> initialized with the result of associated_executor<T, typename ExecutionContext::executor_type>::get(t, ctx.get_executor()).

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

5.21. Class system_executor

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      class system_executor
      {
      public:

        // executor operations:

        execution_context& context() noexcept;

        void on_work_started() noexcept;
        void on_work_finished() noexcept;

        template<class Func, class Alloc>
          void dispatch(Func&& f, const Alloc& a);
        template<class Func, class Alloc>
          void post(Func&& f, const Alloc& a);
        template<class Func, class Alloc>
          void defer(Func&& f, const Alloc& a);
      };

      bool operator==(const system_executor&, const system_executor&) noexcept;
      bool operator!=(const system_executor&, const system_executor&) noexcept;

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

Class system_executor is a DefaultConstructible type (C++ Std, [defaultconstructible]) satisfying Executor requirements. It represents a set of rules where function objects are permitted to execute on any thread.

To satisfy the executor requirements for the post and defer member functions, the system executor may allocate thread objects to run the submitted function objects. If std::exit is called, and there remain unexecuted functions objects that have been submitted using post or defer, the implementation shall discard these function objects without calling them.

5.21.1. system_executor operations

execution_context& context() noexcept;

Returns: A reference to a static-duration object of a type derived from execution_context.

void on_work_started() noexcept;

Effects: Does nothing.

void on_work_finished() noexcept;

Effects: Does nothing.

template<class Func, class Alloc>
  void dispatch(Func&& f, const Alloc& a);

Effects: Calls DECAY_COPY(forward<Func>(f))().

template<class Func, class Alloc>
  void post(Func&& f, const Alloc& a);

Effects: Calls DECAY_COPY(forward<Func>(f))() as if in a thread of execution represented by a thread object, with the call to DECAY_COPY() being evaluated in the thread that called post. Any exception propagated from the execution of DECAY_COPY(forward<Func>(f))() shall result in a call to std::terminate.

template<class Func, class Alloc>
  void defer(Func&& f, const Alloc& a);

Effects: Calls DECAY_COPY(forward<Func>(f))() as if in a thread of execution represented by a thread object, with the call to DECAY_COPY() being evaluated in the thread that called defer. Any exception propagated from the execution of DECAY_COPY(forward<Func>(f))() shall result in a call to std::terminate.

5.21.2. system_executor comparisons

bool operator==(const system_executor&, const system_executor&) noexcept;

Returns: true.

bool operator!=(const system_executor&, const system_executor&) noexcept;

Returns: false.

5.22. Class bad_executor

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      class bad_executor : public exception
      {
      public:
        // constructor:
        bad_executor() noexcept;
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

An exception of type bad_executor is thrown by executor member functions dispatch, post and defer when the executor object has no target.

5.22.1. bad_executor constructor

bad_executor() noexcept;

Effects: Constructs a bad_executor object.

5.23. Class executor

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      class executor
      {
      public:
        // construct / copy / destroy:

        executor() noexcept;
        executor(nullptr_t) noexcept;
        executor(const executor& e) noexcept;
        executor(executor&& e) noexcept;
        template<class Executor> executor(Executor e);
        template<class Executor, class Alloc>
          executor(allocator_arg_t, const Alloc& a, Executor e);

        executor& operator=(const executor& e) noexcept;
        executor& operator=(executor&& e) noexcept;
        executor& operator=(nullptr_t) noexcept;
        template<class Executor> executor& operator=(Executor e);

        ~executor();

        // executor modifiers:

        void swap(executor& other) noexcept;
        template<class Executor, class Alloc>
          void assign(Executor e, const Alloc& e);

        // executor operations:

        execution_context& context() noexcept;

        void on_work_started() noexcept;
        void on_work_finished() noexcept;

        template<class Func, class Alloc>
          void dispatch(Func&& f, const Alloc& a);
        template<class Func, class Alloc>
          void post(Func&& f, const Alloc& a);
        template<class Func, class Alloc>
          void defer(Func&& f, const Alloc& a);

        // executor capacity:

        explicit operator bool() const noexcept;

        // executor target access:

        const type_info& target_type() const noexcept;
        template<class Executor> Executor* target() noexcept;
        template<class Executor> const Executor* target() const noexcept;
      };

      template<> struct is_executor<executor> : true_type {};

      // executor comparisons:

      bool operator==(const executor& a, const executor& b) noexcept;
      bool operator==(const executor& e, nullptr_t) noexcept;
      bool operator==(nullptr_t, const executor& e) noexcept;
      bool operator!=(const executor& a, const executor& b) noexcept;
      bool operator!=(const executor& e, nullptr_t) noexcept;
      bool operator!=(nullptr_t, const executor& e) noexcept;

      // executor specialized algorithms:

      void swap(executor& a, executor& b) noexcept;

    } // inline namespace concurrency_v1
  } // namespace experimental

  template<class Alloc>
    struct uses_allocator<std::experimental::concurrency_v1::executor, Alloc>
      : true_type {};

} // namespace std

The executor class provides a polymorphic wrapper for types that satisfy the Executor requirements. The target object is the executor object that is held by the wrapper. The executor type itself meets the requirements for an Executor.

[Note: To meet the noexcept requirements for executor copy constructors and move constructors, implementations may share a target between two or more executor objects. —end note]

5.23.1. executor constructors

executor() noexcept;

Postconditions: !*this.

executor(nullptr_t) noexcept;

Postconditions: !*this.

executor(const executor& e) noexcept;

Postconditions: !*this if !e; otherwise, *this targets e.target() or a copy of e.target().

executor(executor&& e) noexcept;

Effects: If !e, *this has no target; otherwise, moves e.target() or move-constructs the target of e into the target of *this, leaving e in a valid state with an unspecified value.

template<class Executor> executor(Executor e);

Requires: Executor shall satisfy the Executor requirements.

Effects: *this targets a copy of e initialized with std::move(e).

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class Executor, class Alloc>
  executor(allocator_arg_t, const Alloc& a, Executor e);

Requires: Executor shall satisfy the Executor requirements. Allocator conforms to the Allocator requirements (C++ Std, [allocator.requirements]).

Effects: *this targets a copy of e initialized with std::move(e).

A copy of the allocator argument is used to allocate memory, if necessary, for the internal data structures of the constructed executor object.

5.23.2. executor assignment

executor& operator=(const executor& e) noexcept;

Effects: executor(e).swap(*this).

Returns: *this.

executor& operator=(executor&& e) noexcept;

Effects: Replaces the target of *this with the target of e, leaving e in a valid state with an unspecified value.

Returns: *this.

executor& operator=(nullptr_t) noexcept;

Effects: executor(nullptr).swap(*this).

Returns: *this.

template<class Executor> executor& operator=(Executor e);

Effects: executor(std::move(e)).swap(*this).

Returns: *this.

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

5.23.3. executor destructor

~executor();

Effects: If *this != nullptr, releases shared ownership of, or destroys, the target of *this.

5.23.4. executor modifiers

void swap(executor& other) noexcept;

Effects: Interchanges the targets of *this and other.

template<class Executor, class Alloc>
  void assign(Executor e, const Alloc& e);

Effects: executor(allocator_arg, a, std::move(e)).swap(*this).

5.23.5. executor operations

execution_context& context() noexcept;

Returns: e.context(), where e is the target object of *this.

void on_work_started() noexcept;

Effects: e.on_work_started(), where e is the target object of *this.

void on_work_finished() noexcept;

Effects: e.on_work_finished(), where e is the target object of *this.

template<class Func, class Alloc>
  void dispatch(Func&& f, const Alloc& a);

Effects: e.dispatch(g, a), where e is the target object of *this, and g is a function object of unspecified type that, when called as g(), performs DECAY_COPY(f)().

template<class Func, class Alloc>
  void post(Func&& f, const Alloc& a);

Effects: e.post(g, a), where e is the target object of *this, and g is a function object of unspecified type that, when called as g(), performs DECAY_COPY(f)().

template<class Func, class Alloc>
  void defer(Func&& f, const Alloc& a);

Effects: e.defer(g, a), where e is the target object of *this, and g is a function object of unspecified type that, when called as g(), performs DECAY_COPY(f)().

5.23.6. executor capacity

explicit operator bool() const noexcept;

Returns: true if *this has a target, otherwise false,

5.23.7. executor target access

const type_info& target_type() const noexcept;

Returns: If *this has a target of type T, typeid(T); otherwise, typeid(void).

template<class Executor> Executor* target() noexcept;
template<class Executor> const Executor* target() const noexcept;

Requires: Executor shall satisfy the Executor requirements.

Returns: If target_type() == typeid(Executor) a pointer to the stored executor target; otherwise a null pointer.

5.23.8. executor comparisons

bool operator==(const executor& a, const executor& b) noexcept;

Returns:
true if !a and !b;
true if a and b share a target;
true if e and f are the same type and e == f, where e is the target object of a and f is the target object of b;
— otherwise false.

bool operator==(const executor& e, nullptr_t) noexcept;
bool operator==(nullptr_t, const executor& e) noexcept;

Returns: !e.

bool operator!=(const executor& a, const executor& b) noexcept;

Returns: !(a == b).

bool operator!=(const executor& e, nullptr_t) noexcept;
bool operator!=(nullptr_t, const executor& e) noexcept;

Returns: (bool) e.

5.23.9. executor specialized algorithms

void swap(executor& a, executor& b) noexcept;

Effects: a.swap(b).

5.24. Function dispatch

template<class CompletionToken>
  auto dispatch(CompletionToken&& token);

Let the type Handler be the handler function object type determined by performing handler_type_t<CompletionToken, void()>.

Requires: The type Handler must satisfy the MoveConstructible requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.

Effects:
— Constructs a function object handler of type Handler, initialized with handler(forward<CompletionToken>(token)).
— Constructs an object result of type async_result<Handler>, initializing the object as result(handler).
— Obtains the handler's associated executor object ex by performing get_associated_executor(handler).
— Obtains the handler's associated allocator object alloc by performing get_associated_allocator(handler).
— Performs ex.dispatch(std::move(handler), alloc).

Returns: result.get().

template<class Executor, class CompletionToken>
  auto dispatch(const Executor& ex, CompletionToken&& token);

Let the type Handler be the handler function object type determined by performing handler_type_t<CompletionToken, void()>.

Requires: The type Handler must satisfy the MoveConstructible requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.

Effects:
— Constructs a function object handler of type Handler, initialized with handler(forward<CompletionToken>(token)).
— Constructs an object result of type async_result<Handler>, initializing the object as result(handler).
— Obtains the handler's associated executor object ex1 by performing get_associated_executor(handler).
— Creates a work object w by performing make_work(ex1).
— Obtains the handler's associated allocator object alloc by performing get_associated_allocator(handler).
— Constructs a function object f with a function call operator that performs ex1.dispatch(std::move(handler), alloc) followed by w.reset().
— Performs ex.dispatch(std::move(f), alloc).

Returns: result.get().

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class ExecutionContext, class CompletionToken>
  auto dispatch(ExecutionContext& ctx, CompletionToken&& token);

Returns: std::experimental::concurrency_v1::dispatch(ctx.get_executor(), forward<CompletionToken>(token)).

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

5.25. Function post

template<class CompletionToken>
  auto post(CompletionToken&& token);

Let the type Handler be the handler function object type determined by performing handler_type_t<CompletionToken, void()>.

Requires: The type Handler must satisfy the MoveConstructible requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.

Effects:
— Constructs a function object handler of type Handler, initialized with handler(forward<CompletionToken>(token)).
— Constructs an object result of type async_result<Handler>, initializing the object as result(handler).
— Obtains the handler's associated executor object ex by performing get_associated_executor(handler).
— Obtains the handler's associated allocator object alloc by performing get_associated_allocator(handler).
— Performs ex.post(std::move(handler), alloc).

Returns: result.get().

template<class Executor, class CompletionToken>
  auto post(const Executor& ex, CompletionToken&& token);

Let the type Handler be the handler function object type determined by performing handler_type_t<CompletionToken, void()>.

Requires: The type Handler must satisfy the MoveConstructible requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.

Effects:
— Constructs a function object handler of type Handler, initialized with handler(forward<CompletionToken>(token)).
— Constructs an object result of type async_result<Handler>, initializing the object as result(handler).
— Obtains the handler's associated executor object ex1 by performing get_associated_executor(handler).
— Creates a work object w by performing make_work(ex1).
— Obtains the handler's associated allocator object alloc by performing get_associated_allocator(handler).
— Constructs a function object f with a function call operator that performs ex1.dispatch(std::move(handler), alloc) followed by w.reset().
— Performs ex.post(std::move(f), alloc).

Returns: result.get().

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class ExecutionContext, class CompletionToken>
  auto post(ExecutionContext& ctx, CompletionToken&& token);

Returns: std::experimental::concurrency_v1::post(ctx.get_executor(), forward<CompletionToken>(token)).

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

5.26. Function defer

template<class CompletionToken>
  auto defer(CompletionToken&& token);

Let the type Handler be the handler function object type determined by performing handler_type_t<CompletionToken, void()>.

Requires: The type Handler must satisfy the MoveConstructible requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.

Effects:
— Constructs a function object handler of type Handler, initialized with handler(forward<CompletionToken>(token)).
— Constructs an object result of type async_result<Handler>, initializing the object as result(handler).
— Obtains the handler's associated executor object ex by performing get_associated_executor(handler).
— Obtains the handler's associated allocator object alloc by performing get_associated_allocator(handler).
— Performs ex.defer(std::move(handler), alloc).

Returns: result.get().

template<class Executor, class CompletionToken>
  auto defer(const Executor& ex, CompletionToken&& token);

Let the type Handler be the handler function object type determined by performing handler_type_t<CompletionToken, void()>.

Requires: The type Handler must satisfy the MoveConstructible requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.

Effects:
— Constructs a function object handler of type Handler, initialized with handler(forward<CompletionToken>(token)).
— Constructs an object result of type async_result<Handler>, initializing the object as result(handler).
— Obtains the handler's associated executor object ex1 by performing get_associated_executor(handler).
— Creates a work object w by performing make_work(ex1).
— Obtains the handler's associated allocator object alloc by performing get_associated_allocator(handler).
— Constructs a function object f with a function call operator that performs ex1.dispatch(std::move(handler), alloc) followed by w.reset().
— Performs ex.defer(std::move(f), alloc).

Returns: result.get().

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class ExecutionContext, class CompletionToken>
  auto defer(ExecutionContext& ctx, CompletionToken&& token);

Returns: std::experimental::concurrency_v1::defer(ctx.get_executor(), forward<CompletionToken>(token)).

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

5.27. Header <experimental/strand> synopsis

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class Executor>
        class strand;

      template<class Executor>
        bool operator==(const strand<Executor>& a, const strand<Executor>& b);
      template<class Executor>
        bool operator!=(const strand<Executor>& a, const strand<Executor>& b);

      template<class Executor>
        struct is_executor<strand<Executor>> : true_type {};

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

5.28. Class template strand

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template<class Executor>
      class strand
      {
      public:
        // types:

        typedef Executor inner_executor_type;

        // construct / copy / destroy:

        strand();
        explicit strand(Executor ex);
        strand(const strand& other);
        strand(strand&& other);
        template<class OtherExecutor> strand(const strand<OtherExecutor>& other);
        template<class OtherExecutor> strand(strand<OtherExecutor>&& other);

        strand& operator=(const strand& other);
        strand& operator=(strand&& other);
        template<class OtherExecutor> strand& operator=(const strand<OtherExecutor>& other);
        template<class OtherExecutor> strand& operator=(strand<OtherExecutor>&& other);

        ~strand();

        // strand operations:

        inner_executor_type get_inner_executor() const noexcept;

        bool running_in_this_thread() const noexcept;

        execution_context& context() noexcept;

        void on_work_started() noexcept;
        void on_work_finished() noexcept;

        template<class Func, class Alloc>
          void dispatch(Func&& f, const Alloc& a);
        template<class Func, class Alloc>
          void post(Func&& f, const Alloc& a);
        template<class Func, class Alloc>
          void defer(Func&& f, const Alloc& a);

      private:
        Executor inner_ex_; // exposition only
      };

      bool operator==(const strand<Executor>& a, const strand<Executor>& b);
      bool operator!=(const strand<Executor>& a, const strand<Executor>& b);

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

strand<Executor> is a wrapper around an object of type Executor satisfying Executor requirements. strand<Executor> satisfies the Executor requirements.

A strand provides guarantees of ordering and non-concurrency. Given:

— strand objects s1 and s2 such that s1 == s2

— a function object f1 added to the strand s1 using post or defer, or using dispatch when s1.running_in_this_thread() == false

— a function object f2 added to the strand s2 using post or defer, or using dispatch when s2.running_in_this_thread() == false

then the implementation shall invoke f1 and f2 such that:

— the invocation of f1 is not concurrent with the invocation of f2

— the invocation of f1 synchronizes with the invocation of f2.

Furthermore, if the addition of f1 happens before the addition of f2, then the invocation of f1 happens before the invocation of f2.

The strand copy constructors, comparison operators, and member functions shall not introduce data races as a result of concurrent calls to those functions from different threads.

If any function f executed by the strand throws an exception, the subsequent strand state shall be as if f had exited without throwing an exception.

5.28.1. strand constructors

strand();

Effects: Constructs an object of class strand<Executor> that represents a unique ordered, non-concurrent state. Initializes inner_ex_ with inner_ex_().

Remarks: This overload shall not participate in overload resolution unless Executor satisfies the DefaultConstructible requirements (C++ Std, [defaultconstructible]).

explicit strand(Executor ex);

Effects: Constructs an object of class strand<Executor> that represents a unique ordered, non-concurrent state. Initializes inner_ex_ with inner_ex_(ex).

strand(const strand& other);

Effects: Constructs an object of class strand<Executor>. Initalizes inner_ex_ with inner_ex_(other.inner_ex_).

Postconditions:
*this == other
get_inner_executor() == other.get_inner_executor()

strand(strand&& other);

Effects: Constructs an object of class strand<Executor>. Initalizes inner_ex_ with inner_ex_(std::move(other.inner_ex_)).

Postconditions:
*this is equal to the prior value of other
get_inner_executor() == other.get_inner_executor()

template<class OtherExecutor> strand(const strand<OtherExecutor>& other);

Requires: OtherExecutor is convertible to Executor.

Effects: Constructs an object of class strand<Executor>. Initalizes inner_ex_ with inner_ex_(other.inner_ex_).

Postconditions: *this == other.

template<class OtherExecutor> strand(strand<OtherExecutor>&& other);

Requires: OtherExecutor is convertible to Executor.

Effects: Constructs an object of class strand<Executor>. Initalizes inner_ex_ with inner_ex_(other.inner_ex_).

Postconditions: *this is equal to the prior value of other.

5.28.2. strand assignment

strand& operator=(const strand& other);

Requires: Executor is Assignable (C++ Std [assignable]).

Postconditions:
*this == other
get_inner_executor() == other.get_inner_executor()

Returns: *this.

strand& operator=(strand&& other);

Requires: Executor is Assignable (C++ Std [assignable]).

Postconditions:
*this is equal to the prior value of other
get_inner_executor() == other.get_inner_executor()

Returns: *this.

template<class OtherExecutor> strand& operator=(const strand<OtherExecutor>& other);

Requires: OtherExecutor is convertible to Executor. Executor is Assignable (C++ Std [assignable]).

Effects: Equivalent to strand<Executor>::operator=(Executor(other)).

Returns: *this.

template<class OtherExecutor> strand& operator=(strand<OtherExecutor>&& other);

Requires: OtherExecutor is convertible to Executor. Executor is Assignable (C++ Std [assignable]).

Effects: Equivalent to strand<Executor>::operator=(Executor(std::move(other))).

Returns: *this.

5.28.3. strand destructor

~strand();

Effects: Destroys an object of class strand<Executor>. Function objects that were added to the strand but have not yet been executed shall still be executed in a way that meets the guarantees of ordering and non-concurrency.

5.28.4. strand operations

inner_executor_type get_inner_executor() const noexcept;

Returns: inner_ex_.

bool running_in_this_thread() const noexcept;

Returns: true if the current thread of execution is invoking a function that was submitted to the strand, or to any other strand object s such that s == *this, using dispatch, post or defer; otherwise false.

execution_context& context() noexcept;

Returns: inner_ex_.context().

void on_work_started() noexcept;

Effects: Calls inner_ex_.on_work_started().

void on_work_finished() noexcept;

Effects: Calls inner_ex_.on_work_finished().

template<class Func, class Alloc>
  void dispatch(Func&& f, const Alloc& a);

Effects: If running_in_this_thread() is true, calls DECAY_COPY(forward<Func>(f))(). Otherwise, requests invocation of f, as if by forwarding the function object f and allocator a to the executor inner_ex_, such that the guarantees of ordering and non-concurrency are met.

If f exits via an exception, and the execution of f is performed in the current thread and before dispatch returns, the exception shall propagate to the caller of dispatch()

template<class Func, class Alloc>
  void post(Func&& f, const Alloc& a);

Effects: Requests invocation of f, as if by forwarding the function object f and allocator a to the executor inner_ex_, such that the guarantees of ordering and non-concurrency are met.

template<class Func, class Alloc>
  void defer(Func&& f, const Alloc& a);

Effects: Requests invocation of f, as if by forwarding the function object f and allocator a to the executor inner_ex_, such that the guarantees of ordering and non-concurrency are met.

5.28.5. strand comparisons

bool operator==(const strand<Executor>& a, const strand<Executor>& b);

Returns: true, if the strand objects share the same ordered, non-concurrent state; otherwise false.

bool operator!=(const strand<Executor>& a, const strand<Executor>& b);

Returns: !(a == b).

5.29. Header <experimental/timer> synopsis

#include <chrono>

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

    // dispatch_at:

    template<class Clock, class Duration, class... CompletionTokens>
      auto dispatch_at(const chrono::time_point<Clock, Duration>& abs_time,
                       CompletionTokens&&... tokens);
    template<class Clock, class Duration, class Executor, class... CompletionTokens>
      auto dispatch_at(const chrono::time_point<Clock, Duration>& abs_time,
                       const Executor& ex, CompletionTokens&&... tokens);
    template<class Clock, class Duration, class ExecutionContext, class... CompletionTokens>
      auto dispatch_at(const chrono::time_point<Clock, Duration>& abs_time,
                       ExecutionContext& ctx, CompletionTokens&&... tokens);

    // post_at:

    template<class Clock, class Duration, class... CompletionTokens>
      auto post_at(const chrono::time_point<Clock, Duration>& abs_time,
                   CompletionTokens&&... tokens);
    template<class Clock, class Duration, class Executor, class... CompletionTokens>
      auto post_at(const chrono::time_point<Clock, Duration>& abs_time,
                   const Executor& ex, CompletionTokens&&... tokens);
    template<class Clock, class Duration, class ExecutionContext, class... CompletionTokens>
      auto post_at(const chrono::time_point<Clock, Duration>& abs_time,
                   ExecutionContext& ctx, CompletionTokens&&... tokens);

    // defer_at:

    template<class Clock, class Duration, class... CompletionTokens>
      auto defer_at(const chrono::time_point<Clock, Duration>& abs_time,
        CompletionTokens&&... tokens);
    template<class Clock, class Duration, class Executor, class... CompletionTokens>
      auto defer_at(const chrono::time_point<Clock, Duration>& abs_time,
        const Executor& ex, CompletionTokens&&... tokens);
    template<class Clock, class Duration, class ExecutionContext, class... CompletionTokens>
      auto defer_at(const chrono::time_point<Clock, Duration>& abs_time,
                    ExecutionContext& ctx, CompletionTokens&&... tokens);

    // dispatch_after:

    template<class Rep, class Period, class... CompletionTokens>
      auto dispatch_after(const chrono::duration<Rep, Period>& rel_time,
                          CompletionTokens&&... token);
    template<class Rep, class Period, class Executor, class... CompletionTokens>
      auto dispatch_after(const chrono::duration<Rep, Period>& rel_time,
                          const Executor& ex, CompletionTokens&&... tokens);
    template<class Rep, class Period, class ExecutionContext, class... CompletionTokens>
      auto dispatch_after(const chrono::duration<Rep, Period>& rel_time,
                          ExecutionContext& ctx, CompletionTokens&&... tokens);

    // post_after:

    template<class Rep, class Period, class... CompletionTokens>
      auto post_after(const chrono::duration<Rep, Period>& rel_time,
                      CompletionTokens&&... token);
    template<class Rep, class Period, class Executor, class... CompletionTokens>
      auto post_after(const chrono::duration<Rep, Period>& rel_time,
                      const Executor& ex, CompletionTokens&&... tokens);
    template<class Rep, class Period, class ExecutionContext, class... CompletionTokens>
      auto post_after(const chrono::duration<Rep, Period>& rel_time,
                      ExecutionContext& ctx, CompletionTokens&&... tokens);

    // defer_after:

    template<class Rep, class Period, class... CompletionTokens>
      auto defer_after(const chrono::duration<Rep, Period>& rel_time,
                       CompletionTokens&&... token);
    template<class Rep, class Period, class Executor, class... CompletionTokens>
      auto defer_after(const chrono::duration<Rep, Period>& rel_time,
                       const Executor& ex, CompletionTokens&&... tokens);
    template<class Rep, class Period, class ExecutionContext, class... CompletionTokens>
      auto defer_after(const chrono::duration<Rep, Period>& rel_time,
                       ExecutionContext& ctx, CompletionTokens&&... tokens);

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

5.30. Function dispatch_at

template<class Clock, class Duration, class... CompletionTokens>
  auto dispatch_at(const chrono::time_point<Clock, Duration>& abs_time,
                   CompletionTokens&&... tokens);

Let the type Handler be the handler function object type determined by performing handler_type_t<CompletionToken, void()>.

Requires: The type Handler must satisfy the MoveConstructible requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.

Effects:
— Constructs a function object handler of type Handler, initialized with handler(forward<CompletionToken>(token)).
— Constructs an object result of type async_result<Handler>, initializing the object as result(handler).
— Obtains the handler's associated executor object ex by performing get_associated_executor(handler).
— Creates a work object w by performing make_work(ex).
— Obtains the handler's associated allocator object alloc by performing get_associated_allocator(handler).
— Without blocking the current thread of execution, on expiration of the absolute timeout specified by abs_time performs ex.dispatch(std::move(handler), alloc) followed by w.reset().

Returns: result.get().

template<class Clock, class Duration, class Executor, class... CompletionTokens>
  auto dispatch_at(const chrono::time_point<Clock, Duration>& abs_time,
                   const Executor& ex, CompletionTokens&&... tokens);

Let the type Handler be the handler function object type determined by performing handler_type_t<CompletionToken, void()>.

Requires: The type Handler must satisfy the MoveConstructible requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.

Effects:
— Constructs a function object handler of type Handler, initialized with handler(forward<CompletionToken>(token)).
— Constructs an object result of type async_result<Handler>, initializing the object as result(handler).
— Creates a work object w by performing make_work(ex).
— Obtains the handler's associated executor object ex1 by performing get_associated_executor(handler).
— Creates a work object w1 by performing make_work(ex1).
— Obtains the handler's associated allocator object alloc by performing get_associated_allocator(handler).
— Constructs a function object f with a function call operator that performs ex1.dispatch(std::move(handler), alloc) followed by w1.reset().
— Without blocking the current thread of execution, on expiration of the absolute timeout specified by abs_time performs ex.dispatch(std::move(f), alloc) followed by w.reset().

Returns: result.get().

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class Clock, class Duration, class ExecutionContext, class... CompletionTokens>
  auto dispatch_at(const chrono::time_point<Clock, Duration>& abs_time,
                   ExecutionContext& ctx, CompletionTokens&&... tokens);

Returns: std::experimental::concurrency_v1::dispatch_at(ctx.get_executor(), forward<CompletionToken>(token)).

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

5.31. Function post_at

template<class Clock, class Duration, class... CompletionTokens>
  auto post_at(const chrono::time_point<Clock, Duration>& abs_time,
               CompletionTokens&&... tokens);

Let the type Handler be the handler function object type determined by performing handler_type_t<CompletionToken, void()>.

Requires: The type Handler must satisfy the MoveConstructible requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.

Effects:
— Constructs a function object handler of type Handler, initialized with handler(forward<CompletionToken>(token)).
— Constructs an object result of type async_result<Handler>, initializing the object as result(handler).
— Obtains the handler's associated executor object ex by performing get_associated_executor(handler).
— Creates a work object w by performing make_work(ex).
— Obtains the handler's associated allocator object alloc by performing get_associated_allocator(handler).
— Without blocking the current thread of execution, on expiration of the absolute timeout specified by abs_time performs ex.post(std::move(handler), alloc) followed by w.reset().

Returns: result.get().

template<class Clock, class Duration, class Executor, class... CompletionTokens>
  auto post_at(const chrono::time_point<Clock, Duration>& abs_time,
               const Executor& ex, CompletionTokens&&... tokens);

Let the type Handler be the handler function object type determined by performing handler_type_t<CompletionToken, void()>.

Requires: The type Handler must satisfy the MoveConstructible requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.

Effects:
— Constructs a function object handler of type Handler, initialized with handler(forward<CompletionToken>(token)).
— Constructs an object result of type async_result<Handler>, initializing the object as result(handler).
— Creates a work object w by performing make_work(ex).
— Obtains the handler's associated executor object ex1 by performing get_associated_executor(handler).
— Creates a work object w1 by performing make_work(ex1).
— Obtains the handler's associated allocator object alloc by performing get_associated_allocator(handler).
— Constructs a function object f with a function call operator that performs ex1.dispatch(std::move(handler), alloc) followed by w1.reset().
— Without blocking the current thread of execution, on expiration of the absolute timeout specified by abs_time performs ex.post(std::move(f), alloc) followed by w.reset().

Returns: result.get().

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class Clock, class Duration, class ExecutionContext, class... CompletionTokens>
  auto post_at(const chrono::time_point<Clock, Duration>& abs_time,
               ExecutionContext& ctx, CompletionTokens&&... tokens);

Returns: std::experimental::concurrency_v1::post_at(ctx.get_executor(), forward<CompletionToken>(token)).

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

5.32. Function defer_at

template<class Clock, class Duration, class... CompletionTokens>
  auto defer_at(const chrono::time_point<Clock, Duration>& abs_time,
                CompletionTokens&&... tokens);

Let the type Handler be the handler function object type determined by performing handler_type_t<CompletionToken, void()>.

Requires: The type Handler must satisfy the MoveConstructible requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.

Effects:
— Constructs a function object handler of type Handler, initialized with handler(forward<CompletionToken>(token)).
— Constructs an object result of type async_result<Handler>, initializing the object as result(handler).
— Obtains the handler's associated executor object ex by performing get_associated_executor(handler).
— Creates a work object w by performing make_work(ex).
— Obtains the handler's associated allocator object alloc by performing get_associated_allocator(handler).
— Without blocking the current thread of execution, on expiration of the absolute timeout specified by abs_time performs ex.defer(std::move(handler), alloc) followed by w.reset().

Returns: result.get().

template<class Clock, class Duration, class Executor, class... CompletionTokens>
  auto defer_at(const chrono::time_point<Clock, Duration>& abs_time,
                const Executor& ex, CompletionTokens&&... tokens);

Let the type Handler be the handler function object type determined by performing handler_type_t<CompletionToken, void()>.

Requires: The type Handler must satisfy the MoveConstructible requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.

Effects:
— Constructs a function object handler of type Handler, initialized with handler(forward<CompletionToken>(token)).
— Constructs an object result of type async_result<Handler>, initializing the object as result(handler).
— Creates a work object w by performing make_work(ex).
— Obtains the handler's associated executor object ex1 by performing get_associated_executor(handler).
— Creates a work object w1 by performing make_work(ex1).
— Obtains the handler's associated allocator object alloc by performing get_associated_allocator(handler).
— Constructs a function object f with a function call operator that performs ex1.dispatch(std::move(handler), alloc) followed by w1.reset().
— Without blocking the current thread of execution, on expiration of the absolute timeout specified by abs_time performs ex.defer(std::move(f), alloc) followed by w.reset().

Returns: result.get().

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class Clock, class Duration, class ExecutionContext, class... CompletionTokens>
  auto defer_at(const chrono::time_point<Clock, Duration>& abs_time,
                ExecutionContext& ctx, CompletionTokens&&... tokens);

Returns: std::experimental::concurrency_v1::defer_at(ctx.get_executor(), forward<CompletionToken>(token)).

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

5.33. Function dispatch_after

template<class Rep, class Period, class... CompletionTokens>
  auto dispatch_after(const chrono::duration<Rep, Period>& rel_time,
                      CompletionTokens&&... token);

Returns: std::experimental::concurrency_v1::dispatch_at(chrono::steady_clock::now() + rel_time, forward<CompletionToken>(token)).

template<class Rep, class Period, class Executor, class... CompletionTokens>
  auto dispatch_after(const chrono::duration<Rep, Period>& rel_time,
                      const Executor& e, CompletionTokens&&... tokens);

Returns: std::experimental::concurrency_v1::dispatch_at(chrono::steady_clock::now() + rel_time, e, forward<CompletionToken>(token)).

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class Rep, class Period, class ExecutionContext, class... CompletionTokens>
  auto dispatch_after(const chrono::duration<Rep, Period>& rel_time,
                      ExecutionContext& c, CompletionTokens&&... tokens);

Returns: std::experimental::concurrency_v1::dispatch_at(chrono::steady_clock::now() + rel_time, ctx.get_executor(), forward<CompletionToken>(token)).

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

5.34. Function post_after

template<class Rep, class Period, class... CompletionTokens>
  auto post_after(const chrono::duration<Rep, Period>& rel_time,
                  CompletionTokens&&... token);

Returns: std::experimental::concurrency_v1::post_at(chrono::steady_clock::now() + rel_time, forward<CompletionToken>(token)).

template<class Rep, class Period, class Executor, class... CompletionTokens>
  auto post_after(const chrono::duration<Rep, Period>& rel_time,
                  const Executor& e, CompletionTokens&&... tokens);

Returns: std::experimental::concurrency_v1::post_at(chrono::steady_clock::now() + rel_time, e, forward<CompletionToken>(token)).

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class Rep, class Period, class ExecutionContext, class... CompletionTokens>
  auto post_after(const chrono::duration<Rep, Period>& rel_time,
                  ExecutionContext& c, CompletionTokens&&... tokens);

Returns: std::experimental::concurrency_v1::post_at(chrono::steady_clock::now() + rel_time, ctx.get_executor(), forward<CompletionToken>(token)).

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

5.35. Function defer_after

template<class Rep, class Period, class... CompletionTokens>
  auto defer_after(const chrono::duration<Rep, Period>& rel_time,
                   CompletionTokens&&... token);

Returns: std::experimental::concurrency_v1::defer_at(chrono::steady_clock::now() + rel_time, forward<CompletionToken>(token)).

template<class Rep, class Period, class Executor, class... CompletionTokens>
  auto defer_after(const chrono::duration<Rep, Period>& rel_time,
                   const Executor& e, CompletionTokens&&... tokens);

Returns: std::experimental::concurrency_v1::defer_at(chrono::steady_clock::now() + rel_time, e, forward<CompletionToken>(token)).

Remarks: This function shall not participate in overload resolution unless is_executor<Executor>::value is true.

template<class Rep, class Period, class ExecutionContext, class... CompletionTokens>
  auto defer_after(const chrono::duration<Rep, Period>& rel_time,
                   ExecutionContext& c, CompletionTokens&&... tokens);

Returns: std::experimental::concurrency_v1::defer_at(chrono::steady_clock::now() + rel_time, ctx.get_executor(), forward<CompletionToken>(token)).

Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.

5.36. Header <experimental/future> synopsis

#include <future>

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

    template <class R, class... Args>
      class async_result<packaged_task<R(Args...)>>;

    template <class Signature, class Alloc>
      class packaged_handler;

    template <class Signature, class Alloc>
      class async_result<packaged_handler<Signature, Alloc>>;

    template <class Func, class Alloc = allocator<void>>
      class packaged_token;

    template <class Func, class Alloc, class R, class... Args>
      struct handler_type<packaged_token<Func, Alloc>, R(Args...)>;

    template <class Func, class Alloc = allocator<void>>
      packaged_token<decay_t<Func>, Alloc> package(Func&& f, const Alloc& a = Alloc());

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

5.37. Class template specialization async_result for packaged_task

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template <class R, class... Args>
      class async_result<packaged_task<R(Args...)>>
      {
      public:
        typedef future<R> type;

        async_result(packaged_task<R(Args...)>& t);
        async_result(const async_result&) = delete;
        async_result& operator=(const async_result&) = delete;

        type get();

      private:
        type future_; // exposition only
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

The implementation shall provide a specialization of async_result that meets the async_result specialization requirements.

async_result(packaged_task<R(Args...)>& t);

Effects: Initializes future_ with t.get_future().

type get();

Returns: std::move(future_).

5.38. Class template packaged_handler

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template <class Signature, class Alloc>
      class packaged_handler
        : public packaged_task<Signature>
      {
      public:
        // packaged_handler types:

        typedef Alloc allocator_type;

        // packaged_handler constructors:

        template <class Func>
          explicit packaged_handler(packaged_token<Func, Alloc>&& token);

        // packaged_handler operations:

        allocator_type get_allocator() const noexcept;

      private:
        Alloc allocator_; // exposition only
      };

      template <class Signature, class Alloc>
        class async_result<packaged_handler<Signature, Alloc>>;

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

5.38.1. packaged_handler constructors

template <class Func>
  explicit packaged_handler(packaged_token<Func, Alloc>&& token);

Effects: Constructs an object of class packaged_handler<Signature, Alloc>, initializing the base class with packaged_task<Signature>(std::move(token.f_)) and initializing allocator_ with token.allocator_.

5.38.2. packaged_handler operations

allocator_type get_allocator() const noexcept;

Returns: allocator_.

5.38.3. Class template specialization async_result

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template <class Signature, class Alloc>
      class async_result<packaged_handler<Signature, Alloc>>
        : public async_result<packaged_task<Signature>>
      {
      public:
        explicit async_result(packaged_handler<Signature, Alloc>& h);
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

The implementation shall provide a specialization of async_result that meets the async_result specialization requirements.

explicit async_result(packaged_handler<Signature, Alloc>& h);

Effects: Initializes the base class with h.

5.39. Class template packaged_token

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      template <class Func, class Alloc = allocator<void>>
      class packaged_token
      {
      public:
        // packaged_token types:

        typedef Alloc allocator_type;

        // packaged_token constructors:

        explicit packaged_token(Func f);
        packaged_token(Func f, const Alloc& a);

        // packaged_token operations:

        allocator_type get_allocator() const noexcept;

      private:
        Func f_; // exposition only
        Alloc allocator_; // exposition only
      };

      template <class Func, class Alloc, class R, class... Args>
      struct handler_type<packaged_token<Func, Alloc>, R(Args...)>
      {
        typedef packaged_handler<result_of_t<Func(Args...)>(Args...), Alloc> type;
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

5.39.1. packaged_token constructors

explicit packaged_token(Func f);

Effects: Constructs an object of class packaged_token<Func, Alloc>, initializing f_ with std::move(f) and default constructing allocator_.

packaged_token(Func f, const Alloc& a);

Effects: Constructs an object of class packaged_token<Func, Alloc>, initializing f_ with std::move(f) and allocator_ with a.

5.39.2. packaged_token operations

allocator_type get_allocator() const noexcept;

Returns: allocator_.

5.40. Function package

template <class Func, class Alloc = allocator<void>>
  packaged_token<decay_t<Func>, Alloc> package(Func&& f, const Alloc& a = Alloc());

Returns: packaged_token<decay_t<Func>, Alloc>(forward<Func>(f), a).

5.41. Header <experimental/thread_pool> synopsis

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      class thread_pool;

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

5.42. Class thread_pool

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      class thread_pool : public execution_context
      {
      public:
        // types:

        class executor_type;

        // construct / copy / destroy:

        thread_pool();
        explicit thread_pool(std::size_t num_threads);
        thread_pool(const thread_pool&) = delete;
        thread_pool& operator=(const thread_pool&) = delete;
        ~thread_pool();

        // thread_pool operations:

        executor_type get_executor() const noexcept;

        void stop();

        void join();
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

Class thread_pool implements a fixed-size pool of threads.

An object of type thread_pool has an associated executor object, meeting the Executor type requirements, of type thread_pool::executor_type and obtainable via the thread_pool object's get_executor member function.

For an object of type thread_pool, outstanding work is defined as the sum of:

— the total number of calls to the thread_pool executor's on_work_started function, less the total number of calls to the on_work_finished function;

— the number of function objects that have been added to the thread_pool via the thread_pool executor, but not yet executed; and

— the number of function objects that are currently being executed by the thread_pool.

The thread_pool member functions get_executor, stop, and join, and the thread_pool::executor_type copy constructors, member functions and comparison operators, shall not introduce data races as a result of concurrent calls to those functions from different threads.

5.42.1. thread_pool constructors/destructor

thread_pool();
explicit thread_pool(std::size_t num_threads);

Effects: Creates an object of class thread_pool containing a number of threads of execution, each represented by a thread object. If specified, the number of threads in the pool is num_threads. Otherwise, the number of threads in the pool is implementation-defined. [Note: A suggested value for the implementation-defined number of threads is std::thread::hardware_concurrency() * 2. —end note]

~thread_pool();

Effects: Destroys an object of class thread_pool. Performs stop() followed by join().

5.42.2. thread_pool members

executor_type get_executor() const noexcept;

Returns: An executor that may be used for submitting function objects to the thread_pool.

void stop();

Effects: Signals the threads in the pool to complete as soon as possible. If a thread is currently executing a function object, the thread will exit only after completion of that function object. The call to stop() returns without waiting for the threads to complete.

void join();

Effects: If not already stopped, signals the threads in the pool to exit once the outstanding work is 0. Blocks until all threads in the pool have completed.

Synchronization: The completion of each thread in the pool synchronizes with (C++ Std, [intro.multithread]) the corresponding successful join() return.

Postconditions: The threads in the pool represented by *this have completed.

5.43. Class thread_pool::executor_type

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      class thread_pool::executor_type
      {
      public:
        // construct / copy / destroy:

        executor_type(const executor_type& other) noexcept;
        executor_type(executor_type&& other) noexcept;

        executor_type& operator=(const executor_type& other) noexcept;
        executor_type& operator=(executor_type&& other) noexcept;

        ~executor_type();

        // executor operations:

        bool running_in_this_thread() const noexcept;

        thread_pool& context() noexcept;

        void on_work_started() noexcept;
        void on_work_finished() noexcept;

        template<class Func, class Alloc>
          void dispatch(Func&& f, const Alloc& a);
        template<class Func, class Alloc>
          void post(Func&& f, const Alloc& a);
        template<class Func, class Alloc>
          void defer(Func&& f, const Alloc& a);
      };

      bool operator==(const thread_pool::executor_type& a,
                      const thread_pool::executor_type& b) noexcept;
      bool operator!=(const thread_pool::executor_type& a,
                      const thread_pool::executor_type& b) noexcept;

      template<> struct is_executor<thread_pool::executor_type> : true_type {};

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

thread_pool::executor_type is a type satisfying Executor requirements. Objects of type thread_pool::executor_type are associated with a thread_pool, and function objects submitted using the dispatch, post or defer member functions will be executed by the thread_pool.

5.43.1. thread_pool::executor_type constructors

executor_type(const executor_type& other) noexcept;

Effects: Constructs an object of class thread_pool::executor_type.

Postconditions: *this == other.

executor_type(executor_type&& other) noexcept;

Effects: Constructs an object of class thread_pool::executor_type.

Postconditions: *this is equal to the prior value of other.

5.43.2. thread_pool::executor_type assignment

executor_type& operator=(const executor_type& other) noexcept;

Postconditions: *this == other.

Returns: *this.

executor_type& operator=(executor_type&& other) noexcept;

Postconditions: *this is equal to the prior value of other.

Returns: *this.

5.43.3. thread_pool::executor_type operations

bool running_in_this_thread() const noexcept;

Returns: true if the current thread of execution is a member of the pool; otherwise false.

thread_pool& context() noexcept;

Returns: A reference to the associated thread_pool object.

void on_work_started() noexcept;

Effects: Increases the count of outstanding work associated with the thread_pool.

void on_work_finished() noexcept;

Effects: Decreases the count of outstanding work associated with the thread_pool.

template<class Func, class Alloc>
  void dispatch(Func&& f, const Alloc& a);

Effects: If running_in_this_thread() is true, calls DECAY_COPY(forward<Func>(f))(). Otherwise, requests execution of f.

If f exits via an exception, and the execution of f is performed in the current thread and before dispatch returns, the exception shall propagate to the caller of dispatch.

template<class Func, class Alloc>
  void post(Func&& f, const Alloc& a);

Effects: Requests execution of f.

template<class Func, class Alloc>
  void defer(Func&& f, const Alloc& a);

Effects: Requests execution of f.

5.43.4. thread_pool::executor_type comparisons

bool operator==(const thread_pool::executor_type& a,
                const thread_pool::executor_type& b) noexcept;

Returns: &a.context() == &b.context().

bool operator!=(const thread_pool::executor_type& a,
                const thread_pool::executor_type& b) noexcept;

Returns: !(a == b).

5.44. Header <experimental/loop_scheduler> synopsis

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      class loop_scheduler;

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

5.45. Class loop_scheduler

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      class loop_scheduler : public execution_context
      {
      public:
        // types:

        class executor_type;

        // construct / copy / destroy:

        loop_scheduler();
        explicit loop_scheduler(std::size_t concurrency_hint);
        loop_scheduler(const loop_scheduler&) = delete;
        loop_scheduler& operator=(const loop_scheduler&) = delete;
        ~loop_scheduler();

        // loop_scheduler operations:

        executor_type get_executor() const noexcept;

        size_t run();
        template<class Rep, class Period>
          size_t run_for(const chrono::duration<Rep, Period>& rel_time);
        template<class Clock, class Duration>
          size_t run_until(const chrono::time_point<Clock, Duration>& abs_time);

        size_t run_one();
        template<class Rep, class Period>
          size_t run_one_for(const chrono::duration<Rep, Period>& rel_time);
        template<class Clock, class Duration>
          size_t run_one_until(const chrono::time_point<Clock, Duration>& abs_time);

        size_t poll();

        size_t poll_one();

        void stop();

        bool stopped() const;

        void restart();
      };

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

An object of type loop_scheduler has an associated executor object, meeting the Executor type requirements, of type loop_scheduler::executor_type and obtainable via the loop_scheduler object's get_executor member function.

For an object of type loop_scheduler, outstanding work is defined as the sum of:

— the total number of calls to the loop_scheduler executor's on_work_started function, less the total number of calls to the on_work_finished function;

— the number of function objects that have been added to the loop_scheduler via the loop_scheduler executor, but not yet executed; and

— the number of function objects that are currently being executed by the loop_scheduler.

If at any time the outstanding work falls to 0, the loop_scheduler is stopped as if by stop().

The loop_scheduler member functions get_executor, run, run_for, run_until, run_one, run_one_for, run_one_until, poll, poll_one, stop, and stopped, and the loop_scheduler::executor_type copy constructors, member functions and comparison operators, shall not introduce data races as a result of concurrent calls to those functions from different threads. [Note: The restart member function is excluded from these thread safety requirements. —end note]

5.45.1. loop_scheduler constructors/destructor

loop_scheduler();
explicit loop_scheduler(std::size_t concurrency_hint);

Effects: Creates an object of class loop_scheduler.

Remarks: The concurrency_hint parameter is a suggestion to the implementation on the number of threads that should process asynchronous operations and execute function objects.

~loop_scheduler();

Effects: Destroys an object of class loop_scheduler.

5.45.2. loop_scheduler members

executor_type get_executor() const noexcept;

Returns: An executor that may be used for submitting function objects to the loop_scheduler.

size_t run();

Requires: Must not be called from a thread that is currently calling one of run, run_for, run_until, run_one, run_one_for, run_one_until, poll, or poll_one.

Effects: Equivalent to:

size_t n = 0;
while (run_one())
  if (n != numeric_limits<size_t>::max())
    ++n;

Returns: n.

template<class Rep, class Period>
  size_t run_for(const chrono::duration<Rep, Period>& rel_time);

Effects: Equivalent to:

return run_until(chrono::steady_clock::now() + rel_time);

template<class Clock, class Duration>
  size_t run_until(const chrono::time_point<Clock, Duration>& abs_time);

Effects: Equivalent to:

size_t n = 0;
while (run_one_until(abs_time))
  if (n != numeric_limits<size_t>::max())
    ++n;

Returns: n.

size_t run_one();

Requires: Must not be called from a thread that is currently calling one of run, run_for, run_until, run_one, run_one_for, run_one_until, poll, or poll_one.

Effects: If the loop_scheduler object has no oustanding work, performs stop(). Otherwise, blocks while the loop_scheduler has outstanding work, or until the loop_scheduler is stopped, or until one function object has been executed.

If an executed function object throws an exception, the exception shall be allowed to propagate to the caller of run_one(). The loop_scheduler state shall be as if the function object had returned normally.

Returns: 1 if a function object was executed, otherwise 0.

Notes: This function may invoke additional handlers through nested calls to the loop_scheduler executor's dispatch member function. These do not count towards the return value.

template<class Rep, class Period>
  size_t run_one_for(const chrono::duration<Rep, Period>& rel_time);

Effects: Equivalent to:

return run_until(chrono::steady_clock::now() + rel_time);

Returns: 1 if a function object was executed, otherwise 0.

template<class Clock, class Duration>
  size_t run_one_until(const chrono::time_point<Clock, Duration>& abs_time);

Effects: If the loop_scheduler object has no oustanding work, performs stop(). Otherwise, blocks while the loop_scheduler has outstanding work, or until the expiration of the absolute timeout (C++ Std, [thread.req.timing]) specified by abs_time, or until the loop_scheduler is stopped, or until one function object has been executed.

If an executed function object throws an exception, the exception shall be allowed to propagate to the caller of run_one(). The loop_scheduler state shall be as if the function object had returned normally.

Returns: 1 if a function object was executed, otherwise 0.

Notes: This function may invoke additional handlers through nested calls to the loop_scheduler executor's dispatch member function. These do not count towards the return value.

size_t poll();

Effects: Equivalent to:

size_t n = 0;
while (poll_one())
  if (n != numeric_limits<size_t>::max())
    ++n;

Returns: n.

size_t poll_one();

Effects: If the loop_scheduler object has no oustanding work, performs stop(). Otherwise, if there is a function object ready for immediate execution, executes it.

If an executed function object throws an exception, the exception shall be allowed to propagate to the caller of poll_one(). The loop_scheduler state shall be as if the function object had returned normally.

Returns: 1 if a handler was executed, otherwise 0.

Notes: This function may invoke additional handlers through nested calls to the loop_scheduler executor's dispatch member function. These do not count towards the return value.

void stop();

Effects: Stops the loop_scheduler. Concurrent calls to run, run_for, run_until, run_one, run_one_for, run_one_until, poll or poll_one will end as soon as possible. If a call to run, run_for, run_until, run_one, run_one_for, run_one_until, poll or poll_one is currently executing a function object, the call will end only after completion of that function object. The call to stop() returns without waiting for concurrent calls to run, run_for, run_until, run_one, run_one_for, run_one_until, poll or poll_one to complete.

Postconditions: stopped() == true.

[Note: When stopped() == true, subsequent calls to run, run_for, run_until, run_one, run_one_for, run_one_until, poll or poll_one will exit immediately with a return value of 0, without executing any function objects. A loop_scheduler remains in the stopped state until a call to restart(). —end note]

void restart();

Postconditions: stopped() == false.

5.46. Class loop_scheduler::executor_type

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {

      class loop_scheduler::executor_type
      {
      public:
        // construct / copy / destroy:

        executor_type(const executor_type& other) noexcept;
        executor_type(executor_type&& other) noexcept;

        executor_type& operator=(const executor_type& other) noexcept;
        executor_type& operator=(executor_type&& other) noexcept;

        ~executor_type();

        // executor operations:

        bool running_in_this_thread() const noexcept;

        loop_scheduler& context() noexcept;

        void on_work_started() noexcept;
        void on_work_finished() noexcept;

        template<class Func, class Alloc>
          void dispatch(Func&& f, const Alloc& a);
        template<class Func, class Alloc>
          void post(Func&& f, const Alloc& a);
        template<class Func, class Alloc>
          void defer(Func&& f, const Alloc& a);
      };

      bool operator==(const loop_scheduler::executor_type& a,
                      const loop_scheduler::executor_type& b) noexcept;
      bool operator!=(const loop_scheduler::executor_type& a,
                      const loop_scheduler::executor_type& b) noexcept;

      template<> struct is_executor<loop_scheduler::executor_type> : true_type {};

    } // inline namespace concurrency_v1
  } // namespace experimental
} // namespace std

loop_scheduler::executor_type is a type satisfying Executor requirements. Objects of type loop_scheduler::executor_type are associated with a loop_scheduler, and function objects submitted using the dispatch, post or defer member functions will be executed by the loop_scheduler from within the run, run_for, run_until, run_one, run_one_for, run_one_until, poll or poll_one functions.

5.46.1. loop_scheduler::executor_type constructors

executor_type(const executor_type& other) noexcept;

Effects: Constructs an object of class loop_scheduler::executor_type.

Postconditions: *this == other.

executor_type(executor_type&& other) noexcept;

Effects: Constructs an object of class loop_scheduler::executor_type.

Postconditions: *this is equal to the prior value of other.

5.46.2. loop_scheduler::executor_type assignment

executor_type& operator=(const executor_type& other) noexcept;

Postconditions: *this == other.

Returns: *this.

executor_type& operator=(executor_type&& other) noexcept;

Postconditions: *this is equal to the prior value of other.

Returns: *this.

5.46.3. loop_scheduler::executor_type operations

bool running_in_this_thread() const noexcept;

Returns: true if the current thread of execution is invoking the run, run_for, run_until, run_one, run_one_for, run_one_until, poll or poll_one function of the associated loop_scheduler object.

loop_scheduler& context() noexcept;

Returns: A reference to the associated loop_scheduler object.

void on_work_started() noexcept;

Effects: Increases the count of outstanding work associated with the loop_scheduler.

void on_work_finished() noexcept;

Effects: Decreases the count of outstanding work associated with the loop_scheduler.

template<class Func, class Alloc>
  void dispatch(Func&& f, const Alloc& a);

Effects: If running_in_this_thread() is true, calls DECAY_COPY(forward<Func>(f))(). Otherwise, requests execution of f.

If f exits via an exception, and the execution of f is performed in the current thread and before dispatch returns, the exception shall propagate to the caller of dispatch.

template<class Func, class Alloc>
  void post(Func&& f, const Alloc& a);

Effects: Requests execution of f.

template<class Func, class Alloc>
  void defer(Func&& f, const Alloc& a);

Effects: Requests execution of f.

5.46.4. loop_scheduler::executor_type comparisons

bool operator==(const loop_scheduler::executor_type& a,
                const loop_scheduler::executor_type& b) noexcept;

Returns: &a.context() == &b.context().

bool operator!=(const loop_scheduler::executor_type& a,
                const loop_scheduler::executor_type& b) noexcept;

Returns: !(a == b).