14 Basic I/O services [io_context]

14.1 Header <experimental/io_context> synopsis [io_context.synop]

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

  class io_context;

} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std

14.2 Class io_context [io_context.io_context]

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

  class io_context : public execution_context
  {
  public:
    // types:

    class executor_type;
    using count_type = implementation-defined;

    // construct / copy / destroy:

    io_context();
    explicit io_context(int concurrency_hint);
    io_context(const io_context&) = delete;
    io_context& operator=(const io_context&) = delete;

    // io_context operations:

    executor_type get_executor() noexcept;

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

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

    count_type poll();

    count_type poll_one();

    void stop();

    bool stopped() const noexcept;

    void restart();
  };

} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std

The class io_context satisfies the ExecutionContext type requirements ([async.reqmts.executioncontext]).

count_type is an implementation-defined unsigned integral type of at least 32 bits.

The io_context member functions run, run_for, run_until, run_one, run_one_for, run_one_until, poll, and poll_one are collectively referred to as the run functions. The run functions must be called for the io_context to perform asynchronous operations ([defs.async.op]) on behalf of a C++ program. Notification that an asynchronous operation has completed is delivered by execution of the associated completion handler function object, as determined by the requirements for asynchronous operations ([async.reqmts.async]).

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

  • the total number of calls to the on_work_started function, less the total number of calls to the on_work_finished function, to any executor of the io_context.

  • the number of function objects that have been added to the io_context via any executor of the io_context, but not yet executed; and

  • the number of function objects that are currently being executed by the io_context.

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

The io_context member functions get_executor, stop, and stopped, the run functions, and the io_context::executor_type copy constructors, member functions and comparison operators, do not introduce data races as a result of concurrent calls to those functions from different threads of execution. [ Note: The restart member function is excluded from these thread safety requirements.  — end note ] The run functions may be recursively reentered.

14.2.1 io_context members [io_context.io_context.members]

io_context(); explicit io_context(int concurrency_hint);

Effects: Creates an object of class io_context.

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.

executor_type get_executor() noexcept;

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

count_type run();

Effects: Equivalent to:

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

Returns: n.

Note: Calling run from a thread that is currently calling a run function can introduce the potential for deadlock. It is the caller's responsibility to avoid such deadlocks.  — end note ]

template<class Rep, class Period> count_type 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> count_type run_until(const chrono::time_point<Clock, Duration>& abs_time);

Effects: Equivalent to:

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

Returns: n.

count_type run_one();

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

If an executed function object throws an exception, the exception propagates to the caller of run_one(). The io_context state is as if the function object had returned normally.

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

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

Note: Calling run_one from a thread that is currently calling a run function can introduce the potential for deadlock. It is the caller's responsibility to avoid such deadlocks.  — end note ]

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

Effects: Equivalent to:

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

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

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

If an executed function object throws an exception, the exception propagates to the caller of run_one(). The io_context state is as if the function object had returned normally.

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

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

count_type poll();

Effects: Equivalent to:

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

Returns: n.

count_type poll_one();

Effects: If the io_context object has no outstanding 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 propagates to the caller of poll_one(). The io_context state is as if the function object had returned normally.

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

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

void stop();

Effects: Stops the io_context. Concurrent calls to any run function will end as soon as possible. If a call to a run function 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 functions to complete.

Postconditions: stopped() == true.

Note: When stopped() == true, subsequent calls to a run function will exit immediately with a return value of 0, without executing any function objects. An io_context remains in the stopped state until a call to restart().  — end note ]

bool stopped() const noexcept;

Returns: true if the io_context is stopped.

void restart();

Postconditions: stopped() == false.

14.3 Class io_context::executor_type [io_context.exec]

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

  class io_context::executor_type
  {
  public:
    // [io_context.exec.cons], 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;

    // [io_context.exec.ops], executor operations:

    bool running_in_this_thread() const noexcept;

    io_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;
  };

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

} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std

io_context::executor_type is a type satisfying the Executor requirements ([async.reqmts.executor]). Objects of type io_context::executor_type are associated with an io_context, and function objects submitted using the dispatch, post, or defer member functions will be executed by the io_context from within a run function.]

14.3.1 io_context::executor_type constructors [io_context.exec.cons]

executor_type(const executor_type& other) noexcept;

Postconditions: *this == other.

executor_type(executor_type&& other) noexcept;

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

14.3.2 io_context::executor_type assignment [io_context.exec.assign]

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.

14.3.3 io_context::executor_type operations [io_context.exec.ops]

bool running_in_this_thread() const noexcept;

Returns: true if the current thread of execution is calling a run function of the associated io_context object. [ Note: That is, the current thread of execution's call chain includes a run function.  — end note ]

io_context& context() const noexcept;

Returns: A reference to the associated io_context object.

void on_work_started() const noexcept;

Effects: Increments the count of outstanding work associated with the io_context.

void on_work_finished() const noexcept;

Effects: Decrements the count of outstanding work associated with the io_context.

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, calls post(forward<Func>(f), a).

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

Effects: Adds f to the io_context.

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

Effects: Adds f to the io_context.

14.3.4 io_context::executor_type comparisons [io_context.exec.comparisons]

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

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

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

Returns: !(a == b).