Commit 749d0390 authored by Rémi Denis-Courmont's avatar 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.
parent 9bbea90c
......@@ -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 \
......
......@@ -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")
......
......@@ -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 );
......
/*****************************************************************************
* 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;
}
}
......@@ -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[];
};
......
......@@ -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);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment