mirror of https://github.com/openssl/openssl.git
115 lines
5.1 KiB
C
115 lines
5.1 KiB
C
/*
|
|
* Copyright 2024-2025 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
|
|
*/
|
|
#ifndef OSSL_QUIC_REACTOR_WAIT_CTX_H
|
|
# define OSSL_QUIC_REACTOR_WAIT_CTX_H
|
|
|
|
# include "internal/quic_predef.h"
|
|
# include "internal/quic_reactor.h"
|
|
# include "internal/list.h"
|
|
|
|
# ifndef OPENSSL_NO_QUIC
|
|
|
|
/*
|
|
* QUIC_REACTOR_WAIT_CTX
|
|
* =====================
|
|
*
|
|
* In order to support inter-thread notification of events which may cause a
|
|
* blocking call on another thread to be able to make forward progress, we need
|
|
* to know when a thread enters and exits a blocking call. The details of why
|
|
* this is involves subtleties of inter-thread synchronisation and a detailed
|
|
* discussion can be found in the source of
|
|
* ossl_quic_reactor_block_until_pred().
|
|
*
|
|
* The core mechanism for such tracking is
|
|
* ossl_quic_reactor_(enter/leave)_blocking_section(), however this API does not
|
|
* support recursive usage to keep the internal implementation simple. In some
|
|
* cases, an API which can be used in a recursive fashion (with multiple
|
|
* balanced calls to enter()/leave() on a single thread) is more convenient.
|
|
*
|
|
* This utility allows multiple blocking operations to be registered on a given
|
|
* thread. Moreover, it allows multiple blocking operations to be registered
|
|
* across an arbitrarily large number of QUIC_REACTORs from a given thread.
|
|
*
|
|
* In short, this allows multiple 'concurrent' blocking calls to be ongoing on a
|
|
* given thread for a given reactor. While on the face of it the notion of
|
|
* multiple concurrent blocking calls on a single thread makes no sense, the
|
|
* implementation of our immediate-mode polling implementation (SSL_poll) makes
|
|
* it natural for us to implement it simply by registering a blocking call per
|
|
* SSL object passed in. Since multiple SSL objects may be passed to an SSL_poll
|
|
* call, and some SSL objects may correspond to the same reactor, and other SSL
|
|
* objects may correspond to a different reactor, we need to be able to
|
|
* determine when a SSL_poll() call has finished with all of the SSL objects
|
|
* *corresponding to a given reactor*.
|
|
*
|
|
* Doing this requires some ephemeral state tracking as a SSL_poll() call may
|
|
* map to an arbitrarily large set of reactor objects. For now, we track this
|
|
* separately from the reactor code as the state needed is only ephemeral and
|
|
* this keeps the reactor internals simple.
|
|
*
|
|
* The concept used is that a thread allocates (on the stack) a
|
|
* QUIC_REACTOR_WAIT_CTX before commencing a blocking operation, and then calls
|
|
* ossl_quic_reactor_wait_ctx_enter() whenever encountering a reactor involved
|
|
* in the imminent blocking operation. Later it must ensure it calls
|
|
* ossl_quic_reactor_wait_ctx_leave() the same number of times for each reactor.
|
|
* enter() and leave() may be called multiple times for the same reactor and
|
|
* wait context so long as the number of calls is balanced. The last leave()
|
|
* call for a given thread's wait context *and a given reactor* causes that
|
|
* reactor to do the inter-thread notification housekeeping needed for
|
|
* multithreaded blocking to work correctly.
|
|
*
|
|
* The gist is that a simple reactor-level counter of active concurrent blocking
|
|
* calls across all threads is not accurate and we need an accurate count of how
|
|
* many 'concurrent' blocking calls for a given reactor are active *on a given
|
|
* thread* in order to avoid deadlocks. Conceptually, you can think of this as
|
|
* refcounting a refcount (which is actually how it is implemented).
|
|
*
|
|
* Logically, a wait context is a map from a reactor pointer (i.e., a unique
|
|
* identifier for the reactor) to the number of 'recursive' calls outstanding:
|
|
*
|
|
* (QUIC_REACTOR *) -> (outstanding call count)
|
|
*
|
|
* When the count for a reactor transitions from 0 to a nonzero value, or vice
|
|
* versa, ossl_quic_reactor_(enter/leave)_blocking_section() is called once.
|
|
*
|
|
* The internal implementation is based on linked lists as we expect the actual
|
|
* number of reactors involved in a given blocking operation to be very small,
|
|
* so spinning up a hashtable is not worthwhile.
|
|
*/
|
|
typedef struct quic_reactor_wait_slot_st QUIC_REACTOR_WAIT_SLOT;
|
|
|
|
DECLARE_LIST_OF(quic_reactor_wait_slot, QUIC_REACTOR_WAIT_SLOT);
|
|
|
|
struct quic_reactor_wait_ctx_st {
|
|
OSSL_LIST(quic_reactor_wait_slot) slots;
|
|
};
|
|
|
|
/* Initialises a wait context. */
|
|
void ossl_quic_reactor_wait_ctx_init(QUIC_REACTOR_WAIT_CTX *ctx);
|
|
|
|
/* Uprefs a given reactor. */
|
|
int ossl_quic_reactor_wait_ctx_enter(QUIC_REACTOR_WAIT_CTX *ctx,
|
|
QUIC_REACTOR *rtor);
|
|
|
|
/* Downrefs a given reactor. */
|
|
void ossl_quic_reactor_wait_ctx_leave(QUIC_REACTOR_WAIT_CTX *ctx,
|
|
QUIC_REACTOR *rtor);
|
|
|
|
/*
|
|
* Destroys a wait context. Must be called after calling init().
|
|
*
|
|
* Precondition: All prior calls to ossl_quic_reactor_wait_ctx_enter() must have
|
|
* been balanced with corresponding leave() calls before calling this
|
|
* (unchecked).
|
|
*/
|
|
void ossl_quic_reactor_wait_ctx_cleanup(QUIC_REACTOR_WAIT_CTX *ctx);
|
|
|
|
# endif
|
|
|
|
#endif
|