.. _cleanup_api: Scope-based Cleanup Helpers ########################### .. contents:: :local: :depth: 2 Overview ******** The Cleanup Helper API provides a mechanism for automatic resource cleanup when variables go out of scope. This is similar to RAII in C++ or defer statements in Go. By leveraging compiler support for the ``__cleanup`` attribute, this API helps prevent resource leaks and simplifies error handling by ensuring cleanup code is executed automatically. It is required to set :kconfig:option:`CONFIG_SCOPE_CLEANUP_HELPERS` to enable this feature, which is particularly useful for: * Automatic unlocking of mutexes and semaphores * Automatic freeing of dynamically allocated memory * Ensuring cleanup actions occur on all code paths (including early returns) * Reducing boilerplate cleanup code .. warning:: The cleanup mechanism is implemented using the ``__cleanup`` attribute. If the toolchain doesn't support this attribute, the API is not available. For this reason this API is intended solely for user applications and not for the kernel itself or other subsystems. Core Concepts ************* The cleanup API provides three main abstractions: Scoped Variables ================ A scoped variable defines a type with automatic init and exit behavior. Variables declared with a scoped variable type are initialized using an init function and automatically cleaned up using an exit function when they go out of scope. Scoped Guards ============= Scoped guards are specialized scoped variables that automatically acquire a lock or resource on initialization and release it when going out of scope. This pattern is commonly used with mutexes, semaphores, and other synchronization primitives. Scoped Defers ============= Scoped defers execute a specified function when the variable goes out of scope, without acquiring anything on initialization. This is similar to the ``defer`` statement in languages like Go. Defining Scoped Types ********************* Custom Scoped Variables ======================= Use :c:macro:`SCOPE_VAR_DEFINE` to define a custom scoped variable type with init and exit functions: .. code-block:: c static inline struct flash_area *flash_area_init(int area_id) { struct flash_area *fa; if (flash_area_open(area_id, &fa) < 0) { return NULL; } return fa; } static inline void flash_area_exit(struct flash_area *fa) { if (fa != NULL) { flash_area_close(fa); } } // Define the scoped variable type SCOPE_VAR_DEFINE(flash_area, struct flash_area *, flash_area_exit(_T), flash_area_init(area_id), int area_id); static int some_function(void) { // Declare 'fa' with automatic cleanup scope_var(flash_area, fa)(FIXED_PARTITION_ID(storage_partition)); if (fa == NULL) { return -EINVAL; // Exit function is still called } // Use fa normally printk("Has driver: %d\n", flash_area_has_driver(fa)); // No need to manually close - exit function is called automatically return 0; } The ``_T`` variable in the exit function expression contains the value of the variable being cleaned up. Scoped Guards ============= Use :c:macro:`SCOPE_GUARD_DEFINE` to define a guard that acquires a lock on initialization and releases it on scope exit: .. code-block:: c // Example guard definition (already provided by kernel.h) SCOPE_GUARD_DEFINE(k_mutex, struct k_mutex *, (void)k_mutex_lock(_T, K_FOREVER), (void)k_mutex_unlock(_T)); static K_MUTEX_DEFINE(lock); void critical_section(void) { scope_guard(k_mutex)(&lock); // Lock is held here // Perform critical operations // Lock is automatically released when guard goes out of scope } Scoped Defers ============= Use :c:macro:`SCOPE_DEFER_DEFINE` to define a defer that executes a cleanup function: .. code-block:: c // Define a defer for a custom cleanup function static void cleanup_resources(void) { // Cleanup code here } SCOPE_DEFER_DEFINE(cleanup_resources); void some_function(void) { scope_defer(cleanup_resources)(); // Do work... // cleanup_resources() is called automatically } For functions with parameters: .. code-block:: c // Example deferred k_free (already provided by kernel.h) SCOPE_DEFER_DEFINE(k_free, void *); void allocate_and_use(void) { void *ptr = k_malloc(100); scope_defer(k_free)(ptr); // Use ptr... // k_free(ptr) is called automatically } Usage Notes *********** Order of Cleanup ================ Cleanup functions are called in reverse order of declaration (LIFO - Last In, First Out), which matches the natural nesting of resources: .. code-block:: c { scope_guard(k_mutex)(&lock); // Acquired first void *ptr = k_malloc(100); scope_defer(k_free)(ptr); // Registered second // Do work... } // ptr is freed first, then mutex is unlocked Scope Rules =========== Cleanup occurs when the variable goes out of scope, which includes: * Reaching the end of a block * Early return statements * Break or continue in loops * Goto statements that jump out of scope .. code-block:: c void example_with_early_exit(struct k_mutex *lock) { scope_guard(k_mutex)(lock); if (error_condition) { return; // Guard cleanup happens here } // Normal path } // Guard cleanup also happens here API Reference ************* .. doxygengroup:: cleanup_interface