threading: Add ossl_crypto_condvar_wait_timeout
authorHugo Landau <hlandau@openssl.org>
Tue, 21 Feb 2023 10:18:58 +0000 (10:18 +0000)
committerHugo Landau <hlandau@openssl.org>
Thu, 30 Mar 2023 10:14:07 +0000 (11:14 +0100)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20348)

crypto/thread/arch/thread_posix.c
crypto/thread/arch/thread_win.c
include/internal/thread_arch.h
include/internal/time.h

index b157435fd67f24f2d2ef8d13bb0d6d2ce3c379e7..ae79fe579c6927e6a9dd53847f04daa81c0cc1d6 100644 (file)
@@ -171,6 +171,31 @@ void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex)
     pthread_cond_wait(cv_p, mutex_p);
 }
 
+void ossl_crypto_condvar_wait_timeout(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex,
+                                      OSSL_TIME deadline)
+{
+    pthread_cond_t *cv_p = (pthread_cond_t *)cv;
+    pthread_mutex_t *mutex_p = (pthread_mutex_t *)mutex;
+
+    if (ossl_time_is_infinite(deadline)) {
+        /*
+         * No deadline. Some pthread implementations allow
+         * pthread_cond_timedwait to work the same as pthread_cond_wait when
+         * abstime is NULL, but it is unclear whether this is POSIXly correct.
+         */
+        pthread_cond_wait(cv_p, mutex_p);
+    } else {
+        struct timespec deadline_ts;
+
+        deadline_ts.tv_sec
+            = ossl_time2seconds(deadline);
+        deadline_ts.tv_nsec
+            = (ossl_time2ticks(deadline) % OSSL_TIME_SECOND) / OSSL_TIME_NS;
+
+        pthread_cond_timedwait(cv_p, mutex_p, &deadline_ts);
+    }
+}
+
 void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv)
 {
     pthread_cond_t *cv_p;
index 5bef48458e13a2df5e38b8f7197b84c3b806d869..38a3e9a75bf513dc015eabcafa0520dfe2586e3c 100644 (file)
@@ -162,6 +162,44 @@ void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex)
     SleepConditionVariableCS(cv_p, mutex_p, INFINITE);
 }
 
+void ossl_crypto_condvar_wait_timeout(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex,
+                                      OSSL_TIME deadline, int *timeout_expired)
+{
+    DWORD timeout;
+    CONDITION_VARIABLE *cv_p = (CONDITION_VARIABLE *)cv;
+    CRITICAL_SECTION *mutex_p = (CRITICAL_SECTION *)mutex;
+
+    if (ossl_time_is_infinite(deadline)) {
+        timeout = INFINITE;
+    } else {
+        OSSL_TIME now = ossl_time_now();
+        OSSL_TIME delta = ossl_time_subtract(deadline, now);
+        uint64_t ms;
+
+        if (ossl_time_is_zero(delta)) {
+            if (timeout_expired != NULL)
+                *timeout_expired = 1;
+
+            return;
+        }
+
+        ms = ossl_time2ms(delta);
+
+        /*
+         * Amount of time we want to wait is too long for the 32-bit argument to
+         * the Win32 API, so just wait as long as possible.
+         */
+        if (ms > (uint64_t)(INFINITE - 1))
+            timeout = INFINITE - 1;
+        else
+            timeout = (DWORD)ms;
+    }
+
+    if (!SleepConditionVariableCS(cv_p, mutex_p, timeout)
+        && timeout_expired != NULL)
+        *timeout_expired = 1;
+}
+
 void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv)
 {
     CONDITION_VARIABLE *cv_p;
index bf000006061e79276dffe60a30894a4859e2de93..c8607d50a4a0b28c8f34a4ff675da0bad7d3b83c 100644 (file)
@@ -11,6 +11,7 @@
 # define OSSL_INTERNAL_THREAD_ARCH_H
 # include <openssl/configuration.h>
 # include <openssl/e_os2.h>
+# include "internal/time.h"
 
 # if defined(_WIN32)
 #  include <windows.h>
@@ -44,6 +45,8 @@ void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex);
 
 CRYPTO_CONDVAR *ossl_crypto_condvar_new(void);
 void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex);
+void ossl_crypto_condvar_wait_timeout(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex,
+                                      OSSL_TIME deadline);
 void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv);
 void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv);
 
index 604f9e06d9c5783452e7703945ec96b99bd1772e..968ebbe6bdcf8af768ee8846f202b17837c3df9f 100644 (file)
@@ -35,6 +35,9 @@ typedef struct {
 /* One microsecond. */
 # define OSSL_TIME_US     (OSSL_TIME_MS     / 1000)
 
+/* One nanosecond. */
+# define OSSL_TIME_NS     (OSSL_TIME_US     / 1000)
+
 #define ossl_seconds2time(s) ossl_ticks2time((s) * OSSL_TIME_SECOND)
 #define ossl_time2seconds(t) (ossl_time2ticks(t) / OSSL_TIME_SECOND)
 #define ossl_ms2time(ms) ossl_ticks2time((ms) * OSSL_TIME_MS)