13 Asynchronous model [async]

13.25 Class template strand [async.strand]

The class template strand is a wrapper around an object of type Executor satisfying the Executor requirements ([async.reqmts.executor]).

namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

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

    using inner_executor_type = Executor;

    // [async.strand.cons], construct / copy / destroy:

    strand();
    explicit strand(Executor ex);
    template<class ProtoAllocator>
      strand(allocator_arg_t, const ProtoAllocator& alloc, Executor ex);
    strand(const strand& other) noexcept;
    strand(strand&& other) noexcept;
    template<class OtherExecutor> strand(const strand<OtherExecutor>& other) noexcept;
    template<class OtherExecutor> strand(strand<OtherExecutor>&& other) noexcept;

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

    ~strand();

    // [async.strand.ops], strand operations:

    inner_executor_type get_inner_executor() const noexcept;

    bool running_in_this_thread() const noexcept;

    execution_context& context() const noexcept;

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

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

  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 v1
} // namespace net
} // namespace experimental
} // namespace std

strand<Executor> satisfies the Executor ([async.reqmts.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() is false

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

then the implementation invokes 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.

All member functions, except for the assignment operators and the destructor, do not introduce data races on *this, including its ordered, non-concurrent state. Additionally, constructors and assignment operators do not introduce data races on lvalue arguments.

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

13.25.1 strand constructors [async.strand.cons]

strand();

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

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

explicit strand(Executor ex);

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

template<class ProtoAllocator> strand(allocator_arg_t, const ProtoAllocator& a, Executor ex);

Effects: Constructs an object of class strand<Executor> that represents a unique ordered, non-concurrent state. Initializes inner_ex_ as inner_ex_(ex). A copy of the allocator argument a is used to allocate memory, if necessary, for the internal data structures of the constructed strand object.

strand(const strand& other) noexcept;

Effects: Initializes inner_ex_ as inner_ex_(other.inner_ex_).

Postconditions:

  • *this == other

  • get_inner_executor() == other.get_inner_executor()

strand(strand&& other) noexcept;

Effects: Initializes inner_ex_ as 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) noexcept;

Requires: OtherExecutor is convertible to Executor.

Effects: Initializes inner_ex_ as inner_ex_(other.inner_ex_).

Postconditions: *this == other.

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

Requires: OtherExecutor is convertible to Executor.

Effects: Initializes inner_ex_ as inner_ex_(std::move(other.inner_ex_)).

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

13.25.2 strand assignment [async.strand.assign]

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

Requires: Executor is CopyAssignable (C++ 2014 [copyassignable]).

Postconditions:

  • *this == other

  • get_inner_executor() == other.get_inner_executor()

Returns: *this.

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

Requires: Executor is MoveAssignable (C++ 2014 [moveassignable]).

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) noexcept;

Requires: OtherExecutor is convertible to Executor. Executor is CopyAssignable (C++ 2014 [copyassignable]).

Effects: Assigns other.inner_ex_ to inner_ex_.

Postconditions: *this == other.

Returns: *this.

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

Requires: OtherExecutor is convertible to Executor. Executor is MoveAssignable (C++ 2014 [moveassignable]).

Effects: Assigns std::move(other.inner_ex_) to inner_ex_.

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

Returns: *this.

13.25.3 strand destructor [async.strand.dtor]

~strand();

Effects: Destroys an object of class strand<Executor>. After this destructor completes, objects that were added to the strand but have not yet been executed will be executed in a way that meets the guarantees of ordering and non-concurrency.

13.25.4 strand operations [async.strand.ops]

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 running 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. [ Note: That is, the current thread of execution's call chain includes a function that was submitted to the strand.  — end note ]

execution_context& context() const noexcept;

Returns: inner_ex_.context().

void on_work_started() const noexcept;

Effects: Calls inner_ex_.on_work_started().

void on_work_finished() const noexcept;

Effects: Calls inner_ex_.on_work_finished().

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

Effects: If running_in_this_thread() is true, calls DECAY_COPY(forward<Func>(f))() (C++ 2014 [thread.decaycopy]). [ Note: If f exits via an exception, the exception propagates to the caller of dispatch().  — end note ] 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.

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

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 ProtoAllocator> void defer(Func&& f, const ProtoAllocator& a) const;

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.

13.25.5 strand comparisons [async.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).