/***************************************************************************** * Decrement an object refcount * And destroy the object if its refcount reach zero. *****************************************************************************/ void vlc_object_release( vlc_object_t *p_this ) { vlc_object_internals_t *internals = vlc_internals( p_this ); vlc_object_t *parent = NULL; bool b_should_destroy; vlc_spin_lock( &internals->ref_spin ); assert( internals->i_refcount > 0 ); if( internals->i_refcount > 1 ) { /* Fast path */ /* There are still other references to the object */ internals->i_refcount--; vlc_spin_unlock( &internals->ref_spin ); return; } vlc_spin_unlock( &internals->ref_spin ); /* Slow path */ /* Remember that we cannot hold the spin while waiting on the mutex */ libvlc_lock (p_this->p_libvlc); /* Take the spin again. Note that another thread may have held the * object in the (very short) mean time. */ vlc_spin_lock( &internals->ref_spin ); b_should_destroy = --internals->i_refcount == 0; vlc_spin_unlock( &internals->ref_spin ); if( b_should_destroy ) { /* Detach from parent to protect against FIND_CHILDREN */ parent = p_this->p_parent; if (likely(parent)) { /* Unlink */ if (internals->prev != NULL) internals->prev->next = internals->next; else vlc_internals(parent)->first = internals->next; if (internals->next != NULL) internals->next->prev = internals->prev; } /* We have no children */ assert (internals->first == NULL); } libvlc_unlock (p_this->p_libvlc); if( b_should_destroy ) { int canc; canc = vlc_savecancel (); vlc_object_destroy( p_this ); vlc_restorecancel (canc); if (parent) vlc_object_release (parent); } }
/** **************************************************************************** * detach object from its parent ***************************************************************************** * This function removes all links between an object and its parent. *****************************************************************************/ void vlc_object_detach( vlc_object_t *p_this ) { vlc_object_t *p_parent; if( !p_this ) return; libvlc_lock (p_this->p_libvlc); p_parent = p_this->p_parent; if (p_parent) vlc_object_detach_unlocked( p_this ); libvlc_unlock (p_this->p_libvlc); if (p_parent) vlc_object_release (p_parent); }
/***************************************************************************** * DumpCommand: print the current vlc structure ***************************************************************************** * This function prints either an ASCII tree showing the connections between * vlc objects, and additional information such as their refcount, thread ID, * etc. (command "tree"), or the same data as a simple list (command "list"). *****************************************************************************/ static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { (void)oldval; (void)p_data; vlc_object_t *p_object = NULL; if( *newval.psz_string ) { /* try using the object's name to find it */ p_object = vlc_object_find_name( p_this, newval.psz_string, FIND_ANYWHERE ); if( !p_object ) { return VLC_ENOOBJ; } } libvlc_lock (p_this->p_libvlc); if( *psz_cmd == 't' ) { char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1]; if( !p_object ) p_object = VLC_OBJECT(p_this->p_libvlc); psz_foo[0] = '|'; DumpStructure( vlc_internals(p_object), 0, psz_foo ); } else if( *psz_cmd == 'v' ) { if( !p_object ) p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this; PrintObject( vlc_internals(p_object), "" ); vlc_mutex_lock( &vlc_internals( p_object )->var_lock ); if( vlc_internals( p_object )->var_root == NULL ) puts( " `-o No variables" ); else twalk( vlc_internals( p_object )->var_root, DumpVariable ); vlc_mutex_unlock( &vlc_internals( p_object )->var_lock ); } libvlc_unlock (p_this->p_libvlc); if( *newval.psz_string ) { vlc_object_release( p_object ); } return VLC_SUCCESS; }
/***************************************************************************** * find a typed object and increment its refcount ***************************************************************************** * This function recursively looks for a given object type. i_mode can be one * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE. *****************************************************************************/ void * vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode ) { vlc_object_t *p_found; /* If we are of the requested type ourselves, don't look further */ if( vlc_internals (p_this)->i_object_type == i_type ) { vlc_object_hold( p_this ); return p_this; } /* Otherwise, recursively look for the object */ if (i_mode == FIND_ANYWHERE) return vlc_object_find (VLC_OBJECT(p_this->p_libvlc), i_type, FIND_CHILD); switch (i_type) { case VLC_OBJECT_VOUT: case VLC_OBJECT_AOUT: break; case VLC_OBJECT_INPUT: /* input can only be accessed like this from children, * otherwise we could not promise that it is initialized */ if (i_mode != FIND_PARENT) return NULL; break; default: return NULL; } libvlc_lock (p_this->p_libvlc); switch (i_mode) { case FIND_PARENT: p_found = FindParent (p_this, i_type); break; case FIND_CHILD: p_found = FindChild (vlc_internals (p_this), i_type); break; default: assert (0); } libvlc_unlock (p_this->p_libvlc); return p_found; }
/** **************************************************************************** * attach object to a parent object ***************************************************************************** * This function sets p_this as a child of p_parent, and p_parent as a parent * of p_this. This link can be undone using vlc_object_detach. *****************************************************************************/ void vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent ) { if( !p_this ) return; vlc_object_internals_t *pap = vlc_internals (p_parent); vlc_object_internals_t *priv = vlc_internals (p_this); vlc_object_t *p_old_parent; priv->prev = NULL; vlc_object_hold (p_parent); libvlc_lock (p_this->p_libvlc); #ifndef NDEBUG /* Reparenting an object carries a risk of invalid access to the parent, * from another thread. This can happen when inheriting a variable, or * through any direct access to vlc_object_t.p_parent. Also, reparenting * brings a functional bug, whereby the reparented object uses incorrect * old values for inherited variables (as the new parent may have different * variable values, especially if it is an input). * Note that the old parent may be already destroyed. * So its pointer must not be dereferenced. */ if (priv->old_parent) msg_Info (p_this, "Reparenting an object is dangerous (%p -> %p)!", priv->old_parent, p_parent); #endif p_old_parent = p_this->p_parent; if (p_old_parent) vlc_object_detach_unlocked (p_this); /* Attach the parent to its child */ p_this->p_parent = p_parent; /* Attach the child to its parent */ priv->next = pap->first; if (priv->next != NULL) priv->next->prev = priv; pap->first = priv; libvlc_unlock (p_this->p_libvlc); if (p_old_parent) vlc_object_release (p_old_parent); }
/** * Gets the list of children of an objects, and increment their reference * count. * @return a list (possibly empty) or NULL in case of error. */ vlc_list_t *vlc_list_children( vlc_object_t *obj ) { vlc_list_t *l; vlc_object_internals_t *priv; unsigned count = 0; libvlc_lock (obj->p_libvlc); for (priv = vlc_internals (obj)->first; priv; priv = priv->next) count++; l = NewList (count); if (likely(l != NULL)) { unsigned i = 0; for (priv = vlc_internals (obj)->first; priv; priv = priv->next) l->p_values[i++].p_object = vlc_object_hold (vlc_externals (priv)); } libvlc_unlock (obj->p_libvlc); return l; }
/** **************************************************************************** * attach object to a parent object ***************************************************************************** * This function sets p_this as a child of p_parent, and p_parent as a parent * of p_this. *****************************************************************************/ void vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent ) { if( !p_this ) return; vlc_object_internals_t *pap = vlc_internals (p_parent); vlc_object_internals_t *priv = vlc_internals (p_this); priv->prev = NULL; vlc_object_hold (p_parent); libvlc_lock (p_this->p_libvlc); /* Attach the parent to its child */ assert (p_this->p_parent == NULL); p_this->p_parent = p_parent; /* Attach the child to its parent */ priv->next = pap->first; if (priv->next != NULL) priv->next->prev = priv; pap->first = priv; libvlc_unlock (p_this->p_libvlc); }
/** * Finds a named object and increment its reference count. * Beware that objects found in this manner can be "owned" by another thread, * be of _any_ type, and be attached to any module (if any). With such an * object reference, you can set or get object variables, emit log messages, * and read write-once object parameters (psz_object_type, etc). * You CANNOT cast the object to a more specific object type, and you * definitely cannot invoke object type-specific callbacks with this. * * @param p_this object to search from * @param psz_name name of the object to search for * @param i_mode search direction: FIND_PARENT, FIND_CHILD or FIND_ANYWHERE. * * @return a matching object (must be released by the caller), * or NULL on error. */ vlc_object_t *vlc_object_find_name( vlc_object_t *p_this, const char *psz_name, int i_mode ) { vlc_object_t *p_found; /* Reading psz_object_name from a separate inhibits thread-safety. * Use a libvlc address variable instead for that sort of things! */ msg_Warn( p_this, "%s(%s) is not safe!", __func__, psz_name ); /* If have the requested name ourselves, don't look further */ if( !objnamecmp(p_this, psz_name) ) { vlc_object_hold( p_this ); return p_this; } /* Otherwise, recursively look for the object */ if (i_mode == FIND_ANYWHERE) return vlc_object_find_name (VLC_OBJECT(p_this->p_libvlc), psz_name, FIND_CHILD); libvlc_lock (p_this->p_libvlc); vlc_mutex_lock (&name_lock); switch (i_mode) { case FIND_PARENT: p_found = FindParentName (p_this, psz_name); break; case FIND_CHILD: p_found = FindChildName (vlc_internals (p_this), psz_name); break; default: assert (0); } vlc_mutex_unlock (&name_lock); libvlc_unlock (p_this->p_libvlc); return p_found; }