diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 5edebd83ef..aa967304dd 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -1356,6 +1356,7 @@ fipsCastStatus_get noinline ssize_t sun +taskYIELD versal wc_Des3_SetKey wc_Tls13_HKDF_Expand_Label diff --git a/CMakeLists.txt b/CMakeLists.txt index f35291e03d..4a8136165f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3688,6 +3688,14 @@ if(WOLFSSL_RNG_BANK) list(APPEND WOLFSSL_DEFINITIONS "-DWC_RNG_BANK_SUPPORT") endif() +# Thread-safe DRBG (share one WC_RNG between threads) +add_option("WOLFSSL_THREADSAFE_DRBG" + "Enable sharing one WC_RNG between threads (default: enabled)" + "yes" "yes;no") +if(NOT WOLFSSL_THREADSAFE_DRBG) + list(APPEND WOLFSSL_DEFINITIONS "-DWC_NO_DRBG_THREAD_SAFE") +endif() + # Valgrind (for unit tests) add_option("WOLFSSL_VALGRIND" "Enable valgrind for unit tests (default: disabled)" diff --git a/cmake/options.h.in b/cmake/options.h.in index c7ecfd7c4c..7bed3c04d6 100644 --- a/cmake/options.h.in +++ b/cmake/options.h.in @@ -597,6 +597,8 @@ extern "C" { #cmakedefine FP_ECC #undef WC_RNG_BANK_SUPPORT #cmakedefine WC_RNG_BANK_SUPPORT +#undef WC_NO_DRBG_THREAD_SAFE +#cmakedefine WC_NO_DRBG_THREAD_SAFE #undef HAVE_VALGRIND #cmakedefine HAVE_VALGRIND #undef HAVE_CRL_MONITOR diff --git a/configure.ac b/configure.ac index 3b997f801b..a6e7b83e60 100644 --- a/configure.ac +++ b/configure.ac @@ -2721,6 +2721,33 @@ then AM_CFLAGS="$AM_CFLAGS -DWC_RNG_BANK_SUPPORT" fi +# Thread-safe DRBG. Serializes the generate/reseed path of a single WC_RNG so +# one instance can be shared between threads. Needs real atomics, so a +# single-threaded build elects it off in random.h regardless of this setting. +# +# Default off for --enable-linuxkm: the kernel module reaches the DRBG through +# the RNG bank, which checks out an instance exclusively, so the per-generate +# atomic is redundant there and its wait has no place in an atomic context. +# Enable it explicitly with --enable-threadsafe-drbg. +AC_ARG_ENABLE([threadsafe-drbg], + [AS_HELP_STRING([--enable-threadsafe-drbg],[Enable sharing one WC_RNG between threads (default: enabled, except linuxkm)])], + [ ENABLED_THREADSAFE_DRBG=$enableval ], + [ if test "$ENABLED_LINUXKM" = "no"; then + ENABLED_THREADSAFE_DRBG=yes + else + ENABLED_THREADSAFE_DRBG=no + fi ] + ) + +if test "$ENABLED_THREADSAFE_DRBG" = "no" +then + AM_CFLAGS="$AM_CFLAGS -DWC_NO_DRBG_THREAD_SAFE" +elif test "x$enable_threadsafe_drbg" = "xyes" && \ + test "$ENABLED_SINGLETHREADED" = "yes" +then + AC_MSG_ERROR([--enable-threadsafe-drbg is incompatible with --enable-singlethreaded.]) +fi + # DTLS-SCTP AC_ARG_ENABLE([sctp], diff --git a/doc/dox_comments/header_files/random.h b/doc/dox_comments/header_files/random.h index fa870a927e..12f7fe994c 100644 --- a/doc/dox_comments/header_files/random.h +++ b/doc/dox_comments/header_files/random.h @@ -113,6 +113,13 @@ int wc_InitRng(WC_RNG* rng); \param output buffer to which the block is copied \param sz size of output in bytes + \note One WC_RNG may be shared between threads: the DRBG generate and + reseed path is serialized per instance. Build with + --disable-threadsafe-drbg (WC_NO_DRBG_THREAD_SAFE) to opt out where each + instance is only used by one thread at a time. This covers generate and + reseed only -- wc_InitRng() and wc_FreeRng() must not run concurrently + with a generate on the same instance. + _Example_ \code RNG rng; diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index a600356d9e..3e888a6915 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -686,9 +686,79 @@ static int Hash_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, word32 seedSz * and array_add_one (shared utility) which both must * remain available to SHA-512-only builds */ +#ifndef WC_NO_DRBG_THREAD_SAFE +/* Thread-safe DRBG support. Serializes this instance's generate and reseed + * path so one WC_RNG can be shared between threads; it is not a lock discipline + * and makes no claim about atomic-context callers. + * + * The wait is bounded the way the rng_bank spins are: it breaks out on an + * interrupting signal, and on WC_RNG_EXCL_TIMEOUT_SEC when that is defined. + * No timeout is applied by default -- the holder can legitimately be blocked + * in wc_GenerateSeed() for as long as the OS entropy source takes, and failing + * a generate on a slow entropy read would be worse than waiting for it. + * + * Returns 1 when this call took the flag and the caller must release it, 0 when + * exclusivity comes from the owner, or a negative error code. */ +static int RngExclEnter(WC_RNG* rng) +{ + WC_ATOMIC_INT_ARG expected = WC_RNG_EXCL_FREE; +#ifdef WC_RNG_EXCL_TIMEOUT_SEC + time_t ts1 = XTIME(0); +#endif + + if (WOLFSSL_ATOMIC_LOAD(rng->excl) == WC_RNG_EXCL_OWNER) { + return 0; + } + + while (! wolfSSL_Atomic_Int_CompareExchange(&rng->excl, &expected, + WC_RNG_EXCL_HELD)) + { + int intr_ret; + + #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) + /* fork() copies excl as ordinary memory, so a flag another thread held + * at fork time is inherited HELD with no owner left to release it, and + * the pid recovery further down is never reached. Checked only after a + * failed acquire, so the uncontended path is unchanged; the child is + * single-threaded here, so clearing it is safe. */ + if (rng->pid != getpid()) { + WOLFSSL_ATOMIC_STORE(rng->excl, WC_RNG_EXCL_FREE); + } + #endif + + intr_ret = WC_CHECK_FOR_INTR_SIGNALS(); + if (intr_ret != 0) { + return intr_ret; + } + + #ifdef WC_RNG_EXCL_TIMEOUT_SEC + if (XTIME(0) - ts1 > (time_t)WC_RNG_EXCL_TIMEOUT_SEC) { + return WC_TIMEOUT_E; + } + #endif + + expected = WC_RNG_EXCL_FREE; + WC_SPIN_RELAX(); + } + + return 1; +} + +/* Only ever called by the thread that got 1 from RngExclEnter(). */ +static void RngExclExit(WC_RNG* rng) +{ + WOLFSSL_ATOMIC_STORE(rng->excl, WC_RNG_EXCL_FREE); +} +#endif /* !WC_NO_DRBG_THREAD_SAFE */ + /* Returns: DRBG_SUCCESS and DRBG_FAILURE or BAD_FUNC_ARG on fail */ int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz) { +#ifndef WC_NO_DRBG_THREAD_SAFE + int ret; + int excl; +#endif + if (rng == NULL || seed == NULL) { return BAD_FUNC_ARG; } @@ -704,8 +774,22 @@ int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz) #endif return BAD_FUNC_ARG; } +#ifndef WC_NO_DRBG_THREAD_SAFE + /* Serialize against Generate on the same instance. */ + excl = RngExclEnter(rng); + if (excl < 0) { + return excl; + } + ret = Hash_DRBG_Reseed((DRBG_internal *)rng->drbg, seed, seedSz, + NULL, 0); + if (excl) { + RngExclExit(rng); + } + return ret; +#else return Hash_DRBG_Reseed((DRBG_internal *)rng->drbg, seed, seedSz, NULL, 0); +#endif } #endif #ifdef WOLFSSL_DRBG_SHA512 @@ -719,8 +803,22 @@ int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz) #endif return BAD_FUNC_ARG; } +#ifndef WC_NO_DRBG_THREAD_SAFE + /* Serialize against Generate on the same instance. */ + excl = RngExclEnter(rng); + if (excl < 0) { + return excl; + } + ret = Hash512_DRBG_Reseed((DRBG_SHA512_internal *)rng->drbg512, + seed, seedSz, NULL, 0); + if (excl) { + RngExclExit(rng); + } + return ret; +#else return Hash512_DRBG_Reseed((DRBG_SHA512_internal *)rng->drbg512, seed, seedSz, NULL, 0); +#endif } #endif @@ -1913,6 +2011,7 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, if (nonce == NULL && nonceSz != 0) return BAD_FUNC_ARG; + /* Also initializes rng->excl where the thread-safe DRBG is enabled. */ XMEMSET(rng, 0, sizeof(*rng)); #ifdef WOLFSSL_HEAP_TEST @@ -2525,6 +2624,9 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #endif { int ret; +#ifndef WC_NO_DRBG_THREAD_SAFE + int excl = 0; +#endif if (rng == NULL || output == NULL) return BAD_FUNC_ARG; @@ -2583,12 +2685,34 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) if (rng->status != DRBG_OK) return RNG_FAILURE_E; +#ifndef WC_NO_DRBG_THREAD_SAFE + /* Serialize the DRBG core for callers sharing this instance; the paths + * above touch no DRBG state and stay outside. */ + excl = RngExclEnter(rng); + if (excl < 0) { + return excl; + } + + /* Re-check: the instance may have changed state while we waited. */ + if (rng->status != DRBG_OK) { + if (excl) { + RngExclExit(rng); + } + return RNG_FAILURE_E; + } +#endif + #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) if (rng->pid != getpid()) { rng->pid = getpid(); ret = PollAndReSeed(rng); if (ret != DRBG_SUCCESS) { rng->status = DRBG_FAILED; + #ifndef WC_NO_DRBG_THREAD_SAFE + if (excl) { + RngExclExit(rng); + } + #endif return RNG_FAILURE_E; } } @@ -2636,6 +2760,12 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) ret = RNG_FAILURE_E; rng->status = DRBG_FAILED; } + +#ifndef WC_NO_DRBG_THREAD_SAFE + if (excl) { + RngExclExit(rng); + } +#endif #else /* if we get here then there is an RNG configuration error */ @@ -2791,6 +2921,11 @@ int wc_FreeRng(WC_RNG* rng) } #endif +#ifndef WC_NO_DRBG_THREAD_SAFE + /* Last, so a WC_RNG re-instantiated in place does not start out marked. */ + WOLFSSL_ATOMIC_STORE(rng->excl, WC_RNG_EXCL_FREE); +#endif + return ret; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 0556fd5351..94dede06db 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -925,6 +925,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t srp_test(void); #endif #ifndef WC_NO_RNG WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_test(void); +#ifdef WC_TEST_THREADSAFE_DRBG +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_thread_test(void); +#endif #ifdef WC_RNG_BANK_SUPPORT WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void); #endif @@ -2558,6 +2561,12 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_FAIL("RANDOM test failed!\n", ret); else TEST_PASS("RANDOM test passed!\n"); +#ifdef WC_TEST_THREADSAFE_DRBG + if ((ret = random_thread_test()) != 0) + TEST_FAIL("RNGTHRD test failed!\n", ret); + else + TEST_PASS("RNGTHRD test passed!\n"); +#endif #ifdef WC_RNG_BANK_SUPPORT if ((ret = random_bank_test()) != 0) TEST_FAIL("RNGBANK test failed!\n", ret); @@ -26811,6 +26820,131 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_test(void) #endif /* !HAVE_HASHDRBG || CUSTOM_RAND_GENERATE_BLOCK || HAVE_INTEL_RDRAND */ +#ifdef WC_TEST_THREADSAFE_DRBG + +/* Exercises the thread-safe DRBG: concurrent draws from one shared WC_RNG. + * Needs both the feature and the portable thread API, so it is skipped + * where either is absent. */ + +#ifndef WC_RNG_THREAD_TEST_THREADS + #define WC_RNG_THREAD_TEST_THREADS 4 +#endif +#ifndef WC_RNG_THREAD_TEST_DRAWS + #define WC_RNG_THREAD_TEST_DRAWS 96 +#endif +#ifndef WC_RNG_THREAD_TEST_BLKSZ + #define WC_RNG_THREAD_TEST_BLKSZ 32 +#endif + +#define WC_RNG_THREAD_TEST_BLOCKS \ + (WC_RNG_THREAD_TEST_THREADS * WC_RNG_THREAD_TEST_DRAWS) + +struct rng_thread_test_args { + WC_RNG* rng; + byte* out; /* this worker's slice, DRAWS * BLKSZ bytes */ + int ret; +}; + +static THREAD_RETURN WOLFSSL_THREAD rng_thread_test_worker(void* argp) +{ + struct rng_thread_test_args* args = (struct rng_thread_test_args*)argp; + int i; + + for (i = 0; i < WC_RNG_THREAD_TEST_DRAWS; i++) { + int ret = wc_RNG_GenerateBlock(args->rng, + args->out + ((size_t)i * WC_RNG_THREAD_TEST_BLKSZ), + WC_RNG_THREAD_TEST_BLKSZ); + if (ret != 0) { + args->ret = ret; + break; + } + } + + WOLFSSL_RETURN_FROM_THREAD(0); +} + +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_thread_test(void) +{ + THREAD_TYPE threads[WC_RNG_THREAD_TEST_THREADS]; + struct rng_thread_test_args args[WC_RNG_THREAD_TEST_THREADS]; + WC_RNG rng; + byte* out = NULL; + int rng_inited = 0; + int started = 0; + int nblocks; + int i, j; + wc_test_ret_t ret; + + WOLFSSL_ENTER("random_thread_test"); + + out = (byte*)XMALLOC((size_t)WC_RNG_THREAD_TEST_BLOCKS * + WC_RNG_THREAD_TEST_BLKSZ, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER); + if (out == NULL) { + /* Opportunistic check: a target too small to hold the buffer is not + * evidence of a DRBG defect, so skip rather than report failure. */ + return 0; + } + + ret = wc_InitRng_ex(&rng, HEAP_HINT, devId); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out_free); + rng_inited = 1; + + for (i = 0; i < WC_RNG_THREAD_TEST_THREADS; i++) { + args[i].rng = &rng; + args[i].out = out + ((size_t)i * WC_RNG_THREAD_TEST_DRAWS * + WC_RNG_THREAD_TEST_BLKSZ); + args[i].ret = 0; + if (wolfSSL_NewThread(&threads[i], &rng_thread_test_worker, + &args[i]) != 0) { + /* Out of thread resources; run with the ones we have. */ + break; + } + started++; + } + + for (i = 0; i < started; i++) + (void)wolfSSL_JoinThread(threads[i]); + + /* Fewer than two workers means nothing ran concurrently, so there was + * nothing for this test to observe. Skip rather than report failure. */ + if (started < 2) + goto out_free; + + for (i = 0; i < started; i++) { + if (args[i].ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(args[i].ret), out_free); + } + + /* All-pairs rather than a sort: no XQSORT dependency, and the block count + * makes the quadratic scan negligible. */ + nblocks = started * WC_RNG_THREAD_TEST_DRAWS; + for (i = 1; i < nblocks; i++) { + for (j = 0; j < i; j++) { + if (XMEMCMP(out + ((size_t)i * WC_RNG_THREAD_TEST_BLKSZ), + out + ((size_t)j * WC_RNG_THREAD_TEST_BLKSZ), + WC_RNG_THREAD_TEST_BLKSZ) == 0) { + ERROR_OUT(WC_TEST_RET_ENC_NC, out_free); + } + } + } + +out_free: + + if (rng_inited) { + int free_ret = wc_FreeRng(&rng); + if ((ret == 0) && (free_ret != 0)) + ret = WC_TEST_RET_ENC_EC(free_ret); + } + + XFREE(out, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + + return ret; +} + +#endif /* WC_TEST_THREADSAFE_DRBG */ + #ifdef WC_RNG_BANK_SUPPORT static char *rng_bank_affinity_lock_lock; diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index ab1f22e85b..73882da9bf 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -38,6 +38,31 @@ #include #include +#ifndef WC_NO_RNG + /* for WC_NO_DRBG_THREAD_SAFE, which random.h may elect on. */ + #include +#endif + +/* The thread-safe DRBG test drives one instance from several threads, so it + * needs more than the feature itself: + * - wolfSSL_NewThread()/wolfSSL_JoinThread(), which are only implemented for + * a subset of targets (notably not WOLFSSL_LINUXKM, which builds this file + * into the kernel module), so require one that has them rather than + * assuming !SINGLE_THREADED is enough; + * - a general-purpose heap for the comparison buffer, which rules out + * WOLFSSL_NO_MALLOC and WOLFSSL_STATIC_MEMORY; + * - a random.c/random.h pair that actually carries the feature. A FIPS or + * selftest build checks out locked copies of both from the module's tag, + * and those predate it, so HAVE_FIPS and HAVE_SELFTEST are excluded + * outright. Note the locked random.h also never defines + * WC_NO_DRBG_THREAD_SAFE, so the test above cannot detect this itself. */ +#if !defined(WC_NO_DRBG_THREAD_SAFE) && \ + !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) && \ + !defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY) && \ + (defined(WOLFSSL_PTHREADS) || \ + (defined(USE_WINDOWS_API) && !defined(_WIN32_WCE))) + #define WC_TEST_THREADSAFE_DRBG +#endif #ifdef HAVE_STACK_SIZE THREAD_RETURN WOLFSSL_THREAD wolfcrypt_test(void* args); @@ -251,6 +276,9 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t srp_test(void); #endif #ifndef WC_NO_RNG extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_test(void); +#ifdef WC_TEST_THREADSAFE_DRBG +extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_thread_test(void); +#endif #ifdef WC_RNG_BANK_SUPPORT extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void); #endif diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index d9c5a9289b..02c0922e72 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -79,6 +79,20 @@ /* avoid redefinition of structs */ +/* A build with no DRBG, no atomics, or no threads has nothing to implement + * this with, so elect it off here rather than making every use site restate + * the requirements. Kept ahead of the FIPS-version guard below: the use + * sites test !defined(WC_NO_DRBG_THREAD_SAFE), so this must be evaluated on + * every path that reaches them, including the one where the WC_RNG defined + * below is not the struct in use. */ +#if (!defined(HAVE_HASHDRBG) || defined(CUSTOM_RAND_GENERATE_BLOCK) || \ + defined(SINGLE_THREADED) || defined(WOLFSSL_NO_ATOMICS) || \ + (defined(HAVE_FIPS) && \ + !(defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 2)))) && \ + !defined(WC_NO_DRBG_THREAD_SAFE) + #define WC_NO_DRBG_THREAD_SAFE +#endif + #if !defined(HAVE_FIPS) || \ (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 2)) @@ -357,6 +371,24 @@ enum wc_RngHealthState { WOLF_ENUM_DUMMY_LAST_ELEMENT(wc_RngHealthState) }; +/* Thread-safe DRBG -- enabled by default. + * + * Serializes the generate and reseed path of a single WC_RNG so that one + * instance can be shared across threads without an external lock of its own. + * + * Define WC_NO_DRBG_THREAD_SAFE to opt out where an instance is only ever used + * by one thread at a time and the per-call atomic is not wanted. This is an + * election, independent of SINGLE_THREADED -- a multi-threaded build that keeps + * its WC_RNGs thread-local can opt out and keep the smaller struct. */ + +#ifndef WC_NO_DRBG_THREAD_SAFE + #define WC_RNG_EXCL_FREE 0 + #define WC_RNG_EXCL_HELD 1 + /* Stored once by an owner that already supplies exclusivity for this + * instance, which then takes no flag of its own. Nothing here sets it. */ + #define WC_RNG_EXCL_OWNER 2 +#endif + /* RNG context */ struct WC_RNG { struct OS_Seed seed; @@ -417,6 +449,12 @@ struct WC_RNG { #endif /* WC_RNG_BANK_SUPPORT || HAVE_HASHDRBG */ +#ifndef WC_NO_DRBG_THREAD_SAFE + /* Serializes this instance's DRBG generate/reseed path. Outside the union + * above, and left FREE by the _InitRng() XMEMSET. */ + wolfSSL_Atomic_Int excl; +#endif + #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) pid_t pid; #endif diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index 3cce9b4c70..107cf61ef6 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -2415,6 +2415,41 @@ WOLFSSL_API word32 CheckRunTimeSettings(void); #ifndef WC_RELAX_LONG_LOOP #define WC_RELAX_LONG_LOOP() WC_DO_NOTHING #endif + +/* Yield hook for the DRBG acquire spin in RngExclEnter(). Deliberately + * separate from WC_RELAX_LONG_LOOP(), which also backs the + * SAVE_/RESTORE_NO_VECTOR_REGISTERS fallbacks below and must stay a no-op + * there -- those expand at hundreds of crypto call sites, and a scheduler + * yield does not belong in them. + * + * On a preemptive general-purpose OS a bare spin only wastes cycles, but on a + * uniprocessor RTOS a spinning higher-priority task can starve the + * lower-priority task it waits on, so use that RTOS's cooperative yield where + * one is in scope. SINGLE_THREADED is excluded first: wc_port.h omits these + * kernel headers in that configuration, so the yields have no declaration. + * Any port may define WC_SPIN_RELAX ahead of this. */ +#ifndef WC_SPIN_RELAX + #if defined(SINGLE_THREADED) + #define WC_SPIN_RELAX() WC_DO_NOTHING + #elif defined(WOLFSSL_ZEPHYR) + #define WC_SPIN_RELAX() k_yield() + #elif (defined(FREERTOS) || defined(FREERTOS_TCP) || \ + defined(WOLFSSL_SAFERTOS)) && defined(taskYIELD) + /* taskYIELD() is a macro from FreeRTOS task.h, which none of these + * paths include themselves, so key off the macro rather than assume + * it: a build without task.h falls through to the default. */ + #define WC_SPIN_RELAX() taskYIELD() + #elif defined(THREADX) + #define WC_SPIN_RELAX() tx_thread_relinquish() + #elif defined(WOLFSSL_TIRTOS) + #define WC_SPIN_RELAX() Task_yield() + #elif defined(RTTHREAD) + #define WC_SPIN_RELAX() rt_thread_yield() + #else + /* Ports that supply a real relax hook (linuxkm) keep it here. */ + #define WC_SPIN_RELAX() WC_RELAX_LONG_LOOP() + #endif +#endif #ifndef WC_CHECK_FOR_INTR_SIGNALS #define WC_CHECK_FOR_INTR_SIGNALS() 0 #ifndef SAVE_NO_VECTOR_REGISTERS