1[/ 2 / Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 3 / 4 / Distributed under the Boost Software License, Version 1.0. (See accompanying 5 / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 /] 7 8[section:async The Proactor Design Pattern: Concurrency Without Threads] 9 10The Boost.Asio library offers side-by-side support for synchronous and asynchronous 11operations. The asynchronous support is based on the Proactor design pattern 12[link boost_asio.overview.core.async.references \[POSA2\]]. The advantages and 13disadvantages of this approach, when compared to a synchronous-only or Reactor 14approach, are outlined below. 15 16[heading Proactor and Boost.Asio] 17 18Let us examine how the Proactor design pattern is implemented in Boost.Asio, 19without reference to platform-specific details. 20 21[$boost_asio/proactor.png] 22 23[*Proactor design pattern (adapted from \[POSA2\])] 24 25[mdash] Asynchronous Operation 26 27[:Defines an operation that is executed asynchronously, such as an asynchronous 28read or write on a socket.] 29 30[mdash] Asynchronous Operation Processor 31 32[:Executes asynchronous operations and queues events on a completion event 33queue when operations complete. From a high-level point of view, internal 34services like `reactive_socket_service` are asynchronous operation processors.] 35 36[mdash] Completion Event Queue 37 38[:Buffers completion events until they are dequeued by an asynchronous event 39demultiplexer.] 40 41[mdash] Completion Handler 42 43[:Processes the result of an asynchronous operation. These are function 44objects, often created using `boost::bind`.] 45 46[mdash] Asynchronous Event Demultiplexer 47 48[:Blocks waiting for events to occur on the completion event queue, and returns 49a completed event to its caller.] 50 51[mdash] Proactor 52 53[:Calls the asynchronous event demultiplexer to dequeue events, and dispatches 54the completion handler (i.e. invokes the function object) associated with the 55event. This abstraction is represented by the `io_context` class.] 56 57[mdash] Initiator 58 59[:Application-specific code that starts asynchronous operations. The initiator 60interacts with an asynchronous operation processor via a high-level interface 61such as `basic_stream_socket`, which in turn delegates to a service like 62`reactive_socket_service`.] 63 64[heading Implementation Using Reactor] 65 66On many platforms, Boost.Asio implements the Proactor design pattern in terms 67of a Reactor, such as `select`, `epoll` or `kqueue`. This implementation 68approach corresponds to the Proactor design pattern as follows: 69 70[mdash] Asynchronous Operation Processor 71 72[:A reactor implemented using `select`, `epoll` or `kqueue`. When the reactor 73indicates that the resource is ready to perform the operation, the processor 74executes the asynchronous operation and enqueues the associated completion 75handler on the completion event queue.] 76 77[mdash] Completion Event Queue 78 79[:A linked list of completion handlers (i.e. function objects).] 80 81[mdash] Asynchronous Event Demultiplexer 82 83[:This is implemented by waiting on an event or condition variable until a 84completion handler is available in the completion event queue.] 85 86[heading Implementation Using Windows Overlapped I/O] 87 88On Windows NT, 2000 and XP, Boost.Asio takes advantage of overlapped I/O to 89provide an efficient implementation of the Proactor design pattern. This 90implementation approach corresponds to the Proactor design pattern as follows: 91 92[mdash] Asynchronous Operation Processor 93 94[:This is implemented by the operating system. Operations are initiated by 95calling an overlapped function such as `AcceptEx`.] 96 97[mdash] Completion Event Queue 98 99[:This is implemented by the operating system, and is associated with an I/O 100completion port. There is one I/O completion port for each `io_context` 101instance.] 102 103[mdash] Asynchronous Event Demultiplexer 104 105[:Called by Boost.Asio to dequeue events and their associated completion 106handlers.] 107 108[heading Advantages] 109 110[mdash] Portability. 111 112[:Many operating systems offer a native asynchronous I/O API (such as 113overlapped I/O on __Windows__) as the preferred option for developing high 114performance network applications. The library may be implemented in terms of 115native asynchronous I/O. However, if native support is not available, the 116library may also be implemented using synchronous event demultiplexors that 117typify the Reactor pattern, such as __POSIX__ `select()`.] 118 119[mdash] Decoupling threading from concurrency. 120 121[:Long-duration operations are performed asynchronously by the implementation 122on behalf of the application. Consequently applications do not need to spawn 123many threads in order to increase concurrency.] 124 125[mdash] Performance and scalability. 126 127[:Implementation strategies such as thread-per-connection (which a 128synchronous-only approach would require) can degrade system performance, due to 129increased context switching, synchronisation and data movement among CPUs. With 130asynchronous operations it is possible to avoid the cost of context switching 131by minimising the number of operating system threads [mdash] typically a 132limited resource [mdash] and only activating the logical threads of control 133that have events to process.] 134 135[mdash] Simplified application synchronisation. 136 137[:Asynchronous operation completion handlers can be written as though they 138exist in a single-threaded environment, and so application logic can be 139developed with little or no concern for synchronisation issues.] 140 141[mdash] Function composition. 142 143[:Function composition refers to the implementation of functions to provide a 144higher-level operation, such as sending a message in a particular format. Each 145function is implemented in terms of multiple calls to lower-level read or write 146operations.] 147 148[:For example, consider a protocol where each message consists of a 149fixed-length header followed by a variable length body, where the length of the 150body is specified in the header. A hypothetical read_message operation could be 151implemented using two lower-level reads, the first to receive the header and, 152once the length is known, the second to receive the body.] 153 154[:To compose functions in an asynchronous model, asynchronous operations can be 155chained together. That is, a completion handler for one operation can initiate 156the next. Starting the first call in the chain can be encapsulated so that the 157caller need not be aware that the higher-level operation is implemented as a 158chain of asynchronous operations.] 159 160[:The ability to compose new operations in this way simplifies the development 161of higher levels of abstraction above a networking library, such as functions 162to support a specific protocol.] 163 164[heading Disadvantages] 165 166[mdash] Program complexity. 167 168[:It is more difficult to develop applications using asynchronous mechanisms 169due to the separation in time and space between operation initiation and 170completion. Applications may also be harder to debug due to the inverted flow 171of control.] 172 173[mdash] Memory usage. 174 175[:Buffer space must be committed for the duration of a read or write operation, 176which may continue indefinitely, and a separate buffer is required for each 177concurrent operation. The Reactor pattern, on the other hand, does not require 178buffer space until a socket is ready for reading or writing.] 179 180[heading References] 181 182\[POSA2\] D. Schmidt et al, ['Pattern Oriented Software Architecture, Volume 1832]. Wiley, 2000. 184 185[endsect] 186