[vlc-devel] [PATCH 1/2] core: Add a vlc_instance_holder API

Hugo Beauzée-Luyssen hugo at beauzee.fr
Sat Jul 1 11:22:25 CEST 2017


This API can be used to store an instance in the core, removing the use
for some special shared libraries/shared variables without refcounting
---
 include/vlc_instance_holder.h |  74 +++++++++++++++++
 include/vlc_threads.h         |   1 +
 src/Makefile.am               |   4 +-
 src/libvlc.c                  |   4 +
 src/libvlc.h                  |   2 +
 src/libvlccore.sym            |   3 +
 src/misc/instance_holder.c    | 181 ++++++++++++++++++++++++++++++++++++++++++
 src/misc/threads.c            |   1 +
 8 files changed, 269 insertions(+), 1 deletion(-)
 create mode 100644 include/vlc_instance_holder.h
 create mode 100644 src/misc/instance_holder.c

diff --git a/include/vlc_instance_holder.h b/include/vlc_instance_holder.h
new file mode 100644
index 0000000000..72f21e1e95
--- /dev/null
+++ b/include/vlc_instance_holder.h
@@ -0,0 +1,74 @@
+/*****************************************************************************
+ * vlc_instance_holder.h: Provide functions to hold a unique refcounted opaque
+ * instance.
+ *****************************************************************************
+ * Copyright (C) 2004-2016 VLC authors, VideoLAN and VideoLabs
+ *
+ * Authors: Hugo Beauzée-Luyssen <hugo at beauzee.fr>
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifndef VLC_INSTANCE_HOLDER_H
+#define VLC_INSTANCE_HOLDER_H
+
+#include <vlc_common.h>
+
+/**
+ * Will acquire the instance with the given name.
+ * \param p_obj          A vlc_object_t instance
+ * \param psz_name       The instance name
+ * \param pf_constructor Will be invoked to create the instance when the provided
+ *                       name doesn't refer to an existing instance. If the
+ *                       constructor function pointer is NULL, the instance won't
+ *                       be created.
+ * \param pf_destructor  Will be invoked to destroy the instance when its
+ *                       refount reaches 0. If the opaque context was allocated
+ *                       the destructor is expected to release it as well. This
+ *                       can be NULL.
+ * \param p_opaque       An opaque context that will be owned by the instance
+ *                       holder. It is expected to be release by pf_destructor.
+ *                       This can be NULL.
+ */
+VLC_API void* vlc_instance_holder_acquire( vlc_object_t* p_obj,
+                const char* psz_name, void* (*pf_constructor)( void* ),
+                void (*pf_destructor)( void*, void* ), void* p_opaque );
+
+/**
+ * Return an existing instance, or NULL.
+ *
+ * This is equivalent to calling vlc_instance_holder_acquire with a
+ * NULL constructor, a NULL destructor, and a NULL context.
+ * This is guaranteed not to create a new instance.
+ * If an instance is returned, its refcount will be incremented.
+ * \param p_obj A vlc_object_t
+ * \param psz_name The instance name
+ */
+VLC_API void* vlc_instance_holder_get( vlc_object_t* p_obj,
+                                               const char* psz_name );
+
+/**
+ * Decrease the instance refcount.
+ *
+ * When the refcount reaches 0, the destructor provided upon first creation will
+ * be invoked.
+ */
+VLC_API void vlc_instance_holder_release( vlc_object_t* p_obj,
+                                                   const char* psz_name );
+
+int libvlc_InstanceHolderCreate( libvlc_int_t* p_libvlc );
+void libvlc_InstanceHolderRelease( libvlc_int_t* p_libvlc );
+
+#endif // VLC_INSTANCE_HOLDER_H
diff --git a/include/vlc_threads.h b/include/vlc_threads.h
index 960d458bb2..55b5753234 100644
--- a/include/vlc_threads.h
+++ b/include/vlc_threads.h
@@ -1053,6 +1053,7 @@ enum
    VLC_XLIB_MUTEX,
    VLC_MOSAIC_MUTEX,
    VLC_HIGHLIGHT_MUTEX,
+   VLC_INSTANCE_HOLDER_MUTEX,
 #ifdef _WIN32
    VLC_MTA_MUTEX,
 #endif
diff --git a/src/Makefile.am b/src/Makefile.am
index c93a4021ee..24970b0bec 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -57,6 +57,7 @@ pluginsinclude_HEADERS = \
 	../include/vlc_inhibit.h \
 	../include/vlc_input.h \
 	../include/vlc_input_item.h \
+	../include/vlc_instance_holder.h \
 	../include/vlc_interface.h \
 	../include/vlc_keys.h \
 	../include/vlc_keystore.h \
@@ -348,7 +349,8 @@ libvlccore_la_SOURCES = \
 	misc/fingerprinter.c \
 	misc/text_style.c \
 	misc/subpicture.c \
-	misc/subpicture.h
+	misc/subpicture.h \
+	misc/instance_holder.c
 libvlccore_la_LIBADD = $(LIBS_libvlccore) \
 	../compat/libcompat.la \
 	$(LTLIBINTL) $(LTLIBICONV) \
diff --git a/src/libvlc.c b/src/libvlc.c
index c7e7b9013d..46e97cfd04 100644
--- a/src/libvlc.c
+++ b/src/libvlc.c
@@ -61,6 +61,7 @@
 #include <vlc_cpu.h>
 #include <vlc_url.h>
 #include <vlc_modules.h>
+#include <vlc_instance_holder.h>
 
 #include "libvlc.h"
 #include "playlist/playlist_internal.h"
@@ -225,6 +226,8 @@ int libvlc_InternalInit( libvlc_int_t *p_libvlc, int i_argc,
         goto error;
     if( libvlc_InternalKeystoreInit( p_libvlc ) != VLC_SUCCESS )
         msg_Warn( p_libvlc, "memory keystore init failed" );
+    if ( libvlc_InstanceHolderCreate( p_libvlc ) != VLC_SUCCESS )
+        goto error;
 
     vlc_CPU_dump( VLC_OBJECT(p_libvlc) );
 
@@ -390,6 +393,7 @@ void libvlc_InternalCleanup( libvlc_int_t *p_libvlc )
     msg_Dbg( p_libvlc, "removing all interfaces" );
     intf_DestroyAll( p_libvlc );
 
+    libvlc_InstanceHolderRelease( p_libvlc );
     libvlc_InternalDialogClean( p_libvlc );
     libvlc_InternalKeystoreClean( p_libvlc );
 
diff --git a/src/libvlc.h b/src/libvlc.h
index 299795c549..4866e0da5f 100644
--- a/src/libvlc.h
+++ b/src/libvlc.h
@@ -177,6 +177,7 @@ void vlc_objres_remove(vlc_object_t *obj, void *data,
  */
 typedef struct vlc_dialog_provider vlc_dialog_provider;
 typedef struct vlc_keystore vlc_keystore;
+typedef struct vlc_instance_holder vlc_instance_holder;
 
 typedef struct libvlc_priv_t
 {
@@ -190,6 +191,7 @@ typedef struct libvlc_priv_t
     vlm_t             *p_vlm;  ///< the VLM singleton (or NULL)
     vlc_dialog_provider *p_dialog_provider; ///< dialog provider
     vlc_keystore      *p_memory_keystore; ///< memory keystore
+    vlc_instance_holder *p_instance_holder; ///< Store global opaque instances
     struct playlist_t *playlist; ///< Playlist for interfaces
     struct playlist_preparser_t *parser; ///< Input item meta data handler
     struct vlc_actions *actions; ///< Hotkeys handler
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index 58a54bb0f0..2d809d0b39 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -755,3 +755,6 @@ vlc_rd_get_names
 vlc_rd_new
 vlc_rd_release
 vlc_rd_probe_add
+vlc_instance_holder_acquire
+vlc_instance_holder_release
+vlc_instance_holder_get
diff --git a/src/misc/instance_holder.c b/src/misc/instance_holder.c
new file mode 100644
index 0000000000..c236366fcc
--- /dev/null
+++ b/src/misc/instance_holder.c
@@ -0,0 +1,181 @@
+/*****************************************************************************
+ * instance_holder.c: Holds a unique refcounted opaque instance.
+ *****************************************************************************
+ * Copyright (C) 2004-2016 VLC authors, VideoLAN and VideoLabs
+ *
+ * Authors: Hugo Beauzée-Luyssen <hugo at beauzee.fr>
+ *
+ * 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 <vlc_instance_holder.h>
+#include <vlc_threads.h>
+#include "libvlc.h"
+
+#include <assert.h>
+#ifdef HAVE_SEARCH_H
+# include <search.h>
+#endif
+
+typedef struct instance
+{
+    char* psz_name;
+    void* p_instance;
+    void* p_opaque;
+    unsigned int i_refcount;
+    void (*pf_destructor)( void*, void* );
+    vlc_mutex_t lock;
+} instance;
+
+typedef struct vlc_instance_holder
+{
+    void* p_root;
+} vlc_instance_holder;
+
+static vlc_instance_holder* instance_holder_create( void )
+{
+    vlc_instance_holder* p_holder = malloc( sizeof( *p_holder ) );
+    if( unlikely( p_holder == NULL ) )
+        return NULL;
+    p_holder->p_root = NULL;
+    return p_holder;
+}
+
+int libvlc_InstanceHolderCreate( libvlc_int_t* p_libvlc )
+{
+    libvlc_priv_t* p_priv = libvlc_priv( p_libvlc );
+    p_priv->p_instance_holder = instance_holder_create();
+    if( unlikely( p_priv->p_instance_holder == NULL ) )
+        return VLC_ENOMEM;
+    return VLC_SUCCESS;
+}
+
+void libvlc_InstanceHolderRelease( libvlc_int_t* p_libvlc )
+{
+    libvlc_priv_t* p_priv = libvlc_priv( p_libvlc );
+    // We expect all instances to have been released when the instance holder
+    // gets released.
+    assert( p_priv->p_instance_holder->p_root == NULL );
+    free( p_priv->p_instance_holder );
+    p_priv->p_instance_holder = NULL;
+}
+
+static int instance_key_compare( const void* l, const void* r )
+{
+    const instance* p_left = l, *p_right = r;
+    return strcmp( p_left->psz_name, p_right->psz_name );
+}
+
+void* vlc_instance_holder_acquire( vlc_object_t* p_obj,
+            const char* psz_name, void*(*pf_constructor)( void* ),
+            void(*pf_destructor)( void*, void* ), void* p_opaque )
+{
+    vlc_instance_holder* p_holder = libvlc_priv( p_obj->obj.libvlc )->p_instance_holder;
+    assert( p_holder != NULL );
+    instance* p_instance = malloc( sizeof( *p_instance ) );
+    if( unlikely( p_instance == NULL ) )
+        return NULL;
+    /* Simply copy the pointer to the string until we know if the variable
+     * was created or not: */
+    p_instance->psz_name = (char*)psz_name;
+    vlc_global_lock( VLC_INSTANCE_HOLDER_MUTEX );
+    void** pp_key = tsearch( p_instance, &p_holder->p_root,
+                             &instance_key_compare );
+    /* If the instance just got created, fully initialize it */
+    if( pp_key != NULL && *pp_key == p_instance )
+    {
+        p_instance->psz_name = strdup( psz_name );
+        if( p_instance->psz_name != NULL )
+        {
+            if ( pf_constructor == NULL )
+            {
+                tdelete( p_instance, &p_holder->p_root, &instance_key_compare );
+                free( p_instance );
+                vlc_global_unlock( VLC_INSTANCE_HOLDER_MUTEX );
+                return NULL;
+            }
+            p_instance->p_instance = pf_constructor( p_opaque );
+        }
+        if( p_instance->psz_name == NULL || p_instance->p_instance == NULL )
+        {
+            tdelete( p_instance, &p_holder->p_root, &instance_key_compare );
+            free( p_instance );
+            vlc_global_unlock( VLC_INSTANCE_HOLDER_MUTEX );
+            return NULL;
+        }
+        vlc_mutex_init( &p_instance->lock );
+        p_instance->i_refcount = 1;
+        p_instance->pf_destructor = pf_destructor;
+        p_instance->p_opaque = p_opaque;
+    }
+    vlc_global_unlock( VLC_INSTANCE_HOLDER_MUTEX );
+
+    // Failed to create a new instance:
+    if( unlikely( pp_key == NULL ) )
+        return NULL;
+    // Got an already existing instance back:
+    if( *pp_key == p_instance )
+        return p_instance->p_instance;
+    // The instance was created:
+    free( p_instance );
+    instance* p_existing_instance = (instance*)*pp_key;
+    vlc_mutex_lock( &p_existing_instance->lock );
+    p_existing_instance->i_refcount++;
+    vlc_mutex_unlock( &p_existing_instance->lock );
+    return p_existing_instance->p_instance;
+}
+
+void* vlc_instance_holder_get( vlc_object_t* p_obj,
+                                       const char* psz_name )
+{
+    return vlc_instance_holder_acquire( p_obj, psz_name, NULL,
+                                                 NULL, NULL );
+}
+
+void vlc_instance_holder_release( vlc_object_t* p_obj,
+                                           const char *psz_name )
+{
+    vlc_instance_holder* p_holder = libvlc_priv( p_obj->obj.libvlc )->p_instance_holder;
+    assert( p_holder != NULL );
+    instance inst = { .psz_name = (char*)psz_name };
+    vlc_global_lock( VLC_INSTANCE_HOLDER_MUTEX );
+    instance** pp_inst = tfind( &inst, &p_holder->p_root, &instance_key_compare );
+    instance* p_inst = NULL;
+    bool b_delete = false;
+    if( pp_inst != NULL )
+    {
+        p_inst = *pp_inst;
+        vlc_mutex_lock( &p_inst->lock );
+        if( --p_inst->i_refcount == 0 )
+        {
+            tdelete( p_inst, &p_holder->p_root, &instance_key_compare );
+            b_delete = true;
+        }
+        vlc_mutex_unlock( &p_inst->lock );
+    }
+    vlc_global_unlock( VLC_INSTANCE_HOLDER_MUTEX );
+    if( b_delete == true )
+    {
+        if( p_inst->pf_destructor != NULL )
+            p_inst->pf_destructor( p_inst->p_instance, p_inst->p_opaque );
+        free( p_inst->psz_name );
+        vlc_mutex_destroy( &p_inst->lock );
+        free( p_inst );
+    }
+}
diff --git a/src/misc/threads.c b/src/misc/threads.c
index 3d099db6cb..f0d709e300 100644
--- a/src/misc/threads.c
+++ b/src/misc/threads.c
@@ -37,6 +37,7 @@ void vlc_global_mutex (unsigned n, bool acquire)
         VLC_STATIC_MUTEX,
         VLC_STATIC_MUTEX,
         VLC_STATIC_MUTEX,
+        VLC_STATIC_MUTEX,
 #ifdef _WIN32
         VLC_STATIC_MUTEX, // For MTA holder
 #endif
-- 
2.11.0



More information about the vlc-devel mailing list