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