]> git.sesse.net Git - vlc/blob - src/misc/objects.c
objects: Dump libvlc object when aborting because of leaks.
[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                 fflush(stderr);
410             }
411
412             /* Dump libvlc object to ease debugging */
413             vlc_object_dump( p_this );
414
415             /* Strongly abort, cause we want these to be fixed */
416             abort();
417         }
418
419         /* We are the global object ... no need to lock. */
420         free( p_global->pp_objects );
421         p_global->pp_objects = NULL;
422
423         vlc_mutex_destroy( &structure_lock );
424     }
425
426 #if defined(WIN32) || defined(UNDER_CE)
427     /* if object has an associated thread, close it now */
428     if( p_priv->thread_id.hThread )
429        CloseHandle(p_priv->thread_id.hThread);
430 #endif
431
432     vlc_mutex_destroy( &p_this->object_lock );
433     vlc_cond_destroy( &p_this->object_wait );
434     vlc_spin_destroy( &p_priv->spin );
435     if( p_priv->pipes[1] != -1 )
436         close( p_priv->pipes[1] );
437     if( p_priv->pipes[0] != -1 )
438         close( p_priv->pipes[0] );
439
440     free( p_priv );
441 }
442
443
444 /** Inter-object signaling */
445
446 void __vlc_object_lock( vlc_object_t *obj )
447 {
448     vlc_mutex_lock( &obj->object_lock );
449 }
450
451 void __vlc_object_unlock( vlc_object_t *obj )
452 {
453     vlc_assert_locked( &obj->object_lock );
454     vlc_mutex_unlock( &obj->object_lock );
455 }
456
457 #ifdef WIN32
458 # include <winsock2.h>
459 # include <ws2tcpip.h>
460
461 /**
462  * select()-able pipes emulated using Winsock
463  */
464 static int pipe (int fd[2])
465 {
466     SOCKADDR_IN addr;
467     int addrlen = sizeof (addr);
468
469     SOCKET l = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP), a,
470            c = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
471     if ((l == INVALID_SOCKET) || (c == INVALID_SOCKET))
472         goto error;
473
474     memset (&addr, 0, sizeof (addr));
475     addr.sin_family = AF_INET;
476     addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
477     if (bind (l, (PSOCKADDR)&addr, sizeof (addr))
478      || getsockname (l, (PSOCKADDR)&addr, &addrlen)
479      || listen (l, 1)
480      || connect (c, (PSOCKADDR)&addr, addrlen))
481         goto error;
482
483     a = accept (l, NULL, NULL);
484     if (a == INVALID_SOCKET)
485         goto error;
486
487     closesocket (l);
488     //shutdown (a, 0);
489     //shutdown (c, 1);
490     fd[0] = c;
491     fd[1] = a;
492     return 0;
493
494 error:
495     if (l != INVALID_SOCKET)
496         closesocket (l);
497     if (c != INVALID_SOCKET)
498         closesocket (c);
499     return -1;
500 }
501
502 #undef  read
503 #define read( a, b, c )  recv (a, b, c, 0)
504 #undef  write
505 #define write( a, b, c ) send (a, b, c, 0)
506 #undef  close
507 #define close( a )       closesocket (a)
508 #endif /* WIN32 */
509
510 /**
511  * Returns the readable end of a pipe that becomes readable once termination
512  * of the object is requested (vlc_object_kill()).
513  * This can be used to wake-up out of a select() or poll() event loop, such
514  * typically when doing network I/O.
515  *
516  * Note that the pipe will remain the same for the lifetime of the object.
517  * DO NOT read the pipe nor close it yourself. Ever.
518  *
519  * @param obj object that would be "killed"
520  * @return a readable pipe descriptor, or -1 on error.
521  */
522 int __vlc_object_waitpipe( vlc_object_t *obj )
523 {
524     int pfd[2] = { -1, -1 };
525     struct vlc_object_internals_t *internals = obj->p_internals;
526     bool killed = false;
527
528     vlc_spin_lock (&internals->spin);
529     if (internals->pipes[0] == -1)
530     {
531         /* This can only ever happen if someone killed us without locking: */
532         assert (internals->pipes[1] == -1);
533         vlc_spin_unlock (&internals->spin);
534
535         if (pipe (pfd))
536             return -1;
537
538         vlc_spin_lock (&internals->spin);
539         if (internals->pipes[0] == -1)
540         {
541             internals->pipes[0] = pfd[0];
542             internals->pipes[1] = pfd[1];
543             pfd[0] = pfd[1] = -1;
544         }
545         killed = obj->b_die;
546     }
547     vlc_spin_unlock (&internals->spin);
548
549     if (killed)
550     {
551         /* Race condition: vlc_object_kill() already invoked! */
552         int fd;
553
554         vlc_spin_lock (&internals->spin);
555         fd = internals->pipes[1];
556         internals->pipes[1] = -1;
557         vlc_spin_unlock (&internals->spin);
558
559         msg_Dbg (obj, "waitpipe: object already dying");
560         if (fd != -1)
561             close (fd);
562     }
563
564     /* Race condition: two threads call pipe() - unlikely */
565     if (pfd[0] != -1)
566         close (pfd[0]);
567     if (pfd[1] != -1)
568         close (pfd[1]);
569
570     return internals->pipes[0];
571 }
572
573
574 /**
575  * Waits for the object to be signaled (using vlc_object_signal()).
576  * If the object already has a signal pending, this function will return
577  * immediately. It is asserted that the caller holds the object lock.
578  *
579  * @return true if the object is dying and should terminate.
580  */
581 bool __vlc_object_wait( vlc_object_t *obj )
582 {
583     vlc_assert_locked( &obj->object_lock );
584     vlc_cond_wait( &obj->object_wait, &obj->object_lock );
585     return obj->b_die;
586 }
587
588
589 /**
590  * Waits for the object to be signaled (using vlc_object_signal()), or for
591  * a timer to expire.
592  * If the object already has a signal pending, this function will return
593  * immediately. It is asserted that the caller holds the object lock.
594  *
595  * @return negative if the object is dying and should terminate,
596  * positive if the the object has been signaled but is not dying,
597  * 0 if timeout has been reached.
598  */
599 int __vlc_object_timedwait( vlc_object_t *obj, mtime_t deadline )
600 {
601     int v;
602
603     vlc_assert_locked( &obj->object_lock );
604     v = vlc_cond_timedwait( &obj->object_wait, &obj->object_lock, deadline );
605     if( v == 0 ) /* signaled */
606         return obj->b_die ? -1 : 1;
607     return 0;
608 }
609
610
611 /**
612  * Checks whether an object has been "killed".
613  * The object lock must be held.
614  *
615  * Typical code for an object thread could be:
616  *
617    vlc_object_lock (self);
618    ...initialization...
619    while (vlc_object_alive (self))
620    {
621        ...preprocessing...
622
623        if (vlc_object_wait (self))
624            continue;
625
626        ...postprocessing...
627    }
628    ...deinitialization...
629    vlc_object_unlock (self);
630  *
631  *
632  * @return true iff the object has not been killed yet
633  */
634 bool __vlc_object_alive( vlc_object_t *obj )
635 {
636     vlc_assert_locked( &obj->object_lock );
637     return !obj->b_die;
638 }
639
640
641 /**
642  * Signals an object for which the lock is held.
643  */
644 void __vlc_object_signal_unlocked( vlc_object_t *obj )
645 {
646     vlc_assert_locked (&obj->object_lock);
647     vlc_cond_signal( &obj->object_wait );
648 }
649
650
651 /**
652  * Requests termination of an object.
653  * If the object is LibVLC, also request to terminate all its children.
654  */
655 void __vlc_object_kill( vlc_object_t *p_this )
656 {
657     struct vlc_object_internals_t *internals = p_this->p_internals;
658     int fd;
659
660     vlc_mutex_lock( &p_this->object_lock );
661     p_this->b_die = true;
662
663     vlc_spin_lock (&internals->spin);
664     fd = internals->pipes[1];
665     internals->pipes[1] = -1;
666     vlc_spin_unlock (&internals->spin);
667
668     if( fd != -1 )
669     {
670         msg_Dbg (p_this, "waitpipe: object killed");
671         close (fd);
672     }
673
674     if( p_this->i_object_type == VLC_OBJECT_LIBVLC )
675         for( int i = 0; i < p_this->i_children ; i++ )
676             vlc_object_kill( p_this->pp_children[i] );
677
678     vlc_object_signal_unlocked( p_this );
679     vlc_mutex_unlock( &p_this->object_lock );
680 }
681
682
683 /**
684  * find an object given its ID
685  *
686  * This function looks for the object whose i_object_id field is i_id. We
687  * use a dichotomy so that lookups are in log2(n).
688  *****************************************************************************/
689 void * vlc_object_get( int i_id )
690 {
691     int i_max, i_middle;
692     vlc_object_t **pp_objects;
693     libvlc_global_data_t *p_libvlc_global = vlc_global();
694
695     vlc_mutex_lock( &structure_lock );
696
697     pp_objects = p_libvlc_global->pp_objects;
698
699     /* Perform our dichotomy */
700     for( i_max = p_libvlc_global->i_objects - 1 ; ; )
701     {
702         i_middle = i_max / 2;
703
704         if( pp_objects[i_middle]->i_object_id > i_id )
705         {
706             i_max = i_middle;
707         }
708         else if( pp_objects[i_middle]->i_object_id < i_id )
709         {
710             if( i_middle )
711             {
712                 pp_objects += i_middle;
713                 i_max -= i_middle;
714             }
715             else
716             {
717                 /* This happens when there are only two remaining objects */
718                 if( pp_objects[i_middle+1]->i_object_id == i_id
719                     && pp_objects[i_middle+1]->p_internals->i_refcount > 0 )
720                 {
721                     vlc_object_yield_locked( pp_objects[i_middle+1] );
722                     vlc_mutex_unlock( &structure_lock );
723                     return pp_objects[i_middle+1];
724                 }
725                 break;
726             }
727         }
728         else if( pp_objects[i_middle]->p_internals->i_refcount > 0 )
729         {
730             vlc_object_yield_locked( pp_objects[i_middle] );
731             vlc_mutex_unlock( &structure_lock );
732             return pp_objects[i_middle];
733         }
734
735         if( i_max == 0 )
736         {
737             /* this means that i_max == i_middle, and since we have already
738              * tested pp_objects[i_middle]), p_found is properly set. */
739             break;
740         }
741     }
742
743     vlc_mutex_unlock( &structure_lock );
744     return NULL;
745 }
746
747 /**
748  ****************************************************************************
749  * find a typed object and increment its refcount
750  *****************************************************************************
751  * This function recursively looks for a given object type. i_mode can be one
752  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
753  *****************************************************************************/
754 void * __vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode )
755 {
756     vlc_object_t *p_found;
757
758     vlc_mutex_lock( &structure_lock );
759
760     /* If we are of the requested type ourselves, don't look further */
761     if( !(i_mode & FIND_STRICT) && p_this->i_object_type == i_type
762         && p_this->p_internals->i_refcount > 0 )
763     {
764         vlc_object_yield_locked( p_this );
765         vlc_mutex_unlock( &structure_lock );
766         return p_this;
767     }
768
769     /* Otherwise, recursively look for the object */
770     if( (i_mode & 0x000f) == FIND_ANYWHERE )
771     {
772         vlc_object_t *p_root = p_this;
773
774         /* Find the root */
775         while( p_root->p_parent != NULL &&
776                p_root != VLC_OBJECT( p_this->p_libvlc ) )
777         {
778             p_root = p_root->p_parent;
779         }
780
781         p_found = FindObject( p_root, i_type, (i_mode & ~0x000f)|FIND_CHILD );
782         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
783         {
784             p_found = FindObject( VLC_OBJECT( p_this->p_libvlc ),
785                                   i_type, (i_mode & ~0x000f)|FIND_CHILD );
786         }
787     }
788     else
789     {
790         p_found = FindObject( p_this, i_type, i_mode );
791     }
792
793     vlc_mutex_unlock( &structure_lock );
794
795     return p_found;
796 }
797
798 /**
799  ****************************************************************************
800  * find a named object and increment its refcount
801  *****************************************************************************
802  * This function recursively looks for a given object name. i_mode can be one
803  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
804  *****************************************************************************/
805 void * __vlc_object_find_name( vlc_object_t *p_this, const char *psz_name,
806                                int i_mode )
807 {
808     vlc_object_t *p_found;
809
810     vlc_mutex_lock( &structure_lock );
811
812     /* Avoid obvious freed object uses */
813     assert( p_this->p_internals->i_refcount > 0 );
814
815     /* If have the requested name ourselves, don't look further */
816     if( !(i_mode & FIND_STRICT)
817         && p_this->psz_object_name
818         && !strcmp( p_this->psz_object_name, psz_name )
819         && p_this->p_internals->i_refcount > 0 )
820     {
821         vlc_object_yield_locked( p_this );
822         vlc_mutex_unlock( &structure_lock );
823         return p_this;
824     }
825
826     /* Otherwise, recursively look for the object */
827     if( (i_mode & 0x000f) == FIND_ANYWHERE )
828     {
829         vlc_object_t *p_root = p_this;
830
831         /* Find the root */
832         while( p_root->p_parent != NULL &&
833                p_root != VLC_OBJECT( p_this->p_libvlc ) )
834         {
835             p_root = p_root->p_parent;
836         }
837
838         p_found = FindObjectName( p_root, psz_name,
839                                  (i_mode & ~0x000f)|FIND_CHILD );
840         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
841         {
842             p_found = FindObjectName( VLC_OBJECT( p_this->p_libvlc ),
843                                       psz_name, (i_mode & ~0x000f)|FIND_CHILD );
844         }
845     }
846     else
847     {
848         p_found = FindObjectName( p_this, psz_name, i_mode );
849     }
850
851     vlc_mutex_unlock( &structure_lock );
852
853     return p_found;
854 }
855
856 /**
857  ****************************************************************************
858  * increment an object refcount
859  *****************************************************************************/
860
861 /* When the structure_lock is locked */
862 static void vlc_object_yield_locked( vlc_object_t *p_this )
863 {
864     vlc_assert_locked (&structure_lock);
865
866     /* Avoid obvious freed object uses */
867     assert( p_this->p_internals->i_refcount > 0 );
868
869     /* Increment the counter */
870     p_this->p_internals->i_refcount++;
871 }
872
873 /* Public function */
874 void __vlc_object_yield( vlc_object_t *p_this )
875 {
876     vlc_mutex_lock( &structure_lock );
877     vlc_object_yield_locked( p_this );
878     vlc_mutex_unlock( &structure_lock );
879 }
880
881
882 /*****************************************************************************
883  * decrement an object refcount
884  * And destroy the object if its refcount reach zero.
885  *****************************************************************************/
886 void __vlc_object_release( vlc_object_t *p_this )
887 {
888     bool b_should_destroy;
889
890     vlc_mutex_lock( &structure_lock );
891
892     assert( p_this->p_internals->i_refcount > 0 );
893     p_this->p_internals->i_refcount--;
894     b_should_destroy = (p_this->p_internals->i_refcount == 0);
895
896     if( b_should_destroy )
897     {
898         /* Make sure this object can't be obtained via vlc_find_object now that
899          * it is freed */
900         libvlc_global_data_t *p_libvlc_global = vlc_global();
901         int i_index;
902
903         /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
904          * useless to try and recover anything if pp_objects gets smashed. */
905         i_index = FindIndex( p_this, p_libvlc_global->pp_objects,
906                              p_libvlc_global->i_objects );
907         REMOVE_ELEM( p_libvlc_global->pp_objects,
908                      p_libvlc_global->i_objects, i_index );
909     }
910
911     vlc_mutex_unlock( &structure_lock );
912
913     if( b_should_destroy )
914         vlc_object_destroy( p_this );
915 }
916
917 /**
918  ****************************************************************************
919  * attach object to a parent object
920  *****************************************************************************
921  * This function sets p_this as a child of p_parent, and p_parent as a parent
922  * of p_this. This link can be undone using vlc_object_detach.
923  *****************************************************************************/
924 void __vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent )
925 {
926     if( !p_this ) return;
927
928     vlc_mutex_lock( &structure_lock );
929
930     /* Avoid obvious freed object uses */
931     assert( p_this->p_internals->i_refcount > 0 );
932
933     /* Attach the parent to its child */
934     p_this->p_parent = p_parent;
935
936     /* Attach the child to its parent */
937     INSERT_ELEM( p_parent->pp_children, p_parent->i_children,
938                  p_parent->i_children, p_this );
939
940     /* Climb up the tree to see whether we are connected with the root */
941     if( p_parent->p_internals->b_attached )
942     {
943         SetAttachment( p_this, true );
944     }
945
946     vlc_mutex_unlock( &structure_lock );
947 }
948
949 /**
950  ****************************************************************************
951  * detach object from its parent
952  *****************************************************************************
953  * This function removes all links between an object and its parent.
954  *****************************************************************************/
955 void __vlc_object_detach( vlc_object_t *p_this )
956 {
957     if( !p_this ) return;
958
959     vlc_mutex_lock( &structure_lock );
960
961     if( !p_this->p_parent )
962     {
963         msg_Err( p_this, "object is not attached" );
964         vlc_mutex_unlock( &structure_lock );
965         return;
966     }
967
968     /* Climb up the tree to see whether we are connected with the root */
969     if( p_this->p_parent->p_internals->b_attached )
970     {
971         SetAttachment( p_this, false );
972     }
973
974     DetachObject( p_this );
975     vlc_mutex_unlock( &structure_lock );
976     p_this = NULL;
977 }
978
979 /**
980  ****************************************************************************
981  * find a list typed objects and increment their refcount
982  *****************************************************************************
983  * This function recursively looks for a given object type. i_mode can be one
984  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
985  *****************************************************************************/
986 vlc_list_t * __vlc_list_find( vlc_object_t *p_this, int i_type, int i_mode )
987 {
988     vlc_list_t *p_list;
989     vlc_object_t **pp_current, **pp_end;
990     int i_count = 0, i_index = 0;
991     libvlc_global_data_t *p_libvlc_global = vlc_global();
992
993     vlc_mutex_lock( &structure_lock );
994
995     /* Look for the objects */
996     switch( i_mode & 0x000f )
997     {
998     case FIND_ANYWHERE:
999         pp_current = p_libvlc_global->pp_objects;
1000         pp_end = pp_current + p_libvlc_global->i_objects;
1001
1002         for( ; pp_current < pp_end ; pp_current++ )
1003         {
1004             if( (*pp_current)->p_internals->b_attached
1005                  && (*pp_current)->i_object_type == i_type )
1006             {
1007                 i_count++;
1008             }
1009         }
1010
1011         p_list = NewList( i_count );
1012         pp_current = p_libvlc_global->pp_objects;
1013
1014         for( ; pp_current < pp_end ; pp_current++ )
1015         {
1016             if( (*pp_current)->p_internals->b_attached
1017                  && (*pp_current)->i_object_type == i_type )
1018             {
1019                 ListReplace( p_list, *pp_current, i_index );
1020                 if( i_index < i_count ) i_index++;
1021             }
1022         }
1023     break;
1024
1025     case FIND_CHILD:
1026         i_count = CountChildren( p_this, i_type );
1027         p_list = NewList( i_count );
1028
1029         /* Check allocation was successful */
1030         if( p_list->i_count != i_count )
1031         {
1032             msg_Err( p_this, "list allocation failed!" );
1033             p_list->i_count = 0;
1034             break;
1035         }
1036
1037         p_list->i_count = 0;
1038         ListChildren( p_list, p_this, i_type );
1039         break;
1040
1041     default:
1042         msg_Err( p_this, "unimplemented!" );
1043         p_list = NewList( 0 );
1044         break;
1045     }
1046
1047     vlc_mutex_unlock( &structure_lock );
1048
1049     return p_list;
1050 }
1051
1052 /*****************************************************************************
1053  * DumpCommand: print the current vlc structure
1054  *****************************************************************************
1055  * This function prints either an ASCII tree showing the connections between
1056  * vlc objects, and additional information such as their refcount, thread ID,
1057  * etc. (command "tree"), or the same data as a simple list (command "list").
1058  *****************************************************************************/
1059 static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd,
1060                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1061 {
1062     libvlc_global_data_t *p_libvlc_global = vlc_global();
1063
1064     (void)oldval; (void)p_data;
1065     if( *psz_cmd == 'l' )
1066     {
1067         vlc_mutex_lock( &structure_lock );
1068
1069         vlc_object_t **pp_current, **pp_end;
1070
1071         pp_current = p_libvlc_global->pp_objects;
1072         pp_end = pp_current + p_libvlc_global->i_objects;
1073
1074         for( ; pp_current < pp_end ; pp_current++ )
1075         {
1076             if( (*pp_current)->p_internals->b_attached )
1077             {
1078                 PrintObject( *pp_current, "" );
1079             }
1080             else
1081             {
1082                 printf( " o %.8i %s (not attached)\n",
1083                         (*pp_current)->i_object_id,
1084                         (*pp_current)->psz_object_type );
1085             }
1086         }
1087
1088         vlc_mutex_unlock( &structure_lock );
1089     }
1090     else
1091     {
1092         vlc_object_t *p_object = NULL;
1093
1094         if( *newval.psz_string )
1095         {
1096             char *end;
1097             int i_id = strtol( newval.psz_string, &end, 0 );
1098             if( end != newval.psz_string )
1099                 p_object = vlc_object_get( i_id );
1100             else
1101             {
1102                 /* try using the object's name to find it */
1103                 vlc_object_t *p_libvlc = vlc_object_get( 1 );
1104                 if( p_libvlc )
1105                 {
1106                     /* Look in p_libvlc's children tree */
1107                     p_object = vlc_object_find_name( p_libvlc,
1108                                                      newval.psz_string,
1109                                                      FIND_CHILD );
1110                     vlc_object_release( p_libvlc );
1111                 }
1112                 if( !p_object )
1113                 {
1114                     /* If it's not in libvlc, look in libvlc_global (== p_this) */
1115                     p_object = vlc_object_find_name( p_this,
1116                                                      newval.psz_string,
1117                                                      FIND_CHILD );
1118                 }
1119             }
1120
1121             if( !p_object )
1122             {
1123                 return VLC_ENOOBJ;
1124             }
1125         }
1126
1127         vlc_mutex_lock( &structure_lock );
1128
1129         if( *psz_cmd == 't' )
1130         {
1131             char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
1132
1133             if( !p_object )
1134                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
1135
1136             psz_foo[0] = '|';
1137             DumpStructure( p_object, 0, psz_foo );
1138         }
1139         else if( *psz_cmd == 'v' )
1140         {
1141             int i;
1142
1143             if( !p_object )
1144                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
1145
1146             PrintObject( p_object, "" );
1147
1148             if( !p_object->p_internals->i_vars )
1149                 printf( " `-o No variables\n" );
1150             for( i = 0; i < p_object->p_internals->i_vars; i++ )
1151             {
1152                 variable_t *p_var = p_object->p_internals->p_vars + i;
1153
1154                 const char *psz_type = "unknown";
1155                 switch( p_var->i_type & VLC_VAR_TYPE )
1156                 {
1157 #define MYCASE( type, nice )                \
1158                     case VLC_VAR_ ## type:  \
1159                         psz_type = nice;    \
1160                         break;
1161                     MYCASE( VOID, "void" );
1162                     MYCASE( BOOL, "bool" );
1163                     MYCASE( INTEGER, "integer" );
1164                     MYCASE( HOTKEY, "hotkey" );
1165                     MYCASE( STRING, "string" );
1166                     MYCASE( MODULE, "module" );
1167                     MYCASE( FILE, "file" );
1168                     MYCASE( DIRECTORY, "directory" );
1169                     MYCASE( VARIABLE, "variable" );
1170                     MYCASE( FLOAT, "float" );
1171                     MYCASE( TIME, "time" );
1172                     MYCASE( ADDRESS, "address" );
1173                     MYCASE( MUTEX, "mutex" );
1174                     MYCASE( LIST, "list" );
1175 #undef MYCASE
1176                 }
1177                 printf( " %c-o \"%s\" (%s",
1178                         i + 1 == p_object->p_internals->i_vars ? '`' : '|',
1179                         p_var->psz_name, psz_type );
1180                 if( p_var->psz_text )
1181                     printf( ", %s", p_var->psz_text );
1182                 printf( ")" );
1183                 if( p_var->i_type & VLC_VAR_ISCOMMAND )
1184                     printf( ", command" );
1185                 if( p_var->i_entries )
1186                     printf( ", %d callbacks", p_var->i_entries );
1187                 switch( p_var->i_type & 0x00f0 )
1188                 {
1189                     case VLC_VAR_VOID:
1190                     case VLC_VAR_MUTEX:
1191                         break;
1192                     case VLC_VAR_BOOL:
1193                         printf( ": %s", p_var->val.b_bool ? "true" : "false" );
1194                         break;
1195                     case VLC_VAR_INTEGER:
1196                         printf( ": %d", p_var->val.i_int );
1197                         break;
1198                     case VLC_VAR_STRING:
1199                         printf( ": \"%s\"", p_var->val.psz_string );
1200                         break;
1201                     case VLC_VAR_FLOAT:
1202                         printf( ": %f", p_var->val.f_float );
1203                         break;
1204                     case VLC_VAR_TIME:
1205                         printf( ": " I64Fi, (int64_t)p_var->val.i_time );
1206                         break;
1207                     case VLC_VAR_ADDRESS:
1208                         printf( ": %p", p_var->val.p_address );
1209                         break;
1210                     case VLC_VAR_LIST:
1211                         printf( ": TODO" );
1212                         break;
1213                 }
1214                 printf( "\n" );
1215             }
1216         }
1217
1218         vlc_mutex_unlock( &structure_lock );
1219
1220         if( *newval.psz_string )
1221         {
1222             vlc_object_release( p_object );
1223         }
1224     }
1225
1226     return VLC_SUCCESS;
1227 }
1228
1229 /*****************************************************************************
1230  * vlc_list_release: free a list previously allocated by vlc_list_find
1231  *****************************************************************************
1232  * This function decreases the refcount of all objects in the list and
1233  * frees the list.
1234  *****************************************************************************/
1235 void vlc_list_release( vlc_list_t *p_list )
1236 {
1237     int i_index;
1238
1239     for( i_index = 0; i_index < p_list->i_count; i_index++ )
1240     {
1241         vlc_object_release( p_list->p_values[i_index].p_object );
1242     }
1243
1244     free( p_list->p_values );
1245     free( p_list );
1246 }
1247
1248 /*****************************************************************************
1249  * dump an object. (Debug function)
1250  *****************************************************************************/
1251 void __vlc_object_dump( vlc_object_t *p_this )
1252 {
1253     vlc_mutex_lock( &structure_lock );
1254     char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
1255     psz_foo[0] = '|';
1256     DumpStructure( p_this, 0, psz_foo );
1257     vlc_mutex_unlock( &structure_lock );
1258 }
1259
1260 /* Following functions are local */
1261
1262 /*****************************************************************************
1263  * FindIndex: find the index of an object in an array of objects
1264  *****************************************************************************
1265  * This function assumes that p_this can be found in pp_objects. It will not
1266  * crash if p_this cannot be found, but will return a wrong value. It is your
1267  * duty to check the return value if you are not certain that the object could
1268  * be found for sure.
1269  *****************************************************************************/
1270 static int FindIndex( vlc_object_t *p_this,
1271                       vlc_object_t **pp_objects, int i_count )
1272 {
1273     int i_middle = i_count / 2;
1274
1275     if( i_count == 0 )
1276     {
1277         return 0;
1278     }
1279
1280     if( pp_objects[i_middle] == p_this )
1281     {
1282         return i_middle;
1283     }
1284
1285     if( i_count == 1 )
1286     {
1287         return 0;
1288     }
1289
1290     /* We take advantage of the sorted array */
1291     if( pp_objects[i_middle]->i_object_id < p_this->i_object_id )
1292     {
1293         return i_middle + FindIndex( p_this, pp_objects + i_middle,
1294                                              i_count - i_middle );
1295     }
1296     else
1297     {
1298         return FindIndex( p_this, pp_objects, i_middle );
1299     }
1300 }
1301
1302 static vlc_object_t * FindObject( vlc_object_t *p_this, int i_type, int i_mode )
1303 {
1304     int i;
1305     vlc_object_t *p_tmp;
1306
1307     switch( i_mode & 0x000f )
1308     {
1309     case FIND_PARENT:
1310         p_tmp = p_this->p_parent;
1311         if( p_tmp )
1312         {
1313             if( p_tmp->i_object_type == i_type
1314                 && p_tmp->p_internals->i_refcount > 0 )
1315             {
1316                 vlc_object_yield_locked( p_tmp );
1317                 return p_tmp;
1318             }
1319             else
1320             {
1321                 return FindObject( p_tmp, i_type, i_mode );
1322             }
1323         }
1324         break;
1325
1326     case FIND_CHILD:
1327         for( i = p_this->i_children; i--; )
1328         {
1329             p_tmp = p_this->pp_children[i];
1330             if( p_tmp->i_object_type == i_type
1331                 && p_tmp->p_internals->i_refcount > 0 )
1332             {
1333                 vlc_object_yield_locked( p_tmp );
1334                 return p_tmp;
1335             }
1336             else if( p_tmp->i_children )
1337             {
1338                 p_tmp = FindObject( p_tmp, i_type, i_mode );
1339                 if( p_tmp )
1340                 {
1341                     return p_tmp;
1342                 }
1343             }
1344         }
1345         break;
1346
1347     case FIND_ANYWHERE:
1348         /* Handled in vlc_object_find */
1349         break;
1350     }
1351
1352     return NULL;
1353 }
1354
1355 static vlc_object_t * FindObjectName( vlc_object_t *p_this,
1356                                       const char *psz_name,
1357                                       int i_mode )
1358 {
1359     int i;
1360     vlc_object_t *p_tmp;
1361
1362     switch( i_mode & 0x000f )
1363     {
1364     case FIND_PARENT:
1365         p_tmp = p_this->p_parent;
1366         if( p_tmp )
1367         {
1368             if( p_tmp->psz_object_name
1369                 && !strcmp( p_tmp->psz_object_name, psz_name )
1370                 && p_tmp->p_internals->i_refcount > 0 )
1371             {
1372                 vlc_object_yield_locked( p_tmp );
1373                 return p_tmp;
1374             }
1375             else
1376             {
1377                 return FindObjectName( p_tmp, psz_name, i_mode );
1378             }
1379         }
1380         break;
1381
1382     case FIND_CHILD:
1383         for( i = p_this->i_children; i--; )
1384         {
1385             p_tmp = p_this->pp_children[i];
1386             if( p_tmp->psz_object_name
1387                 && !strcmp( p_tmp->psz_object_name, psz_name )
1388                 && p_tmp->p_internals->i_refcount > 0 )
1389             {
1390                 vlc_object_yield_locked( p_tmp );
1391                 return p_tmp;
1392             }
1393             else if( p_tmp->i_children )
1394             {
1395                 p_tmp = FindObjectName( p_tmp, psz_name, i_mode );
1396                 if( p_tmp )
1397                 {
1398                     return p_tmp;
1399                 }
1400             }
1401         }
1402         break;
1403
1404     case FIND_ANYWHERE:
1405         /* Handled in vlc_object_find */
1406         break;
1407     }
1408
1409     return NULL;
1410 }
1411
1412 static void DetachObject( vlc_object_t *p_this )
1413 {
1414     vlc_object_t *p_parent = p_this->p_parent;
1415     int i_index, i;
1416
1417     /* Remove p_this's parent */
1418     p_this->p_parent = NULL;
1419
1420     /* Remove all of p_parent's children which are p_this */
1421     for( i_index = p_parent->i_children ; i_index-- ; )
1422     {
1423         if( p_parent->pp_children[i_index] == p_this )
1424         {
1425             p_parent->i_children--;
1426             for( i = i_index ; i < p_parent->i_children ; i++ )
1427             {
1428                 p_parent->pp_children[i] = p_parent->pp_children[i+1];
1429             }
1430         }
1431     }
1432
1433     if( p_parent->i_children )
1434     {
1435         p_parent->pp_children = (vlc_object_t **)realloc( p_parent->pp_children,
1436                                p_parent->i_children * sizeof(vlc_object_t *) );
1437     }
1438     else
1439     {
1440         free( p_parent->pp_children );
1441         p_parent->pp_children = NULL;
1442     }
1443 }
1444
1445 /*****************************************************************************
1446  * SetAttachment: recursively set the b_attached flag of a subtree.
1447  *****************************************************************************
1448  * This function is used by the attach and detach functions to propagate
1449  * the b_attached flag in a subtree.
1450  *****************************************************************************/
1451 static void SetAttachment( vlc_object_t *p_this, bool b_attached )
1452 {
1453     int i_index;
1454
1455     for( i_index = p_this->i_children ; i_index-- ; )
1456     {
1457         SetAttachment( p_this->pp_children[i_index], b_attached );
1458     }
1459
1460     p_this->p_internals->b_attached = b_attached;
1461 }
1462
1463 static void PrintObject( vlc_object_t *p_this, const char *psz_prefix )
1464 {
1465     char psz_children[20], psz_refcount[20], psz_thread[30], psz_name[50],
1466          psz_parent[20];
1467
1468     psz_name[0] = '\0';
1469     if( p_this->psz_object_name )
1470     {
1471         snprintf( psz_name, 49, " \"%s\"", p_this->psz_object_name );
1472         if( psz_name[48] )
1473             psz_name[48] = '\"';
1474     }
1475
1476     psz_children[0] = '\0';
1477     switch( p_this->i_children )
1478     {
1479         case 0:
1480             break;
1481         case 1:
1482             strcpy( psz_children, ", 1 child" );
1483             break;
1484         default:
1485             snprintf( psz_children, 19, ", %i children", p_this->i_children );
1486             break;
1487     }
1488
1489     psz_refcount[0] = '\0';
1490     if( p_this->p_internals->i_refcount > 0 )
1491         snprintf( psz_refcount, 19, ", refcount %u",
1492                   p_this->p_internals->i_refcount );
1493
1494     psz_thread[0] = '\0';
1495     if( p_this->p_internals->b_thread )
1496         snprintf( psz_thread, 29, " (thread %u)",
1497 #if defined(WIN32) || defined(UNDER_CE)
1498                   (unsigned)p_this->p_internals->thread_id.id );
1499 #else
1500                   (unsigned)p_this->p_internals->thread_id );
1501 #endif
1502
1503     psz_parent[0] = '\0';
1504     if( p_this->p_parent )
1505         snprintf( psz_parent, 19, ", parent %i", p_this->p_parent->i_object_id );
1506
1507     printf( " %so %.8i %s%s%s%s%s%s\n", psz_prefix,
1508             p_this->i_object_id, p_this->psz_object_type,
1509             psz_name, psz_thread, psz_refcount, psz_children,
1510             psz_parent );
1511 }
1512
1513 static void DumpStructure( vlc_object_t *p_this, int i_level, char *psz_foo )
1514 {
1515     int i;
1516     char i_back = psz_foo[i_level];
1517     psz_foo[i_level] = '\0';
1518
1519     PrintObject( p_this, psz_foo );
1520
1521     psz_foo[i_level] = i_back;
1522
1523     if( i_level / 2 >= MAX_DUMPSTRUCTURE_DEPTH )
1524     {
1525         msg_Warn( p_this, "structure tree is too deep" );
1526         return;
1527     }
1528
1529     for( i = 0 ; i < p_this->i_children ; i++ )
1530     {
1531         if( i_level )
1532         {
1533             psz_foo[i_level-1] = ' ';
1534
1535             if( psz_foo[i_level-2] == '`' )
1536             {
1537                 psz_foo[i_level-2] = ' ';
1538             }
1539         }
1540
1541         if( i == p_this->i_children - 1 )
1542         {
1543             psz_foo[i_level] = '`';
1544         }
1545         else
1546         {
1547             psz_foo[i_level] = '|';
1548         }
1549
1550         psz_foo[i_level+1] = '-';
1551         psz_foo[i_level+2] = '\0';
1552
1553         DumpStructure( p_this->pp_children[i], i_level + 2, psz_foo );
1554     }
1555 }
1556
1557 static vlc_list_t * NewList( int i_count )
1558 {
1559     vlc_list_t * p_list = (vlc_list_t *)malloc( sizeof( vlc_list_t ) );
1560     if( p_list == NULL )
1561     {
1562         return NULL;
1563     }
1564
1565     p_list->i_count = i_count;
1566
1567     if( i_count == 0 )
1568     {
1569         p_list->p_values = NULL;
1570         return p_list;
1571     }
1572
1573     p_list->p_values = malloc( i_count * sizeof( vlc_value_t ) );
1574     if( p_list->p_values == NULL )
1575     {
1576         p_list->i_count = 0;
1577         return p_list;
1578     }
1579
1580     return p_list;
1581 }
1582
1583 static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object,
1584                          int i_index )
1585 {
1586     if( p_list == NULL || i_index >= p_list->i_count )
1587     {
1588         return;
1589     }
1590
1591     vlc_object_yield_locked( p_object );
1592
1593     p_list->p_values[i_index].p_object = p_object;
1594
1595     return;
1596 }
1597
1598 /*static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object )
1599 {
1600     if( p_list == NULL )
1601     {
1602         return;
1603     }
1604
1605     p_list->p_values = realloc( p_list->p_values, (p_list->i_count + 1)
1606                                 * sizeof( vlc_value_t ) );
1607     if( p_list->p_values == NULL )
1608     {
1609         p_list->i_count = 0;
1610         return;
1611     }
1612
1613     vlc_object_yield_locked( p_object );
1614
1615     p_list->p_values[p_list->i_count].p_object = p_object;
1616     p_list->i_count++;
1617
1618     return;
1619 }*/
1620
1621 static int CountChildren( vlc_object_t *p_this, int i_type )
1622 {
1623     vlc_object_t *p_tmp;
1624     int i, i_count = 0;
1625
1626     for( i = 0; i < p_this->i_children; i++ )
1627     {
1628         p_tmp = p_this->pp_children[i];
1629
1630         if( p_tmp->i_object_type == i_type )
1631         {
1632             i_count++;
1633         }
1634
1635         if( p_tmp->i_children )
1636         {
1637             i_count += CountChildren( p_tmp, i_type );
1638         }
1639     }
1640
1641     return i_count;
1642 }
1643
1644 static void ListChildren( vlc_list_t *p_list, vlc_object_t *p_this, int i_type )
1645 {
1646     vlc_object_t *p_tmp;
1647     int i;
1648
1649     for( i = 0; i < p_this->i_children; i++ )
1650     {
1651         p_tmp = p_this->pp_children[i];
1652
1653         if( p_tmp->i_object_type == i_type )
1654         {
1655             ListReplace( p_list, p_tmp, p_list->i_count++ );
1656         }
1657
1658         if( p_tmp->i_children )
1659         {
1660             ListChildren( p_list, p_tmp, i_type );
1661         }
1662     }
1663 }