From df8c6a8ce5cf12baabe5e7c9213aaeeffb18bd82 Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Mon, 4 May 2026 17:59:42 -0700 Subject: [PATCH 1/3] Add support for freeing cpuinfo memory --- src/cpuinfo/internal-api.h | 3 ++ src/init.c | 98 +++++++++++++++++++++++++++++++++++++- src/x86/windows/init.c | 3 ++ 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/cpuinfo/internal-api.h b/src/cpuinfo/internal-api.h index d84b26a8..e12e2dff 100644 --- a/src/cpuinfo/internal-api.h +++ b/src/cpuinfo/internal-api.h @@ -45,6 +45,9 @@ extern CPUINFO_INTERNAL struct cpuinfo_uarch_info cpuinfo_global_uarch; extern CPUINFO_INTERNAL uint32_t cpuinfo_linux_cpu_max; extern CPUINFO_INTERNAL const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map; extern CPUINFO_INTERNAL const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map; +#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 +extern CPUINFO_INTERNAL const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map; +#endif #endif CPUINFO_PRIVATE void cpuinfo_x86_mach_init(void); diff --git a/src/init.c b/src/init.c index 81d5721c..fdda7de2 100644 --- a/src/init.c +++ b/src/init.c @@ -2,6 +2,7 @@ #include #elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__) #include +#include #endif #include @@ -14,10 +15,13 @@ #if defined(_WIN32) || defined(__CYGWIN__) static INIT_ONCE init_guard = INIT_ONCE_STATIC_INIT; +static INIT_ONCE deinit_guard = INIT_ONCE_STATIC_INIT; #elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__) static pthread_once_t init_guard = PTHREAD_ONCE_INIT; +static pthread_once_t deinit_guard = PTHREAD_ONCE_INIT; #else static bool init_guard = false; +static bool deinit_guard = false; #endif bool CPUINFO_ABI cpuinfo_initialize(void) { @@ -64,4 +68,96 @@ bool CPUINFO_ABI cpuinfo_initialize(void) { return cpuinfo_is_initialized; } -void CPUINFO_ABI cpuinfo_deinitialize(void) {} +/* + * Cleanup support for platforms where cpuinfo may be dynamically loaded/unloaded. + * ARM Linux and Emscripten are excluded because they use static storage for some + * globals (packages, L3 cache) that cannot be safely freed. + */ +#if defined(_WIN32) || defined(__CYGWIN__) +#define CPUINFO_FREE(ptr) \ + do { \ + if (ptr) \ + HeapFree(GetProcessHeap(), 0, (void*)(ptr)); \ + } while (0) +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) +#define CPUINFO_FREE(ptr) free(ptr) +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) + +static void cpuinfo_cleanup(void) { + if (!cpuinfo_is_initialized) { + return; + } + + cpuinfo_is_initialized = false; + + CPUINFO_FREE(cpuinfo_processors); + cpuinfo_processors = NULL; + cpuinfo_processors_count = 0; + + CPUINFO_FREE(cpuinfo_cores); + cpuinfo_cores = NULL; + cpuinfo_cores_count = 0; + + CPUINFO_FREE(cpuinfo_clusters); + cpuinfo_clusters = NULL; + cpuinfo_clusters_count = 0; + + CPUINFO_FREE(cpuinfo_packages); + cpuinfo_packages = NULL; + cpuinfo_packages_count = 0; + + /* ARM64 Windows allocates all cache levels as a single contiguous block. + * All other supported platforms allocate each level separately. */ +#if CPUINFO_ARCH_ARM64 && defined(_WIN32) + CPUINFO_FREE(cpuinfo_cache[cpuinfo_cache_level_1i]); +#else + for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { + CPUINFO_FREE(cpuinfo_cache[lvl]); + } +#endif + for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { + cpuinfo_cache[lvl] = NULL; + cpuinfo_cache_count[lvl] = 0; + } + cpuinfo_max_cache_size = 0; + +#if (CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64) + CPUINFO_FREE(cpuinfo_uarchs); + cpuinfo_uarchs = NULL; + cpuinfo_uarchs_count = 0; +#endif + +#ifdef __linux__ + CPUINFO_FREE(cpuinfo_linux_cpu_to_processor_map); + cpuinfo_linux_cpu_to_processor_map = NULL; + + CPUINFO_FREE(cpuinfo_linux_cpu_to_core_map); + cpuinfo_linux_cpu_to_core_map = NULL; + +#if (CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64) + CPUINFO_FREE(cpuinfo_linux_cpu_to_uarch_index_map); + cpuinfo_linux_cpu_to_uarch_index_map = NULL; +#endif + + cpuinfo_linux_cpu_max = 0; +#endif +} + +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +static BOOL CALLBACK cpuinfo_deinit_once(PINIT_ONCE once, PVOID param, PVOID* ctx) { + cpuinfo_cleanup(); + return TRUE; +} +#endif + +void CPUINFO_ABI cpuinfo_deinitialize(void) { +#if defined(_WIN32) || defined(__CYGWIN__) + InitOnceExecuteOnce(&deinit_guard, cpuinfo_deinit_once, NULL, NULL); +#elif (defined(__linux__) && !CPUINFO_ARCH_ARM) || defined(__APPLE__) || defined(__FreeBSD__) + pthread_once(&deinit_guard, cpuinfo_cleanup); +#endif +} diff --git a/src/x86/windows/init.c b/src/x86/windows/init.c index d2332802..3f4c69bd 100644 --- a/src/x86/windows/init.c +++ b/src/x86/windows/init.c @@ -667,5 +667,8 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV if (l4 != NULL) { HeapFree(heap, 0, l4); } + if (processor_infos != NULL) { + HeapFree(heap, 0, processor_infos); + } return TRUE; } From 8ff94bd9a22648c346cf6d91baa0ecd2fc648a1c Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Wed, 6 May 2026 11:59:14 -0700 Subject: [PATCH 2/3] Add deinit tests --- CMakeLists.txt | 6 ++++ test/deinit.cc | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 test/deinit.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 072c9873..19bd339a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -815,6 +815,12 @@ IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_UNIT_TESTS) TARGET_LINK_LIBRARIES(init-test PRIVATE cpuinfo gtest gtest_main) ADD_TEST(NAME init-test COMMAND init-test) + ADD_EXECUTABLE(deinit-test test/deinit.cc) + CPUINFO_TARGET_ENABLE_CXX11(deinit-test) + CPUINFO_TARGET_RUNTIME_LIBRARY(deinit-test) + TARGET_LINK_LIBRARIES(deinit-test PRIVATE cpuinfo gtest gtest_main) + ADD_TEST(NAME deinit-test COMMAND deinit-test) + IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android") ADD_EXECUTABLE(get-current-test test/get-current.cc) CPUINFO_TARGET_ENABLE_CXX11(get-current-test) diff --git a/test/deinit.cc b/test/deinit.cc new file mode 100644 index 00000000..fb7cec70 --- /dev/null +++ b/test/deinit.cc @@ -0,0 +1,87 @@ +#include + +#include + +TEST(DEINITIALIZE, clears_processors) { + ASSERT_TRUE(cpuinfo_initialize()); + EXPECT_NE(0, cpuinfo_get_processors_count()); + EXPECT_TRUE(cpuinfo_get_processors()); + + cpuinfo_deinitialize(); + + EXPECT_EQ(0, cpuinfo_get_processors_count()); + EXPECT_FALSE(cpuinfo_get_processors()); +} + +TEST(DEINITIALIZE, clears_cores) { + ASSERT_TRUE(cpuinfo_initialize()); + EXPECT_NE(0, cpuinfo_get_cores_count()); + EXPECT_TRUE(cpuinfo_get_cores()); + + cpuinfo_deinitialize(); + + EXPECT_EQ(0, cpuinfo_get_cores_count()); + EXPECT_FALSE(cpuinfo_get_cores()); +} + +TEST(DEINITIALIZE, clears_clusters) { + ASSERT_TRUE(cpuinfo_initialize()); + EXPECT_NE(0, cpuinfo_get_clusters_count()); + EXPECT_TRUE(cpuinfo_get_clusters()); + + cpuinfo_deinitialize(); + + EXPECT_EQ(0, cpuinfo_get_clusters_count()); + EXPECT_FALSE(cpuinfo_get_clusters()); +} + +TEST(DEINITIALIZE, clears_packages) { + ASSERT_TRUE(cpuinfo_initialize()); + EXPECT_NE(0, cpuinfo_get_packages_count()); + EXPECT_TRUE(cpuinfo_get_packages()); + + cpuinfo_deinitialize(); + + EXPECT_EQ(0, cpuinfo_get_packages_count()); + EXPECT_FALSE(cpuinfo_get_packages()); +} + +TEST(DEINITIALIZE, clears_caches) { + ASSERT_TRUE(cpuinfo_initialize()); + + cpuinfo_deinitialize(); + + EXPECT_EQ(0, cpuinfo_get_l1i_caches_count()); + EXPECT_EQ(0, cpuinfo_get_l1d_caches_count()); + EXPECT_EQ(0, cpuinfo_get_l2_caches_count()); + EXPECT_EQ(0, cpuinfo_get_l3_caches_count()); + EXPECT_EQ(0, cpuinfo_get_l4_caches_count()); + EXPECT_FALSE(cpuinfo_get_l1i_caches()); + EXPECT_FALSE(cpuinfo_get_l1d_caches()); + EXPECT_FALSE(cpuinfo_get_l2_caches()); + EXPECT_FALSE(cpuinfo_get_l3_caches()); + EXPECT_FALSE(cpuinfo_get_l4_caches()); +} + +TEST(DEINITIALIZE, clears_uarchs) { + ASSERT_TRUE(cpuinfo_initialize()); + EXPECT_NE(0, cpuinfo_get_uarchs_count()); + EXPECT_TRUE(cpuinfo_get_uarchs()); + + cpuinfo_deinitialize(); + + EXPECT_EQ(0, cpuinfo_get_uarchs_count()); + EXPECT_FALSE(cpuinfo_get_uarchs()); +} + +TEST(DEINITIALIZE, double_deinit_is_safe) { + ASSERT_TRUE(cpuinfo_initialize()); + cpuinfo_deinitialize(); + cpuinfo_deinitialize(); + EXPECT_EQ(0, cpuinfo_get_processors_count()); +} + +TEST(DEINITIALIZE, before_init_is_safe) { + cpuinfo_deinitialize(); + EXPECT_EQ(0, cpuinfo_get_processors_count()); +} From f94ccafa04bd59fcd8dc2b1e48b1a0a75a8b0fcb Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Fri, 8 May 2026 16:21:52 -0700 Subject: [PATCH 3/3] Move the deinits to respective allocation routines --- CMakeLists.txt | 6 -- src/arm/linux/init.c | 40 +++++++ src/arm/mach/init.c | 29 +++++ src/arm/windows/init-by-logical-sys-info.c | 34 ++++++ src/cpuinfo/internal-api.h | 17 +++ src/emscripten/init.c | 32 ++++++ src/init.c | 119 +++++++-------------- src/riscv/linux/init.c | 33 ++++++ src/x86/freebsd/init.c | 27 +++++ src/x86/linux/init.c | 35 ++++++ src/x86/mach/init.c | 27 +++++ src/x86/windows/init.c | 31 ++++++ test/deinit.cc | 87 --------------- 13 files changed, 342 insertions(+), 175 deletions(-) delete mode 100644 test/deinit.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 19bd339a..072c9873 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -815,12 +815,6 @@ IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_UNIT_TESTS) TARGET_LINK_LIBRARIES(init-test PRIVATE cpuinfo gtest gtest_main) ADD_TEST(NAME init-test COMMAND init-test) - ADD_EXECUTABLE(deinit-test test/deinit.cc) - CPUINFO_TARGET_ENABLE_CXX11(deinit-test) - CPUINFO_TARGET_RUNTIME_LIBRARY(deinit-test) - TARGET_LINK_LIBRARIES(deinit-test PRIVATE cpuinfo gtest gtest_main) - ADD_TEST(NAME deinit-test COMMAND deinit-test) - IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android") ADD_EXECUTABLE(get-current-test test/get-current.cc) CPUINFO_TARGET_ENABLE_CXX11(get-current-test) diff --git a/src/arm/linux/init.c b/src/arm/linux/init.c index 1bf0014d..53e9a43d 100644 --- a/src/arm/linux/init.c +++ b/src/arm/linux/init.c @@ -1001,3 +1001,43 @@ void cpuinfo_arm_linux_init(void) { free(linux_cpu_to_core_map); free(linux_cpu_to_uarch_index_map); } + +void cpuinfo_arm_linux_deinit(void) { + free(cpuinfo_processors); + cpuinfo_processors = NULL; + cpuinfo_processors_count = 0; + + free(cpuinfo_cores); + cpuinfo_cores = NULL; + cpuinfo_cores_count = 0; + + free(cpuinfo_clusters); + cpuinfo_clusters = NULL; + cpuinfo_clusters_count = 0; + + /* cpuinfo_packages points to static storage (&package) — do not free */ + cpuinfo_packages = NULL; + cpuinfo_packages_count = 0; + + free(cpuinfo_uarchs); + cpuinfo_uarchs = NULL; + cpuinfo_uarchs_count = 0; + + for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { + free(cpuinfo_cache[lvl]); + cpuinfo_cache[lvl] = NULL; + cpuinfo_cache_count[lvl] = 0; + } + cpuinfo_max_cache_size = 0; + + free(cpuinfo_linux_cpu_to_processor_map); + cpuinfo_linux_cpu_to_processor_map = NULL; + + free(cpuinfo_linux_cpu_to_core_map); + cpuinfo_linux_cpu_to_core_map = NULL; + + free((void*)cpuinfo_linux_cpu_to_uarch_index_map); + cpuinfo_linux_cpu_to_uarch_index_map = NULL; + + cpuinfo_linux_cpu_max = 0; +} diff --git a/src/arm/mach/init.c b/src/arm/mach/init.c index 11de4ae3..eac431f7 100644 --- a/src/arm/mach/init.c +++ b/src/arm/mach/init.c @@ -755,3 +755,32 @@ void cpuinfo_arm_mach_init(void) { free(l2); free(l3); } + +void cpuinfo_arm_mach_deinit(void) { + free(cpuinfo_processors); + cpuinfo_processors = NULL; + cpuinfo_processors_count = 0; + + free(cpuinfo_cores); + cpuinfo_cores = NULL; + cpuinfo_cores_count = 0; + + free(cpuinfo_clusters); + cpuinfo_clusters = NULL; + cpuinfo_clusters_count = 0; + + free(cpuinfo_packages); + cpuinfo_packages = NULL; + cpuinfo_packages_count = 0; + + free(cpuinfo_uarchs); + cpuinfo_uarchs = NULL; + cpuinfo_uarchs_count = 0; + + for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { + free(cpuinfo_cache[lvl]); + cpuinfo_cache[lvl] = NULL; + cpuinfo_cache_count[lvl] = 0; + } + cpuinfo_max_cache_size = 0; +} diff --git a/src/arm/windows/init-by-logical-sys-info.c b/src/arm/windows/init-by-logical-sys-info.c index 815ecb77..2e44f8aa 100644 --- a/src/arm/windows/init-by-logical-sys-info.c +++ b/src/arm/windows/init-by-logical-sys-info.c @@ -372,6 +372,40 @@ bool cpu_info_init_by_logical_sys_info(const struct woa_chip_info* chip_info, co return result; } +BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { + HANDLE heap = GetProcessHeap(); + + HeapFree(heap, 0, cpuinfo_processors); + cpuinfo_processors = NULL; + cpuinfo_processors_count = 0; + + HeapFree(heap, 0, cpuinfo_packages); + cpuinfo_packages = NULL; + cpuinfo_packages_count = 0; + + HeapFree(heap, 0, cpuinfo_clusters); + cpuinfo_clusters = NULL; + cpuinfo_clusters_count = 0; + + HeapFree(heap, 0, cpuinfo_cores); + cpuinfo_cores = NULL; + cpuinfo_cores_count = 0; + + HeapFree(heap, 0, cpuinfo_uarchs); + cpuinfo_uarchs = NULL; + cpuinfo_uarchs_count = 0; + + /* Caches are allocated as a single contiguous block starting at l1i */ + HeapFree(heap, 0, cpuinfo_cache[cpuinfo_cache_level_1i]); + for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { + cpuinfo_cache[lvl] = NULL; + cpuinfo_cache_count[lvl] = 0; + } + cpuinfo_max_cache_size = 0; + + return TRUE; +} + static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) { uint32_t nr_of_processors = 0; diff --git a/src/cpuinfo/internal-api.h b/src/cpuinfo/internal-api.h index e12e2dff..9c158887 100644 --- a/src/cpuinfo/internal-api.h +++ b/src/cpuinfo/internal-api.h @@ -65,6 +65,23 @@ CPUINFO_PRIVATE void cpuinfo_arm_linux_init(void); CPUINFO_PRIVATE void cpuinfo_riscv_linux_init(void); CPUINFO_PRIVATE void cpuinfo_emscripten_init(void); +/* Platform-specific deinitialization functions. + * Each frees the globals allocated by its corresponding init function. */ +CPUINFO_PRIVATE void cpuinfo_x86_mach_deinit(void); +CPUINFO_PRIVATE void cpuinfo_x86_linux_deinit(void); +CPUINFO_PRIVATE void cpuinfo_x86_freebsd_deinit(void); +#if defined(_WIN32) || defined(__CYGWIN__) +#if CPUINFO_ARCH_ARM64 +CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context); +#else +CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_x86_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context); +#endif +#endif +CPUINFO_PRIVATE void cpuinfo_arm_mach_deinit(void); +CPUINFO_PRIVATE void cpuinfo_arm_linux_deinit(void); +CPUINFO_PRIVATE void cpuinfo_riscv_linux_deinit(void); +CPUINFO_PRIVATE void cpuinfo_emscripten_deinit(void); + CPUINFO_PRIVATE uint32_t cpuinfo_compute_max_cache_size(const struct cpuinfo_processor* processor); typedef void (*cpuinfo_processor_callback)(uint32_t); diff --git a/src/emscripten/init.c b/src/emscripten/init.c index c2393243..6c8210f2 100644 --- a/src/emscripten/init.c +++ b/src/emscripten/init.c @@ -286,3 +286,35 @@ void cpuinfo_emscripten_init(void) { free(l1d); free(l2); } + +void cpuinfo_emscripten_deinit(void) { + free(cpuinfo_processors); + cpuinfo_processors = NULL; + cpuinfo_processors_count = 0; + + free(cpuinfo_cores); + cpuinfo_cores = NULL; + cpuinfo_cores_count = 0; + + free(cpuinfo_clusters); + cpuinfo_clusters = NULL; + cpuinfo_clusters_count = 0; + + /* cpuinfo_packages points to static storage (&static_package) — do not free */ + cpuinfo_packages = NULL; + cpuinfo_packages_count = 0; + + /* L1i, L1d, L2 are dynamically allocated; L3 may point to static + * storage (&static_x86_l3) so only free levels that were calloc'd */ + free(cpuinfo_cache[cpuinfo_cache_level_1i]); + free(cpuinfo_cache[cpuinfo_cache_level_1d]); + free(cpuinfo_cache[cpuinfo_cache_level_2]); + /* cpuinfo_cache[cpuinfo_cache_level_3] may be &static_x86_l3 — do not free */ + for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { + cpuinfo_cache[lvl] = NULL; + cpuinfo_cache_count[lvl] = 0; + } + cpuinfo_max_cache_size = 0; + + cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; +} diff --git a/src/init.c b/src/init.c index fdda7de2..e4b684bd 100644 --- a/src/init.c +++ b/src/init.c @@ -2,7 +2,6 @@ #include #elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__) #include -#include #endif #include @@ -68,96 +67,52 @@ bool CPUINFO_ABI cpuinfo_initialize(void) { return cpuinfo_is_initialized; } -/* - * Cleanup support for platforms where cpuinfo may be dynamically loaded/unloaded. - * ARM Linux and Emscripten are excluded because they use static storage for some - * globals (packages, L3 cache) that cannot be safely freed. - */ -#if defined(_WIN32) || defined(__CYGWIN__) -#define CPUINFO_FREE(ptr) \ - do { \ - if (ptr) \ - HeapFree(GetProcessHeap(), 0, (void*)(ptr)); \ - } while (0) -#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) -#define CPUINFO_FREE(ptr) free(ptr) -#endif - -#if defined(_WIN32) || defined(__CYGWIN__) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) - -static void cpuinfo_cleanup(void) { +void CPUINFO_ABI cpuinfo_deinitialize(void) { if (!cpuinfo_is_initialized) { return; } - cpuinfo_is_initialized = false; - - CPUINFO_FREE(cpuinfo_processors); - cpuinfo_processors = NULL; - cpuinfo_processors_count = 0; - - CPUINFO_FREE(cpuinfo_cores); - cpuinfo_cores = NULL; - cpuinfo_cores_count = 0; - - CPUINFO_FREE(cpuinfo_clusters); - cpuinfo_clusters = NULL; - cpuinfo_clusters_count = 0; - - CPUINFO_FREE(cpuinfo_packages); - cpuinfo_packages = NULL; - cpuinfo_packages_count = 0; - - /* ARM64 Windows allocates all cache levels as a single contiguous block. - * All other supported platforms allocate each level separately. */ -#if CPUINFO_ARCH_ARM64 && defined(_WIN32) - CPUINFO_FREE(cpuinfo_cache[cpuinfo_cache_level_1i]); -#else - for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { - CPUINFO_FREE(cpuinfo_cache[lvl]); - } -#endif - for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { - cpuinfo_cache[lvl] = NULL; - cpuinfo_cache_count[lvl] = 0; - } - cpuinfo_max_cache_size = 0; - -#if (CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64) - CPUINFO_FREE(cpuinfo_uarchs); - cpuinfo_uarchs = NULL; - cpuinfo_uarchs_count = 0; +#if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 +#if defined(__MACH__) && defined(__APPLE__) + pthread_once(&deinit_guard, &cpuinfo_x86_mach_deinit); +#elif defined(__FreeBSD__) + pthread_once(&deinit_guard, &cpuinfo_x86_freebsd_deinit); +#elif defined(__linux__) + pthread_once(&deinit_guard, &cpuinfo_x86_linux_deinit); +#elif defined(_WIN32) || defined(__CYGWIN__) + InitOnceExecuteOnce(&deinit_guard, &cpuinfo_x86_windows_deinit, NULL, NULL); #endif - -#ifdef __linux__ - CPUINFO_FREE(cpuinfo_linux_cpu_to_processor_map); - cpuinfo_linux_cpu_to_processor_map = NULL; - - CPUINFO_FREE(cpuinfo_linux_cpu_to_core_map); - cpuinfo_linux_cpu_to_core_map = NULL; - -#if (CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64) - CPUINFO_FREE(cpuinfo_linux_cpu_to_uarch_index_map); - cpuinfo_linux_cpu_to_uarch_index_map = NULL; +#elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 +#if defined(__linux__) + pthread_once(&deinit_guard, &cpuinfo_arm_linux_deinit); +#elif defined(__MACH__) && defined(__APPLE__) + pthread_once(&deinit_guard, &cpuinfo_arm_mach_deinit); +#elif defined(_WIN32) + InitOnceExecuteOnce(&deinit_guard, &cpuinfo_arm_windows_deinit, NULL, NULL); #endif - - cpuinfo_linux_cpu_max = 0; +#elif CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 +#if defined(__linux__) + pthread_once(&deinit_guard, &cpuinfo_riscv_linux_deinit); #endif -} - +#elif CPUINFO_ARCH_ASMJS || CPUINFO_ARCH_WASM || CPUINFO_ARCH_WASMSIMD +#if defined(__EMSCRIPTEN_PTHREADS__) + pthread_once(&deinit_guard, &cpuinfo_emscripten_deinit); +#else + if (!deinit_guard) { + cpuinfo_emscripten_deinit(); + } + deinit_guard = true; #endif - -#if defined(_WIN32) || defined(__CYGWIN__) -static BOOL CALLBACK cpuinfo_deinit_once(PINIT_ONCE once, PVOID param, PVOID* ctx) { - cpuinfo_cleanup(); - return TRUE; -} #endif - -void CPUINFO_ABI cpuinfo_deinitialize(void) { + /* Reset guards to allow re-initialization */ #if defined(_WIN32) || defined(__CYGWIN__) - InitOnceExecuteOnce(&deinit_guard, cpuinfo_deinit_once, NULL, NULL); -#elif (defined(__linux__) && !CPUINFO_ARCH_ARM) || defined(__APPLE__) || defined(__FreeBSD__) - pthread_once(&deinit_guard, cpuinfo_cleanup); + init_guard = INIT_ONCE_STATIC_INIT; + deinit_guard = INIT_ONCE_STATIC_INIT; +#elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__) + init_guard = PTHREAD_ONCE_INIT; + deinit_guard = PTHREAD_ONCE_INIT; +#else + init_guard = false; + deinit_guard = false; #endif } diff --git a/src/riscv/linux/init.c b/src/riscv/linux/init.c index 45168c7e..3da8c3ff 100644 --- a/src/riscv/linux/init.c +++ b/src/riscv/linux/init.c @@ -618,3 +618,36 @@ void cpuinfo_riscv_linux_init(void) { free(linux_cpu_to_core_map); free(linux_cpu_to_uarch_index_map); } + +void cpuinfo_riscv_linux_deinit(void) { + free(cpuinfo_processors); + cpuinfo_processors = NULL; + cpuinfo_processors_count = 0; + + free(cpuinfo_cores); + cpuinfo_cores = NULL; + cpuinfo_cores_count = 0; + + free(cpuinfo_clusters); + cpuinfo_clusters = NULL; + cpuinfo_clusters_count = 0; + + free(cpuinfo_packages); + cpuinfo_packages = NULL; + cpuinfo_packages_count = 0; + + free(cpuinfo_uarchs); + cpuinfo_uarchs = NULL; + cpuinfo_uarchs_count = 0; + + free(cpuinfo_linux_cpu_to_processor_map); + cpuinfo_linux_cpu_to_processor_map = NULL; + + free(cpuinfo_linux_cpu_to_core_map); + cpuinfo_linux_cpu_to_core_map = NULL; + + free((void*)cpuinfo_linux_cpu_to_uarch_index_map); + cpuinfo_linux_cpu_to_uarch_index_map = NULL; + + cpuinfo_linux_cpu_max = 0; +} diff --git a/src/x86/freebsd/init.c b/src/x86/freebsd/init.c index 797fa24b..ba30a5d9 100644 --- a/src/x86/freebsd/init.c +++ b/src/x86/freebsd/init.c @@ -396,3 +396,30 @@ void cpuinfo_x86_freebsd_init(void) { free(l3); free(l4); } + +void cpuinfo_x86_freebsd_deinit(void) { + free(cpuinfo_processors); + cpuinfo_processors = NULL; + cpuinfo_processors_count = 0; + + free(cpuinfo_cores); + cpuinfo_cores = NULL; + cpuinfo_cores_count = 0; + + free(cpuinfo_clusters); + cpuinfo_clusters = NULL; + cpuinfo_clusters_count = 0; + + free(cpuinfo_packages); + cpuinfo_packages = NULL; + cpuinfo_packages_count = 0; + + for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { + free(cpuinfo_cache[lvl]); + cpuinfo_cache[lvl] = NULL; + cpuinfo_cache_count[lvl] = 0; + } + cpuinfo_max_cache_size = 0; + + cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; +} diff --git a/src/x86/linux/init.c b/src/x86/linux/init.c index d2b2d475..5f6dd6a5 100644 --- a/src/x86/linux/init.c +++ b/src/x86/linux/init.c @@ -676,3 +676,38 @@ void cpuinfo_x86_linux_init(void) { free(linux_cpu_to_processor_map); free(linux_cpu_to_core_map); } + +void cpuinfo_x86_linux_deinit(void) { + free(cpuinfo_processors); + cpuinfo_processors = NULL; + cpuinfo_processors_count = 0; + + free(cpuinfo_cores); + cpuinfo_cores = NULL; + cpuinfo_cores_count = 0; + + free(cpuinfo_clusters); + cpuinfo_clusters = NULL; + cpuinfo_clusters_count = 0; + + free(cpuinfo_packages); + cpuinfo_packages = NULL; + cpuinfo_packages_count = 0; + + for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { + free(cpuinfo_cache[lvl]); + cpuinfo_cache[lvl] = NULL; + cpuinfo_cache_count[lvl] = 0; + } + cpuinfo_max_cache_size = 0; + + free(cpuinfo_linux_cpu_to_processor_map); + cpuinfo_linux_cpu_to_processor_map = NULL; + + free(cpuinfo_linux_cpu_to_core_map); + cpuinfo_linux_cpu_to_core_map = NULL; + + cpuinfo_linux_cpu_max = 0; + + cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; +} \ No newline at end of file diff --git a/src/x86/mach/init.c b/src/x86/mach/init.c index b8ea6047..321f8879 100644 --- a/src/x86/mach/init.c +++ b/src/x86/mach/init.c @@ -378,3 +378,30 @@ void cpuinfo_x86_mach_init(void) { free(l3); free(l4); } + +void cpuinfo_x86_mach_deinit(void) { + free(cpuinfo_processors); + cpuinfo_processors = NULL; + cpuinfo_processors_count = 0; + + free(cpuinfo_cores); + cpuinfo_cores = NULL; + cpuinfo_cores_count = 0; + + free(cpuinfo_clusters); + cpuinfo_clusters = NULL; + cpuinfo_clusters_count = 0; + + free(cpuinfo_packages); + cpuinfo_packages = NULL; + cpuinfo_packages_count = 0; + + for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { + free(cpuinfo_cache[lvl]); + cpuinfo_cache[lvl] = NULL; + cpuinfo_cache_count[lvl] = 0; + } + cpuinfo_max_cache_size = 0; + + cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; +} \ No newline at end of file diff --git a/src/x86/windows/init.c b/src/x86/windows/init.c index 3f4c69bd..7b97d114 100644 --- a/src/x86/windows/init.c +++ b/src/x86/windows/init.c @@ -672,3 +672,34 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV } return TRUE; } + +BOOL CALLBACK cpuinfo_x86_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { + HANDLE heap = GetProcessHeap(); + + HeapFree(heap, 0, cpuinfo_processors); + cpuinfo_processors = NULL; + cpuinfo_processors_count = 0; + + HeapFree(heap, 0, cpuinfo_cores); + cpuinfo_cores = NULL; + cpuinfo_cores_count = 0; + + HeapFree(heap, 0, cpuinfo_clusters); + cpuinfo_clusters = NULL; + cpuinfo_clusters_count = 0; + + HeapFree(heap, 0, cpuinfo_packages); + cpuinfo_packages = NULL; + cpuinfo_packages_count = 0; + + for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { + HeapFree(heap, 0, cpuinfo_cache[lvl]); + cpuinfo_cache[lvl] = NULL; + cpuinfo_cache_count[lvl] = 0; + } + cpuinfo_max_cache_size = 0; + + cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; + + return TRUE; +} diff --git a/test/deinit.cc b/test/deinit.cc deleted file mode 100644 index fb7cec70..00000000 --- a/test/deinit.cc +++ /dev/null @@ -1,87 +0,0 @@ -#include - -#include - -TEST(DEINITIALIZE, clears_processors) { - ASSERT_TRUE(cpuinfo_initialize()); - EXPECT_NE(0, cpuinfo_get_processors_count()); - EXPECT_TRUE(cpuinfo_get_processors()); - - cpuinfo_deinitialize(); - - EXPECT_EQ(0, cpuinfo_get_processors_count()); - EXPECT_FALSE(cpuinfo_get_processors()); -} - -TEST(DEINITIALIZE, clears_cores) { - ASSERT_TRUE(cpuinfo_initialize()); - EXPECT_NE(0, cpuinfo_get_cores_count()); - EXPECT_TRUE(cpuinfo_get_cores()); - - cpuinfo_deinitialize(); - - EXPECT_EQ(0, cpuinfo_get_cores_count()); - EXPECT_FALSE(cpuinfo_get_cores()); -} - -TEST(DEINITIALIZE, clears_clusters) { - ASSERT_TRUE(cpuinfo_initialize()); - EXPECT_NE(0, cpuinfo_get_clusters_count()); - EXPECT_TRUE(cpuinfo_get_clusters()); - - cpuinfo_deinitialize(); - - EXPECT_EQ(0, cpuinfo_get_clusters_count()); - EXPECT_FALSE(cpuinfo_get_clusters()); -} - -TEST(DEINITIALIZE, clears_packages) { - ASSERT_TRUE(cpuinfo_initialize()); - EXPECT_NE(0, cpuinfo_get_packages_count()); - EXPECT_TRUE(cpuinfo_get_packages()); - - cpuinfo_deinitialize(); - - EXPECT_EQ(0, cpuinfo_get_packages_count()); - EXPECT_FALSE(cpuinfo_get_packages()); -} - -TEST(DEINITIALIZE, clears_caches) { - ASSERT_TRUE(cpuinfo_initialize()); - - cpuinfo_deinitialize(); - - EXPECT_EQ(0, cpuinfo_get_l1i_caches_count()); - EXPECT_EQ(0, cpuinfo_get_l1d_caches_count()); - EXPECT_EQ(0, cpuinfo_get_l2_caches_count()); - EXPECT_EQ(0, cpuinfo_get_l3_caches_count()); - EXPECT_EQ(0, cpuinfo_get_l4_caches_count()); - EXPECT_FALSE(cpuinfo_get_l1i_caches()); - EXPECT_FALSE(cpuinfo_get_l1d_caches()); - EXPECT_FALSE(cpuinfo_get_l2_caches()); - EXPECT_FALSE(cpuinfo_get_l3_caches()); - EXPECT_FALSE(cpuinfo_get_l4_caches()); -} - -TEST(DEINITIALIZE, clears_uarchs) { - ASSERT_TRUE(cpuinfo_initialize()); - EXPECT_NE(0, cpuinfo_get_uarchs_count()); - EXPECT_TRUE(cpuinfo_get_uarchs()); - - cpuinfo_deinitialize(); - - EXPECT_EQ(0, cpuinfo_get_uarchs_count()); - EXPECT_FALSE(cpuinfo_get_uarchs()); -} - -TEST(DEINITIALIZE, double_deinit_is_safe) { - ASSERT_TRUE(cpuinfo_initialize()); - cpuinfo_deinitialize(); - cpuinfo_deinitialize(); - EXPECT_EQ(0, cpuinfo_get_processors_count()); -} - -TEST(DEINITIALIZE, before_init_is_safe) { - cpuinfo_deinitialize(); - EXPECT_EQ(0, cpuinfo_get_processors_count()); -}