X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;ds=sidebyside;f=src%2Fmisc%2Fobjects.c;h=99f08fb1e511b9e38eba5032c1f635a45b6c5e95;hb=c79a340d36130d8f4bd4898a0c076de2472b52ef;hp=27e39c326b34ef0f7743f130fd47e75849d71cd0;hpb=609a114637e858ff03123decde419019803f8b08;p=vlc diff --git a/src/misc/objects.c b/src/misc/objects.c index 27e39c326b..99f08fb1e5 100644 --- a/src/misc/objects.c +++ b/src/misc/objects.c @@ -1,7 +1,7 @@ /***************************************************************************** * objects.c: vlc_object_t handling ***************************************************************************** - * Copyright (C) 2004-2007 the VideoLAN team + * Copyright (C) 2004-2008 the VideoLAN team * $Id$ * * Authors: Samuel Hocevar @@ -30,12 +30,12 @@ /***************************************************************************** * Preamble *****************************************************************************/ -#include - -#ifdef HAVE_STDLIB_H -# include /* realloc() */ +#ifdef HAVE_CONFIG_H +# include "config.h" #endif +#include + #include "../libvlc.h" #include #include @@ -48,21 +48,19 @@ #include #include "stream_output/stream_output.h" -#include "vlc_playlist.h" #include "vlc_interface.h" #include "vlc_codec.h" #include "vlc_filter.h" -#include "vlc_httpd.h" -#include "vlc_vlm.h" -#include "input/vlm_internal.h" -#include "vlc_vod.h" -#include "vlc_tls.h" -#include "vlc_xml.h" -#include "vlc_osd.h" -#include "vlc_meta.h" - #include "variables.h" +#ifndef WIN32 +# include +#else +# include +# include +# include /* ENOSYS */ +#endif +#include /***************************************************************************** * Local prototypes @@ -76,105 +74,92 @@ static void DetachObject ( vlc_object_t * ); static void PrintObject ( vlc_object_t *, const char * ); static void DumpStructure ( vlc_object_t *, int, char * ); static int FindIndex ( vlc_object_t *, vlc_object_t **, int ); -static void SetAttachment ( vlc_object_t *, vlc_bool_t ); +static void SetAttachment ( vlc_object_t *, bool ); static vlc_list_t * NewList ( int ); static void ListReplace ( vlc_list_t *, vlc_object_t *, int ); -/*static void ListAppend ( vlc_list_t *, vlc_object_t * );*/ +static void ListAppend ( vlc_list_t *, vlc_object_t * ); static int CountChildren ( vlc_object_t *, int ); static void ListChildren ( vlc_list_t *, vlc_object_t *, int ); +static void vlc_object_destroy( vlc_object_t *p_this ); +static void vlc_object_detach_unlocked (vlc_object_t *p_this); + /***************************************************************************** * Local structure lock *****************************************************************************/ static vlc_mutex_t structure_lock; -static vlc_object_internals_t global_internals; -vlc_object_t *vlc_custom_create( vlc_object_t *p_this, size_t i_size, - int i_type, const char *psz_type ) +void *vlc_custom_create( vlc_object_t *p_this, size_t i_size, + int i_type, const char *psz_type ) { vlc_object_t *p_new; vlc_object_internals_t *p_priv; - if( i_type == VLC_OBJECT_GLOBAL ) - { - p_new = p_this; - p_priv = &global_internals; - memset( p_priv, 0, sizeof( *p_priv ) ); - } - else - { - p_priv = calloc( 1, sizeof( *p_priv ) + i_size ); - if( p_priv == NULL ) - return NULL; + /* NOTE: + * VLC objects are laid out as follow: + * - first the LibVLC-private per-object data, + * - then VLC_COMMON members from vlc_object_t, + * - finally, the type-specific data (if any). + * + * This function initializes the LibVLC and common data, + * and zeroes the rest. + */ + p_priv = calloc( 1, sizeof( *p_priv ) + i_size ); + if( p_priv == NULL ) + return NULL; - p_new = (vlc_object_t *)(p_priv + 1); - } + assert (i_size >= sizeof (vlc_object_t)); + p_new = (vlc_object_t *)(p_priv + 1); - p_new->p_internals = p_priv; p_new->i_object_type = i_type; p_new->psz_object_type = psz_type; - p_new->psz_object_name = NULL; - p_new->b_die = VLC_FALSE; - p_new->b_error = VLC_FALSE; - p_new->b_dead = VLC_FALSE; - p_new->b_attached = VLC_FALSE; - p_new->b_force = VLC_FALSE; + p_new->b_die = false; + p_new->b_error = false; + p_new->b_dead = false; + p_new->b_force = false; p_new->psz_header = NULL; - if( p_this->i_flags & OBJECT_FLAGS_NODBG ) - p_new->i_flags |= OBJECT_FLAGS_NODBG; - if( p_this->i_flags & OBJECT_FLAGS_QUIET ) - p_new->i_flags |= OBJECT_FLAGS_QUIET; - if( p_this->i_flags & OBJECT_FLAGS_NOINTERACT ) - p_new->i_flags |= OBJECT_FLAGS_NOINTERACT; + if (p_this) + p_new->i_flags = p_this->i_flags + & (OBJECT_FLAGS_NODBG|OBJECT_FLAGS_QUIET|OBJECT_FLAGS_NOINTERACT); p_priv->p_vars = calloc( sizeof( variable_t ), 16 ); if( !p_priv->p_vars ) { - if( i_type != VLC_OBJECT_GLOBAL ) - free( p_priv ); + free( p_priv ); return NULL; } - if( i_type == VLC_OBJECT_GLOBAL ) + libvlc_global_data_t *p_libvlc_global; + if( p_this == NULL ) { - /* If i_type is global, then p_new is actually p_libvlc_global */ - libvlc_global_data_t *p_libvlc_global = (libvlc_global_data_t *)p_new; + /* Only the global root object is created out of the blue */ + p_libvlc_global = (libvlc_global_data_t *)p_new; p_new->p_libvlc = NULL; p_libvlc_global->i_counter = 0; - p_new->i_object_id = 0; - - p_libvlc_global->i_objects = 1; - p_libvlc_global->pp_objects = malloc( sizeof(vlc_object_t *) ); - p_libvlc_global->pp_objects[0] = p_new; - p_new->b_attached = VLC_TRUE; + p_libvlc_global->i_objects = 0; + p_libvlc_global->pp_objects = NULL; + vlc_mutex_init( &structure_lock ); } else { - libvlc_global_data_t *p_libvlc_global = vlc_global(); - p_new->p_libvlc = ( i_type == VLC_OBJECT_LIBVLC ) ? (libvlc_int_t*)p_new - : p_this->p_libvlc; - - vlc_mutex_lock( &structure_lock ); - - p_libvlc_global->i_counter++; - p_new->i_object_id = p_libvlc_global->i_counter; - - /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's - * useless to try and recover anything if pp_objects gets smashed. */ - TAB_APPEND( p_libvlc_global->i_objects, p_libvlc_global->pp_objects, - p_new ); - - vlc_mutex_unlock( &structure_lock ); + p_libvlc_global = vlc_global(); + if( i_type == VLC_OBJECT_LIBVLC ) + p_new->p_libvlc = (libvlc_int_t*)p_new; + else + p_new->p_libvlc = p_this->p_libvlc; } - p_new->i_refcount = 0; + vlc_spin_init( &p_priv->ref_spin ); + p_priv->i_refcount = 1; + p_priv->pf_destructor = NULL; + p_priv->b_thread = false; p_new->p_parent = NULL; p_new->pp_children = NULL; p_new->i_children = 0; @@ -182,14 +167,22 @@ vlc_object_t *vlc_custom_create( vlc_object_t *p_this, size_t i_size, p_new->p_private = NULL; /* Initialize mutexes and condvars */ - vlc_mutex_init( p_new, &p_new->object_lock ); + vlc_mutex_init( &p_new->object_lock ); vlc_cond_init( p_new, &p_new->object_wait ); - vlc_mutex_init( p_new, &p_priv->var_lock ); + vlc_mutex_init( &p_priv->var_lock ); + vlc_spin_init( &p_priv->spin ); + p_priv->pipes[0] = p_priv->pipes[1] = -1; - if( i_type == VLC_OBJECT_GLOBAL ) - { - vlc_mutex_init( p_new, &structure_lock ); + vlc_mutex_lock( &structure_lock ); + p_new->i_object_id = p_libvlc_global->i_counter++; + /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's + * useless to try and recover anything if pp_objects gets smashed. */ + TAB_APPEND( p_libvlc_global->i_objects, p_libvlc_global->pp_objects, + p_new ); + vlc_mutex_unlock( &structure_lock ); + if( i_type == VLC_OBJECT_LIBVLC ) + { var_Create( p_new, "list", VLC_VAR_STRING | VLC_VAR_ISCOMMAND ); var_AddCallback( p_new, "list", DumpCommand, NULL ); var_Create( p_new, "tree", VLC_VAR_STRING | VLC_VAR_ISCOMMAND ); @@ -217,10 +210,6 @@ void * __vlc_object_create( vlc_object_t *p_this, int i_type ) switch( i_type ) { - case VLC_OBJECT_GLOBAL: - i_size = sizeof(libvlc_global_data_t); - psz_type = "global"; - break; case VLC_OBJECT_LIBVLC: i_size = sizeof(libvlc_int_t); psz_type = "libvlc"; @@ -233,18 +222,6 @@ void * __vlc_object_create( vlc_object_t *p_this, int i_type ) i_size = sizeof(intf_thread_t); psz_type = "dialogs"; break; - case VLC_OBJECT_PLAYLIST: - i_size = sizeof(playlist_t); - psz_type = "playlist"; - break; - case VLC_OBJECT_SD: - i_size = sizeof(services_discovery_t); - psz_type = "services discovery"; - break; - case VLC_OBJECT_INPUT: - i_size = sizeof(input_thread_t); - psz_type = "input"; - break; case VLC_OBJECT_DEMUX: i_size = sizeof(demux_t); psz_type = "demux"; @@ -273,10 +250,6 @@ void * __vlc_object_create( vlc_object_t *p_this, int i_type ) i_size = sizeof(vout_thread_t); psz_type = "video output"; break; - case VLC_OBJECT_SPU: - i_size = sizeof(spu_t); - psz_type = "subpicture"; - break; case VLC_OBJECT_AOUT: i_size = sizeof(aout_instance_t); psz_type = "audio output"; @@ -285,22 +258,6 @@ void * __vlc_object_create( vlc_object_t *p_this, int i_type ) i_size = sizeof(sout_instance_t); psz_type = "stream output"; break; - case VLC_OBJECT_VLM: - i_size = sizeof( vlm_t ); - psz_type = "vlm dameon"; - break; - case VLC_OBJECT_VOD: - i_size = sizeof( vod_t ); - psz_type = "vod server"; - break; - case VLC_OBJECT_TLS: - i_size = sizeof( tls_t ); - psz_type = "tls"; - break; - case VLC_OBJECT_XML: - i_size = sizeof( xml_t ); - psz_type = "xml"; - break; case VLC_OBJECT_OPENGL: i_size = sizeof( vout_thread_t ); psz_type = "opengl"; @@ -309,13 +266,9 @@ void * __vlc_object_create( vlc_object_t *p_this, int i_type ) i_size = sizeof( announce_handler_t ); psz_type = "announce"; break; - case VLC_OBJECT_META_ENGINE: - i_size = sizeof( meta_engine_t ); - psz_type = "meta engine"; - break; - case VLC_OBJECT_OSDMENU: - i_size = sizeof( osd_menu_t ); - psz_type = "osd menu"; + case VLC_OBJECT_INTERACTION: + i_size = sizeof( interaction_t ); + psz_type = "interaction"; break; default: i_size = i_type > (int)sizeof(vlc_object_t) @@ -331,59 +284,70 @@ void * __vlc_object_create( vlc_object_t *p_this, int i_type ) /** **************************************************************************** - * Destroy a vlc object + * Set the destructor of a vlc object + * + * This function sets the destructor of the vlc object. It will be called + * when the object is destroyed when the its refcount reaches 0. + * (It is called by the internal function vlc_object_destroy()) + *****************************************************************************/ +void __vlc_object_set_destructor( vlc_object_t *p_this, + vlc_destructor_t pf_destructor ) +{ + vlc_object_internals_t *p_priv = vlc_internals(p_this ); + p_priv->pf_destructor = pf_destructor; +} + +/** + **************************************************************************** + * Destroy a vlc object (Internal) * * This function destroys an object that has been previously allocated with * vlc_object_create. The object's refcount must be zero and it must not be * attached to other objects in any way. *****************************************************************************/ -void __vlc_object_destroy( vlc_object_t *p_this ) +static void vlc_object_destroy( vlc_object_t *p_this ) { vlc_object_internals_t *p_priv = vlc_internals( p_this ); - int i_delay = 0; - if( p_this->i_children ) - { - msg_Err( p_this, "cannot delete object (%i, %s) with children" , - p_this->i_object_id, p_this->psz_object_name ); - return; - } + /* Objects are always detached beforehand */ + assert( !p_this->p_parent ); - if( p_this->p_parent ) - { - msg_Err( p_this, "cannot delete object (%i, %s) with a parent", - p_this->i_object_id, p_this->psz_object_name ); - return; - } + /* Send a kill to the object's thread if applicable */ + vlc_object_kill( p_this ); + + /* If we are running on a thread, wait until it ends */ + if( p_priv->b_thread ) + vlc_thread_join( p_this ); + + /* Call the custom "subclass" destructor */ + if( p_priv->pf_destructor ) + p_priv->pf_destructor( p_this ); - while( p_this->i_refcount ) + /* Sanity checks */ + if( p_this->i_children ) { - i_delay++; + int i; - /* Don't warn immediately ... 100ms seems OK */ - if( i_delay == 2 ) - { - msg_Warn( p_this, - "refcount is %i, delaying before deletion (id=%d,type=%d)", - p_this->i_refcount, p_this->i_object_id, - p_this->i_object_type ); - } - else if( i_delay == 10 ) - { - msg_Err( p_this, - "refcount is %i, delaying again (id=%d,type=%d)", - p_this->i_refcount, p_this->i_object_id, - p_this->i_object_type ); - } - else if( i_delay == 20 ) + fprintf( stderr, + "ERROR: cannot delete object (%i, %s) with %d children\n", + p_this->i_object_id, p_this->psz_object_name, + p_this->i_children ); + + for( i = 0; i < p_this->i_children; i++ ) { - msg_Err( p_this, - "waited too long, cancelling destruction (id=%d,type=%d)", - p_this->i_object_id, p_this->i_object_type ); - return; + fprintf( stderr, + "ERROR: Remaining children object " + "(id:%i, type:%s, name:%s)\n", + p_this->pp_children[i]->i_object_id, + p_this->pp_children[i]->psz_object_type, + p_this->pp_children[i]->psz_object_name ); } + fflush(stderr); - msleep( 100000 ); + /* Dump libvlc object to ease debugging */ + vlc_object_dump( p_this->p_libvlc ); + + abort(); } /* Destroy the associated variables, starting from the end so that @@ -396,66 +360,305 @@ void __vlc_object_destroy( vlc_object_t *p_this ) free( p_priv->p_vars ); vlc_mutex_destroy( &p_priv->var_lock ); - if( p_this->psz_header ) free( p_this->psz_header ); + free( p_this->psz_header ); - if( p_this->i_object_type == VLC_OBJECT_GLOBAL ) + if( p_this->p_libvlc == NULL ) { libvlc_global_data_t *p_global = (libvlc_global_data_t *)p_this; + +#ifndef NDEBUG + assert( p_global == vlc_global() ); + /* Test for leaks */ + if( p_global->i_objects > 0 ) + { + int i; + for( i = 0; i < p_global->i_objects; i++ ) + { + /* We are leaking this object */ + fprintf( stderr, + "ERROR: leaking object (id:%i, type:%s, name:%s)\n", + p_global->pp_objects[i]->i_object_id, + p_global->pp_objects[i]->psz_object_type, + p_global->pp_objects[i]->psz_object_name ); + + /* Dump libvlc object to ease debugging */ + vlc_object_dump( p_global->pp_objects[i] ); + + fflush(stderr); + } + + /* Dump libvlc object to ease debugging */ + vlc_object_dump( p_this ); + + /* Strongly abort, cause we want these to be fixed */ + abort(); + } +#endif + /* We are the global object ... no need to lock. */ free( p_global->pp_objects ); p_global->pp_objects = NULL; - p_global->i_objects--; vlc_mutex_destroy( &structure_lock ); } - else + + FREENULL( p_this->psz_object_name ); + +#if defined(WIN32) || defined(UNDER_CE) + /* if object has an associated thread, close it now */ + if( p_priv->thread_id ) + CloseHandle(p_priv->thread_id); +#endif + + vlc_spin_destroy( &p_priv->ref_spin ); + vlc_mutex_destroy( &p_this->object_lock ); + vlc_cond_destroy( &p_this->object_wait ); + vlc_spin_destroy( &p_priv->spin ); + if( p_priv->pipes[1] != -1 ) + close( p_priv->pipes[1] ); + if( p_priv->pipes[0] != -1 ) + close( p_priv->pipes[0] ); + + free( p_priv ); +} + + +/** Inter-object signaling */ + +void __vlc_object_lock( vlc_object_t *obj ) +{ + vlc_mutex_lock( &obj->object_lock ); +} + +void __vlc_object_unlock( vlc_object_t *obj ) +{ + vlc_assert_locked( &obj->object_lock ); + vlc_mutex_unlock( &obj->object_lock ); +} + +#ifdef WIN32 +# include +# include + +/** + * select()-able pipes emulated using Winsock + */ +static int pipe (int fd[2]) +{ + SOCKADDR_IN addr; + int addrlen = sizeof (addr); + + SOCKET l = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP), a, + c = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if ((l == INVALID_SOCKET) || (c == INVALID_SOCKET)) + goto error; + + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + if (bind (l, (PSOCKADDR)&addr, sizeof (addr)) + || getsockname (l, (PSOCKADDR)&addr, &addrlen) + || listen (l, 1) + || connect (c, (PSOCKADDR)&addr, addrlen)) + goto error; + + a = accept (l, NULL, NULL); + if (a == INVALID_SOCKET) + goto error; + + closesocket (l); + //shutdown (a, 0); + //shutdown (c, 1); + fd[0] = c; + fd[1] = a; + return 0; + +error: + if (l != INVALID_SOCKET) + closesocket (l); + if (c != INVALID_SOCKET) + closesocket (c); + return -1; +} + +#undef read +#define read( a, b, c ) recv (a, b, c, 0) +#undef write +#define write( a, b, c ) send (a, b, c, 0) +#undef close +#define close( a ) closesocket (a) +#endif /* WIN32 */ + +/** + * Returns the readable end of a pipe that becomes readable once termination + * of the object is requested (vlc_object_kill()). + * This can be used to wake-up out of a select() or poll() event loop, such + * typically when doing network I/O. + * + * Note that the pipe will remain the same for the lifetime of the object. + * DO NOT read the pipe nor close it yourself. Ever. + * + * @param obj object that would be "killed" + * @return a readable pipe descriptor, or -1 on error. + */ +int __vlc_object_waitpipe( vlc_object_t *obj ) +{ + int pfd[2] = { -1, -1 }; + vlc_object_internals_t *internals = vlc_internals( obj ); + bool killed = false; + + vlc_spin_lock (&internals->spin); + if (internals->pipes[0] == -1) { - libvlc_global_data_t *p_libvlc_global = vlc_global(); - int i_index; + /* This can only ever happen if someone killed us without locking: */ + assert (internals->pipes[1] == -1); + vlc_spin_unlock (&internals->spin); - vlc_mutex_lock( &structure_lock ); + if (pipe (pfd)) + return -1; - /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's - * useless to try and recover anything if pp_objects gets smashed. */ - i_index = FindIndex( p_this, p_libvlc_global->pp_objects, - p_libvlc_global->i_objects ); - REMOVE_ELEM( p_libvlc_global->pp_objects, - p_libvlc_global->i_objects, i_index ); + vlc_spin_lock (&internals->spin); + if (internals->pipes[0] == -1) + { + internals->pipes[0] = pfd[0]; + internals->pipes[1] = pfd[1]; + pfd[0] = pfd[1] = -1; + } + killed = obj->b_die; + } + vlc_spin_unlock (&internals->spin); - vlc_mutex_unlock( &structure_lock ); + if (killed) + { + /* Race condition: vlc_object_kill() already invoked! */ + int fd; + + vlc_spin_lock (&internals->spin); + fd = internals->pipes[1]; + internals->pipes[1] = -1; + vlc_spin_unlock (&internals->spin); + + msg_Dbg (obj, "waitpipe: object already dying"); + if (fd != -1) + close (fd); } - vlc_mutex_destroy( &p_this->object_lock ); - vlc_cond_destroy( &p_this->object_wait ); + /* Race condition: two threads call pipe() - unlikely */ + if (pfd[0] != -1) + close (pfd[0]); + if (pfd[1] != -1) + close (pfd[1]); - /* global is not dynamically allocated by vlc_object_create */ - if( p_this->i_object_type != VLC_OBJECT_GLOBAL ) - free( p_priv ); + return internals->pipes[0]; } -void __vlc_object_kill( vlc_object_t *p_this ) +/** + * Waits for the object to be signaled (using vlc_object_signal()). + * If the object already has a signal pending, this function will return + * immediately. It is asserted that the caller holds the object lock. + * + * @return true if the object is dying and should terminate. + */ +bool __vlc_object_wait( vlc_object_t *obj ) { - vlc_mutex_lock( &p_this->object_lock ); - p_this->b_die = VLC_TRUE; - vlc_mutex_unlock( &p_this->object_lock ); + vlc_assert_locked( &obj->object_lock ); + vlc_cond_wait( &obj->object_wait, &obj->object_lock ); + return obj->b_die; +} + + +/** + * Waits for the object to be signaled (using vlc_object_signal()), or for + * a timer to expire. + * If the object already has a signal pending, this function will return + * immediately. It is asserted that the caller holds the object lock. + * + * @return negative if the object is dying and should terminate, + * positive if the the object has been signaled but is not dying, + * 0 if timeout has been reached. + */ +int __vlc_object_timedwait( vlc_object_t *obj, mtime_t deadline ) +{ + int v; + + vlc_assert_locked( &obj->object_lock ); + v = vlc_cond_timedwait( &obj->object_wait, &obj->object_lock, deadline ); + if( v == 0 ) /* signaled */ + return obj->b_die ? -1 : 1; + return 0; } -vlc_bool_t __vlc_object_dying_unlocked( vlc_object_t *p_this ) +/** + * Checks whether an object has been "killed". + * The object lock must be held. + * + * Typical code for an object thread could be: + * + vlc_object_lock (self); + ...initialization... + while (vlc_object_alive (self)) + { + ...preprocessing... + + if (vlc_object_wait (self)) + continue; + + ...postprocessing... + } + ...deinitialization... + vlc_object_unlock (self); + * + * + * @return true iff the object has not been killed yet + */ +bool __vlc_object_alive( vlc_object_t *obj ) { - vlc_assert_locked( &p_this->object_lock ); - return p_this->b_die; + vlc_assert_locked( &obj->object_lock ); + return !obj->b_die; } -vlc_bool_t __vlc_object_dying( vlc_object_t *p_this ) +/** + * Signals an object for which the lock is held. + */ +void __vlc_object_signal_unlocked( vlc_object_t *obj ) +{ + vlc_assert_locked (&obj->object_lock); + vlc_cond_signal( &obj->object_wait ); +} + + +/** + * Requests termination of an object. + * If the object is LibVLC, also request to terminate all its children. + */ +void __vlc_object_kill( vlc_object_t *p_this ) { - vlc_bool_t b; - vlc_mutex_lock( &p_this->object_lock ); - b = __vlc_object_dying_unlocked( p_this ); - vlc_mutex_unlock( &p_this->object_lock ); - return b; + vlc_object_internals_t *internals = vlc_internals( p_this ); + int fd; + + vlc_mutex_lock( &p_this->object_lock ); + p_this->b_die = true; + + vlc_spin_lock (&internals->spin); + fd = internals->pipes[1]; + internals->pipes[1] = -1; + vlc_spin_unlock (&internals->spin); + + if( fd != -1 ) + { + msg_Dbg (p_this, "waitpipe: object killed"); + close (fd); + } + + if( p_this->i_object_type == VLC_OBJECT_LIBVLC ) + for( int i = 0; i < p_this->i_children ; i++ ) + vlc_object_kill( p_this->pp_children[i] ); + + vlc_object_signal_unlocked( p_this ); + vlc_mutex_unlock( &p_this->object_lock ); } @@ -465,11 +668,12 @@ vlc_bool_t __vlc_object_dying( vlc_object_t *p_this ) * This function looks for the object whose i_object_id field is i_id. We * use a dichotomy so that lookups are in log2(n). *****************************************************************************/ -void * __vlc_object_get( vlc_object_t *p_this, int i_id ) +void * vlc_object_get( int i_id ) { int i_max, i_middle; vlc_object_t **pp_objects; libvlc_global_data_t *p_libvlc_global = vlc_global(); + vlc_object_t *obj = NULL; vlc_mutex_lock( &structure_lock ); @@ -496,30 +700,22 @@ void * __vlc_object_get( vlc_object_t *p_this, int i_id ) /* This happens when there are only two remaining objects */ if( pp_objects[i_middle+1]->i_object_id == i_id ) { - vlc_mutex_unlock( &structure_lock ); - pp_objects[i_middle+1]->i_refcount++; - return pp_objects[i_middle+1]; + vlc_object_yield( pp_objects[i_middle+1] ); + obj = pp_objects[i_middle+1]; } break; } } else { - vlc_mutex_unlock( &structure_lock ); - pp_objects[i_middle]->i_refcount++; - return pp_objects[i_middle]; - } - - if( i_max == 0 ) - { - /* this means that i_max == i_middle, and since we have already - * tested pp_objects[i_middle]), p_found is properly set. */ + vlc_object_yield( pp_objects[i_middle] ); + obj = pp_objects[i_middle]; break; } } vlc_mutex_unlock( &structure_lock ); - return NULL; + return obj; } /** @@ -533,16 +729,15 @@ void * __vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode ) { vlc_object_t *p_found; - vlc_mutex_lock( &structure_lock ); - /* If we are of the requested type ourselves, don't look further */ if( !(i_mode & FIND_STRICT) && p_this->i_object_type == i_type ) { - p_this->i_refcount++; - vlc_mutex_unlock( &structure_lock ); + vlc_object_yield( p_this ); return p_this; } + vlc_mutex_lock( &structure_lock ); + /* Otherwise, recursively look for the object */ if( (i_mode & 0x000f) == FIND_ANYWHERE ) { @@ -584,18 +779,17 @@ void * __vlc_object_find_name( vlc_object_t *p_this, const char *psz_name, { vlc_object_t *p_found; - vlc_mutex_lock( &structure_lock ); - /* If have the requested name ourselves, don't look further */ if( !(i_mode & FIND_STRICT) && p_this->psz_object_name && !strcmp( p_this->psz_object_name, psz_name ) ) { - p_this->i_refcount++; - vlc_mutex_unlock( &structure_lock ); + vlc_object_yield( p_this ); return p_this; } + vlc_mutex_lock( &structure_lock ); + /* Otherwise, recursively look for the object */ if( (i_mode & 0x000f) == FIND_ANYWHERE ) { @@ -627,25 +821,70 @@ void * __vlc_object_find_name( vlc_object_t *p_this, const char *psz_name, } /** - **************************************************************************** - * increment an object refcount - *****************************************************************************/ + * Increment an object reference counter. + */ void __vlc_object_yield( vlc_object_t *p_this ) { - vlc_mutex_lock( &structure_lock ); - p_this->i_refcount++; - vlc_mutex_unlock( &structure_lock ); + vlc_object_internals_t *internals = vlc_internals( p_this ); + + vlc_spin_lock( &internals->ref_spin ); + /* Avoid obvious freed object uses */ + assert( internals->i_refcount > 0 ); + /* Increment the counter */ + internals->i_refcount++; + vlc_spin_unlock( &internals->ref_spin ); } -/** - **************************************************************************** +/***************************************************************************** * 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 ); + 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 */ vlc_mutex_lock( &structure_lock ); - p_this->i_refcount--; + /* Take the spin again. Note that another thread may have yielded 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 ) + { + /* Remove the object from the table so that it cannot be returned from + * vlc_object_find() and friends. */ + libvlc_global_data_t *p_libvlc_global = vlc_global(); + int i_index; + + i_index = FindIndex( p_this, p_libvlc_global->pp_objects, + p_libvlc_global->i_objects ); + REMOVE_ELEM( p_libvlc_global->pp_objects, + p_libvlc_global->i_objects, i_index ); + if (p_this->p_parent) + vlc_object_detach_unlocked (p_this); + } + vlc_mutex_unlock( &structure_lock ); + + if( b_should_destroy ) + vlc_object_destroy( p_this ); } /** @@ -668,15 +907,18 @@ void __vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent ) INSERT_ELEM( p_parent->pp_children, p_parent->i_children, p_parent->i_children, p_this ); - /* Climb up the tree to see whether we are connected with the root */ - if( p_parent->b_attached ) - { - SetAttachment( p_this, VLC_TRUE ); - } - vlc_mutex_unlock( &structure_lock ); } + +static void vlc_object_detach_unlocked (vlc_object_t *p_this) +{ + assert (p_this->p_parent); + + DetachObject( p_this ); +} + + /** **************************************************************************** * detach object from its parent @@ -688,6 +930,7 @@ void __vlc_object_detach( vlc_object_t *p_this ) if( !p_this ) return; vlc_mutex_lock( &structure_lock ); + if( !p_this->p_parent ) { msg_Err( p_this, "object is not attached" ); @@ -695,15 +938,21 @@ void __vlc_object_detach( vlc_object_t *p_this ) return; } - /* Climb up the tree to see whether we are connected with the root */ - if( p_this->p_parent->b_attached ) - { - SetAttachment( p_this, VLC_FALSE ); - } - - DetachObject( p_this ); + vlc_object_detach_unlocked( p_this ); vlc_mutex_unlock( &structure_lock ); - p_this = NULL; +} + + +static void vlc_tree_find (vlc_object_t *node, int type, vlc_list_t *list) +{ + assert (node); + vlc_assert_locked (&structure_lock); + + if (node->i_object_type == type) + ListAppend (list, node); + + for (int i = 0; i < node->i_children; i++) + vlc_tree_find (node->pp_children[i], type, list); } /** @@ -716,7 +965,6 @@ void __vlc_object_detach( vlc_object_t *p_this ) vlc_list_t * __vlc_list_find( vlc_object_t *p_this, int i_type, int i_mode ) { vlc_list_t *p_list; - vlc_object_t **pp_current, **pp_end; int i_count = 0, i_index = 0; libvlc_global_data_t *p_libvlc_global = vlc_global(); @@ -726,31 +974,10 @@ vlc_list_t * __vlc_list_find( vlc_object_t *p_this, int i_type, int i_mode ) switch( i_mode & 0x000f ) { case FIND_ANYWHERE: - pp_current = p_libvlc_global->pp_objects; - pp_end = pp_current + p_libvlc_global->i_objects; - - for( ; pp_current < pp_end ; pp_current++ ) - { - if( (*pp_current)->b_attached - && (*pp_current)->i_object_type == i_type ) - { - i_count++; - } - } - - p_list = NewList( i_count ); - pp_current = p_libvlc_global->pp_objects; - - for( ; pp_current < pp_end ; pp_current++ ) - { - if( (*pp_current)->b_attached - && (*pp_current)->i_object_type == i_type ) - { - ListReplace( p_list, *pp_current, i_index ); - if( i_index < i_count ) i_index++; - } - } - break; + p_list = NewList (0); + if (p_list != NULL) + vlc_tree_find (VLC_OBJECT (p_libvlc_global), i_type, p_list); + break; case FIND_CHILD: i_count = CountChildren( p_this, i_type ); @@ -802,18 +1029,7 @@ static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd, pp_end = pp_current + p_libvlc_global->i_objects; for( ; pp_current < pp_end ; pp_current++ ) - { - if( (*pp_current)->b_attached ) - { - PrintObject( *pp_current, "" ); - } - else - { - printf( " o %.8i %s (not attached)\n", - (*pp_current)->i_object_id, - (*pp_current)->psz_object_type ); - } - } + PrintObject( *pp_current, "" ); vlc_mutex_unlock( &structure_lock ); } @@ -826,11 +1042,11 @@ static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd, char *end; int i_id = strtol( newval.psz_string, &end, 0 ); if( end != newval.psz_string ) - p_object = vlc_object_get( p_this, i_id ); + p_object = vlc_object_get( i_id ); else { /* try using the object's name to find it */ - vlc_object_t *p_libvlc = vlc_object_get( p_this, 1 ); + vlc_object_t *p_libvlc = vlc_object_get( 1 ); if( p_libvlc ) { /* Look in p_libvlc's children tree */ @@ -875,11 +1091,11 @@ static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd, PrintObject( p_object, "" ); - if( !p_object->p_internals->i_vars ) + if( !vlc_internals( p_object )->i_vars ) printf( " `-o No variables\n" ); - for( i = 0; i < p_object->p_internals->i_vars; i++ ) + for( i = 0; i < vlc_internals( p_object )->i_vars; i++ ) { - variable_t *p_var = p_object->p_internals->p_vars + i; + variable_t *p_var = vlc_internals( p_object )->p_vars + i; const char *psz_type = "unknown"; switch( p_var->i_type & VLC_VAR_TYPE ) @@ -905,7 +1121,7 @@ static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd, #undef MYCASE } printf( " %c-o \"%s\" (%s", - i + 1 == p_object->p_internals->i_vars ? '`' : '|', + i + 1 == vlc_internals( p_object )->i_vars ? '`' : '|', p_var->psz_name, psz_type ); if( p_var->psz_text ) printf( ", %s", p_var->psz_text ); @@ -932,7 +1148,7 @@ static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd, printf( ": %f", p_var->val.f_float ); break; case VLC_VAR_TIME: - printf( ": " I64Fi, (int64_t)p_var->val.i_time ); + printf( ": %"PRIi64, (int64_t)p_var->val.i_time ); break; case VLC_VAR_ADDRESS: printf( ": %p", p_var->val.p_address ); @@ -966,17 +1182,27 @@ void vlc_list_release( vlc_list_t *p_list ) { int i_index; - vlc_mutex_lock( &structure_lock ); for( i_index = 0; i_index < p_list->i_count; i_index++ ) { - p_list->p_values[i_index].p_object->i_refcount--; + vlc_object_release( p_list->p_values[i_index].p_object ); } - vlc_mutex_unlock( &structure_lock ); free( p_list->p_values ); free( p_list ); } +/***************************************************************************** + * dump an object. (Debug function) + *****************************************************************************/ +void __vlc_object_dump( vlc_object_t *p_this ) +{ + vlc_mutex_lock( &structure_lock ); + char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1]; + psz_foo[0] = '|'; + DumpStructure( p_this, 0, psz_foo ); + vlc_mutex_unlock( &structure_lock ); +} + /* Following functions are local */ /***************************************************************************** @@ -1032,7 +1258,7 @@ static vlc_object_t * FindObject( vlc_object_t *p_this, int i_type, int i_mode ) { if( p_tmp->i_object_type == i_type ) { - p_tmp->i_refcount++; + vlc_object_yield( p_tmp ); return p_tmp; } else @@ -1048,7 +1274,7 @@ static vlc_object_t * FindObject( vlc_object_t *p_this, int i_type, int i_mode ) p_tmp = p_this->pp_children[i]; if( p_tmp->i_object_type == i_type ) { - p_tmp->i_refcount++; + vlc_object_yield( p_tmp ); return p_tmp; } else if( p_tmp->i_children ) @@ -1086,7 +1312,7 @@ static vlc_object_t * FindObjectName( vlc_object_t *p_this, if( p_tmp->psz_object_name && !strcmp( p_tmp->psz_object_name, psz_name ) ) { - p_tmp->i_refcount++; + vlc_object_yield( p_tmp ); return p_tmp; } else @@ -1103,7 +1329,7 @@ static vlc_object_t * FindObjectName( vlc_object_t *p_this, if( p_tmp->psz_object_name && !strcmp( p_tmp->psz_object_name, psz_name ) ) { - p_tmp->i_refcount++; + vlc_object_yield( p_tmp ); return p_tmp; } else if( p_tmp->i_children ) @@ -1158,30 +1384,13 @@ static void DetachObject( vlc_object_t *p_this ) } } -/***************************************************************************** - * SetAttachment: recursively set the b_attached flag of a subtree. - ***************************************************************************** - * This function is used by the attach and detach functions to propagate - * the b_attached flag in a subtree. - *****************************************************************************/ -static void SetAttachment( vlc_object_t *p_this, vlc_bool_t b_attached ) -{ - int i_index; - - for( i_index = p_this->i_children ; i_index-- ; ) - { - SetAttachment( p_this->pp_children[i_index], b_attached ); - } - - p_this->b_attached = b_attached; -} static void PrintObject( vlc_object_t *p_this, const char *psz_prefix ) { char psz_children[20], psz_refcount[20], psz_thread[30], psz_name[50], psz_parent[20]; - psz_name[0] = '\0'; + memset( &psz_name, 0, sizeof(psz_name) ); if( p_this->psz_object_name ) { snprintf( psz_name, 49, " \"%s\"", p_this->psz_object_name ); @@ -1203,12 +1412,14 @@ static void PrintObject( vlc_object_t *p_this, const char *psz_prefix ) } psz_refcount[0] = '\0'; - if( p_this->i_refcount ) - snprintf( psz_refcount, 19, ", refcount %i", p_this->i_refcount ); + if( vlc_internals( p_this )->i_refcount > 0 ) + snprintf( psz_refcount, 19, ", refcount %u", + vlc_internals( p_this )->i_refcount ); psz_thread[0] = '\0'; - if( p_this->b_thread ) - snprintf( psz_thread, 29, " (thread %d)", (int)p_this->thread_id ); + if( vlc_internals( p_this )->b_thread ) + snprintf( psz_thread, 29, " (thread %lu)", + (unsigned long)vlc_internals( p_this )->thread_id ); psz_parent[0] = '\0'; if( p_this->p_parent ) @@ -1298,14 +1509,14 @@ static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object, return; } - p_object->i_refcount++; + vlc_object_yield( p_object ); p_list->p_values[i_index].p_object = p_object; return; } -/*static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object ) +static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object ) { if( p_list == NULL ) { @@ -1320,13 +1531,13 @@ static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object, return; } - p_object->i_refcount++; + vlc_object_yield( p_object ); p_list->p_values[p_list->i_count].p_object = p_object; p_list->i_count++; return; -}*/ +} static int CountChildren( vlc_object_t *p_this, int i_type ) {