mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
spa: utils: add scope based resource cleanup
systemd, dbus-broker, and many glib applications heavily
utilize the GNU C attribute "cleanup" to achieve C++ RAII-like
semantics for local resource management. This commit introduces
essentialy same mechanism into pipewire.
At the moment, this is inteded to be a strictly private API.
free() and close() as cleanup targets are sufficiently common
to warrant their own special macros:
spa_autofree char *s = strdup(p);
// will call `free(s)` at the end of the lifetime of `s`
spa_autoclose int fd = openat(...);
// will call `close(fd)` if `fd >= 0` at the end of the lifetime of `fd`
However, with `spa_auto()` or `spa_autoptr()` it is possible to define
other variables that will be cleaned up properly. Currently four are supported:
spa_autoptr(FILE) f = fopen(...);
// `f` has type `FILE *`
// will call `fclose(f)` if `f != NULL`
spa_autoptr(DIR) d = opendir(...);
// `d` has type `DIR *`
// will call `closedir(d)` if `d != NULL`
spa_autoptr(pw_properties) p = pw_properties_new(NULL, NULL);
// `p` has type `struct pw_properties *`
// will call `pw_properties_free(p)`
spa_auto(pw_strv) v = pw_split_strv(...);
// `v` has type `char **`
// will call `pw_strv_free(v)`
It is possible to add support for other types, e.g.
SPA_DEFINE_AUTOPTR_CLEANUP(pw_main_loop, struct pw_main_loop, {
// the pointer can be accessed using `*thing`
// `thing` has type `struct pw_main_loop **`
spa_clear_ptr(*thing, pw_main_loop_destroy);
})
spa_autoptr(pw_main_loop) l = ...;
// `l` has type `struct pw_main_loop *`
// will call `pw_main_loop_destroy(l)`
or
SPA_DEFINE_AUTO_CLEANUP(spa_pod_dynamic_builder, struct spa_pod_dynamic_builder, {
// `thing` has type `struct spa_pod_dynamic_builder *`
spa_pod_dynamic_builder_clean(thing);
})
spa_auto(spa_pod_dynamic_builder) builder = ...
// `builder` has type `struct spa_pod_dynamic_builder`
// will call `spa_pod_dynamic_builder_clean(&builder)`
The first argument is always an arbitrary name. This name must be passed to
`spa_auto()` and `spa_autoptr()` as it is what determines the actual type
and destructor used. The second parameter is the concrete type. For
`SPA_DEFINE_AUTO_CLEANUP()` this is the concrete type to be used, while for
`SPA_DEFINE_AUTOPTR_CLEANUP()` it is the concrete type without the
outermost pointer. That is,
SPA_DEFINE_AUTOPTR_CLEANUP(A, foo, ...)
SPA_DEFINE_AUTO_CLEANUP(B, foo, ...)
spa_autoptr(A) x; // `x` has type `foo *`
spa_auto(B) y; // `y` has type `foo`
A couple other macros are also added:
spa_clear_ptr(ptr, destructor)
// calls `destructor(ptr)` if `ptr != NULL` and sets `ptr` to `NULL`
spa_clear_fd(fd)
// calls `close(fd)` if `fd >= 0` and sets `fd` to -1
spa_steal_ptr(ptr)
// sets `ptr` to `NULL` and returns the old value,
// useful for preventing the auto cleanup mechanism from kicking in
// when returning the pointer from a function
spa_steal_fd(fd)
// sets `fd` to -1 and returns the old value
This commit is contained in:
parent
4456f2efd1
commit
65d949558b
4 changed files with 125 additions and 3 deletions
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
LIST=""
|
||||
|
||||
for i in $(find spa/include -name '*.h' | sed s#spa/include/##);
|
||||
for i in $(find spa/include -name '*.h' -a -not -path 'spa/include/spa/utils/cleanup.h' | sed s#spa/include/##);
|
||||
do
|
||||
[ -f "$PREFIX/include/spa-0.2/$i" ] || LIST="$i $LIST"
|
||||
done
|
||||
|
||||
for i in $(find src/pipewire -name '*.h' -a -not -name '*private.h' | sed s#src/##);
|
||||
for i in $(find src/pipewire -name '*.h' -a -not -name '*private.h' -a -not -name 'cleanup.h' | sed s#src/##);
|
||||
do
|
||||
[ -f "$PREFIX/include/pipewire-0.3/$i" ] || LIST="$i $LIST"
|
||||
done
|
||||
|
|
|
|||
|
|
@ -14,5 +14,8 @@ spa_sections = [
|
|||
|
||||
spa_headers = 'spa' # used by doxygen
|
||||
install_subdir('spa',
|
||||
install_dir : get_option('includedir') / spa_name
|
||||
install_dir : get_option('includedir') / spa_name,
|
||||
exclude_files : [
|
||||
'utils/cleanup.h',
|
||||
],
|
||||
)
|
||||
|
|
|
|||
98
spa/include/spa/utils/cleanup.h
Normal file
98
spa/include/spa/utils/cleanup.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/* Simple Plugin API */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2023 PipeWire authors */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef SPA_UTILS_CLEANUP_H
|
||||
#define SPA_UTILS_CLEANUP_H
|
||||
|
||||
#if !defined(__has_attribute) || !__has_attribute(__cleanup__)
|
||||
#error "attribute `cleanup` is required"
|
||||
#endif
|
||||
|
||||
#define spa_cleanup(func) __attribute__((__cleanup__(func)))
|
||||
|
||||
#define SPA_DEFINE_AUTO_CLEANUP(name, type, ...) \
|
||||
typedef __typeof__(type) _spa_auto_cleanup_type_ ## name; \
|
||||
static inline void _spa_auto_cleanup_func_ ## name (__typeof__(type) *thing) \
|
||||
{ \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
|
||||
#define spa_auto(name) \
|
||||
spa_cleanup(_spa_auto_cleanup_func_ ## name) \
|
||||
_spa_auto_cleanup_type_ ## name
|
||||
|
||||
#define SPA_DEFINE_AUTOPTR_CLEANUP(name, type, ...) \
|
||||
typedef __typeof__(type) * _spa_autoptr_cleanup_type_ ## name; \
|
||||
static inline void _spa_autoptr_cleanup_func_ ## name (__typeof__(type) **thing) \
|
||||
{ \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
|
||||
#define spa_autoptr(name) \
|
||||
spa_cleanup(_spa_autoptr_cleanup_func_ ## name) \
|
||||
_spa_autoptr_cleanup_type_ ## name
|
||||
|
||||
#define spa_exchange(var, new_value) \
|
||||
__extension__ ({ \
|
||||
__typeof__(var) _old_value = (var); \
|
||||
(var) = (new_value); \
|
||||
_old_value; \
|
||||
})
|
||||
|
||||
#define spa_steal_ptr(ptr) ((__typeof__(*(ptr)) *) spa_exchange((ptr), NULL))
|
||||
#define spa_steal_fd(fd) spa_exchange((fd), -1)
|
||||
|
||||
/* ========================================================================== */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define spa_clear_ptr(ptr, destructor) \
|
||||
__extension__ ({ \
|
||||
__typeof__(*(ptr)) *_old_value = spa_steal_ptr(ptr); \
|
||||
if (_old_value) \
|
||||
destructor(_old_value); \
|
||||
(void) 0; \
|
||||
})
|
||||
|
||||
static inline void _spa_autofree_cleanup_func(void *p)
|
||||
{
|
||||
free(*(void **) p);
|
||||
}
|
||||
#define spa_autofree spa_cleanup(_spa_autofree_cleanup_func)
|
||||
|
||||
/* ========================================================================== */
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#define spa_clear_fd(fd) \
|
||||
__extension__ ({ \
|
||||
int _old_value = spa_steal_fd(fd), _res = 0; \
|
||||
if (_old_value >= 0) \
|
||||
_res = close(_old_value); \
|
||||
_res; \
|
||||
})
|
||||
|
||||
static inline void _spa_autoclose_cleanup_func(int *fd)
|
||||
{
|
||||
spa_clear_fd(*fd);
|
||||
}
|
||||
#define spa_autoclose spa_cleanup(_spa_autoclose_cleanup_func)
|
||||
|
||||
/* ========================================================================== */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
SPA_DEFINE_AUTOPTR_CLEANUP(FILE, FILE, {
|
||||
spa_clear_ptr(*thing, fclose);
|
||||
})
|
||||
|
||||
/* ========================================================================== */
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
SPA_DEFINE_AUTOPTR_CLEANUP(DIR, DIR, {
|
||||
spa_clear_ptr(*thing, closedir);
|
||||
})
|
||||
|
||||
#endif /* SPA_UTILS_CLEANUP_H */
|
||||
21
src/pipewire/cleanup.h
Normal file
21
src/pipewire/cleanup.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* PipeWire */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2023 PipeWire authors */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef PIPEWIRE_CLEANUP_H
|
||||
#define PIPEWIRE_CLEANUP_H
|
||||
|
||||
#include <spa/utils/cleanup.h>
|
||||
|
||||
#include <pipewire/properties.h>
|
||||
#include <pipewire/utils.h>
|
||||
|
||||
SPA_DEFINE_AUTOPTR_CLEANUP(pw_properties, struct pw_properties, {
|
||||
spa_clear_ptr(*thing, pw_properties_free);
|
||||
})
|
||||
|
||||
SPA_DEFINE_AUTO_CLEANUP(pw_strv, char **, {
|
||||
spa_clear_ptr(*thing, pw_free_strv);
|
||||
})
|
||||
|
||||
#endif /* PIPEWIRE_CLEANUP_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue