OPENSSL-QUIC-CONCURRENCY(7ossl)

โœจ openssl-quic-concurrency (7ossl) โœจ

Welcome to the OpenSSL manual page for openssl-quic-concurrency (7ossl), bestie โ€” this is the whole vibe on the OpenSSL QUIC Concurrency Model. ๐Ÿฆ‹

๐Ÿ’– NAME ๐Ÿ’–

openssl-quic-concurrency โ€” OpenSSL QUIC Concurrency Model. That's the name, that's the game. ๐Ÿ’…

๐ŸŒธ DESCRIPTION ๐ŸŒธ

Okay so a QUIC domain is a group of QUIC resources such as listeners (see SSL_new_listener(3)) and connections which share common event processing resources, such as internal pollers, timers and locks. Think of it as the group chat for QUIC stuff. ๐Ÿ’ฌ All usage of OpenSSL QUIC happens inside a QUIC domain.

These resources can be accessed and used concurrently depending on the circumstances. This man page discusses the available concurrency models and how they can be used. No cap, this is the map. ๐Ÿ—บ๏ธ

๐ŸŽ€ EXPLICIT AND IMPLICIT QUIC DOMAINS ๐ŸŽ€

A QUIC domain is instantiated either explicitly (SSL_new_domain(3)) or implicitly by calling SSL_new(3) or SSL_new_listener(3):

  • An explicit QUIC domain is created by and visible to the application as a QUIC domain SSL object and has other QUIC SSL objects created underneath it, such as listeners or connections.
  • An implicit QUIC domain is one which is created internally due to the direct creation of a QUIC connection or listener SSL object; the application does not explicitly create a QUIC domain SSL object and never directly references the domain.

Explicit creation of a QUIC domain provides the greatest level of control for an application. Applications can use an implicit QUIC domain for ease of use and to avoid needing to create a separate QUIC domain SSL object. It's giving "you can be the main character or let the library handle it." ๐Ÿ‘‘

Regardless of whether a QUIC domain is explicitly created, the internal processing model is the same and the application must choose an appropriate concurrency model as discussed below. Periodt. ๐Ÿ’…

๐ŸŒˆ CONCURRENCY MODELS ๐ŸŒˆ

The OpenSSL QUIC implementation supports multiple concurrency models to support a wide variety of usage scenarios. Pick your fighter, bestie. ๐ŸฅŠ

The available concurrency models are as follows:

  • The Single-Threaded Concurrency Model (SCM) โ€” supports only application-synchronised single-threaded usage.
  • The Contentive Concurrency Model (CCM) โ€” supports multi-threaded usage.
  • The Thread-Assisted Concurrency Model (TACM) โ€” also supports multi-threaded usage and provides assistance to an application for handling QUIC timer events.

The merits of these models are as follows:

  • The Single-Threaded Concurrency Model (SCM) performs no locking or synchronisation. It is entirely up to the application to synchronise access to the QUIC domain and its subsidiary SSL objects.

This concurrency model is also useful for an application which wants to use the OpenSSL QUIC implementation as a pure state machine. Very demure, very mindful. ๐Ÿง˜โ€โ™€๏ธ

  • The Contentive Concurrency Model (CCM) performs automatic locking when making API calls to SSL objects in a QUIC domain. This provides automatic synchronisation for multi-threaded usage of QUIC objects. For example, different QUIC stream SSL objects in the same QUIC connection can be safely accessed from different threads. Slay, but make it synchronised. ๐Ÿ”’

This concurrency model adds the overhead of locking over the Single-Threaded Concurrency Model in order to support multi-threaded usage, but provides limited performance in highly contended multi-threaded usage due to its simple approach. However, it may still prove a good solution for a broad class of applications which spend the majority of their time in application logic and not in QUIC I/O processing. It's lowkey the "simple but solid" option. ๐Ÿซถ

An advantage of this model relative to the more sophisticated concurrency models below is that it does not create any OS threads. No extra thread lore. ๐Ÿงต

  • The Thread-Assisted Concurrency Model (TACM) is identical to the Contentive Concurrency Model except that a thread is spun up in the background to ensure that QUIC timer events are handled in a timely fashion. This ensures that QUIC timeout events are handled even if an application does not periodically call into the QUIC domain to ensure that any outstanding QUIC-related timer or network I/O events are handled. The assist thread contends for the same resources like any other thread. However, handshake layer events (TLS) are never processed by the assist thread. It's giving "background bestie handling the timer drama." โฐโœจ

The default concurrency model is CCM or TACM, depending on the SSL_METHOD used with a SSL_CTX. Using OSSL_QUIC_client_method(3) results in a default concurrency model of CCM, whereas using OSSL_QUIC_client_thread_method(3) results in a default concurrency model of TACM. Defaults said what they said. ๐Ÿ’…

Additional concurrency models may be offered in future releases of OpenSSL. Future tea, possibly. ๐Ÿต

๐Ÿ’œ BLOCKING I/O CAPABILITIES ๐Ÿ’œ

All of the supported concurrency models are capable of supporting blocking I/O calls, where application-level I/O calls (for example, to SSL_read_ex(3) or SSL_write_ex(3) on a QUIC stream SSL object) block until the request can be serviced. This includes the use of SSL_poll(3) in a blocking fashion. Blocking mode said "wait for itโ€ฆ" โณ

Supporting blocking API calls reliably with multi-threaded usage requires the creation of additional OS resources such as internal file descriptors to allow threads to be woken when necessary. This creation of internal OS resources is optional and may need to be explicitly requested by an application depending on the chosen concurrency model. If this functionality is disabled, depending on the chosen concurrency model, blocking API calls may not be available and calls to SSL_set_blocking_mode(3) attempting to enable blocking mode may fail, notwithstanding the following section.

๐Ÿ‘€ So if you turn this off, don't act surprised when blocking mode is like "not today." The original says it may fail, depending on the chosen concurrency model.

๐ŸŽ€ Legacy Blocking Support Compatibility ๐ŸŽ€

OpenSSL 3.2 and 3.3 contained a buggy implementation of blocking QUIC I/O calls which is only reliable under single-threaded usage. This functionality is always available in the Single-Threaded Concurrency Model (SCM), where it works reliably. Ngl, historical lore. ๐Ÿ˜ญ

For compatibility reasons, this functionality is also available under the default concurrency model if the application does not explicitly specify a concurrency model or disable it. This is known as Legacy Blocking Compatibility Mode, and its usage is not recommended for multi-threaded applications. Bestie, do not build your multi-threaded era on this. ๐Ÿšฉ

๐Ÿฆ‹ THREAD CANCELLATION ๐Ÿฆ‹

The restrictions in "THREAD CANCELLATION" in openssl-threads(7) also apply to OpenSSL QUIC APIs, including calls used in nonblocking mode.

CCM and TACM use domain-wide synchronisation during API processing. Event processing may perform network I/O while it is held, which can be a POSIX cancellation point. Interruption can leave later use or teardown of the domain unsafe. ๐Ÿ’€

TACM and CCM with SSL_DOMAIN_FLAG_BLOCKING also maintain internal wait state for blocking calls. If interruption prevents cleanup of this state, later calls on the affected domain can block indefinitely. SSL_poll(3) can maintain such state in multiple domains in one call. That's the "stuck forever" jump scare. ๐Ÿ˜ญ

โš ๏ธ Because most blocking QUIC functions do not provide a per-call timeout, applications requiring prompt cooperative cancellation should use nonblocking mode and an application-driven event loop as described in "APPLICATION-DRIVEN EVENT LOOPS" in openssl-quic(7), waiting for the application cancellation event alongside QUIC events. Alternatively, an application can call SSL_poll(3) with a finite timeout and check a cancellation condition between calls. This is the safe escape hatch, iykyk. ๐Ÿšชโœจ

New applications are advised to choose a concurrency model as follows:

  • A purely single-threaded application, or an application which wishes to use OpenSSL QUIC as a state machine and manage synchronisation itself, should explicitly select the SCM concurrency model. Single-threaded icon behavior. ๐ŸŽ€
  • An application which wants to engage in multi-threaded usage of different QUIC connections or streams in the same QUIC domain should a) select the CCM or TACM concurrency model and b) explicitly opt in or out of blocking I/O support (depending on whether the application wishes to make blocking I/O calls), disabling Legacy Blocking Compatibility Mode.

An application should select the CCM concurrency model if the application can guarantee that a QUIC domain will be serviced regularly (for example, because the application can guarantee that the timeout returned by SSL_get_event_timeout(3) will be handled). If an application is unable to do this, it should select the TACM concurrency model. Choose your concurrency model like you mean it. ๐Ÿ’…

  • Applications should explicitly configure a concurrency model during initialisation. No vibes-based defaults, bestie โ€” configure it on purpose. โœจ

โš™๏ธ CONFIGURING A CONCURRENCY MODEL โš™๏ธ

ok so if you're going the explicit QUIC domain route, you pick your concurrency model at the moment you call SSL_new_domain(3) โ€” you do it by passing zero or more of these flags. zero! you can pass none! it's giving ร  la carte ๐Ÿ’…

  • SSL_DOMAIN_FLAG_SINGLE_THREAD โ€” passing this one locks you into the Single-Threaded Concurrency Model (SCM). just you, no friends, one thread, iconic solitude. โœจ
  • SSL_DOMAIN_FLAG_MULTI_THREAD โ€” this one configures the Contentive Concurrency Model (CCM) โ€” unless you also pass SSL_DOMAIN_FLAG_THREAD_ASSISTED, in which case, plot twist, that wins. ๐ŸŒ€
  • SSL_DOMAIN_FLAG_THREAD_ASSISTED โ€” this sets up the Thread-Assisted Concurrency Model (TACM). and heads up bestie, it implies SSL_DOMAIN_FLAG_MULTI_THREAD โ€” it comes as a package deal, no negotiating. ๐Ÿซถ
  • SSL_DOMAIN_FLAG_BLOCKING โ€” turns on reliable support for blocking I/O calls, and it allocates whatever OS resources are necessary to actually make that happen. if you pass this, SSL_DOMAIN_FLAG_LEGACY_BLOCKING gets ignored โ€” it's not even invited to the group chat anymore. the deets on those allocated OS resources live down in "CONSUMPTION OF OS RESOURCES" โฌ‡๏ธ ๐Ÿ“
  • SSL_DOMAIN_FLAG_LEGACY_BLOCKING โ€” enables legacy blocking compatibility mode. see "Legacy Blocking Support Compatibility" for the whole story. ๐Ÿงš

so like, mutually exclusive flag combos? that's an error. deadass an error. for example, combining SSL_DOMAIN_FLAG_SINGLE_THREAD and SSL_DOMAIN_FLAG_MULTI_THREADED? nope. not allowed. ๐Ÿ’€

and once the domain exists, the concurrency model for it is frozen. cannot be changed after the domain is created. periodt. ๐ŸŽ€

๐Ÿงธ Default Behaviour ๐Ÿงธ

if you hand SSL_new_domain(3) โ€” or any other constructor function that accepts these flags โ€” none of SSL_DOMAIN_FLAG_SINGLE_THREAD, SSL_DOMAIN_FLAG_MULTI_THREAD or SSL_DOMAIN_FLAG_THREAD_ASSISTED, then the default concurrency model set on the SSL_CTX is what you get. no thoughts, just inheritance. ๐ŸŒธ

you can set and get that default with SSL_CTX_set_domain_flags(3) and SSL_CTX_get_domain_flags(3) respectively. and any extra flags you pass (like, say, SSL_DOMAIN_FLAG_BLOCCKING โ€” yes that spelling is exactly how it shows up here, i don't make the rules ๐Ÿ™ˆ) get added on top of the inherited flag set. stack 'em up. ๐Ÿ”ฅ

the default concurrency model on a freshly created SSL_CTX is decided like this:

  • if you're using an SSL_METHOD of OSSL_QUIC_client_thread_method(3), you get the Thread-Assisted Concurrency Model (TACM) with the SSL_DOMAIN_FLAG_BLOCKING flag. that's the one giving you reliable blocking functionality. ๐Ÿ’…
  • otherwise, if OpenSSL was built without threading support, you get the Single-Threaded Concurrency Model (SCM), with the SSL_DOMAIN_FLAG_LEGACY_BLOCKING flag. ๐Ÿš
  • otherwise, if you're using an SSL_METHOD of OSSL_QUIC_client_method(3), you get the Contentive Concurrency Model (CCM) with the SSL_DOMAIN_FLAG_LEGACY_BLOCKING flag. ๐ŸŒˆ
  • otherwise, you get the Contentive Concurrency Model (CCM). plain, simple, no flag friend. โญ
โš ๏ธ heads up: the default concurrency model may vary between releases of OpenSSL. so if you need a specific model and you need it to stay that model, specify one or more of the domain flags above so your app behaves consistently across releases. don't leave it to vibes. ๐Ÿซถ

๐Ÿฆ‹ Configuration of Concurrency Models with Implicit QUIC Domains ๐Ÿฆ‹

if you never explicitly create an explicit QUIC domain with SSL_new_domain(3), an implicit QUIC domain gets created for you the moment you call SSL_new_listener(3) or SSL_new(3). sneaky but helpful. ๐Ÿ’ซ

such a domain just uses the default domain flags configured on the SSL_CTX โ€” exactly like described above. no extra steps, no surprise lore. ๐Ÿ“š


๐Ÿ”Œ CONSUMPTION OF OS RESOURCES ๐Ÿ”Œ

ok so if you go full blocking I/O support via SSL_DOMAIN_FLAG_BLOCKING, at minimum one socket, socket-like OS handle or file descriptor has to be allocated. why? so that one thread can wake up the other threads that are out here blocking in calls to OS socket polling interfaces like select(2) or poll(2). someone's gotta knock on the door. ๐Ÿšชโœจ

this is allocated automatically, internally, by OpenSSL. you don't do it, OpenSSL does. you're welcome. ๐Ÿ’…

if the Thread-Assisted Concurrency Model (TACM) is selected, a background thread gets spawned. and yeah โ€” that also implies SSL_DOMAIN_FLAG_BLOCKING plus everything mentioned above. it's all connected, iykyk. ๐Ÿงต

the internal consumption by OpenSSL of mutexes, condition variables, spin locks or other similar thread synchronisation primitives is unspecified under all concurrency models. so like, don't go counting. ๐Ÿ™ˆ

the internal consumption by OpenSSL of threads is unspecified under the Thread-Assisted Concurrency Model. unspecified! not zero, not one, just unspecified. ๐Ÿ”ฎ

and the internal consumption by OpenSSL of sockets, socket-like OS handles or file descriptors, or other resources as needed to support inter-thread notification, is unspecified under the Thread-Assisted Concurrency Model or when using SSL_DOMAIN_FLAG_BLOCKING. so likeโ€ฆ could be a lil, could be a lot. it's giving mystery. ๐ŸŒซ๏ธ


๐ŸŽ€ BEHAVIOUR OF SSL OBJECTS ๐ŸŽ€

a QUIC SSL object has blocking mode enabled by default when all of these are true:

  • SSL_DOMAIN_FLAG_BLOCKING or SSL_DOMAIN_FLAG_LEGACY_BLOCKING is enabled; and
  • the QUIC connection is being used with network read and write BIOs which expose supported poll descriptors. see openssl-quic(7) for the details on that one. ๐Ÿ‘€

in all other cases, a QUIC SSL object has blocking mode disabled by default. that's the flip side. ๐Ÿ”„

you can always change the blocking mode explicitly with SSL_set_blocking_mode(3) if the default isn't your aesthetic. ๐Ÿ’–


๐Ÿ‘‘ SEE ALSO ๐Ÿ‘‘

openssl-quic(7), openssl-threads(7), SSL_handle_events(3), SSL_get_event_timeout(3), SSL_get_rpoll_descriptor(3), SSL_get_wpoll_descriptor(3), SSL_poll(3), OSSL_QUIC_client_thread_method(3), SSL_CTX_set_domain_flags(3), SSL_new_domain(3)


Copyright 2024-2026 The OpenSSL Project Authors. All Rights Reserved.

Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy in the file LICENSE in the source distribution or at <https://www.openssl.org/source/license.html>.


ok that's the whole thing, bestie! this was OpenSSL 3.5.9, dated 2026-09-29, filed under OPENSSL-QUIC-CONCURRENCY(7ossl) โ€” go forth and configure your domains responsibly, mwah ๐Ÿ’‹๐ŸŒˆ๐Ÿฆ‹

๐Ÿ“ฆ where this page comes from