Skip to content

Commit 712c104

Browse files
pnoltesPengZheng
andauthored
Add SBRM documentation
--------- Co-authored-by: PengZheng <howtofly@gmail.com>
1 parent d1ad26b commit 712c104

File tree

3 files changed

+122
-28
lines changed

3 files changed

+122
-28
lines changed

documents/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,4 @@ bundles contains binaries depending on the stdlibc++ library.
9898
* [Apache Celix CMake Commands](cmake_commands)
9999
* [Apache Celix Sub Projects](subprojects.md)
100100
* [Apache Celix Coding Conventions Guide](development/README.md)
101+
* [Apache Celix Scope Bound Resource Management](SBRM.md)

documents/SBRM.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
title: Scope-Based Resource Management
3+
---
4+
5+
<!--
6+
Licensed to the Apache Software Foundation (ASF) under one or more
7+
contributor license agreements. See the NOTICE file distributed with
8+
this work for additional information regarding copyright ownership.
9+
The ASF licenses this file to You under the Apache License, Version 2.0
10+
(the "License"); you may not use this file except in compliance with
11+
the License. You may obtain a copy of the License at
12+
13+
http://www.apache.org/licenses/LICENSE-2.0
14+
15+
Unless required by applicable law or agreed to in writing, software
16+
distributed under the License is distributed on an "AS IS" BASIS,
17+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
See the License for the specific language governing permissions and
19+
limitations under the License.
20+
-->
21+
22+
# Scope-Based Resource Management (SBRM)
23+
24+
Apache Celix provides *auto pointers* and *auto values* as a lightweight form of scope-based resource management in C.
25+
These features are inspired by the RAII (Resource Acquisition Is Initialization) paradigm from C++,
26+
as well as ideas from [Scope-based resource management for the kernel](https://lwn.net/Articles/934679/).
27+
28+
Using the macros defined in `celix_cleanup.h` and related headers (`celix_stdio_cleanup.h`, `celix_stdlib_cleanup.h`),
29+
resources are automatically released when their associated variables go out of scope. This enables safer and more
30+
concise C code, especially when handling early returns or error paths.
31+
32+
## Defining Cleanup Functions
33+
34+
Before using auto cleanup, types must opt-in by defining a cleanup function.
35+
36+
- For pointer types:
37+
38+
```C
39+
CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_filter_t, celix_filter_destroy)
40+
```
41+
42+
- For value types:
43+
44+
```C
45+
CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_mutex_lock_guard_t, celixMutexLockGuard_deinit)
46+
```
47+
48+
## Using Auto Pointers and Auto Values
49+
50+
### Auto Pointers
51+
52+
Use `celix_autoptr(type)` to declare a pointer that will be cleaned up automatically:
53+
54+
```C
55+
void my_function(void) {
56+
celix_autoptr(celix_filter_t) filter = celix_filter_create("(foo=bar)");
57+
// use filter
58+
} // filter is destroyed automatically
59+
```
60+
61+
To transfer ownership and disable automatic cleanup, use:
62+
63+
```C
64+
celix_filter_t* raw = celix_steal_ptr(filter);
65+
```
66+
67+
### Auto Values
68+
69+
Use `celix_auto(type)` to declare a value-based resource with automatic cleanup:
70+
71+
```C
72+
void my_function(void) {
73+
celix_auto(celix_mutex_lock_guard_t) lock = celixMutexLockGuard_init(&myMutex);
74+
// use guarded memory
75+
} // lock guard is cleaned up automatically (myMutex is unlocked)
76+
```
77+
78+
## Benefits
79+
80+
Celix's SBRM features allow for:
81+
82+
- Simplified cleanup logic
83+
- RAII-style early returns
84+
- Cleaner, more maintainable C code

documents/development/README.md

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -333,10 +333,8 @@ add_library(celix::MyBundle ALIAS MyBundle)
333333
- Use `while` statements for loops that may not execute.
334334
- Use `do`/`while` statements for loops that must execute at least once.
335335
- Use `for` statements for loops with a known number of iterations.
336-
- The use of `goto` is not allowed, except for error handling in C (for C++ use RAII).
337-
- For C, try to prevent deeply nested control structures and prefer early returns or error handling `goto` statements.
338-
- To prevent deeply nested control structures, the `CELIX_DO_IF`, `CELIX_GOTO_IF_NULL` and `CELIX_GOTO_IF_ERR`
339-
macros can also be used.
336+
- Avoid using `goto` for error handling. Prefer early returns and automatic cleanup using celix auto pointers.
337+
- To prevent deeply nested control structures, the `CELIX_DO_IF` macro can also be used.
340338

341339
## Functions and Methods
342340

@@ -350,7 +348,7 @@ add_library(celix::MyBundle ALIAS MyBundle)
350348
- For C++ functions with a lot of different parameters, consider using a builder pattern.
351349
- A builder pattern can be updated backwards compatible.
352350
- A builder pattern ensure that a lot of parameters can be configured, but also direct set on construction.
353-
351+
354352
## Error Handling and Logging
355353

356354
- 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)
362360
- Use consistent error handling techniques, such as returning error codes or using designated error handling functions.
363361
- Log errors, warnings, and other important events using the Apache Celix log helper functions or - for libraries -
364362
the `celix_err` functionality.
365-
- Always check for errors and log them.
363+
- Always check for errors and log them.
366364
- Error handling should free resources in the reverse order of their allocation/creation.
367365
- Ensure error handling is correct, using test suite with error injection.
366+
- Prefer early returns together with celix auto pointers to ensure cleanup without using `goto`.
368367

369368
For log levels use the following guidelines:
370369
- 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:
381380
- fatal: Use this level to report severe errors that prevent the program from continuing to run.
382381
After logging a fatal error, the program will typically terminate.
383382

384-
Example of error handling and logging:
383+
Example of error handling and logging using auto pointers:
385384
```c
385+
typedef struct celix_foo {
386+
celix_thread_mutex_t mutex;
387+
celix_array_list_t* list;
388+
celix_long_hash_map_t* map;
389+
} celix_foo_t;
390+
391+
CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_foo_t, celix_foo_destroy)
392+
386393
celix_foo_t* celix_foo_create(celix_log_helper_t* logHelper) {
387-
celix_foo_t* foo = calloc(1, sizeof(*foo));
394+
celix_autofree celix_foo_t* foo = calloc(1, sizeof(*foo));
388395
if (!foo) {
389-
goto create_enomem_err;
396+
celix_logHelper_log(logHelper, CELIX_LOG_LEVEL_ERROR,
397+
"Error creating foo, out of memory");
398+
return NULL;
390399
}
391-
392-
CELIX_GOTO_IF_ERR(create_mutex_err, celixThreadMutex_create(&foo->mutex, NULL));
393-
394-
foo->list = celix_arrayList_create();
395-
foo->map = celix_longHashMap_create();
396-
if (!foo->list || !foo->map) {
397-
goto create_enomem_err;
400+
401+
if (celixThreadMutex_create(&foo->mutex, NULL) != CELIX_SUCCESS) {
402+
celix_logHelper_log(logHelper, CELIX_LOG_LEVEL_ERROR,
403+
"Error creating mutex");
404+
return NULL; //foo cleaned up automatically (celix_autofree will call free)
398405
}
399406

400-
return foo;
401-
create_mutex_err:
402-
celix_logHelper_log(logHelper, CELIX_LOG_LEVEL_ERROR, "Error creating mutex");
403-
free(foo); //mutex not created, do not use celix_foo_destroy to prevent mutex destroy
404-
return NULL;
405-
create_enomem_err:
406-
celix_logHelper_log(logHelper, CELIX_LOG_LEVEL_ERROR, "Error creating foo, out of memory");
407-
celix_foo_destroy(foo); //note celix_foo_destroy can handle NULL
408-
return NULL;
407+
celix_autoptr(celix_thread_mutex_t) mutex = &foo->mutex;
408+
celix_autoptr(celix_array_list_t) list = celix_arrayList_create();
409+
celix_autoptr(celix_long_hash_map_t) map = celix_longHashMap_create();
410+
if (!list || !map) {
411+
celix_logHelper_log(logHelper, CELIX_LOG_LEVEL_ERROR,
412+
"Error creating foo, out of memory");
413+
return NULL; //foo, list and/or map are cleaned up automatically
414+
}
415+
416+
celix_steal_ptr(mutex);
417+
foo->list = celix_steal_ptr(list);
418+
foo->map = celix_steal_ptr(map);
419+
return celix_steal_ptr(foo);
409420
}
410421

411422
void celix_foo_destroy(celix_foo_t* foo) {
412-
if (foo != NULL) {
413-
//note reverse order of creation
414-
celixThreadMutex_destroy(&foo->mutex);
423+
if (foo) {
415424
celix_arrayList_destroy(foo->list);
416425
celix_longHashMap_destroy(foo->map);
417426
free(foo);

0 commit comments

Comments
 (0)