[vlc-commits] objres: introduce object resources tracking
Rémi Denis-Courmont
git at videolan.org
Mon Jun 19 21:52:23 CEST 2017
vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Wed Jun 14 19:49:22 2017 +0300| [749d0390621ceff07a51cbde9c54102bce6ed144] | committer: Rémi Denis-Courmont
objres: introduce object resources tracking
This very simplistic system (inspired by Linux kernel "devres") tracks a
list of allocated resources. It is intended to automatically release
resources allocated by a module instance when either activation fails,
or upon deactivation. That is meant to simplify error and cleanup code
paths.
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=749d0390621ceff07a51cbde9c54102bce6ed144
---
src/Makefile.am | 1 +
src/libvlc.h | 42 +++++++++++++++++
src/misc/objects.c | 3 ++
src/misc/objres.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/misc/variables.h | 5 ++
src/modules/modules.c | 7 ++-
6 files changed, 181 insertions(+), 1 deletion(-)
diff --git a/src/Makefile.am b/src/Makefile.am
index 5f3726c3e6..87cf8e3e29 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -336,6 +336,7 @@ libvlccore_la_SOURCES = \
misc/messages.c \
misc/mime.c \
misc/objects.c \
+ misc/objres.c \
misc/variables.h \
misc/variables.c \
misc/error.c \
diff --git a/src/libvlc.h b/src/libvlc.h
index 15db8e037f..299795c549 100644
--- a/src/libvlc.h
+++ b/src/libvlc.h
@@ -124,6 +124,48 @@ void vlc_object_set_destructor (vlc_object_t *, vlc_destructor_t);
#define vlc_object_set_destructor(a,b) \
vlc_object_set_destructor (VLC_OBJECT(a), b)
+/**
+ * Allocates an object resource.
+ *
+ * @param size storage size in bytes of the resource data
+ * @param release callback to release the resource
+ *
+ * @return a pointer to the (uninitialized) storage space, or NULL on error
+ */
+void *vlc_objres_new(size_t size, void (*release)(void *));
+
+/**
+ * Pushes an object resource on the object resources stack.
+ *
+ * @param obj object to allocate the resource for
+ * @param data resource base address (as returned by vlc_objres_new())
+ */
+void vlc_objres_push(vlc_object_t *obj, void *data);
+
+/**
+ * Releases all resources of an object.
+ *
+ * All resources added with vlc_objres_add() are released in reverse order.
+ * The resource list is reset to empty.
+ *
+ * @param obj object whose resources to release
+ */
+void vlc_objres_clear(vlc_object_t *obj);
+
+/**
+ * Releases one object resource explicitly.
+ *
+ * If a resource associated with an object needs to be released explicitly
+ * earlier than normal, call this function. This is relatively slow and should
+ * be avoided.
+ *
+ * @param obj object whose resource to release
+ * @param data private data for the comparison function
+ * @param match comparison function to match the targeted resource
+ */
+void vlc_objres_remove(vlc_object_t *obj, void *data,
+ bool (*match)(void *, void *));
+
#define ZOOM_SECTION N_("Zoom")
#define ZOOM_QUARTER_KEY_TEXT N_("1:4 Quarter")
#define ZOOM_HALF_KEY_TEXT N_("1:2 Half")
diff --git a/src/misc/objects.c b/src/misc/objects.c
index 3ed52713e8..c8fcccc9af 100644
--- a/src/misc/objects.c
+++ b/src/misc/objects.c
@@ -199,6 +199,7 @@ void *vlc_custom_create (vlc_object_t *parent, size_t length,
priv->prev = NULL;
priv->first = NULL;
vlc_mutex_init (&priv->tree_lock);
+ priv->resources = NULL;
vlc_object_t *obj = (vlc_object_t *)(priv + 1);
obj->obj.object_type = typename;
@@ -315,6 +316,8 @@ static void vlc_object_destroy( vlc_object_t *p_this )
{
vlc_object_internals_t *p_priv = vlc_internals( p_this );
+ assert(p_priv->resources == NULL);
+
/* Call the custom "subclass" destructor */
if( p_priv->pf_destructor )
p_priv->pf_destructor( p_this );
diff --git a/src/misc/objres.c b/src/misc/objres.c
new file mode 100644
index 0000000000..7f215bfccc
--- /dev/null
+++ b/src/misc/objres.c
@@ -0,0 +1,124 @@
+/*****************************************************************************
+ * objres.c: vlc_object_t resources
+ *****************************************************************************
+ * Copyright (C) 2017 Rémi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdalign.h>
+
+#include <vlc_common.h>
+
+#include "libvlc.h"
+#include "variables.h"
+
+struct vlc_res
+{
+ struct vlc_res *prev;
+ void (*release)(void *);
+ max_align_t payload[];
+};
+
+static struct vlc_res **vlc_obj_res(vlc_object_t *obj)
+{
+ return &vlc_internals(obj)->resources;
+}
+
+void *vlc_objres_new(size_t size, void (*release)(void *))
+{
+ if (unlikely(size > SIZE_MAX - sizeof (struct vlc_res)))
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ struct vlc_res *res = malloc(sizeof (*res) + size);
+ if (unlikely(res == NULL))
+ return NULL;
+
+ res->release = release;
+ return res->payload;
+}
+
+void vlc_objres_push(vlc_object_t *obj, void *data)
+{
+ struct vlc_res **restrict pp = vlc_obj_res(obj);
+ struct vlc_res *res = container_of(data, struct vlc_res, payload);
+
+ res->prev = *pp;
+ *pp = res;
+}
+
+static void *vlc_objres_pop(vlc_object_t *obj)
+{
+ struct vlc_res **restrict pp = vlc_obj_res(obj);
+ struct vlc_res *res = *pp;
+
+ if (res == NULL)
+ return NULL;
+ *pp = res->prev;
+ return res->payload;
+}
+
+void vlc_objres_clear(vlc_object_t *obj)
+{
+ void *data;
+
+ while ((data = vlc_objres_pop(obj)) != NULL)
+ {
+ struct vlc_res *res = container_of(data, struct vlc_res, payload);
+
+ res->release(res->payload);
+ free(res);
+ }
+}
+
+void vlc_objres_remove(vlc_object_t *obj, void *data,
+ bool (*match)(void *, void *))
+{
+ struct vlc_res **restrict pp = vlc_obj_res(obj);
+
+ /* With a doubly-linked list, this function could have constant complexity.
+ * But that would require one more pointer per resource.
+ *
+ * Any given list should contain a fairly small number of resources,
+ * and in most cases, the resources are destroyed implicitly by
+ * vlc_objres_clear().
+ */
+ for (;;)
+ {
+ struct vlc_res *res = *pp;
+
+ assert(res != NULL); /* invalid free? */
+
+ if (match(res->payload, data))
+ {
+ *pp = res->prev;
+ res->release(res->payload);
+ free(res);
+ return;
+ }
+
+ pp = &res->prev;
+ }
+}
diff --git a/src/misc/variables.h b/src/misc/variables.h
index cdfafe19d6..bab14c8567 100644
--- a/src/misc/variables.h
+++ b/src/misc/variables.h
@@ -26,6 +26,8 @@
# include <stdalign.h>
# include <vlc_atomic.h>
+struct vlc_res;
+
/**
* Private LibVLC data for each object.
*/
@@ -50,6 +52,9 @@ struct vlc_object_internals
vlc_object_internals_t *first; /* first child */
vlc_mutex_t tree_lock;
+ /* Object resources */
+ struct vlc_res *resources;
+
max_align_t aligned_end[];
};
diff --git a/src/modules/modules.c b/src/modules/modules.c
index 5873eff38a..ab6d29584e 100644
--- a/src/modules/modules.c
+++ b/src/modules/modules.c
@@ -183,6 +183,10 @@ static int module_load (vlc_object_t *obj, module_t *m,
ret = init (m->pf_activate, ap);
va_end (ap);
}
+
+ if (ret != VLC_SUCCESS)
+ vlc_objres_clear(obj);
+
return ret;
}
@@ -339,7 +343,8 @@ void vlc_module_unload(vlc_object_t *obj, module_t *module,
deinit(module->pf_deactivate, ap);
va_end(ap);
}
- (void) obj;
+
+ vlc_objres_clear(obj);
}
More information about the vlc-commits
mailing list