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