]> git.sesse.net Git - vlc/blob - src/misc/objects.c
Hack to get the leaked object tree back
[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 <fcntl.h>
54 # include <errno.h> /* ENOSYS */
55 #endif
56
57 #include <assert.h>
58
59 #if defined (HAVE_SYS_EVENTFD_H)
60 # include <sys/eventfd.h>
61 # define HAVE_EVENTFD 1
62 #endif
63
64
65 /*****************************************************************************
66  * Local prototypes
67  *****************************************************************************/
68 static int  DumpCommand( vlc_object_t *, char const *,
69                          vlc_value_t, vlc_value_t, void * );
70
71 static vlc_object_t * FindObject    ( vlc_object_t *, int, int );
72 static vlc_object_t * FindObjectName( vlc_object_t *, const char *, int );
73 static void           PrintObject   ( vlc_object_t *, const char * );
74 static void           DumpStructure ( vlc_object_t *, int, char * );
75
76 static vlc_list_t   * NewList       ( int );
77 static void           ListReplace   ( vlc_list_t *, vlc_object_t *, int );
78 /*static void           ListAppend    ( vlc_list_t *, vlc_object_t * );*/
79 static int            CountChildren ( vlc_object_t *, int );
80 static void           ListChildren  ( vlc_list_t *, vlc_object_t *, int );
81
82 static void vlc_object_destroy( vlc_object_t *p_this );
83 static void vlc_object_detach_unlocked (vlc_object_t *p_this);
84 #ifndef NDEBUG
85 static void vlc_object_dump( vlc_object_t *p_this );
86 #endif
87
88 /*****************************************************************************
89  * Local structure lock
90  *****************************************************************************/
91 static void libvlc_lock (libvlc_int_t *p_libvlc)
92 {
93     vlc_mutex_lock (&(libvlc_priv (p_libvlc)->structure_lock));
94 }
95
96 static void libvlc_unlock (libvlc_int_t *p_libvlc)
97 {
98     vlc_mutex_unlock (&(libvlc_priv (p_libvlc)->structure_lock));
99 }
100
101 void *__vlc_custom_create( vlc_object_t *p_this, size_t i_size,
102                            int i_type, const char *psz_type )
103 {
104     vlc_object_t *p_new;
105     vlc_object_internals_t *p_priv;
106
107     /* NOTE:
108      * VLC objects are laid out as follow:
109      * - first the LibVLC-private per-object data,
110      * - then VLC_COMMON members from vlc_object_t,
111      * - finally, the type-specific data (if any).
112      *
113      * This function initializes the LibVLC and common data,
114      * and zeroes the rest.
115      */
116     p_priv = calloc( 1, sizeof( *p_priv ) + i_size );
117     if( p_priv == NULL )
118         return NULL;
119
120     assert (i_size >= sizeof (vlc_object_t));
121     p_new = (vlc_object_t *)(p_priv + 1);
122
123     p_priv->i_object_type = i_type;
124     p_new->psz_object_type = psz_type;
125     p_new->psz_object_name = NULL;
126
127     p_new->b_die = false;
128     p_new->b_error = false;
129     p_new->b_force = false;
130
131     p_new->psz_header = NULL;
132
133     if (p_this)
134         p_new->i_flags = p_this->i_flags
135             & (OBJECT_FLAGS_NODBG|OBJECT_FLAGS_QUIET|OBJECT_FLAGS_NOINTERACT);
136
137     p_priv->p_vars = calloc( 16, sizeof( variable_t ) );
138
139     if( !p_priv->p_vars )
140     {
141         free( p_priv );
142         return NULL;
143     }
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_priv->next = p_priv->prev = 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     p_new->p_private = NULL;
164
165     /* Initialize mutexes and condvars */
166     vlc_mutex_init( &p_priv->var_lock );
167     vlc_cond_init( &p_priv->var_wait );
168     p_priv->pipes[0] = p_priv->pipes[1] = -1;
169
170     p_priv->next = p_this;
171     libvlc_lock (p_new->p_libvlc);
172     p_priv->prev = vlc_internals (p_this)->prev;
173     vlc_internals (p_this)->prev = p_new;
174     vlc_internals (p_priv->prev)->next = p_new;
175     libvlc_unlock (p_new->p_libvlc);
176
177     if (p_new == VLC_OBJECT(p_new->p_libvlc))
178     {   /* TODO: should be in src/libvlc.c */
179         int canc = vlc_savecancel ();
180         var_Create( p_new, "list", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
181         var_AddCallback( p_new, "list", DumpCommand, NULL );
182         var_Create( p_new, "tree", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
183         var_AddCallback( p_new, "tree", DumpCommand, NULL );
184         var_Create( p_new, "vars", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
185         var_AddCallback( p_new, "vars", DumpCommand, NULL );
186         vlc_restorecancel (canc);
187     }
188
189     return p_new;
190 }
191
192
193 /**
194  * Allocates and initializes a vlc object.
195  *
196  * @param i_type known object type (all of them are negative integer values),
197  *               or object byte size (always positive).
198  *
199  * @return the new object, or NULL on error.
200  */
201 void * __vlc_object_create( vlc_object_t *p_this, int i_type )
202 {
203     const char   * psz_type;
204     size_t         i_size;
205
206     switch( i_type )
207     {
208         case VLC_OBJECT_INTF:
209             i_size = sizeof(intf_thread_t);
210             psz_type = "interface";
211             break;
212         case VLC_OBJECT_DECODER:
213             i_size = sizeof(decoder_t);
214             psz_type = "decoder";
215             break;
216         case VLC_OBJECT_PACKETIZER:
217             i_size = sizeof(decoder_t);
218             psz_type = "packetizer";
219             break;
220         case VLC_OBJECT_AOUT:
221             i_size = sizeof(aout_instance_t);
222             psz_type = "audio output";
223             break;
224         default:
225             assert( i_type > 0 ); /* unknown type?! */
226             i_size = i_type;
227             i_type = VLC_OBJECT_GENERIC;
228             psz_type = "generic";
229             break;
230     }
231
232     return vlc_custom_create( p_this, i_size, i_type, psz_type );
233 }
234
235
236 /**
237  ****************************************************************************
238  * Set the destructor of a vlc object
239  *
240  * This function sets the destructor of the vlc object. It will be called
241  * when the object is destroyed when the its refcount reaches 0.
242  * (It is called by the internal function vlc_object_destroy())
243  *****************************************************************************/
244 void __vlc_object_set_destructor( vlc_object_t *p_this,
245                                   vlc_destructor_t pf_destructor )
246 {
247     vlc_object_internals_t *p_priv = vlc_internals(p_this );
248     p_priv->pf_destructor = pf_destructor;
249 }
250
251 /**
252  ****************************************************************************
253  * Destroy a vlc object (Internal)
254  *
255  * This function destroys an object that has been previously allocated with
256  * vlc_object_create. The object's refcount must be zero and it must not be
257  * attached to other objects in any way.
258  *
259  * This function must be called with cancellation disabled (currently).
260  *****************************************************************************/
261 static void vlc_object_destroy( vlc_object_t *p_this )
262 {
263     vlc_object_internals_t *p_priv = vlc_internals( p_this );
264
265     /* Objects are always detached beforehand */
266     assert( !p_this->p_parent );
267
268     /* Send a kill to the object's thread if applicable */
269     vlc_object_kill( p_this );
270
271     /* Call the custom "subclass" destructor */
272     if( p_priv->pf_destructor )
273         p_priv->pf_destructor( p_this );
274
275     /* Any thread must have been cleaned up at this point. */
276     assert( !p_priv->b_thread );
277
278     /* Destroy the associated variables, starting from the end so that
279      * no memmove calls have to be done. */
280     while( p_priv->i_vars )
281     {
282         var_Destroy( p_this, p_priv->p_vars[p_priv->i_vars - 1].psz_name );
283     }
284
285     free( p_priv->p_vars );
286     vlc_cond_destroy( &p_priv->var_wait );
287     vlc_mutex_destroy( &p_priv->var_lock );
288
289     free( p_this->psz_header );
290
291     FREENULL( p_this->psz_object_name );
292
293     vlc_spin_destroy( &p_priv->ref_spin );
294     if( p_priv->pipes[1] != -1 && p_priv->pipes[1] != p_priv->pipes[0] )
295         close( p_priv->pipes[1] );
296     if( p_priv->pipes[0] != -1 )
297         close( p_priv->pipes[0] );
298     if( VLC_OBJECT(p_this->p_libvlc) == p_this )
299         vlc_mutex_destroy (&(libvlc_priv ((libvlc_int_t *)p_this)->structure_lock));
300
301     free( p_priv );
302 }
303
304
305 #ifdef WIN32
306 # include <winsock2.h>
307 # include <ws2tcpip.h>
308
309 /**
310  * select()-able pipes emulated using Winsock
311  */
312 static int pipe (int fd[2])
313 {
314     SOCKADDR_IN addr;
315     int addrlen = sizeof (addr);
316
317     SOCKET l = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP), a,
318            c = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
319     if ((l == INVALID_SOCKET) || (c == INVALID_SOCKET))
320         goto error;
321
322     memset (&addr, 0, sizeof (addr));
323     addr.sin_family = AF_INET;
324     addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
325     if (bind (l, (PSOCKADDR)&addr, sizeof (addr))
326      || getsockname (l, (PSOCKADDR)&addr, &addrlen)
327      || listen (l, 1)
328      || connect (c, (PSOCKADDR)&addr, addrlen))
329         goto error;
330
331     a = accept (l, NULL, NULL);
332     if (a == INVALID_SOCKET)
333         goto error;
334
335     closesocket (l);
336     //shutdown (a, 0);
337     //shutdown (c, 1);
338     fd[0] = c;
339     fd[1] = a;
340     return 0;
341
342 error:
343     if (l != INVALID_SOCKET)
344         closesocket (l);
345     if (c != INVALID_SOCKET)
346         closesocket (c);
347     return -1;
348 }
349
350 #undef  read
351 #define read( a, b, c )  recv (a, b, c, 0)
352 #undef  write
353 #define write( a, b, c ) send (a, b, c, 0)
354 #undef  close
355 #define close( a )       closesocket (a)
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 #ifdef HAVE_EVENTFD
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( !(i_mode & FIND_STRICT)
446      && vlc_internals (p_this)->i_object_type == i_type )
447     {
448         vlc_object_hold( p_this );
449         return p_this;
450     }
451
452     /* Otherwise, recursively look for the object */
453     if ((i_mode & 0x000f) == FIND_ANYWHERE)
454         return vlc_object_find (p_this->p_libvlc, i_type,
455                                 (i_mode & ~0x000f)|FIND_CHILD);
456
457     libvlc_lock (p_this->p_libvlc);
458     p_found = FindObject( p_this, i_type, i_mode );
459     libvlc_unlock (p_this->p_libvlc);
460     return p_found;
461 }
462
463 #undef vlc_object_find_name
464 /**
465  * Finds a named object and increment its reference count.
466  * Beware that objects found in this manner can be "owned" by another thread,
467  * be of _any_ type, and be attached to any module (if any). With such an
468  * object reference, you can set or get object variables, emit log messages,
469  * and read write-once object parameters (psz_object_type, etc).
470  * You CANNOT cast the object to a more specific object type, and you
471  * definitely cannot invoke object type-specific callbacks with this.
472  *
473  * @param p_this object to search from
474  * @param psz_name name of the object to search for
475  * @param i_mode search direction: FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
476  *
477  * @return a matching object (must be released by the caller),
478  * or NULL on error.
479  */
480 vlc_object_t *vlc_object_find_name( vlc_object_t *p_this,
481                                     const char *psz_name, int i_mode )
482 {
483     vlc_object_t *p_found;
484
485     /* If have the requested name ourselves, don't look further */
486     if( !(i_mode & FIND_STRICT)
487         && p_this->psz_object_name
488         && !strcmp( p_this->psz_object_name, psz_name ) )
489     {
490         vlc_object_hold( p_this );
491         return p_this;
492     }
493
494     libvlc_lock (p_this->p_libvlc);
495
496     /* Otherwise, recursively look for the object */
497     if( (i_mode & 0x000f) == FIND_ANYWHERE )
498     {
499         vlc_object_t *p_root = p_this;
500
501         /* Find the root */
502         while( p_root->p_parent != NULL &&
503                p_root != VLC_OBJECT( p_this->p_libvlc ) )
504         {
505             p_root = p_root->p_parent;
506         }
507
508         p_found = FindObjectName( p_root, psz_name,
509                                  (i_mode & ~0x000f)|FIND_CHILD );
510         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
511         {
512             p_found = FindObjectName( VLC_OBJECT( p_this->p_libvlc ),
513                                       psz_name, (i_mode & ~0x000f)|FIND_CHILD );
514         }
515     }
516     else
517     {
518         p_found = FindObjectName( p_this, psz_name, i_mode );
519     }
520
521     libvlc_unlock (p_this->p_libvlc);
522     return p_found;
523 }
524
525 /**
526  * Increment an object reference counter.
527  */
528 void * __vlc_object_hold( vlc_object_t *p_this )
529 {
530     vlc_object_internals_t *internals = vlc_internals( p_this );
531
532     vlc_spin_lock( &internals->ref_spin );
533     /* Avoid obvious freed object uses */
534     assert( internals->i_refcount > 0 );
535     /* Increment the counter */
536     internals->i_refcount++;
537     vlc_spin_unlock( &internals->ref_spin );
538     return p_this;
539 }
540
541 /*****************************************************************************
542  * Decrement an object refcount
543  * And destroy the object if its refcount reach zero.
544  *****************************************************************************/
545 void __vlc_object_release( vlc_object_t *p_this )
546 {
547     vlc_object_internals_t *internals = vlc_internals( p_this );
548     vlc_object_t *parent = NULL;
549     bool b_should_destroy;
550
551     vlc_spin_lock( &internals->ref_spin );
552     assert( internals->i_refcount > 0 );
553
554     if( internals->i_refcount > 1 )
555     {
556         /* Fast path */
557         /* There are still other references to the object */
558         internals->i_refcount--;
559         vlc_spin_unlock( &internals->ref_spin );
560         return;
561     }
562     vlc_spin_unlock( &internals->ref_spin );
563
564     /* Slow path */
565     /* Remember that we cannot hold the spin while waiting on the mutex */
566     libvlc_lock (p_this->p_libvlc);
567     /* Take the spin again. Note that another thread may have held the
568      * object in the (very short) mean time. */
569     vlc_spin_lock( &internals->ref_spin );
570     b_should_destroy = --internals->i_refcount == 0;
571     vlc_spin_unlock( &internals->ref_spin );
572
573     if( b_should_destroy )
574     {
575         parent = p_this->p_parent;
576
577 #ifndef NDEBUG
578         if( VLC_OBJECT(p_this->p_libvlc) == p_this )
579         {
580             /* Test for leaks */
581             vlc_object_t *leaked = internals->next;
582             while( leaked != p_this )
583             {
584                 /* We are leaking this object */
585                 fprintf( stderr,
586                          "ERROR: leaking object (%p, type:%s, name:%s)\n",
587                          leaked, leaked->psz_object_type,
588                          leaked->psz_object_name );
589                 /* Dump object to ease debugging */
590                 vlc_object_dump( leaked );
591                 fflush(stderr);
592                 leaked = vlc_internals (leaked)->next;
593             }
594
595             if( internals->next != p_this )
596                 /* Dump libvlc object to ease debugging */
597                 vlc_object_dump( p_this );
598         }
599 #endif
600         /* Remove the object from object list
601          * so that it cannot be encountered by vlc_object_get() */
602         vlc_internals (internals->next)->prev = internals->prev;
603         vlc_internals (internals->prev)->next = internals->next;
604
605         if (parent)
606             /* Detach from parent to protect against FIND_CHILDREN */
607             vlc_object_detach_unlocked (p_this);
608
609         /* We have no children */
610         assert (internals->i_children == 0);
611     }
612     libvlc_unlock (p_this->p_libvlc);
613
614     if( b_should_destroy )
615     {
616         int canc;
617
618         canc = vlc_savecancel ();
619         vlc_object_destroy( p_this );
620         vlc_restorecancel (canc);
621         if (parent)
622             vlc_object_release (parent);
623     }
624 }
625
626 /**
627  ****************************************************************************
628  * attach object to a parent object
629  *****************************************************************************
630  * This function sets p_this as a child of p_parent, and p_parent as a parent
631  * of p_this. This link can be undone using vlc_object_detach.
632  *****************************************************************************/
633 void __vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent )
634 {
635     if( !p_this ) return;
636
637     vlc_object_hold (p_parent);
638     libvlc_lock (p_this->p_libvlc);
639
640     /* Attach the parent to its child */
641     assert (!p_this->p_parent);
642     p_this->p_parent = p_parent;
643
644     /* Attach the child to its parent */
645     vlc_object_internals_t *priv = vlc_internals( p_parent );
646     INSERT_ELEM( priv->pp_children, priv->i_children, priv->i_children,
647                  p_this );
648     libvlc_unlock (p_this->p_libvlc);
649 }
650
651
652 static void vlc_object_detach_unlocked (vlc_object_t *p_this)
653 {
654     if (p_this->p_parent == NULL)
655         return;
656
657     vlc_object_internals_t *priv = vlc_internals( p_this->p_parent );
658
659     int i_index, i;
660
661     /* Remove p_this's parent */
662     p_this->p_parent = NULL;
663
664     /* Remove all of p_parent's children which are p_this */
665     for( i_index = priv->i_children ; i_index-- ; )
666     {
667         if( priv->pp_children[i_index] == p_this )
668         {
669             priv->i_children--;
670             for( i = i_index ; i < priv->i_children ; i++ )
671                 priv->pp_children[i] = priv->pp_children[i+1];
672         }
673     }
674
675     if( priv->i_children )
676     {
677         vlc_object_t **pp_children = (vlc_object_t **)
678             realloc( priv->pp_children,
679                      priv->i_children * sizeof(vlc_object_t *) );
680         if( pp_children )
681             priv->pp_children = pp_children;
682     }
683     else
684     {
685         /* Special case - don't realloc() to zero to avoid leaking */
686         free( priv->pp_children );
687         priv->pp_children = NULL;
688     }
689 }
690
691
692 /**
693  ****************************************************************************
694  * detach object from its parent
695  *****************************************************************************
696  * This function removes all links between an object and its parent.
697  *****************************************************************************/
698 void __vlc_object_detach( vlc_object_t *p_this )
699 {
700     vlc_object_t *p_parent;
701     if( !p_this ) return;
702
703     libvlc_lock (p_this->p_libvlc);
704     p_parent = p_this->p_parent;
705     if (p_parent)
706         vlc_object_detach_unlocked( p_this );
707     libvlc_unlock (p_this->p_libvlc);
708
709     if (p_parent)
710         vlc_object_release (p_parent);
711 }
712
713
714 /**
715  ****************************************************************************
716  * find a list typed objects and increment their refcount
717  *****************************************************************************
718  * This function recursively looks for a given object type. i_mode can be one
719  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
720  *****************************************************************************/
721 vlc_list_t * vlc_list_find( vlc_object_t *p_this, int i_type, int i_mode )
722 {
723     vlc_list_t *p_list;
724     int i_count = 0;
725
726     /* Look for the objects */
727     switch( i_mode & 0x000f )
728     {
729     case FIND_ANYWHERE:
730         return vlc_list_find (VLC_OBJECT(p_this->p_libvlc), i_type, FIND_CHILD);
731
732     case FIND_CHILD:
733         libvlc_lock (p_this->p_libvlc);
734         i_count = CountChildren( p_this, i_type );
735         p_list = NewList( i_count );
736
737         /* Check allocation was successful */
738         if( p_list->i_count != i_count )
739         {
740             libvlc_unlock (p_this->p_libvlc);
741             p_list->i_count = 0;
742             break;
743         }
744
745         p_list->i_count = 0;
746         ListChildren( p_list, p_this, i_type );
747         libvlc_unlock (p_this->p_libvlc);
748         break;
749
750     default:
751         msg_Err( p_this, "unimplemented!" );
752         p_list = NewList( 0 );
753         break;
754     }
755
756     return p_list;
757 }
758
759 /**
760  * Gets the list of children of an objects, and increment their reference
761  * count.
762  * @return a list (possibly empty) or NULL in case of error.
763  */
764 vlc_list_t *__vlc_list_children( vlc_object_t *obj )
765 {
766     vlc_list_t *l;
767     vlc_object_internals_t *priv = vlc_internals( obj );
768
769     libvlc_lock (obj->p_libvlc);
770     l = NewList( priv->i_children );
771     for (int i = 0; i < l->i_count; i++)
772     {
773         vlc_object_hold( priv->pp_children[i] );
774         l->p_values[i].p_object = priv->pp_children[i];
775     }
776     libvlc_unlock (obj->p_libvlc);
777     return l;
778 }
779
780 /*****************************************************************************
781  * DumpCommand: print the current vlc structure
782  *****************************************************************************
783  * This function prints either an ASCII tree showing the connections between
784  * vlc objects, and additional information such as their refcount, thread ID,
785  * etc. (command "tree"), or the same data as a simple list (command "list").
786  *****************************************************************************/
787 static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd,
788                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
789 {
790     libvlc_int_t *p_libvlc = p_this->p_libvlc;
791
792     (void)oldval; (void)p_data;
793     if( *psz_cmd == 'l' )
794     {
795         vlc_object_t *cur = VLC_OBJECT (p_libvlc);
796
797         libvlc_lock (p_this->p_libvlc);
798         do
799         {
800             PrintObject (cur, "");
801             cur = vlc_internals (cur)->next;
802         }
803         while (cur != VLC_OBJECT(p_libvlc));
804         libvlc_unlock (p_this->p_libvlc);
805     }
806     else
807     {
808         vlc_object_t *p_object = NULL;
809
810         if( *newval.psz_string )
811         {
812             /* try using the object's name to find it */
813             p_object = vlc_object_find_name( p_this, newval.psz_string,
814                                              FIND_ANYWHERE );
815             if( !p_object )
816             {
817                 return VLC_ENOOBJ;
818             }
819         }
820
821         libvlc_lock (p_this->p_libvlc);
822         if( *psz_cmd == 't' )
823         {
824             char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
825
826             if( !p_object )
827                 p_object = VLC_OBJECT(p_this->p_libvlc);
828
829             psz_foo[0] = '|';
830             DumpStructure( p_object, 0, psz_foo );
831         }
832         else if( *psz_cmd == 'v' )
833         {
834             int i;
835
836             if( !p_object )
837                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
838
839             PrintObject( p_object, "" );
840
841             if( !vlc_internals( p_object )->i_vars )
842                 printf( " `-o No variables\n" );
843             for( i = 0; i < vlc_internals( p_object )->i_vars; i++ )
844             {
845                 variable_t *p_var = vlc_internals( p_object )->p_vars + i;
846
847                 const char *psz_type = "unknown";
848                 switch( p_var->i_type & VLC_VAR_TYPE )
849                 {
850 #define MYCASE( type, nice )                \
851                     case VLC_VAR_ ## type:  \
852                         psz_type = nice;    \
853                         break;
854                     MYCASE( VOID, "void" );
855                     MYCASE( BOOL, "bool" );
856                     MYCASE( INTEGER, "integer" );
857                     MYCASE( HOTKEY, "hotkey" );
858                     MYCASE( STRING, "string" );
859                     MYCASE( MODULE, "module" );
860                     MYCASE( FILE, "file" );
861                     MYCASE( DIRECTORY, "directory" );
862                     MYCASE( VARIABLE, "variable" );
863                     MYCASE( FLOAT, "float" );
864                     MYCASE( TIME, "time" );
865                     MYCASE( ADDRESS, "address" );
866                     MYCASE( MUTEX, "mutex" );
867                     MYCASE( LIST, "list" );
868 #undef MYCASE
869                 }
870                 printf( " %c-o \"%s\" (%s",
871                         i + 1 == vlc_internals( p_object )->i_vars ? '`' : '|',
872                         p_var->psz_name, psz_type );
873                 if( p_var->psz_text )
874                     printf( ", %s", p_var->psz_text );
875                 printf( ")" );
876                 if( p_var->i_type & VLC_VAR_HASCHOICE )
877                     printf( ", has choices" );
878                 if( p_var->i_type & VLC_VAR_ISCOMMAND )
879                     printf( ", command" );
880                 if( p_var->i_entries )
881                     printf( ", %d callbacks", p_var->i_entries );
882                 switch( p_var->i_type & VLC_VAR_CLASS )
883                 {
884                     case VLC_VAR_VOID:
885                     case VLC_VAR_MUTEX:
886                         break;
887                     case VLC_VAR_BOOL:
888                         printf( ": %s", p_var->val.b_bool ? "true" : "false" );
889                         break;
890                     case VLC_VAR_INTEGER:
891                         printf( ": %d", p_var->val.i_int );
892                         break;
893                     case VLC_VAR_STRING:
894                         printf( ": \"%s\"", p_var->val.psz_string );
895                         break;
896                     case VLC_VAR_FLOAT:
897                         printf( ": %f", p_var->val.f_float );
898                         break;
899                     case VLC_VAR_TIME:
900                         printf( ": %"PRIi64, (int64_t)p_var->val.i_time );
901                         break;
902                     case VLC_VAR_ADDRESS:
903                         printf( ": %p", p_var->val.p_address );
904                         break;
905                     case VLC_VAR_LIST:
906                         printf( ": TODO" );
907                         break;
908                 }
909                 printf( "\n" );
910             }
911         }
912         libvlc_unlock (p_this->p_libvlc);
913
914         if( *newval.psz_string )
915         {
916             vlc_object_release( p_object );
917         }
918     }
919
920     return VLC_SUCCESS;
921 }
922
923 /*****************************************************************************
924  * vlc_list_release: free a list previously allocated by vlc_list_find
925  *****************************************************************************
926  * This function decreases the refcount of all objects in the list and
927  * frees the list.
928  *****************************************************************************/
929 void vlc_list_release( vlc_list_t *p_list )
930 {
931     int i_index;
932
933     for( i_index = 0; i_index < p_list->i_count; i_index++ )
934     {
935         vlc_object_release( p_list->p_values[i_index].p_object );
936     }
937
938     free( p_list->p_values );
939     free( p_list );
940 }
941
942 /*****************************************************************************
943  * dump an object. (Debug function)
944  *****************************************************************************/
945 #ifndef NDEBUG
946 static void vlc_object_dump( vlc_object_t *p_this )
947 {
948     char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
949     psz_foo[0] = '|';
950
951     DumpStructure( p_this, 0, psz_foo );
952 }
953 #endif
954
955 /* Following functions are local */
956
957 static vlc_object_t * FindObject( vlc_object_t *p_this, int i_type, int i_mode )
958 {
959     int i;
960     vlc_object_t *p_tmp;
961
962     switch( i_mode & 0x000f )
963     {
964     case FIND_PARENT:
965         p_tmp = p_this->p_parent;
966         if( p_tmp )
967         {
968             if( vlc_internals( p_tmp )->i_object_type == i_type )
969             {
970                 vlc_object_hold( p_tmp );
971                 return p_tmp;
972             }
973             else
974             {
975                 return FindObject( p_tmp, i_type, i_mode );
976             }
977         }
978         break;
979
980     case FIND_CHILD:
981         for( i = vlc_internals( p_this )->i_children; i--; )
982         {
983             p_tmp = vlc_internals( p_this )->pp_children[i];
984             if( vlc_internals( p_tmp )->i_object_type == i_type )
985             {
986                 vlc_object_hold( p_tmp );
987                 return p_tmp;
988             }
989             else if( vlc_internals( p_tmp )->i_children )
990             {
991                 p_tmp = FindObject( p_tmp, i_type, i_mode );
992                 if( p_tmp )
993                 {
994                     return p_tmp;
995                 }
996             }
997         }
998         break;
999
1000     case FIND_ANYWHERE:
1001         /* Handled in vlc_object_find */
1002         break;
1003     }
1004
1005     return NULL;
1006 }
1007
1008 static vlc_object_t * FindObjectName( vlc_object_t *p_this,
1009                                       const char *psz_name,
1010                                       int i_mode )
1011 {
1012     int i;
1013     vlc_object_t *p_tmp;
1014
1015     switch( i_mode & 0x000f )
1016     {
1017     case FIND_PARENT:
1018         p_tmp = p_this->p_parent;
1019         if( p_tmp )
1020         {
1021             if( p_tmp->psz_object_name
1022                 && !strcmp( p_tmp->psz_object_name, psz_name ) )
1023             {
1024                 vlc_object_hold( p_tmp );
1025                 return p_tmp;
1026             }
1027             else
1028             {
1029                 return FindObjectName( p_tmp, psz_name, i_mode );
1030             }
1031         }
1032         break;
1033
1034     case FIND_CHILD:
1035         for( i = vlc_internals( p_this )->i_children; i--; )
1036         {
1037             p_tmp = vlc_internals( p_this )->pp_children[i];
1038             if( p_tmp->psz_object_name
1039                 && !strcmp( p_tmp->psz_object_name, psz_name ) )
1040             {
1041                 vlc_object_hold( p_tmp );
1042                 return p_tmp;
1043             }
1044             else if( vlc_internals( p_tmp )->i_children )
1045             {
1046                 p_tmp = FindObjectName( p_tmp, psz_name, i_mode );
1047                 if( p_tmp )
1048                 {
1049                     return p_tmp;
1050                 }
1051             }
1052         }
1053         break;
1054
1055     case FIND_ANYWHERE:
1056         /* Handled in vlc_object_find */
1057         break;
1058     }
1059
1060     return NULL;
1061 }
1062
1063
1064 static void PrintObject( vlc_object_t *p_this, const char *psz_prefix )
1065 {
1066     char psz_children[20], psz_refcount[20], psz_thread[30], psz_name[50],
1067          psz_parent[20];
1068
1069     int canc = vlc_savecancel ();
1070     memset( &psz_name, 0, sizeof(psz_name) );
1071     if( p_this->psz_object_name )
1072     {
1073         snprintf( psz_name, 49, " \"%s\"", p_this->psz_object_name );
1074         if( psz_name[48] )
1075             psz_name[48] = '\"';
1076     }
1077
1078     psz_children[0] = '\0';
1079     switch( vlc_internals( p_this )->i_children )
1080     {
1081         case 0:
1082             break;
1083         case 1:
1084             strcpy( psz_children, ", 1 child" );
1085             break;
1086         default:
1087             snprintf( psz_children, 19, ", %i children",
1088                       vlc_internals( p_this )->i_children );
1089             break;
1090     }
1091
1092     psz_refcount[0] = '\0';
1093     if( vlc_internals( p_this )->i_refcount > 0 )
1094         snprintf( psz_refcount, 19, ", refcount %u",
1095                   vlc_internals( p_this )->i_refcount );
1096
1097     psz_thread[0] = '\0';
1098     if( vlc_internals( p_this )->b_thread )
1099         snprintf( psz_thread, 29, " (thread %lu)",
1100                   (unsigned long)vlc_internals( p_this )->thread_id );
1101
1102     psz_parent[0] = '\0';
1103     if( p_this->p_parent )
1104         snprintf( psz_parent, 19, ", parent %p", p_this->p_parent );
1105
1106     printf( " %so %p %s%s%s%s%s%s\n", psz_prefix,
1107             p_this, p_this->psz_object_type,
1108             psz_name, psz_thread, psz_refcount, psz_children,
1109             psz_parent );
1110     vlc_restorecancel (canc);
1111 }
1112
1113 static void DumpStructure( vlc_object_t *p_this, int i_level, char *psz_foo )
1114 {
1115     int i;
1116     char i_back = psz_foo[i_level];
1117     psz_foo[i_level] = '\0';
1118
1119     PrintObject( p_this, psz_foo );
1120
1121     psz_foo[i_level] = i_back;
1122
1123     if( i_level / 2 >= MAX_DUMPSTRUCTURE_DEPTH )
1124     {
1125         msg_Warn( p_this, "structure tree is too deep" );
1126         return;
1127     }
1128
1129     for( i = 0 ; i < vlc_internals( p_this )->i_children ; i++ )
1130     {
1131         if( i_level )
1132         {
1133             psz_foo[i_level-1] = ' ';
1134
1135             if( psz_foo[i_level-2] == '`' )
1136             {
1137                 psz_foo[i_level-2] = ' ';
1138             }
1139         }
1140
1141         if( i == vlc_internals( p_this )->i_children - 1 )
1142         {
1143             psz_foo[i_level] = '`';
1144         }
1145         else
1146         {
1147             psz_foo[i_level] = '|';
1148         }
1149
1150         psz_foo[i_level+1] = '-';
1151         psz_foo[i_level+2] = '\0';
1152
1153         DumpStructure( vlc_internals( p_this )->pp_children[i], i_level + 2,
1154                        psz_foo );
1155     }
1156 }
1157
1158 static vlc_list_t * NewList( int i_count )
1159 {
1160     vlc_list_t * p_list = (vlc_list_t *)malloc( sizeof( vlc_list_t ) );
1161     if( p_list == NULL )
1162     {
1163         return NULL;
1164     }
1165
1166     p_list->i_count = i_count;
1167
1168     if( i_count == 0 )
1169     {
1170         p_list->p_values = NULL;
1171         return p_list;
1172     }
1173
1174     p_list->p_values = malloc( i_count * sizeof( vlc_value_t ) );
1175     if( p_list->p_values == NULL )
1176     {
1177         p_list->i_count = 0;
1178         return p_list;
1179     }
1180
1181     return p_list;
1182 }
1183
1184 static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object,
1185                          int i_index )
1186 {
1187     if( p_list == NULL || i_index >= p_list->i_count )
1188     {
1189         return;
1190     }
1191
1192     vlc_object_hold( p_object );
1193
1194     p_list->p_values[i_index].p_object = p_object;
1195
1196     return;
1197 }
1198
1199 /*static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object )
1200 {
1201     if( p_list == NULL )
1202     {
1203         return;
1204     }
1205
1206     p_list->p_values = realloc( p_list->p_values, (p_list->i_count + 1)
1207                                 * sizeof( vlc_value_t ) );
1208     if( p_list->p_values == NULL )
1209     {
1210         p_list->i_count = 0;
1211         return;
1212     }
1213
1214     vlc_object_hold( p_object );
1215
1216     p_list->p_values[p_list->i_count].p_object = p_object;
1217     p_list->i_count++;
1218
1219     return;
1220 }*/
1221
1222 static int CountChildren( vlc_object_t *p_this, int i_type )
1223 {
1224     vlc_object_t *p_tmp;
1225     int i, i_count = 0;
1226
1227     for( i = 0; i < vlc_internals( p_this )->i_children; i++ )
1228     {
1229         p_tmp = vlc_internals( p_this )->pp_children[i];
1230
1231         if( vlc_internals( p_tmp )->i_object_type == i_type )
1232         {
1233             i_count++;
1234         }
1235         i_count += CountChildren( p_tmp, i_type );
1236     }
1237
1238     return i_count;
1239 }
1240
1241 static void ListChildren( vlc_list_t *p_list, vlc_object_t *p_this, int i_type )
1242 {
1243     vlc_object_t *p_tmp;
1244     int i;
1245
1246     for( i = 0; i < vlc_internals( p_this )->i_children; i++ )
1247     {
1248         p_tmp = vlc_internals( p_this )->pp_children[i];
1249
1250         if( vlc_internals( p_tmp )->i_object_type == i_type )
1251             ListReplace( p_list, p_tmp, p_list->i_count++ );
1252
1253         ListChildren( p_list, p_tmp, i_type );
1254     }
1255 }