QUIC Reactor: Allow a mutex to be released during waits

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20348)
This commit is contained in:
Hugo Landau 2023-02-21 10:18:58 +00:00
parent 2b2b26788e
commit c019e1efe9
3 changed files with 58 additions and 7 deletions

View File

@ -150,12 +150,21 @@ int ossl_quic_reactor_tick(QUIC_REACTOR *rtor);
* the first call to pred() is skipped. This is useful if it is known that * the first call to pred() is skipped. This is useful if it is known that
* ticking the reactor again will not be useful (e.g. because it has already * ticking the reactor again will not be useful (e.g. because it has already
* been done). * been done).
*
* This function assumes a write lock is held for the entire QUIC_CHANNEL. If
* mutex is non-NULL, it must be a lock currently held for write; it will be
* unlocked during any sleep, and then relocked for write afterwards.
*
* Precondition: mutex is NULL or is held for write (unchecked)
* Postcondition: mutex is NULL or is held for write (unless
* CRYPTO_THREAD_write_lock fails)
*/ */
#define SKIP_FIRST_TICK (1U << 0) #define SKIP_FIRST_TICK (1U << 0)
int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor, int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor,
int (*pred)(void *arg), void *pred_arg, int (*pred)(void *arg), void *pred_arg,
uint32_t flags); uint32_t flags,
CRYPTO_RWLOCK *mutex);
# endif # endif

View File

@ -39,7 +39,8 @@ static int block_until_pred(QUIC_CONNECTION *qc,
assert(qc->ch != NULL); assert(qc->ch != NULL);
rtor = ossl_quic_channel_get_reactor(qc->ch); rtor = ossl_quic_channel_get_reactor(qc->ch);
return ossl_quic_reactor_block_until_pred(rtor, pred, pred_arg, flags); return ossl_quic_reactor_block_until_pred(rtor, pred, pred_arg, flags,
ossl_quic_channel_get_mutex(qc->ch));
} }
/* /*

View File

@ -121,10 +121,18 @@ int ossl_quic_reactor_tick(QUIC_REACTOR *rtor)
* Returns 0 on error and 1 on success. Timeout expiry is considered a success * Returns 0 on error and 1 on success. Timeout expiry is considered a success
* condition. We don't elaborate our return values here because the way we are * condition. We don't elaborate our return values here because the way we are
* actually using this doesn't currently care. * actually using this doesn't currently care.
*
* If mutex is non-NULL, it is assumed to be held for write and is unlocked for
* the duration of the call.
*
* Precondition: mutex is NULL or is held for write (unchecked)
* Postcondition: mutex is NULL or is held for write (unless
* CRYPTO_THREAD_write_lock fails)
*/ */
static int poll_two_fds(int rfd, int rfd_want_read, static int poll_two_fds(int rfd, int rfd_want_read,
int wfd, int wfd_want_write, int wfd, int wfd_want_write,
OSSL_TIME deadline) OSSL_TIME deadline,
CRYPTO_RWLOCK *mutex)
{ {
#if defined(OPENSSL_SYS_WINDOWS) || !defined(POLLIN) #if defined(OPENSSL_SYS_WINDOWS) || !defined(POLLIN)
fd_set rfd_set, wfd_set, efd_set; fd_set rfd_set, wfd_set, efd_set;
@ -165,6 +173,9 @@ static int poll_two_fds(int rfd, int rfd_want_read,
/* Do not block forever; should not happen. */ /* Do not block forever; should not happen. */
return 0; return 0;
if (mutex != NULL)
CRYPTO_THREAD_unlock(mutex);
do { do {
/* /*
* select expects a timeout, not a deadline, so do the conversion. * select expects a timeout, not a deadline, so do the conversion.
@ -187,6 +198,9 @@ static int poll_two_fds(int rfd, int rfd_want_read,
pres = select(maxfd + 1, &rfd_set, &wfd_set, &efd_set, ptv); pres = select(maxfd + 1, &rfd_set, &wfd_set, &efd_set, ptv);
} while (pres == -1 && get_last_socket_error_is_eintr()); } while (pres == -1 && get_last_socket_error_is_eintr());
if (mutex != NULL && !CRYPTO_THREAD_write_lock(mutex))
return 0;
return pres < 0 ? 0 : 1; return pres < 0 ? 0 : 1;
#else #else
int pres, timeout_ms; int pres, timeout_ms;
@ -216,6 +230,9 @@ static int poll_two_fds(int rfd, int rfd_want_read,
/* Do not block forever; should not happen. */ /* Do not block forever; should not happen. */
return 0; return 0;
if (mutex != NULL)
CRYPTO_THREAD_unlock(mutex);
do { do {
if (ossl_time_is_infinite(deadline)) { if (ossl_time_is_infinite(deadline)) {
timeout_ms = -1; timeout_ms = -1;
@ -228,6 +245,9 @@ static int poll_two_fds(int rfd, int rfd_want_read,
pres = poll(pfds, npfd, timeout_ms); pres = poll(pfds, npfd, timeout_ms);
} while (pres == -1 && get_last_socket_error_is_eintr()); } while (pres == -1 && get_last_socket_error_is_eintr());
if (mutex != NULL && !CRYPTO_THREAD_write_lock(mutex))
return 0;
return pres < 0 ? 0 : 1; return pres < 0 ? 0 : 1;
#endif #endif
} }
@ -250,10 +270,18 @@ static int poll_descriptor_to_fd(const BIO_POLL_DESCRIPTOR *d, int *fd)
/* /*
* Poll up to two abstract poll descriptors. Currently we only support * Poll up to two abstract poll descriptors. Currently we only support
* poll descriptors which represent FDs. * poll descriptors which represent FDs.
*
* If mutex is non-NULL, it is assumed be a lock currently held for write and is
* unlocked for the duration of any wait.
*
* Precondition: mutex is NULL or is held for write (unchecked)
* Postcondition: mutex is NULL or is held for write (unless
* CRYPTO_THREAD_write_lock fails)
*/ */
static int poll_two_descriptors(const BIO_POLL_DESCRIPTOR *r, int r_want_read, static int poll_two_descriptors(const BIO_POLL_DESCRIPTOR *r, int r_want_read,
const BIO_POLL_DESCRIPTOR *w, int w_want_write, const BIO_POLL_DESCRIPTOR *w, int w_want_write,
OSSL_TIME deadline) OSSL_TIME deadline,
CRYPTO_RWLOCK *mutex)
{ {
int rfd, wfd; int rfd, wfd;
@ -261,12 +289,24 @@ static int poll_two_descriptors(const BIO_POLL_DESCRIPTOR *r, int r_want_read,
|| !poll_descriptor_to_fd(w, &wfd)) || !poll_descriptor_to_fd(w, &wfd))
return 0; return 0;
return poll_two_fds(rfd, r_want_read, wfd, w_want_write, deadline); return poll_two_fds(rfd, r_want_read, wfd, w_want_write, deadline, mutex);
} }
/*
* Block until a predicate function evaluates to true.
*
* If mutex is non-NULL, it is assumed be a lock currently held for write and is
* unlocked for the duration of any wait.
*
* Precondition: Must hold channel write lock (unchecked)
* Precondition: mutex is NULL or is held for write (unchecked)
* Postcondition: mutex is NULL or is held for write (unless
* CRYPTO_THREAD_write_lock fails)
*/
int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor, int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor,
int (*pred)(void *arg), void *pred_arg, int (*pred)(void *arg), void *pred_arg,
uint32_t flags) uint32_t flags,
CRYPTO_RWLOCK *mutex)
{ {
int res; int res;
@ -284,7 +324,8 @@ int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor,
ossl_quic_reactor_net_read_desired(rtor), ossl_quic_reactor_net_read_desired(rtor),
ossl_quic_reactor_get_poll_w(rtor), ossl_quic_reactor_get_poll_w(rtor),
ossl_quic_reactor_net_write_desired(rtor), ossl_quic_reactor_net_write_desired(rtor),
ossl_quic_reactor_get_tick_deadline(rtor))) ossl_quic_reactor_get_tick_deadline(rtor),
mutex))
/* /*
* We don't actually care why the call succeeded (timeout, FD * We don't actually care why the call succeeded (timeout, FD
* readiness), we just call reactor_tick and start trying to do I/O * readiness), we just call reactor_tick and start trying to do I/O