[vlc-devel] [PATCH 2/4] objres: introduce object resources tracking

Rémi Denis-Courmont remi at remlab.net
Sat Jun 17 21:39:44 CEST 2017


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.
---
 src/Makefile.am       |   1 +
 src/libvlc.h          |  42 +++++++++++++++++
 src/misc/objects.c    |   3 ++
 src/misc/objres.c     | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/misc/variables.h  |   5 ++
 src/modules/modules.c |   7 ++-
 6 files changed, 183 insertions(+), 1 deletion(-)
 create mode 100644 src/misc/objres.c

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..e6c9c46623
--- /dev/null
+++ b/src/misc/objres.c
@@ -0,0 +1,126 @@
+/*****************************************************************************
+ * 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);
+    }
+}
+
+#ifndef UNUSED_YET
+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;
+    }
+}
+#endif
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);
 }
 
 
-- 
2.11.0



More information about the vlc-devel mailing list