]> git.sesse.net Git - vlc/blob - src/misc/objects.c
objects: Also dump leaked objects.
[vlc] / src / misc / objects.c
1 /*****************************************************************************
2  * objects.c: vlc_object_t handling
3  *****************************************************************************
4  * Copyright (C) 2004-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /**
25  * \file
26  * This file contains the functions to handle the vlc_object_t type
27  */
28
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <vlc/vlc.h>
38
39 #include "../libvlc.h"
40 #include <vlc_vout.h>
41 #include <vlc_aout.h>
42 #include "audio_output/aout_internal.h"
43
44 #include <vlc_access.h>
45 #include <vlc_demux.h>
46 #include <vlc_stream.h>
47
48 #include <vlc_sout.h>
49 #include "stream_output/stream_output.h"
50
51 #include "vlc_interface.h"
52 #include "vlc_codec.h"
53 #include "vlc_filter.h"
54
55 #include "variables.h"
56 #ifndef WIN32
57 # include <unistd.h>
58 #else
59 # include <io.h>
60 # include <fcntl.h>
61 # include <errno.h> /* ENOSYS */
62 #endif
63 #include <assert.h>
64
65 /*****************************************************************************
66  * Constants
67  *****************************************************************************/
68
69 const vlc_destructor_t kVLCDestructor = NULL;
70
71 /*****************************************************************************
72  * Local prototypes
73  *****************************************************************************/
74 static int  DumpCommand( vlc_object_t *, char const *,
75                          vlc_value_t, vlc_value_t, void * );
76
77 static vlc_object_t * FindObject    ( vlc_object_t *, int, int );
78 static vlc_object_t * FindObjectName( vlc_object_t *, const char *, int );
79 static void           DetachObject  ( vlc_object_t * );
80 static void           PrintObject   ( vlc_object_t *, const char * );
81 static void           DumpStructure ( vlc_object_t *, int, char * );
82 static int            FindIndex     ( vlc_object_t *, vlc_object_t **, int );
83 static void           SetAttachment ( vlc_object_t *, bool );
84
85 static vlc_list_t   * NewList       ( int );
86 static void           ListReplace   ( vlc_list_t *, vlc_object_t *, int );
87 /*static void           ListAppend    ( vlc_list_t *, vlc_object_t * );*/
88 static int            CountChildren ( vlc_object_t *, int );
89 static void           ListChildren  ( vlc_list_t *, vlc_object_t *, int );
90
91 static void vlc_object_destroy( vlc_object_t *p_this );
92 static void vlc_object_yield_locked( vlc_object_t *p_this );
93
94 /*****************************************************************************
95  * Local structure lock
96  *****************************************************************************/
97 static vlc_mutex_t    structure_lock;
98
99 void *vlc_custom_create( vlc_object_t *p_this, size_t i_size,
100                          int i_type, const char *psz_type )
101 {
102     vlc_object_t *p_new;
103     vlc_object_internals_t *p_priv;
104
105     /* NOTE:
106      * VLC objects are laid out as follow:
107      * - first the LibVLC-private per-object data,
108      * - then VLC_COMMON members from vlc_object_t,
109      * - finally, the type-specific data (if any).
110      *
111      * This function initializes the LibVLC and common data,
112      * and zeroes the rest.
113      */
114     p_priv = calloc( 1, sizeof( *p_priv ) + i_size );
115     if( p_priv == NULL )
116         return NULL;
117
118     if( i_type == VLC_OBJECT_GLOBAL )
119         p_new = p_this;
120     else
121     {
122         assert (i_size >= sizeof (vlc_object_t));
123         p_new = (vlc_object_t *)(p_priv + 1);
124     }
125
126     p_new->p_internals = p_priv;
127     p_new->i_object_type = i_type;
128     p_new->psz_object_type = psz_type;
129
130     p_new->psz_object_name = NULL;
131
132     p_new->b_die = false;
133     p_new->b_error = false;
134     p_new->b_dead = false;
135     p_priv->b_attached = false;
136     p_new->b_force = false;
137
138     p_new->psz_header = NULL;
139
140     p_new->i_flags |= p_this->i_flags
141         & (OBJECT_FLAGS_NODBG|OBJECT_FLAGS_QUIET|OBJECT_FLAGS_NOINTERACT);
142
143     p_priv->p_vars = calloc( sizeof( variable_t ), 16 );
144
145     if( !p_priv->p_vars )
146     {
147         free( p_priv );
148         return NULL;
149     }
150
151     if( i_type == VLC_OBJECT_GLOBAL )
152     {
153         /* If i_type is global, then p_new is actually p_libvlc_global */
154         libvlc_global_data_t *p_libvlc_global = (libvlc_global_data_t *)p_new;
155         p_new->p_libvlc = NULL;
156
157         p_libvlc_global->i_counter = 0;
158         p_new->i_object_id = 0;
159
160         p_libvlc_global->i_objects = 1;
161         p_libvlc_global->pp_objects = malloc( sizeof(vlc_object_t *) );
162         p_libvlc_global->pp_objects[0] = p_new;
163         p_priv->b_attached = true;
164         vlc_mutex_init( p_new, &structure_lock );
165     }
166     else
167     {
168         libvlc_global_data_t *p_libvlc_global = vlc_global();
169         if( i_type == VLC_OBJECT_LIBVLC )
170         {
171             p_new->p_libvlc = (libvlc_int_t*)p_new;
172             p_priv->b_attached = true;
173         }
174         else
175         {
176             p_new->p_libvlc = p_this->p_libvlc;
177         }
178
179         vlc_mutex_lock( &structure_lock );
180
181         p_libvlc_global->i_counter++;
182         p_new->i_object_id = p_libvlc_global->i_counter;
183
184         /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
185          * useless to try and recover anything if pp_objects gets smashed. */
186         TAB_APPEND( p_libvlc_global->i_objects, p_libvlc_global->pp_objects,
187                     p_new );
188
189         vlc_mutex_unlock( &structure_lock );
190     }
191
192     p_priv->i_refcount = 1;
193     p_priv->pf_destructor = kVLCDestructor;
194     p_priv->b_thread = false;
195     p_new->p_parent = NULL;
196     p_new->pp_children = NULL;
197     p_new->i_children = 0;
198
199     p_new->p_private = NULL;
200
201     /* Initialize mutexes and condvars */
202     vlc_mutex_init( p_new, &p_new->object_lock );
203     vlc_cond_init( p_new, &p_new->object_wait );
204     vlc_mutex_init( p_new, &p_priv->var_lock );
205     vlc_spin_init( &p_priv->spin );
206     p_priv->pipes[0] = p_priv->pipes[1] = -1;
207
208     if( i_type == VLC_OBJECT_LIBVLC )
209     {
210         var_Create( p_new, "list", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
211         var_AddCallback( p_new, "list", DumpCommand, NULL );
212         var_Create( p_new, "tree", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
213         var_AddCallback( p_new, "tree", DumpCommand, NULL );
214         var_Create( p_new, "vars", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
215         var_AddCallback( p_new, "vars", DumpCommand, NULL );
216     }
217
218     return p_new;
219 }
220
221
222 /**
223  * Allocates and initializes a vlc object.
224  *
225  * @param i_type known object type (all of them are negative integer values),
226  *               or object byte size (always positive).
227  *
228  * @return the new object, or NULL on error.
229  */
230 void * __vlc_object_create( vlc_object_t *p_this, int i_type )
231 {
232     const char   * psz_type;
233     size_t         i_size;
234
235     switch( i_type )
236     {
237         case VLC_OBJECT_LIBVLC:
238             i_size = sizeof(libvlc_int_t);
239             psz_type = "libvlc";
240             break;
241         case VLC_OBJECT_INTF:
242             i_size = sizeof(intf_thread_t);
243             psz_type = "interface";
244             break;
245         case VLC_OBJECT_DIALOGS:
246             i_size = sizeof(intf_thread_t);
247             psz_type = "dialogs";
248             break;
249         case VLC_OBJECT_DEMUX:
250             i_size = sizeof(demux_t);
251             psz_type = "demux";
252             break;
253         case VLC_OBJECT_ACCESS:
254             i_size = sizeof(access_t);
255             psz_type = "access";
256             break;
257         case VLC_OBJECT_DECODER:
258             i_size = sizeof(decoder_t);
259             psz_type = "decoder";
260             break;
261         case VLC_OBJECT_PACKETIZER:
262             i_size = sizeof(decoder_t);
263             psz_type = "packetizer";
264             break;
265         case VLC_OBJECT_ENCODER:
266             i_size = sizeof(encoder_t);
267             psz_type = "encoder";
268             break;
269         case VLC_OBJECT_FILTER:
270             i_size = sizeof(filter_t);
271             psz_type = "filter";
272             break;
273         case VLC_OBJECT_VOUT:
274             i_size = sizeof(vout_thread_t);
275             psz_type = "video output";
276             break;
277         case VLC_OBJECT_AOUT:
278             i_size = sizeof(aout_instance_t);
279             psz_type = "audio output";
280             break;
281         case VLC_OBJECT_SOUT:
282             i_size = sizeof(sout_instance_t);
283             psz_type = "stream output";
284             break;
285         case VLC_OBJECT_OPENGL:
286             i_size = sizeof( vout_thread_t );
287             psz_type = "opengl";
288             break;
289         case VLC_OBJECT_ANNOUNCE:
290             i_size = sizeof( announce_handler_t );
291             psz_type = "announce";
292             break;
293         case VLC_OBJECT_INTERACTION:
294             i_size = sizeof( interaction_t );
295             psz_type = "interaction";
296             break;
297         default:
298             i_size = i_type > (int)sizeof(vlc_object_t)
299                          ? i_type : (int)sizeof(vlc_object_t);
300             i_type = VLC_OBJECT_GENERIC;
301             psz_type = "generic";
302             break;
303     }
304
305     return vlc_custom_create( p_this, i_size, i_type, psz_type );
306 }
307
308
309 /**
310  ****************************************************************************
311  * Set the destructor of a vlc object
312  *
313  * This function sets the destructor of the vlc object. It will be called
314  * when the object is destroyed when the its refcount reaches 0.
315  * (It is called by the internal function vlc_object_destroy())
316  *****************************************************************************/
317 void __vlc_object_set_destructor( vlc_object_t *p_this,
318                                   vlc_destructor_t pf_destructor )
319 {
320     vlc_object_internals_t *p_priv = vlc_internals(p_this );
321
322     vlc_mutex_lock( &structure_lock );
323     p_priv->pf_destructor = pf_destructor;
324     vlc_mutex_unlock( &structure_lock );
325 }
326
327 /**
328  ****************************************************************************
329  * Destroy a vlc object (Internal)
330  *
331  * This function destroys an object that has been previously allocated with
332  * vlc_object_create. The object's refcount must be zero and it must not be
333  * attached to other objects in any way.
334  *****************************************************************************/
335 static void vlc_object_destroy( vlc_object_t *p_this )
336 {
337     vlc_object_internals_t *p_priv = vlc_internals( p_this );
338
339     /* Automatically detach the object from its parents */
340     if( p_this->p_parent ) vlc_object_detach( p_this );
341
342
343     /* Send a kill to the object's thread if applicable */
344     vlc_object_kill( p_this );
345
346     /* If we are running on a thread, wait until it ends */
347     if( p_priv->b_thread )
348         vlc_thread_join( p_this );
349
350     /* Call the custom "subclass" destructor */
351     if( p_priv->pf_destructor )
352         p_priv->pf_destructor( p_this );
353
354     /* Sanity checks */
355     if( p_this->i_children )
356     {
357         int i;
358
359         fprintf( stderr,
360                  "ERROR: cannot delete object (%i, %s) with %d children\n",
361                  p_this->i_object_id, p_this->psz_object_name,
362                  p_this->i_children );
363
364         for( i = 0; i < p_this->i_children; i++ )
365         {
366             fprintf( stderr,
367                      "ERROR: Remaining children object "
368                      "(id:%i, type:%s, name:%s)\n",
369                      p_this->pp_children[i]->i_object_id,
370                      p_this->pp_children[i]->psz_object_type,
371                      p_this->pp_children[i]->psz_object_name );
372         }
373         fflush(stderr);
374
375         /* Dump libvlc object to ease debugging */
376         vlc_object_dump( p_this->p_libvlc );
377
378         abort();
379     }
380
381     /* Destroy the associated variables, starting from the end so that
382      * no memmove calls have to be done. */
383     while( p_priv->i_vars )
384     {
385         var_Destroy( p_this, p_priv->p_vars[p_priv->i_vars - 1].psz_name );
386     }
387
388     free( p_priv->p_vars );
389     vlc_mutex_destroy( &p_priv->var_lock );
390
391     free( p_this->psz_header );
392
393     if( p_this->i_object_type == VLC_OBJECT_GLOBAL )
394     {
395         libvlc_global_data_t *p_global = (libvlc_global_data_t *)p_this;
396
397         /* Test for leaks */
398         if( p_global->i_objects > 0 )
399         {
400             int i;
401             for( i = 0; i < p_global->i_objects; i++ )
402             {
403                 /* We are leaking this object */
404                 fprintf( stderr,
405                          "ERROR: leaking object (id:%i, type:%s, name:%s)\n",
406                          p_global->pp_objects[i]->i_object_id,
407                          p_global->pp_objects[i]->psz_object_type,
408                          p_global->pp_objects[i]->psz_object_name );
409
410                 /* Dump libvlc object to ease debugging */
411                 vlc_object_dump( p_global->pp_objects[i] );
412
413                 fflush(stderr);
414             }
415
416             /* Dump libvlc object to ease debugging */
417             vlc_object_dump( p_this );
418
419             /* Strongly abort, cause we want these to be fixed */
420             abort();
421         }
422
423         /* We are the global object ... no need to lock. */
424         free( p_global->pp_objects );
425         p_global->pp_objects = NULL;
426
427         vlc_mutex_destroy( &structure_lock );
428     }
429
430 #if defined(WIN32) || defined(UNDER_CE)
431     /* if object has an associated thread, close it now */
432     if( p_priv->thread_id.hThread )
433        CloseHandle(p_priv->thread_id.hThread);
434 #endif
435
436     vlc_mutex_destroy( &p_this->object_lock );
437     vlc_cond_destroy( &p_this->object_wait );
438     vlc_spin_destroy( &p_priv->spin );
439     if( p_priv->pipes[1] != -1 )
440         close( p_priv->pipes[1] );
441     if( p_priv->pipes[0] != -1 )
442         close( p_priv->pipes[0] );
443
444     free( p_priv );
445 }
446
447
448 /** Inter-object signaling */
449
450 void __vlc_object_lock( vlc_object_t *obj )
451 {
452     vlc_mutex_lock( &obj->object_lock );
453 }
454
455 void __vlc_object_unlock( vlc_object_t *obj )
456 {
457     vlc_assert_locked( &obj->object_lock );
458     vlc_mutex_unlock( &obj->object_lock );
459 }
460
461 #ifdef WIN32
462 # include <winsock2.h>
463 # include <ws2tcpip.h>
464
465 /**
466  * select()-able pipes emulated using Winsock
467  */
468 static int pipe (int fd[2])
469 {
470     SOCKADDR_IN addr;
471     int addrlen = sizeof (addr);
472
473     SOCKET l = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP), a,
474            c = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
475     if ((l == INVALID_SOCKET) || (c == INVALID_SOCKET))
476         goto error;
477
478     memset (&addr, 0, sizeof (addr));
479     addr.sin_family = AF_INET;
480     addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
481     if (bind (l, (PSOCKADDR)&addr, sizeof (addr))
482      || getsockname (l, (PSOCKADDR)&addr, &addrlen)
483      || listen (l, 1)
484      || connect (c, (PSOCKADDR)&addr, addrlen))
485         goto error;
486
487     a = accept (l, NULL, NULL);
488     if (a == INVALID_SOCKET)
489         goto error;
490
491     closesocket (l);
492     //shutdown (a, 0);
493     //shutdown (c, 1);
494     fd[0] = c;
495     fd[1] = a;
496     return 0;
497
498 error:
499     if (l != INVALID_SOCKET)
500         closesocket (l);
501     if (c != INVALID_SOCKET)
502         closesocket (c);
503     return -1;
504 }
505
506 #undef  read
507 #define read( a, b, c )  recv (a, b, c, 0)
508 #undef  write
509 #define write( a, b, c ) send (a, b, c, 0)
510 #undef  close
511 #define close( a )       closesocket (a)
512 #endif /* WIN32 */
513
514 /**
515  * Returns the readable end of a pipe that becomes readable once termination
516  * of the object is requested (vlc_object_kill()).
517  * This can be used to wake-up out of a select() or poll() event loop, such
518  * typically when doing network I/O.
519  *
520  * Note that the pipe will remain the same for the lifetime of the object.
521  * DO NOT read the pipe nor close it yourself. Ever.
522  *
523  * @param obj object that would be "killed"
524  * @return a readable pipe descriptor, or -1 on error.
525  */
526 int __vlc_object_waitpipe( vlc_object_t *obj )
527 {
528     int pfd[2] = { -1, -1 };
529     struct vlc_object_internals_t *internals = obj->p_internals;
530     bool killed = false;
531
532     vlc_spin_lock (&internals->spin);
533     if (internals->pipes[0] == -1)
534     {
535         /* This can only ever happen if someone killed us without locking: */
536         assert (internals->pipes[1] == -1);
537         vlc_spin_unlock (&internals->spin);
538
539         if (pipe (pfd))
540             return -1;
541
542         vlc_spin_lock (&internals->spin);
543         if (internals->pipes[0] == -1)
544         {
545             internals->pipes[0] = pfd[0];
546             internals->pipes[1] = pfd[1];
547             pfd[0] = pfd[1] = -1;
548         }
549         killed = obj->b_die;
550     }
551     vlc_spin_unlock (&internals->spin);
552
553     if (killed)
554     {
555         /* Race condition: vlc_object_kill() already invoked! */
556         int fd;
557
558         vlc_spin_lock (&internals->spin);
559         fd = internals->pipes[1];
560         internals->pipes[1] = -1;
561         vlc_spin_unlock (&internals->spin);
562
563         msg_Dbg (obj, "waitpipe: object already dying");
564         if (fd != -1)
565             close (fd);
566     }
567
568     /* Race condition: two threads call pipe() - unlikely */
569     if (pfd[0] != -1)
570         close (pfd[0]);
571     if (pfd[1] != -1)
572         close (pfd[1]);
573
574     return internals->pipes[0];
575 }
576
577
578 /**
579  * Waits for the object to be signaled (using vlc_object_signal()).
580  * If the object already has a signal pending, this function will return
581  * immediately. It is asserted that the caller holds the object lock.
582  *
583  * @return true if the object is dying and should terminate.
584  */
585 bool __vlc_object_wait( vlc_object_t *obj )
586 {
587     vlc_assert_locked( &obj->object_lock );
588     vlc_cond_wait( &obj->object_wait, &obj->object_lock );
589     return obj->b_die;
590 }
591
592
593 /**
594  * Waits for the object to be signaled (using vlc_object_signal()), or for
595  * a timer to expire.
596  * If the object already has a signal pending, this function will return
597  * immediately. It is asserted that the caller holds the object lock.
598  *
599  * @return negative if the object is dying and should terminate,
600  * positive if the the object has been signaled but is not dying,
601  * 0 if timeout has been reached.
602  */
603 int __vlc_object_timedwait( vlc_object_t *obj, mtime_t deadline )
604 {
605     int v;
606
607     vlc_assert_locked( &obj->object_lock );
608     v = vlc_cond_timedwait( &obj->object_wait, &obj->object_lock, deadline );
609     if( v == 0 ) /* signaled */
610         return obj->b_die ? -1 : 1;
611     return 0;
612 }
613
614
615 /**
616  * Checks whether an object has been "killed".
617  * The object lock must be held.
618  *
619  * Typical code for an object thread could be:
620  *
621    vlc_object_lock (self);
622    ...initialization...
623    while (vlc_object_alive (self))
624    {
625        ...preprocessing...
626
627        if (vlc_object_wait (self))
628            continue;
629
630        ...postprocessing...
631    }
632    ...deinitialization...
633    vlc_object_unlock (self);
634  *
635  *
636  * @return true iff the object has not been killed yet
637  */
638 bool __vlc_object_alive( vlc_object_t *obj )
639 {
640     vlc_assert_locked( &obj->object_lock );
641     return !obj->b_die;
642 }
643
644
645 /**
646  * Signals an object for which the lock is held.
647  */
648 void __vlc_object_signal_unlocked( vlc_object_t *obj )
649 {
650     vlc_assert_locked (&obj->object_lock);
651     vlc_cond_signal( &obj->object_wait );
652 }
653
654
655 /**
656  * Requests termination of an object.
657  * If the object is LibVLC, also request to terminate all its children.
658  */
659 void __vlc_object_kill( vlc_object_t *p_this )
660 {
661     struct vlc_object_internals_t *internals = p_this->p_internals;
662     int fd;
663
664     vlc_mutex_lock( &p_this->object_lock );
665     p_this->b_die = true;
666
667     vlc_spin_lock (&internals->spin);
668     fd = internals->pipes[1];
669     internals->pipes[1] = -1;
670     vlc_spin_unlock (&internals->spin);
671
672     if( fd != -1 )
673     {
674         msg_Dbg (p_this, "waitpipe: object killed");
675         close (fd);
676     }
677
678     if( p_this->i_object_type == VLC_OBJECT_LIBVLC )
679         for( int i = 0; i < p_this->i_children ; i++ )
680             vlc_object_kill( p_this->pp_children[i] );
681
682     vlc_object_signal_unlocked( p_this );
683     vlc_mutex_unlock( &p_this->object_lock );
684 }
685
686
687 /**
688  * find an object given its ID
689  *
690  * This function looks for the object whose i_object_id field is i_id. We
691  * use a dichotomy so that lookups are in log2(n).
692  *****************************************************************************/
693 void * vlc_object_get( int i_id )
694 {
695     int i_max, i_middle;
696     vlc_object_t **pp_objects;
697     libvlc_global_data_t *p_libvlc_global = vlc_global();
698
699     vlc_mutex_lock( &structure_lock );
700
701     pp_objects = p_libvlc_global->pp_objects;
702
703     /* Perform our dichotomy */
704     for( i_max = p_libvlc_global->i_objects - 1 ; ; )
705     {
706         i_middle = i_max / 2;
707
708         if( pp_objects[i_middle]->i_object_id > i_id )
709         {
710             i_max = i_middle;
711         }
712         else if( pp_objects[i_middle]->i_object_id < i_id )
713         {
714             if( i_middle )
715             {
716                 pp_objects += i_middle;
717                 i_max -= i_middle;
718             }
719             else
720             {
721                 /* This happens when there are only two remaining objects */
722                 if( pp_objects[i_middle+1]->i_object_id == i_id
723                     && pp_objects[i_middle+1]->p_internals->i_refcount > 0 )
724                 {
725                     vlc_object_yield_locked( pp_objects[i_middle+1] );
726                     vlc_mutex_unlock( &structure_lock );
727                     return pp_objects[i_middle+1];
728                 }
729                 break;
730             }
731         }
732         else if( pp_objects[i_middle]->p_internals->i_refcount > 0 )
733         {
734             vlc_object_yield_locked( pp_objects[i_middle] );
735             vlc_mutex_unlock( &structure_lock );
736             return pp_objects[i_middle];
737         }
738
739         if( i_max == 0 )
740         {
741             /* this means that i_max == i_middle, and since we have already
742              * tested pp_objects[i_middle]), p_found is properly set. */
743             break;
744         }
745     }
746
747     vlc_mutex_unlock( &structure_lock );
748     return NULL;
749 }
750
751 /**
752  ****************************************************************************
753  * find a typed object and increment its refcount
754  *****************************************************************************
755  * This function recursively looks for a given object type. i_mode can be one
756  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
757  *****************************************************************************/
758 void * __vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode )
759 {
760     vlc_object_t *p_found;
761
762     vlc_mutex_lock( &structure_lock );
763
764     /* If we are of the requested type ourselves, don't look further */
765     if( !(i_mode & FIND_STRICT) && p_this->i_object_type == i_type
766         && p_this->p_internals->i_refcount > 0 )
767     {
768         vlc_object_yield_locked( p_this );
769         vlc_mutex_unlock( &structure_lock );
770         return p_this;
771     }
772
773     /* Otherwise, recursively look for the object */
774     if( (i_mode & 0x000f) == FIND_ANYWHERE )
775     {
776         vlc_object_t *p_root = p_this;
777
778         /* Find the root */
779         while( p_root->p_parent != NULL &&
780                p_root != VLC_OBJECT( p_this->p_libvlc ) )
781         {
782             p_root = p_root->p_parent;
783         }
784
785         p_found = FindObject( p_root, i_type, (i_mode & ~0x000f)|FIND_CHILD );
786         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
787         {
788             p_found = FindObject( VLC_OBJECT( p_this->p_libvlc ),
789                                   i_type, (i_mode & ~0x000f)|FIND_CHILD );
790         }
791     }
792     else
793     {
794         p_found = FindObject( p_this, i_type, i_mode );
795     }
796
797     vlc_mutex_unlock( &structure_lock );
798
799     return p_found;
800 }
801
802 /**
803  ****************************************************************************
804  * find a named object and increment its refcount
805  *****************************************************************************
806  * This function recursively looks for a given object name. i_mode can be one
807  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
808  *****************************************************************************/
809 void * __vlc_object_find_name( vlc_object_t *p_this, const char *psz_name,
810                                int i_mode )
811 {
812     vlc_object_t *p_found;
813
814     vlc_mutex_lock( &structure_lock );
815
816     /* Avoid obvious freed object uses */
817     assert( p_this->p_internals->i_refcount > 0 );
818
819     /* If have the requested name ourselves, don't look further */
820     if( !(i_mode & FIND_STRICT)
821         && p_this->psz_object_name
822         && !strcmp( p_this->psz_object_name, psz_name )
823         && p_this->p_internals->i_refcount > 0 )
824     {
825         vlc_object_yield_locked( p_this );
826         vlc_mutex_unlock( &structure_lock );
827         return p_this;
828     }
829
830     /* Otherwise, recursively look for the object */
831     if( (i_mode & 0x000f) == FIND_ANYWHERE )
832     {
833         vlc_object_t *p_root = p_this;
834
835         /* Find the root */
836         while( p_root->p_parent != NULL &&
837                p_root != VLC_OBJECT( p_this->p_libvlc ) )
838         {
839             p_root = p_root->p_parent;
840         }
841
842         p_found = FindObjectName( p_root, psz_name,
843                                  (i_mode & ~0x000f)|FIND_CHILD );
844         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
845         {
846             p_found = FindObjectName( VLC_OBJECT( p_this->p_libvlc ),
847                                       psz_name, (i_mode & ~0x000f)|FIND_CHILD );
848         }
849     }
850     else
851     {
852         p_found = FindObjectName( p_this, psz_name, i_mode );
853     }
854
855     vlc_mutex_unlock( &structure_lock );
856
857     return p_found;
858 }
859
860 /**
861  ****************************************************************************
862  * increment an object refcount
863  *****************************************************************************/
864
865 /* When the structure_lock is locked */
866 static void vlc_object_yield_locked( vlc_object_t *p_this )
867 {
868     vlc_assert_locked (&structure_lock);
869
870     /* Avoid obvious freed object uses */
871     assert( p_this->p_internals->i_refcount > 0 );
872
873     /* Increment the counter */
874     p_this->p_internals->i_refcount++;
875 }
876
877 /* Public function */
878 void __vlc_object_yield( vlc_object_t *p_this )
879 {
880     vlc_mutex_lock( &structure_lock );
881     vlc_object_yield_locked( p_this );
882     vlc_mutex_unlock( &structure_lock );
883 }
884
885
886 /*****************************************************************************
887  * decrement an object refcount
888  * And destroy the object if its refcount reach zero.
889  *****************************************************************************/
890 void __vlc_object_release( vlc_object_t *p_this )
891 {
892     bool b_should_destroy;
893
894     vlc_mutex_lock( &structure_lock );
895
896     assert( p_this->p_internals->i_refcount > 0 );
897     p_this->p_internals->i_refcount--;
898     b_should_destroy = (p_this->p_internals->i_refcount == 0);
899
900     if( b_should_destroy )
901     {
902         /* Make sure this object can't be obtained via vlc_find_object now that
903          * it is freed */
904         libvlc_global_data_t *p_libvlc_global = vlc_global();
905         int i_index;
906
907         /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
908          * useless to try and recover anything if pp_objects gets smashed. */
909         i_index = FindIndex( p_this, p_libvlc_global->pp_objects,
910                              p_libvlc_global->i_objects );
911         REMOVE_ELEM( p_libvlc_global->pp_objects,
912                      p_libvlc_global->i_objects, i_index );
913     }
914
915     vlc_mutex_unlock( &structure_lock );
916
917     if( b_should_destroy )
918         vlc_object_destroy( p_this );
919 }
920
921 /**
922  ****************************************************************************
923  * attach object to a parent object
924  *****************************************************************************
925  * This function sets p_this as a child of p_parent, and p_parent as a parent
926  * of p_this. This link can be undone using vlc_object_detach.
927  *****************************************************************************/
928 void __vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent )
929 {
930     if( !p_this ) return;
931
932     vlc_mutex_lock( &structure_lock );
933
934     /* Avoid obvious freed object uses */
935     assert( p_this->p_internals->i_refcount > 0 );
936
937     /* Attach the parent to its child */
938     p_this->p_parent = p_parent;
939
940     /* Attach the child to its parent */
941     INSERT_ELEM( p_parent->pp_children, p_parent->i_children,
942                  p_parent->i_children, p_this );
943
944     /* Climb up the tree to see whether we are connected with the root */
945     if( p_parent->p_internals->b_attached )
946     {
947         SetAttachment( p_this, true );
948     }
949
950     vlc_mutex_unlock( &structure_lock );
951 }
952
953 /**
954  ****************************************************************************
955  * detach object from its parent
956  *****************************************************************************
957  * This function removes all links between an object and its parent.
958  *****************************************************************************/
959 void __vlc_object_detach( vlc_object_t *p_this )
960 {
961     if( !p_this ) return;
962
963     vlc_mutex_lock( &structure_lock );
964
965     if( !p_this->p_parent )
966     {
967         msg_Err( p_this, "object is not attached" );
968         vlc_mutex_unlock( &structure_lock );
969         return;
970     }
971
972     /* Climb up the tree to see whether we are connected with the root */
973     if( p_this->p_parent->p_internals->b_attached )
974     {
975         SetAttachment( p_this, false );
976     }
977
978     DetachObject( p_this );
979     vlc_mutex_unlock( &structure_lock );
980     p_this = NULL;
981 }
982
983 /**
984  ****************************************************************************
985  * find a list typed objects and increment their refcount
986  *****************************************************************************
987  * This function recursively looks for a given object type. i_mode can be one
988  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
989  *****************************************************************************/
990 vlc_list_t * __vlc_list_find( vlc_object_t *p_this, int i_type, int i_mode )
991 {
992     vlc_list_t *p_list;
993     vlc_object_t **pp_current, **pp_end;
994     int i_count = 0, i_index = 0;
995     libvlc_global_data_t *p_libvlc_global = vlc_global();
996
997     vlc_mutex_lock( &structure_lock );
998
999     /* Look for the objects */
1000     switch( i_mode & 0x000f )
1001     {
1002     case FIND_ANYWHERE:
1003         pp_current = p_libvlc_global->pp_objects;
1004         pp_end = pp_current + p_libvlc_global->i_objects;
1005
1006         for( ; pp_current < pp_end ; pp_current++ )
1007         {
1008             if( (*pp_current)->p_internals->b_attached
1009                  && (*pp_current)->i_object_type == i_type )
1010             {
1011                 i_count++;
1012             }
1013         }
1014
1015         p_list = NewList( i_count );
1016         pp_current = p_libvlc_global->pp_objects;
1017
1018         for( ; pp_current < pp_end ; pp_current++ )
1019         {
1020             if( (*pp_current)->p_internals->b_attached
1021                  && (*pp_current)->i_object_type == i_type )
1022             {
1023                 ListReplace( p_list, *pp_current, i_index );
1024                 if( i_index < i_count ) i_index++;
1025             }
1026         }
1027     break;
1028
1029     case FIND_CHILD:
1030         i_count = CountChildren( p_this, i_type );
1031         p_list = NewList( i_count );
1032
1033         /* Check allocation was successful */
1034         if( p_list->i_count != i_count )
1035         {
1036             msg_Err( p_this, "list allocation failed!" );
1037             p_list->i_count = 0;
1038             break;
1039         }
1040
1041         p_list->i_count = 0;
1042         ListChildren( p_list, p_this, i_type );
1043         break;
1044
1045     default:
1046         msg_Err( p_this, "unimplemented!" );
1047         p_list = NewList( 0 );
1048         break;
1049     }
1050
1051     vlc_mutex_unlock( &structure_lock );
1052
1053     return p_list;
1054 }
1055
1056 /*****************************************************************************
1057  * DumpCommand: print the current vlc structure
1058  *****************************************************************************
1059  * This function prints either an ASCII tree showing the connections between
1060  * vlc objects, and additional information such as their refcount, thread ID,
1061  * etc. (command "tree"), or the same data as a simple list (command "list").
1062  *****************************************************************************/
1063 static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd,
1064                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1065 {
1066     libvlc_global_data_t *p_libvlc_global = vlc_global();
1067
1068     (void)oldval; (void)p_data;
1069     if( *psz_cmd == 'l' )
1070     {
1071         vlc_mutex_lock( &structure_lock );
1072
1073         vlc_object_t **pp_current, **pp_end;
1074
1075         pp_current = p_libvlc_global->pp_objects;
1076         pp_end = pp_current + p_libvlc_global->i_objects;
1077
1078         for( ; pp_current < pp_end ; pp_current++ )
1079         {
1080             if( (*pp_current)->p_internals->b_attached )
1081             {
1082                 PrintObject( *pp_current, "" );
1083             }
1084             else
1085             {
1086                 printf( " o %.8i %s (not attached)\n",
1087                         (*pp_current)->i_object_id,
1088                         (*pp_current)->psz_object_type );
1089             }
1090         }
1091
1092         vlc_mutex_unlock( &structure_lock );
1093     }
1094     else
1095     {
1096         vlc_object_t *p_object = NULL;
1097
1098         if( *newval.psz_string )
1099         {
1100             char *end;
1101             int i_id = strtol( newval.psz_string, &end, 0 );
1102             if( end != newval.psz_string )
1103                 p_object = vlc_object_get( i_id );
1104             else
1105             {
1106                 /* try using the object's name to find it */
1107                 vlc_object_t *p_libvlc = vlc_object_get( 1 );
1108                 if( p_libvlc )
1109                 {
1110                     /* Look in p_libvlc's children tree */
1111                     p_object = vlc_object_find_name( p_libvlc,
1112                                                      newval.psz_string,
1113                                                      FIND_CHILD );
1114                     vlc_object_release( p_libvlc );
1115                 }
1116                 if( !p_object )
1117                 {
1118                     /* If it's not in libvlc, look in libvlc_global (== p_this) */
1119                     p_object = vlc_object_find_name( p_this,
1120                                                      newval.psz_string,
1121                                                      FIND_CHILD );
1122                 }
1123             }
1124
1125             if( !p_object )
1126             {
1127                 return VLC_ENOOBJ;
1128             }
1129         }
1130
1131         vlc_mutex_lock( &structure_lock );
1132
1133         if( *psz_cmd == 't' )
1134         {
1135             char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
1136
1137             if( !p_object )
1138                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
1139
1140             psz_foo[0] = '|';
1141             DumpStructure( p_object, 0, psz_foo );
1142         }
1143         else if( *psz_cmd == 'v' )
1144         {
1145             int i;
1146
1147             if( !p_object )
1148                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
1149
1150             PrintObject( p_object, "" );
1151
1152             if( !p_object->p_internals->i_vars )
1153                 printf( " `-o No variables\n" );
1154             for( i = 0; i < p_object->p_internals->i_vars; i++ )
1155             {
1156                 variable_t *p_var = p_object->p_internals->p_vars + i;
1157
1158                 const char *psz_type = "unknown";
1159                 switch( p_var->i_type & VLC_VAR_TYPE )
1160                 {
1161 #define MYCASE( type, nice )                \
1162                     case VLC_VAR_ ## type:  \
1163                         psz_type = nice;    \
1164                         break;
1165                     MYCASE( VOID, "void" );
1166                     MYCASE( BOOL, "bool" );
1167                     MYCASE( INTEGER, "integer" );
1168                     MYCASE( HOTKEY, "hotkey" );
1169                     MYCASE( STRING, "string" );
1170                     MYCASE( MODULE, "module" );
1171                     MYCASE( FILE, "file" );
1172                     MYCASE( DIRECTORY, "directory" );
1173                     MYCASE( VARIABLE, "variable" );
1174                     MYCASE( FLOAT, "float" );
1175                     MYCASE( TIME, "time" );
1176                     MYCASE( ADDRESS, "address" );
1177                     MYCASE( MUTEX, "mutex" );
1178                     MYCASE( LIST, "list" );
1179 #undef MYCASE
1180                 }
1181                 printf( " %c-o \"%s\" (%s",
1182                         i + 1 == p_object->p_internals->i_vars ? '`' : '|',
1183                         p_var->psz_name, psz_type );
1184                 if( p_var->psz_text )
1185                     printf( ", %s", p_var->psz_text );
1186                 printf( ")" );
1187                 if( p_var->i_type & VLC_VAR_ISCOMMAND )
1188                     printf( ", command" );
1189                 if( p_var->i_entries )
1190                     printf( ", %d callbacks", p_var->i_entries );
1191                 switch( p_var->i_type & 0x00f0 )
1192                 {
1193                     case VLC_VAR_VOID:
1194                     case VLC_VAR_MUTEX:
1195                         break;
1196                     case VLC_VAR_BOOL:
1197                         printf( ": %s", p_var->val.b_bool ? "true" : "false" );
1198                         break;
1199                     case VLC_VAR_INTEGER:
1200                         printf( ": %d", p_var->val.i_int );
1201                         break;
1202                     case VLC_VAR_STRING:
1203                         printf( ": \"%s\"", p_var->val.psz_string );
1204                         break;
1205                     case VLC_VAR_FLOAT:
1206                         printf( ": %f", p_var->val.f_float );
1207                         break;
1208                     case VLC_VAR_TIME:
1209                         printf( ": " I64Fi, (int64_t)p_var->val.i_time );
1210                         break;
1211                     case VLC_VAR_ADDRESS:
1212                         printf( ": %p", p_var->val.p_address );
1213                         break;
1214                     case VLC_VAR_LIST:
1215                         printf( ": TODO" );
1216                         break;
1217                 }
1218                 printf( "\n" );
1219             }
1220         }
1221
1222         vlc_mutex_unlock( &structure_lock );
1223
1224         if( *newval.psz_string )
1225         {
1226             vlc_object_release( p_object );
1227         }
1228     }
1229
1230     return VLC_SUCCESS;
1231 }
1232
1233 /*****************************************************************************
1234  * vlc_list_release: free a list previously allocated by vlc_list_find
1235  *****************************************************************************
1236  * This function decreases the refcount of all objects in the list and
1237  * frees the list.
1238  *****************************************************************************/
1239 void vlc_list_release( vlc_list_t *p_list )
1240 {
1241     int i_index;
1242
1243     for( i_index = 0; i_index < p_list->i_count; i_index++ )
1244     {
1245         vlc_object_release( p_list->p_values[i_index].p_object );
1246     }
1247
1248     free( p_list->p_values );
1249     free( p_list );
1250 }
1251
1252 /*****************************************************************************
1253  * dump an object. (Debug function)
1254  *****************************************************************************/
1255 void __vlc_object_dump( vlc_object_t *p_this )
1256 {
1257     vlc_mutex_lock( &structure_lock );
1258     char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
1259     psz_foo[0] = '|';
1260     DumpStructure( p_this, 0, psz_foo );
1261     vlc_mutex_unlock( &structure_lock );
1262 }
1263
1264 /* Following functions are local */
1265
1266 /*****************************************************************************
1267  * FindIndex: find the index of an object in an array of objects
1268  *****************************************************************************
1269  * This function assumes that p_this can be found in pp_objects. It will not
1270  * crash if p_this cannot be found, but will return a wrong value. It is your
1271  * duty to check the return value if you are not certain that the object could
1272  * be found for sure.
1273  *****************************************************************************/
1274 static int FindIndex( vlc_object_t *p_this,
1275                       vlc_object_t **pp_objects, int i_count )
1276 {
1277     int i_middle = i_count / 2;
1278
1279     if( i_count == 0 )
1280     {
1281         return 0;
1282     }
1283
1284     if( pp_objects[i_middle] == p_this )
1285     {
1286         return i_middle;
1287     }
1288
1289     if( i_count == 1 )
1290     {
1291         return 0;
1292     }
1293
1294     /* We take advantage of the sorted array */
1295     if( pp_objects[i_middle]->i_object_id < p_this->i_object_id )
1296     {
1297         return i_middle + FindIndex( p_this, pp_objects + i_middle,
1298                                              i_count - i_middle );
1299     }
1300     else
1301     {
1302         return FindIndex( p_this, pp_objects, i_middle );
1303     }
1304 }
1305
1306 static vlc_object_t * FindObject( vlc_object_t *p_this, int i_type, int i_mode )
1307 {
1308     int i;
1309     vlc_object_t *p_tmp;
1310
1311     switch( i_mode & 0x000f )
1312     {
1313     case FIND_PARENT:
1314         p_tmp = p_this->p_parent;
1315         if( p_tmp )
1316         {
1317             if( p_tmp->i_object_type == i_type
1318                 && p_tmp->p_internals->i_refcount > 0 )
1319             {
1320                 vlc_object_yield_locked( p_tmp );
1321                 return p_tmp;
1322             }
1323             else
1324             {
1325                 return FindObject( p_tmp, i_type, i_mode );
1326             }
1327         }
1328         break;
1329
1330     case FIND_CHILD:
1331         for( i = p_this->i_children; i--; )
1332         {
1333             p_tmp = p_this->pp_children[i];
1334             if( p_tmp->i_object_type == i_type
1335                 && p_tmp->p_internals->i_refcount > 0 )
1336             {
1337                 vlc_object_yield_locked( p_tmp );
1338                 return p_tmp;
1339             }
1340             else if( p_tmp->i_children )
1341             {
1342                 p_tmp = FindObject( p_tmp, i_type, i_mode );
1343                 if( p_tmp )
1344                 {
1345                     return p_tmp;
1346                 }
1347             }
1348         }
1349         break;
1350
1351     case FIND_ANYWHERE:
1352         /* Handled in vlc_object_find */
1353         break;
1354     }
1355
1356     return NULL;
1357 }
1358
1359 static vlc_object_t * FindObjectName( vlc_object_t *p_this,
1360                                       const char *psz_name,
1361                                       int i_mode )
1362 {
1363     int i;
1364     vlc_object_t *p_tmp;
1365
1366     switch( i_mode & 0x000f )
1367     {
1368     case FIND_PARENT:
1369         p_tmp = p_this->p_parent;
1370         if( p_tmp )
1371         {
1372             if( p_tmp->psz_object_name
1373                 && !strcmp( p_tmp->psz_object_name, psz_name )
1374                 && p_tmp->p_internals->i_refcount > 0 )
1375             {
1376                 vlc_object_yield_locked( p_tmp );
1377                 return p_tmp;
1378             }
1379             else
1380             {
1381                 return FindObjectName( p_tmp, psz_name, i_mode );
1382             }
1383         }
1384         break;
1385
1386     case FIND_CHILD:
1387         for( i = p_this->i_children; i--; )
1388         {
1389             p_tmp = p_this->pp_children[i];
1390             if( p_tmp->psz_object_name
1391                 && !strcmp( p_tmp->psz_object_name, psz_name )
1392                 && p_tmp->p_internals->i_refcount > 0 )
1393             {
1394                 vlc_object_yield_locked( p_tmp );
1395                 return p_tmp;
1396             }
1397             else if( p_tmp->i_children )
1398             {
1399                 p_tmp = FindObjectName( p_tmp, psz_name, i_mode );
1400                 if( p_tmp )
1401                 {
1402                     return p_tmp;
1403                 }
1404             }
1405         }
1406         break;
1407
1408     case FIND_ANYWHERE:
1409         /* Handled in vlc_object_find */
1410         break;
1411     }
1412
1413     return NULL;
1414 }
1415
1416 static void DetachObject( vlc_object_t *p_this )
1417 {
1418     vlc_object_t *p_parent = p_this->p_parent;
1419     int i_index, i;
1420
1421     /* Remove p_this's parent */
1422     p_this->p_parent = NULL;
1423
1424     /* Remove all of p_parent's children which are p_this */
1425     for( i_index = p_parent->i_children ; i_index-- ; )
1426     {
1427         if( p_parent->pp_children[i_index] == p_this )
1428         {
1429             p_parent->i_children--;
1430             for( i = i_index ; i < p_parent->i_children ; i++ )
1431             {
1432                 p_parent->pp_children[i] = p_parent->pp_children[i+1];
1433             }
1434         }
1435     }
1436
1437     if( p_parent->i_children )
1438     {
1439         p_parent->pp_children = (vlc_object_t **)realloc( p_parent->pp_children,
1440                                p_parent->i_children * sizeof(vlc_object_t *) );
1441     }
1442     else
1443     {
1444         free( p_parent->pp_children );
1445         p_parent->pp_children = NULL;
1446     }
1447 }
1448
1449 /*****************************************************************************
1450  * SetAttachment: recursively set the b_attached flag of a subtree.
1451  *****************************************************************************
1452  * This function is used by the attach and detach functions to propagate
1453  * the b_attached flag in a subtree.
1454  *****************************************************************************/
1455 static void SetAttachment( vlc_object_t *p_this, bool b_attached )
1456 {
1457     int i_index;
1458
1459     for( i_index = p_this->i_children ; i_index-- ; )
1460     {
1461         SetAttachment( p_this->pp_children[i_index], b_attached );
1462     }
1463
1464     p_this->p_internals->b_attached = b_attached;
1465 }
1466
1467 static void PrintObject( vlc_object_t *p_this, const char *psz_prefix )
1468 {
1469     char psz_children[20], psz_refcount[20], psz_thread[30], psz_name[50],
1470          psz_parent[20];
1471
1472     psz_name[0] = '\0';
1473     if( p_this->psz_object_name )
1474     {
1475         snprintf( psz_name, 49, " \"%s\"", p_this->psz_object_name );
1476         if( psz_name[48] )
1477             psz_name[48] = '\"';
1478     }
1479
1480     psz_children[0] = '\0';
1481     switch( p_this->i_children )
1482     {
1483         case 0:
1484             break;
1485         case 1:
1486             strcpy( psz_children, ", 1 child" );
1487             break;
1488         default:
1489             snprintf( psz_children, 19, ", %i children", p_this->i_children );
1490             break;
1491     }
1492
1493     psz_refcount[0] = '\0';
1494     if( p_this->p_internals->i_refcount > 0 )
1495         snprintf( psz_refcount, 19, ", refcount %u",
1496                   p_this->p_internals->i_refcount );
1497
1498     psz_thread[0] = '\0';
1499     if( p_this->p_internals->b_thread )
1500         snprintf( psz_thread, 29, " (thread %u)",
1501 #if defined(WIN32) || defined(UNDER_CE)
1502                   (unsigned)p_this->p_internals->thread_id.id );
1503 #else
1504                   (unsigned)p_this->p_internals->thread_id );
1505 #endif
1506
1507     psz_parent[0] = '\0';
1508     if( p_this->p_parent )
1509         snprintf( psz_parent, 19, ", parent %i", p_this->p_parent->i_object_id );
1510
1511     printf( " %so %.8i %s%s%s%s%s%s\n", psz_prefix,
1512             p_this->i_object_id, p_this->psz_object_type,
1513             psz_name, psz_thread, psz_refcount, psz_children,
1514             psz_parent );
1515 }
1516
1517 static void DumpStructure( vlc_object_t *p_this, int i_level, char *psz_foo )
1518 {
1519     int i;
1520     char i_back = psz_foo[i_level];
1521     psz_foo[i_level] = '\0';
1522
1523     PrintObject( p_this, psz_foo );
1524
1525     psz_foo[i_level] = i_back;
1526
1527     if( i_level / 2 >= MAX_DUMPSTRUCTURE_DEPTH )
1528     {
1529         msg_Warn( p_this, "structure tree is too deep" );
1530         return;
1531     }
1532
1533     for( i = 0 ; i < p_this->i_children ; i++ )
1534     {
1535         if( i_level )
1536         {
1537             psz_foo[i_level-1] = ' ';
1538
1539             if( psz_foo[i_level-2] == '`' )
1540             {
1541                 psz_foo[i_level-2] = ' ';
1542             }
1543         }
1544
1545         if( i == p_this->i_children - 1 )
1546         {
1547             psz_foo[i_level] = '`';
1548         }
1549         else
1550         {
1551             psz_foo[i_level] = '|';
1552         }
1553
1554         psz_foo[i_level+1] = '-';
1555         psz_foo[i_level+2] = '\0';
1556
1557         DumpStructure( p_this->pp_children[i], i_level + 2, psz_foo );
1558     }
1559 }
1560
1561 static vlc_list_t * NewList( int i_count )
1562 {
1563     vlc_list_t * p_list = (vlc_list_t *)malloc( sizeof( vlc_list_t ) );
1564     if( p_list == NULL )
1565     {
1566         return NULL;
1567     }
1568
1569     p_list->i_count = i_count;
1570
1571     if( i_count == 0 )
1572     {
1573         p_list->p_values = NULL;
1574         return p_list;
1575     }
1576
1577     p_list->p_values = malloc( i_count * sizeof( vlc_value_t ) );
1578     if( p_list->p_values == NULL )
1579     {
1580         p_list->i_count = 0;
1581         return p_list;
1582     }
1583
1584     return p_list;
1585 }
1586
1587 static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object,
1588                          int i_index )
1589 {
1590     if( p_list == NULL || i_index >= p_list->i_count )
1591     {
1592         return;
1593     }
1594
1595     vlc_object_yield_locked( p_object );
1596
1597     p_list->p_values[i_index].p_object = p_object;
1598
1599     return;
1600 }
1601
1602 /*static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object )
1603 {
1604     if( p_list == NULL )
1605     {
1606         return;
1607     }
1608
1609     p_list->p_values = realloc( p_list->p_values, (p_list->i_count + 1)
1610                                 * sizeof( vlc_value_t ) );
1611     if( p_list->p_values == NULL )
1612     {
1613         p_list->i_count = 0;
1614         return;
1615     }
1616
1617     vlc_object_yield_locked( p_object );
1618
1619     p_list->p_values[p_list->i_count].p_object = p_object;
1620     p_list->i_count++;
1621
1622     return;
1623 }*/
1624
1625 static int CountChildren( vlc_object_t *p_this, int i_type )
1626 {
1627     vlc_object_t *p_tmp;
1628     int i, i_count = 0;
1629
1630     for( i = 0; i < p_this->i_children; i++ )
1631     {
1632         p_tmp = p_this->pp_children[i];
1633
1634         if( p_tmp->i_object_type == i_type )
1635         {
1636             i_count++;
1637         }
1638
1639         if( p_tmp->i_children )
1640         {
1641             i_count += CountChildren( p_tmp, i_type );
1642         }
1643     }
1644
1645     return i_count;
1646 }
1647
1648 static void ListChildren( vlc_list_t *p_list, vlc_object_t *p_this, int i_type )
1649 {
1650     vlc_object_t *p_tmp;
1651     int i;
1652
1653     for( i = 0; i < p_this->i_children; i++ )
1654     {
1655         p_tmp = p_this->pp_children[i];
1656
1657         if( p_tmp->i_object_type == i_type )
1658         {
1659             ListReplace( p_list, p_tmp, p_list->i_count++ );
1660         }
1661
1662         if( p_tmp->i_children )
1663         {
1664             ListChildren( p_list, p_tmp, i_type );
1665         }
1666     }
1667 }