Commit c5539fac authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

objects: per-object tree lock

This replaces the structure lock per instance with a lock per (parent)
object. The point is to reduce lock contention in vlc_object_release().
parent f3c8e16f
...@@ -155,9 +155,6 @@ typedef struct libvlc_priv_t ...@@ -155,9 +155,6 @@ typedef struct libvlc_priv_t
struct playlist_preparser_t *parser; ///< Input item meta data handler struct playlist_preparser_t *parser; ///< Input item meta data handler
struct vlc_actions *actions; ///< Hotkeys handler struct vlc_actions *actions; ///< Hotkeys handler
/* Objects tree */
vlc_mutex_t structure_lock;
/* Exit callback */ /* Exit callback */
vlc_exit_t exit; vlc_exit_t exit;
} libvlc_priv_t; } libvlc_priv_t;
......
...@@ -55,19 +55,6 @@ ...@@ -55,19 +55,6 @@
#include <limits.h> #include <limits.h>
#include <assert.h> #include <assert.h>
/*****************************************************************************
* Local structure lock
*****************************************************************************/
static void libvlc_lock (libvlc_int_t *p_libvlc)
{
vlc_mutex_lock (&(libvlc_priv (p_libvlc)->structure_lock));
}
static void libvlc_unlock (libvlc_int_t *p_libvlc)
{
vlc_mutex_unlock (&(libvlc_priv (p_libvlc)->structure_lock));
}
static void PrintObject (vlc_object_t *obj, const char *prefix) static void PrintObject (vlc_object_t *obj, const char *prefix)
{ {
vlc_object_internals_t *priv = vlc_internals(obj); vlc_object_internals_t *priv = vlc_internals(obj);
...@@ -95,6 +82,8 @@ static void DumpStructure (vlc_object_t *obj, unsigned level, char *psz_foo) ...@@ -95,6 +82,8 @@ static void DumpStructure (vlc_object_t *obj, unsigned level, char *psz_foo)
vlc_object_internals_t *priv = vlc_internals(obj); vlc_object_internals_t *priv = vlc_internals(obj);
/* NOTE: nested locking here (due to recursive call) */
vlc_mutex_lock (&vlc_internals(obj)->tree_lock);
for (priv = priv->first; priv != NULL; priv = priv->next) for (priv = priv->first; priv != NULL; priv = priv->next)
{ {
if (level > 0) if (level > 0)
...@@ -112,6 +101,7 @@ static void DumpStructure (vlc_object_t *obj, unsigned level, char *psz_foo) ...@@ -112,6 +101,7 @@ static void DumpStructure (vlc_object_t *obj, unsigned level, char *psz_foo)
DumpStructure (vlc_externals(priv), level + 2, psz_foo); DumpStructure (vlc_externals(priv), level + 2, psz_foo);
} }
vlc_mutex_unlock (&vlc_internals(obj)->tree_lock);
} }
/** /**
...@@ -126,7 +116,6 @@ static int TreeCommand (vlc_object_t *obj, char const *cmd, ...@@ -126,7 +116,6 @@ static int TreeCommand (vlc_object_t *obj, char const *cmd,
{ {
(void) cmd; (void) oldval; (void) newval; (void) data; (void) cmd; (void) oldval; (void) newval; (void) data;
libvlc_lock ((libvlc_int_t *)obj);
if (cmd[0] == 't') if (cmd[0] == 't')
{ {
char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1]; char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
...@@ -134,23 +123,26 @@ static int TreeCommand (vlc_object_t *obj, char const *cmd, ...@@ -134,23 +123,26 @@ static int TreeCommand (vlc_object_t *obj, char const *cmd,
psz_foo[0] = '|'; psz_foo[0] = '|';
DumpStructure (obj, 0, psz_foo); DumpStructure (obj, 0, psz_foo);
} }
libvlc_unlock ((libvlc_int_t *)obj);
return VLC_SUCCESS; return VLC_SUCCESS;
} }
static bool ObjectExists (vlc_object_t *root, void *obj) static vlc_object_t *ObjectExists (vlc_object_t *root, void *obj)
{ {
if (root == obj) if (root == obj)
return true; return vlc_object_hold (root);
vlc_object_internals_t *priv = vlc_internals(root); vlc_object_internals_t *priv = vlc_internals(root);
vlc_object_t *ret = NULL;
for (priv = priv->first; priv != NULL; priv = priv->next) /* NOTE: nested locking here (due to recursive call) */
if (ObjectExists (vlc_externals (priv), obj)) vlc_mutex_lock (&vlc_internals(root)->tree_lock);
return true;
return false; for (priv = priv->first; priv != NULL && ret == NULL; priv = priv->next)
ret = ObjectExists (vlc_externals (priv), obj);
vlc_mutex_unlock (&vlc_internals(root)->tree_lock);
return ret;
} }
static int VarsCommand (vlc_object_t *obj, char const *cmd, static int VarsCommand (vlc_object_t *obj, char const *cmd,
...@@ -162,19 +154,12 @@ static int VarsCommand (vlc_object_t *obj, char const *cmd, ...@@ -162,19 +154,12 @@ static int VarsCommand (vlc_object_t *obj, char const *cmd,
if (sscanf (newval.psz_string, "%p", &p) == 1) if (sscanf (newval.psz_string, "%p", &p) == 1)
{ {
libvlc_lock ((libvlc_int_t *)obj); p = ObjectExists (obj, p);
if (ObjectExists (obj, p))
vlc_object_hold ((vlc_object_t *)p);
else
p = NULL;
libvlc_unlock ((libvlc_int_t *)obj);
if (p == NULL) if (p == NULL)
{ {
msg_Err (obj, "no such object: %s", newval.psz_string); msg_Err (obj, "no such object: %s", newval.psz_string);
return VLC_ENOOBJ; return VLC_ENOOBJ;
} }
obj = p; obj = p;
} }
else else
...@@ -213,6 +198,7 @@ void *vlc_custom_create (vlc_object_t *parent, size_t length, ...@@ -213,6 +198,7 @@ void *vlc_custom_create (vlc_object_t *parent, size_t length,
priv->pf_destructor = NULL; priv->pf_destructor = NULL;
priv->prev = NULL; priv->prev = NULL;
priv->first = NULL; priv->first = NULL;
vlc_mutex_init (&priv->tree_lock);
vlc_object_t *obj = (vlc_object_t *)(priv + 1); vlc_object_t *obj = (vlc_object_t *)(priv + 1);
obj->psz_object_type = typename; obj->psz_object_type = typename;
...@@ -231,12 +217,12 @@ void *vlc_custom_create (vlc_object_t *parent, size_t length, ...@@ -231,12 +217,12 @@ void *vlc_custom_create (vlc_object_t *parent, size_t length,
obj->p_parent = vlc_object_hold (parent); obj->p_parent = vlc_object_hold (parent);
/* Attach the parent to its child (structure lock needed) */ /* Attach the parent to its child (structure lock needed) */
libvlc_lock (obj->p_libvlc); vlc_mutex_lock (&papriv->tree_lock);
priv->next = papriv->first; priv->next = papriv->first;
if (priv->next != NULL) if (priv->next != NULL)
priv->next->prev = priv; priv->next->prev = priv;
papriv->first = priv; papriv->first = priv;
libvlc_unlock (obj->p_libvlc); vlc_mutex_unlock (&papriv->tree_lock);
} }
else else
{ {
...@@ -246,7 +232,6 @@ void *vlc_custom_create (vlc_object_t *parent, size_t length, ...@@ -246,7 +232,6 @@ void *vlc_custom_create (vlc_object_t *parent, size_t length,
obj->p_libvlc = self; obj->p_libvlc = self;
obj->p_parent = NULL; obj->p_parent = NULL;
priv->next = NULL; priv->next = NULL;
vlc_mutex_init (&(libvlc_priv (self)->structure_lock));
/* TODO: should be in src/libvlc.c */ /* TODO: should be in src/libvlc.c */
int canc = vlc_savecancel (); int canc = vlc_savecancel ();
...@@ -344,31 +329,31 @@ static void vlc_object_destroy( vlc_object_t *p_this ) ...@@ -344,31 +329,31 @@ static void vlc_object_destroy( vlc_object_t *p_this )
/* Destroy the associated variables. */ /* Destroy the associated variables. */
var_DestroyAll( p_this ); var_DestroyAll( p_this );
vlc_mutex_destroy (&p_priv->tree_lock);
vlc_cond_destroy( &p_priv->var_wait ); vlc_cond_destroy( &p_priv->var_wait );
vlc_mutex_destroy( &p_priv->var_lock ); vlc_mutex_destroy( &p_priv->var_lock );
free( p_this->psz_header ); free( p_this->psz_header );
free( p_priv->psz_name ); free( p_priv->psz_name );
if( VLC_OBJECT(p_this->p_libvlc) == p_this )
vlc_mutex_destroy (&(libvlc_priv ((libvlc_int_t *)p_this)->structure_lock));
free( p_priv ); free( p_priv );
} }
static vlc_object_t *FindName (vlc_object_internals_t *priv, const char *name) static vlc_object_t *FindName (vlc_object_t *obj, const char *name)
{ {
vlc_object_internals_t *priv = vlc_internals(obj);
if (priv->psz_name != NULL && !strcmp (priv->psz_name, name)) if (priv->psz_name != NULL && !strcmp (priv->psz_name, name))
return vlc_object_hold (vlc_externals (priv)); return vlc_object_hold (obj);
for (priv = priv->first; priv != NULL; priv = priv->next) vlc_object_t *found = NULL;
{ /* NOTE: nested locking here (due to recursive call) */
vlc_object_t *found = FindName (priv, name); vlc_mutex_lock (&vlc_internals(obj)->tree_lock);
if (found != NULL)
for (priv = priv->first; priv != NULL && found == NULL; priv = priv->next)
found = FindName (vlc_externals(priv), name);
/* NOTE: nested locking here (due to recursive call) */
vlc_mutex_unlock (&vlc_internals(obj)->tree_lock);
return found; return found;
}
return NULL;
} }
#undef vlc_object_find_name #undef vlc_object_find_name
...@@ -411,11 +396,9 @@ vlc_object_t *vlc_object_find_name( vlc_object_t *p_this, const char *psz_name ) ...@@ -411,11 +396,9 @@ vlc_object_t *vlc_object_find_name( vlc_object_t *p_this, const char *psz_name )
msg_Err( p_this, "looking for object \"%s\"... FIXME XXX", psz_name ); msg_Err( p_this, "looking for object \"%s\"... FIXME XXX", psz_name );
#endif #endif
libvlc_lock (p_this->p_libvlc);
vlc_mutex_lock (&name_lock); vlc_mutex_lock (&name_lock);
p_found = FindName (vlc_internals (p_this), psz_name); p_found = FindName (p_this, psz_name);
vlc_mutex_unlock (&name_lock); vlc_mutex_unlock (&name_lock);
libvlc_unlock (p_this->p_libvlc);
return p_found; return p_found;
} }
...@@ -440,55 +423,73 @@ void * vlc_object_hold( vlc_object_t *p_this ) ...@@ -440,55 +423,73 @@ void * vlc_object_hold( vlc_object_t *p_this )
* Drops a reference to an object (decrements the reference count). * Drops a reference to an object (decrements the reference count).
* If the count reaches zero, the object is destroyed. * If the count reaches zero, the object is destroyed.
*/ */
void vlc_object_release( vlc_object_t *p_this ) void vlc_object_release (vlc_object_t *obj)
{ {
vlc_object_internals_t *internals = vlc_internals( p_this ); vlc_object_internals_t *priv = vlc_internals(obj);
vlc_object_t *parent = NULL; unsigned refs = atomic_load (&priv->refs);
unsigned refs = atomic_load (&internals->refs);
/* Fast path */ /* Fast path */
while (refs > 1) while (refs > 1)
{ {
if (atomic_compare_exchange_weak (&internals->refs, &refs, refs - 1)) if (atomic_compare_exchange_weak (&priv->refs, &refs, refs - 1))
return; /* There are still other references to the object */ return; /* There are still other references to the object */
assert (refs > 0); assert (refs > 0);
} }
vlc_object_t *parent = obj->p_parent;
if (unlikely(parent == NULL))
{ /* Destroying the root object */
refs = atomic_fetch_sub (&priv->refs, 1);
assert (refs == 1); /* nobody to race against in this case */
assert (priv->first == NULL); /* no children can be left */
int canc = vlc_savecancel ();
vlc_object_destroy (obj);
vlc_restorecancel (canc);
return;
}
/* Slow path */ /* Slow path */
libvlc_lock (p_this->p_libvlc); vlc_object_internals_t *papriv = vlc_internals (parent);
refs = atomic_fetch_sub (&internals->refs, 1);
vlc_mutex_lock (&papriv->tree_lock);
refs = atomic_fetch_sub (&priv->refs, 1);
assert (refs > 0); assert (refs > 0);
if (likely(refs == 1)) if (likely(refs == 1))
{ { /* Detach from parent to protect against vlc_object_find_name() */
/* Detach from parent to protect against vlc_object_find_name() */ vlc_object_internals_t *prev = priv->prev;
parent = p_this->p_parent; vlc_object_internals_t *next = priv->next;
if (likely(parent))
{
/* Unlink */
vlc_object_internals_t *prev = internals->prev;
vlc_object_internals_t *next = internals->next;
if (prev != NULL) if (prev != NULL)
{
assert (prev->next == priv);
prev->next = next; prev->next = next;
}
else else
vlc_internals (parent)->first = next; {
assert (papriv->first == priv);
papriv->first = next;
}
if (next != NULL) if (next != NULL)
{
assert (next->prev == priv);
next->prev = prev; next->prev = prev;
} }
/* We have no children */
assert (internals->first == NULL);
} }
libvlc_unlock (p_this->p_libvlc); vlc_mutex_unlock (&papriv->tree_lock);
if (likely(refs == 1)) if (likely(refs == 1))
{ {
assert (priv->first == NULL); /* no children can be left */
int canc = vlc_savecancel (); int canc = vlc_savecancel ();
vlc_object_destroy( p_this ); vlc_object_destroy (obj);
vlc_restorecancel (canc); vlc_restorecancel (canc);
if (parent)
vlc_object_release (parent); vlc_object_release (parent);
} }
} }
...@@ -511,7 +512,7 @@ vlc_list_t *vlc_list_children( vlc_object_t *obj ) ...@@ -511,7 +512,7 @@ vlc_list_t *vlc_list_children( vlc_object_t *obj )
vlc_object_internals_t *priv; vlc_object_internals_t *priv;
unsigned count = 0; unsigned count = 0;
libvlc_lock (obj->p_libvlc); vlc_mutex_lock (&vlc_internals(obj)->tree_lock);
for (priv = vlc_internals (obj)->first; priv; priv = priv->next) for (priv = vlc_internals (obj)->first; priv; priv = priv->next)
count++; count++;
...@@ -520,7 +521,7 @@ vlc_list_t *vlc_list_children( vlc_object_t *obj ) ...@@ -520,7 +521,7 @@ vlc_list_t *vlc_list_children( vlc_object_t *obj )
l->p_values = malloc (count * sizeof (vlc_value_t)); l->p_values = malloc (count * sizeof (vlc_value_t));
if (unlikely(l->p_values == NULL)) if (unlikely(l->p_values == NULL))
{ {
libvlc_unlock (obj->p_libvlc); vlc_mutex_unlock (&vlc_internals(obj)->tree_lock);
free (l); free (l);
return NULL; return NULL;
} }
...@@ -530,7 +531,7 @@ vlc_list_t *vlc_list_children( vlc_object_t *obj ) ...@@ -530,7 +531,7 @@ vlc_list_t *vlc_list_children( vlc_object_t *obj )
for (priv = vlc_internals (obj)->first; priv; priv = priv->next) for (priv = vlc_internals (obj)->first; priv; priv = priv->next)
l->p_values[i++].p_address = vlc_object_hold (vlc_externals (priv)); l->p_values[i++].p_address = vlc_object_hold (vlc_externals (priv));
libvlc_unlock (obj->p_libvlc); vlc_mutex_unlock (&vlc_internals(obj)->tree_lock);
return l; return l;
} }
......
...@@ -47,6 +47,7 @@ struct vlc_object_internals ...@@ -47,6 +47,7 @@ struct vlc_object_internals
vlc_object_internals_t *next; /* next sibling */ vlc_object_internals_t *next; /* next sibling */
vlc_object_internals_t *prev; /* previous sibling */ vlc_object_internals_t *prev; /* previous sibling */
vlc_object_internals_t *first; /* first child */ vlc_object_internals_t *first; /* first child */
vlc_mutex_t tree_lock;
}; };
# define vlc_internals( obj ) (((vlc_object_internals_t*)(VLC_OBJECT(obj)))-1) # define vlc_internals( obj ) (((vlc_object_internals_t*)(VLC_OBJECT(obj)))-1)
......
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