Implement `cpuinfo_deinitialize()` to free heap-allocated globals (#387)
* Add support for freeing cpuinfo memory
* Add deinit tests
* Move the deinits to respective allocation routines
* Use compound literals for guard resets
---------
Co-authored-by: Vineeth Chelur <vchelur@microsoft.com>
diff --git a/src/arm/linux/init.c b/src/arm/linux/init.c
index 1bf0014..53e9a43 100644
--- a/src/arm/linux/init.c
+++ b/src/arm/linux/init.c
@@ -1001,3 +1001,43 @@
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 11de4ae..eac431f 100644
--- a/src/arm/mach/init.c
+++ b/src/arm/mach/init.c
@@ -755,3 +755,32 @@
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 815ecb7..2e44f8a 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 @@
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 d84b26a..9c15888 100644
--- a/src/cpuinfo/internal-api.h
+++ b/src/cpuinfo/internal-api.h
@@ -45,6 +45,9 @@
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);
@@ -62,6 +65,23 @@
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 c239324..6c8210f 100644
--- a/src/emscripten/init.c
+++ b/src/emscripten/init.c
@@ -286,3 +286,35 @@
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 81d5721..e71b186 100644
--- a/src/init.c
+++ b/src/init.c
@@ -14,10 +14,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 +67,52 @@
return cpuinfo_is_initialized;
}
-void CPUINFO_ABI cpuinfo_deinitialize(void) {}
+void CPUINFO_ABI cpuinfo_deinitialize(void) {
+ if (!cpuinfo_is_initialized) {
+ return;
+ }
+ cpuinfo_is_initialized = false;
+#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
+#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
+#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
+#endif
+ /* Reset guards to allow re-initialization */
+#if defined(_WIN32) || defined(__CYGWIN__)
+ init_guard = (INIT_ONCE)INIT_ONCE_STATIC_INIT;
+ deinit_guard = (INIT_ONCE)INIT_ONCE_STATIC_INIT;
+#elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__)
+ init_guard = (pthread_once_t)PTHREAD_ONCE_INIT;
+ deinit_guard = (pthread_once_t)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 45168c7..3da8c3f 100644
--- a/src/riscv/linux/init.c
+++ b/src/riscv/linux/init.c
@@ -618,3 +618,36 @@
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 797fa24..ba30a5d 100644
--- a/src/x86/freebsd/init.c
+++ b/src/x86/freebsd/init.c
@@ -396,3 +396,30 @@
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 d2b2d47..62e1187 100644
--- a/src/x86/linux/init.c
+++ b/src/x86/linux/init.c
@@ -676,3 +676,38 @@
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};
+}
diff --git a/src/x86/mach/init.c b/src/x86/mach/init.c
index b8ea604..9009e83 100644
--- a/src/x86/mach/init.c
+++ b/src/x86/mach/init.c
@@ -378,3 +378,30 @@
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};
+}
diff --git a/src/x86/windows/init.c b/src/x86/windows/init.c
index d233280..7b97d11 100644
--- a/src/x86/windows/init.c
+++ b/src/x86/windows/init.c
@@ -667,5 +667,39 @@
if (l4 != NULL) {
HeapFree(heap, 0, l4);
}
+ if (processor_infos != NULL) {
+ HeapFree(heap, 0, processor_infos);
+ }
+ 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;
}