1*e7be843bSPierre ProncheryQUIC Concurrency Architecture 2*e7be843bSPierre Pronchery============================= 3*e7be843bSPierre Pronchery 4*e7be843bSPierre ProncheryIntroduction 5*e7be843bSPierre Pronchery------------ 6*e7be843bSPierre Pronchery 7*e7be843bSPierre ProncheryMost QUIC implementations in C are offered as a simple state machine without any 8*e7be843bSPierre Proncheryincluded I/O solution. Applications must do significant integration work to 9*e7be843bSPierre Proncheryprovide the necessary infrastructure for a QUIC implementation to integrate 10*e7be843bSPierre Proncherywith. Moreover, blocking I/O at an application level may not be supported. 11*e7be843bSPierre Pronchery 12*e7be843bSPierre ProncheryOpenSSL QUIC seeks to offer a QUIC solution which can serve multiple use cases: 13*e7be843bSPierre Pronchery 14*e7be843bSPierre Pronchery- Firstly, it seeks to offer the simple state machine model and a fully 15*e7be843bSPierre Pronchery customisable network path (via a BIO) for those who want it; 16*e7be843bSPierre Pronchery 17*e7be843bSPierre Pronchery- Secondly, it seeks to offer a turnkey solution with an in-the-box I/O 18*e7be843bSPierre Pronchery and polling solution which can support blocking API calls in a Berkeley 19*e7be843bSPierre Pronchery sockets-like way. 20*e7be843bSPierre Pronchery 21*e7be843bSPierre ProncheryThese usage modes are somewhat diametrically opposed. One involves libssl 22*e7be843bSPierre Proncheryconsuming no resources but those it is given, with an application responsible 23*e7be843bSPierre Proncheryfor synchronisation and a potentially custom network I/O path. This usage model 24*e7be843bSPierre Proncheryis not “smart”. Network traffic is connected to the state machine and state is 25*e7be843bSPierre Proncheryinput and output from the state machine as needed by an application on a purely 26*e7be843bSPierre Proncherynon-blocking basis. Determining *when* to do anything is largely the 27*e7be843bSPierre Proncheryapplication's responsibility. 28*e7be843bSPierre Pronchery 29*e7be843bSPierre ProncheryThe other diametrically opposed usage mode involves libssl managing more things 30*e7be843bSPierre Proncheryinternally to provide an easier to use solution. For example, it may involve 31*e7be843bSPierre Proncheryspinning up background threads to ensure connections are serviced regularly (as 32*e7be843bSPierre Proncheryin our existing client-side thread assisted mode). 33*e7be843bSPierre Pronchery 34*e7be843bSPierre ProncheryIn order to provide for these different use cases, the concept of concurrency 35*e7be843bSPierre Proncherymodels is introduced. A concurrency model defines how “cleverly” the QUIC engine 36*e7be843bSPierre Proncherywill operate and how many background resources (e.g. threads, other OS 37*e7be843bSPierre Proncheryresources) will be established to support operation. 38*e7be843bSPierre Pronchery 39*e7be843bSPierre ProncheryConcurrency Models 40*e7be843bSPierre Pronchery------------------ 41*e7be843bSPierre Pronchery 42*e7be843bSPierre Pronchery- **Unsynchronised Concurrency Model (UCM):** In the Unsynchronised Concurrency 43*e7be843bSPierre Pronchery Model, calls to SSL objects are not synchronised. There is no locking on any 44*e7be843bSPierre Pronchery APL call (the omission of which is purely an optimisation). The application is 45*e7be843bSPierre Pronchery either single-threaded or is otherwise responsible for doing synchronisation 46*e7be843bSPierre Pronchery itself. 47*e7be843bSPierre Pronchery 48*e7be843bSPierre Pronchery Blocking API calls are not supported under this model. This model is intended 49*e7be843bSPierre Pronchery primarily for single-threaded use as a simple state machine by advanced 50*e7be843bSPierre Pronchery applications, and many applications will be likely to disable autoticking. 51*e7be843bSPierre Pronchery 52*e7be843bSPierre Pronchery- **Contentive Concurrency Model (CCM):** In the 53*e7be843bSPierre Pronchery Contentive Concurrency Model, calls to SSL objects are wrapped in locks and 54*e7be843bSPierre Pronchery multi-threaded usage of a QUIC connection (for example, parallel writes to 55*e7be843bSPierre Pronchery different QUIC stream SSL objects belonging to the same QUIC connection) is 56*e7be843bSPierre Pronchery synchronised by a mutex. 57*e7be843bSPierre Pronchery 58*e7be843bSPierre Pronchery This is contentive in the sense that if a large number of threads are trying 59*e7be843bSPierre Pronchery to write to different streams on the same connection, a large amount of lock 60*e7be843bSPierre Pronchery contention will occur. As such, this concurrency model will not scale and 61*e7be843bSPierre Pronchery provide good performance, at least within the context of concurrent use 62*e7be843bSPierre Pronchery of a single connection. 63*e7be843bSPierre Pronchery 64*e7be843bSPierre Pronchery Under this model, APL calls by the application result in lock-wrapped 65*e7be843bSPierre Pronchery mutations of QUIC core objects (`QUIC_CHANNEL`, `QUIC_STREAM`, etc.) on the 66*e7be843bSPierre Pronchery same thread. 67*e7be843bSPierre Pronchery 68*e7be843bSPierre Pronchery This model may be used either in a variant which does not support blocking 69*e7be843bSPierre Pronchery (NB-CCM) or which does support blocking (B-CCM). The blocking variant must 70*e7be843bSPierre Pronchery spin up additional OS resources to correctly support blocking semantics. 71*e7be843bSPierre Pronchery 72*e7be843bSPierre Pronchery- **Thread Assisted Contentive Concurrency Model (TA-CCM):** This is currently 73*e7be843bSPierre Pronchery implemented by our thread assisted mode for client-side QUIC usage. It does 74*e7be843bSPierre Pronchery not realise the full state separation or performance of the Worker Concurrency 75*e7be843bSPierre Pronchery Model (WCM) below. Instead, it simply spawns a background thread which ensures 76*e7be843bSPierre Pronchery QUIC timer events are handled as needed. It makes use of the Contentive 77*e7be843bSPierre Pronchery Concurrency Model for performing that handling, in that it obtains a lock when 78*e7be843bSPierre Pronchery ticking a QUIC connection just as any call by an application would. 79*e7be843bSPierre Pronchery 80*e7be843bSPierre Pronchery This mode is likely to be deprecated in favour of the full Worker Concurrency 81*e7be843bSPierre Pronchery Model (WCM), which it will naturally be subsumed by. 82*e7be843bSPierre Pronchery 83*e7be843bSPierre Pronchery- **Worker Concurrency Model (WCM):** In the Worker Concurrency Model, 84*e7be843bSPierre Pronchery a background worker thread is spawned to manage connection processing. All 85*e7be843bSPierre Pronchery interaction with a SSL object goes through this thread in some way. 86*e7be843bSPierre Pronchery Interactions with SSL objects are essentially translated into commands and 87*e7be843bSPierre Pronchery handled by the worker thread. To optimise performance and minimise lock 88*e7be843bSPierre Pronchery contention, there is an emphasis on message passing over locking. 89*e7be843bSPierre Pronchery Internal dataflow for application data can be managed in a zero-copy way to 90*e7be843bSPierre Pronchery minimise the costs of this message passing. 91*e7be843bSPierre Pronchery 92*e7be843bSPierre Pronchery Under this model, QUIC core objects (`QUIC_CHANNEL`, `QUIC_STREAM`, etc.) will 93*e7be843bSPierre Pronchery live solely on the worker thread and access to these objects by an application 94*e7be843bSPierre Pronchery thread will be entirely forbidden. 95*e7be843bSPierre Pronchery 96*e7be843bSPierre Pronchery Blocking API calls are supported under this model. 97*e7be843bSPierre Pronchery 98*e7be843bSPierre ProncheryThese concurrency models are summarised as follows: 99*e7be843bSPierre Pronchery 100*e7be843bSPierre Pronchery| Model | Sophistication | Concurrency | Blocking Supported | OS Resources | Timer Events | RX Steering | Core State Affinity | 101*e7be843bSPierre Pronchery|--------|----------------|-----------------------|--------------------|---------------------------|-----------------|-------------|----------------------| 102*e7be843bSPierre Pronchery| UCM | Lowest | ST only | No | None | App Responsible | None | App Thread | 103*e7be843bSPierre Pronchery| CCM | | MT (Contentive) | Optional | Mutex, (Notifier) | App Responsible | TBD | App Threads | 104*e7be843bSPierre Pronchery| TA-CCM† | | MT (Contentive) | Optional | Mutex, Thread, (Notifier) | Managed | TBD | App & Assist Threads | 105*e7be843bSPierre Pronchery| WCM | Highest | MT (High Performance) | Yes | Mutex, Thread, Notifier | Managed | Futureproof | Worker Thread | 106*e7be843bSPierre Pronchery 107*e7be843bSPierre Pronchery† To eventually be deprecated in favour of WCM. 108*e7be843bSPierre Pronchery 109*e7be843bSPierre ProncheryLegend: 110*e7be843bSPierre Pronchery 111*e7be843bSPierre Pronchery- **Blocking Supported:** Whether blocking calls to e.g. `SSL_read` can be 112*e7be843bSPierre Pronchery supported. If this is listed as “optional”, extra resources are required to 113*e7be843bSPierre Pronchery support this under the listed model and these resources could be omitted if an 114*e7be843bSPierre Pronchery application indicates it does not need this functionality at initialisation 115*e7be843bSPierre Pronchery time. 116*e7be843bSPierre Pronchery 117*e7be843bSPierre Pronchery- **OS Resources:** “Mutex” refers to mutex and condition variable resources. 118*e7be843bSPierre Pronchery “Notifier” refers to a kind of OS resource needed to allow one thread to wake 119*e7be843bSPierre Pronchery another thread which is currently blocking in an OS socket polling call such 120*e7be843bSPierre Pronchery as poll(2) (e.g. an eventfd or socketpair). Resources listed in parentheses in 121*e7be843bSPierre Pronchery the table above are required only if blocking support is desired. 122*e7be843bSPierre Pronchery 123*e7be843bSPierre Pronchery- **Timer Events:** Is an application responsible for ensuring QUIC timeout 124*e7be843bSPierre Pronchery events are handled in a timely manner? 125*e7be843bSPierre Pronchery 126*e7be843bSPierre Pronchery- **RX Steering:** The matter of RX steering will be discussed in detail in a 127*e7be843bSPierre Pronchery future document. Broadly speaking, RX steering concerns whether incoming 128*e7be843bSPierre Pronchery traffic for multiple different QUIC connections on the same local port (e.g. 129*e7be843bSPierre Pronchery for a server) can be vectored *by the OS* to different threads or whether the 130*e7be843bSPierre Pronchery demuxing of incoming traffic for different connections has to be done manually 131*e7be843bSPierre Pronchery on an in-process basis. 132*e7be843bSPierre Pronchery 133*e7be843bSPierre Pronchery The WCM model most readily supports RX steering and is futureproof in this 134*e7be843bSPierre Pronchery regard. The feasibility of having the UCM and CCM models support RX steering 135*e7be843bSPierre Pronchery is left for future analysis. 136*e7be843bSPierre Pronchery 137*e7be843bSPierre Pronchery- **Core State Affinity:** Which threads are allowed to touch the QUIC core 138*e7be843bSPierre Pronchery objects (`QUIC_CHANNEL`, `QUIC_STREAM`, etc.) 139*e7be843bSPierre Pronchery 140*e7be843bSPierre ProncheryArchitecture 141*e7be843bSPierre Pronchery------------ 142*e7be843bSPierre Pronchery 143*e7be843bSPierre ProncheryTo recap, the API Personality Layer (APL) refers to the code in `quic_impl.c` 144*e7be843bSPierre Proncherywhich implements the libssl API personality (`SSL_write`, etc.). The APL is 145*e7be843bSPierre Proncherycleanly separated from the QUIC core implementation (`QUIC_CHANNEL`, etc.). 146*e7be843bSPierre Pronchery 147*e7be843bSPierre ProncherySince UCM is basically a slight optimisation of CCM in which unnecessary locking 148*e7be843bSPierre Proncheryis elided, discussion from hereon in will focus on CCM and WCM except where 149*e7be843bSPierre Proncherythere are specific differences between CCM and UCM. 150*e7be843bSPierre Pronchery 151*e7be843bSPierre ProncherySupporting both CCM and WCM creates significant architectural challenges. Under 152*e7be843bSPierre ProncheryCCM, QUIC core objects have their state mutated under lock by arbitrary 153*e7be843bSPierre Proncheryapplication threads and these mutations happen during APL calls. By contrast, a 154*e7be843bSPierre Proncheryperformant WCM architecture requires that APL calls be recorded and serviced in 155*e7be843bSPierre Proncheryan asynchronous fashion involving message passing to a worker thread. This 156*e7be843bSPierre Proncherythreatens to require highly divergent dispatch architectures for the two 157*e7be843bSPierre Proncheryconcurrency models. 158*e7be843bSPierre Pronchery 159*e7be843bSPierre ProncheryAs such, the concept of a **Concurrency Management Layer (CML)** is introduced. 160*e7be843bSPierre ProncheryThe CML lives between the APL and the QUIC core code. It is responsible for 161*e7be843bSPierre Proncherydispatching in-thread mutations of QUIC core objects when operating under CCM, 162*e7be843bSPierre Proncheryand for dispatching messages to a worker thread under WCM. 163*e7be843bSPierre Pronchery 164*e7be843bSPierre Pronchery 165*e7be843bSPierre Pronchery 166*e7be843bSPierre ProncheryThere are two different CMLs: 167*e7be843bSPierre Pronchery 168*e7be843bSPierre Pronchery- **Direct CML (DCML)**, in which core objects are worked on in the same thread 169*e7be843bSPierre Pronchery which made an APL call, under lock; 170*e7be843bSPierre Pronchery 171*e7be843bSPierre Pronchery- **Worker CML (WCML)**, in which core objects are managed by a worker thread 172*e7be843bSPierre Pronchery with communication via message passing. This CML is split into a front end 173*e7be843bSPierre Pronchery (WCML-FE) and back end (WCML-BE). 174*e7be843bSPierre Pronchery 175*e7be843bSPierre ProncheryThe legacy thread assisted mode uses a bespoke method which is similar to the 176*e7be843bSPierre Proncheryapproach used by the DCML. 177*e7be843bSPierre Pronchery 178*e7be843bSPierre ProncheryCML Design 179*e7be843bSPierre Pronchery---------- 180*e7be843bSPierre Pronchery 181*e7be843bSPierre ProncheryThe CML is designed to have as small an API surface area as possible to enable 182*e7be843bSPierre Proncheryunified handling of as many kinds of (APL) API operations as possible. The idea 183*e7be843bSPierre Proncheryis that complex APL calls are translated into simple operations on the CML. 184*e7be843bSPierre Pronchery 185*e7be843bSPierre ProncheryAt its core, the CML exposes some number of *pipes*. The number of pipes which 186*e7be843bSPierre Proncherycan be accessed via the CML varies as connections and streams are created and 187*e7be843bSPierre Proncherydestroyed. A pipe is a *unidirectional* transport for byte streams. Zero-copy 188*e7be843bSPierre Proncheryoptimisations are expected to be implemented in future but are deferred. 189*e7be843bSPierre Pronchery 190*e7be843bSPierre ProncheryThe CML (`QUIC_CML`) allows the caller to refer to a pipe by providing an opaque 191*e7be843bSPierre Proncherypipe handle (`QUIC_CML_PIPE`). If the pipe is a sending pipe, the caller can use 192*e7be843bSPierre Pronchery`ossl_cml_write` to try and add bytes to it. Conversely, if it is a receiving 193*e7be843bSPierre Proncherypipe, the caller can use `ossl_cml_read` to try and read bytes from it. 194*e7be843bSPierre Pronchery 195*e7be843bSPierre ProncheryThe method `ossl_cml_block_until` allows the caller to block until at least one 196*e7be843bSPierre Proncheryof the provided pipe handles is ready. Ready means that at least one byte can be 197*e7be843bSPierre Proncherywritten (for a sending pipe) or at least one byte can be read (for a receiving 198*e7be843bSPierre Proncherypipe). 199*e7be843bSPierre Pronchery 200*e7be843bSPierre ProncheryNote that there is only expected to be one `QUIC_CML` instance per QUIC event 201*e7be843bSPierre Proncheryprocessing domain (i.e., per `QUIC_DOMAIN` / `QUIC_ENGINE` instance). The CML 202*e7be843bSPierre Proncheryfully abstracts the QUIC core objects such as `QUIC_ENGINE` or `QUIC_CHANNEL` so 203*e7be843bSPierre Proncherythat the APL never sees them. 204*e7be843bSPierre Pronchery 205*e7be843bSPierre ProncheryThe caller retrieves a pipe handle using `ossl_cml_get_pipe`. This function 206*e7be843bSPierre Proncheryretrieves a pipe based on two values: 207*e7be843bSPierre Pronchery 208*e7be843bSPierre Pronchery - a CML pipe class; 209*e7be843bSPierre Pronchery - a CML *selector*. 210*e7be843bSPierre Pronchery 211*e7be843bSPierre ProncheryThe CML selector is a tagged union structure which specifies what pipe is to be 212*e7be843bSPierre Proncheryretrieved. Abstractly, examples of selectors include: 213*e7be843bSPierre Pronchery 214*e7be843bSPierre Pronchery```text 215*e7be843bSPierre Pronchery Domain () 216*e7be843bSPierre Pronchery Listener (listener_id: uint) 217*e7be843bSPierre Pronchery Conn (conn_id: uint) 218*e7be843bSPierre Pronchery Stream (conn_id: uint, stream_id: u64) 219*e7be843bSPierre Pronchery``` 220*e7be843bSPierre Pronchery 221*e7be843bSPierre ProncheryIn other words, the CML selector selects the “object” to retrieve a pipe from. 222*e7be843bSPierre Pronchery 223*e7be843bSPierre ProncheryThe CML pipe class is one of the following values: 224*e7be843bSPierre Pronchery 225*e7be843bSPierre Pronchery- Request 226*e7be843bSPierre Pronchery- Notification 227*e7be843bSPierre Pronchery- App Send 228*e7be843bSPierre Pronchery- App Recv 229*e7be843bSPierre Pronchery 230*e7be843bSPierre ProncheryThe pipe classes available for a given selector vary. For example, the “App 231*e7be843bSPierre ProncherySend” and “App Recv” pipes only exist on a stream, so it is invalid to request 232*e7be843bSPierre Proncherysuch a pipe in conjunction with a different type of selector. 233*e7be843bSPierre Pronchery 234*e7be843bSPierre ProncheryThe “Request” and “App Send” classes expose send-only streams, and the 235*e7be843bSPierre Pronchery“Notification” and “App Recv” classes expose receive-only streams. 236*e7be843bSPierre Pronchery 237*e7be843bSPierre ProncheryFor any given CML selector, the Request pipe is used to send serialized commands 238*e7be843bSPierre Proncheryfor asynchronous processing in relation to the entity selected by that selector. 239*e7be843bSPierre ProncheryConversely, the Notification pipe returns asynchronous notifications. These 240*e7be843bSPierre Proncherycould be in relation to a previous Command (e.g. indicating whether a command 241*e7be843bSPierre Proncherysucceeded), or unprompted notifications about other events. 242*e7be843bSPierre Pronchery 243*e7be843bSPierre ProncheryThe underlying pattern here is that there is a bidirectional channel for control 244*e7be843bSPierre Proncherymessages, and a bidirectional channel for application data, both comprised of 245*e7be843bSPierre Proncherytwo unidirectional pipes in turn. 246*e7be843bSPierre Pronchery 247*e7be843bSPierre ProncheryPipe handles are stable for as long as the pipe they reference exists, so an APL 248*e7be843bSPierre Proncheryobject can cache a pipe handle if desired. 249*e7be843bSPierre Pronchery 250*e7be843bSPierre ProncheryAll CML methods are thread safe. The CML implementation handles any necessary 251*e7be843bSPierre Proncherylocking (if any) internally. 252*e7be843bSPierre Pronchery 253*e7be843bSPierre ProncheryThe `ossl_cml_write_available` and `ossl_cml_read_available` calls determine the 254*e7be843bSPierre Proncherynumber of bytes which can currently be written to a send-only pipe, or read from 255*e7be843bSPierre Proncherya receive-only pipe, respectively. 256*e7be843bSPierre Pronchery 257*e7be843bSPierre Pronchery**Race conditions.** Because these are separate calls to `ossl_cml_write` and 258*e7be843bSPierre Pronchery`ossl_cml_read`, the values returned by these functions may become out of date 259*e7be843bSPierre Proncherybefore the caller has a chance to read `ossl_cml_write` or `ossl_cml_read`. 260*e7be843bSPierre ProncheryHowever, such changes are guaranteed to be monotonically in favour of the 261*e7be843bSPierre Proncherycaller; for example, the value returned by `ossl_cml_write_available` will only 262*e7be843bSPierre Proncheryever increase asynchronously (and only decrease as a result of an 263*e7be843bSPierre Pronchery`ossl_cml_write` call). Conversely, the value returned by 264*e7be843bSPierre Pronchery`ossl_cml_read_available` will only ever increase asynchronously (and only 265*e7be843bSPierre Proncherydecrease as a result of an `ossl_cml_read` call). Assuming that only one thread 266*e7be843bSPierre Proncherymakes calls to CML functions at a given time *for a given pipe*, this therefore 267*e7be843bSPierre Proncheryposes no issue for callers. 268*e7be843bSPierre Pronchery 269*e7be843bSPierre ProncheryConcurrent use of `ossl_cml_write` or `ossl_cml_read` for a given pipe is not 270*e7be843bSPierre Proncheryintended (and would not make sense in any case). The caller is responsible for 271*e7be843bSPierre Proncherysynchronising such calls. 272*e7be843bSPierre Pronchery 273*e7be843bSPierre Pronchery**Examples of pipe usage.** The application data pipes are used to serialize the 274*e7be843bSPierre Proncheryactual application data sent or received on a QUIC stream. The usage of the 275*e7be843bSPierre Proncheryrequest/notification pipes is more varied and used for control activity. There 276*e7be843bSPierre Proncheryis therefore a “control/data” separation here. The request and notification 277*e7be843bSPierre Proncherypipes transport tagged unions. Abstractly, commands and notifications might 278*e7be843bSPierre Proncheryinclude: 279*e7be843bSPierre Pronchery 280*e7be843bSPierre Pronchery- Request: Reset Stream (error code: u64) 281*e7be843bSPierre Pronchery- Notification: Connection Terminated by Peer 282*e7be843bSPierre Pronchery 283*e7be843bSPierre Pronchery**Example implementation of `SSL_write`.** An `SSL_write`-like API might be 284*e7be843bSPierre Proncheryimplemented in the APL like this: 285*e7be843bSPierre Pronchery 286*e7be843bSPierre Pronchery```c 287*e7be843bSPierre Proncheryint do_write(QUIC_CML *cml, 288*e7be843bSPierre Pronchery QUIC_CML_PIPE notification_pipe, 289*e7be843bSPierre Pronchery QUIC_CML_PIPE app_send_pipe, 290*e7be843bSPierre Pronchery const void *buf, size_t buf_len) 291*e7be843bSPierre Pronchery{ 292*e7be843bSPierre Pronchery size_t bytes_written = 0; 293*e7be843bSPierre Pronchery 294*e7be843bSPierre Pronchery for (;;) { 295*e7be843bSPierre Pronchery /* e.g. connection termination */ 296*e7be843bSPierre Pronchery process_any_notifications(notification_pipe); 297*e7be843bSPierre Pronchery 298*e7be843bSPierre Pronchery /* state checks, etc. */ 299*e7be843bSPierre Pronchery if (...->conn_terminated) 300*e7be843bSPierre Pronchery return 0; 301*e7be843bSPierre Pronchery 302*e7be843bSPierre Pronchery if (buf_len == 0) 303*e7be843bSPierre Pronchery return 1; 304*e7be843bSPierre Pronchery 305*e7be843bSPierre Pronchery if (!ossl_cml_write(cml, app_send_pipe, buf, buf_len, &bytes_written)) 306*e7be843bSPierre Pronchery return 0; 307*e7be843bSPierre Pronchery 308*e7be843bSPierre Pronchery if (bytes_written == 0) { 309*e7be843bSPierre Pronchery if (!should_block()) 310*e7be843bSPierre Pronchery break; 311*e7be843bSPierre Pronchery 312*e7be843bSPierre Pronchery ossl_cml_block_until(cml, {notification_pipe, app_send_pipe}); 313*e7be843bSPierre Pronchery continue; /* try again */ 314*e7be843bSPierre Pronchery } 315*e7be843bSPierre Pronchery 316*e7be843bSPierre Pronchery buf += bytes_written; 317*e7be843bSPierre Pronchery buf_len -= bytes_written; 318*e7be843bSPierre Pronchery } 319*e7be843bSPierre Pronchery 320*e7be843bSPierre Pronchery return 1; 321*e7be843bSPierre Pronchery} 322*e7be843bSPierre Pronchery``` 323*e7be843bSPierre Pronchery 324*e7be843bSPierre Pronchery```c 325*e7be843bSPierre Pronchery/* 326*e7be843bSPierre Pronchery * Creates a new CML using the Direct CML (DCML) implementation. need_locking 327*e7be843bSPierre Pronchery * may be 0 to elide mutex usage if the application is guaranteed to synchronise 328*e7be843bSPierre Pronchery * access or is purely single-threaded. 329*e7be843bSPierre Pronchery */ 330*e7be843bSPierre ProncheryQUIC_CML *ossl_cml_new_direct(int need_locking); 331*e7be843bSPierre Pronchery 332*e7be843bSPierre Pronchery/* Creates a new CML using the Worker CML (WCML) implementation. */ 333*e7be843bSPierre ProncheryQUIC_CML *ossl_cml_new_worker(size_t num_worker_threads); 334*e7be843bSPierre Pronchery 335*e7be843bSPierre Pronchery/* 336*e7be843bSPierre Pronchery * Starts the CML operating. Idempotent after it returns successfully. For the 337*e7be843bSPierre Pronchery * WCML this might e.g. start background threads; for the DCML it is likely to 338*e7be843bSPierre Pronchery * be a no-op (but must still be called). 339*e7be843bSPierre Pronchery */ 340*e7be843bSPierre Proncheryint ossl_cml_start(QUIC_CML *cml); 341*e7be843bSPierre Pronchery 342*e7be843bSPierre Pronchery/* 343*e7be843bSPierre Pronchery * Begins the CML shutdown process. Returns 1 once shutdown is complete; may 344*e7be843bSPierre Pronchery * need to be called multiple times until shutdown is done. 345*e7be843bSPierre Pronchery */ 346*e7be843bSPierre Proncheryint ossl_cml_shutdown(QUIC_CML *cml); 347*e7be843bSPierre Pronchery 348*e7be843bSPierre Pronchery/* 349*e7be843bSPierre Pronchery * Immediate free of the CML. This is always safe but may cause handling 350*e7be843bSPierre Pronchery * of a connection to be aborted abruptly as it is an immediate teardown 351*e7be843bSPierre Pronchery * of all state. 352*e7be843bSPierre Pronchery */ 353*e7be843bSPierre Proncheryvoid ossl_cml_free(QUIC_CML *cml); 354*e7be843bSPierre Pronchery 355*e7be843bSPierre Pronchery/* 356*e7be843bSPierre Pronchery * Retrieves a pipe for a logical CML object described by selector. The pipe 357*e7be843bSPierre Pronchery * handle, which is stable over the life of the logical CML object, is written 358*e7be843bSPierre Pronchery * to *pipe_handle. class_ is a QUIC_CML_CLASS value. 359*e7be843bSPierre Pronchery */ 360*e7be843bSPierre Proncheryenum { 361*e7be843bSPierre Pronchery QUIC_CML_CLASS_REQUEST, /* control; send */ 362*e7be843bSPierre Pronchery QUIC_CML_CLASS_NOTIFICATION, /* control; recv */ 363*e7be843bSPierre Pronchery QUIC_CML_CLASS_APP_SEND, /* data; send */ 364*e7be843bSPierre Pronchery QUIC_CML_CLASS_APP_RECV /* data; recv */ 365*e7be843bSPierre Pronchery}; 366*e7be843bSPierre Pronchery 367*e7be843bSPierre Proncheryint ossl_cml_get_pipe(QUIC_CML *cml, 368*e7be843bSPierre Pronchery int class_, 369*e7be843bSPierre Pronchery const QUIC_CML_SELECTOR *selector, 370*e7be843bSPierre Pronchery QUIC_CML_PIPE *pipe_handle); 371*e7be843bSPierre Pronchery 372*e7be843bSPierre Pronchery/* 373*e7be843bSPierre Pronchery * Returns the number of bytes a sending pipe can currently accept. The returned 374*e7be843bSPierre Pronchery * value may increase over time asynchronously but will only decrease in 375*e7be843bSPierre Pronchery * response to an ossl_cml_write call. 376*e7be843bSPierre Pronchery */ 377*e7be843bSPierre Proncherysize_t ossl_cml_write_available(QUIC_CML *cml, QUIC_CML_PIPE pipe_handle); 378*e7be843bSPierre Pronchery 379*e7be843bSPierre Pronchery/* 380*e7be843bSPierre Pronchery * Appends bytes into a sending pipe by copying them. The buffer can be freed 381*e7be843bSPierre Pronchery * as soon as this call returns. 382*e7be843bSPierre Pronchery */ 383*e7be843bSPierre Proncheryint ossl_cml_write(QUIC_CML *cml, QUIC_CML_PIPE pipe_handle, 384*e7be843bSPierre Pronchery const void *buf, size_t buf_len); 385*e7be843bSPierre Pronchery 386*e7be843bSPierre Pronchery/* 387*e7be843bSPierre Pronchery * Returns the number of bytes a receiving pipe currently has waiting to be 388*e7be843bSPierre Pronchery * read. The returned value may increase over time asynchronously but will only 389*e7be843bSPierre Pronchery * decreate in response to an ossl_cml_read call. 390*e7be843bSPierre Pronchery */ 391*e7be843bSPierre Proncherysize_t ossl_cml_read_available(QUIC_CML *cml, QUIC_CML_PIPE pipe_handle); 392*e7be843bSPierre Pronchery 393*e7be843bSPierre Pronchery/* 394*e7be843bSPierre Pronchery * Reads bytes from a receiving pipe by copying them. 395*e7be843bSPierre Pronchery */ 396*e7be843bSPierre Proncheryint ossl_cml_read(QUIC_CML *cml, QUIC_CML_PIPE pipe_handle, 397*e7be843bSPierre Pronchery void *buf, size_t buf_len); 398*e7be843bSPierre Pronchery 399*e7be843bSPierre Pronchery/* 400*e7be843bSPierre Pronchery * Blocks until at least one of the pipes in the array specified by 401*e7be843bSPierre Pronchery * pipe_handles is ready, or until the deadline given is reached. 402*e7be843bSPierre Pronchery * 403*e7be843bSPierre Pronchery * A pipe is ready if: 404*e7be843bSPierre Pronchery * 405*e7be843bSPierre Pronchery * - it is a sending pipe and one or more bytes can now be written; 406*e7be843bSPierre Pronchery * - it is a receiving pipe and one or more bytes can now be read. 407*e7be843bSPierre Pronchery */ 408*e7be843bSPierre Proncheryint ossl_cml_block_until(QUIC_CML *cml, 409*e7be843bSPierre Pronchery const QUIC_CML_PIPE *pipe_handles, 410*e7be843bSPierre Pronchery size_t num_pipe_handles, 411*e7be843bSPierre Pronchery OSSL_TIME deadline); 412*e7be843bSPierre Pronchery``` 413