This is an automated email from the ASF dual-hosted git repository.
pnoltes pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/celix.git
The following commit(s) were added to refs/heads/master by this push:
new 712c1040b Add SBRM documentation
712c1040b is described below
commit 712c1040bbf8671713837890579adde9505bbb49
Author: Pepijn Noltes <[email protected]>
AuthorDate: Mon Aug 18 20:41:59 2025 +0200
Add SBRM documentation
---------
Co-authored-by: PengZheng <[email protected]>
---
documents/README.md | 1 +
documents/SBRM.md | 84 +++++++++++++++++++++++++++++++++++++++++
documents/development/README.md | 65 +++++++++++++++++--------------
3 files changed, 122 insertions(+), 28 deletions(-)
diff --git a/documents/README.md b/documents/README.md
index c174acbe7..fefec7971 100644
--- a/documents/README.md
+++ b/documents/README.md
@@ -98,3 +98,4 @@ bundles contains binaries depending on the stdlibc++ library.
* [Apache Celix CMake Commands](cmake_commands)
* [Apache Celix Sub Projects](subprojects.md)
* [Apache Celix Coding Conventions Guide](development/README.md)
+* [Apache Celix Scope Bound Resource Management](SBRM.md)
diff --git a/documents/SBRM.md b/documents/SBRM.md
new file mode 100644
index 000000000..c4da9aa2f
--- /dev/null
+++ b/documents/SBRM.md
@@ -0,0 +1,84 @@
+---
+title: Scope-Based Resource Management
+---
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+# Scope-Based Resource Management (SBRM)
+
+Apache Celix provides *auto pointers* and *auto values* as a lightweight form
of scope-based resource management in C.
+These features are inspired by the RAII (Resource Acquisition Is
Initialization) paradigm from C++,
+as well as ideas from [Scope-based resource management for the
kernel](https://lwn.net/Articles/934679/).
+
+Using the macros defined in `celix_cleanup.h` and related headers
(`celix_stdio_cleanup.h`, `celix_stdlib_cleanup.h`),
+resources are automatically released when their associated variables go out of
scope. This enables safer and more
+concise C code, especially when handling early returns or error paths.
+
+## Defining Cleanup Functions
+
+Before using auto cleanup, types must opt-in by defining a cleanup function.
+
+- For pointer types:
+
+```C
+ CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_filter_t, celix_filter_destroy)
+```
+
+- For value types:
+
+```C
+CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_mutex_lock_guard_t,
celixMutexLockGuard_deinit)
+```
+
+## Using Auto Pointers and Auto Values
+
+### Auto Pointers
+
+Use `celix_autoptr(type)` to declare a pointer that will be cleaned up
automatically:
+
+```C
+void my_function(void) {
+ celix_autoptr(celix_filter_t) filter = celix_filter_create("(foo=bar)");
+ // use filter
+} // filter is destroyed automatically
+```
+
+To transfer ownership and disable automatic cleanup, use:
+
+```C
+celix_filter_t* raw = celix_steal_ptr(filter);
+```
+
+### Auto Values
+
+Use `celix_auto(type)` to declare a value-based resource with automatic
cleanup:
+
+```C
+void my_function(void) {
+ celix_auto(celix_mutex_lock_guard_t) lock =
celixMutexLockGuard_init(&myMutex);
+ // use guarded memory
+} // lock guard is cleaned up automatically (myMutex is unlocked)
+```
+
+## Benefits
+
+Celix's SBRM features allow for:
+
+- Simplified cleanup logic
+- RAII-style early returns
+- Cleaner, more maintainable C code
diff --git a/documents/development/README.md b/documents/development/README.md
index e1f4a99a2..d521acb34 100644
--- a/documents/development/README.md
+++ b/documents/development/README.md
@@ -333,10 +333,8 @@ add_library(celix::MyBundle ALIAS MyBundle)
- Use `while` statements for loops that may not execute.
- Use `do`/`while` statements for loops that must execute at least once.
- Use `for` statements for loops with a known number of iterations.
-- The use of `goto` is not allowed, except for error handling in C (for C++
use RAII).
-- For C, try to prevent deeply nested control structures and prefer early
returns or error handling `goto` statements.
- - To prevent deeply nested control structures, the `CELIX_DO_IF`,
`CELIX_GOTO_IF_NULL` and `CELIX_GOTO_IF_ERR`
- macros can also be used.
+- Avoid using `goto` for error handling. Prefer early returns and automatic
cleanup using celix auto pointers.
+- To prevent deeply nested control structures, the `CELIX_DO_IF` macro can
also be used.
## Functions and Methods
@@ -350,7 +348,7 @@ add_library(celix::MyBundle ALIAS MyBundle)
- For C++ functions with a lot of different parameters, consider using a
builder pattern.
- A builder pattern can be updated backwards compatible.
- A builder pattern ensure that a lot of parameters can be configured, but
also direct set on construction.
-
+
## Error Handling and Logging
- For C++, throw an exception when an error occurs and use RAII to ensure that
resources are freed.
@@ -362,9 +360,10 @@ add_library(celix::MyBundle ALIAS MyBundle)
- Use consistent error handling techniques, such as returning error codes or
using designated error handling functions.
- Log errors, warnings, and other important events using the Apache Celix log
helper functions or - for libraries -
the `celix_err` functionality.
-- Always check for errors and log them.
+- Always check for errors and log them.
- Error handling should free resources in the reverse order of their
allocation/creation.
- Ensure error handling is correct, using test suite with error injection.
+ - Prefer early returns together with celix auto pointers to ensure cleanup
without using `goto`.
For log levels use the following guidelines:
- trace: Use this level for very detailed that you would only want to have
while diagnosing problems.
@@ -381,37 +380,47 @@ For log levels use the following guidelines:
- fatal: Use this level to report severe errors that prevent the program from
continuing to run.
After logging a fatal error, the program will typically terminate.
-Example of error handling and logging:
+Example of error handling and logging using auto pointers:
```c
+typedef struct celix_foo {
+ celix_thread_mutex_t mutex;
+ celix_array_list_t* list;
+ celix_long_hash_map_t* map;
+} celix_foo_t;
+
+CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_foo_t, celix_foo_destroy)
+
celix_foo_t* celix_foo_create(celix_log_helper_t* logHelper) {
- celix_foo_t* foo = calloc(1, sizeof(*foo));
+ celix_autofree celix_foo_t* foo = calloc(1, sizeof(*foo));
if (!foo) {
- goto create_enomem_err;
+ celix_logHelper_log(logHelper, CELIX_LOG_LEVEL_ERROR,
+ "Error creating foo, out of memory");
+ return NULL;
}
-
- CELIX_GOTO_IF_ERR(create_mutex_err, celixThreadMutex_create(&foo->mutex,
NULL));
-
- foo->list = celix_arrayList_create();
- foo->map = celix_longHashMap_create();
- if (!foo->list || !foo->map) {
- goto create_enomem_err;
+
+ if (celixThreadMutex_create(&foo->mutex, NULL) != CELIX_SUCCESS) {
+ celix_logHelper_log(logHelper, CELIX_LOG_LEVEL_ERROR,
+ "Error creating mutex");
+ return NULL; //foo cleaned up automatically (celix_autofree will call
free)
}
- return foo;
-create_mutex_err:
- celix_logHelper_log(logHelper, CELIX_LOG_LEVEL_ERROR, "Error creating
mutex");
- free(foo); //mutex not created, do not use celix_foo_destroy to prevent
mutex destroy
- return NULL;
-create_enomem_err:
- celix_logHelper_log(logHelper, CELIX_LOG_LEVEL_ERROR, "Error creating foo,
out of memory");
- celix_foo_destroy(foo); //note celix_foo_destroy can handle NULL
- return NULL;
+ celix_autoptr(celix_thread_mutex_t) mutex = &foo->mutex;
+ celix_autoptr(celix_array_list_t) list = celix_arrayList_create();
+ celix_autoptr(celix_long_hash_map_t) map = celix_longHashMap_create();
+ if (!list || !map) {
+ celix_logHelper_log(logHelper, CELIX_LOG_LEVEL_ERROR,
+ "Error creating foo, out of memory");
+ return NULL; //foo, list and/or map are cleaned up automatically
+ }
+
+ celix_steal_ptr(mutex);
+ foo->list = celix_steal_ptr(list);
+ foo->map = celix_steal_ptr(map);
+ return celix_steal_ptr(foo);
}
void celix_foo_destroy(celix_foo_t* foo) {
- if (foo != NULL) {
- //note reverse order of creation
- celixThreadMutex_destroy(&foo->mutex);
+ if (foo) {
celix_arrayList_destroy(foo->list);
celix_longHashMap_destroy(foo->map);
free(foo);