|
7 | 7 | /*
|
8 | 8 | * DEFINE_FREE(name, type, free):
|
9 | 9 | * simple helper macro that defines the required wrapper for a __free()
|
10 |
| - * based cleanup function. @free is an expression using '_T' to access |
11 |
| - * the variable. |
| 10 | + * based cleanup function. @free is an expression using '_T' to access the |
| 11 | + * variable. @free should typically include a NULL test before calling a |
| 12 | + * function, see the example below. |
12 | 13 | *
|
13 | 14 | * __free(name):
|
14 | 15 | * variable attribute to add a scoped based cleanup to the variable.
|
|
17 | 18 | * like a non-atomic xchg(var, NULL), such that the cleanup function will
|
18 | 19 | * be inhibited -- provided it sanely deals with a NULL value.
|
19 | 20 | *
|
| 21 | + * NOTE: this has __must_check semantics so that it is harder to accidentally |
| 22 | + * leak the resource. |
| 23 | + * |
20 | 24 | * return_ptr(p):
|
21 | 25 | * returns p while inhibiting the __free().
|
22 | 26 | *
|
23 | 27 | * Ex.
|
24 | 28 | *
|
25 | 29 | * DEFINE_FREE(kfree, void *, if (_T) kfree(_T))
|
26 | 30 | *
|
| 31 | + * void *alloc_obj(...) |
| 32 | + * { |
27 | 33 | * struct obj *p __free(kfree) = kmalloc(...);
|
28 | 34 | * if (!p)
|
29 | 35 | * return NULL;
|
|
32 | 38 | * return NULL;
|
33 | 39 | *
|
34 | 40 | * return_ptr(p);
|
| 41 | + * } |
| 42 | + * |
| 43 | + * NOTE: the DEFINE_FREE()'s @free expression includes a NULL test even though |
| 44 | + * kfree() is fine to be called with a NULL value. This is on purpose. This way |
| 45 | + * the compiler sees the end of our alloc_obj() function as: |
| 46 | + * |
| 47 | + * tmp = p; |
| 48 | + * p = NULL; |
| 49 | + * if (p) |
| 50 | + * kfree(p); |
| 51 | + * return tmp; |
| 52 | + * |
| 53 | + * And through the magic of value-propagation and dead-code-elimination, it |
| 54 | + * eliminates the actual cleanup call and compiles into: |
| 55 | + * |
| 56 | + * return p; |
| 57 | + * |
| 58 | + * Without the NULL test it turns into a mess and the compiler can't help us. |
35 | 59 | */
|
36 | 60 |
|
37 | 61 | #define DEFINE_FREE(_name, _type, _free) \
|
38 | 62 | static inline void __free_##_name(void *p) { _type _T = *(_type *)p; _free; }
|
39 | 63 |
|
40 | 64 | #define __free(_name) __cleanup(__free_##_name)
|
41 | 65 |
|
| 66 | +#define __get_and_null_ptr(p) \ |
| 67 | + ({ __auto_type __ptr = &(p); \ |
| 68 | + __auto_type __val = *__ptr; \ |
| 69 | + *__ptr = NULL; __val; }) |
| 70 | + |
| 71 | +static inline __must_check |
| 72 | +const volatile void * __must_check_fn(const volatile void *val) |
| 73 | +{ return val; } |
| 74 | + |
42 | 75 | #define no_free_ptr(p) \
|
43 |
| - ({ __auto_type __ptr = (p); (p) = NULL; __ptr; }) |
| 76 | + ((typeof(p)) __must_check_fn(__get_and_null_ptr(p))) |
44 | 77 |
|
45 | 78 | #define return_ptr(p) return no_free_ptr(p)
|
46 | 79 |
|
|
0 commit comments