]> git.sesse.net Git - vlc/blob - src/misc/objects.c
Hide object reference counter.
[vlc] / src / misc / objects.c
1 /*****************************************************************************
2  * objects.c: vlc_object_t handling
3  *****************************************************************************
4  * Copyright (C) 2004-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /**
25  * \file
26  * This file contains the functions to handle the vlc_object_t type
27  */
28
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #include <vlc/vlc.h>
34
35 #include "../libvlc.h"
36 #include <vlc_vout.h>
37 #include <vlc_aout.h>
38 #include "audio_output/aout_internal.h"
39
40 #include <vlc_access.h>
41 #include <vlc_demux.h>
42 #include <vlc_stream.h>
43
44 #include <vlc_sout.h>
45 #include "stream_output/stream_output.h"
46
47 #include "vlc_playlist.h"
48 #include "vlc_interface.h"
49 #include "vlc_codec.h"
50 #include "vlc_filter.h"
51
52 #include "vlc_httpd.h"
53 #include "vlc_vlm.h"
54 #include "input/vlm_internal.h"
55 #include "vlc_vod.h"
56 #include "vlc_tls.h"
57 #include "vlc_xml.h"
58 #include "vlc_osd.h"
59 #include "vlc_meta.h"
60
61 #include "variables.h"
62
63 /*****************************************************************************
64  * Local prototypes
65  *****************************************************************************/
66 static int  DumpCommand( vlc_object_t *, char const *,
67                          vlc_value_t, vlc_value_t, void * );
68
69 static vlc_object_t * FindObject    ( vlc_object_t *, int, int );
70 static vlc_object_t * FindObjectName( vlc_object_t *, const char *, int );
71 static void           DetachObject  ( vlc_object_t * );
72 static void           PrintObject   ( vlc_object_t *, const char * );
73 static void           DumpStructure ( vlc_object_t *, int, char * );
74 static int            FindIndex     ( vlc_object_t *, vlc_object_t **, int );
75 static void           SetAttachment ( vlc_object_t *, vlc_bool_t );
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 /*****************************************************************************
84  * Local structure lock
85  *****************************************************************************/
86 static vlc_mutex_t    structure_lock;
87 static vlc_object_internals_t global_internals;
88
89 vlc_object_t *vlc_custom_create( vlc_object_t *p_this, size_t i_size,
90                                  int i_type, const char *psz_type )
91 {
92     vlc_object_t *p_new;
93     vlc_object_internals_t *p_priv;
94
95     if( i_type == VLC_OBJECT_GLOBAL )
96     {
97         p_new = p_this;
98         p_priv = &global_internals;
99         memset( p_priv, 0, sizeof( *p_priv ) );
100     }
101     else
102     {
103         p_priv = calloc( 1, sizeof( *p_priv ) + i_size );
104         if( p_priv == NULL )
105             return NULL;
106
107         p_new = (vlc_object_t *)(p_priv + 1);
108     }
109
110     p_new->p_internals = p_priv;
111     p_new->i_object_type = i_type;
112     p_new->psz_object_type = psz_type;
113
114     p_new->psz_object_name = NULL;
115
116     p_new->b_die = VLC_FALSE;
117     p_new->b_error = VLC_FALSE;
118     p_new->b_dead = VLC_FALSE;
119     p_priv->b_attached = VLC_FALSE;
120     p_new->b_force = VLC_FALSE;
121
122     p_new->psz_header = NULL;
123
124     if( p_this->i_flags & OBJECT_FLAGS_NODBG )
125         p_new->i_flags |= OBJECT_FLAGS_NODBG;
126     if( p_this->i_flags & OBJECT_FLAGS_QUIET )
127         p_new->i_flags |= OBJECT_FLAGS_QUIET;
128     if( p_this->i_flags & OBJECT_FLAGS_NOINTERACT )
129         p_new->i_flags |= OBJECT_FLAGS_NOINTERACT;
130
131     p_priv->p_vars = calloc( sizeof( variable_t ), 16 );
132
133     if( !p_priv->p_vars )
134     {
135         if( i_type != VLC_OBJECT_GLOBAL )
136             free( p_priv );
137         return NULL;
138     }
139
140     if( i_type == VLC_OBJECT_GLOBAL )
141     {
142         /* If i_type is global, then p_new is actually p_libvlc_global */
143         libvlc_global_data_t *p_libvlc_global = (libvlc_global_data_t *)p_new;
144         p_new->p_libvlc = NULL;
145
146         p_libvlc_global->i_counter = 0;
147         p_new->i_object_id = 0;
148
149         p_libvlc_global->i_objects = 1;
150         p_libvlc_global->pp_objects = malloc( sizeof(vlc_object_t *) );
151         p_libvlc_global->pp_objects[0] = p_new;
152         p_priv->b_attached = VLC_TRUE;
153     }
154     else
155     {
156         libvlc_global_data_t *p_libvlc_global = vlc_global();
157         if( i_type == VLC_OBJECT_LIBVLC )
158         {
159             p_new->p_libvlc = (libvlc_int_t*)p_new;
160             p_priv->b_attached = VLC_TRUE;
161         }
162         else
163         {
164             p_new->p_libvlc = p_this->p_libvlc;
165         }
166
167         vlc_mutex_lock( &structure_lock );
168
169         p_libvlc_global->i_counter++;
170         p_new->i_object_id = p_libvlc_global->i_counter;
171
172         /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
173          * useless to try and recover anything if pp_objects gets smashed. */
174         TAB_APPEND( p_libvlc_global->i_objects, p_libvlc_global->pp_objects,
175                     p_new );
176
177         vlc_mutex_unlock( &structure_lock );
178     }
179
180     p_priv->i_refcount = 0;
181     p_new->p_parent = NULL;
182     p_new->pp_children = NULL;
183     p_new->i_children = 0;
184
185     p_new->p_private = NULL;
186
187     /* Initialize mutexes and condvars */
188     vlc_mutex_init( p_new, &p_new->object_lock );
189     vlc_cond_init( p_new, &p_new->object_wait );
190     vlc_mutex_init( p_new, &p_priv->var_lock );
191
192     if( i_type == VLC_OBJECT_GLOBAL )
193     {
194         vlc_mutex_init( p_new, &structure_lock );
195     }
196
197     if( i_type == VLC_OBJECT_LIBVLC )
198     {
199         var_Create( p_new, "list", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
200         var_AddCallback( p_new, "list", DumpCommand, NULL );
201         var_Create( p_new, "tree", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
202         var_AddCallback( p_new, "tree", DumpCommand, NULL );
203         var_Create( p_new, "vars", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
204         var_AddCallback( p_new, "vars", DumpCommand, NULL );
205     }
206
207     return p_new;
208 }
209
210
211 /**
212  * Allocates and initializes a vlc object.
213  *
214  * @param i_type known object type (all of them are negative integer values),
215  *               or object byte size (always positive).
216  *
217  * @return the new object, or NULL on error.
218  */
219 void * __vlc_object_create( vlc_object_t *p_this, int i_type )
220 {
221     const char   * psz_type;
222     size_t         i_size;
223
224     switch( i_type )
225     {
226         case VLC_OBJECT_GLOBAL:
227             i_size = sizeof(libvlc_global_data_t);
228             psz_type = "global";
229             break;
230         case VLC_OBJECT_LIBVLC:
231             i_size = sizeof(libvlc_int_t);
232             psz_type = "libvlc";
233             break;
234         case VLC_OBJECT_INTF:
235             i_size = sizeof(intf_thread_t);
236             psz_type = "interface";
237             break;
238         case VLC_OBJECT_DIALOGS:
239             i_size = sizeof(intf_thread_t);
240             psz_type = "dialogs";
241             break;
242         case VLC_OBJECT_PLAYLIST:
243             i_size = sizeof(playlist_t);
244             psz_type = "playlist";
245             break;
246         case VLC_OBJECT_SD:
247             i_size = sizeof(services_discovery_t);
248             psz_type = "services discovery";
249             break;
250         case VLC_OBJECT_INPUT:
251             i_size = sizeof(input_thread_t);
252             psz_type = "input";
253             break;
254         case VLC_OBJECT_DEMUX:
255             i_size = sizeof(demux_t);
256             psz_type = "demux";
257             break;
258         case VLC_OBJECT_ACCESS:
259             i_size = sizeof(access_t);
260             psz_type = "access";
261             break;
262         case VLC_OBJECT_DECODER:
263             i_size = sizeof(decoder_t);
264             psz_type = "decoder";
265             break;
266         case VLC_OBJECT_PACKETIZER:
267             i_size = sizeof(decoder_t);
268             psz_type = "packetizer";
269             break;
270         case VLC_OBJECT_ENCODER:
271             i_size = sizeof(encoder_t);
272             psz_type = "encoder";
273             break;
274         case VLC_OBJECT_FILTER:
275             i_size = sizeof(filter_t);
276             psz_type = "filter";
277             break;
278         case VLC_OBJECT_VOUT:
279             i_size = sizeof(vout_thread_t);
280             psz_type = "video output";
281             break;
282         case VLC_OBJECT_SPU:
283             i_size = sizeof(spu_t);
284             psz_type = "subpicture";
285             break;
286         case VLC_OBJECT_AOUT:
287             i_size = sizeof(aout_instance_t);
288             psz_type = "audio output";
289             break;
290         case VLC_OBJECT_SOUT:
291             i_size = sizeof(sout_instance_t);
292             psz_type = "stream output";
293             break;
294         case VLC_OBJECT_VLM:
295             i_size = sizeof( vlm_t );
296             psz_type = "vlm dameon";
297             break;
298         case VLC_OBJECT_VOD:
299             i_size = sizeof( vod_t );
300             psz_type = "vod server";
301             break;
302         case VLC_OBJECT_XML:
303             i_size = sizeof( xml_t );
304             psz_type = "xml";
305             break;
306         case VLC_OBJECT_OPENGL:
307             i_size = sizeof( vout_thread_t );
308             psz_type = "opengl";
309             break;
310         case VLC_OBJECT_ANNOUNCE:
311             i_size = sizeof( announce_handler_t );
312             psz_type = "announce";
313             break;
314         case VLC_OBJECT_META_ENGINE:
315             i_size = sizeof( meta_engine_t );
316             psz_type = "meta engine";
317             break;
318         case VLC_OBJECT_OSDMENU:
319             i_size = sizeof( osd_menu_t );
320             psz_type = "osd menu";
321             break;
322         default:
323             i_size = i_type > (int)sizeof(vlc_object_t)
324                          ? i_type : (int)sizeof(vlc_object_t);
325             i_type = VLC_OBJECT_GENERIC;
326             psz_type = "generic";
327             break;
328     }
329
330     return vlc_custom_create( p_this, i_size, i_type, psz_type );
331 }
332
333
334 /**
335  ****************************************************************************
336  * Destroy a vlc object
337  *
338  * This function destroys an object that has been previously allocated with
339  * vlc_object_create. The object's refcount must be zero and it must not be
340  * attached to other objects in any way.
341  *****************************************************************************/
342 void __vlc_object_destroy( vlc_object_t *p_this )
343 {
344     vlc_object_internals_t *p_priv = vlc_internals( p_this );
345     int i_delay = 0;
346
347     if( p_this->i_children )
348     {
349         msg_Err( p_this, "cannot delete object (%i, %s) with children" ,
350                  p_this->i_object_id, p_this->psz_object_name );
351         return;
352     }
353
354     if( p_this->p_parent )
355     {
356         msg_Err( p_this, "cannot delete object (%i, %s) with a parent",
357                  p_this->i_object_id, p_this->psz_object_name );
358         return;
359     }
360
361     while( p_priv->i_refcount > 0 )
362     {
363         i_delay++;
364
365         /* Don't warn immediately ... 100ms seems OK */
366         if( i_delay == 2 )
367         {
368             msg_Warn( p_this,
369                   "refcount is %u, delaying before deletion (id=%d,type=%d)",
370                   p_priv->i_refcount, p_this->i_object_id,
371                   p_this->i_object_type );
372         }
373         else if( i_delay == 10 )
374         {
375             msg_Err( p_this,
376                   "refcount is %u, delaying again (id=%d,type=%d)",
377                   p_priv->i_refcount, p_this->i_object_id,
378                   p_this->i_object_type );
379         }
380         else if( i_delay == 20 )
381         {
382             msg_Err( p_this,
383                   "waited too long, cancelling destruction (id=%d,type=%d)",
384                   p_this->i_object_id, p_this->i_object_type );
385             return;
386         }
387
388         msleep( 100000 );
389     }
390
391     /* Destroy the associated variables, starting from the end so that
392      * no memmove calls have to be done. */
393     while( p_priv->i_vars )
394     {
395         var_Destroy( p_this, p_priv->p_vars[p_priv->i_vars - 1].psz_name );
396     }
397
398     free( p_priv->p_vars );
399     vlc_mutex_destroy( &p_priv->var_lock );
400
401     if( p_this->psz_header ) free( p_this->psz_header );
402
403     if( p_this->i_object_type == VLC_OBJECT_GLOBAL )
404     {
405         libvlc_global_data_t *p_global = (libvlc_global_data_t *)p_this;
406         /* We are the global object ... no need to lock. */
407         free( p_global->pp_objects );
408         p_global->pp_objects = NULL;
409         p_global->i_objects--;
410
411         vlc_mutex_destroy( &structure_lock );
412     }
413     else
414     {
415         libvlc_global_data_t *p_libvlc_global = vlc_global();
416         int i_index;
417
418         vlc_mutex_lock( &structure_lock );
419
420         /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
421          * useless to try and recover anything if pp_objects gets smashed. */
422         i_index = FindIndex( p_this, p_libvlc_global->pp_objects,
423                              p_libvlc_global->i_objects );
424         REMOVE_ELEM( p_libvlc_global->pp_objects,
425                      p_libvlc_global->i_objects, i_index );
426
427         vlc_mutex_unlock( &structure_lock );
428     }
429
430 #if defined(WIN32) || defined(UNDER_CE)
431     /* if object has an associated thread, close it now */
432     if( p_priv->thread_id.hThread )
433        CloseHandle(p_priv->thread_id.hThread);
434 #endif
435
436     vlc_mutex_destroy( &p_this->object_lock );
437     vlc_cond_destroy( &p_this->object_wait );
438
439     /* global is not dynamically allocated by vlc_object_create */
440     if( p_this->i_object_type != VLC_OBJECT_GLOBAL )
441         free( p_priv );
442 }
443
444
445 /** Inter-object signaling */
446
447 void __vlc_object_lock( vlc_object_t *obj )
448 {
449     vlc_mutex_lock( &obj->object_lock );
450 }
451
452 void __vlc_object_unlock( vlc_object_t *obj )
453 {
454     vlc_assert_locked( &obj->object_lock );
455     vlc_mutex_unlock( &obj->object_lock );
456 }
457
458 /**
459  * Waits for the object to be signaled (using vlc_object_signal()).
460  * If the object already has a signal pending, this function will return
461  * immediately. It is asserted that the caller holds the object lock.
462  *
463  * @return true if the object is dying and should terminate.
464  */
465 vlc_bool_t __vlc_object_wait( vlc_object_t *obj )
466 {
467     vlc_assert_locked( &obj->object_lock );
468     vlc_cond_wait( &obj->object_wait, &obj->object_lock );
469     return obj->b_die;
470 }
471
472
473 /**
474  * Waits for the object to be signaled (using vlc_object_signal()), or for
475  * a timer to expire.
476  * If the object already has a signal pending, this function will return
477  * immediately. It is asserted that the caller holds the object lock.
478  *
479  * @return negative if the object is dying and should terminate,
480  * positive if the the object has been signaled but is not dying,
481  * 0 if timeout has been reached.
482  */
483 int __vlc_object_timedwait( vlc_object_t *obj, mtime_t deadline )
484 {
485     int v;
486
487     vlc_assert_locked( &obj->object_lock );
488     v = vlc_cond_timedwait( &obj->object_wait, &obj->object_lock, deadline );
489     if( v == 0 ) /* signaled */
490         return obj->b_die ? -1 : 1;
491     return 0;
492 }
493
494
495 /**
496  * Signals an object for which the lock is held.
497  */
498 void __vlc_object_signal_unlocked( vlc_object_t *obj )
499 {
500     vlc_assert_locked( &obj->object_lock );
501     vlc_cond_signal( &obj->object_wait );
502 }
503
504
505 /**
506  * Requests termination of an object.
507  * If the object is LibVLC, also request to terminate all its children.
508  */
509 void __vlc_object_kill( vlc_object_t *p_this )
510 {
511     vlc_mutex_lock( &p_this->object_lock );
512
513     if( p_this->i_object_type == VLC_OBJECT_LIBVLC )
514         for( int i = 0; i < p_this->i_children ; i++ )
515             vlc_object_kill( p_this->pp_children[i] );
516
517     p_this->b_die = VLC_TRUE;
518     vlc_object_signal_unlocked( p_this );
519     vlc_mutex_unlock( &p_this->object_lock );
520 }
521
522
523 /**
524  * find an object given its ID
525  *
526  * This function looks for the object whose i_object_id field is i_id. We
527  * use a dichotomy so that lookups are in log2(n).
528  *****************************************************************************/
529 void * __vlc_object_get( vlc_object_t *p_this, int i_id )
530 {
531     int i_max, i_middle;
532     vlc_object_t **pp_objects;
533     libvlc_global_data_t *p_libvlc_global = vlc_global();
534
535     vlc_mutex_lock( &structure_lock );
536
537     pp_objects = p_libvlc_global->pp_objects;
538
539     /* Perform our dichotomy */
540     for( i_max = p_libvlc_global->i_objects - 1 ; ; )
541     {
542         i_middle = i_max / 2;
543
544         if( pp_objects[i_middle]->i_object_id > i_id )
545         {
546             i_max = i_middle;
547         }
548         else if( pp_objects[i_middle]->i_object_id < i_id )
549         {
550             if( i_middle )
551             {
552                 pp_objects += i_middle;
553                 i_max -= i_middle;
554             }
555             else
556             {
557                 /* This happens when there are only two remaining objects */
558                 if( pp_objects[i_middle+1]->i_object_id == i_id )
559                 {
560                     vlc_mutex_unlock( &structure_lock );
561                     pp_objects[i_middle+1]->p_internals->i_refcount++;
562                     return pp_objects[i_middle+1];
563                 }
564                 break;
565             }
566         }
567         else
568         {
569             vlc_mutex_unlock( &structure_lock );
570             pp_objects[i_middle]->p_internals->i_refcount++;
571             return pp_objects[i_middle];
572         }
573
574         if( i_max == 0 )
575         {
576             /* this means that i_max == i_middle, and since we have already
577              * tested pp_objects[i_middle]), p_found is properly set. */
578             break;
579         }
580     }
581
582     vlc_mutex_unlock( &structure_lock );
583     return NULL;
584 }
585
586 /**
587  ****************************************************************************
588  * find a typed object and increment its refcount
589  *****************************************************************************
590  * This function recursively looks for a given object type. i_mode can be one
591  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
592  *****************************************************************************/
593 void * __vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode )
594 {
595     vlc_object_t *p_found;
596
597     vlc_mutex_lock( &structure_lock );
598
599     /* If we are of the requested type ourselves, don't look further */
600     if( !(i_mode & FIND_STRICT) && p_this->i_object_type == i_type )
601     {
602         p_this->p_internals->i_refcount++;
603         vlc_mutex_unlock( &structure_lock );
604         return p_this;
605     }
606
607     /* Otherwise, recursively look for the object */
608     if( (i_mode & 0x000f) == FIND_ANYWHERE )
609     {
610         vlc_object_t *p_root = p_this;
611
612         /* Find the root */
613         while( p_root->p_parent != NULL &&
614                p_root != VLC_OBJECT( p_this->p_libvlc ) )
615         {
616             p_root = p_root->p_parent;
617         }
618
619         p_found = FindObject( p_root, i_type, (i_mode & ~0x000f)|FIND_CHILD );
620         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
621         {
622             p_found = FindObject( VLC_OBJECT( p_this->p_libvlc ),
623                                   i_type, (i_mode & ~0x000f)|FIND_CHILD );
624         }
625     }
626     else
627     {
628         p_found = FindObject( p_this, i_type, i_mode );
629     }
630
631     vlc_mutex_unlock( &structure_lock );
632
633     return p_found;
634 }
635
636 /**
637  ****************************************************************************
638  * find a named object and increment its refcount
639  *****************************************************************************
640  * This function recursively looks for a given object name. i_mode can be one
641  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
642  *****************************************************************************/
643 void * __vlc_object_find_name( vlc_object_t *p_this, const char *psz_name,
644                                int i_mode )
645 {
646     vlc_object_t *p_found;
647
648     vlc_mutex_lock( &structure_lock );
649
650     /* If have the requested name ourselves, don't look further */
651     if( !(i_mode & FIND_STRICT)
652         && p_this->psz_object_name
653         && !strcmp( p_this->psz_object_name, psz_name ) )
654     {
655         p_this->p_internals->i_refcount++;
656         vlc_mutex_unlock( &structure_lock );
657         return p_this;
658     }
659
660     /* Otherwise, recursively look for the object */
661     if( (i_mode & 0x000f) == FIND_ANYWHERE )
662     {
663         vlc_object_t *p_root = p_this;
664
665         /* Find the root */
666         while( p_root->p_parent != NULL &&
667                p_root != VLC_OBJECT( p_this->p_libvlc ) )
668         {
669             p_root = p_root->p_parent;
670         }
671
672         p_found = FindObjectName( p_root, psz_name,
673                                  (i_mode & ~0x000f)|FIND_CHILD );
674         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
675         {
676             p_found = FindObjectName( VLC_OBJECT( p_this->p_libvlc ),
677                                       psz_name, (i_mode & ~0x000f)|FIND_CHILD );
678         }
679     }
680     else
681     {
682         p_found = FindObjectName( p_this, psz_name, i_mode );
683     }
684
685     vlc_mutex_unlock( &structure_lock );
686
687     return p_found;
688 }
689
690 /**
691  ****************************************************************************
692  * increment an object refcount
693  *****************************************************************************/
694 void __vlc_object_yield( vlc_object_t *p_this )
695 {
696     vlc_mutex_lock( &structure_lock );
697     p_this->p_internals->i_refcount++;
698     vlc_mutex_unlock( &structure_lock );
699 }
700
701 static inline void Release( vlc_object_t *obj )
702 {
703     assert( obj->p_internals->i_refcount > 0 );
704     obj->p_internals->i_refcount--;
705 }
706
707 /*****************************************************************************
708  * decrement an object refcount
709  *****************************************************************************/
710 void __vlc_object_release( vlc_object_t *p_this )
711 {
712     vlc_mutex_lock( &structure_lock );
713     Release( p_this );
714     vlc_mutex_unlock( &structure_lock );
715 }
716
717 /**
718  ****************************************************************************
719  * attach object to a parent object
720  *****************************************************************************
721  * This function sets p_this as a child of p_parent, and p_parent as a parent
722  * of p_this. This link can be undone using vlc_object_detach.
723  *****************************************************************************/
724 void __vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent )
725 {
726     if( !p_this ) return;
727
728     vlc_mutex_lock( &structure_lock );
729
730     /* Attach the parent to its child */
731     p_this->p_parent = p_parent;
732
733     /* Attach the child to its parent */
734     INSERT_ELEM( p_parent->pp_children, p_parent->i_children,
735                  p_parent->i_children, p_this );
736
737     /* Climb up the tree to see whether we are connected with the root */
738     if( p_parent->p_internals->b_attached )
739     {
740         SetAttachment( p_this, VLC_TRUE );
741     }
742
743     vlc_mutex_unlock( &structure_lock );
744 }
745
746 /**
747  ****************************************************************************
748  * detach object from its parent
749  *****************************************************************************
750  * This function removes all links between an object and its parent.
751  *****************************************************************************/
752 void __vlc_object_detach( vlc_object_t *p_this )
753 {
754     if( !p_this ) return;
755
756     vlc_mutex_lock( &structure_lock );
757     if( !p_this->p_parent )
758     {
759         msg_Err( p_this, "object is not attached" );
760         vlc_mutex_unlock( &structure_lock );
761         return;
762     }
763
764     /* Climb up the tree to see whether we are connected with the root */
765     if( p_this->p_parent->p_internals->b_attached )
766     {
767         SetAttachment( p_this, VLC_FALSE );
768     }
769
770     DetachObject( p_this );
771     vlc_mutex_unlock( &structure_lock );
772     p_this = NULL;
773 }
774
775 /**
776  ****************************************************************************
777  * find a list typed objects and increment their refcount
778  *****************************************************************************
779  * This function recursively looks for a given object type. i_mode can be one
780  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
781  *****************************************************************************/
782 vlc_list_t * __vlc_list_find( vlc_object_t *p_this, int i_type, int i_mode )
783 {
784     vlc_list_t *p_list;
785     vlc_object_t **pp_current, **pp_end;
786     int i_count = 0, i_index = 0;
787     libvlc_global_data_t *p_libvlc_global = vlc_global();
788
789     vlc_mutex_lock( &structure_lock );
790
791     /* Look for the objects */
792     switch( i_mode & 0x000f )
793     {
794     case FIND_ANYWHERE:
795         pp_current = p_libvlc_global->pp_objects;
796         pp_end = pp_current + p_libvlc_global->i_objects;
797
798         for( ; pp_current < pp_end ; pp_current++ )
799         {
800             if( (*pp_current)->p_internals->b_attached
801                  && (*pp_current)->i_object_type == i_type )
802             {
803                 i_count++;
804             }
805         }
806
807         p_list = NewList( i_count );
808         pp_current = p_libvlc_global->pp_objects;
809
810         for( ; pp_current < pp_end ; pp_current++ )
811         {
812             if( (*pp_current)->p_internals->b_attached
813                  && (*pp_current)->i_object_type == i_type )
814             {
815                 ListReplace( p_list, *pp_current, i_index );
816                 if( i_index < i_count ) i_index++;
817             }
818         }
819     break;
820
821     case FIND_CHILD:
822         i_count = CountChildren( p_this, i_type );
823         p_list = NewList( i_count );
824
825         /* Check allocation was successful */
826         if( p_list->i_count != i_count )
827         {
828             msg_Err( p_this, "list allocation failed!" );
829             p_list->i_count = 0;
830             break;
831         }
832
833         p_list->i_count = 0;
834         ListChildren( p_list, p_this, i_type );
835         break;
836
837     default:
838         msg_Err( p_this, "unimplemented!" );
839         p_list = NewList( 0 );
840         break;
841     }
842
843     vlc_mutex_unlock( &structure_lock );
844
845     return p_list;
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     libvlc_global_data_t *p_libvlc_global = vlc_global();
859
860     (void)oldval; (void)p_data;
861     if( *psz_cmd == 'l' )
862     {
863         vlc_mutex_lock( &structure_lock );
864
865         vlc_object_t **pp_current, **pp_end;
866
867         pp_current = p_libvlc_global->pp_objects;
868         pp_end = pp_current + p_libvlc_global->i_objects;
869
870         for( ; pp_current < pp_end ; pp_current++ )
871         {
872             if( (*pp_current)->p_internals->b_attached )
873             {
874                 PrintObject( *pp_current, "" );
875             }
876             else
877             {
878                 printf( " o %.8i %s (not attached)\n",
879                         (*pp_current)->i_object_id,
880                         (*pp_current)->psz_object_type );
881             }
882         }
883
884         vlc_mutex_unlock( &structure_lock );
885     }
886     else
887     {
888         vlc_object_t *p_object = NULL;
889
890         if( *newval.psz_string )
891         {
892             char *end;
893             int i_id = strtol( newval.psz_string, &end, 0 );
894             if( end != newval.psz_string )
895                 p_object = vlc_object_get( p_this, i_id );
896             else
897             {
898                 /* try using the object's name to find it */
899                 vlc_object_t *p_libvlc = vlc_object_get( p_this, 1 );
900                 if( p_libvlc )
901                 {
902                     /* Look in p_libvlc's children tree */
903                     p_object = vlc_object_find_name( p_libvlc,
904                                                      newval.psz_string,
905                                                      FIND_CHILD );
906                     vlc_object_release( p_libvlc );
907                 }
908                 if( !p_object )
909                 {
910                     /* If it's not in libvlc, look in libvlc_global (== p_this) */
911                     p_object = vlc_object_find_name( p_this,
912                                                      newval.psz_string,
913                                                      FIND_CHILD );
914                 }
915             }
916
917             if( !p_object )
918             {
919                 return VLC_ENOOBJ;
920             }
921         }
922
923         vlc_mutex_lock( &structure_lock );
924
925         if( *psz_cmd == 't' )
926         {
927             char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
928
929             if( !p_object )
930                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
931
932             psz_foo[0] = '|';
933             DumpStructure( p_object, 0, psz_foo );
934         }
935         else if( *psz_cmd == 'v' )
936         {
937             int i;
938
939             if( !p_object )
940                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
941
942             PrintObject( p_object, "" );
943
944             if( !p_object->p_internals->i_vars )
945                 printf( " `-o No variables\n" );
946             for( i = 0; i < p_object->p_internals->i_vars; i++ )
947             {
948                 variable_t *p_var = p_object->p_internals->p_vars + i;
949
950                 const char *psz_type = "unknown";
951                 switch( p_var->i_type & VLC_VAR_TYPE )
952                 {
953 #define MYCASE( type, nice )                \
954                     case VLC_VAR_ ## type:  \
955                         psz_type = nice;    \
956                         break;
957                     MYCASE( VOID, "void" );
958                     MYCASE( BOOL, "bool" );
959                     MYCASE( INTEGER, "integer" );
960                     MYCASE( HOTKEY, "hotkey" );
961                     MYCASE( STRING, "string" );
962                     MYCASE( MODULE, "module" );
963                     MYCASE( FILE, "file" );
964                     MYCASE( DIRECTORY, "directory" );
965                     MYCASE( VARIABLE, "variable" );
966                     MYCASE( FLOAT, "float" );
967                     MYCASE( TIME, "time" );
968                     MYCASE( ADDRESS, "address" );
969                     MYCASE( MUTEX, "mutex" );
970                     MYCASE( LIST, "list" );
971 #undef MYCASE
972                 }
973                 printf( " %c-o \"%s\" (%s",
974                         i + 1 == p_object->p_internals->i_vars ? '`' : '|',
975                         p_var->psz_name, psz_type );
976                 if( p_var->psz_text )
977                     printf( ", %s", p_var->psz_text );
978                 printf( ")" );
979                 if( p_var->i_type & VLC_VAR_ISCOMMAND )
980                     printf( ", command" );
981                 if( p_var->i_entries )
982                     printf( ", %d callbacks", p_var->i_entries );
983                 switch( p_var->i_type & 0x00f0 )
984                 {
985                     case VLC_VAR_VOID:
986                     case VLC_VAR_MUTEX:
987                         break;
988                     case VLC_VAR_BOOL:
989                         printf( ": %s", p_var->val.b_bool ? "true" : "false" );
990                         break;
991                     case VLC_VAR_INTEGER:
992                         printf( ": %d", p_var->val.i_int );
993                         break;
994                     case VLC_VAR_STRING:
995                         printf( ": \"%s\"", p_var->val.psz_string );
996                         break;
997                     case VLC_VAR_FLOAT:
998                         printf( ": %f", p_var->val.f_float );
999                         break;
1000                     case VLC_VAR_TIME:
1001                         printf( ": " I64Fi, (int64_t)p_var->val.i_time );
1002                         break;
1003                     case VLC_VAR_ADDRESS:
1004                         printf( ": %p", p_var->val.p_address );
1005                         break;
1006                     case VLC_VAR_LIST:
1007                         printf( ": TODO" );
1008                         break;
1009                 }
1010                 printf( "\n" );
1011             }
1012         }
1013
1014         vlc_mutex_unlock( &structure_lock );
1015
1016         if( *newval.psz_string )
1017         {
1018             vlc_object_release( p_object );
1019         }
1020     }
1021
1022     return VLC_SUCCESS;
1023 }
1024
1025 /*****************************************************************************
1026  * vlc_list_release: free a list previously allocated by vlc_list_find
1027  *****************************************************************************
1028  * This function decreases the refcount of all objects in the list and
1029  * frees the list.
1030  *****************************************************************************/
1031 void vlc_list_release( vlc_list_t *p_list )
1032 {
1033     int i_index;
1034
1035     vlc_mutex_lock( &structure_lock );
1036     for( i_index = 0; i_index < p_list->i_count; i_index++ )
1037     {
1038         Release( p_list->p_values[i_index].p_object );
1039     }
1040     vlc_mutex_unlock( &structure_lock );
1041
1042     free( p_list->p_values );
1043     free( p_list );
1044 }
1045
1046 /* Following functions are local */
1047
1048 /*****************************************************************************
1049  * FindIndex: find the index of an object in an array of objects
1050  *****************************************************************************
1051  * This function assumes that p_this can be found in pp_objects. It will not
1052  * crash if p_this cannot be found, but will return a wrong value. It is your
1053  * duty to check the return value if you are not certain that the object could
1054  * be found for sure.
1055  *****************************************************************************/
1056 static int FindIndex( vlc_object_t *p_this,
1057                       vlc_object_t **pp_objects, int i_count )
1058 {
1059     int i_middle = i_count / 2;
1060
1061     if( i_count == 0 )
1062     {
1063         return 0;
1064     }
1065
1066     if( pp_objects[i_middle] == p_this )
1067     {
1068         return i_middle;
1069     }
1070
1071     if( i_count == 1 )
1072     {
1073         return 0;
1074     }
1075
1076     /* We take advantage of the sorted array */
1077     if( pp_objects[i_middle]->i_object_id < p_this->i_object_id )
1078     {
1079         return i_middle + FindIndex( p_this, pp_objects + i_middle,
1080                                              i_count - i_middle );
1081     }
1082     else
1083     {
1084         return FindIndex( p_this, pp_objects, i_middle );
1085     }
1086 }
1087
1088 static vlc_object_t * FindObject( vlc_object_t *p_this, int i_type, int i_mode )
1089 {
1090     int i;
1091     vlc_object_t *p_tmp;
1092
1093     switch( i_mode & 0x000f )
1094     {
1095     case FIND_PARENT:
1096         p_tmp = p_this->p_parent;
1097         if( p_tmp )
1098         {
1099             if( p_tmp->i_object_type == i_type )
1100             {
1101                 p_tmp->p_internals->i_refcount++;
1102                 return p_tmp;
1103             }
1104             else
1105             {
1106                 return FindObject( p_tmp, i_type, i_mode );
1107             }
1108         }
1109         break;
1110
1111     case FIND_CHILD:
1112         for( i = p_this->i_children; i--; )
1113         {
1114             p_tmp = p_this->pp_children[i];
1115             if( p_tmp->i_object_type == i_type )
1116             {
1117                 p_tmp->p_internals->i_refcount++;
1118                 return p_tmp;
1119             }
1120             else if( p_tmp->i_children )
1121             {
1122                 p_tmp = FindObject( p_tmp, i_type, i_mode );
1123                 if( p_tmp )
1124                 {
1125                     return p_tmp;
1126                 }
1127             }
1128         }
1129         break;
1130
1131     case FIND_ANYWHERE:
1132         /* Handled in vlc_object_find */
1133         break;
1134     }
1135
1136     return NULL;
1137 }
1138
1139 static vlc_object_t * FindObjectName( vlc_object_t *p_this,
1140                                       const char *psz_name,
1141                                       int i_mode )
1142 {
1143     int i;
1144     vlc_object_t *p_tmp;
1145
1146     switch( i_mode & 0x000f )
1147     {
1148     case FIND_PARENT:
1149         p_tmp = p_this->p_parent;
1150         if( p_tmp )
1151         {
1152             if( p_tmp->psz_object_name
1153                 && !strcmp( p_tmp->psz_object_name, psz_name ) )
1154             {
1155                 p_tmp->p_internals->i_refcount++;
1156                 return p_tmp;
1157             }
1158             else
1159             {
1160                 return FindObjectName( p_tmp, psz_name, i_mode );
1161             }
1162         }
1163         break;
1164
1165     case FIND_CHILD:
1166         for( i = p_this->i_children; i--; )
1167         {
1168             p_tmp = p_this->pp_children[i];
1169             if( p_tmp->psz_object_name
1170                 && !strcmp( p_tmp->psz_object_name, psz_name ) )
1171             {
1172                 p_tmp->p_internals->i_refcount++;
1173                 return p_tmp;
1174             }
1175             else if( p_tmp->i_children )
1176             {
1177                 p_tmp = FindObjectName( p_tmp, psz_name, i_mode );
1178                 if( p_tmp )
1179                 {
1180                     return p_tmp;
1181                 }
1182             }
1183         }
1184         break;
1185
1186     case FIND_ANYWHERE:
1187         /* Handled in vlc_object_find */
1188         break;
1189     }
1190
1191     return NULL;
1192 }
1193
1194 static void DetachObject( vlc_object_t *p_this )
1195 {
1196     vlc_object_t *p_parent = p_this->p_parent;
1197     int i_index, i;
1198
1199     /* Remove p_this's parent */
1200     p_this->p_parent = NULL;
1201
1202     /* Remove all of p_parent's children which are p_this */
1203     for( i_index = p_parent->i_children ; i_index-- ; )
1204     {
1205         if( p_parent->pp_children[i_index] == p_this )
1206         {
1207             p_parent->i_children--;
1208             for( i = i_index ; i < p_parent->i_children ; i++ )
1209             {
1210                 p_parent->pp_children[i] = p_parent->pp_children[i+1];
1211             }
1212         }
1213     }
1214
1215     if( p_parent->i_children )
1216     {
1217         p_parent->pp_children = (vlc_object_t **)realloc( p_parent->pp_children,
1218                                p_parent->i_children * sizeof(vlc_object_t *) );
1219     }
1220     else
1221     {
1222         free( p_parent->pp_children );
1223         p_parent->pp_children = NULL;
1224     }
1225 }
1226
1227 /*****************************************************************************
1228  * SetAttachment: recursively set the b_attached flag of a subtree.
1229  *****************************************************************************
1230  * This function is used by the attach and detach functions to propagate
1231  * the b_attached flag in a subtree.
1232  *****************************************************************************/
1233 static void SetAttachment( vlc_object_t *p_this, vlc_bool_t b_attached )
1234 {
1235     int i_index;
1236
1237     for( i_index = p_this->i_children ; i_index-- ; )
1238     {
1239         SetAttachment( p_this->pp_children[i_index], b_attached );
1240     }
1241
1242     p_this->p_internals->b_attached = b_attached;
1243 }
1244
1245 static void PrintObject( vlc_object_t *p_this, const char *psz_prefix )
1246 {
1247     char psz_children[20], psz_refcount[20], psz_thread[30], psz_name[50],
1248          psz_parent[20];
1249
1250     psz_name[0] = '\0';
1251     if( p_this->psz_object_name )
1252     {
1253         snprintf( psz_name, 49, " \"%s\"", p_this->psz_object_name );
1254         if( psz_name[48] )
1255             psz_name[48] = '\"';
1256     }
1257
1258     psz_children[0] = '\0';
1259     switch( p_this->i_children )
1260     {
1261         case 0:
1262             break;
1263         case 1:
1264             strcpy( psz_children, ", 1 child" );
1265             break;
1266         default:
1267             snprintf( psz_children, 19, ", %i children", p_this->i_children );
1268             break;
1269     }
1270
1271     psz_refcount[0] = '\0';
1272     if( p_this->p_internals->i_refcount > 0 )
1273         snprintf( psz_refcount, 19, ", refcount %u",
1274                   p_this->p_internals->i_refcount );
1275
1276     psz_thread[0] = '\0';
1277     if( p_this->p_internals->b_thread )
1278         snprintf( psz_thread, 29, " (thread %u)",
1279 #if defined(WIN32) || defined(UNDER_CE)
1280                   (unsigned)p_this->p_internals->thread_id.id );
1281 #else
1282                   (unsigned)p_this->p_internals->thread_id );
1283 #endif
1284
1285     psz_parent[0] = '\0';
1286     if( p_this->p_parent )
1287         snprintf( psz_parent, 19, ", parent %i", p_this->p_parent->i_object_id );
1288
1289     printf( " %so %.8i %s%s%s%s%s%s\n", psz_prefix,
1290             p_this->i_object_id, p_this->psz_object_type,
1291             psz_name, psz_thread, psz_refcount, psz_children,
1292             psz_parent );
1293 }
1294
1295 static void DumpStructure( vlc_object_t *p_this, int i_level, char *psz_foo )
1296 {
1297     int i;
1298     char i_back = psz_foo[i_level];
1299     psz_foo[i_level] = '\0';
1300
1301     PrintObject( p_this, psz_foo );
1302
1303     psz_foo[i_level] = i_back;
1304
1305     if( i_level / 2 >= MAX_DUMPSTRUCTURE_DEPTH )
1306     {
1307         msg_Warn( p_this, "structure tree is too deep" );
1308         return;
1309     }
1310
1311     for( i = 0 ; i < p_this->i_children ; i++ )
1312     {
1313         if( i_level )
1314         {
1315             psz_foo[i_level-1] = ' ';
1316
1317             if( psz_foo[i_level-2] == '`' )
1318             {
1319                 psz_foo[i_level-2] = ' ';
1320             }
1321         }
1322
1323         if( i == p_this->i_children - 1 )
1324         {
1325             psz_foo[i_level] = '`';
1326         }
1327         else
1328         {
1329             psz_foo[i_level] = '|';
1330         }
1331
1332         psz_foo[i_level+1] = '-';
1333         psz_foo[i_level+2] = '\0';
1334
1335         DumpStructure( p_this->pp_children[i], i_level + 2, psz_foo );
1336     }
1337 }
1338
1339 static vlc_list_t * NewList( int i_count )
1340 {
1341     vlc_list_t * p_list = (vlc_list_t *)malloc( sizeof( vlc_list_t ) );
1342     if( p_list == NULL )
1343     {
1344         return NULL;
1345     }
1346
1347     p_list->i_count = i_count;
1348
1349     if( i_count == 0 )
1350     {
1351         p_list->p_values = NULL;
1352         return p_list;
1353     }
1354
1355     p_list->p_values = malloc( i_count * sizeof( vlc_value_t ) );
1356     if( p_list->p_values == NULL )
1357     {
1358         p_list->i_count = 0;
1359         return p_list;
1360     }
1361
1362     return p_list;
1363 }
1364
1365 static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object,
1366                          int i_index )
1367 {
1368     if( p_list == NULL || i_index >= p_list->i_count )
1369     {
1370         return;
1371     }
1372
1373     p_object->p_internals->i_refcount++;
1374
1375     p_list->p_values[i_index].p_object = p_object;
1376
1377     return;
1378 }
1379
1380 /*static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object )
1381 {
1382     if( p_list == NULL )
1383     {
1384         return;
1385     }
1386
1387     p_list->p_values = realloc( p_list->p_values, (p_list->i_count + 1)
1388                                 * sizeof( vlc_value_t ) );
1389     if( p_list->p_values == NULL )
1390     {
1391         p_list->i_count = 0;
1392         return;
1393     }
1394
1395     p_object->p_internals->i_refcount++;
1396
1397     p_list->p_values[p_list->i_count].p_object = p_object;
1398     p_list->i_count++;
1399
1400     return;
1401 }*/
1402
1403 static int CountChildren( vlc_object_t *p_this, int i_type )
1404 {
1405     vlc_object_t *p_tmp;
1406     int i, i_count = 0;
1407
1408     for( i = 0; i < p_this->i_children; i++ )
1409     {
1410         p_tmp = p_this->pp_children[i];
1411
1412         if( p_tmp->i_object_type == i_type )
1413         {
1414             i_count++;
1415         }
1416
1417         if( p_tmp->i_children )
1418         {
1419             i_count += CountChildren( p_tmp, i_type );
1420         }
1421     }
1422
1423     return i_count;
1424 }
1425
1426 static void ListChildren( vlc_list_t *p_list, vlc_object_t *p_this, int i_type )
1427 {
1428     vlc_object_t *p_tmp;
1429     int i;
1430
1431     for( i = 0; i < p_this->i_children; i++ )
1432     {
1433         p_tmp = p_this->pp_children[i];
1434
1435         if( p_tmp->i_object_type == i_type )
1436         {
1437             ListReplace( p_list, p_tmp, p_list->i_count++ );
1438         }
1439
1440         if( p_tmp->i_children )
1441         {
1442             ListChildren( p_list, p_tmp, i_type );
1443         }
1444     }
1445 }