]> git.sesse.net Git - vlc/blob - src/misc/objects.c
2b6ed7debc28b74b3e54aeba8e39c04bbb97d804
[vlc] / src / misc / objects.c
1 /*****************************************************************************
2  * objects.c: vlc_object_t handling
3  *****************************************************************************
4  * Copyright (C) 2004-2008 the VideoLAN team
5  *
6  * Authors: Samuel Hocevar <sam@zoy.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /**
24  * \file
25  * This file contains the functions to handle the vlc_object_t type
26  *
27  * Unless otherwise stated, functions in this file are not cancellation point.
28  * All functions in this file are safe w.r.t. deferred cancellation.
29  */
30
31
32 /*****************************************************************************
33  * Preamble
34  *****************************************************************************/
35 #ifdef HAVE_CONFIG_H
36 # include "config.h"
37 #endif
38
39 #include <vlc_common.h>
40
41 #include "../libvlc.h"
42 #include <vlc_aout.h>
43 #include "audio_output/aout_internal.h"
44
45 #include "vlc_interface.h"
46 #include "vlc_codec.h"
47
48 #include "variables.h"
49 #ifndef WIN32
50 # include <unistd.h>
51 #else
52 # include <io.h>
53 # include <winsock2.h>
54 # include <ws2tcpip.h>
55 # undef  read
56 # define read( a, b, c )  recv (a, b, c, 0)
57 # undef  write
58 # define write( a, b, c ) send (a, b, c, 0)
59 # undef  close
60 # define close( a )       closesocket (a)
61 #endif
62
63 #include <search.h>
64 #include <limits.h>
65 #include <assert.h>
66
67 #if defined (HAVE_SYS_EVENTFD_H)
68 # include <sys/eventfd.h>
69 #endif
70
71
72 /*****************************************************************************
73  * Local prototypes
74  *****************************************************************************/
75 static int  DumpCommand( vlc_object_t *, char const *,
76                          vlc_value_t, vlc_value_t, void * );
77
78 static vlc_object_t * FindParent    ( vlc_object_t *, int );
79 static vlc_object_t * FindChild     ( vlc_object_t *, int );
80 static vlc_object_t * FindParentName( vlc_object_t *, const char * );
81 static vlc_object_t * FindChildName ( vlc_object_t *, const char * );
82 static void           PrintObject   ( vlc_object_t *, const char * );
83 static void           DumpStructure ( vlc_object_t *, int, char * );
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_detach_unlocked (vlc_object_t *p_this);
93
94 /*****************************************************************************
95  * Local structure lock
96  *****************************************************************************/
97 static void libvlc_lock (libvlc_int_t *p_libvlc)
98 {
99     vlc_mutex_lock (&(libvlc_priv (p_libvlc)->structure_lock));
100 }
101
102 static void libvlc_unlock (libvlc_int_t *p_libvlc)
103 {
104     vlc_mutex_unlock (&(libvlc_priv (p_libvlc)->structure_lock));
105 }
106
107 void *__vlc_custom_create( vlc_object_t *p_this, size_t i_size,
108                            int i_type, const char *psz_type )
109 {
110     vlc_object_t *p_new;
111     vlc_object_internals_t *p_priv;
112
113     /* NOTE:
114      * VLC objects are laid out as follow:
115      * - first the LibVLC-private per-object data,
116      * - then VLC_COMMON members from vlc_object_t,
117      * - finally, the type-specific data (if any).
118      *
119      * This function initializes the LibVLC and common data,
120      * and zeroes the rest.
121      */
122     p_priv = calloc( 1, sizeof( *p_priv ) + i_size );
123     if( p_priv == NULL )
124         return NULL;
125
126     assert (i_size >= sizeof (vlc_object_t));
127     p_new = (vlc_object_t *)(p_priv + 1);
128
129     p_priv->i_object_type = i_type;
130     p_new->psz_object_type = psz_type;
131     p_priv->psz_name = NULL;
132
133     p_new->b_die = false;
134     p_new->b_error = false;
135     p_new->b_force = false;
136
137     p_new->psz_header = NULL;
138
139     if (p_this)
140         p_new->i_flags = p_this->i_flags
141             & (OBJECT_FLAGS_NODBG|OBJECT_FLAGS_QUIET|OBJECT_FLAGS_NOINTERACT);
142
143     p_priv->var_root = NULL;
144
145     if( p_this == NULL )
146     {
147         libvlc_int_t *self = (libvlc_int_t*)p_new;
148         p_new->p_libvlc = self;
149         vlc_mutex_init (&(libvlc_priv (self)->structure_lock));
150         p_this = p_new;
151     }
152     else
153         p_new->p_libvlc = p_this->p_libvlc;
154
155     vlc_spin_init( &p_priv->ref_spin );
156     p_priv->i_refcount = 1;
157     p_priv->pf_destructor = NULL;
158     p_priv->b_thread = false;
159     p_new->p_parent = NULL;
160     p_priv->pp_children = NULL;
161     p_priv->i_children = 0;
162
163     /* Initialize mutexes and condvars */
164     vlc_mutex_init( &p_priv->var_lock );
165     vlc_cond_init( &p_priv->var_wait );
166     p_priv->pipes[0] = p_priv->pipes[1] = -1;
167
168     if (p_new == VLC_OBJECT(p_new->p_libvlc))
169     {   /* TODO: should be in src/libvlc.c */
170         int canc = vlc_savecancel ();
171         var_Create( p_new, "tree", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
172         var_AddCallback( p_new, "tree", DumpCommand, NULL );
173         var_Create( p_new, "vars", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
174         var_AddCallback( p_new, "vars", DumpCommand, NULL );
175         vlc_restorecancel (canc);
176     }
177
178     return p_new;
179 }
180
181
182 /**
183  * Allocates and initializes a vlc object.
184  *
185  * @param i_type known object type (all of them are negative integer values),
186  *               or object byte size (always positive).
187  *
188  * @return the new object, or NULL on error.
189  */
190 void * __vlc_object_create( vlc_object_t *p_this, int i_type )
191 {
192     const char   * psz_type;
193     size_t         i_size;
194
195     switch( i_type )
196     {
197         case VLC_OBJECT_DECODER:
198             i_size = sizeof(decoder_t);
199             psz_type = "decoder";
200             break;
201         case VLC_OBJECT_AOUT:
202             i_size = sizeof(aout_instance_t);
203             psz_type = "audio output";
204             break;
205         default:
206             assert( i_type > 0 ); /* unknown type?! */
207             i_size = i_type;
208             i_type = VLC_OBJECT_GENERIC;
209             psz_type = "generic";
210             break;
211     }
212
213     return vlc_custom_create( p_this, i_size, i_type, psz_type );
214 }
215
216
217 /**
218  ****************************************************************************
219  * Set the destructor of a vlc object
220  *
221  * This function sets the destructor of the vlc object. It will be called
222  * when the object is destroyed when the its refcount reaches 0.
223  * (It is called by the internal function vlc_object_destroy())
224  *****************************************************************************/
225 void __vlc_object_set_destructor( vlc_object_t *p_this,
226                                   vlc_destructor_t pf_destructor )
227 {
228     vlc_object_internals_t *p_priv = vlc_internals(p_this );
229
230     vlc_spin_lock( &p_priv->ref_spin );
231     p_priv->pf_destructor = pf_destructor;
232     vlc_spin_unlock( &p_priv->ref_spin );
233 }
234
235 static vlc_mutex_t name_lock = VLC_STATIC_MUTEX;
236
237 #undef vlc_object_set_name
238 int vlc_object_set_name(vlc_object_t *obj, const char *name)
239 {
240     vlc_object_internals_t *priv = vlc_internals(obj);
241     char *newname = name ? strdup (name) : NULL;
242     char *oldname;
243
244     vlc_mutex_lock (&name_lock);
245     oldname = priv->psz_name;
246     priv->psz_name = newname;
247     vlc_mutex_unlock (&name_lock);
248
249     free (oldname);
250     return (priv->psz_name || !name) ? VLC_SUCCESS : VLC_ENOMEM;
251 }
252
253 #undef vlc_object_get_name
254 char *vlc_object_get_name(const vlc_object_t *obj)
255 {
256     vlc_object_internals_t *priv = vlc_internals(obj);
257     char *name;
258
259     vlc_mutex_lock (&name_lock);
260     name = priv->psz_name ? strdup (priv->psz_name) : NULL;
261     vlc_mutex_unlock (&name_lock);
262
263     return name;
264 }
265
266 /**
267  ****************************************************************************
268  * Destroy a vlc object (Internal)
269  *
270  * This function destroys an object that has been previously allocated with
271  * vlc_object_create. The object's refcount must be zero and it must not be
272  * attached to other objects in any way.
273  *
274  * This function must be called with cancellation disabled (currently).
275  *****************************************************************************/
276 static void vlc_object_destroy( vlc_object_t *p_this )
277 {
278     vlc_object_internals_t *p_priv = vlc_internals( p_this );
279
280     /* Objects are always detached beforehand */
281     assert( !p_this->p_parent );
282
283     /* Send a kill to the object's thread if applicable */
284     vlc_object_kill( p_this );
285
286     /* Call the custom "subclass" destructor */
287     if( p_priv->pf_destructor )
288         p_priv->pf_destructor( p_this );
289
290     /* Any thread must have been cleaned up at this point. */
291     assert( !p_priv->b_thread );
292
293     /* Destroy the associated variables. */
294     var_DestroyAll( p_this );
295
296     vlc_cond_destroy( &p_priv->var_wait );
297     vlc_mutex_destroy( &p_priv->var_lock );
298
299     free( p_this->psz_header );
300
301     free( p_priv->psz_name );
302
303     vlc_spin_destroy( &p_priv->ref_spin );
304     if( p_priv->pipes[1] != -1 && p_priv->pipes[1] != p_priv->pipes[0] )
305         close( p_priv->pipes[1] );
306     if( p_priv->pipes[0] != -1 )
307         close( p_priv->pipes[0] );
308     if( VLC_OBJECT(p_this->p_libvlc) == p_this )
309         vlc_mutex_destroy (&(libvlc_priv ((libvlc_int_t *)p_this)->structure_lock));
310
311     free( p_priv );
312 }
313
314
315 #ifdef WIN32
316 /**
317  * select()-able pipes emulated using Winsock
318  */
319 static int pipe (int fd[2])
320 {
321     SOCKADDR_IN addr;
322     int addrlen = sizeof (addr);
323
324     SOCKET l = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP), a,
325            c = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
326     if ((l == INVALID_SOCKET) || (c == INVALID_SOCKET))
327         goto error;
328
329     memset (&addr, 0, sizeof (addr));
330     addr.sin_family = AF_INET;
331     addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
332     if (bind (l, (PSOCKADDR)&addr, sizeof (addr))
333      || getsockname (l, (PSOCKADDR)&addr, &addrlen)
334      || listen (l, 1)
335      || connect (c, (PSOCKADDR)&addr, addrlen))
336         goto error;
337
338     a = accept (l, NULL, NULL);
339     if (a == INVALID_SOCKET)
340         goto error;
341
342     closesocket (l);
343     //shutdown (a, 0);
344     //shutdown (c, 1);
345     fd[0] = c;
346     fd[1] = a;
347     return 0;
348
349 error:
350     if (l != INVALID_SOCKET)
351         closesocket (l);
352     if (c != INVALID_SOCKET)
353         closesocket (c);
354     return -1;
355 }
356 #endif /* WIN32 */
357
358 static vlc_mutex_t pipe_lock = VLC_STATIC_MUTEX;
359
360 /**
361  * Returns the readable end of a pipe that becomes readable once termination
362  * of the object is requested (vlc_object_kill()).
363  * This can be used to wake-up out of a select() or poll() event loop, such
364  * typically when doing network I/O.
365  *
366  * Note that the pipe will remain the same for the lifetime of the object.
367  * DO NOT read the pipe nor close it yourself. Ever.
368  *
369  * @param obj object that would be "killed"
370  * @return a readable pipe descriptor, or -1 on error.
371  */
372 int vlc_object_waitpipe( vlc_object_t *obj )
373 {
374     vlc_object_internals_t *internals = vlc_internals( obj );
375
376     vlc_mutex_lock (&pipe_lock);
377     if (internals->pipes[0] == -1)
378     {
379         /* This can only ever happen if someone killed us without locking: */
380         assert (internals->pipes[1] == -1);
381
382 #if defined (HAVE_SYS_EVENTFD_H)
383         internals->pipes[0] = internals->pipes[1] = eventfd (0, 0);
384         if (internals->pipes[0] == -1)
385 #endif
386         {
387             if (pipe (internals->pipes))
388                 internals->pipes[0] = internals->pipes[1] = -1;
389         }
390
391         if (internals->pipes[0] != -1 && obj->b_die)
392         {   /* Race condition: vlc_object_kill() already invoked! */
393             msg_Dbg (obj, "waitpipe: object already dying");
394             write (internals->pipes[1], &(uint64_t){ 1 }, sizeof (uint64_t));
395         }
396     }
397     vlc_mutex_unlock (&pipe_lock);
398     return internals->pipes[0];
399 }
400
401
402 /**
403  * Requests termination of an object, cancels the object thread, and make the
404  * object wait pipe (if it exists) readable. Not a cancellation point.
405  */
406 void __vlc_object_kill( vlc_object_t *p_this )
407 {
408     vlc_object_internals_t *priv = vlc_internals( p_this );
409     int fd = -1;
410
411     vlc_thread_cancel( p_this );
412     vlc_mutex_lock( &pipe_lock );
413     if( !p_this->b_die )
414     {
415         fd = priv->pipes[1];
416         p_this->b_die = true;
417     }
418
419     /* This also serves as a memory barrier toward vlc_object_alive(): */
420     vlc_mutex_unlock( &pipe_lock );
421
422     if (fd != -1)
423     {
424         int canc = vlc_savecancel ();
425
426         /* write _after_ setting b_die, so vlc_object_alive() returns false */
427         write (fd, &(uint64_t){ 1 }, sizeof (uint64_t));
428         msg_Dbg (p_this, "waitpipe: object killed");
429         vlc_restorecancel (canc);
430     }
431 }
432
433
434 /*****************************************************************************
435  * find a typed object and increment its refcount
436  *****************************************************************************
437  * This function recursively looks for a given object type. i_mode can be one
438  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
439  *****************************************************************************/
440 void * __vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode )
441 {
442     vlc_object_t *p_found;
443
444     /* If we are of the requested type ourselves, don't look further */
445     if( vlc_internals (p_this)->i_object_type == i_type )
446     {
447         vlc_object_hold( p_this );
448         return p_this;
449     }
450
451     /* Otherwise, recursively look for the object */
452     if (i_mode == FIND_ANYWHERE)
453         return vlc_object_find (p_this->p_libvlc, i_type, FIND_CHILD);
454
455     switch (i_type)
456     {
457         case VLC_OBJECT_VOUT:
458         case VLC_OBJECT_AOUT:
459             break;
460         case VLC_OBJECT_INPUT:
461             /* input can only be accessed like this from children,
462              * otherwise we could not promise that it is initialized */
463             if (i_mode != FIND_PARENT)
464                 return NULL;
465             break;
466         default:
467             return NULL;
468     }
469
470     libvlc_lock (p_this->p_libvlc);
471     switch (i_mode)
472     {
473         case FIND_PARENT:
474             p_found = FindParent (p_this, i_type);
475             break;
476         case FIND_CHILD:
477             p_found = FindChild (p_this, i_type);
478             break;
479         default:
480             assert (0);
481     }
482     libvlc_unlock (p_this->p_libvlc);
483     return p_found;
484 }
485
486
487 static int objnamecmp(const vlc_object_t *obj, const char *name)
488 {
489     char *objname = vlc_object_get_name(obj);
490     if (objname == NULL)
491         return INT_MIN;
492
493     int ret = strcmp (objname, name);
494     free (objname);
495     return ret;
496 }
497
498 #undef vlc_object_find_name
499 /**
500  * Finds a named object and increment its reference count.
501  * Beware that objects found in this manner can be "owned" by another thread,
502  * be of _any_ type, and be attached to any module (if any). With such an
503  * object reference, you can set or get object variables, emit log messages,
504  * and read write-once object parameters (psz_object_type, etc).
505  * You CANNOT cast the object to a more specific object type, and you
506  * definitely cannot invoke object type-specific callbacks with this.
507  *
508  * @param p_this object to search from
509  * @param psz_name name of the object to search for
510  * @param i_mode search direction: FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
511  *
512  * @return a matching object (must be released by the caller),
513  * or NULL on error.
514  */
515 vlc_object_t *vlc_object_find_name( vlc_object_t *p_this,
516                                     const char *psz_name, int i_mode )
517 {
518     vlc_object_t *p_found;
519
520     /* Reading psz_object_name from a separate inhibits thread-safety.
521      * Use a libvlc address variable instead for that sort of things! */
522     msg_Warn( p_this, "%s(%s) is not safe!", __func__, psz_name );
523     /* If have the requested name ourselves, don't look further */
524     if( !objnamecmp(p_this, psz_name) )
525     {
526         vlc_object_hold( p_this );
527         return p_this;
528     }
529
530     /* Otherwise, recursively look for the object */
531     if (i_mode == FIND_ANYWHERE)
532         return vlc_object_find_name (VLC_OBJECT(p_this->p_libvlc), psz_name,
533                                      FIND_CHILD);
534
535     libvlc_lock (p_this->p_libvlc);
536     switch (i_mode)
537     {
538         case FIND_PARENT:
539             p_found = FindParentName (p_this, psz_name);
540             break;
541         case FIND_CHILD:
542             p_found = FindChildName (p_this, psz_name);
543             break;
544         default:
545             assert (0);
546     }
547     libvlc_unlock (p_this->p_libvlc);
548     return p_found;
549 }
550
551 /**
552  * Increment an object reference counter.
553  */
554 void * __vlc_object_hold( vlc_object_t *p_this )
555 {
556     vlc_object_internals_t *internals = vlc_internals( p_this );
557
558     vlc_spin_lock( &internals->ref_spin );
559     /* Avoid obvious freed object uses */
560     assert( internals->i_refcount > 0 );
561     /* Increment the counter */
562     internals->i_refcount++;
563     vlc_spin_unlock( &internals->ref_spin );
564     return p_this;
565 }
566
567 /*****************************************************************************
568  * Decrement an object refcount
569  * And destroy the object if its refcount reach zero.
570  *****************************************************************************/
571 void __vlc_object_release( vlc_object_t *p_this )
572 {
573     vlc_object_internals_t *internals = vlc_internals( p_this );
574     vlc_object_t *parent = NULL;
575     bool b_should_destroy;
576
577     vlc_spin_lock( &internals->ref_spin );
578     assert( internals->i_refcount > 0 );
579
580     if( internals->i_refcount > 1 )
581     {
582         /* Fast path */
583         /* There are still other references to the object */
584         internals->i_refcount--;
585         vlc_spin_unlock( &internals->ref_spin );
586         return;
587     }
588     vlc_spin_unlock( &internals->ref_spin );
589
590     /* Slow path */
591     /* Remember that we cannot hold the spin while waiting on the mutex */
592     libvlc_lock (p_this->p_libvlc);
593     /* Take the spin again. Note that another thread may have held the
594      * object in the (very short) mean time. */
595     vlc_spin_lock( &internals->ref_spin );
596     b_should_destroy = --internals->i_refcount == 0;
597     vlc_spin_unlock( &internals->ref_spin );
598
599     if( b_should_destroy )
600     {
601         parent = p_this->p_parent;
602         if (parent)
603             /* Detach from parent to protect against FIND_CHILDREN */
604             vlc_object_detach_unlocked (p_this);
605
606         /* We have no children */
607         assert (internals->i_children == 0);
608     }
609     libvlc_unlock (p_this->p_libvlc);
610
611     if( b_should_destroy )
612     {
613         int canc;
614
615         canc = vlc_savecancel ();
616         vlc_object_destroy( p_this );
617         vlc_restorecancel (canc);
618         if (parent)
619             vlc_object_release (parent);
620     }
621 }
622
623 /**
624  ****************************************************************************
625  * attach object to a parent object
626  *****************************************************************************
627  * This function sets p_this as a child of p_parent, and p_parent as a parent
628  * of p_this. This link can be undone using vlc_object_detach.
629  *****************************************************************************/
630 void __vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent )
631 {
632     if( !p_this ) return;
633
634     vlc_object_hold (p_parent);
635     libvlc_lock (p_this->p_libvlc);
636
637     /* Attach the parent to its child */
638     assert (!p_this->p_parent);
639     p_this->p_parent = p_parent;
640
641     /* Attach the child to its parent */
642     vlc_object_internals_t *priv = vlc_internals( p_parent );
643     INSERT_ELEM( priv->pp_children, priv->i_children, priv->i_children,
644                  p_this );
645     libvlc_unlock (p_this->p_libvlc);
646 }
647
648
649 static void vlc_object_detach_unlocked (vlc_object_t *p_this)
650 {
651     if (p_this->p_parent == NULL)
652         return;
653
654     vlc_object_internals_t *priv = vlc_internals( p_this->p_parent );
655
656     int i_index, i;
657
658     /* Remove p_this's parent */
659     p_this->p_parent = NULL;
660
661     /* Remove all of p_parent's children which are p_this */
662     for( i_index = priv->i_children ; i_index-- ; )
663     {
664         if( priv->pp_children[i_index] == p_this )
665         {
666             priv->i_children--;
667             for( i = i_index ; i < priv->i_children ; i++ )
668                 priv->pp_children[i] = priv->pp_children[i+1];
669         }
670     }
671
672     if( priv->i_children )
673     {
674         vlc_object_t **pp_children = (vlc_object_t **)
675             realloc( priv->pp_children,
676                      priv->i_children * sizeof(vlc_object_t *) );
677         if( pp_children )
678             priv->pp_children = pp_children;
679     }
680     else
681     {
682         /* Special case - don't realloc() to zero to avoid leaking */
683         free( priv->pp_children );
684         priv->pp_children = NULL;
685     }
686 }
687
688
689 /**
690  ****************************************************************************
691  * detach object from its parent
692  *****************************************************************************
693  * This function removes all links between an object and its parent.
694  *****************************************************************************/
695 void __vlc_object_detach( vlc_object_t *p_this )
696 {
697     vlc_object_t *p_parent;
698     if( !p_this ) return;
699
700     libvlc_lock (p_this->p_libvlc);
701     p_parent = p_this->p_parent;
702     if (p_parent)
703         vlc_object_detach_unlocked( p_this );
704     libvlc_unlock (p_this->p_libvlc);
705
706     if (p_parent)
707         vlc_object_release (p_parent);
708 }
709
710
711 /**
712  ****************************************************************************
713  * find a list typed objects and increment their refcount
714  *****************************************************************************
715  * This function recursively looks for a given object type. i_mode can be one
716  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
717  *****************************************************************************/
718 vlc_list_t * vlc_list_find( vlc_object_t *p_this, int i_type, int i_mode )
719 {
720     vlc_list_t *p_list;
721     int i_count = 0;
722
723     /* Look for the objects */
724     switch( i_mode )
725     {
726     case FIND_ANYWHERE:
727         return vlc_list_find (VLC_OBJECT(p_this->p_libvlc), i_type, FIND_CHILD);
728
729     case FIND_CHILD:
730         libvlc_lock (p_this->p_libvlc);
731         i_count = CountChildren( p_this, i_type );
732         p_list = NewList( i_count );
733
734         /* Check allocation was successful */
735         if( p_list->i_count != i_count )
736         {
737             libvlc_unlock (p_this->p_libvlc);
738             p_list->i_count = 0;
739             break;
740         }
741
742         p_list->i_count = 0;
743         ListChildren( p_list, p_this, i_type );
744         libvlc_unlock (p_this->p_libvlc);
745         break;
746
747     default:
748         msg_Err( p_this, "unimplemented!" );
749         p_list = NewList( 0 );
750         break;
751     }
752
753     return p_list;
754 }
755
756 /**
757  * Gets the list of children of an objects, and increment their reference
758  * count.
759  * @return a list (possibly empty) or NULL in case of error.
760  */
761 vlc_list_t *__vlc_list_children( vlc_object_t *obj )
762 {
763     vlc_list_t *l;
764     vlc_object_internals_t *priv = vlc_internals( obj );
765
766     libvlc_lock (obj->p_libvlc);
767     l = NewList( priv->i_children );
768     for (int i = 0; i < l->i_count; i++)
769     {
770         vlc_object_hold( priv->pp_children[i] );
771         l->p_values[i].p_object = priv->pp_children[i];
772     }
773     libvlc_unlock (obj->p_libvlc);
774     return l;
775 }
776
777 static void DumpVariable (const void *data, const VISIT which, const int depth)
778 {
779     if (which != postorder && which != leaf)
780         return;
781     (void) depth;
782
783     const variable_t *p_var = *(const variable_t **)data;
784     const char *psz_type = "unknown";
785
786     switch( p_var->i_type & VLC_VAR_TYPE )
787     {
788 #define MYCASE( type, nice )    \
789         case VLC_VAR_ ## type:  \
790             psz_type = nice;    \
791             break;
792         MYCASE( VOID, "void" );
793         MYCASE( BOOL, "bool" );
794         MYCASE( INTEGER, "integer" );
795         MYCASE( HOTKEY, "hotkey" );
796         MYCASE( STRING, "string" );
797         MYCASE( MODULE, "module" );
798         MYCASE( FILE, "file" );
799         MYCASE( DIRECTORY, "directory" );
800         MYCASE( VARIABLE, "variable" );
801         MYCASE( FLOAT, "float" );
802         MYCASE( TIME, "time" );
803         MYCASE( ADDRESS, "address" );
804         MYCASE( MUTEX, "mutex" );
805         MYCASE( LIST, "list" );
806 #undef MYCASE
807     }
808     printf( " *-o \"%s\" (%s", p_var->psz_name, psz_type );
809     if( p_var->psz_text )
810         printf( ", %s", p_var->psz_text );
811     fputc( ')', stdout );
812     if( p_var->i_type & VLC_VAR_HASCHOICE )
813         fputs( ", has choices", stdout );
814     if( p_var->i_type & VLC_VAR_ISCOMMAND )
815         fputs( ", command", stdout );
816     if( p_var->i_entries )
817         printf( ", %d callbacks", p_var->i_entries );
818     switch( p_var->i_type & VLC_VAR_CLASS )
819     {
820         case VLC_VAR_VOID:
821         case VLC_VAR_MUTEX:
822             break;
823         case VLC_VAR_BOOL:
824             printf( ": %s", p_var->val.b_bool ? "true" : "false" );
825             break;
826         case VLC_VAR_INTEGER:
827             printf( ": %d", p_var->val.i_int );
828             break;
829         case VLC_VAR_STRING:
830             printf( ": \"%s\"", p_var->val.psz_string );
831             break;
832         case VLC_VAR_FLOAT:
833             printf( ": %f", p_var->val.f_float );
834             break;
835         case VLC_VAR_TIME:
836             printf( ": %"PRIi64, (int64_t)p_var->val.i_time );
837             break;
838         case VLC_VAR_ADDRESS:
839             printf( ": %p", p_var->val.p_address );
840             break;
841         case VLC_VAR_LIST:
842             fputs( ": TODO", stdout );
843             break;
844     }
845     fputc( '\n', stdout );
846 }
847
848 /*****************************************************************************
849  * DumpCommand: print the current vlc structure
850  *****************************************************************************
851  * This function prints either an ASCII tree showing the connections between
852  * vlc objects, and additional information such as their refcount, thread ID,
853  * etc. (command "tree"), or the same data as a simple list (command "list").
854  *****************************************************************************/
855 static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd,
856                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
857 {
858     (void)oldval; (void)p_data;
859     vlc_object_t *p_object = NULL;
860
861     if( *newval.psz_string )
862     {
863         /* try using the object's name to find it */
864         p_object = vlc_object_find_name( p_this, newval.psz_string,
865                                          FIND_ANYWHERE );
866         if( !p_object )
867         {
868             return VLC_ENOOBJ;
869         }
870     }
871
872     libvlc_lock (p_this->p_libvlc);
873     if( *psz_cmd == 't' )
874     {
875         char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
876
877         if( !p_object )
878             p_object = VLC_OBJECT(p_this->p_libvlc);
879
880         psz_foo[0] = '|';
881         DumpStructure( p_object, 0, psz_foo );
882     }
883     else if( *psz_cmd == 'v' )
884     {
885         if( !p_object )
886             p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
887
888         PrintObject( p_object, "" );
889         vlc_mutex_lock( &vlc_internals( p_object )->var_lock );
890         if( vlc_internals( p_object )->var_root == NULL )
891             puts( " `-o No variables" );
892         else
893             twalk( vlc_internals( p_object )->var_root, DumpVariable );
894         vlc_mutex_unlock( &vlc_internals( p_object )->var_lock );
895     }
896     libvlc_unlock (p_this->p_libvlc);
897
898     if( *newval.psz_string )
899     {
900         vlc_object_release( p_object );
901     }
902     return VLC_SUCCESS;
903 }
904
905 /*****************************************************************************
906  * vlc_list_release: free a list previously allocated by vlc_list_find
907  *****************************************************************************
908  * This function decreases the refcount of all objects in the list and
909  * frees the list.
910  *****************************************************************************/
911 void vlc_list_release( vlc_list_t *p_list )
912 {
913     int i_index;
914
915     for( i_index = 0; i_index < p_list->i_count; i_index++ )
916     {
917         vlc_object_release( p_list->p_values[i_index].p_object );
918     }
919
920     free( p_list->p_values );
921     free( p_list );
922 }
923
924 /* Following functions are local */
925
926 static vlc_object_t *FindParent (vlc_object_t *p_this, int i_type)
927 {
928     for (vlc_object_t *parent = p_this->p_parent;
929          parent != NULL;
930          parent = parent->p_parent)
931     {
932         if (vlc_internals (parent)->i_object_type == i_type)
933             return vlc_object_hold (parent);
934     }
935     return NULL;
936 }
937
938 static vlc_object_t *FindParentName (vlc_object_t *p_this, const char *name)
939 {
940     for (vlc_object_t *parent = p_this->p_parent;
941          parent != NULL;
942          parent = parent->p_parent)
943     {
944         if (!objnamecmp (parent, name))
945             return vlc_object_hold (parent);
946     }
947     return NULL;
948 }
949
950 static vlc_object_t *FindChild (vlc_object_t *p_this, int i_type)
951 {
952     for (int i = vlc_internals( p_this )->i_children; i--; )
953     {
954         vlc_object_t *child = vlc_internals (p_this)->pp_children[i];
955         if (vlc_internals (child)->i_object_type == i_type)
956             return vlc_object_hold (child);
957
958         child = FindChild (child, i_type);
959         if (child != NULL)
960             return child;
961     }
962     return NULL;
963 }
964
965 static vlc_object_t *FindChildName (vlc_object_t *p_this, const char *name)
966 {
967     for (int i = vlc_internals( p_this )->i_children; i--; )
968     {
969         vlc_object_t *child = vlc_internals (p_this)->pp_children[i];
970         if (!objnamecmp (child, name))
971             return vlc_object_hold (child);
972
973         child = FindChildName (child, name);
974         if (child != NULL)
975             return child;
976     }
977     return NULL;
978 }
979
980 static void PrintObject( vlc_object_t *p_this, const char *psz_prefix )
981 {
982     char psz_children[20], psz_refcount[20], psz_thread[30], psz_name[50],
983          psz_parent[20];
984
985     int canc = vlc_savecancel ();
986     memset( &psz_name, 0, sizeof(psz_name) );
987     char *name = vlc_object_get_name(p_this);
988     if( name )
989     {
990         snprintf( psz_name, 49, " \"%s\"", name );
991         free( name );
992         if( psz_name[48] )
993             psz_name[48] = '\"';
994     }
995
996     psz_children[0] = '\0';
997     switch( vlc_internals( p_this )->i_children )
998     {
999         case 0:
1000             break;
1001         case 1:
1002             strcpy( psz_children, ", 1 child" );
1003             break;
1004         default:
1005             snprintf( psz_children, 19, ", %i children",
1006                       vlc_internals( p_this )->i_children );
1007             break;
1008     }
1009
1010     psz_refcount[0] = '\0';
1011     if( vlc_internals( p_this )->i_refcount > 0 )
1012         snprintf( psz_refcount, 19, ", refcount %u",
1013                   vlc_internals( p_this )->i_refcount );
1014
1015     psz_thread[0] = '\0';
1016     if( vlc_internals( p_this )->b_thread )
1017         snprintf( psz_thread, 29, " (thread %lu)",
1018                   (unsigned long)vlc_internals( p_this )->thread_id );
1019
1020     psz_parent[0] = '\0';
1021     if( p_this->p_parent )
1022         snprintf( psz_parent, 19, ", parent %p", p_this->p_parent );
1023
1024     printf( " %so %p %s%s%s%s%s%s\n", psz_prefix,
1025             p_this, p_this->psz_object_type,
1026             psz_name, psz_thread, psz_refcount, psz_children,
1027             psz_parent );
1028     vlc_restorecancel (canc);
1029 }
1030
1031 static void DumpStructure( vlc_object_t *p_this, int i_level, char *psz_foo )
1032 {
1033     int i;
1034     char i_back = psz_foo[i_level];
1035     psz_foo[i_level] = '\0';
1036
1037     PrintObject( p_this, psz_foo );
1038
1039     psz_foo[i_level] = i_back;
1040
1041     if( i_level / 2 >= MAX_DUMPSTRUCTURE_DEPTH )
1042     {
1043         msg_Warn( p_this, "structure tree is too deep" );
1044         return;
1045     }
1046
1047     for( i = 0 ; i < vlc_internals( p_this )->i_children ; i++ )
1048     {
1049         if( i_level )
1050         {
1051             psz_foo[i_level-1] = ' ';
1052
1053             if( psz_foo[i_level-2] == '`' )
1054             {
1055                 psz_foo[i_level-2] = ' ';
1056             }
1057         }
1058
1059         if( i == vlc_internals( p_this )->i_children - 1 )
1060         {
1061             psz_foo[i_level] = '`';
1062         }
1063         else
1064         {
1065             psz_foo[i_level] = '|';
1066         }
1067
1068         psz_foo[i_level+1] = '-';
1069         psz_foo[i_level+2] = '\0';
1070
1071         DumpStructure( vlc_internals( p_this )->pp_children[i], i_level + 2,
1072                        psz_foo );
1073     }
1074 }
1075
1076 static vlc_list_t * NewList( int i_count )
1077 {
1078     vlc_list_t * p_list = malloc( sizeof( vlc_list_t ) );
1079     if( p_list == NULL )
1080         return NULL;
1081
1082     p_list->i_count = i_count;
1083
1084     if( i_count == 0 )
1085     {
1086         p_list->p_values = NULL;
1087         return p_list;
1088     }
1089
1090     p_list->p_values = malloc( i_count * sizeof( vlc_value_t ) );
1091     if( p_list->p_values == NULL )
1092     {
1093         p_list->i_count = 0;
1094         return p_list;
1095     }
1096
1097     return p_list;
1098 }
1099
1100 static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object,
1101                          int i_index )
1102 {
1103     if( p_list == NULL || i_index >= p_list->i_count )
1104     {
1105         return;
1106     }
1107
1108     vlc_object_hold( p_object );
1109
1110     p_list->p_values[i_index].p_object = p_object;
1111
1112     return;
1113 }
1114
1115 /*static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object )
1116 {
1117     if( p_list == NULL )
1118     {
1119         return;
1120     }
1121
1122     p_list->p_values = realloc_or_free( p_list->p_values,
1123                               (p_list->i_count + 1) * sizeof( vlc_value_t ) );
1124     if( p_list->p_values == NULL )
1125     {
1126         p_list->i_count = 0;
1127         return;
1128     }
1129
1130     vlc_object_hold( p_object );
1131
1132     p_list->p_values[p_list->i_count].p_object = p_object;
1133     p_list->i_count++;
1134
1135     return;
1136 }*/
1137
1138 static int CountChildren( vlc_object_t *p_this, int i_type )
1139 {
1140     vlc_object_t *p_tmp;
1141     int i, i_count = 0;
1142
1143     for( i = 0; i < vlc_internals( p_this )->i_children; i++ )
1144     {
1145         p_tmp = vlc_internals( p_this )->pp_children[i];
1146
1147         if( vlc_internals( p_tmp )->i_object_type == i_type )
1148         {
1149             i_count++;
1150         }
1151         i_count += CountChildren( p_tmp, i_type );
1152     }
1153
1154     return i_count;
1155 }
1156
1157 static void ListChildren( vlc_list_t *p_list, vlc_object_t *p_this, int i_type )
1158 {
1159     vlc_object_t *p_tmp;
1160     int i;
1161
1162     for( i = 0; i < vlc_internals( p_this )->i_children; i++ )
1163     {
1164         p_tmp = vlc_internals( p_this )->pp_children[i];
1165
1166         if( vlc_internals( p_tmp )->i_object_type == i_type )
1167             ListReplace( p_list, p_tmp, p_list->i_count++ );
1168
1169         ListChildren( p_list, p_tmp, i_type );
1170     }
1171 }