[vlc-devel] [PATCH 1/2] variables: var_CreateOps creates variable with create/dup/free callbacks

Antti Ajanki antti.ajanki at iki.fi
Tue Sep 9 18:58:15 CEST 2014


Use case: A reference counted variable can be safely stored as
VLC_VAR_VARIABLE if it is created with callbacks that correctly
manipulate the reference count.
---
 include/vlc_variables.h |   11 ++++
 src/misc/variables.c    |  150 +++++++++++++++++++++++++++++++++--------------
 src/misc/variables.h    |    2 +-
 3 files changed, 119 insertions(+), 44 deletions(-)

diff --git a/include/vlc_variables.h b/include/vlc_variables.h
index 6f48aa1..3a81d9a 100644
--- a/include/vlc_variables.h
+++ b/include/vlc_variables.h
@@ -138,12 +138,23 @@ enum {
 };
 /**@}*/
 
+typedef struct variable_management_ops_t
+{
+    vlc_value_t (*pf_init) ();
+    int         (*pf_cmp) ( vlc_value_t, vlc_value_t );
+    void        (*pf_dup) ( vlc_value_t * );
+    void        (*pf_free) ( vlc_value_t * );
+} variable_management_ops_t;
+
 /*****************************************************************************
  * Prototypes
  *****************************************************************************/
 VLC_API int var_Create( vlc_object_t *, const char *, int );
 #define var_Create(a,b,c) var_Create( VLC_OBJECT(a), b, c )
 
+VLC_API int var_CreateOps( vlc_object_t *, const char *, int, const variable_management_ops_t * );
+#define var_CreateOps(a,b,c,d) var_CreateOps( VLC_OBJECT(a), b, c, d )
+
 VLC_API int var_Destroy( vlc_object_t *, const char * );
 #define var_Destroy(a,b) var_Destroy( VLC_OBJECT(a), b )
 
diff --git a/src/misc/variables.c b/src/misc/variables.c
index 19b5fb7..07a1b9c 100644
--- a/src/misc/variables.c
+++ b/src/misc/variables.c
@@ -145,6 +145,12 @@ static int      TriggerListCallback( vlc_object_t *, variable_t *,
                                      const char *, int,
                                      vlc_value_t * );
 
+static int      InheritWithOps( vlc_object_t *, const char *, int,
+                                vlc_value_t *, variable_ops_t * );
+
+static int      GetCheckedWithOps( vlc_object_t *, const char *, int,
+                                   vlc_value_t *, variable_ops_t * );
+
 static int varcmp( const void *a, const void *b )
 {
     const variable_t *va = a, *vb = b;
@@ -166,12 +172,12 @@ static variable_t *Lookup( vlc_object_t *obj, const char *psz_name )
 
 static void Destroy( variable_t *p_var )
 {
-    p_var->ops->pf_free( &p_var->val );
+    p_var->ops.pf_free( &p_var->val );
     if( p_var->choices.i_count )
     {
         for( int i = 0 ; i < p_var->choices.i_count ; i++ )
         {
-            p_var->ops->pf_free( &p_var->choices.p_values[i] );
+            p_var->ops.pf_free( &p_var->choices.p_values[i] );
             free( p_var->choices_text.p_values[i].psz_string );
         }
         free( p_var->choices.p_values );
@@ -207,6 +213,24 @@ static void Destroy( variable_t *p_var )
 /**
  * Initialize a vlc variable
  *
+ * \param p_this The object in which to create the variable
+ * \param psz_name The name of the variable
+ * \param i_type The variables type. Must be one of \ref var_type combined with
+ *               zero or more \ref var_flags
+ */
+int var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
+{
+    return var_CreateOps( p_this, psz_name, i_type, NULL );
+}
+
+#undef var_CreateOps
+/**
+ * Initialize a vlc variable with callbacks
+ *
+ * If p_ops is defined, the callbacks in p_ops will be used to create,
+ * copy and release the value of this variable. The callbacks will be
+ * called with the variable mutex locked.
+ *
  * We hash the given string and insert it into the sorted list. The insertion
  * may require slow memory copies, but think about what we gain in the log(n)
  * lookup phase when setting/getting the variable value!
@@ -215,8 +239,9 @@ static void Destroy( variable_t *p_var )
  * \param psz_name The name of the variable
  * \param i_type The variables type. Must be one of \ref var_type combined with
  *               zero or more \ref var_flags
+ * \param p_ops An optional structure of resource management callbacks
  */
-int var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
+int var_CreateOps( vlc_object_t *p_this, const char *psz_name, int i_type, const variable_management_ops_t *p_ops )
 {
     assert( p_this );
 
@@ -246,43 +271,58 @@ int var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
     switch( i_type & VLC_VAR_CLASS )
     {
         case VLC_VAR_BOOL:
-            p_var->ops = &bool_ops;
+            p_var->ops = bool_ops;
             p_var->val.b_bool = false;
             break;
         case VLC_VAR_INTEGER:
-            p_var->ops = &int_ops;
+            p_var->ops = int_ops;
             p_var->val.i_int = 0;
             break;
         case VLC_VAR_STRING:
-            p_var->ops = &string_ops;
+            p_var->ops = string_ops;
             p_var->val.psz_string = NULL;
             break;
         case VLC_VAR_FLOAT:
-            p_var->ops = &float_ops;
+            p_var->ops = float_ops;
             p_var->val.f_float = 0.0;
             break;
         case VLC_VAR_TIME:
-            p_var->ops = &time_ops;
+            p_var->ops = time_ops;
             p_var->val.i_time = 0;
             break;
         case VLC_VAR_COORDS:
-            p_var->ops = &coords_ops;
+            p_var->ops = coords_ops;
             p_var->val.coords.x = p_var->val.coords.y = 0;
             break;
         case VLC_VAR_ADDRESS:
-            p_var->ops = &addr_ops;
+            p_var->ops = addr_ops;
             p_var->val.p_address = NULL;
             break;
         case VLC_VAR_VOID:
-            p_var->ops = &void_ops;
+            p_var->ops = void_ops;
             break;
         default:
             assert (0);
     }
 
+    if ( p_ops )
+    {
+        assert( p_ops->pf_init );
+        assert( p_ops->pf_cmp );
+        assert( p_ops->pf_dup );
+        assert( p_ops->pf_free );
+
+        p_var->ops.pf_cmp = p_ops->pf_cmp;
+        p_var->ops.pf_dup = p_ops->pf_dup;
+        p_var->ops.pf_free = p_ops->pf_free;
+    }
+
+    bool initialized_by_inherit = false;
     if( (i_type & VLC_VAR_DOINHERIT)
-     && var_Inherit( p_this, psz_name, i_type, &p_var->val ) == 0 )
+     && InheritWithOps( p_this, psz_name, i_type, &p_var->val, &p_var->ops ) == 0 )
     {
+        initialized_by_inherit = true;
+
         if( i_type & VLC_VAR_HASCHOICE )
         {
             /* We must add the inherited value to our choice list */
@@ -292,7 +332,7 @@ int var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
                          0, p_var->val );
             INSERT_ELEM( p_var->choices_text.p_values,
                          p_var->choices_text.i_count, 0, p_var->val );
-            p_var->ops->pf_dup( &p_var->choices.p_values[0] );
+            p_var->ops.pf_dup( &p_var->choices.p_values[0] );
             p_var->choices_text.p_values[0].psz_string = NULL;
         }
     }
@@ -307,7 +347,11 @@ int var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
     if( unlikely(pp_var == NULL) )
         ret = VLC_ENOMEM;
     else if( (p_oldvar = *pp_var) == p_var ) /* Variable create */
+    {
         p_var = NULL; /* Variable created */
+        if ( p_ops && !initialized_by_inherit )
+            (*pp_var)->val = p_ops->pf_init();
+    }
     else /* Variable already exists */
     {
         assert (((i_type ^ p_oldvar->i_type) & VLC_VAR_CLASS) == 0);
@@ -411,11 +455,11 @@ int var_Change( vlc_object_t *p_this, const char *psz_name,
         case VLC_VAR_SETMIN:
             if( p_var->i_type & VLC_VAR_HASMIN )
             {
-                p_var->ops->pf_free( &p_var->min );
+                p_var->ops.pf_free( &p_var->min );
             }
             p_var->i_type |= VLC_VAR_HASMIN;
             p_var->min = *p_val;
-            p_var->ops->pf_dup( &p_var->min );
+            p_var->ops.pf_dup( &p_var->min );
             CheckValue( p_var, &p_var->val );
             break;
         case VLC_VAR_GETMIN:
@@ -427,11 +471,11 @@ int var_Change( vlc_object_t *p_this, const char *psz_name,
         case VLC_VAR_SETMAX:
             if( p_var->i_type & VLC_VAR_HASMAX )
             {
-                p_var->ops->pf_free( &p_var->max );
+                p_var->ops.pf_free( &p_var->max );
             }
             p_var->i_type |= VLC_VAR_HASMAX;
             p_var->max = *p_val;
-            p_var->ops->pf_dup( &p_var->max );
+            p_var->ops.pf_dup( &p_var->max );
             CheckValue( p_var, &p_var->val );
             break;
         case VLC_VAR_GETMAX:
@@ -443,11 +487,11 @@ int var_Change( vlc_object_t *p_this, const char *psz_name,
         case VLC_VAR_SETSTEP:
             if( p_var->i_type & VLC_VAR_HASSTEP )
             {
-                p_var->ops->pf_free( &p_var->step );
+                p_var->ops.pf_free( &p_var->step );
             }
             p_var->i_type |= VLC_VAR_HASSTEP;
             p_var->step = *p_val;
-            p_var->ops->pf_dup( &p_var->step );
+            p_var->ops.pf_dup( &p_var->step );
             CheckValue( p_var, &p_var->val );
             break;
         case VLC_VAR_GETSTEP:
@@ -464,7 +508,7 @@ int var_Change( vlc_object_t *p_this, const char *psz_name,
                          i, *p_val );
             INSERT_ELEM( p_var->choices_text.p_values,
                          p_var->choices_text.i_count, i, *p_val );
-            p_var->ops->pf_dup( &p_var->choices.p_values[i] );
+            p_var->ops.pf_dup( &p_var->choices.p_values[i] );
             p_var->choices_text.p_values[i].psz_string =
                 ( p_val2 && p_val2->psz_string ) ?
                 strdup( p_val2->psz_string ) : NULL;
@@ -479,7 +523,7 @@ int var_Change( vlc_object_t *p_this, const char *psz_name,
             int i;
 
             for( i = 0 ; i < p_var->choices.i_count ; i++ )
-                if( p_var->ops->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
+                if( p_var->ops.pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
                     break;
 
             if( i == p_var->choices.i_count )
@@ -494,7 +538,7 @@ int var_Change( vlc_object_t *p_this, const char *psz_name,
             else if( p_var->i_default == i )
                 p_var->i_default = -1;
 
-            p_var->ops->pf_free( &p_var->choices.p_values[i] );
+            p_var->ops.pf_free( &p_var->choices.p_values[i] );
             free( p_var->choices_text.p_values[i].psz_string );
             REMOVE_ELEM( p_var->choices.p_values, p_var->choices.i_count, i );
             REMOVE_ELEM( p_var->choices_text.p_values,
@@ -510,7 +554,7 @@ int var_Change( vlc_object_t *p_this, const char *psz_name,
             break;
         case VLC_VAR_CLEARCHOICES:
             for( int i = 0 ; i < p_var->choices.i_count ; i++ )
-                p_var->ops->pf_free( &p_var->choices.p_values[i] );
+                p_var->ops.pf_free( &p_var->choices.p_values[i] );
             for( int i = 0 ; i < p_var->choices_text.i_count ; i++ )
                 free( p_var->choices_text.p_values[i].psz_string );
 
@@ -529,7 +573,7 @@ int var_Change( vlc_object_t *p_this, const char *psz_name,
             int i;
             /* FIXME: the list is sorted, dude. Use something cleverer. */
             for( i = 0 ; i < p_var->choices.i_count ; i++ )
-                if( p_var->ops->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
+                if( p_var->ops.pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
                     break;
 
             if( i == p_var->choices.i_count )
@@ -543,7 +587,7 @@ int var_Change( vlc_object_t *p_this, const char *psz_name,
         case VLC_VAR_SETVALUE:
             /* Duplicate data if needed */
             newval = *p_val;
-            p_var->ops->pf_dup( &newval );
+            p_var->ops.pf_dup( &newval );
             /* Backup needed stuff */
             oldval = p_var->val;
             /* Check boundaries and list */
@@ -551,7 +595,7 @@ int var_Change( vlc_object_t *p_this, const char *psz_name,
             /* Set the variable */
             p_var->val = newval;
             /* Free data if needed */
-            p_var->ops->pf_free( &oldval );
+            p_var->ops.pf_free( &oldval );
             break;
         case VLC_VAR_GETCHOICES:
         case VLC_VAR_GETLIST:
@@ -577,7 +621,7 @@ int var_Change( vlc_object_t *p_this, const char *psz_name,
             {
                 p_val->p_list->p_values[i] = p_var->choices.p_values[i];
                 p_val->p_list->pi_types[i] = p_var->i_type;
-                p_var->ops->pf_dup( &p_val->p_list->p_values[i] );
+                p_var->ops.pf_dup( &p_val->p_list->p_values[i] );
                 if( p_val2 )
                 {
                     p_val2->p_list->p_values[i].psz_string =
@@ -640,7 +684,7 @@ int var_GetAndSet( vlc_object_t *p_this, const char *psz_name, int i_action,
     WaitUnused( p_this, p_var );
 
     /* Duplicated data if needed */
-    //p_var->ops->pf_dup( &val );
+    //p_var->ops.pf_dup( &val );
 
     /* Backup needed stuff */
     oldval = p_var->val;
@@ -737,7 +781,7 @@ int var_SetChecked( vlc_object_t *p_this, const char *psz_name,
     WaitUnused( p_this, p_var );
 
     /* Duplicate data if needed */
-    p_var->ops->pf_dup( &val );
+    p_var->ops.pf_dup( &val );
 
     /* Backup needed stuff */
     oldval = p_var->val;
@@ -752,7 +796,7 @@ int var_SetChecked( vlc_object_t *p_this, const char *psz_name,
     i_ret = TriggerCallback( p_this, p_var, psz_name, oldval );
 
     /* Free data if needed */
-    p_var->ops->pf_free( &oldval );
+    p_var->ops.pf_free( &oldval );
 
     vlc_mutex_unlock( &p_priv->var_lock );
 
@@ -776,6 +820,15 @@ int var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
 int var_GetChecked( vlc_object_t *p_this, const char *psz_name,
                     int expected_type, vlc_value_t *p_val )
 {
+    return GetCheckedWithOps( p_this, psz_name, expected_type, p_val, NULL );
+}
+
+static int GetCheckedWithOps( vlc_object_t *p_this, const char *psz_name,
+                              int expected_type, vlc_value_t *p_val,
+                              variable_ops_t *p_ops )
+{
+    VLC_UNUSED(expected_type);
+
     assert( p_this );
 
     vlc_object_internals_t *p_priv = vlc_internals( p_this );
@@ -795,7 +848,11 @@ int var_GetChecked( vlc_object_t *p_this, const char *psz_name,
         *p_val = p_var->val;
 
         /* Duplicate value if needed */
-        p_var->ops->pf_dup( p_val );
+        p_var->ops.pf_dup( p_val );
+
+        /* Optionally return ops */
+        if ( p_ops )
+            *p_ops = p_var->ops;
     }
     else
         err = VLC_ENOVAR;
@@ -1207,7 +1264,7 @@ static void CheckValue ( variable_t *p_var, vlc_value_t *p_val )
         /* This list is not sorted so go throug it (this is a small list) */
         for( i = p_var->choices.i_count ; i-- ; )
         {
-            if( p_var->ops->pf_cmp( *p_val, p_var->choices.p_values[i] ) == 0 )
+            if( p_var->ops.pf_cmp( *p_val, p_var->choices.p_values[i] ) == 0 )
             {
                 break;
             }
@@ -1217,10 +1274,10 @@ static void CheckValue ( variable_t *p_var, vlc_value_t *p_val )
         if( i < 0 )
         {
             /* Free the old variable, get the new one, dup it */
-            p_var->ops->pf_free( p_val );
+            p_var->ops.pf_free( p_val );
             *p_val = p_var->choices.p_values[p_var->i_default >= 0
                                           ? p_var->i_default : 0 ];
-            p_var->ops->pf_dup( p_val );
+            p_var->ops.pf_dup( p_val );
         }
     }
 
@@ -1269,19 +1326,14 @@ static void CheckValue ( variable_t *p_var, vlc_value_t *p_val )
     }
 }
 
-/**
- * Finds the value of a variable. If the specified object does not hold a
- * variable with the specified name, try the parent object, and iterate until
- * the top of the tree. If no match is found, the value is read from the
- * configuration.
- */
-int var_Inherit( vlc_object_t *p_this, const char *psz_name, int i_type,
-                 vlc_value_t *p_val )
+static int InheritWithOps( vlc_object_t *p_this, const char *psz_name,
+                           int i_type, vlc_value_t *p_val,
+                           variable_ops_t *p_ops )
 {
     i_type &= VLC_VAR_CLASS;
     for( vlc_object_t *obj = p_this; obj != NULL; obj = obj->p_parent )
     {
-        if( var_GetChecked( obj, psz_name, i_type, p_val ) == VLC_SUCCESS )
+        if( GetCheckedWithOps( obj, psz_name, i_type, p_val, p_ops ) == VLC_SUCCESS )
             return VLC_SUCCESS;
     }
 
@@ -1309,6 +1361,18 @@ int var_Inherit( vlc_object_t *p_this, const char *psz_name, int i_type,
     return VLC_SUCCESS;
 }
 
+/**
+ * Finds the value of a variable. If the specified object does not hold a
+ * variable with the specified name, try the parent object, and iterate until
+ * the top of the tree. If no match is found, the value is read from the
+ * configuration.
+ */
+int var_Inherit( vlc_object_t *p_this, const char *psz_name, int i_type,
+                 vlc_value_t *p_val )
+{
+    return InheritWithOps( p_this, psz_name, i_type, p_val, NULL );
+}
+
 
 /**
  * It inherits a string as an unsigned rational number (it also accepts basic
diff --git a/src/misc/variables.h b/src/misc/variables.h
index 35b8203..8571143 100644
--- a/src/misc/variables.h
+++ b/src/misc/variables.h
@@ -86,7 +86,7 @@ struct variable_t
     /** The variable display name, mainly for use by the interfaces */
     char *       psz_text;
 
-    const variable_ops_t *ops;
+    variable_ops_t ops;
 
     int          i_type;   /**< The type of the variable */
     unsigned     i_usage;  /**< Reference count */
-- 
1.7.10.4




More information about the vlc-devel mailing list