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_;