]> git.sesse.net Git - vlc/blob - src/misc/objects.c
Introduce realloc_or_free() to src/*, and add assert() to mark unhandled ENOMEM error...
[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 #include <vlc_memory.h>
41
42 #include "../libvlc.h"
43 #include <vlc_aout.h>
44 #include "audio_output/aout_internal.h"
45
46 #include "vlc_interface.h"
47 #include "vlc_codec.h"
48
49 #include "variables.h"
50 #ifndef WIN32
51 # include <unistd.h>
52 #else
53 # include <io.h>
54 # include <fcntl.h>
55 # include <errno.h> /* ENOSYS */
56 #endif
57
58 #include <limits.h>
59 #include <assert.h>
60
61 #if defined (HAVE_SYS_EVENTFD_H)
62 # include <sys/eventfd.h>
63 #endif
64
65
66 /*****************************************************************************
67  * Local prototypes
68  *****************************************************************************/
69 static int  DumpCommand( vlc_object_t *, char const *,
70                          vlc_value_t, vlc_value_t, void * );
71
72 static vlc_object_t * FindObject    ( vlc_object_t *, int, int );
73 static vlc_object_t * FindObjectName( vlc_object_t *, const char *, int );
74 static void           PrintObject   ( vlc_object_t *, const char * );
75 static void           DumpStructure ( vlc_object_t *, int, char * );
76
77 static vlc_list_t   * NewList       ( int );
78 static void           ListReplace   ( vlc_list_t *, vlc_object_t *, int );
79 /*static void           ListAppend    ( vlc_list_t *, vlc_object_t * );*/
80 static int            CountChildren ( vlc_object_t *, int );
81 static void           ListChildren  ( vlc_list_t *, vlc_object_t *, int );
82
83 static void vlc_object_destroy( vlc_object_t *p_this );
84 static void vlc_object_detach_unlocked (vlc_object_t *p_this);
85
86 /*****************************************************************************
87  * Local structure lock
88  *****************************************************************************/
89 static void libvlc_lock (libvlc_int_t *p_libvlc)
90 {
91     vlc_mutex_lock (&(libvlc_priv (p_libvlc)->structure_lock));
92 }
93
94 static void libvlc_unlock (libvlc_int_t *p_libvlc)
95 {
96     vlc_mutex_unlock (&(libvlc_priv (p_libvlc)->structure_lock));
97 }
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     assert (i_size >= sizeof (vlc_object_t));
119     p_new = (vlc_object_t *)(p_priv + 1);
120
121     p_priv->i_object_type = i_type;
122     p_new->psz_object_type = psz_type;
123     p_priv->psz_name = NULL;
124
125     p_new->b_die = false;
126     p_new->b_error = false;
127     p_new->b_force = false;
128
129     p_new->psz_header = NULL;
130
131     if (p_this)
132         p_new->i_flags = p_this->i_flags
133             & (OBJECT_FLAGS_NODBG|OBJECT_FLAGS_QUIET|OBJECT_FLAGS_NOINTERACT);
134
135     p_priv->p_vars = calloc( 16, sizeof( variable_t ) );
136
137     if( !p_priv->p_vars )
138     {
139         free( p_priv );
140         return NULL;
141     }
142
143     if( p_this == NULL )
144     {
145         libvlc_int_t *self = (libvlc_int_t*)p_new;
146         p_new->p_libvlc = self;
147         vlc_mutex_init (&(libvlc_priv (self)->structure_lock));
148         p_this = p_new;
149     }
150     else
151         p_new->p_libvlc = p_this->p_libvlc;
152
153     vlc_spin_init( &p_priv->ref_spin );
154     p_priv->i_refcount = 1;
155     p_priv->pf_destructor = NULL;
156     p_priv->b_thread = false;
157     p_new->p_parent = NULL;
158     p_priv->pp_children = NULL;
159     p_priv->i_children = 0;
160
161     /* Initialize mutexes and condvars */
162     vlc_mutex_init( &p_priv->var_lock );
163     vlc_cond_init( &p_priv->var_wait );
164     p_priv->pipes[0] = p_priv->pipes[1] = -1;
165
166     if (p_new == VLC_OBJECT(p_new->p_libvlc))
167     {   /* TODO: should be in src/libvlc.c */
168         int canc = vlc_savecancel ();
169         var_Create( p_new, "tree", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
170         var_AddCallback( p_new, "tree", DumpCommand, NULL );
171         var_Create( p_new, "vars", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
172         var_AddCallback( p_new, "vars", DumpCommand, NULL );
173         vlc_restorecancel (canc);
174     }
175
176     return p_new;
177 }
178
179
180 /**
181  * Allocates and initializes a vlc object.
182  *
183  * @param i_type known object type (all of them are negative integer values),
184  *               or object byte size (always positive).
185  *
186  * @return the new object, or NULL on error.
187  */
188 void * __vlc_object_create( vlc_object_t *p_this, int i_type )
189 {
190     const char   * psz_type;
191     size_t         i_size;
192
193     switch( i_type )
194     {
195         case VLC_OBJECT_DECODER:
196             i_size = sizeof(decoder_t);
197             psz_type = "decoder";
198             break;
199         case VLC_OBJECT_AOUT:
200             i_size = sizeof(aout_instance_t);
201             psz_type = "audio output";
202             break;
203         default:
204             assert( i_type > 0 ); /* unknown type?! */
205             i_size = i_type;
206             i_type = VLC_OBJECT_GENERIC;
207             psz_type = "generic";
208             break;
209     }
210
211     return vlc_custom_create( p_this, i_size, i_type, psz_type );
212 }
213
214
215 /**
216  ****************************************************************************
217  * Set the destructor of a vlc object
218  *
219  * This function sets the destructor of the vlc object. It will be called
220  * when the object is destroyed when the its refcount reaches 0.
221  * (It is called by the internal function vlc_object_destroy())
222  *****************************************************************************/
223 void __vlc_object_set_destructor( vlc_object_t *p_this,
224                                   vlc_destructor_t pf_destructor )
225 {
226     vlc_object_internals_t *p_priv = vlc_internals(p_this );
227
228     vlc_spin_lock( &p_priv->ref_spin );
229     p_priv->pf_destructor = pf_destructor;
230     vlc_spin_unlock( &p_priv->ref_spin );
231 }
232
233 static vlc_mutex_t name_lock = VLC_STATIC_MUTEX;
234
235 #undef vlc_object_set_name
236 int vlc_object_set_name(vlc_object_t *obj, const char *name)
237 {
238     vlc_object_internals_t *priv = vlc_internals(obj);
239     char *newname = name ? strdup (name) : NULL;
240     char *oldname;
241
242     vlc_mutex_lock (&name_lock);
243     oldname = priv->psz_name;
244     priv->psz_name = newname;
245     vlc_mutex_unlock (&name_lock);
246
247     free (oldname);
248     return (priv->psz_name || !name) ? VLC_SUCCESS : VLC_ENOMEM;
249 }
250
251 #undef vlc_object_get_name
252 char *vlc_object_get_name(const vlc_object_t *obj)
253 {
254     vlc_object_internals_t *priv = vlc_internals(obj);
255     char *name;
256
257     vlc_mutex_lock (&name_lock);
258     name = priv->psz_name ? strdup (priv->psz_name) : NULL;
259     vlc_mutex_unlock (&name_lock);
260
261     return name;
262 }
263
264 /**
265  ****************************************************************************
266  * Destroy a vlc object (Internal)
267  *
268  * This function destroys an object that has been previously allocated with
269  * vlc_object_create. The object's refcount must be zero and it must not be
270  * attached to other objects in any way.
271  *
272  * This function must be called with cancellation disabled (currently).
273  *****************************************************************************/
274 static void vlc_object_destroy( vlc_object_t *p_this )
275 {
276     vlc_object_internals_t *p_priv = vlc_internals( p_this );
277
278     /* Objects are always detached beforehand */
279     assert( !p_this->p_parent );
280
281     /* Send a kill to the object's thread if applicable */
282     vlc_object_kill( p_this );
283
284     /* Call the custom "subclass" destructor */
285     if( p_priv->pf_destructor )
286         p_priv->pf_destructor( p_this );
287
288     /* Any thread must have been cleaned up at this point. */
289     assert( !p_priv->b_thread );
290
291     /* Destroy the associated variables, starting from the end so that
292      * no memmove calls have to be done. */
293     while( p_priv->i_vars )
294     {
295         var_Destroy( p_this, p_priv->p_vars[p_priv->i_vars - 1].psz_name );
296     }
297
298     free( p_priv->p_vars );
299     vlc_cond_destroy( &p_priv->var_wait );
300     vlc_mutex_destroy( &p_priv->var_lock );
301
302     free( p_this->psz_header );
303
304     free( p_priv->psz_name );
305
306     vlc_spin_destroy( &p_priv->ref_spin );
307     if( p_priv->pipes[1] != -1 && p_priv->pipes[1] != p_priv->pipes[0] )
308         close( p_priv->pipes[1] );
309     if( p_priv->pipes[0] != -1 )
310         close( p_priv->pipes[0] );
311     if( VLC_OBJECT(p_this->p_libvlc) == p_this )
312         vlc_mutex_destroy (&(libvlc_priv ((libvlc_int_t *)p_this)->structure_lock));
313
314     free( p_priv );
315 }
316
317
318 #ifdef WIN32
319 # include <winsock2.h>
320 # include <ws2tcpip.h>
321
322 /**
323  * select()-able pipes emulated using Winsock
324  */
325 static int pipe (int fd[2])
326 {
327     SOCKADDR_IN addr;
328     int addrlen = sizeof (addr);
329
330     SOCKET l = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP), a,
331            c = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
332     if ((l == INVALID_SOCKET) || (c == INVALID_SOCKET))
333         goto error;
334
335     memset (&addr, 0, sizeof (addr));
336     addr.sin_family = AF_INET;
337     addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
338     if (bind (l, (PSOCKADDR)&addr, sizeof (addr))
339      || getsockname (l, (PSOCKADDR)&addr, &addrlen)
340      || listen (l, 1)
341      || connect (c, (PSOCKADDR)&addr, addrlen))
342         goto error;
343
344     a = accept (l, NULL, NULL);
345     if (a == INVALID_SOCKET)
346         goto error;
347
348     closesocket (l);
349     //shutdown (a, 0);
350     //shutdown (c, 1);
351     fd[0] = c;
352     fd[1] = a;
353     return 0;
354
355 error:
356     if (l != INVALID_SOCKET)
357         closesocket (l);
358     if (c != INVALID_SOCKET)
359         closesocket (c);
360     return -1;
361 }
362
363 #undef  read
364 #define read( a, b, c )  recv (a, b, c, 0)
365 #undef  write
366 #define write( a, b, c ) send (a, b, c, 0)
367 #undef  close
368 #define close( a )       closesocket (a)
369 #endif /* WIN32 */
370
371 static vlc_mutex_t pipe_lock = VLC_STATIC_MUTEX;
372
373 /**
374  * Returns the readable end of a pipe that becomes readable once termination
375  * of the object is requested (vlc_object_kill()).
376  * This can be used to wake-up out of a select() or poll() event loop, such
377  * typically when doing network I/O.
378  *
379  * Note that the pipe will remain the same for the lifetime of the object.
380  * DO NOT read the pipe nor close it yourself. Ever.
381  *
382  * @param obj object that would be "killed"
383  * @return a readable pipe descriptor, or -1 on error.
384  */
385 int vlc_object_waitpipe( vlc_object_t *obj )
386 {
387     vlc_object_internals_t *internals = vlc_internals( obj );
388
389     vlc_mutex_lock (&pipe_lock);
390     if (internals->pipes[0] == -1)
391     {
392         /* This can only ever happen if someone killed us without locking: */
393         assert (internals->pipes[1] == -1);
394
395 #if defined (HAVE_SYS_EVENTFD_H)
396         internals->pipes[0] = internals->pipes[1] = eventfd (0, 0);
397         if (internals->pipes[0] == -1)
398 #endif
399         {
400             if (pipe (internals->pipes))
401                 internals->pipes[0] = internals->pipes[1] = -1;
402         }
403
404         if (internals->pipes[0] != -1 && obj->b_die)
405         {   /* Race condition: vlc_object_kill() already invoked! */
406             msg_Dbg (obj, "waitpipe: object already dying");
407             write (internals->pipes[1], &(uint64_t){ 1 }, sizeof (uint64_t));
408         }
409     }
410     vlc_mutex_unlock (&pipe_lock);
411     return internals->pipes[0];
412 }
413
414
415 /**
416  * Requests termination of an object, cancels the object thread, and make the
417  * object wait pipe (if it exists) readable. Not a cancellation point.
418  */
419 void __vlc_object_kill( vlc_object_t *p_this )
420 {
421     vlc_object_internals_t *priv = vlc_internals( p_this );
422     int fd = -1;
423
424     vlc_thread_cancel( p_this );
425     vlc_mutex_lock( &pipe_lock );
426     if( !p_this->b_die )
427     {
428         fd = priv->pipes[1];
429         p_this->b_die = true;
430     }
431
432     /* This also serves as a memory barrier toward vlc_object_alive(): */
433     vlc_mutex_unlock( &pipe_lock );
434
435     if (fd != -1)
436     {
437         int canc = vlc_savecancel ();
438
439         /* write _after_ setting b_die, so vlc_object_alive() returns false */
440         write (fd, &(uint64_t){ 1 }, sizeof (uint64_t));
441         msg_Dbg (p_this, "waitpipe: object killed");
442         vlc_restorecancel (canc);
443     }
444 }
445
446
447 /*****************************************************************************
448  * find a typed object and increment its refcount
449  *****************************************************************************
450  * This function recursively looks for a given object type. i_mode can be one
451  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
452  *****************************************************************************/
453 void * __vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode )
454 {
455     vlc_object_t *p_found;
456
457     /* If we are of the requested type ourselves, don't look further */
458     if( !(i_mode & FIND_STRICT)
459      && vlc_internals (p_this)->i_object_type == i_type )
460     {
461         vlc_object_hold( p_this );
462         return p_this;
463     }
464
465     /* Otherwise, recursively look for the object */
466     if ((i_mode & 0x000f) == FIND_ANYWHERE)
467         return vlc_object_find (p_this->p_libvlc, i_type,
468                                 (i_mode & ~0x000f)|FIND_CHILD);
469
470     libvlc_lock (p_this->p_libvlc);
471     p_found = FindObject( p_this, i_type, i_mode );
472     libvlc_unlock (p_this->p_libvlc);
473     return p_found;
474 }
475
476
477 static int objnamecmp(const vlc_object_t *obj, const char *name)
478 {
479     char *objname = vlc_object_get_name(obj);
480     if (objname == NULL)
481         return INT_MIN;
482
483     int ret = strcmp (objname, name);
484     free (objname);
485     return ret;
486 }
487
488 #undef vlc_object_find_name
489 /**
490  * Finds a named object and increment its reference count.
491  * Beware that objects found in this manner can be "owned" by another thread,
492  * be of _any_ type, and be attached to any module (if any). With such an
493  * object reference, you can set or get object variables, emit log messages,
494  * and read write-once object parameters (psz_object_type, etc).
495  * You CANNOT cast the object to a more specific object type, and you
496  * definitely cannot invoke object type-specific callbacks with this.
497  *
498  * @param p_this object to search from
499  * @param psz_name name of the object to search for
500  * @param i_mode search direction: FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
501  *
502  * @return a matching object (must be released by the caller),
503  * or NULL on error.
504  */
505 vlc_object_t *vlc_object_find_name( vlc_object_t *p_this,
506                                     const char *psz_name, int i_mode )
507 {
508     vlc_object_t *p_found;
509
510     /* Reading psz_object_name from a separate inhibits thread-safety.
511      * Use a libvlc address variable instead for that sort of things! */
512     msg_Warn( p_this, "%s(%s) is not safe!", __func__, psz_name );
513     /* If have the requested name ourselves, don't look further */
514     if( !(i_mode & FIND_STRICT) && !objnamecmp(p_this, psz_name) )
515     {
516         vlc_object_hold( p_this );
517         return p_this;
518     }
519
520     libvlc_lock (p_this->p_libvlc);
521
522     /* Otherwise, recursively look for the object */
523     if( (i_mode & 0x000f) == FIND_ANYWHERE )
524     {
525         vlc_object_t *p_root = p_this;
526
527         /* Find the root */
528         while( p_root->p_parent != NULL &&
529                p_root != VLC_OBJECT( p_this->p_libvlc ) )
530         {
531             p_root = p_root->p_parent;
532         }
533
534         p_found = FindObjectName( p_root, psz_name,
535                                  (i_mode & ~0x000f)|FIND_CHILD );
536         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
537         {
538             p_found = FindObjectName( VLC_OBJECT( p_this->p_libvlc ),
539                                       psz_name, (i_mode & ~0x000f)|FIND_CHILD );
540         }
541     }
542     else
543     {
544         p_found = FindObjectName( p_this, psz_name, i_mode );
545     }
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 & 0x000f )
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 /*****************************************************************************
778  * DumpCommand: print the current vlc structure
779  *****************************************************************************
780  * This function prints either an ASCII tree showing the connections between
781  * vlc objects, and additional information such as their refcount, thread ID,
782  * etc. (command "tree"), or the same data as a simple list (command "list").
783  *****************************************************************************/
784 static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd,
785                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
786 {
787     (void)oldval; (void)p_data;
788     vlc_object_t *p_object = NULL;
789
790     if( *newval.psz_string )
791     {
792         /* try using the object's name to find it */
793         p_object = vlc_object_find_name( p_this, newval.psz_string,
794                                          FIND_ANYWHERE );
795         if( !p_object )
796         {
797             return VLC_ENOOBJ;
798         }
799     }
800
801     libvlc_lock (p_this->p_libvlc);
802     if( *psz_cmd == 't' )
803     {
804         char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
805
806         if( !p_object )
807             p_object = VLC_OBJECT(p_this->p_libvlc);
808
809         psz_foo[0] = '|';
810         DumpStructure( p_object, 0, psz_foo );
811     }
812     else if( *psz_cmd == 'v' )
813     {
814         int i;
815
816         if( !p_object )
817             p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
818
819         PrintObject( p_object, "" );
820
821         vlc_mutex_lock( &vlc_internals( p_object )->var_lock );
822         if( !vlc_internals( p_object )->i_vars )
823             printf( " `-o No variables\n" );
824         for( i = 0; i < vlc_internals( p_object )->i_vars; i++ )
825         {
826             variable_t *p_var = vlc_internals( p_object )->p_vars + i;
827             const char *psz_type = "unknown";
828
829             switch( p_var->i_type & VLC_VAR_TYPE )
830             {
831 #define MYCASE( type, nice )                \
832                 case VLC_VAR_ ## type:  \
833                     psz_type = nice;    \
834                     break;
835                 MYCASE( VOID, "void" );
836                 MYCASE( BOOL, "bool" );
837                 MYCASE( INTEGER, "integer" );
838                 MYCASE( HOTKEY, "hotkey" );
839                 MYCASE( STRING, "string" );
840                 MYCASE( MODULE, "module" );
841                 MYCASE( FILE, "file" );
842                 MYCASE( DIRECTORY, "directory" );
843                 MYCASE( VARIABLE, "variable" );
844                 MYCASE( FLOAT, "float" );
845                 MYCASE( TIME, "time" );
846                 MYCASE( ADDRESS, "address" );
847                 MYCASE( MUTEX, "mutex" );
848                 MYCASE( LIST, "list" );
849 #undef MYCASE
850             }
851             printf( " %c-o \"%s\" (%s",
852                     i + 1 == vlc_internals( p_object )->i_vars ? '`' : '|',
853                     p_var->psz_name, psz_type );
854             if( p_var->psz_text )
855                 printf( ", %s", p_var->psz_text );
856             printf( ")" );
857             if( p_var->i_type & VLC_VAR_HASCHOICE )
858                 printf( ", has choices" );
859             if( p_var->i_type & VLC_VAR_ISCOMMAND )
860                 printf( ", command" );
861             if( p_var->i_entries )
862                 printf( ", %d callbacks", p_var->i_entries );
863             switch( p_var->i_type & VLC_VAR_CLASS )
864             {
865                 case VLC_VAR_VOID:
866                 case VLC_VAR_MUTEX:
867                     break;
868                 case VLC_VAR_BOOL:
869                     printf( ": %s", p_var->val.b_bool ? "true" : "false" );
870                     break;
871                 case VLC_VAR_INTEGER:
872                     printf( ": %d", p_var->val.i_int );
873                     break;
874                 case VLC_VAR_STRING:
875                     printf( ": \"%s\"", p_var->val.psz_string );
876                     break;
877                 case VLC_VAR_FLOAT:
878                     printf( ": %f", p_var->val.f_float );
879                     break;
880                 case VLC_VAR_TIME:
881                     printf( ": %"PRIi64, (int64_t)p_var->val.i_time );
882                     break;
883                 case VLC_VAR_ADDRESS:
884                     printf( ": %p", p_var->val.p_address );
885                     break;
886                 case VLC_VAR_LIST:
887                     printf( ": TODO" );
888                     break;
889             }
890             printf( "\n" );
891         }
892         vlc_mutex_unlock( &vlc_internals( p_object )->var_lock );
893     }
894     libvlc_unlock (p_this->p_libvlc);
895
896     if( *newval.psz_string )
897     {
898         vlc_object_release( p_object );
899     }
900     return VLC_SUCCESS;
901 }
902
903 /*****************************************************************************
904  * vlc_list_release: free a list previously allocated by vlc_list_find
905  *****************************************************************************
906  * This function decreases the refcount of all objects in the list and
907  * frees the list.
908  *****************************************************************************/
909 void vlc_list_release( vlc_list_t *p_list )
910 {
911     int i_index;
912
913     for( i_index = 0; i_index < p_list->i_count; i_index++ )
914     {
915         vlc_object_release( p_list->p_values[i_index].p_object );
916     }
917
918     free( p_list->p_values );
919     free( p_list );
920 }
921
922 /* Following functions are local */
923
924 static vlc_object_t * FindObject( vlc_object_t *p_this, int i_type, int i_mode )
925 {
926     int i;
927     vlc_object_t *p_tmp;
928
929     switch( i_mode & 0x000f )
930     {
931     case FIND_PARENT:
932         p_tmp = p_this->p_parent;
933         if( p_tmp )
934         {
935             if( vlc_internals( p_tmp )->i_object_type == i_type )
936             {
937                 vlc_object_hold( p_tmp );
938                 return p_tmp;
939             }
940             else
941             {
942                 return FindObject( p_tmp, i_type, i_mode );
943             }
944         }
945         break;
946
947     case FIND_CHILD:
948         for( i = vlc_internals( p_this )->i_children; i--; )
949         {
950             p_tmp = vlc_internals( p_this )->pp_children[i];
951             if( vlc_internals( p_tmp )->i_object_type == i_type )
952             {
953                 vlc_object_hold( p_tmp );
954                 return p_tmp;
955             }
956             else if( vlc_internals( p_tmp )->i_children )
957             {
958                 p_tmp = FindObject( p_tmp, i_type, i_mode );
959                 if( p_tmp )
960                 {
961                     return p_tmp;
962                 }
963             }
964         }
965         break;
966
967     case FIND_ANYWHERE:
968         /* Handled in vlc_object_find */
969         break;
970     }
971
972     return NULL;
973 }
974
975 static vlc_object_t * FindObjectName( vlc_object_t *p_this,
976                                       const char *psz_name,
977                                       int i_mode )
978 {
979     int i;
980     vlc_object_t *p_tmp;
981
982     switch( i_mode & 0x000f )
983     {
984     case FIND_PARENT:
985         p_tmp = p_this->p_parent;
986         if( p_tmp )
987         {
988             if( !objnamecmp(p_tmp, psz_name) )
989             {
990                 vlc_object_hold( p_tmp );
991                 return p_tmp;
992             }
993             else
994             {
995                 return FindObjectName( p_tmp, psz_name, i_mode );
996             }
997         }
998         break;
999
1000     case FIND_CHILD:
1001         for( i = vlc_internals( p_this )->i_children; i--; )
1002         {
1003             p_tmp = vlc_internals( p_this )->pp_children[i];
1004             if( !objnamecmp(p_tmp, psz_name ) )
1005             {
1006                 vlc_object_hold( p_tmp );
1007                 return p_tmp;
1008             }
1009             else if( vlc_internals( p_tmp )->i_children )
1010             {
1011                 p_tmp = FindObjectName( p_tmp, psz_name, i_mode );
1012                 if( p_tmp )
1013                 {
1014                     return p_tmp;
1015                 }
1016             }
1017         }
1018         break;
1019
1020     case FIND_ANYWHERE:
1021         /* Handled in vlc_object_find */
1022         break;
1023     }
1024
1025     return NULL;
1026 }
1027
1028
1029 static void PrintObject( vlc_object_t *p_this, const char *psz_prefix )
1030 {
1031     char psz_children[20], psz_refcount[20], psz_thread[30], psz_name[50],
1032          psz_parent[20];
1033
1034     int canc = vlc_savecancel ();
1035     memset( &psz_name, 0, sizeof(psz_name) );
1036     char *name = vlc_object_get_name(p_this);
1037     if( name )
1038     {
1039         snprintf( psz_name, 49, " \"%s\"", name );
1040         free( name );
1041         if( psz_name[48] )
1042             psz_name[48] = '\"';
1043     }
1044
1045     psz_children[0] = '\0';
1046     switch( vlc_internals( p_this )->i_children )
1047     {
1048         case 0:
1049             break;
1050         case 1:
1051             strcpy( psz_children, ", 1 child" );
1052             break;
1053         default:
1054             snprintf( psz_children, 19, ", %i children",
1055                       vlc_internals( p_this )->i_children );
1056             break;
1057     }
1058
1059     psz_refcount[0] = '\0';
1060     if( vlc_internals( p_this )->i_refcount > 0 )
1061         snprintf( psz_refcount, 19, ", refcount %u",
1062                   vlc_internals( p_this )->i_refcount );
1063
1064     psz_thread[0] = '\0';
1065     if( vlc_internals( p_this )->b_thread )
1066         snprintf( psz_thread, 29, " (thread %lu)",
1067                   (unsigned long)vlc_internals( p_this )->thread_id );
1068
1069     psz_parent[0] = '\0';
1070     if( p_this->p_parent )
1071         snprintf( psz_parent, 19, ", parent %p", p_this->p_parent );
1072
1073     printf( " %so %p %s%s%s%s%s%s\n", psz_prefix,
1074             p_this, p_this->psz_object_type,
1075             psz_name, psz_thread, psz_refcount, psz_children,
1076             psz_parent );
1077     vlc_restorecancel (canc);
1078 }
1079
1080 static void DumpStructure( vlc_object_t *p_this, int i_level, char *psz_foo )
1081 {
1082     int i;
1083     char i_back = psz_foo[i_level];
1084     psz_foo[i_level] = '\0';
1085
1086     PrintObject( p_this, psz_foo );
1087
1088     psz_foo[i_level] = i_back;
1089
1090     if( i_level / 2 >= MAX_DUMPSTRUCTURE_DEPTH )
1091     {
1092         msg_Warn( p_this, "structure tree is too deep" );
1093         return;
1094     }
1095
1096     for( i = 0 ; i < vlc_internals( p_this )->i_children ; i++ )
1097     {
1098         if( i_level )
1099         {
1100             psz_foo[i_level-1] = ' ';
1101
1102             if( psz_foo[i_level-2] == '`' )
1103             {
1104                 psz_foo[i_level-2] = ' ';
1105             }
1106         }
1107
1108         if( i == vlc_internals( p_this )->i_children - 1 )
1109         {
1110             psz_foo[i_level] = '`';
1111         }
1112         else
1113         {
1114             psz_foo[i_level] = '|';
1115         }
1116
1117         psz_foo[i_level+1] = '-';
1118         psz_foo[i_level+2] = '\0';
1119
1120         DumpStructure( vlc_internals( p_this )->pp_children[i], i_level + 2,
1121                        psz_foo );
1122     }
1123 }
1124
1125 static vlc_list_t * NewList( int i_count )
1126 {
1127     vlc_list_t * p_list = malloc( sizeof( vlc_list_t ) );
1128     if( p_list == NULL )
1129         return NULL;
1130
1131     p_list->i_count = i_count;
1132
1133     if( i_count == 0 )
1134     {
1135         p_list->p_values = NULL;
1136         return p_list;
1137     }
1138
1139     p_list->p_values = malloc( i_count * sizeof( vlc_value_t ) );
1140     if( p_list->p_values == NULL )
1141     {
1142         p_list->i_count = 0;
1143         return p_list;
1144     }
1145
1146     return p_list;
1147 }
1148
1149 static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object,
1150                          int i_index )
1151 {
1152     if( p_list == NULL || i_index >= p_list->i_count )
1153     {
1154         return;
1155     }
1156
1157     vlc_object_hold( p_object );
1158
1159     p_list->p_values[i_index].p_object = p_object;
1160
1161     return;
1162 }
1163
1164 /*static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object )
1165 {
1166     if( p_list == NULL )
1167     {
1168         return;
1169     }
1170
1171     p_list->p_values = realloc_or_free( p_list->p_values,
1172                               (p_list->i_count + 1) * sizeof( vlc_value_t ) );
1173     if( p_list->p_values == NULL )
1174     {
1175         p_list->i_count = 0;
1176         return;
1177     }
1178
1179     vlc_object_hold( p_object );
1180
1181     p_list->p_values[p_list->i_count].p_object = p_object;
1182     p_list->i_count++;
1183
1184     return;
1185 }*/
1186
1187 static int CountChildren( vlc_object_t *p_this, int i_type )
1188 {
1189     vlc_object_t *p_tmp;
1190     int i, i_count = 0;
1191
1192     for( i = 0; i < vlc_internals( p_this )->i_children; i++ )
1193     {
1194         p_tmp = vlc_internals( p_this )->pp_children[i];
1195
1196         if( vlc_internals( p_tmp )->i_object_type == i_type )
1197         {
1198             i_count++;
1199         }
1200         i_count += CountChildren( p_tmp, i_type );
1201     }
1202
1203     return i_count;
1204 }
1205
1206 static void ListChildren( vlc_list_t *p_list, vlc_object_t *p_this, int i_type )
1207 {
1208     vlc_object_t *p_tmp;
1209     int i;
1210
1211     for( i = 0; i < vlc_internals( p_this )->i_children; i++ )
1212     {
1213         p_tmp = vlc_internals( p_this )->pp_children[i];
1214
1215         if( vlc_internals( p_tmp )->i_object_type == i_type )
1216             ListReplace( p_list, p_tmp, p_list->i_count++ );
1217
1218         ListChildren( p_list, p_tmp, i_type );
1219     }
1220 }