Doc. no:  P0112R0 
Date:     2015-09-25
Revises:  N4478
Reply-To: Christopher Kohlhoff <chris@kohlhoff.com>

Networking Library (Revision 6)

1. Introduction

In the June 2014 committee meeting in Rapperswil, LEWG requested that Boost.Asio-based N2175 Networking Library Proposal for TR2 (Revision 1) be updated for C++14 and brought forward as a proposed Networking Technical Specification. This document is that revision. As well as updating the proposal for C++14, it incorporates improvements to Asio that are based on the widespread field experience accumulated since 2007.

The Boost.Asio library, from which this proposal is derived, has been deployed in numerous systems, from large (including internet-facing HTTP servers, instant messaging gateways and financial markets applications) to small (mobile phones and embedded systems). The Asio library supports, or has been ported to, many operating systems including Linux, Mac OS X, Windows (native), Windows Runtime, Solaris, FreeBSD, NetBSD, OpenBSD, HP-UX, Tru64, AIX, iOS, Android, WinCE, Symbian, vxWorks and QNX Neutrino.

2. Changes in this revision

This revision includes changes to address the remaining issues from the LWG wording review held in Cologne in February 2015, as well applying design changes following LEWG review in Lenexa. As the changes are too extensive to list here, readers may wish to view the GitHub page for this proposal at https://github.com/chriskohlhoff/asio-tr2/ for further information.

Since the previous revision, the design changes of note include:

  • Renaming io_service to io_context.
  • Renaming wrap() to bind_executor().
  • Changing package() to be the function call operator of use_future.
  • Removal of the const_buffers_1 and mutable_buffers_1 classes. The buffer sequence requirements have been updated, and const_buffer and mutable_buffer now satisfy these requirements directly.
  • Add new data() and size() member functions to const_buffer and mutable_buffer, replacing buffer_cast<>() and buffer_size() respectively.

3. Reference implementation

An almost complete implementation of the proposal text may be found in a variant of Asio that stands alone from Boost. This variant is available at https://github.com/chriskohlhoff/asio/tree/master.

4. Library examples

Unfamiliar readers are encouraged to look to the Boost.Asio documentation and examples for a more complete picture of the use of the library.

However, to give some idea of the flavour of the proposed library, consider the following sample code. This is part of a server program that echoes the characters it receives back to the client in upper case.

template <typename Iterator>
void uppercase(Iterator begin, Iterator end)
{
  std::locale loc("");
  for (Iterator iter = begin; iter != end; ++iter)
    *iter = std::toupper(*iter, loc);
}

void sync_connection(tcp::socket& socket)
{
  try
  {
    std::vector<char> buffer_space(1024);
    for (;;)
    {
      std::size_t length = socket.read_some(buffer(buffer_space));
      uppercase(buffer_space.begin(), buffer_space.begin() + length);
      write(socket, buffer(buffer_space, length));
    }
  }
  catch (std::system_error& e)
  {
    // ...
  }
}

The synchronous operations used above are functions that do not return control to the caller until the corresponding operating system operation completes. In Asio-based programs their use cases typically fall into two categories:

  • Simple programs that do not care about timeouts, or are happy to rely on the timeout behaviour provided by the underlying operating system.
  • Programs that require fine grained control over system calls, and are aware of the conditions under which synchronous operations will or will not block.

Next, the equivalent code developed using asynchronous operations might look something like this:

class async_connection
  : public std::enable_shared_from_this<async_connection>
{
public:
  async_connection(tcp::socket socket)
    : socket_(std::move(socket))
  {
  }

  void start()
  {
    do_read();
  }

private:
  void do_read()
  {
    auto self(shared_from_this());
    socket_.async_read_some(buffer(buffer_space_),
        [this, self](std::error_code ec, std::size_t length)
        {
          if (!ec)
          {
            uppercase(buffer_space_.begin(), buffer_space_.begin() + length);
            do_write(length);
          }
        });
  }

  void do_write(std::size_t length)
  {
    auto self(shared_from_this());
    async_write(socket_, buffer(buffer_space_, length),
        [this, self](std::error_code ec, std::size_t /*length*/)
        {
          if (!ec)
          {
            do_read();
          }
        });
  }

  tcp::socket socket_;
  std::vector<char> buffer_space_{1024};
};

Asynchronous operations do not block the caller, but instead involve the delivery of a notification to the program when the corresponding operating system operation completes. Most non-trivial Asio-based programs will make use of asynchronous operations.

While the code may appear more complex due to the inverted flow of control, it allows a knowledgeable programmer to write code that will scale to a great many concurrent connections. However, this proposal uses the asynchronous model described in [N4045]. This is an extensible model that allows the asynchronous operations to support a variety of composition and notification mechanisms, and these mechanisms may alleviate this complexity. This includes futures:

std::future<std::size_t> fut =
  socket.async_read_some(buffer(buffer_space), use_future);

// ...

std::size_t length = fut.get();

and, through library extensions, coroutines:

void coro_connection(tcp::socket& socket, yield_context yield)
{
  try
  {
    std::vector<char> buffer_space(1024);
    for (;;)
    {
      std::size_t length = socket.async_read_some(buffer(buffer_space), yield);
      uppercase(buffer_space.begin(), buffer_space.begin() + length);
      async_write(socket, buffer(buffer_space, length), yield);
    }
  }
  catch (std::system_error& e)
  {
    // ...
  }
}

Finally, for many applications, networking is not a core feature, nor is it seen as a core competency of the application’s programmers. To cater to these use cases, the proposal provides a high-level interface to TCP sockets that is designed around the familiar C++ I/O streams framework.

Using the library in this way is as easy as opening a stream object with the remote host’s details:

tcp::iostream s("www.boost.org", "http");

Once connected, you send and receive any data as needed. In this case you send a request:

s << "GET / HTTP/1.0\r\n";
s << "Host: www.boost.org\r\n";
s << "Accept: */*\r\n";
s << "Connection: close\r\n\r\n";

Then receive and process the response:

std::string header;
while (std::getline(s, header) && header != "\r")
  std::cout << header << "\n";
std::cout << s.rdbuf();

You can set a timeout to detect unresponsive connections:

s.expires_after(std::chrono::seconds(60));

And, if at any time there is an error, the tcp::iostream class’s error() member function may be used to obtain an error_code that identifies the reason for failure:

if (!s)
{
  std::cout << "Unable to connect: " << s.error().message() << "\n";
  return 1;
}

5. Scope

Problem areas addressed by this proposal include:

  • Networking using TCP and UDP, including support for multicast.
  • Client and server applications.
  • Scalability to handle many concurrent connections.
  • Protocol independence between IPv4 and IPv6.
  • Name resolution (i.e. DNS).
  • Timers.

Features that are considered outside the scope of this proposal include:

  • Protocol implementations such as HTTP, SMTP or FTP.
  • Encryption (e.g. SSL, TLS).
  • Operating system specific demultiplexing APIs.
  • Support for realtime environments.
  • QoS-enabled sockets.
  • Other TCP/IP protocols such as ICMP.
  • Functions and classes for enumerating network interfaces.
  • Other forms of asynchronous I/O, such as files. (However, the asynchronous model defined below is capable of supporting these facilities.)

6. Target audience

The bulk of the library interface is intended for use by developers with at least some understanding of networking concepts (or a willingness to learn). A high level iostreams interface supports simple use cases and permits novices to develop network code without needing to get into too much depth.

7. Related work

The interface is based on the BSD sockets API, which is widely implemented and supported by extensive literature. It is also used as the basis of networking APIs in other languages (e.g. Java). Unsafe practices of the BSD sockets API, e.g. lack of compile-time type safety, are not included.

Asynchronous support is derived from the Proactor design pattern as implemented by the ADAPTIVE Communication Environment [ACE], and is influenced by the design of the Symbian C++ sockets API [SYMBIAN], which supports synchronous and asynchronous operations side-by-side. The Microsoft .NET socket classes [MS-NET] and the Extended Sockets API [ES-API] developed by The Open Group support similar styles of network programming.

8. 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. It makes additions to experimental headers that may also be modified by other Technical Specifications.

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.

9. Relationship to other proposals

The asynchronous operations defined in this proposal use the asynchronous model previously described in [N4045]. With the extensible asynchronous model presented in that paper, the user has the ability to select an asynchronous approach that is appropriate to each use case. With these library foundations, a single extensible asynchronous model can support a variety of composition methods, including:

  • Callbacks, where minimal runtime penalty is desirable.
  • Futures, and not just std::future but also future classes supplied by other libraries.
  • Coroutines or resumable functions, without adding new keywords to the language.

To facilitate the coordination of asynchronous operations in multithreaded programs, the asynchronous model also utilises the executors design described and specified in [P0113].

As executors and the extensible asynchronous model are a prerequisite for the networking library, the proposed text below incorporates a complete specification of these facilities.

10. Proposed text

10.1. Scope
10.2. Conformance
10.2.1. POSIX conformance
10.2.2. Conditionally-supported features
10.3. Normative references
10.4. Namespaces and headers
10.5. Definitions
10.5.1. host byte order
10.5.2. network byte order
10.5.3. synchronous operation
10.5.4. asynchronous operation
10.5.5. orderly shutdown
10.6. Future plans (Informative)
10.7. Feature test macros (Informative)
10.8. Method of description (Informative)
10.8.1. Structure of each clause
10.8.1.1. Detailed specifications
10.8.2. Other conventions
10.8.2.1. Nested classes
10.9. Error reporting
10.9.1. Synchronous operations
10.9.2. Asynchronous operations
10.9.3. Error conditions
10.9.4. Suppression of signals
10.10. Library summary
10.11. Convenience header
10.11.1. Header <experimental/net> synopsis
10.12. Forward declarations
10.12.1. Header <experimental/netfwd> synopsis
10.13. Asynchronous model
10.13.1. Header <experimental/executor> synopsis
10.13.2. Requirements
10.13.2.1. Proto-allocator requirements
10.13.2.2. Executor requirements
10.13.2.3. Execution context requirements
10.13.2.4. Service requirements
10.13.2.5. Signature requirements
10.13.2.6. Associator requirements
10.13.2.7. Requirements on asynchronous operations
10.13.2.7.1. General asynchronous operation concepts
10.13.2.7.2. Completion tokens and handlers
10.13.2.7.3. Automatic deduction of initiating function return type
10.13.2.7.4. Production of initiating function return value
10.13.2.7.5. Lifetime of initiating function arguments
10.13.2.7.6. Non-blocking requirements on initiating functions
10.13.2.7.7. Associated executor
10.13.2.7.8. I/O executor
10.13.2.7.9. Completion handler executor
10.13.2.7.10. Outstanding work
10.13.2.7.11. Allocation of intermediate storage
10.13.2.7.12. Execution of completion handler on completion of asynchronous operation
10.13.2.7.13. Completion handlers and exceptions
10.13.3. Class template async_result
10.13.4. Class template async_completion
10.13.5. Class template associated_allocator
10.13.5.1. associated_allocator members
10.13.6. Function get_associated_allocator
10.13.7. Class execution_context
10.13.7.1. execution_context constructor
10.13.7.2. execution_context destructor
10.13.7.3. execution_context operations
10.13.7.4. execution_context protected operations
10.13.7.5. execution_context globals
10.13.8. Class execution_context::service
10.13.9. Class template is_executor
10.13.10. Executor argument tag
10.13.11. uses_executor
10.13.11.1. uses_executor trait
10.13.11.2. uses-executor construction
10.13.12. Class template associated_executor
10.13.12.1. associated_executor members
10.13.13. Function get_associated_executor
10.13.14. Class template executor_binder
10.13.14.1. executor_binder constructors
10.13.14.2. executor_binder access
10.13.14.3. executor_binder invocation
10.13.14.4. Class template partial specialization async_result
10.13.14.5. Class template partial specialization associated_allocator
10.13.14.6. Class template partial specialization associated_executor
10.13.15. Function bind_executor
10.13.16. Class template executor_work_guard
10.13.16.1. executor_work_guard members
10.13.17. Function make_work_guard
10.13.18. Class system_executor
10.13.18.1. system_executor operations
10.13.18.2. system_executor comparisons
10.13.19. Class system_context
10.13.20. Class bad_executor
10.13.21. Class executor
10.13.21.1. executor constructors
10.13.21.2. executor assignment
10.13.21.3. executor destructor
10.13.21.4. executor modifiers
10.13.21.5. executor operations
10.13.21.6. executor capacity
10.13.21.7. executor target access
10.13.21.8. executor comparisons
10.13.21.9. executor specialized algorithms
10.13.22. Function dispatch
10.13.23. Function post
10.13.24. Function defer
10.13.25. Class template strand
10.13.25.1. strand constructors
10.13.25.2. strand assignment
10.13.25.3. strand destructor
10.13.25.4. strand operations
10.13.25.5. strand comparisons
10.13.26. Class template use_future_t
10.13.26.1. use_future_t constructors
10.13.26.2. use_future_t members
10.13.26.3. Partial class template specialization async_result for use_future_t
10.13.27. Partial class template specialization async_result for packaged_task
10.14. Basic I/O services
10.14.1. Header <experimental/io_context> synopsis
10.14.2. Class io_context
10.14.2.1. io_context members
10.14.3. Class io_context::executor_type
10.14.3.1. io_context::executor_type constructors
10.14.3.2. io_context::executor_type assignment
10.14.3.3. io_context::executor_type operations
10.14.3.4. io_context::executor_type comparisons
10.15. Timers
10.15.1. Header <experimental/timer> synopsis
10.15.2. Requirements
10.15.2.1. Wait traits requirements
10.15.3. Class template wait_traits
10.15.4. Class template basic_waitable_timer
10.15.4.1. basic_waitable_timer constructors
10.15.4.2. basic_waitable_timer destructor
10.15.4.3. basic_waitable_timer assignment
10.15.4.4. basic_waitable_timer operations
10.16. Buffers
10.16.1. Header <experimental/buffer> synopsis
10.16.2. Requirements
10.16.2.1. Mutable buffer sequence requirements
10.16.2.2. Constant buffer sequence requirements
10.16.2.3. Dynamic buffer requirements
10.16.2.4. Requirements on read and write operations
10.16.3. Class mutable_buffer
10.16.4. Class const_buffer
10.16.5. Buffer type traits
10.16.6. Buffer sequence access
10.16.7. Function buffer_size
10.16.8. Function buffer_copy
10.16.9. Buffer arithmetic
10.16.10. Buffer creation functions
10.16.11. Class template dynamic_vector_buffer
10.16.12. Class template dynamic_string_buffer
10.16.13. Dynamic buffer creation functions
10.17. Buffer-oriented streams
10.17.1. Requirements
10.17.1.1. Buffer-oriented synchronous read stream requirements
10.17.1.2. Buffer-oriented asynchronous read stream requirements
10.17.1.3. Buffer-oriented synchronous write stream requirements
10.17.1.4. Buffer-oriented asynchronous write stream requirements
10.17.1.5. Completion condition requirements
10.17.2. Class transfer_all
10.17.3. Class transfer_at_least
10.17.4. Class transfer_exactly
10.17.5. Synchronous read operations
10.17.6. Asynchronous read operations
10.17.7. Synchronous write operations
10.17.8. Asynchronous write operations
10.17.9. Synchronous delimited read operations
10.17.10. Asynchronous delimited read operations
10.18. Sockets
10.18.1. Header <experimental/socket> synopsis
10.18.2. Requirements
10.18.2.1. Requirements on synchronous socket operations
10.18.2.2. Requirements on asynchronous socket operations
10.18.2.3. Native handles
10.18.2.4. Endpoint requirements
10.18.2.5. Protocol requirements
10.18.2.6. Acceptable protocol requirements
10.18.2.7. Gettable socket option requirements
10.18.2.8. Settable socket option requirements
10.18.2.9. Boolean socket options
10.18.2.10. Integer socket options
10.18.2.11. I/O control command requirements
10.18.2.12. Connect condition requirements
10.18.3. Error codes
10.18.4. Class socket_base
10.18.5. Socket options
10.18.5.1. Class socket_base::linger
10.18.6. Class template basic_socket
10.18.6.1. basic_socket constructors
10.18.6.2. basic_socket destructor
10.18.6.3. basic_socket assignment
10.18.6.4. basic_socket operations
10.18.7. Class template basic_datagram_socket
10.18.7.1. basic_datagram_socket constructors
10.18.7.2. basic_datagram_socket assignment
10.18.7.3. basic_datagram_socket operations
10.18.8. Class template basic_stream_socket
10.18.8.1. basic_stream_socket constructors
10.18.8.2. basic_stream_socket assignment
10.18.8.3. basic_stream_socket operations
10.18.9. Class template basic_socket_acceptor
10.18.9.1. basic_socket_acceptor constructors
10.18.9.2. basic_socket_acceptor destructor
10.18.9.3. basic_socket_acceptor assignment
10.18.9.4. basic_socket_acceptor operations
10.19. Socket iostreams
10.19.1. Class template basic_socket_streambuf
10.19.1.1. basic_socket_streambuf constructors
10.19.1.2. basic_socket_streambuf members
10.19.1.3. basic_socket_streambuf overridden virtual functions
10.19.2. Class template basic_socket_iostream
10.19.2.1. basic_socket_iostream constructors
10.19.2.2. basic_socket_iostream members
10.20. Socket algorithms
10.20.1. Synchronous connect operations
10.20.2. Asynchronous connect operations
10.21. Internet protocol
10.21.1. Header <experimental/internet> synopsis
10.21.2. Requirements
10.21.2.1. Internet protocol requirements
10.21.2.2. Multicast group socket options
10.21.3. Error codes
10.21.4. Class ip::address
10.21.4.1. ip::address constructors
10.21.4.2. ip::address assignment
10.21.4.3. ip::address members
10.21.4.4. ip::address comparisons
10.21.4.5. ip::address creation
10.21.4.6. ip::address I/O
10.21.5. Class ip::address_v4
10.21.5.1. Struct ip::address_v4::bytes_type
10.21.5.2. ip::address_v4 constructors
10.21.5.3. ip::address_v4 members
10.21.5.4. ip::address_v4 static members
10.21.5.5. ip::address_v4 comparisons
10.21.5.6. ip::address_v4 creation
10.21.5.7. ip::address_v4 I/O
10.21.6. Class ip::address_v6
10.21.6.1. Struct ip::address_v6::bytes_type
10.21.6.2. ip::address_v6 constructors
10.21.6.3. ip::address_v6 members
10.21.6.4. ip::address_v6 static members
10.21.6.5. ip::address_v6 comparisons
10.21.6.6. ip::address_v6 creation
10.21.6.7. ip::address_v6 I/O
10.21.7. Class ip::bad_address_cast
10.21.8. Hash support
10.21.9. Class template ip::basic_address_iterator specializations
10.21.10. Class template ip::basic_address_range specializations
10.21.11. Class template ip::network_v4
10.21.11.1. ip::network_v4 constructors
10.21.11.2. ip::network_v4 members
10.21.11.3. ip::network_v4 comparisons
10.21.11.4. ip::network_v4 creation
10.21.11.5. ip::network_v4 I/O
10.21.12. Class template ip::network_v6
10.21.12.1. ip::network_v6 constructors
10.21.12.2. ip::network_v6 members
10.21.12.3. ip::network_v6 comparisons
10.21.12.4. ip::network_v6 creation
10.21.12.5. ip::network_v6 I/O
10.21.13. Class template ip::basic_endpoint
10.21.13.1. ip::basic_endpoint constructors
10.21.13.2. ip::basic_endpoint members
10.21.13.3. ip::basic_endpoint comparisons
10.21.13.4. ip::basic_endpoint I/O
10.21.13.5. ip::basic_endpoint members (extensible implementations)
10.21.14. Class template ip::basic_resolver_entry
10.21.14.1. ip::basic_resolver_entry constructors
10.21.14.2. ip::basic_resolver_entry members
10.21.14.3. op::basic_resolver_entry comparisons
10.21.15. Class template ip::basic_resolver_results
10.21.15.1. ip::basic_resolver_results constructors
10.21.15.2. ip::basic_resolver_results assignment
10.21.15.3. ip::basic_resolver_results size
10.21.15.4. ip::basic_resolver_results element access
10.21.15.5. ip::basic_resolver_results swap
10.21.15.6. ip::basic_resolver_results comparisons
10.21.16. Class ip::resolver_base
10.21.17. Class template ip::basic_resolver
10.21.17.1. ip::basic_resolver constructors
10.21.17.2. ip::basic_resolver destructor
10.21.17.3. ip::basic_resolver assignment
10.21.17.4. ip::basic_resolver operations
10.21.18. Host name functions
10.21.19. Class ip::tcp
10.21.19.1. ip::tcp comparisons
10.21.20. Class ip::udp
10.21.20.1. ip::udp comparisons
10.21.21. Internet socket options
10.21.21.1. Class ip::multicast::outbound_interface
10.22. Index

10.1. Scope

[scope]

This Technical Specification describes extensions to the C++ Standard Library. This Technical Specification specifies requirements for implementations of an interface that computer programs written in the C++ programming language may use to perform operations related to networking, such as operations involving sockets, timers, buffer management, host name resolution and internet protocols. This Technical Specification is applicable to information technology systems that can perform network operations, such as those with operating systems that conform to the POSIX interface. This Technical Specification is applicable only to vendors who wish to provide the interface it describes.

10.2. Conformance

[conformance]

Conformance is specified in terms of behavior. Ideal behavior is not always implementable, so the conformance sub-clauses take that into account.

10.2.1. POSIX conformance

[conformance.9945]

Some behavior is specified by reference to POSIX. How such behavior is actually implemented is unspecified.

[Note: This constitutes an "as if" rule allowing implementations to call native operating system or other APIs. —end note]

Implementations are encouraged to provide such behavior as it is defined by POSIX. Implementations shall document any behavior that differs from the behavior defined by POSIX. Implementations that do not support exact POSIX behavior are encouraged to provide behavior as close to POSIX behavior as is reasonable given the limitations of actual operating systems and file systems. If an implementation cannot provide any reasonable behavior, the implementation shall report an error as specified in Error Reporting.

[Note: This allows users to rely on an exception being thrown or an error code being set when an implementation cannot provide any reasonable behavior. —end note]

Implementations are not required to provide behavior that is not supported by a particular operating system.

10.2.2. Conditionally-supported features

[conformance.conditional]

This Technical Specification defines conditially-supported features, in the form of additional member functions on types that satisfy Protocol, Endpoint, SettableSocketOption, GettableSocketOption or IoControlCommand requirements.

[Note: This is so that, when the additional member functions are available, C++ programs may extend the library to add support for other protocols and socket options. —end note]

For the purposes of this Technical Specification, implementations that provide all of the additional member functions are known as extensible implementations.

[Note: Implementations are encouraged to provide the additional member functions, where possible. It is intended that POSIX and Windows implementations will provide them. —end note]

10.3. Normative references

[references]

The following referenced documents are indispensable for the application of this document. For dated references, only the edition cited applies. For undated references, the latest edition of the referenced document (including any amendments) applies.

  • ISO/IEC 14882, Programming Language C++
  • ISO/IEC 9945, Information Technology — Portable Operating System Interface (POSIX)
  • C++ Extensions for Library Fundamentals

[Note: The programming language and library described in ISO/IEC 14882 is herein called the C++ Standard. References to clauses within the C++ Standard are written as "C++Std [xref]".
The operating system interface described in ISO/IEC 9945 is herein called POSIX. —end note]

This Technical Specification mentions commercially available operating systems for purposes of exposition. [1]

Unless otherwise specified, the whole of the C++ Standard's Library introduction (C++Std [library]) is included into this Technical Specification by reference.

10.4. Namespaces and headers

[namespaces]

The components described in this Technical Specification are experimental and not part of the C++ standard library. All components described in this Technical Specification are declared in namespace std::experimental::net::v1 or a sub-namespace thereof unless otherwise specified. The headers described in this technical specification shall import the contents of std::experimental::net::v1 into std::experimental::net as if by:

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

Unless otherwise specified, references to other entities described in this Technical Specification are assumed to be qualified with std::experimental::net::v1::, references to entities described in the C++ standard are assumed to be qualified with std::, and references to entities described in C++ Extensions for Library Fundamentals are assumed to be qualified with std::experimental::fundamentals_v1::.

10.5. Definitions

[defs]

10.5.1. host byte order

[defs.host.byte.order] See section 3.194 of POSIX Base Definitions, Host Byte Order.

10.5.2. network byte order

[defs.net.byte.order] See section 3.238 of POSIX Base Definitions, Network Byte Order.

10.5.3. synchronous operation

[defs.sync.op] A synchronous operation is one where control is not returned until the operation completes.

10.5.4. asynchronous operation

[defs.async.op] An asynchronous operation is one where control is returned immediately without waiting for the operation to complete. Multiple asynchronous operations may be executed concurrently.

10.5.5. orderly shutdown

[defs.orderly.shutdown] The procedure for shutting down a stream after all work in progress has been completed, without loss of data.

10.6. Future plans (Informative)

[plans]

This section describes tentative plans for future versions of this technical specification and plans for moving content into future versions of the C++ Standard.

The C++ committee may release new versions of this technical specification, containing networking library extensions we hope to add to a near-future version of the C++ Standard. Future versions will define their contents in std::experimental::net::v2, std::experimental::net::v3, etc., with the most recent implemented version inlined into std::experimental::net.

When an extension defined in this or a future version of this technical specification represents enough existing practice, it will be moved into the next version of the C++ Standard by replacing the experimental::net::vN segment of its namespace with net, and by removing the experimental/ prefix from its header's path.

10.7. Feature test macros (Informative)

[feature.test]

These macros allow users to determine which version of this Technical Specification is supported by the headers defined by the specification. All headers in this Technical Specification shall supply the following macro definition:

#define __cpp_lib_experimental_net yyyymm

If an implementation supplies all of the conditionally-supported features specified in [conformance.conditional], all headers in this Technical Specification shall supply the following macro definition:

#define __cpp_lib_experimental_net_extensible yyyymm

[Note: The value of the macros __cpp_lib_experimental_net and __cpp_lib_experimental_net_extensible is yyyymm where yyyy is the year and mm the month when the version of the Technical Specification was completed. —end note]

10.8. Method of description (Informative)

[description]

This subclause describes the conventions used to specify this Technical Specification, in addition to those conventions specified in C++Std [description].

10.8.1. Structure of each clause

[structure]

10.8.1.1. Detailed specifications

[structure.specifications]

In addition to the elements defined in C++Std [structure.specifications], descriptions of function semantics contain the following elements (as appropriate):

Completion signature: - if the function initiates an asynchronous operation, specifies the signature of a completion handler used to receive the result of the operation.

10.8.2. Other conventions

[conventions]

10.8.2.1. Nested classes

[nested.class]

Several classes defined in this Technical Specification are nested classes. For a specified nested class A::B, an implementation is permitted to define A::B as a synonym for a class with equivalent functionality to class A::B. [Note: When A::B is a synonym for another type A shall provide a nested type B, to emulate the injected class name. —end note]

10.9. Error reporting

[err.report]

10.9.1. Synchronous operations

[err.report.sync]

Most synchronous network library functions provide two overloads, one that throws an exception to report system errors, and another that sets an error_code (C++Std [syserr]).

[Note: This supports two common use cases:

— Uses where system errors are truly exceptional and indicate a serious failure. Throwing an exception is the most appropriate response.

— Uses where system errors are routine and do not necessarily represent failure. Returning an error code is the most appropriate response. This allows application specific error handling, including simply ignoring the error.

end note]

Functions not having an argument of type error_code& report errors as follows, unless otherwise specified:

— When a call by the implementation to an operating system or other underlying API results in an error that prevents the function from meeting its specifications, the function exits via an exception of a type that would match a handler of type system_error.

— Destructors throw nothing.

Functions having an argument of type error_code& report errors as follows, unless otherwise specified:

— If a call by the implementation to an operating system or other underlying API results in an error that prevents the function from meeting its specifications, the error_code& argument ec is set as appropriate for the specific error. Otherwise, the ec argument is set such that !ec is true.

Where a function is specified as two overloads, with and without an argument of type error_code&:

R f(A1 a1, A2 a2, ..., AN aN);
R f(A1 a1, A2 a2, ..., AN aN, error_code& ec);

then, when R is non-void, the effects of the first overload are as if:

error_code ec;
R r(f(a1, a2, ..., aN, ec));
if (ec) throw system_error(ec, __func__);
return r;

otherwise, when R is void, the effects of the first overload are as if:

error_code ec;
f(a1, a2, ..., aN, ec);
if (ec) throw system_error(ec, __func__);

except that the type thrown may differ as specified above.

For both overloads, failure to allocate storage is reported by throwing an exception as described in the C++ standard (C++Std [res.on.exception.handling]).

In this Technical Specification, when a type requirement is specified using two function call expressions f, with and without an argument ec of type error_code:

f(a1, a2, ..., aN)
f(a1, a2, ..., aN, ec)

then the effects of the first call expression of f shall be as described for the first overload above.

10.9.2. Asynchronous operations

[err.report.async]

Asynchronous network library functions in this Technical Specification are identified by having the prefix async_ and take a completion handler [async.reqmts.async.token]. These asynchronous operations report errors as follows:

— If a call by the implementation to an operating system or other underlying API results in an error that prevents the asynchronous operation from meeting its specifications, the completion handler is invoked with an error_code value ec that is set as appropriate for the specific error. Otherwise, the error_code value ec is set such that !ec is true.

— Asynchronous operations shall not fail with an error condition that indicates interruption of an operating system or underlying API by a signal [Note: Such as POSIX error number EINTRend note] . Asynchronous operations shall not fail with any error condition associated with non-blocking operations [Note: Such as POSIX error numbers EWOULDBLOCK, EAGAIN, or EINPROGRESS; Windows error numbers WSAEWOULDBLOCK or WSAEINPROGRESSend note] .

In this Technical Specification, when a type requirement is specified as a call to a function or member function having the prefix async_, then the function shall satisfy the error reporting requirements described above.

10.9.3. Error conditions

[err.report.conditions]

Unless otherwise specified, when the behavior of a synchronous or asynchronous operation is defined "as if" implemented by a POSIX function, the error_code produced by the function shall meet the following requirements:

— If the failure condition is one that is listed by POSIX for that function, the error_code shall compare equal to the error's corresponding enum class errc (C++Std [syserr]) or enum class resolver_errc constant.

— Otherwise, the error_code shall be set to an implementation-defined value that reflects the underlying operating system error.

[Example: The POSIX specification for shutdown lists EBADF as one of its possible errors. If a function that is specified "as if" implemented by shutdown fails with EBADF then the following condition holds for the error_code value ec: ec == errc::bad_file_descriptorend example]

When the description of a function contains the element Error conditions, this lists conditions where the operation may fail. The conditions are listed, together with a suitable explanation, as enum class constants. Unless otherwise specified, this list is a subset of the failure conditions associated with the function.

10.9.4. Suppression of signals

[err.report.signal]

Some POSIX functions referred to in this Technical Specification may report errors by raising a SIGPIPE signal. Where a synchronous or asynchronous operation is specified in terms of these POSIX functions, the generation of SIGPIPE is suppressed and an error condition corresponding to POSIX EPIPE is produced instead.

10.10. Library summary

[summary]

Table 1. Networking library summary

Clause

Header(s)

Convenience header

<experimental/net>

Forward declarations

<experimental/netfwd>

Asynchronous model

<experimental/executor>

Basic I/O services

<experimental/io_context>

Timers

<experimental/timer>

Buffers
Buffer-oriented streams

<experimental/buffer>

Sockets
Socket iostreams
Socket algorithms

<experimental/socket>

Internet protocol

<experimental/internet>


Throughout this Technical Specification, the names of the template parameters are used to express type requirements, as listed in the table below.

Table 2. Template parameters and type requirements

template parameter name

type requirements

AcceptableProtocol

acceptable protocol

Allocator

C++Std [allocator.requirements]

AsyncReadStream

buffer-oriented asynchronous read stream

AsyncWriteStream

buffer-oriented asynchronous write stream

Clock

C++Std [time.clock.req]

CompletionCondition

completion condition

CompletionToken

completion token

ConnectCondition

connect condition

ConstBufferSequence

constant buffer sequence

DynamicBuffer

dynamic buffer

ExecutionContext

execution context

Executor

executor

GettableSocketOption

gettable socket option

InternetProtocol

Internet protocol

IoControlCommand

I/O control command

MutableBufferSequence

mutable buffer sequence

ProtoAllocator

proto-allocator

Protocol

protocol

Service

service

SettableSocketOption

settable socket option

Signature

signature

SyncReadStream

buffer-oriented synchronous read stream

SyncWriteStream

buffer-oriented synchronous write stream

WaitTraits

wait traits


10.11. Convenience header

[convenience.hdr]

10.11.1. Header <experimental/net> synopsis

[convenience.hdr.synop]

#include <experimental/executor>
#include <experimental/io_context>
#include <experimental/timer>
#include <experimental/buffer>
#include <experimental/socket>
#include <experimental/internet>

[Note: This header is provided as a convenience for programs so that they may access all networking facilities via a single, self-contained #include. —end note]

10.12. Forward declarations

[fwd.decl]

10.12.1. Header <experimental/netfwd> synopsis

[fwd.decl.synop]

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

  class execution_context;
  template<class T, class Executor>
    class executor_binder;
  template<class Executor>
    class executor_work_guard;
  class system_executor;
  class executor;
  template<class Executor>
    class strand;

  class io_context;

  template<class Clock> struct wait_traits;
  template<class Clock, class WaitTraits = wait_traits<Clock>>
    class basic_waitable_timer;
  typedef basic_waitable_timer<chrono::system_clock> system_timer;
  typedef basic_waitable_timer<chrono::steady_clock> steady_timer;
  typedef basic_waitable_timer<chrono::high_resolution_clock> high_resolution_timer;

  template<class Protocol>
    class basic_socket;
  template<class Protocol>
    class basic_datagram_socket;
  template<class Protocol>
    class basic_stream_socket;
  template<class Protocol>
    class basic_socket_acceptor;
  template<class Protocol, class Clock = chrono::steady_clock,
    class WaitTraits = wait_traits<Clock>>
      class basic_socket_streambuf;
  template<class Protocol, class Clock = chrono::steady_clock,
    class WaitTraits = wait_traits<Clock>>
      class basic_socket_iostream;

  namespace ip {

    class address;
    class address_v4;
    class address_v6;
    class address_iterator_v4;
    class address_iterator_v6;
    class address_range_v4;
    class address_range_v6;
    class network_v4;
    class network_v6;
    template<class InternetProtocol>
      class basic_endpoint;
    template<class InternetProtocol>
      class basic_resolver_entry;
    template<class InternetProtocol>
      class basic_resolver_results;
    template<class InternetProtocol>
      class basic_resolver;
    class tcp;
    class udp;

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

Default template arguments are described as appearing both in <netfwd> and in the synopsis of other headers but it is well-formed to include both <netfwd> and one or more of the other headers. [Note: It is the implementation’s responsibility to implement headers so that including <netfwd> and other headers does not violate the rules about multiple occurrences of default arguments. —end note]

10.13. Asynchronous model

[async]

10.13.1. Header <experimental/executor> synopsis

[async.synop]

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

  template<class CompletionToken, class Signature, class = void>
    class async_result;

  template<class CompletionToken, class Signature>
    struct async_completion;

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

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

  // get_associated_allocator:

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

  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;

  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) noexcept;
  template<class T, class Executor>
    associated_executor_t<T, Executor>
      get_associated_executor(const T& t, const Executor& ex) noexcept;
  template<class T, class ExecutionContext>
    associated_executor_t<T, typename ExecutionContext::executor_type>
      get_associated_executor(const T& t, ExecutionContext& ctx) noexcept;

  template<class T, class Executor>
    class executor_binder;

  template<class T, class Executor, class Signature>
    class async_result<executor_binder<T, Executor>, Signature>;

  template<class T, class Executor, class ProtoAllocator>
    struct associated_allocator<executor_binder<T, Executor>, ProtoAllocator>;

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

  // bind_executor:

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

  template<class Executor>
    class executor_work_guard;

  // make_work_guard:

  template<class Executor>
    executor_work_guard<Executor>
      make_work_guard(const Executor& ex);
  template<class ExecutionContext>
    executor_work_guard<typename ExecutionContext::executor_type>
      make_work_guard(ExecutionContext& ctx);
  template<class T>
    executor_work_guard<associated_executor_t<T>>
      make_work_guard(const T& t);
  template<class T, class U>
    auto make_work_guard(const T& t, U&& u)
      -> decltype(make_work_guard(get_associated_executor(t, forward<U>(u))));

  class system_executor;
  class system_context;

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

  class bad_executor;

  class executor;

  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>
    DEDUCED dispatch(CompletionToken&& token);
  template<class Executor, class CompletionToken>
    DEDUCED dispatch(const Executor& ex, CompletionToken&& token);
  template<class ExecutionContext, class CompletionToken>
    DEDUCED dispatch(ExecutionContext& ctx, CompletionToken&& token);

  // post:

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

  // defer:

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

  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 ProtoAllocator = allocator<void>>
    class use_future_t;

  constexpr use_future_t<> use_future = use_future_t<>();

  template<class ProtoAllocator, class Result, class... Args>
    class async_result<use_future_t<ProtoAllocator>, Result(Args...)>;

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

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

  template<class Allocator>
    struct uses_allocator<experimental::net::v1::executor, Allocator>
      : true_type {};

} // namespace std

10.13.2. Requirements

[async.reqmts]

10.13.2.1. Proto-allocator requirements

[async.reqmts.proto.allocator]

A type A meets the proto-allocator requirements if A is CopyConstructible (C++Std [copyconstructible]), Destructible (C++Std [destructible]), and allocator_traits<A>::rebind_alloc<U> meets the allocator requirements (C++Std [allocator.requirements]), where U is an object type. [Note: For example, std::allocator<void> meets the proto-allocator requirements but not the allocator requirements. —end note] No constructor, comparison operator, copy operation, move operation, or swap operation on these types shall exit via an exception.

10.13.2.2. Executor requirements

[async.reqmts.executor]

The library describes a standard set of requirements for executors. A type meeting the Executor requirements embodies a set of rules for determining how submitted function objects are to be executed.

A type X meets the Executor requirements if it satisfies the requirements of CopyConstructible (C++Std [copyconstructible]) and Destructible (C++Std [destructible]), as well as the additional requirements listed below.

No constructor, comparison operator, copy operation, move operation, swap operation, or member functions context, on_work_started, and on_work_finished on these types shall exit via an exception.

The executor copy constructor, comparison operators, and other member functions defined in these requirements shall not introduce data races as a result of concurrent calls to those functions from different threads.

In the table below, x1 and x2 denote values of type X, cx1 and cx2 denote (possibly const) values of type X, mx1 denotes an xvalue of type X, f denotes a MoveConstructible (C++Std [moveconstructible]) function object callable with zero arguments, a denotes a (possibly const) value of type A meeting the Allocator requirements (C++Std [allocator.requirements]), and u denotes an identifier.

Table 3. Executor requirements

expression

type

assertion/note
pre/post-conditions

X u(cx1);

Shall not exit via an exception.

post: u == cx1 and std::addressof(u.context()) == std::addressof(cx1.context()).

X u(mx1);

Shall not exit via an exception.

post: u equals the prior value of mx1 and std::addressof(u.context()) equals the prior value of std::addressof(mx1.context()).

cx1 == cx2

bool

Returns true only if cx1 and cx2 can be interchanged with identical effects in any of the expressions defined in these type requirements. [Note: Returning 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.

cx1 != cx2

bool

Same as !(cx1 == cx2).

x1.context()

execution_context&, or E& where E is a type that satifisfies the ExecutionContext requirements.

Shall not exit via an exception.

The comparison operators and member functions defined in these requirements shall not alter the reference returned by this function.

x1.on_work_started()

Shall not exit via an exception.

x1.on_work_finished()

Shall not exit via an exception.

Precondition: A preceding call x2.on_work_started() where x1 == x2.

x1.dispatch(std::move(f),a)

Effects: Creates an object f1 initialized with DECAY_COPY(forward<Func>(f)) (C++Std [thread.decaycopy]) in the current thread of execution . Calls f1() at most once. The executor may block forward progress of the caller until f1() finishes execution.

Executor implementations should use the supplied allocator to allocate any memory required to store the function object. Prior to invoking the function object, the executor shall deallocate any memory allocated. [Note: Executors defined in this Technical Specification always use the supplied allocator unless otherwise specified. —end note]

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

x1.post(std::move(f),a)
x1.defer(std::move(f),a)

Effects: Creates an object f1 initialized with DECAY_COPY(forward<Func>(f)) in the current thread of execution. Calls f1() at most once. The executor shall not block forward progress of the caller pending completion of f1().

Executor implementations should use the supplied allocator to allocate any memory required to store the function object. Prior to invoking the function object, the executor shall deallocate any memory allocated. [Note: Executors defined in this Technical Specification always use the supplied allocator unless otherwise specified. —end note]

Synchronization: The invocation of post or defer synchronizes with (C++Std [intro.multithread]) the invocation of f1.

[Note: Although the requirements placed on defer are identical to post, the use of post conveys a preference that the caller does not block the first step of f1's progress, whereas defer conveys a preference that the caller does block the first step of f1. One use of defer is to convey the intention of the caller that f1 is a continuation of the current call context. The executor may use this information to optimize or otherwise adjust the way in which f1 is invoked. —end note]


10.13.2.3. Execution context requirements

[async.reqmts.executioncontext]

A type X meets the ExecutionContext requirements if it is publicly and unambiguously derived from execution_context, and satisfies the additional requirements listed below.

In the table below, x denotes a value of type X.

Table 4. ExecutionContext requirements

expression

return type

assertion/note
pre/post-condition

X::executor_type

type meeting Executor requirements

x.~X()

Destroys all unexecuted function objects that were submitted via an executor object that is associated with the execution context.

x.get_executor()

X::executor_type

Returns an executor object that is associated with the execution context.


10.13.2.4. Service requirements

[async.reqmts.service]

A class is a service if it is publicly and unambiguously derived from execution_context::service, or if it is publicly and unambiguously derived from another service. For a service S, S::key_type shall be valid and denote a type (C++Std [temp.deduct]), is_base_of_v<typename S::key_type, S> shall be true, and S shall satisfy the Destructible requirements (C++Std [destructible]).

The first parameter of all service constructors shall be an lvalue reference to execution_context. This parameter denotes the execution_context object that represents a set of services, of which the service object will be a member. [Note: These constructors may be called by the make_service function. —end note]

A service shall provide an explicit constructor with a single parameter of lvalue reference to execution_context. [Note: This constructor may be called by the use_service function. —end note]

[Example:

class my_service : public execution_context::service
{
public:
  typedef my_service key_type;
  explicit my_service(execution_context& ctx);
  my_service(execution_context& ctx, int some_value);
private:
  virtual void shutdown() noexcept override;
  ...
};

end example]

A service's shutdown member function shall destroy all copies of user-defined function objects that are held by the service.

10.13.2.5. Signature requirements

[async.reqmts.signature]

A type satisfies the signature requirements if it is a call signature (C++Std [func.def]).

10.13.2.6. Associator requirements

[async.reqmts.associator]

An associator defines a relationship between different types and objects where, given:

— a source object s of type S,

— type requirements R, and

— a candidate object c of type C meeting the type requirements R

an associated type A meeting the type requirements R may be computed, and an associated object a of type A may be obtained.

An associator shall be a class template that takes two template type arguments. The first template argument is the source type S. The second template argument is the candidate type C. The second template argument shall be defaulted to some default candidate type D that satisfies the type requirements R.

An associator shall additionally satisfy the requirements in the table below. In this table, X is a class template that meets the associator requirements, S is the source type, s is a (possibly const) value of type S, C is the candidate type, c is a (possibly const) value of type C, D is the default candidate type, and d is a (possibly const) value of type D that is the default candidate object.

Table 5. Associator requirements

expression

return type

assertion/note
pre/post-conditions

X<S>::type

X<S, D>::type

X<S, C>::type

The associated type.

X<S>::get(s)

X<S>::type

Returns X<S>::get(S, d).

X<S, C>::get(s, c)

X<S, C>::type

Returns the associated object.


The associator's primary template shall be defined. A program may partially specialize the associator class template for some user-defined type S.

Finally, the associator shall provide the following type alias and function template in the enclosing namespace:

template<class S, class C = D> using X_t = typename X<S, C>::type;

template<class S, class C = D>
typename X<S, C>::type get_X(const S& s, const C& c = d)
{
  return X<S, C>::get(s, c);
}

where X is replaced with the name of the associator class template. [Note: This function template is provided as a convenience, to automatically deduce the source and candidate types. —end note]

10.13.2.7. Requirements on asynchronous operations

[async.reqmts.async]

This section uses the names Alloc1, Alloc2, alloc1, alloc2, Args, CompletionHandler, completion_handler, Executor1, Executor2, ex1, ex2, f, i, N, Signature, token, Ti, ti, work1, and work2 as placeholders for specifying the requirements below.

10.13.2.7.1. General asynchronous operation concepts

[async.reqmts.async.concepts]

An initiating function is a function which may be called to start an asynchronous operation. A completion handler is a function object that will be invoked, at most once, with the result of the asynchronous operation.

The lifecycle of an asynchronous operation is comprised of the following events and phases:

— Event 1: The asynchronous operation is started by a call to the initiating function.

— Phase 1: The asynchronous operation is now outstanding.

— Event 2: The externally observable side effects of the asynchronous operation, if any, are fully established. The completion handler is submitted to an executor.

— Phase 2: The asynchronous operation is now completed.

— Event 3: The completion handler is called with the result of the asynchronous operation.

In this Technical Specification, all functions with the prefix async_ are initiating functions.

10.13.2.7.2. Completion tokens and handlers

[async.reqmts.async.token]

Initiating functions:

— are function templates with template parameter CompletionToken;

— accept, as the final parameter, a completion token object token of type CompletionToken;

— specify a completion signature, which is a call signature (C++Std [func.def]) Signature that determines the arguments to the completion handler.

An initiating function determines the type CompletionHandler of its completion handler function object by performing typename async_result<decay_t<CompletionToken>, Signature>::completion_handler_type. The completion handler object completion_handler is initialized with forward<CompletionToken>(token). [Note: No other requirements are placed on the type CompletionToken. —end note]

The type CompletionHandler must satisfy the requirements of Destructible (C++Std [destructible]) and MoveConstructible (C++Std [moveconstructible]), and be callable with the specified call signature.

In this Technical Specification, all initiating functions specify a Completion signature element that defines the call signature Signature. The Completion signature elements in this Technical Specification have named parameters, and the results of an asynchronous operation are specified in terms of these names.

10.13.2.7.3. Automatic deduction of initiating function return type

[async.reqmts.async.return.type]

The return type of an initiating function is typename async_result<decay_t<CompletionToken>, Signature>::return_type.

For the sake of exposition, this Technical Specification sometimes annotates functions with a return type DEDUCED. For every function declaration that returns DEDUCED, the meaning is equivalent to specifying the return type as typename async_result<decay_t<CompletionToken>, Signature>::return_type.

10.13.2.7.4. Production of initiating function return value

[async.reqmts.async.return.value]

An initiating function produces its return type as follows:

— constructing an object result of type async_result<decay_t<CompletionToken>, Signature>, initialized as result(completion_handler); and

— using result.get() as the operand of the return statement.

[Example: Given an asynchronous operation with Completion signature void(R1 r1, R2 r2), an initiating function meeting these requirements may be implemented as follows:

template<class CompletionToken>
auto async_xyz(T1 t1, T2 t2, CompletionToken&& token)
{
  typename async_result<decay_t<CompletionToken>, void(R1, R2)>::completion_handler_type
    completion_handler(forward<CompletionToken>(token));

  async_result<decay_t<CompletionToken>, void(R1, R2)> result(completion_handler);

  // initiate the operation and cause completion_handler to be invoked with
  // the result

  return result.get();
}

For convenience, initiating functions may be implemented using the async_completion template:

template<class CompletionToken>
auto async_xyz(T1 t1, T2 t2, CompletionToken&& token)
{
  async_completion<CompletionToken, void(R1, R2)> init(token);

  // initiate the operation and cause init.completion_handler to be invoked
  // with the result

  return init.result.get();
}

end example]

10.13.2.7.5. Lifetime of initiating function arguments

[async.reqmts.async.lifetime]

Unless otherwise specified, the lifetime of arguments to initiating functions shall be treated as follows:

— If the parameter has a pointer type or has a type of lvalue reference to non-const, the implementation may assume the validity of the pointee or referent, respectively, until the completion handler is invoked. [Note: In other words, the program must guarantee the validity of the argument until the completion handler is invoked. —end note]

— Otherwise, the implementation must not assume the validity of the argument after the initiating function completes. [Note: In other words, the program is not required to guarantee the validity of the argument after the initiating function completes. —end note] The implementation may make copies of the argument, and all copies shall be destroyed no later than immediately after invocation of the completion handler.

10.13.2.7.6. Non-blocking requirements on initiating functions

[async.reqmts.async.non.blocking]

An initiating function shall not block (C++Std [defns.block]) the calling thread pending completion of the outstanding operation.

[Note: Initiating functions may still block the calling thread for other reasons. For example, an initiating function may lock a mutex in order to synchronize access to shared data. —end note]

10.13.2.7.7. Associated executor

[async.reqmts.async.assoc.exec]

Certain objects that participate in asynchronous operations have an associated executor. These are obtained as specified below.

10.13.2.7.8. I/O executor

[async.reqmts.async.io.exec]

An asynchronous operation has an associated executor satisfying the Executor requirements. If not otherwise specified by the asynchronous operation, this associated executor is an object of type system_executor.

All asynchronous operations in this Technical Specification have an associated executor object that is determined as follows:

— If the initiating function is a member function, the associated executor is that returned by the get_executor member function on the same object.

— If the initiating function is not a member function, the associated executor is that returned by the get_executor member function of the first argument to the initiating function.

Let Executor1 be the type of the associated executor. Let ex1 be a value of type Executor1, representing the associated executor object obtained as described above.

10.13.2.7.9. Completion handler executor

[async.reqmts.async.handler.exec]

A completion handler object of type CompletionHandler has an associated executor of type Executor2 satisfying the Executor requirements. The type Executor2 is associated_executor_t<CompletionHandler, Executor1>. Let ex2 be a value of type Executor2 obtained by performing get_associated_executor(completion_handler, ex1).

10.13.2.7.10. Outstanding work

[async.reqmts.async.work]

The implementation of an asynchronous operation shall maintain an object work1 of type executor_work_guard<Executor1>, initialized with work1(ex1) and with work1.owns_work() == true, until the asynchronous operation has completed.

The implementation of an asynchronous operation shall maintain an object work2 of type executor_work_guard<Executor2>, initialized with work2(ex2) and with work2.owns_work() == true, until the asynchronous operation has completed and completion_handler has been submitted for execution.

10.13.2.7.11. Allocation of intermediate storage

[async.reqmts.async.alloc]

Asynchronous operations may allocate memory. [Note: Such as a data structure to store copies of the completion_handler object and the initiating function's arguments. —end note]

Let Alloc1 be a type, satisfying the ProtoAllocator requirements, that represents the asynchronous operation's default allocation strategy. [Note: Typically std::allocator<void>. —end note] Let alloc1 be a value of type Alloc1.

A completion handler object of type CompletionHandler has an associated allocator object alloc2 of type Alloc2 satisfying the ProtoAllocator requirements. The type Alloc2 is associated_allocator_t<CompletionHandler, Alloc1>. Let alloc2 be a value of type Alloc2 obtained by performing get_associated_allocator(completion_handler, alloc1).

The asynchronous operations defined in this Technical Specification:

— If required, allocate memory using only the completion handler's associated allocator.

— Prior to completion handler execution, deallocate any memory allocated using the completion handler's associated allocator.

[Note: The implementation may perform operating system or underlying API calls that perform memory allocations not using the associated allocator. Invocations of the allocator functions may not introduce data races (See C++Std [res.on.data.races]). —end note]

10.13.2.7.12. Execution of completion handler on completion of asynchronous operation

[async.reqmts.async.completion]

Let Args... be the argument types of the completion signature Signature and let N be sizeof...(Args). Let i be in the range [0,N). Let Ti be the ith type in Args... and let ti be the ith completion handler argument associated with Ti.

Let f be a function object, callable as f(), that invokes completion_handler as if by completion_handler(forward<T0>(t0), ..., forward<TN-1>(tN-1)).

If an asynchonous operation completes immediately (that is, within the thread of execution calling the initiating function, and before the initiating function returns), the completion handler shall be submitted for execution as if by performing ex2.post(std::move(f), alloc2). Otherwise, the completion handler shall be submitted for execution as if by performing ex2.dispatch(std::move(f), alloc2).

10.13.2.7.13. Completion handlers and exceptions

[async.reqmts.async.exceptions]

Completion handlers are permitted to throw exceptions. The effect of any exception propagated from the execution of a completion handler is determined by the executor which is executing the completion handler.

10.13.3. Class template async_result

[async.async.result]

The async_result class template is a customization point for asynchronous operations. 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 completion handler type invoked on completion of the asynchronous operation. The async_result template:

— transforms a CompletionToken into a completion handler type that is based on a Signature; and

— determines the return type and return value of an asynchronous operation's initiating function.

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

  template<class CompletionToken, class Signature, class = void>
  class async_result
  {
  public:
    typedef CompletionToken completion_handler_type;
    typedef void return_type;

    explicit async_result(completion_handler_type&) {}
    async_result(const async_result&) = delete;
    async_result& operator=(const async_result&) = delete;

    return_type get() {}
  };

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

The template parameter CompletionToken shall be an object type. The template parameter Signature shall be a call signature (C++Std [func.def]).

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; r is a modifiable lvalue of type R; and h is a modifiable lvalue of type R::completion_handler_type.

Table 6. async_result specialization requirements

Expression

Return type

Requirement

R::completion_handler_type

A type satisfying MoveConstructible requirements (C++Std [moveconstructible]), An object of type completion_handler_type shall be a function object with call signature Signature, and completion_handler_type shall be constructible with an rvalue of type CompletionToken.

R::return_type

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

R r(h);

r.get()

R::return_type

[Note: An asynchronous operation's initiating function uses the get() member function as the sole operand of a return statement. —end note]


10.13.4. Class template async_completion

[async.async.completion]

Class template async_completion is provided as a convenience, to simplify the implementation of asynchronous operations that use async_result.

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

  template<class CompletionToken, class Signature>
  struct async_completion
  {
    typedef async_result<decay_t<CompletionToken>,
      Signature>::completion_handler_type
        completion_handler_type;

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

    see below completion_handler;
    async_result<decay_t<CompletionToken>, Signature> result;
  };

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

The template parameter Signature shall be a call signature (C++Std [func.def]).

explicit async_completion(CompletionToken& t);

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

see below completion_handler;

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

10.13.5. Class template associated_allocator

[async.assoc.alloc]

Class template associated_allocator is an associator for the ProtoAllocator type requirements, with default candidate type allocator<void> and default candidate object allocator<void>().

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

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

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

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

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 parameters T and ProtoAllocator; t is a value of (possibly const) T; and a is an object of type ProtoAllocator.

Table 7. associated_allocator specialization requirements

Expression

Return type

Note

typename X::type

A type meeting the proto-allocator requirements.

X::get(t)

X::type

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

X::get(t, a)

X::type

Shall not exit via an exception.


10.13.5.1. associated_allocator members

[async.assoc.alloc.members]

typedef see below type;

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

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

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

10.13.6. Function get_associated_allocator

[async.assoc.alloc.get]

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

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

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

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

10.13.7. Class execution_context

[async.exec.ctx]

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

namespace std {
namespace experimental {
namespace net {
inline namespace 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() noexcept;
    void destroy() noexcept;
  };

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

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

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. If the service is not present in an execution_context, an object of type Service is created and added to the execution_context. A 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, make_service exits via an exception of type service_already_exists.

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

10.13.7.1. execution_context constructor

[async.exec.ctx.cons]

execution_context();

Effects: Creates an object of class execution_context which contains no services. [Note: An implementation might preload services of internal service types for its own use. —end note]

10.13.7.2. execution_context destructor

[async.exec.ctx.dtor]

~execution_context();

Effects: Destroys an object of class execution_context. Performs shutdown() followed by destroy().

10.13.7.3. execution_context operations

[async.exec.ctx.ops]

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 addition to the set.
— Otherwise, performs svc->notify_fork(e) in order of addition to the set.

10.13.7.4. execution_context protected operations

[async.exec.ctx.protected]

void shutdown() noexcept;

Effects: For each service object svc in the execution_context set, in reverse order of addition to the set, performs svc->shutdown(). For each service in the set, svc->shutdown() is called only once irrespective of the number of calls to shutdown on the execution_context.

void destroy() noexcept;

Effects: Destroys each service object in the execution_context set, and removes it from the set, in reverse order of addition to the set.

10.13.7.5. execution_context globals

[async.exec.ctx.globals]

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

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

Effects: If an object of type Service::key_type does not already exist in the execution_context set identified by ctx, creates an object of type Service, initialized as 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.

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

Requires: A service object of type Service::key_type does not already exist in the execution_context set identified by ctx.

Effects: Creates an object of type Service, initialized as 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.

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

Returns: true if an object of type Service::key_type is present in ctx, otherwise false.

10.13.8. Class execution_context::service

[async.exec.ctx.svc]

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

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

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

    // service observers:

    execution_context& context() noexcept;

  private:
    // service operations:

    virtual void shutdown() noexcept = 0;
    virtual void notify_fork(fork_event e) {}

    execution_context& context_; // exposition only
  };

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

explicit service(execution_context& owner);

Postconditions: std::addressof(context_) == std::addressof(owner).

execution_context& context() noexcept;

Returns: context_.

10.13.9. Class template is_executor

[async.is.exec]

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

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

  template<class T> struct is_executor;

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

T shall be a complete type.

Class template is_executor is a UnaryTypeTrait (C++Std [meta.rqmts]) with a BaseCharacteristic of true_type if the type T meets the syntactic requirements for Executor, otherwise false_type.

10.13.10. Executor argument tag

[async.executor.arg]

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

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

} // inline namespace v1
} // namespace net
} // 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.

10.13.11. uses_executor

[async.uses.executor]

10.13.11.1. uses_executor trait

[async.uses.executor.trait]

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

  template<class T, class Executor> struct uses_executor;

} // inline namespace v1
} // namespace net
} // 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 provides a definition that is derived from true_type if a type T::executor_type exists and is_convertible<Executor, T::executor_type>::value != false, otherwise it 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 if the first argument of a constructor has type executor_arg_t and the second argument has type Executor.

10.13.11.2. uses-executor construction

[async.uses.executor.cons]

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 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, obj is initialized as obj(v1, v2, ..., vN).

10.13.12. Class template associated_executor

[async.assoc.exec]

Class template associated_allocator is an associator for the Executor type requirements, with default candidate type system_executor and default candidate object system_executor().

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

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 parameters T and Executor; t is a value of (possibly const) T; and e is an object of type Executor.

Table 8. 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.


10.13.12.1. associated_executor members

[async.assoc.exec.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.

10.13.13. Function get_associated_executor

[async.assoc.exec.get]

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

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

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

Returns: get_associated_executor(t, ctx.get_executor()).

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

10.13.14. Class template executor_binder

[async.exec.binder]

executor_binder<T, Executor> binds an executor of type Executor satisfying Executor requirements to an object or function of type T.

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

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

    typedef T target_type;
    typedef Executor executor_type;

    // construct / copy / destroy:

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

    ~executor_binder();

    // executor binder access:

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

    // executor binder invocation:

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

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

  template<class T, class Executor, class Signature>
    class async_result<executor_binder<T, Executor>, Signature>;

  template<class T, class Executor, class ProtoAllocator>
    struct associated_allocator<executor_binder<T, Executor>, ProtoAllocator>;

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

} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std
10.13.14.1. executor_binder constructors

[async.exec.binder.cons]

executor_binder(T t, const Executor& ex);

Effects: Initializes ex_ with ex. Initializes target_ by performing uses-executor construction, using the constructor argument std::move(t) and the executor ex_.

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

Requires: If U is not convertible to T, or if OtherExecutor is not convertible to Executor, the program is ill-formed.

Effects: Initializes ex_ with other.get_executor(). Initializes target_ by performing uses-executor construction, using the constructor argument other.get() and the executor ex_.

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

Requires: If U is not convertible to T, or if OtherExecutor is not convertible to Executor, the program is ill-formed.

Effects: Initializes ex_ with other.get_executor(). Initializes target_ by performing uses-executor construction, using the constructor argument std::move(other.get()) and the executor ex_.

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

Requires: If U is not convertible to T the program is ill-formed.

Effects: Initializes ex_ with ex. Initializes target_ by performing uses-executor construction, using the constructor argument other.get() and the executor ex_.

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

Requires: U is T or convertible to T.

Effects: Initializes ex_ with ex. Initializes target_ by performing uses-executor construction, using the constructor argument std::move(other.get()) and the executor ex_.

10.13.14.2. executor_binder access

[async.exec.binder.access]

T& get() noexcept;
const T& get() const noexcept;

Returns: target_.

executor_type get_executor() const noexcept;

Returns: executor_.

10.13.14.3. executor_binder invocation

[async.exec.binder.invocation]

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

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

10.13.14.4. Class template partial specialization async_result

[async.exec.binder.async.result]

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

  template<class T, class Executor, class Signature>
  class async_result<executor_binder<T, Executor>, Signature>
  {
  public:
    typedef executor_binder<
      typename async_result<T, Signature>::completion_handler_type,
        Executor> completion_handler_type;
    typedef typename async_result<T, Signature>::return_type return_type;

    explicit async_result(completion_handler_type& h);
    async_result(const async_result&) = delete;
    async_result& operator=(const async_result&) = delete;

    return_type get();

  private:
    async_result<T, Signature> target_; // exposition only
  };

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

explicit async_result(completion_handler_type& h);

Effects: Initializes target_ as target_(h.get()).

return_type get();

Returns: target_.get().

10.13.14.5. Class template partial specialization associated_allocator

[async.exec.binder.assoc.alloc]

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

  template<class T, class Executor, class ProtoAllocator>
    struct associated_allocator<executor_binder<T, Executor>, ProtoAllocator>
  {
    typedef associated_allocator_t<T, ProtoAllocator> type;

    static type get(const executor_binder<T, Executor>& b,
                    const ProtoAllocator& a = ProtoAllocator()) noexcept;
  };

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

static type get(const executor_binder<T, Executor>& b,
                const ProtoAllocator& a = ProtoAllocator()) noexcept;

Returns: associated_allocator<T, ProtoAllocator>::get(b.get(), a).

10.13.14.6. Class template partial specialization associated_executor

[async.exec.binder.assoc.exec]

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

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

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

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

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

Returns: b.get_executor().

10.13.15. Function bind_executor

[async.bind.executor]

template<class Executor, class T>
  executor_binder<decay_t<T>, Executor>
    bind_executor(const Executor& ex, T&& t);

Returns: executor_binder<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_binder<decay_t<T>, typename ExecutionContext::executor_type>
    bind_executor(ExecutionContext& ctx, T&& t);

Returns: bind_executor(ctx.get_executor(), forward<T>(t)).

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

10.13.16. Class template executor_work_guard

[async.exec.work.guard]

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

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

    typedef Executor executor_type;

    // construct / copy / destroy:

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

    executor_work_guard& operator=(const executor_work_guard&) = delete;

    ~executor_work_guard();

    // executor work guard observers:

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

    // executor work guard modifiers:

    void reset() noexcept;

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

} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std
10.13.16.1. executor_work_guard members

[async.exec.work.guard.members]

explicit executor_work_guard(const executor_type& ex) noexcept;

Effects: Initializes ex_ with ex, and then performs ex_.on_work_started().

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

executor_work_guard(const executor_work_guard& other) noexcept;

Effects: Initializes ex_ with other.ex_. If other.owns_ == true, performs ex_.on_work_started().

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

executor_work_guard(executor_work_guard&& other) noexcept;

Effects: Initializes ex_ with std::move(other.ex_) and owns_ with other.owns_, and sets other.owns_ to false.

~executor_work_guard();

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

executor_type get_executor() const noexcept;

Returns: ex_.

bool owns_work() const noexcept;

Returns: owns_.

void reset() noexcept;

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

Postconditions: owns_ == false.

10.13.17. Function make_work_guard

[async.make.work.guard]

template<class Executor>
  executor_work_guard<Executor>
    make_work_guard(const Executor& ex);

Returns: executor_work_guard<Executor>(ex).

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

template<class ExecutionContext>
  executor_work_guard<typename ExecutionContext::executor_type>
    make_work_guard(ExecutionContext& ctx);

Returns: make_work_guard(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_guard<associated_executor_t<T>>
    make_work_guard(const T& t);

Returns: make_work_guard(get_associated_executor(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 U>
  auto make_work_guard(const T& t, U&& u)
    -> decltype(make_work_guard(get_associated_executor(t, forward<U>(u))));

Returns: make_work_guard(get_associated_executor(t, forward<U>(u))).

10.13.18. Class system_executor

[async.system.exec]

Class system_executor represents a set of rules where function objects are permitted to execute on any thread.

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

  class system_executor
  {
  public:
    // constructors:

    system_executor() {}

    // executor operations:

    system_context& context() noexcept;

    void on_work_started() noexcept {}
    void on_work_finished() noexcept {}

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

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

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

Class system_executor satisfies the Destructible (C++Std [destructible]), DefaultConstructible (C++Std [defaultconstructible]), and Executor type requirements.

To satisfy the Executor requirements for the post and defer member functions, the system executor may create thread objects to run the submitted function objects. These thread objects are collectively referred to as system threads.

10.13.18.1. system_executor operations

[async.system.exec.ops]

system_context& context() noexcept;

Returns: A reference to an object with static storage duration. All calls to this function return references to the same object.

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

Effects: Equivalent to DECAY_COPY(forward<Func>(f))() (C++Std [thread.decaycopy]).

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

Effects: If context().stopped() == false, creates an object f1 initialized with DECAY_COPY(forward<Func>(f)), and calls f1 as if in a thread of execution represented by a thread object. Any exception propagated from the execution of DECAY_COPY(forward<Func>(f))() results in a call to std::terminate.

10.13.18.2. system_executor comparisons

[async.system.exec.comparisons]

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

Returns: true.

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

Returns: false.

10.13.19. Class system_context

[async.system.context]

Class system_context implements the execution context associated with system_executor objects.

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

  class system_context : public execution_context
  {
  public:
    // types:

    typedef system_executor executor_type;

    // construct / copy / destroy:

    system_context() = delete;
    system_context(const system_context&) = delete;
    system_context& operator=(const system_context&) = delete;
    ~system_context();

    // system_context operations:

    executor_type get_executor() noexcept;

    void stop();
    bool stopped() const noexcept;
    void join();
  };

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

The class system_context satisfies the ExecutionContext type requirements.

The system_context member functions get_executor, stop, and stopped, and the system_executor 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.

~system_context();

Effects: Performs stop() followed by join().

executor_type get_executor() noexcept;

Returns: system_executor().

void stop();

Effects: Signals all system threads to exit as soon as possible. If a system thread is currently executing a function object, the thread will exit only after completion of that function object. Returns without waiting for the system threads to complete.

Postconditions: stopped() == true.

bool stopped() const noexcept;

Returns: true if the system_context has been stopped by a prior call to stop.

void join();

Effects: Blocks the calling thread (C++Std [defns.block]) until all system threads have completed.

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

10.13.20. Class bad_executor

[async.bad.exec]

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

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

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

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

bad_executor() noexcept;

Effects: constructs a bad_executor object.

Postconditions: what() returns an implementation-defined NTBS.

10.13.21. Class executor

[async.executor]

The executor class provides a polymorphic wrapper for types that satisfy the Executor requirements.

namespace std {
namespace experimental {
namespace net {
inline namespace 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 ProtoAllocator>
      executor(allocator_arg_t, const ProtoAllocator& 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 ProtoAllocator>
      void assign(Executor e, const ProtoAllocator& a);

    // executor operations:

    execution_context& context() noexcept;

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

    template<class Func, class ProtoAllocator>
      void dispatch(Func&& f, const ProtoAllocator& a);
    template<class Func, class ProtoAllocator>
      void post(Func&& f, const ProtoAllocator& a);
    template<class Func, class ProtoAllocator>
      void defer(Func&& f, const ProtoAllocator& 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 v1
} // namespace net
} // namespace experimental

  template<class Allocator>
    struct uses_allocator<experimental::net::v1::executor, Allocator>
      : true_type {};

} // namespace std

Class executor meets the requirements of Executor, DefaultConstructible (C++Std [defaultconstructible]), and CopyAssignable (C++Std [copyassignable]).

[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]

The target is the executor object that is held by the wrapper.

10.13.21.1. executor constructors

[async.executor.cons]

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

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

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

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.

10.13.21.2. executor assignment

[async.executor.assign]

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.

10.13.21.3. executor destructor

[async.executor.dtor]

~executor();

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

10.13.21.4. executor modifiers

[async.executor.modifiers]

void swap(executor& other) noexcept;

Effects: Interchanges the targets of *this and other.

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

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

10.13.21.5. executor operations

[async.executor.ops]

execution_context& context() noexcept;

Requires: *this != nullptr.

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

void on_work_started() noexcept;

Requires: *this != nullptr.

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

void on_work_finished() noexcept;

Requires: *this != nullptr.

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

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

Let e be the target object of *this. Let a1 be the allocator that was specified when the target was set. Let fd be the result of DECAY_COPY(f) (C++Std [thread.decaycopy]).

Effects: e.dispatch(g, a1), where g is a function object of unspecified type that, when called as g(), performs fd(). The allocator a is used to allocate any memory required to implement g.

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

Let e be the target object of *this. Let a1 be the allocator that was specified when the target was set. Let fd be the result of DECAY_COPY(f).

Effects: e.post(g, a1), where g is a function object of unspecified type that, when called as g(), performs fd(). The allocator a is used to allocate any memory required to implement g.

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

Let e be the target object of *this. Let a1 be the allocator that was specified when the target was set. Let fd be the result of DECAY_COPY(f).

Effects: e.defer(g, a1), where g is a function object of unspecified type that, when called as g(), performs fd(). The allocator a is used to allocate any memory required to implement g.

10.13.21.6. executor capacity

[async.executor.capacity]

explicit operator bool() const noexcept;

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

10.13.21.7. executor target access

[async.executor.target]

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;

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

10.13.21.8. executor comparisons

[async.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 of a and f is the target 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.

10.13.21.9. executor specialized algorithms

[async.executor.algo]

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

Effects: a.swap(b).

10.13.22. Function dispatch

[async.dispatch]

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

Effects:
— Constructs an object completion of type async_completion<CompletionToken, void()>, initialized with forward<CompletionToken>(token).
— Performs ex.dispatch(std::move(completion.completion_handler), alloc), where ex is the result of get_associated_executor(completion.completion_handler), and alloc is the result of get_associated_allocator(completion.completion_handler).

Returns: completion.result.get().

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

Effects:
— Constructs an object completion of type async_completion<CompletionToken, void()>, initialized with forward<CompletionToken>(token).
— Constructs a function object f containing as members:
    • a copy of the completion handler h, initialized with std::move(completion.completion_handler),
    • an executor_work_guard object w for the completion handler's associated executor, initialized with make_work_guard(h),
    and where the effect of f() is:
    • w.get_executor().dispatch(std::move(h), alloc), where alloc is the result of get_associated_allocator(h), followed by
    • w.reset().
— Performs ex.dispatch(std::move(f), alloc), where alloc is the result of get_associated_allocator(completion.completion_handler) prior to the construction of f.

Returns: completion.result.get().

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

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

Returns: std::experimental::net::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.

10.13.23. Function post

[async.post]

[Note: The function post satisfies the requirements for an asynchronous operation. —end note]

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

Effects:
— Constructs an object completion of type async_completion<CompletionToken, void()>, initialized with forward<CompletionToken>(token).
— Performs ex.post(std::move(completion.completion_handler), alloc), where ex is the result of get_associated_executor(completion.completion_handler), and alloc is the result of get_associated_allocator(completion.completion_handler).

Returns: completion.result.get().

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

Effects:
— Constructs an object completion of type async_completion<CompletionToken, void()>, initialized with forward<CompletionToken>(token).
— Constructs a function object f containing as members:
    • a copy of the completion handler h, initialized with std::move(completion.completion_handler),
    • an executor_work_guard object w for the completion handler's associated executor, initialized with make_work_guard(h),
    and where the effect of f() is:
    • w.get_executor().dispatch(std::move(h), alloc), where alloc is the result of get_associated_allocator(h), followed by
    • w.reset().
— Performs ex.post(std::move(f), alloc), where alloc is the result of get_associated_allocator(completion.completion_handler) prior to the construction of f.

Returns: completion.result.get().

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

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

Returns: std::experimental::net::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.

10.13.24. Function defer

[async.defer]

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

Effects:
— Constructs an object completion of type async_completion<CompletionToken, void()>, initialized with forward<CompletionToken>(token).
— Performs ex.defer(std::move(completion.completion_handler), alloc), where ex is the result of get_associated_executor(completion.completion_handler), and alloc is the result of get_associated_allocator(completion.completion_handler).

Returns: completion.result.get().

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

Effects:
— Constructs an object completion of type async_completion<CompletionToken, void()>, initialized with forward<CompletionToken>(token).
— Constructs a function object f containing as members:
    • a copy of the completion handler h, initialized with std::move(completion.completion_handler),
    • an executor_work_guard object w for the completion handler's associated executor, initialized with make_work_guard(h),
    and where the effect of f() is:
    • w.get_executor().dispatch(std::move(h), alloc), where alloc is the result of get_associated_allocator(h), followed by
    • w.reset().
— Performs ex.defer(std::move(f), alloc), where alloc is the result of get_associated_allocator(completion.completion_handler) prior to the construction of f.

Returns: completion.result.get().

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

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

Returns: std::experimental::net::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.

10.13.25. Class template strand

[async.strand]

The class template strand is a wrapper around an object of type Executor satisfying the Executor requirements.

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

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

    typedef Executor inner_executor_type;

    // 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();

    // 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 ProtoAllocator>
      void dispatch(Func&& f, const ProtoAllocator& a);
    template<class Func, class ProtoAllocator>
      void post(Func&& f, const ProtoAllocator& a);
    template<class Func, class ProtoAllocator>
      void defer(Func&& f, const ProtoAllocator& 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 v1
} // namespace net
} // namespace experimental
} // namespace std

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

10.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_ 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_ 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_ 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) noexcept;

Requires: OtherExecutor is convertible to Executor.

Effects: Initializes inner_ex_ with 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_ with inner_ex_(std::move(other.inner_ex_)).

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

10.13.25.2. strand assignment

[async.strand.assign]

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

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

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

Returns: *this.

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

Requires: Executor is MoveAssignable (C++Std [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++Std [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++Std [moveassignable]).

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

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

Returns: *this.

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

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

Effects: If running_in_this_thread() == true, calls DECAY_COPY(forward<Func>(f))() (C++Std [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);

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

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.

10.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).

10.13.26. Class template use_future_t

[async.use.future]

The class template use_future_t defines a set of types that, when passed as a completion token to an asynchronous operation's initiating function, cause the result of the asynchronous operation to be delivered via a future (C++Std [futures.unique_future]).

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

  template<class ProtoAllocator = allocator<void>>
  class use_future_t
  {
  public:
    // use_future_t types:
    typedef ProtoAllocator allocator_type;

    // use_future_t members:
    constexpr use_future_t() noexcept;
    explicit use_future_t(const allocator_type& a) noexcept;
    template<class OtherProtoAllocator> use_future_t<OtherProtoAllocator>
      rebind(const OtherProtoAllocator& a) const noexcept;
    allocator_type get_allocator() const noexcept;
    template <class F> unspecified operator()(F&& f) const;
  };

} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std
10.13.26.1. use_future_t constructors

[async.use.future.cons]

constexpr use_future_t() noexcept;

Effects: Constructs a use_future_t with a default-constructed allocator.

explicit use_future_t(const allocator_type& a) noexcept;

Postconditions: get_allocator() == a.

10.13.26.2. use_future_t members

[async.use.future.members]

template<class OtherProtoAllocator> use_future_t<OtherProtoAllocator>
  rebind(const OtherProtoAllocator& a) const noexcept;

Returns: A use_future_t object where get_allocator() == a.

allocator_type get_allocator() const noexcept;

Returns: The associated allocator object.

template <class F> unspecified operator()(F&& f) const;

Let T be a completion token type. Let H be a completion handler type and let h be an object of type H. Let FD be the type decay_t<F> and let fd be an lvalue of type FD constructed with forward<F>(f). Let Args... by the completion signature of H and let N be sizeof...(Args). Let i be in the range [0,N) and let Ai be the ith type in Args. Let ai be the ith argument associated with Ai.

Returns: A completion token t of type T.

Remarks: The return type T satisfies the Destructible (C++Std [destructible]) and MoveConstructible (C++Std [moveconstructible]) requirements.

The object h of type H is an asynchronous provider with an associated shared state (C++Std [futures.state]). The effect of h(a0, ..., aN-1) is to atomically store the result of INVOKE(fd, forward<A0>(a0), ..., forward<AN-1>(aN-1)) (C++Std [func.require]) in the shared state and make the shared state ready. If fd exits via an exception then that exception is atomically stored in the shared state and the shared state is made ready.

The implementation provides a partial specialization template <class Result, class... Args> async_result<T, Result(Args...)> such that:
— the nested typedef completion_handler_type is a type H;
— the nested typedef return_type is future<result_of_t<FD(decay_t<Args>...)>>; and
— when an object r1 of type async_result<T, Result(Args...)> is constructed from h, the expression r1.get() returns a future with the same shared state as h.

For any executor type E, the associated object for the associator associated_executor<H, E> is an executor where, for function objects executed using the executor's dispatch(), post() or defer() functions, any exception thrown is caught by a function object and stored in the associated shared state.

10.13.26.3. Partial class template specialization async_result for use_future_t

[async.use.future.result]

template<class ProtoAllocator, class Result, class... Args>
class async_result<use_future_t<ProtoAllocator>, Result(Args...)>
{
  typedef see below completion_handler_type;
  typedef see below return_type;

  explicit async_result(completion_handler_type& h);
  async_result(const async_result&) = delete;
  async_result& operator=(const async_result&) = delete;

  return_type get();
};

Let R be the type async_result<use_future_t<ProtoAllocator>, Result(Args...)>. Let F be the nested function object type R::completion_handler_type.

An object t1 of type F is an asynchronous provider with an associated shared state (C++Std [futures.state]). The type F provides F::operator() such that the expression t1(declval<Args>()...) is well formed.

The implementation specializes associated_executor for F. For function objects executed using the associated executor's dispatch(), post() or defer() functions, any exception thrown is caught by the executor and stored in the associated shared state.

For any executor type E, the associated object for the associator associated_executor<F, E> is an executor where, for function objects executed using the executor's dispatch(), post() or defer() functions, any exception thrown by a function object is caught by the executor and stored in the associated shared state.

When an object r1 of type R is constructed from t1, the expression r1.get() returns a future with the same shared state as t1.

The type of R::return_type and the effects of F::operator() are defined in the table below. After establishing these effects, F::operator() makes the shared state ready. In this table, N is the value of sizeof...(Args); let i be in the range [0,N) and let Ti be the ith type in Args; let Ui be decay_t<Ti> for each type Ti in Args; let Ai be the deduced type of the ith argument to F::operator(); and let ai be the ith argument to F::operator().

Table 9. async_result<use_future_t<ProtoAllocator>, Result(Args...)> semantics

N

U0

R::return_type

F::operator() effects

0

future<void>

None.

1

error_code

future<void>

If a0 evaluates to true, atomically stores the exception pointer produced by make_exception_ptr(system_error(a0)) in the shared state.

1

exception_ptr

future<void>

If a0 is non-null, atomically stores the exception pointer a0 in the shared state.

1

all other types

future<U0>

Atomically stores forward<A0>(a0) in the shared state.

2

error_code

future<U1>

If a0 evaluates to true, atomically stores the exception pointer produced by make_exception_ptr(system_error(a0)) in the shared state; otherwise, atomically stores forward<A1>(a1) in the shared state.

2

exception_ptr

future<U1>

If a0 is non-null, atomically stores the exception pointer in the shared state; otherwise, atomically stores forward<A1>(a1) in the shared state.

2

all other types

future<tuple<U0,U1>>

Atomically stores forward_as_tuple<A0,A1>(a0,a1) in the shared state.

>2

error_code

future<tuple<U1,...,UN-1>>

If a0 evaluates to true, atomically stores the exception pointer produced by make_exception_ptr(system_error(a0)) in the shared state; otherwise, atomically stores forward_as_tuple<A1,...,AN-1)>(a1,...,aN-1) in the shared state.

>2

exception_ptr

future<tuple<U1,...,UN-1>>

If a0 is non-null, atomically stores the exception pointer in the shared state; otherwise, atomically stores forward_as_tuple<A1,...,AN-1>(a1,...,aN-1) in the shared state.

>2

all other types

future<tuple<U0,...,UN-1>>

Atomically stores forward_as_tuple<A0,...,AN-1>(a0,...,aN-1) in the shared state.


10.13.27. Partial class template specialization async_result for packaged_task

[async.packaged.task.specializations]

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

  template<class Result, class... Args, class Signature>
  class async_result<packaged_task<Result(Args...)>, Signature>
  {
  public:
    typedef packaged_task<Result(Args...)> completion_handler_type;
    typedef future<Result> return_type;

    explicit async_result(completion_handler_type& h);
    async_result(const async_result&) = delete;
    async_result& operator=(const async_result&) = delete;

    return_type get();

  private:
    return_type future_; // exposition only
  };

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

explicit async_result(completion_handler_type& h);

Effects: Initializes future_ with h.get_future().

return_type get();

Returns: std::move(future_).

10.14. Basic I/O services

[io_context]

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

10.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;
    typedef implementation defined count_type;

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

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

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]

10.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();

Requires: Must not be called from a thread that is currently calling a run function.

Effects: Equivalent to:

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

Returns: n.

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

Requires: Must not be called from a thread that is currently calling a run function.

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.

Notes: 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.

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++Std [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.

Notes: 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.

Notes: 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.

10.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:
    // 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 operations:

    bool running_in_this_thread() const noexcept;

    io_context& context() noexcept;

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

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

  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;

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

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

io_context::executor_type is a type satisfying Executor requirements. 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.]

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

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

10.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() noexcept;

Returns: A reference to the associated io_context object.

void on_work_started() noexcept;

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

void on_work_finished() noexcept;

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

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

Effects: If running_in_this_thread() is true, calls DECAY_COPY(forward<Func>(f))() (C++Std [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);

Effects: Adds f to the io_context.

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

Effects: Adds f to the io_context.

10.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).

10.15. Timers

[timer] This clause defines components for performing timer operations.

[Example: Performing a synchronous wait operation on a timer:

io_context c;
steady_timer t(c);
t.expires_after(seconds(5));
t.wait();

end example]

[Example: Performing an asynchronous wait operation on a timer:

void handler(error_code ec) { ... }
...
io_context c;
steady_timer t(c);
t.expires_after(seconds(5));
t.async_wait(handler);
i.run();

end example]

10.15.1. Header <experimental/timer> synopsis

[timer.synop]

#include <chrono>

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

  template<class Clock> struct wait_traits;

  template<class Clock, class WaitTraits = wait_traits<Clock>>
    class basic_waitable_timer;

  typedef basic_waitable_timer<chrono::system_clock> system_timer;
  typedef basic_waitable_timer<chrono::steady_clock> steady_timer;
  typedef basic_waitable_timer<chrono::high_resolution_clock> high_resolution_timer;

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

10.15.2. Requirements

[timer.reqmts]

10.15.2.1. Wait traits requirements

[timer.reqmts.waittraits]

The basic_waitable_timer template uses wait traits to allow programs to customize wait and async_wait behavior. [Note: Possible uses of wait traits include:
— To enable timers based on non-realtime clocks.
— Determining how quickly wallclock-based timers respond to system time changes.
— Correcting for errors or rounding timeouts to boundaries.
— Preventing duration overflow. That is, a program may set a timer's expiry e to be Clock::max() (meaning never reached) or Clock::min() (meaning always in the past). As a result, computing the duration until timer expiry as e - Clock::now() may cause overflow. —end note]

For a type Clock meeting the Clock requirements (C++Std [time.clock.req]), a type X meets the WaitTraits requirements if it satisfies the requirements listed below.

In the table below, t denotes a (possibly const) value of type Clock::time_point; and d denotes a (possibly const) value of type Clock::duration.

Table 10. WaitTraits requirements

expression

return type

assertion/note
pre/post-condition

X::to_wait_duration(d)

Clock::duration

Returns a Clock::duration value to be used in a wait or async_wait operation. [Note: The return value is typically representative of the duration d. —end note]

X::to_wait_duration(t)

Clock::duration

Returns a Clock::duration value to be used in a wait or async_wait operation. [Note: The return value is typically representative of the duration from Clock::now() until the time point t. —end note]


10.15.3. Class template wait_traits

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

  template<class Clock>
  struct wait_traits
  {
    static typename Clock::duration to_wait_duration(
      const typename Clock::duration& d);

    static typename Clock::duration to_wait_duration(
      const typename Clock::time_point& t);
  };

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

Class template wait_traits satisfies the WaitTraits type requirements. Template argument Clock is a type meeting the Clock requirements (C++Std [time.clock.req]).

static typename Clock::duration to_wait_duration(
  const typename Clock::duration& d);

Returns: d.

static typename Clock::duration to_wait_duration(
  const typename Clock::time_point& t);

Returns: Let now be Clock::now(). If now + Clock::duration::max() is before t, Clock::duration::max(); if now + Clock::duration::min() is after t, Clock::duration::min(); otherwise, t - now.

10.15.4. Class template basic_waitable_timer

[timer.waitable]

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

  template<class Clock, class WaitTraits = wait_traits<Clock>>
  class basic_waitable_timer
  {
  public:
    // types:

    typedef io_context::executor_type executor_type;
    typedef Clock clock_type;
    typedef typename clock_type::duration duration;
    typedef typename clock_type::time_point time_point;
    typedef WaitTraits traits_type;

    // construct / copy / destroy:

    explicit basic_waitable_timer(io_context& ctx);
    basic_waitable_timer(io_context& ctx, const time_point& t);
    basic_waitable_timer(io_context& ctx, const duration& d);
    basic_waitable_timer(const basic_waitable_timer&) = delete;
    basic_waitable_timer(basic_waitable_timer&& rhs);

    ~basic_waitable_timer();

    basic_waitable_timer& operator=(const basic_waitable_timer&) = delete;
    basic_waitable_timer& operator=(basic_waitable_timer&& rhs);

    // basic_waitable_timer operations:

    executor_type get_executor() noexcept;

    size_t cancel();
    size_t cancel_one();

    time_point expiry() const;
    size_t expires_at(const time_point& t);
    size_t expires_after(const duration& d);

    void wait();
    void wait(error_code& ec);

    template<class CompletionToken>
      DEDUCED async_wait(CompletionToken&& token);
  };

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

Instances of class template basic_waitable_timer meet the requirements of Destructible (C++Std [destructible]), MoveConstructible (C++Std [moveconstructible]), and MoveAssignable (C++Std [moveassignable]).

10.15.4.1. basic_waitable_timer constructors

[timer.waitable.cons]

explicit basic_waitable_timer(io_context& ctx);

Effects: Equivalent to basic_waitable_timer(ctx, time_point()).

basic_waitable_timer(io_context& ctx, const time_point& t);

Postconditions:
get_executor() == ctx.get_executor().
expiry() == t.

basic_waitable_timer(io_context& ctx, const duration& d);

Effects: Sets the expiry time as if by calling expires_after(d).

Postconditions: get_executor() == ctx.get_executor().

basic_waitable_timer(basic_waitable_timer&& rhs);

Effects: Move constructs an object of class basic_waitable_timer<Clock, WaitTraits> that refers to the state originally represented by rhs.

Postconditions:
get_executor() is equal to the prior value of rhs.get_executor().
expiry() returns the same value as rhs.expiry() prior to the constructor invocation.
rhs.expiry() == time_point().

10.15.4.2. basic_waitable_timer destructor

[timer.waitable.dtor]

~basic_waitable_timer();

Effects: Destroys the timer, cancelling any asynchronous wait operations associated with the timer as if by calling cancel().

10.15.4.3. basic_waitable_timer assignment

[timer.waitable.assign]

basic_waitable_timer& operator=(basic_waitable_timer&& rhs);

Effects: Cancels any outstanding asynchronous operations associated with *this as if by calling cancel(), then moves into *this the state originally represented by rhs.

Postconditions:
get_executor() is equal to the prior value of rhs.get_executor().
expiry() returns the same value as rhs.expiry() prior to the assignment.
rhs.expiry() == time_point().

Returns: *this.

10.15.4.4. basic_waitable_timer operations

[timer.waitable.ops]

executor_type get_executor() noexcept;

Returns: The associated executor.

size_t cancel();

Effects: Causes any outstanding asynchronous wait operations to complete. Completion handlers for canceled operations are passed an error code ec such that ec == errc::operation_canceled yields true.

Returns: The number of operations that were canceled.

size_t cancel_one();

Effects: Causes the outstanding asynchronous wait operation that was initiated first, if any, to complete as soon as possible. The completion handler for the canceled operation is passed an error code ec such that ec == errc::operation_canceled yields true.

Returns: 1 if an operation was cancelled, otherwise 0.

time_point expiry() const;

Returns: The expiry time associated with the timer, as previously set using expires_at() or expires_after().

size_t expires_at(const time_point& t);

Effects: Cancels outstanding asynchronous wait operations, as if by calling cancel(). Sets the expiry time associated with the timer.

Returns: The number of operations that were canceled.

Postconditions: expiry() == t.

size_t expires_after(const duration& d);

Returns: expires_at(clock_type::now() + d).

void wait();
void wait(error_code& ec);

Effects: Establishes the postcondition as if by repeatedly blocking the calling thread (C++Std [defns.block]) for the relative time produced by WaitTraits::to_wait_duration(expiry()).

Postconditions: ec || expiry() <= clock_type::now().

template<class CompletionToken>
  DEDUCED async_wait(CompletionToken&& token);

Completion signature: void(error_code ec).

Effects: Initiates an asynchronous wait operation to repeatedly wait for the relative time produced by WaitTraits::to_wait_duration(e), where e is a value of type time_point such that e <= expiry(). The completion handler is submitted for execution only when the condition ec || expiry() <= clock_type::now() yields true.

[Note: To implement async_wait, an io_context object ctx may maintain a priority queue for each specialization of basic_waitable_timer<Clock, WaitTraits> for which a timer object was initialized with ctx. Only the time point e of the earliest outstanding expiry need be passed to WaitTraits::to_wait_duration(e). —end note]

10.16. Buffers

[buffer]

10.16.1. Header <experimental/buffer> synopsis

[buffer.synop]

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

  enum class stream_errc {
    eof = implementation defined,
    not_found = implementation defined
  };

  const error_category& stream_category() noexcept;

  error_code make_error_code(stream_errc e) noexcept;
  error_condition make_error_condition(stream_errc e) noexcept;

  class mutable_buffer;
  class const_buffer;

  // buffer type traits:

  template<class T> is_mutable_buffer_sequence;
  template<class T> is_const_buffer_sequence;
  template<class T> is_dynamic_buffer;

  // buffer sequence access:

  const mutable_buffer* buffer_sequence_begin(const mutable_buffer& b);
  const const_buffer* buffer_sequence_begin(const const_buffer& b);
  const mutable_buffer* buffer_sequence_end(const mutable_buffer& b);
  const const_buffer* buffer_sequence_end(const const_buffer& b);
  template <class C> auto buffer_sequence_begin(C& c) -> decltype(c.begin());
  template <class C> auto buffer_sequence_begin(const C& c) -> decltype(c.begin());
  template <class C> auto buffer_sequence_end(C& c) -> decltype(c.end());
  template <class C> auto buffer_sequence_end(const C& c) -> decltype(c.end());

  // buffer size:

  template<class ConstBufferSequence>
    size_t buffer_size(const ConstBufferSequence& buffers) noexcept;

  // buffer copy:

  template<class MutableBufferSequence, class ConstBufferSequence>
    size_t buffer_copy(const MutableBufferSequence& dest,
                       const ConstBufferSequence& source) noexcept;
  template<class MutableBufferSequence, class ConstBufferSequence>
    size_t buffer_copy(const MutableBufferSequence& dest,
                       const ConstBufferSequence& source,
                       max_size) noexcept;

  // buffer arithmetic:

  mutable_buffer operator+(const mutable_buffer& b, size_t n) noexcept;
  mutable_buffer operator+(size_t n, const mutable_buffer& b) noexcept;
  const_buffer operator+(const const_buffer&, size_t n) noexcept;
  const_buffer operator+(size_t, const const_buffer&) noexcept;

  // buffer creation:

  mutable_buffer buffer(void* p, size_t n) noexcept;
  const_buffer buffer(const void* p, size_t n) noexcept;

  mutable_buffer buffer(const mutable_buffer& b) noexcept;
  mutable_buffer buffer(const mutable_buffer& b, size_t n) noexcept;
  const_buffer buffer(const const_buffer& b) noexcept;
  const_buffer buffer(const const_buffer& b, size_t n) noexcept;

  template<class T, size_t N>
    mutable_buffer buffer(T (&data)[N]) noexcept;
  template<class T, size_t N>
    const_buffer buffer(const T (&data)[N]) noexcept;
  template<class T, size_t N>
    mutable_buffer buffer(array<T, N>& data) noexcept;
  template<class T, size_t N>
    const_buffer buffer(array<const T, N>& data) noexcept;
  template<class T, size_t N>
    const_buffer buffer(const array<T, N>& data) noexcept;
  template<class T, class Allocator>
    mutable_buffer buffer(vector<T, Allocator>& data) noexcept;
  template<class T, class Allocator>
    const_buffer buffer(const vector<T, Allocator>& data) noexcept;
  template<class CharT, class Traits, class Allocator>
    mutable_buffer buffer(basic_string<CharT, Traits, Allocator>& data) noexcept;
  template<class CharT, class Traits>
    const_buffer buffer(basic_string_view<CharT, Traits> data) noexcept;

  template<class T, size_t N>
    mutable_buffer buffer(T (&data)[N], size_t n) noexcept;
  template<class T, size_t N>
    const_buffer buffer(const T (&data)[N], size_t n) noexcept;
  template<class T, size_t N>
    mutable_buffer buffer(array<T, N>& data, size_t n) noexcept;
  template<class T, size_t N>
    const_buffer buffer(array<const T, N>& data, size_t n) noexcept;
  template<class T, size_t N>
    const_buffer buffer(const array<T, N>& data, size_t n) noexcept;
  template<class T, class Allocator>
    mutable_buffer buffer(vector<T, Allocator>& data, size_t n) noexcept;
  template<class T, class Allocator>
    const_buffer buffer(const vector<T, Allocator>& data, size_t n) noexcept;
  template<class CharT, class Traits, class Allocator>
    mutable_buffer buffer(basic_string<CharT, Traits, Allocator>& data,
                          size_t n) noexcept;
  template<class CharT, class Traits>
    const_buffer buffer(basic_string_view<CharT, Traits> data,
                        size_t n) noexcept;

  template<class T, Allocator>
    class dynamic_vector_buffer;

  template<class CharT, class Traits, Allocator>
    class dynamic_string_buffer;

  // dynamic buffer creation:

  template<class T, class Allocator>
    dynamic_vector_buffer<T, Allocator>
      dynamic_buffer(vector<T, Allocator>& vec) noexcept;
  template<class T, class Allocator>
    dynamic_vector_buffer<T, Allocator>
      dynamic_buffer(vector<T, Allocator>& vec, size_t n) noexcept;

  template<class CharT, class Traits, class Allocator>
    dynamic_string_buffer<CharT, Traits, Allocator>
      dynamic_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept;
  template<class CharT, class Traits, class Allocator>
    dynamic_string_buffer<CharT, Traits, Allocator>
      dynamic_buffer(basic_string<CharT, Traits, Allocator>& str, size_t n) noexcept;

  class transfer_all;
  class transfer_at_least;
  class transfer_exactly;

  // synchronous read operations:

  template<class SyncReadStream, class MutableBufferSequence>
    size_t read(SyncReadStream& stream,
                const MutableBufferSequence& buffers);
  template<class SyncReadStream, class MutableBufferSequence>
    size_t read(SyncReadStream& stream,
                const MutableBufferSequence& buffers, error_code& ec);
  template<class SyncReadStream, class MutableBufferSequence,
    class CompletionCondition>
      size_t read(SyncReadStream& stream,
                  const MutableBufferSequence& buffers,
                  CompletionCondition completion_condition);
  template<class SyncReadStream, class MutableBufferSequence,
    class CompletionCondition>
      size_t read(SyncReadStream& stream,
                  const MutableBufferSequence& buffers,
                  CompletionCondition completion_condition,
                  error_code& ec);

  template<class SyncReadStream, class DynamicBuffer>
    size_t read(SyncReadStream& stream, DynamicBuffer&& b);
  template<class SyncReadStream, class DynamicBuffer>
    size_t read(SyncReadStream& stream, DynamicBuffer&& b, error_code& ec);
  template<class SyncReadStream, class DynamicBuffer, class CompletionCondition>
    size_t read(SyncReadStream& stream, DynamicBuffer&& b,
                CompletionCondition completion_condition);
  template<class SyncReadStream, class DynamicBuffer, class CompletionCondition>
    size_t read(SyncReadStream& stream, DynamicBuffer&& b,
                CompletionCondition completion_condition, error_code& ec);

  // asynchronous read operations:

  template<class AsyncReadStream, class MutableBufferSequence,
    class CompletionToken>
      DEDUCED async_read(AsyncReadStream& stream,
                      const MutableBufferSequence& buffers,
                      CompletionToken&& token);
  template<class AsyncReadStream, class MutableBufferSequence,
    class CompletionCondition, class CompletionToken>
      DEDUCED async_read(AsyncReadStream& stream,
                      const MutableBufferSequence& buffers,
                      CompletionCondition completion_condition,
                      CompletionToken&& token);

  template<class AsyncReadStream, class DynamicBuffer, class CompletionToken>
    DEDUCED async_read(AsyncReadStream& stream,
                    DynamicBuffer&& b, CompletionToken&& token);
  template<class AsyncReadStream, class DynamicBuffer,
    class CompletionCondition, class CompletionToken>
      DEDUCED async_read(AsyncReadStream& stream,
                      DynamicBuffer&& b,
                      CompletionCondition completion_condition,
                      CompletionToken&& token);

  // synchronous write operations:

  template<class SyncWriteStream, class ConstBufferSequence>
    size_t write(SyncWriteStream& stream,
                 const ConstBufferSequence& buffers);
  template<class SyncWriteStream, class ConstBufferSequence>
    size_t write(SyncWriteStream& stream,
                 const ConstBufferSequence& buffers, error_code& ec);
  template<class SyncWriteStream, class ConstBufferSequence,
    class CompletionCondition>
      size_t write(SyncWriteStream& stream,
                   const ConstBufferSequence& buffers,
                   CompletionCondition completion_condition);
  template<class SyncWriteStream, class ConstBufferSequence,
    class CompletionCondition>
      size_t write(SyncWriteStream& stream,
                   const ConstBufferSequence& buffers,
                   CompletionCondition completion_condition,
                   error_code& ec);

  template<class SyncWriteStream, class DynamicBuffer>
    size_t write(SyncWriteStream& stream, DynamicBuffer&& b);
  template<class SyncWriteStream, class DynamicBuffer>
    size_t write(SyncWriteStream& stream, DynamicBuffer&& b, error_code& ec);
  template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition>
    size_t write(SyncWriteStream& stream, DynamicBuffer&& b,
                 CompletionCondition completion_condition);
  template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition>
    size_t write(SyncWriteStream& stream, DynamicBuffer&& b,
                 CompletionCondition completion_condition, error_code& ec);

  // asynchronous write operations:

  template<class AsyncWriteStream, class ConstBufferSequence,
    class CompletionToken>
      DEDUCED async_write(AsyncWriteStream& stream,
                       const ConstBufferSequence& buffers,
                       CompletionToken&& token);
  template<class AsyncWriteStream, class ConstBufferSequence,
    class CompletionCondition, class CompletionToken>
      DEDUCED async_write(AsyncWriteStream& stream,
                       const ConstBufferSequence& buffers,
                       CompletionCondition completion_condition,
                       CompletionToken&& token);

  template<class AsyncWriteStream, class DynamicBuffer, class CompletionToken>
    DEDUCED async_write(AsyncWriteStream& stream,
                     DynamicBuffer&& b, CompletionToken&& token);
  template<class AsyncWriteStream, class DynamicBuffer,
    class CompletionCondition, class CompletionToken>
      DEDUCED async_write(AsyncWriteStream& stream,
                       DynamicBuffer&& b,
                       CompletionCondition completion_condition,
                       CompletionToken&& token);

  // synchronous delimited read operations:

  template<class SyncReadStream, class DynamicBuffer>
    size_t read_until(SyncReadStream& s, DynamicBuffer&& b, char delim);
  template<class SyncReadStream, class DynamicBuffer>
    size_t read_until(SyncReadStream& s, DynamicBuffer&& b,
                      char delim, error_code& ec);
  template<class SyncReadStream, class DynamicBuffer>
    size_t read_until(SyncReadStream& s, DynamicBuffer&& b, string_view delim);
  template<class SyncReadStream, class DynamicBuffer>
    size_t read_until(SyncReadStream& s, DynamicBuffer&& b,
                      string_view delim, error_code& ec);

  // asynchronous delimited read operations:

  template<class AsyncReadStream, class DynamicBuffer, class CompletionToken>
    DEDUCED async_read_until(AsyncReadStream& s,
                          DynamicBuffer&& b, char delim,
                          CompletionToken&& token);
  template<class AsyncReadStream, class DynamicBuffer, class CompletionToken>
    DEDUCED async_read_until(AsyncReadStream& s,
                          DynamicBuffer&& b, string_view delim,
                          CompletionToken&& token);

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

  template<> struct is_error_code_enum<
    experimental::net::v1::stream_errc>
      : public true_type {};

} // namespace std

10.16.2. Requirements

[buffer.reqmts]

10.16.2.1. Mutable buffer sequence requirements

[buffer.reqmts.mutablebuffersequence]

A mutable buffer sequence represents a set of memory regions that may be used to receive the output of an operation, such as the receive operation of a socket.

A type X meets the MutableBufferSequence requirements if it satisfies the requirements of Destructible (C++Std [destructible]) and CopyConstructible (C++Std [copyconstructible]), as well as the additional requirements listed below.

In the table below, x denotes a (possibly const) value of type X, and u denotes an identifier.

Table 11. MutableBufferSequence requirements

expression

return type

assertion/note
pre/post-condition

std::experimental::net::buffer_sequence_begin(x)
std::experimental::net::buffer_sequence_end(x)

An iterator type meeting the requirements for bidirectional iterators (C++Std [bidirectional.iterators]) whose value type is convertible to mutable_buffer.

X u(x);

post:

equal(
  std::experimental::net::buffer_sequence_begin(x),
  std::experimental::net::buffer_sequence_end(x),
  std::experimental::net::buffer_sequence_begin(u),
  std::experimental::net::buffer_sequence_end(u),
  [](const typename X::value_type& v1,
     const typename X::value_type& v2)
   {
     mutable_buffer b1(v1);
     mutable_buffer b2(v2);
     return b1.data() == b2.data()
         && b1.size() == b2.size();
   })


10.16.2.2. Constant buffer sequence requirements

[buffer.reqmts.constbuffersequence]

A constant buffer sequence represents a set of memory regions that may be used as input to an operation, such as the send operation of a socket.

A type X meets the ConstBufferSequence requirements if it satisfies the requirements of Destructible (C++Std [destructible]) and CopyConstructible (C++Std [copyconstructible]), as well as the additional requirements listed below.

In the table below, x denotes a (possibly const) value of type X, and u denotes an identifier.

Table 12. ConstBufferSequence requirements

expression

return type

assertion/note
pre/post-condition

std::experimental::net::buffer_sequence_begin(x)
std::experimental::net::buffer_sequence_end(x)

An iterator type meeting the requirements for bidirectional iterators (C++Std [bidirectional.iterators]) whose value type is convertible to const_buffer.

X u(x);

post:

equal(
  std::experimental::net::buffer_sequence_begin(x),
  std::experimental::net::buffer_sequence_end(x),
  std::experimental::net::buffer_sequence_begin(u),
  std::experimental::net::buffer_sequence_end(u),
  [](const typename X::value_type& v1,
     const typename X::value_type& v2)
   {
     const_buffer b1(v1);
     const_buffer b2(v2);
     return b1.data() == b2.data()
         && b1.size() == b2.size();
   })


10.16.2.3. Dynamic buffer requirements

[buffer.reqmts.dynamicbuffer]

A dynamic buffer encapsulates memory storage that may be automatically resized as required, where the memory is divided into two regions: readable bytes followed by writable bytes. These memory regions are internal to the dynamic buffer, but direct access to the elements is provided to permit them to be efficiently used with I/O operations. [Note: Such as the send or receive operations of a socket. The readable bytes would be used as the constant buffer sequence for send, and the writable bytes used as the mutable buffer sequence for receive. —end note] Data written to the writable bytes of a dynamic buffer object is appended to the readable bytes of the same object.

A type X meets the DynamicBuffer requirements if it satisfies the requirements of Destructible (C++Std [destructible]) and MoveConstructible (C++Std [moveconstructible]), as well as the additional requirements listed below.

In the table below, x denotes a value of type X, x1 denotes a (possibly const) value of type X, and n denotes a (possibly const) value of type size_t.

Table 13. DynamicBuffer requirements

expression

type

assertion/note
pre/post-conditions

X::const_buffers_type

type meeting ConstBufferSequence requirements.

This type represents the memory associated with the readable bytes.

X::mutable_buffers_type

type meeting MutableBufferSequence requirements.

This type represents the memory associated with the writable bytes.

x1.size()

size_t

Returns the number of readable bytes.

x1.max_size()

size_t

Returns the maximum number of bytes, both readable and writable, that can be held by x1.

x1.capacity()

size_t

Returns the maximum number of bytes, both readable and writeable, that can be held by x1 without requiring reallocation.

x1.data()

X::const_buffers_type

Returns a constant buffer sequence u that represents the readable bytes, and where buffer_size(u) == size().

x.prepare(n)

X::mutable_buffers_type

Returns a mutable buffer sequence u representing the writable bytes, and where buffer_size(u) == n. The dynamic buffer reallocates memory as required. All constant or mutable buffer sequences previously obtained using data() or prepare() are invalidated.

Throws: length_error if size() + n exceeds max_size().

x.commit(n)

Appends n bytes from the start of the writable bytes to the end of the readable bytes. The remainder of the writable bytes are discarded. If n is greater than the number of writable bytes, all writable bytes are appended to the readable bytes. All constant or mutable buffer sequences previously obtained using data() or prepare() are invalidated.

x.consume(n)

Removes n bytes from beginning of the readable bytes. If n is greater than the number of readable bytes, all readable bytes are removed. All constant or mutable buffer sequences previously obtained using data() or prepare() are invalidated.


10.16.2.4. Requirements on read and write operations

[buffer.reqmts.read.write]

A read operation is an operation that reads data into a mutable buffer sequence argument of a type meeting MutableBufferSequence requirements. The mutable buffer sequence specifies memory where the data should be placed. A read operation shall always fill a buffer in the sequence completely before proceeding to the next.

A write operation is an operation that writes data from a constant buffer sequence argument of a type meeting ConstBufferSequence requirements. The constant buffer sequence specifies memory where the data to be written is located. A write operation shall always write a buffer in the sequence completely before proceeding to the next.

If a read or write operation is also an asynchronous operation, the operation shall maintain one or more copies of the buffer sequence until such time as the operation no longer requires access to the memory specified by the buffers in the sequence. The program shall ensure the memory remains valid until:

— the last copy of the buffer sequence is destroyed, or

— the completion handler for the asynchronous operation is invoked,

whichever comes first.

10.16.3. Class mutable_buffer

[buffer.mutable]

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

  class mutable_buffer
  {
  public:
    // constructors:
    mutable_buffer() noexcept;
    mutable_buffer(void* p, size_t n) noexcept;

    // members:
    void* data() const noexcept;
    size_t size() const noexcept;

  private:
    void* data_; // exposition only
    size_t size_; // exposition only
  };

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

The mutable_buffer class satisfies requirements of MutableBufferSequence, DefaultConstructible (C++Std [defaultconstructible]), and CopyAssignable (C++Std [copyassignable]).

mutable_buffer() noexcept;

Postconditions: data_ == nullptr and size_ == 0.

mutable_buffer(void* p, size_t n) noexcept;

Postconditions: data_ == p and size_ == n.

void* data() const noexcept;

Returns: data_.

size_t size() const noexcept;

Returns: size_.

10.16.4. Class const_buffer

[buffer.const]

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

  class const_buffer
  {
  public:
    // constructors:
    const_buffer() noexcept;
    const_buffer(const void* p, size_t n) noexcept;
    const_buffer(const mutable_buffer& b) noexcept;

    // members:
    const void* data() const noexcept;
    size_t size() const noexcept;

  private:
    const void* data_; // exposition only
    size_t size_; // exposition only
  };

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

The const_buffer class satisfies requirements of ConstBufferSequence, DefaultConstructible (C++Std [defaultconstructible]), and CopyAssignable (C++Std [copyassignable]).

const_buffer() noexcept;

Postconditions: data_ == nullptr and size_ == 0.

const_buffer(const void* p, size_t n) noexcept;

Postconditions: data_ == p and size_ == n.

const_buffer(const mutable_buffer& b);

Postconditions: data_ == b.data_ and size_ == b.size_.

const void* data() const noexcept;

Returns: data_.

size_t size() const noexcept;

Returns: size_.

10.16.5. Buffer type traits

[buffer.traits]

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

  template<class T> is_mutable_buffer_sequence;
  template<class T> is_const_buffer_sequence;
  template<class T> is_dynamic_buffer;

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

This sub-clause contains templates that may be used to query the properties of a type at compile time. Each of these templates is a UnaryTypeTrait (C++Std [meta.rqmts]) with a BaseCharacteristic of true_type if the corresponding condition is true, otherwise false_type.

Table 14. Buffer type traits

Template

Condition

Preconditions

template<class T> is_mutable_buffer_sequence

T meets the syntactic requirements for mutable buffer sequence.

T is a complete type.

template<class T> is_const_buffer_sequence

T meets the syntactic requirements for constant buffer sequence.

T is a complete type.

template<class T> is_dynamic_buffer

T meets the syntactic requirements for dynamic buffer.

T is a complete type.


10.16.6. Buffer sequence access

[buffer.seq.access]

const mutable_buffer* buffer_sequence_begin(const mutable_buffer& b);
const const_buffer* buffer_sequence_begin(const const_buffer& b);

Returns: std::addressof(b).

const mutable_buffer* buffer_sequence_end(const mutable_buffer& b);
const const_buffer* buffer_sequence_end(const const_buffer& b);

Returns: std::addressof(b) + 1.

template <class C> auto buffer_sequence_begin(C& c) -> decltype(c.begin());
template <class C> auto buffer_sequence_begin(const C& c) -> decltype(c.begin());

Returns: c.begin().

template <class C> auto buffer_sequence_end(C& c) -> decltype(c.end());
template <class C> auto buffer_sequence_end(const C& c) -> decltype(c.end());

Returns: c.end().

10.16.7. Function buffer_size

[buffer.size]

template<class ConstBufferSequence>
  size_t buffer_size(const ConstBufferSequence& buffers) noexcept;

Returns: The total size of all buffers in the sequence, as if computed as follows:

size_t total_size = 0;
auto i = std::experimental::net::buffer_sequence_begin(buffers);
auto end = std::experimental::net::buffer_sequence_end(buffers);
for (; i != end; ++i)
{
  const_buffer b(v);
  total_size += b.size();
}
return total_size;

10.16.8. Function buffer_copy

[buffer.copy]

template<class MutableBufferSequence, class ConstBufferSequence>
  size_t buffer_copy(const MutableBufferSequence& dest,
                     const ConstBufferSequence& source) noexcept;
template<class MutableBufferSequence, class ConstBufferSequence>
  size_t buffer_copy(const MutableBufferSequence& dest,
                     const ConstBufferSequence& source,
                     size_t max_size) noexcept;

Effects: Copies bytes from the buffer sequence source to the buffer sequence dest, as if by calls to memcpy.

The number of bytes copied is the lesser of:
buffer_size(dest);
buffer_size(source); and
max_size, if specified.

The mutable buffer sequence dest specifies memory where the data should be placed. The operation always fills a buffer in the sequence completely before proceeding to the next.

The constant buffer sequence source specifies memory where the data to be written is located. The operation always copies a buffer in the sequence completely before proceeding to the next.

Returns: The number of bytes copied from source to dest.

10.16.9. Buffer arithmetic

[buffer.arithmetic]

mutable_buffer operator+(const mutable_buffer& b, size_t n) noexcept;
mutable_buffer operator+(size_t n, const mutable_buffer& b) noexcept;

Returns: A mutable_buffer equivalent to

mutable_buffer(
  static_cast<char*>(b.data()) + min(n, b.size()),
  b.size() - min(n, b.size()));

const_buffer operator+(const const_buffer& b, size_t n) noexcept;
const_buffer operator+(size_t n, const const_buffer& b) noexcept;

Returns: A const_buffer equivalent to

const_buffer(
  static_cast<const char*>(b.data()) + min(n, b.size()),
  b.size() - min(n, b.size()));

10.16.10. Buffer creation functions

[buffer.creation]

In the functions below, T must be a trivially copyable or standard-layout type (C++Std [basic.types]).

For the function overloads below that accept an argument of type vector<>, the buffer objects returned are invalidated by any vector operation that also invalidates all references, pointers and iterators referring to the elements in the sequence (C++Std [vector]).

For the function overloads below that accept an argument of type basic_string<>, the buffer objects returned are invalidated according to the rules defined for invalidation of references, pointers and iterators referring to elements of the sequence (C++Std [string.require]).

mutable_buffer buffer(void* p, size_t n) noexcept;

Returns: mutable_buffer(p, n).

const_buffer buffer(const void* p, size_t n) noexcept;

Returns: const_buffer(p, n).

mutable_buffer buffer(const mutable_buffer& b) noexcept;

Returns: b.

mutable_buffer buffer(const mutable_buffer& b, size_t n) noexcept;

Returns: mutable_buffer(b.data(), min(b.size(), n)).

const_buffer buffer(const const_buffer& b) noexcept;

Returns: b.

const_buffer buffer(const const_buffer& b, size_t n) noexcept;

Returns: const_buffer(b.data(), min(b.size(), n)).

template<class T, size_t N>
  mutable_buffer buffer(T (&data)[N]) noexcept;
template<class T, size_t N>
  const_buffer buffer(const T (&data)[N]) noexcept;
template<class T, size_t N>
  mutable_buffer buffer(array<T, N>& data) noexcept;
template<class T, size_t N>
  const_buffer buffer(array<const T, N>& data) noexcept;
template<class T, size_t N>
  const_buffer buffer(const array<T, N>& data) noexcept;
template<class T, class Allocator>
  mutable_buffer buffer(vector<T, Allocator>& data) noexcept;
template<class T, class Allocator>
  const_buffer buffer(const vector<T, Allocator>& data) noexcept;
template<class CharT, class Traits, class Allocator>
  mutable_buffer buffer(basic_string<CharT, Traits, Allocator>& data) noexcept;
template<class CharT, class Traits>
  const_buffer buffer(basic_string_view<CharT, Traits> data) noexcept;

Returns:

buffer(
  begin(data) != end(data) ? std::addressof(*begin(data)) : nullptr,
  (end(data) - begin(data)) * sizeof(*begin(data)));

template<class T, size_t N>
  mutable_buffer buffer(T (&data)[N], size_t n) noexcept;
template<class T, size_t N>
  const_buffer buffer(const T (&data)[N], size_t n) noexcept;
template<class T, size_t N>
  mutable_buffer buffer(array<T, N>& data, size_t n) noexcept;
template<class T, size_t N>
  const_buffer buffer(array<const T, N>& data, size_t n) noexcept;
template<class T, size_t N>
  const_buffer buffer(const array<T, N>& data, size_t n) noexcept;
template<class T, class Allocator>
  mutable_buffer buffer(vector<T, Allocator>& data, size_t n) noexcept;
template<class T, class Allocator>
  const_buffer buffer(const vector<T, Allocator>& data, size_t n) noexcept;
template<class CharT, class Traits, class Allocator>
  mutable_buffer buffer(basic_string<CharT, Traits, Allocator>& data,
                        size_t n) noexcept;
template<class CharT, class Traits>
  const_buffer buffer(basic_string_view<CharT, Traits> data,
                      size_t n) noexcept;

Returns: buffer(buffer(data), n).

10.16.11. Class template dynamic_vector_buffer

[buffer.dynamic.vector]

Class template dynamic_vector_buffer is an adaptor used to automatically grow or shrink a vector object, to reflect the data successfully transferred in an I/O operation.

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

  template<class T, class Allocator>
  class dynamic_vector_buffer
  {
  public:
    // types:
    typedef const_buffer const_buffers_type;
    typedef mutable_buffer mutable_buffers_type;

    // constructors:
    explicit dynamic_vector_buffer(vector<T, Allocator>& vec) noexcept;
    dynamic_vector_buffer(vector<T, Allocator>& vec,
                          size_t maximum_size) noexcept;
    dynamic_vector_buffer(dynamic_vector_buffer&&) = default;

    // members:
    size_t size() const noexcept;
    size_t max_size() const noexcept;
    size_t capacity() const noexcept;
    const_buffers_type data() const noexcept;
    mutable_buffers_type prepare(size_t n);
    void commit(size_t n);
    void consume(size_t n);

  private:
    vector<T, Allocator>& vec_; // exposition only
    size_t size_; // exposition only
    const size_t max_size_; // exposition only
  };

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

The dynamic_vector_buffer class template meets the requirements of DynamicBuffer.

The dynamic_vector_buffer class template requires that T is a trivially copyable or standard-layout type (C++Std [basic.types]) and that sizeof(T) == 1.

explicit dynamic_vector_buffer(vector<T, Allocator>& vec) noexcept;

Effects: Initializes vec_ with vec, size_ with vec.size(), and max_size_ with vec.max_size().

dynamic_vector_buffer(vector<T, Allocator>& vec,
                      size_t maximum_size) noexcept;

Requires: vec.size() <= maximum_size.

Effects: Initializes vec_ with vec, size_ with vec.size(), and max_size_ with maximum_size.

size_t size() const noexcept;

Returns: size_.

size_t max_size() const noexcept;

Returns: max_size_.

size_t capacity() const noexcept;

Returns: vec_.capacity().

const_buffers_type data() const noexcept;

Returns: buffer(vec_, size_).

mutable_buffers_type prepare(size_t n);

Effects: Performs vec_.resize(size_ + n).

Returns: buffer(buffer(vec_) + size_, n).

Throws: length_error if size() + n exceeds max_size().

void commit(size_t n);

Effects: Performs:

size_ += min(n, vec_.size() - size_);
vec_.resize(size_);

void consume(size_t n);

Effects: Performs:

size_t m = min(n, size_);
vec_.erase(vec_.begin(), vec_.begin() + m);
size_ -= m;

10.16.12. Class template dynamic_string_buffer

[buffer.dynamic.string]

Class template dynamic_string_buffer is an adaptor used to automatically grow or shrink a basic_string object, to reflect the data successfully transferred in an I/O operation.

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

  template<class CharT, class Traits, class Allocator>
  class dynamic_string_buffer
  {
  public:
    // types:
    typedef const_buffer const_buffers_type;
    typedef mutable_buffer mutable_buffers_type;

    // constructors:
    explicit dynamic_string_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept;
    dynamic_string_buffer(basic_string<CharT, Traits, Allocator>& str,
                          size_t maximum_size) noexcept;
    dynamic_string_buffer(dynamic_string_buffer&&) = default;

    // members:
    size_t size() const noexcept;
    size_t max_size() const noexcept;
    size_t capacity() const noexcept;
    const_buffers_type data() const noexcept;
    mutable_buffers_type prepare(size_t n);
    void commit(size_t n) noexcept;
    void consume(size_t n);

  private:
    basic_string<CharT, Traits, Allocator>& str_; // exposition only
    size_t size_; // exposition only
    const size_t max_size_; // exposition only
  };

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

The dynamic_string_buffer class template meets the requirements of DynamicBuffer.

The dynamic_string_buffer class template requires that sizeof(CharT) == 1.

explicit dynamic_string_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept;

Effects: Initializes str_ with str, size_ with str.size(), and max_size_ with str.max_size().

dynamic_string_buffer(basic_string<CharT, Traits, Allocator>& str,
                      size_t maximum_size) noexcept;

Requires: str.size() <= maximum_size.

Effects: Initializes str_ with str, size_ with str.size(), and max_size_ with maximum_size.

size_t size() const noexcept;

Returns: size_.

size_t max_size() const noexcept;

Returns: max_size_.

size_t capacity() const noexcept;

Returns: str_.capacity().

const_buffers_type data() const noexcept;

Returns: buffer(str_, size_).

mutable_buffers_type prepare(size_t n);

Effects: Performs str_.resize(size_ + n).

Returns: buffer(buffer(str_) + size_, n).

Throws: length_error if size() + n exceeds max_size().

void commit(size_t n) noexcept;

Effects: Performs:

size_ += min(n, str_.size() - size_);
str_.resize(size_);

void consume(size_t n);

Effects: Performs:

size_t m = min(n, size_);
str_.erase(m);
size_ -= m;

10.16.13. Dynamic buffer creation functions

[buffer.dynamic.creation]

template<class T, class Allocator>
  dynamic_vector_buffer<T, Allocator>
    dynamic_buffer(vector<T, Allocator>& vec) noexcept;

Returns: dynamic_vector_buffer<T, Allocator>(vec).

template<class T, class Allocator>
  dynamic_vector_buffer<T, Allocator>
    dynamic_buffer(vector<T, Allocator>& vec, size_t n) noexcept;

Returns: dynamic_vector_buffer<T, Allocator>(vec, n).

template<class CharT, class Traits, class Allocator>
  dynamic_string_buffer<CharT, Traits, Allocator>
    dynamic_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept;

Returns: dynamic_string_buffer<CharT, Traits, Allocator>(str).

template<class CharT, class Traits, class Allocator>
  dynamic_string_buffer<CharT, Traits, Allocator>
    dynamic_buffer(basic_string<CharT, Traits, Allocator>& str, size_t n) noexcept;

Returns: dynamic_string_buffer<CharT, Traits, Allocator>(str, n).

10.17. Buffer-oriented streams

[buffer.stream]

10.17.1. Requirements

[buffer.stream.reqmts]

10.17.1.1. Buffer-oriented synchronous read stream requirements

[buffer.stream.reqmts.syncreadstream]

A type X meets the SyncReadStream requirements if it satisfies the requirements listed below.

In the table below, a denotes a value of type X, mb denotes a (possibly const) value satisfying the MutableBufferSequence requirements, and ec denotes an object of type error_code.

Table 15. SyncReadStream requirements

operation

type

semantics, pre/post-conditions

a.read_some(mb)
a.read_some(mb,ec)

size_t

Meets the requirements for a read operation.

If buffer_size(mb) > 0, reads one or more bytes of data from the stream a into the buffer sequence mb. If successful, sets ec such that !ec is true, and returns the number of bytes read. If an error occurred, sets ec such that !!ec is true, and returns 0. If all data has been read from the stream, and the stream performed an orderly shutdown, sets ec to stream_errc::eof and returns 0.

If buffer_size(mb) == 0, the operation shall not block. Sets ec such that !ec is true, and returns 0.


10.17.1.2. Buffer-oriented asynchronous read stream requirements

[buffer.stream.reqmts.asyncreadstream]

A type X meets the AsyncReadStream requirements if it satisfies the requirements listed below.

In the table below, a denotes a value of type X, mb denotes a (possibly const) value satisfying the MutableBufferSequence requirements, and t is a completion token.

Table 16. AsyncReadStream requirements

operation

type

semantics, pre/post-conditions

a.get_executor()

A type satisfying the Executor requirements.

Returns the associated I/O executor.

a.async_read_some(mb,t)

The return type is determined according to the requirements for an asynchronous operation.

Meets the requirements for a read operation and an asynchronous operation with completion signature void(error_code ec, size_t n).

If buffer_size(mb) > 0, initiates an asynchronous operation to read one or more bytes of data from the stream a into the buffer sequence mb. If successful, ec is set such that !ec is true, and n is the number of bytes read. If an error occurred, ec is set such that !!ec is true, and n is 0. If all data has been read from the stream, and the stream performed an orderly shutdown, ec is stream_errc::eof and n is 0.

If buffer_size(mb) == 0, the operation completes immediately. ec is set such that !ec is true, and n is 0.


10.17.1.3. Buffer-oriented synchronous write stream requirements

[buffer.stream.reqmts.syncwritestream]

A type X meets the SyncWriteStream requirements if it satisfies the requirements listed below.

In the table below, a denotes a value of type X, cb denotes a (possibly const) value satisfying the ConstBufferSequence requirements, and ec denotes an object of type error_code.

Table 17. SyncWriteStream requirements

operation

type

semantics, pre/post-conditions

a.write_some(cb)
a.write_some(cb,ec)

size_t

Meets the requirements for a write operation.

If buffer_size(cb) > 0, writes one or more bytes of data to the stream a from the buffer sequence cb. If successful, sets ec such that !ec is true, and returns the number of bytes written. If an error occurred, sets ec such that !!ec is true, and returns 0.

If buffer_size(cb) == 0, the operation shall not block. Sets ec such that !ec is true, and returns 0.


10.17.1.4. Buffer-oriented asynchronous write stream requirements

[buffer.stream.reqmts.asyncwritestream]

A type X meets the AsyncWriteStream requirements if it satisfies the requirements listed below.

In the table below, a denotes a value of type X, cb denotes a (possibly const) value satisfying the ConstBufferSequence requirements, and t is a completion token.

Table 18. AsyncWriteStream requirements

operation

type

semantics, pre/post-conditions

a.get_executor()

A type satisfying the Executor requirements.

Returns the associated I/O executor.

a.async_write_some(cb,t)

The return type is determined according to the requirements for an asynchronous operation.

Meets the requirements for a write operation and an asynchronous operation with completion signature void(error_code ec, size_t n).

If buffer_size(cb) > 0, initiates an asynchronous operation to write one or more bytes of data to the stream a from the buffer sequence cb. If successful, ec is set such that !ec is true, and n is the number of bytes written. If an error occurred, ec is set such that !!ec is true, and n is 0.

If buffer_size(cb) == 0, the operation completes immediately. ec is set such that !ec is true, and n is 0.


10.17.1.5. Completion condition requirements

[buffer.stream.reqmts.completioncondition]

A completion condition is a function object that is used with the algorithms read, async_read, write, and async_write to determine when the algorithm has completed transferring data.

A type X meets the CompletionCondition requirements if it satisfies the requirements of Destructible (C++Std [destructible]) and CopyConstructible (C++Std [copyconstructible]), as well as the additional requirements listed below.

In the table below, x denotes a value of type X, ec denotes a (possibly const) value of type error_code, and n denotes a (possibly const) value of type size_t.

Table 19. CompletionCondition requirements

expression

return type

assertion/note
pre/post-condition

x(ec, n)

size_t

Let n be the total number of bytes transferred by the read or write algorithm so far.

Returns the maximum number of bytes to be transferred on the next read_some, async_read_some, write_some, or async_write_some operation performed by the algorithm. Returns 0 to indicate that the algorithm is complete.


10.17.2. Class transfer_all

[buffer.stream.transfer.all]

The class transfer_all is a completion condition that is used to specify that a read or write operation should continue until all of the data has been transferred, or until an error occurs.

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

  class transfer_all
  {
  public:
    size_t operator()(const error_code& ec, size_t) const;
  };

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

The class transfer_all satisfies the CompletionCondition requirements.

size_t operator()(const error_code& ec, size_t) const;

Returns: If !ec, an unspecified non-zero value. Otherwise 0.

10.17.3. Class transfer_at_least

[buffer.stream.transfer.at.least]

The class transfer_at_least is a completion condition that is used to specify that a read or write operation should continue until a minimum number of bytes has been transferred, or until an error occurs.

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

  class transfer_at_least
  {
  public:
    explicit transfer_at_least(size_t m);
    size_t operator()(const error_code& ec, size_t s) const;
  private:
    size_t minimum_; // exposition only
  };

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

The class transfer_at_least satisfies the CompletionCondition requirements.

explicit transfer_at_least(size_t m);

Postconditions: minimum_ == m.

size_t operator()(const error_code& ec, size_t n) const;

Returns: If !ec && n < minimum_, an unspecified non-zero value. Otherwise 0.

10.17.4. Class transfer_exactly

[buffer.stream.transfer.exactly]

The class transfer_exactly is a completion condition that is used to specify that a read or write operation should continue until an exact number of bytes has been transferred, or until an error occurs.

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

  class transfer_exactly
  {
  public:
    explicit transfer_exactly(size_t e);
    size_t operator()(const error_code& ec, size_t s) const;
  private:
    size_t exact_; // exposition only
  };

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

The class transfer_exactly satisfies the CompletionCondition requirements.

explicit transfer_exactly(size_t e);

Postconditions: exact_ == e.

size_t operator()(const error_code& ec, size_t n) const;

Returns: If !ec && n < exact_, the result of min(exact_ - n, N), where N is an unspecified non-zero value. Otherwise 0.

10.17.5. Synchronous read operations

[buffer.read]

template<class SyncReadStream, class MutableBufferSequence>
  size_t read(SyncReadStream& stream,
              const MutableBufferSequence& buffers);
template<class SyncReadStream, class MutableBufferSequence>
  size_t read(SyncReadStream& stream,
              const MutableBufferSequence& buffers, error_code& ec);
template<class SyncReadStream, class MutableBufferSequence,
  class CompletionCondition>
    size_t read(SyncReadStream& stream,
                const MutableBufferSequence& buffers,
                CompletionCondition completion_condition);
template<class SyncReadStream, class MutableBufferSequence,
  class CompletionCondition>
    size_t read(SyncReadStream& stream,
                const MutableBufferSequence& buffers,
                CompletionCondition completion_condition,
                error_code& ec);

Effects: Clears ec, then reads data from the buffer-oriented synchronous read stream object stream by performing zero or more calls to the stream's read_some member function.

The completion_condition parameter specifies a completion condition to be called prior to each call to the stream's read_some member function. The completion condition is passed the error_code value from the most recent read_some call, and the total number of bytes transferred in the synchronous read operation so far. The completion condition return value specifies the maximum number of bytes to be read on the subsequent read_some call. Overloads where a completion condition is not specified behave as if called with an object of class transfer_all.

The synchronous read operation continues until:

— the total number of bytes transferred is equal to buffer_size(buffers); or

— the completion condition returns 0.

On return, ec contains the error_code value from the most recent read_some call.

Returns: The total number of bytes transferred in the synchronous read operation.

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

template<class SyncReadStream, class DynamicBuffer>
  size_t read(SyncReadStream& stream, DynamicBuffer&& b);
template<class SyncReadStream, class DynamicBuffer>
  size_t read(SyncReadStream& stream, DynamicBuffer&& b, error_code& ec);
template<class SyncReadStream, class DynamicBuffer,
  class CompletionCondition>
    size_t read(SyncReadStream& stream, DynamicBuffer&& b,
                CompletionCondition completion_condition);
template<class SyncReadStream, class DynamicBuffer,
  class CompletionCondition>
    size_t read(SyncReadStream& stream, DynamicBuffer&& b,
                CompletionCondition completion_condition,
                error_code& ec);

Effects: Clears ec, then reads data from the synchronous read stream object stream by performing zero or more calls to the stream's read_some member function.

Data is placed into the dynamic buffer object b. A mutable buffer sequence is obtained prior to each read_some call using b.prepare(N), where N is an unspecified value less than or equal to b.max_size() - b.size(). [Note: Implementations are encouraged to use b.capacity() when determining N, to minimize the number of read_some calls performed on the stream. —end note] After each read_some call, the implementation performs b.commit(n), where n is the return value from read_some.

The completion_condition parameter specifies a completion condition to be called prior to each call to the stream's read_some member function. The completion condition is passed the error_code value from the most recent read_some call, and the total number of bytes transferred in the synchronous read operation so far. The completion condition return value specifies the maximum number of bytes to be read on the subsequent read_some call. Overloads where a completion condition is not specified behave as if called with an object of class transfer_all.

The synchronous read operation continues until:

b.size() == b.max_size(); or

— the completion condition returns 0.

On return, ec contains the error_code value from the most recent read_some call.

Returns: The total number of bytes transferred in the synchronous read operation.

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

10.17.6. Asynchronous read operations

[buffer.async.read]

template<class AsyncReadStream, class MutableBufferSequence,
  class CompletionToken>
    DEDUCED async_read(AsyncReadStream& stream,
                    const MutableBufferSequence& buffers,
                    CompletionToken&& token);
template<class AsyncReadStream, class MutableBufferSequence,
  class CompletionCondition, class CompletionToken>
    DEDUCED async_read(AsyncReadStream& stream,
                    const MutableBufferSequence& buffers,
                    CompletionCondition completion_condition,
                    CompletionToken&& token);

Completion signature: void(error_code ec, size_t n).

Effects: Reads data from the buffer-oriented asynchronous read stream object stream by invoking the stream's async_read_some member function (henceforth referred to as asynchronous read_some operations) zero or more times.

The completion_condition parameter specifies a completion condition to be called prior to each asynchronous read_some operation. The completion condition is passed the error_code value from the most recent asynchronous read_some operation, and the total number of bytes transferred in the asynchronous read operation so far. The completion condition return value specifies the maximum number of bytes to be read on the subsequent asynchronous read_some operation. Overloads where a completion condition is not specified behave as if called with an object of class transfer_all.

This asynchronous read operation is outstanding until:

— the total number of bytes transferred is equal to buffer_size(buffers); or

— the completion condition returns 0.

The program shall ensure the AsyncReadStream object stream is valid until the completion handler for the asynchronous operation is invoked.

On completion of the asynchronous operation, ec is the error_code value from the most recent asynchronous read_some operation, and n is the total number of bytes transferred.

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

template<class AsyncReadStream, class DynamicBuffer,
  class CompletionToken>
    DEDUCED async_read(AsyncReadStream& stream,
                    DynamicBuffer&& b, CompletionToken&& token);
template<class AsyncReadStream, class DynamicBuffer,
  class CompletionCondition, class CompletionToken>
    DEDUCED async_read(AsyncReadStream& stream,
                    DynamicBuffer&& b,
                    CompletionCondition completion_condition,
                    CompletionToken&& token);

Completion signature: void(error_code ec, size_t n).

Effects: Initiates an asynchronous operation to read data from the buffer-oriented asynchronous read stream object stream by performing one or more asynchronous read_some operations on the stream.

Data is placed into the dynamic buffer object b. A mutable buffer sequence is obtained prior to each async_read_some call using b.prepare(N), where N is an unspecified value such that N is less than or equal to b.max_size() - b.size(). [Note: Implementations are encouraged to use b.capacity() when determining N, to minimize the number of asynchronous read_some operations performed on the stream. —end note] After the completion of each asynchronous read_some operation, the implementation performs b.commit(n), where n is the value passed to the asynchronous read_some operation's completion handler.

The completion_condition parameter specifies a completion condition to be called prior to each asynchronous read_some operation. The completion condition is passed the error_code value from the most recent asynchronous read_some operation, and the total number of bytes transferred in the asynchronous read operation so far. The completion condition return value specifies the maximum number of bytes to be read on the subsequent asynchronous read_some operation. Overloads where a completion condition is not specified behave as if called with an object of class transfer_all.

The asynchronous read operation is outstanding until:

b.size() == b.max_size(); or

— the completion condition returns 0.

The program shall ensure the AsyncReadStream object stream is valid until the completion handler for the asynchronous operation is invoked.

On completion of the asynchronous operation, ec is the error_code value from the most recent asynchronous read_some operation, and n is the total number of bytes transferred.

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

10.17.7. Synchronous write operations

[buffer.write]

template<class SyncWriteStream, class ConstBufferSequence>
  size_t write(SyncWriteStream& stream,
               const ConstBufferSequence& buffers);
template<class SyncWriteStream, class ConstBufferSequence>
  size_t write(SyncWriteStream& stream,
               const ConstBufferSequence& buffers, error_code& ec);
template<class SyncWriteStream, class ConstBufferSequence,
  class CompletionCondition>
    size_t write(SyncWriteStream& stream,
                 const ConstBufferSequence& buffers,
                 CompletionCondition completion_condition);
template<class SyncWriteStream, class ConstBufferSequence,
  class CompletionCondition>
    size_t write(SyncWriteStream& stream,
                 const ConstBufferSequence& buffers,
                 CompletionCondition completion_condition,
                 error_code& ec);

Effects: Writes data to the buffer-oriented synchronous write stream object stream by performing zero or more calls to the stream's write_some member function.

The completion_condition parameter specifies a completion condition to be called prior to each call to the stream's write_some member function. The completion condition is passed the error_code value from the most recent write_some call, and the total number of bytes transferred in the synchronous write operation so far. The completion condition return value specifies the maximum number of bytes to be written on the subsequent write_some call. Overloads where a completion condition is not specified behave as if called with an object of class transfer_all.

The synchronous write operation continues until:

— the total number of bytes transferred is equal to buffer_size(buffers); or

— the completion condition returns 0.

On return, ec contains the error_code value from the most recent write_some call.

Returns: The total number of bytes transferred in the synchronous write operation.

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

template<class SyncWriteStream, class DynamicBuffer>
  size_t write(SyncWriteStream& stream, DynamicBuffer&& b);
template<class SyncWriteStream, class DynamicBuffer>
  size_t write(SyncWriteStream& stream, DynamicBuffer&& b, error_code& ec);
template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition>
  size_t write(SyncWriteStream& stream, DynamicBuffer&& b,
               CompletionCondition completion_condition);
template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition>
  size_t write(SyncWriteStream& stream, DynamicBuffer&& b,
               CompletionCondition completion_condition,
               error_code& ec);

Effects: Writes data to the synchronous write stream object stream by performing zero or more calls to the stream's write_some member function.

Data is written from the dynamic buffer object b. A constant buffer sequence is obtained using b.data(). After the data has been written to the stream, the implementation performs b.consume(n), where n is the number of bytes successfully written.

The completion_condition parameter specifies a completion condition to be called after each call to the stream's write_some member function. The completion condition is passed the error_code value from the most recent write_some call, and the total number of bytes transferred in the synchronous write operation so far. The completion condition return value specifies the maximum number of bytes to be written on the subsequent write_some call. Overloads where a completion condition is not specified behave as if called with an object of class transfer_all.

The synchronous write operation continues until:

b.size() == 0; or

— the completion condition returns 0.

On return, ec contains the error_code value from the most recent write_some call.

Returns: The total number of bytes transferred in the synchronous write operation.

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

10.17.8. Asynchronous write operations

[buffer.async.write]

template<class AsyncWriteStream, class ConstBufferSequence,
  class CompletionToken>
    DEDUCED async_write(AsyncWriteStream& stream,
                     const ConstBufferSequence& buffers,
                     CompletionToken&& token);
template<class AsyncWriteStream, class ConstBufferSequence,
  class CompletionCondition, class CompletionToken>
    DEDUCED async_write(AsyncWriteStream& stream,
                     const ConstBufferSequence& buffers,
                     CompletionCondition completion_condition,
                     CompletionToken&& token);

Completion signature: void(error_code ec, size_t n).

Effects: Initiates an asynchronous operation to write data to the buffer-oriented asynchronous write stream object stream by performing zero or more asynchronous operations on the stream using the stream's async_write_some member function (henceforth referred to as asynchronous write_some operations).

The completion_condition parameter specifies a completion condition to be called prior to each asynchronous write_some operation. The completion condition is passed the error_code value from the most recent asynchronous write_some operation, and the total number of bytes transferred in the asynchronous write operation so far. The completion condition return value specifies the maximum number of bytes to be written on the subsequent asynchronous write_some operation. Overloads where a completion condition is not specified behave as if called with an object of class transfer_all.

The asynchronous write operation continues until:

— the total number of bytes transferred is equal to buffer_size(buffers); or

— the completion condition returns 0.

The program must ensure the AsyncWriteStream object stream is valid until the completion handler for the asynchronous operation is invoked.

On completion of the asynchronous operation, ec is the error_code value from the most recent asynchronous write_some operation, and n is the total number of bytes transferred.

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

template<class AsyncWriteStream, class DynamicBuffer, class CompletionToken>
  DEDUCED async_write(AsyncWriteStream& stream,
                   DynamicBuffer&& b, CompletionToken&& token);
template<class AsyncWriteStream, class DynamicBuffer,
  class CompletionCondition, class CompletionToken>
    DEDUCED async_write(AsyncWriteStream& stream,
                     DynamicBuffer&& b,
                     CompletionCondition completion_condition,
                     CompletionToken&& token);

Completion signature: void(error_code ec, size_t n).

Effects: Initiates an asynchronous operation to write data to the buffer-oriented asynchronous write stream object stream by performing zero or more asynchronous write_some operations on the stream.

Data is written from the dynamic buffer object b. A constant buffer sequence is obtained using b.data(). After the data has been written to the stream, the implementation performs b.consume(n), where n is the number of bytes successfully written.

The completion_condition parameter specifies a completion condition to be called prior to each asynchronous write_some operation. The completion condition is passed the error_code value from the most recent asynchronous write_some operation, and the total number of bytes transferred in the asynchronous write operation so far. The completion condition return value specifies the maximum number of bytes to be written on the subsequent asynchronous write_some operation. Overloads where a completion condition is not specified behave as if called with an object of class transfer_all.

The asynchronous write operation continues until:

b.size() == 0; or

— the completion condition returns 0.

The program must ensure both the AsyncWriteStream object stream and the memory associated with the dynamic buffer b are valid until the completion handler for the asynchronous operation is invoked.

On completion of the asynchronous operation, ec is the error_code value from the most recent asynchronous write_some operation, and n is the total number of bytes transferred.

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

10.17.9. Synchronous delimited read operations

[buffer.read.until]

template<class SyncReadStream, class DynamicBuffer>
  size_t read_until(SyncReadStream& s, DynamicBuffer&& b, char delim);
template<class SyncReadStream, class DynamicBuffer>
  size_t read_until(SyncReadStream& s, DynamicBuffer&& b,
                    char delim, error_code& ec);
template<class SyncReadStream, class DynamicBuffer>
  size_t read_until(SyncReadStream& s, DynamicBuffer&& b, string_view delim);
template<class SyncReadStream, class DynamicBuffer>
  size_t read_until(SyncReadStream& s,