From 0ae69ec30376c7d88f0e9f80a25764a2e590e34a Mon Sep 17 00:00:00 2001 From: RJ Ascani Date: Wed, 18 Feb 2026 17:32:39 -0800 Subject: [PATCH] Auto-initialize PAL when functions are called before et_pal_init() Replace the debug-only assertion behavior with automatic initialization. When et_pal_current_ticks() or et_pal_emit_log_message() are called before et_pal_init(), they now call et_pal_init() automatically instead of aborting in debug builds or producing undefined behavior in release. Uses std::call_once with a function-local static flag for thread-safe one-time initialization. Rename executor_pal_death_test to executor_pal_autoinit_test since we now test auto-initialization behavior rather than death on missing init. Co-authored-by: Claude --- runtime/platform/default/android.cpp | 46 +++--------------- runtime/platform/default/posix.cpp | 47 +++---------------- runtime/platform/test/CMakeLists.txt | 2 +- ...est.cpp => executor_pal_autoinit_test.cpp} | 23 +++------ runtime/platform/test/targets.bzl | 4 +- 5 files changed, 22 insertions(+), 100 deletions(-) rename runtime/platform/test/{executor_pal_death_test.cpp => executor_pal_autoinit_test.cpp} (52%) diff --git a/runtime/platform/default/android.cpp b/runtime/platform/default/android.cpp index fdaf7db3b1b..9b822e31834 100644 --- a/runtime/platform/default/android.cpp +++ b/runtime/platform/default/android.cpp @@ -21,44 +21,13 @@ #include #include #include +#include #include -/** - * On debug builds, ensure that `et_pal_init` has been called before - * other PAL functions which depend on initialization. - */ -#ifdef NDEBUG - -/** - * Assert that the PAL has been initialized. - */ -#define _ASSERT_PAL_INITIALIZED() ((void)0) - -#else // NDEBUG - -/** - * Assert that the PAL has been initialized. - */ -#define _ASSERT_PAL_INITIALIZED() \ - do { \ - if (!initialized) { \ - __android_log_print( \ - ANDROID_LOG_FATAL, \ - "ExecuTorch", \ - "ExecuTorch PAL must be initialized before call to %s()", \ - ET_FUNCTION); \ - } \ - } while (0) - -#endif // NDEBUG - /// Start time of the system (used to zero the system timestamp). static std::chrono::time_point systemStartTime; -/// Flag set to true if the PAL has been successfully initialized. -static bool initialized = false; - /** * Initialize the platform abstraction layer. * @@ -69,12 +38,9 @@ static bool initialized = false; #pragma weak et_pal_init #endif // _MSC_VER void et_pal_init(void) { - if (initialized) { - return; - } - - systemStartTime = std::chrono::steady_clock::now(); - initialized = true; + static std::once_flag init_flag; + std::call_once( + init_flag, []() { systemStartTime = std::chrono::steady_clock::now(); }); } /** @@ -97,7 +63,7 @@ ET_NORETURN void et_pal_abort(void) { #pragma weak et_pal_current_ticks #endif // _MSC_VER et_timestamp_t et_pal_current_ticks(void) { - _ASSERT_PAL_INITIALIZED(); + et_pal_init(); auto systemCurrentTime = std::chrono::steady_clock::now(); return std::chrono::duration_cast( systemCurrentTime - systemStartTime) @@ -143,7 +109,7 @@ void et_pal_emit_log_message( ET_UNUSED size_t line, const char* message, ET_UNUSED size_t length) { - _ASSERT_PAL_INITIALIZED(); + et_pal_init(); int android_log_level = ANDROID_LOG_UNKNOWN; if (level == 'D') { diff --git a/runtime/platform/default/posix.cpp b/runtime/platform/default/posix.cpp index 837ffd02833..50f4ae4116c 100644 --- a/runtime/platform/default/posix.cpp +++ b/runtime/platform/default/posix.cpp @@ -27,48 +27,16 @@ #include #include #include +#include #include // The FILE* to write logs to. #define ET_LOG_OUTPUT_FILE stderr -/** - * On debug builds, ensure that `et_pal_init` has been called before - * other PAL functions which depend on initialization. - */ -#ifdef NDEBUG - -/** - * Assert that the PAL has been initialized. - */ -#define _ASSERT_PAL_INITIALIZED() ((void)0) - -#else // NDEBUG - -/** - * Assert that the PAL has been initialized. - */ -#define _ASSERT_PAL_INITIALIZED() \ - do { \ - if (!initialized) { \ - fprintf( \ - ET_LOG_OUTPUT_FILE, \ - "ExecuTorch PAL must be initialized before call to %s()", \ - ET_FUNCTION); \ - fflush(ET_LOG_OUTPUT_FILE); \ - et_pal_abort(); \ - } \ - } while (0) - -#endif // NDEBUG - /// Start time of the system (used to zero the system timestamp). static std::chrono::time_point systemStartTime; -/// Flag set to true if the PAL has been successfully initialized. -static bool initialized = false; - /** * Initialize the platform abstraction layer. * @@ -79,12 +47,9 @@ static bool initialized = false; #pragma weak et_pal_init #endif // _MSC_VER void et_pal_init(void) { - if (initialized) { - return; - } - - systemStartTime = std::chrono::steady_clock::now(); - initialized = true; + static std::once_flag init_flag; + std::call_once( + init_flag, []() { systemStartTime = std::chrono::steady_clock::now(); }); } /** @@ -107,7 +72,7 @@ ET_NORETURN void et_pal_abort(void) { #pragma weak et_pal_current_ticks #endif // _MSC_VER et_timestamp_t et_pal_current_ticks(void) { - _ASSERT_PAL_INITIALIZED(); + et_pal_init(); auto systemCurrentTime = std::chrono::steady_clock::now(); return std::chrono::duration_cast( systemCurrentTime - systemStartTime) @@ -153,7 +118,7 @@ void et_pal_emit_log_message( size_t line, const char* message, ET_UNUSED size_t length) { - _ASSERT_PAL_INITIALIZED(); + et_pal_init(); // Not all platforms have ticks == nanoseconds, but this one does. timestamp /= 1000; // To microseconds diff --git a/runtime/platform/test/CMakeLists.txt b/runtime/platform/test/CMakeLists.txt index 8fa9b325368..769bb702653 100644 --- a/runtime/platform/test/CMakeLists.txt +++ b/runtime/platform/test/CMakeLists.txt @@ -29,7 +29,7 @@ et_cxx_test( executor_pal_static_runtime_override_test.cpp ) -et_cxx_test(platform_death_test SOURCES executor_pal_death_test.cpp) +et_cxx_test(platform_autoinit_test SOURCES executor_pal_autoinit_test.cpp) # No weak function symbols on Windows/MSVC, thus PAL intercept doesn't work. # Skip logging tests in Release mode. diff --git a/runtime/platform/test/executor_pal_death_test.cpp b/runtime/platform/test/executor_pal_autoinit_test.cpp similarity index 52% rename from runtime/platform/test/executor_pal_death_test.cpp rename to runtime/platform/test/executor_pal_autoinit_test.cpp index ca682daaf0a..76ac575fc99 100644 --- a/runtime/platform/test/executor_pal_death_test.cpp +++ b/runtime/platform/test/executor_pal_autoinit_test.cpp @@ -9,24 +9,15 @@ #include #include -#include -TEST(ExecutorPalTest, UninitializedPalDeath) { - // Check for assertion failure on debug builds. +TEST(ExecutorPalTest, AutoInitialization) { + // Functions should auto-initialize and work without explicit et_pal_init() + et_timestamp_t time = et_pal_current_ticks(); + EXPECT_GE(time, 0); -#ifndef NDEBUG - - ET_EXPECT_DEATH_NO_PAL_INIT( - { et_pal_current_ticks(); }, "PAL must be initialized"); - - ET_EXPECT_DEATH_NO_PAL_INIT( - { - et_pal_emit_log_message( - 0, et_pal_log_level_t::kFatal, "", "", 0, "", 0); - }, - "PAL must be initialized"); - -#endif // !defined(NDEBUG) + // Logging should also work + et_pal_emit_log_message( + 0, et_pal_log_level_t::kInfo, "test", "test", 0, "auto-init test", 14); } /// Override the default weak main declaration. diff --git a/runtime/platform/test/targets.bzl b/runtime/platform/test/targets.bzl index a5d77ef5a4e..265ed639f3f 100644 --- a/runtime/platform/test/targets.bzl +++ b/runtime/platform/test/targets.bzl @@ -19,9 +19,9 @@ def define_common_targets(): ) runtime.cxx_test( - name = "platform_death_test", + name = "platform_autoinit_test", srcs = [ - "executor_pal_death_test.cpp", + "executor_pal_autoinit_test.cpp", ], deps = [ "//executorch/runtime/core:core",