aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier Blazquez <[email protected]>2022-09-10 13:09:45 -0700
committerJack O'Connor <[email protected]>2023-07-21 19:18:40 -0700
commit12823b87604cbb7bcd0e5cdc347a53f80b2a617c (patch)
tree5f8dd3544d035ad3ffe769170b792c3846edb650
parente302cdf36f1a8f3b9833b151c6c57964053cd587 (diff)
blake3_dispatch: Fix race condition initializing g_cpu_features.
If multiple threads try to compute a hash simultaneously before the library has been used for the first time, the logic in get_cpu_features that detects CPU features will write to g_cpu_features without synchronization, which is a race condition and flagged by ThreadSanitizer. This change marks g_cpu_features as an atomic variable to address the race condition.
-rw-r--r--c/blake3_dispatch.c39
1 files changed, 34 insertions, 5 deletions
diff --git a/c/blake3_dispatch.c b/c/blake3_dispatch.c
index 2ab0093..af6c3da 100644
--- a/c/blake3_dispatch.c
+++ b/c/blake3_dispatch.c
@@ -6,6 +6,7 @@
#if defined(IS_X86)
#if defined(_MSC_VER)
+#include <Windows.h>
#include <intrin.h>
#elif defined(__GNUC__)
#include <immintrin.h>
@@ -14,6 +15,32 @@
#endif
#endif
+#if !defined(BLAKE3_ATOMICS)
+#if defined(__has_include)
+#if __has_include(<stdatomic.h>) && !defined(_MSC_VER)
+#define BLAKE3_ATOMICS 1
+#else
+#define BLAKE3_ATOMICS 0
+#endif /* __has_include(<stdatomic.h>) && !defined(_MSC_VER) */
+#else
+#define BLAKE3_ATOMICS 0
+#endif /* defined(__has_include) */
+#endif /* BLAKE3_ATOMICS */
+
+#if BLAKE3_ATOMICS
+#define ATOMIC_INT _Atomic int
+#define ATOMIC_LOAD(x) x
+#define ATOMIC_STORE(x, y) x = y
+#elif defined(_MSC_VER)
+#define ATOMIC_INT LONG
+#define ATOMIC_LOAD(x) InterlockedOr(&x, 0)
+#define ATOMIC_STORE(x, y) InterlockedExchange(&x, y)
+#else
+#define ATOMIC_INT int
+#define ATOMIC_LOAD(x) x
+#define ATOMIC_STORE(x, y) x = y
+#endif
+
#define MAYBE_UNUSED(x) (void)((x))
#if defined(IS_X86)
@@ -76,7 +103,7 @@ enum cpu_feature {
#if !defined(BLAKE3_TESTING)
static /* Allow the variable to be controlled manually for testing */
#endif
- enum cpu_feature g_cpu_features = UNDEFINED;
+ ATOMIC_INT g_cpu_features = UNDEFINED;
#if !defined(BLAKE3_TESTING)
static
@@ -84,14 +111,16 @@ static
enum cpu_feature
get_cpu_features(void) {
- if (g_cpu_features != UNDEFINED) {
- return g_cpu_features;
+ /* If TSAN detects a data race here, try compiling with -DBLAKE3_ATOMICS=1 */
+ enum cpu_feature features = ATOMIC_LOAD(g_cpu_features);
+ if (features != UNDEFINED) {
+ return features;
} else {
#if defined(IS_X86)
uint32_t regs[4] = {0};
uint32_t *eax = &regs[0], *ebx = &regs[1], *ecx = &regs[2], *edx = &regs[3];
(void)edx;
- enum cpu_feature features = 0;
+ features = 0;
cpuid(regs, 0);
const int max_id = *eax;
cpuid(regs, 1);
@@ -124,7 +153,7 @@ static
}
}
}
- g_cpu_features = features;
+ ATOMIC_STORE(g_cpu_features, features);
return features;
#else
/* How to detect NEON? */