Commit ef499708 for tesseract
commit ef499708e19c9132197b63f911c79cbbd87e5723
Author: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Date: Sun Aug 9 20:43:37 2026 +0000
Fix recursive static initialization in SIMDDetect::GetDetector()
The SIMDDetect constructor called Update(), which called GetDetector(),
which tried to re-enter initialization of its own function-local static
before construction finished (undefined behavior / recursive_init_error).
Fix by moving the DOTPRODUCT environment variable override out of the
constructor and into GetDetector(), applied via a one-time static IIFE
after the detector object is fully constructed. The constructor no longer
calls Update(), eliminating the recursion.
Also convert Is*Available() accessors to use GetDetector() and remove the
old class-static detector member, matching the PR's intended design.
Co-authored-by: stweil <6734573+stweil@users.noreply.github.com>
diff --git a/src/arch/simddetect.cpp b/src/arch/simddetect.cpp
index 084a2b92..324bba18 100644
--- a/src/arch/simddetect.cpp
+++ b/src/arch/simddetect.cpp
@@ -88,7 +88,29 @@ DotProductFunction DotProduct;
static STRING_VAR(dotproduct, "auto", "Function used for calculation of dot product");
-SIMDDetect SIMDDetect::detector;
+const SIMDDetect &SIMDDetect::GetDetector() {
+ // A function local static variable is initialized when the function is
+ // first called, so there is no problem with the order of initialization.
+ static const SIMDDetect detector;
+ // Apply DOTPRODUCT environment variable override after construction.
+ // Using a static flag ensures this only runs once, and defers the override
+ // until after the detector is fully constructed to avoid recursive
+ // re-entry of GetDetector() during static initialization.
+ [[maybe_unused]] static bool env_override_applied = []() -> bool {
+ const char *dotproduct_env = getenv("DOTPRODUCT");
+ if (dotproduct_env != nullptr) {
+ dotproduct = dotproduct_env;
+ Update();
+ }
+ return true;
+ }();
+ return detector;
+}
+
+// Force the construction of the detector at program startup, so that the dot
+// product function is set even when the library is used without calling
+// SIMDDetect::Update() or any of the Is*Available() functions.
+static const SIMDDetect &detector_init = SIMDDetect::GetDetector();
#if defined(__aarch64__)
// ARMv8 always has NEON.
@@ -285,15 +307,11 @@ SIMDDetect::SIMDDetect() {
#endif
}
- const char *dotproduct_env = getenv("DOTPRODUCT");
- if (dotproduct_env != nullptr) {
- // Override automatic settings by value from environment variable.
- dotproduct = dotproduct_env;
- Update();
- }
}
void SIMDDetect::Update() {
+ // Ensure that the detector is initialized before using it.
+ (void)GetDetector();
// Select code for calculation of dot product based on the
// value of the config variable if that value is not empty.
const char *dotproduct_method = "generic";
diff --git a/src/arch/simddetect.h b/src/arch/simddetect.h
index 5d4eb338..e4fedace 100644
--- a/src/arch/simddetect.h
+++ b/src/arch/simddetect.h
@@ -33,51 +33,54 @@ class SIMDDetect {
public:
// Returns true if AVX is available on this system.
static inline bool IsAVXAvailable() {
- return detector.avx_available_;
+ return GetDetector().avx_available_;
}
// Returns true if AVX2 (integer support) is available on this system.
static inline bool IsAVX2Available() {
- return detector.avx2_available_;
+ return GetDetector().avx2_available_;
}
// Returns true if AVX512 Foundation (float) is available on this system.
static inline bool IsAVX512FAvailable() {
- return detector.avx512F_available_;
+ return GetDetector().avx512F_available_;
}
// Returns true if AVX512 integer is available on this system.
static inline bool IsAVX512BWAvailable() {
- return detector.avx512BW_available_;
+ return GetDetector().avx512BW_available_;
}
// Returns true if AVX512 Vector Neural Network Instructions are available.
static inline bool IsAVX512VNNIAvailable() {
- return detector.avx512VNNI_available_;
+ return GetDetector().avx512VNNI_available_;
}
// Returns true if FMA is available on this system.
static inline bool IsFMAAvailable() {
- return detector.fma_available_;
+ return GetDetector().fma_available_;
}
// Returns true if SSE4.1 is available on this system.
static inline bool IsSSEAvailable() {
- return detector.sse_available_;
+ return GetDetector().sse_available_;
}
// Returns true if NEON is available on this system.
static inline bool IsNEONAvailable() {
- return detector.neon_available_;
+ return GetDetector().neon_available_;
}
// Returns true if RVV is available on this system.
static inline bool IsRVVAvailable() {
- return detector.rvv_available_;
+ return GetDetector().rvv_available_;
}
// Update settings after config variable was set.
static TESS_API void Update();
+ // Returns the singleton detector instance.
+ // The detector is constructed on first use (a function local static
+ // variable), which avoids an unspecified order of static initialization
+ // with the static members of other classes (like IntSimdMatrix).
+ static const SIMDDetect &GetDetector();
private:
// Constructor, must set all static member variables.
SIMDDetect();
private:
- // Singleton.
- static SIMDDetect detector;
// If true, then AVX has been detected.
static TESS_API bool avx_available_;
static TESS_API bool avx2_available_;