source: trunk/third/gstreamer/gst/gstthread.c @ 21448

Revision 21448, 22.6 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21447, which included commits to RCS files with non-trunk default branches.
Line 
1/* GStreamer
2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 *                    2000 Wim Taymans <wtay@chello.be>
4 *                    2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
5 *
6 * gstthread.c: Threaded container object
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library 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 GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23
24#include "gst_private.h"
25
26#include "gstthread.h"
27#include "gstmarshal.h"
28#include "gstscheduler.h"
29#include "gstinfo.h"
30
31#define GST_CAT_DEFAULT GST_CAT_THREAD
32#define STACK_SIZE 0x200000
33
34static GstElementDetails gst_thread_details =
35GST_ELEMENT_DETAILS ("Threaded container",
36    "Generic/Bin",
37    "Container that creates/manages a thread",
38    "Erik Walthinsen <omega@cse.ogi.edu>, "
39    "Benjamin Otte <in7y118@informatik.uni-hamburg.de"
40    "Wim Taymans <wim@fluendo.com");
41
42/* Thread signals and args */
43enum
44{
45  SHUTDOWN,
46  /* FILL ME */
47  LAST_SIGNAL
48};
49
50enum
51{
52  SPINUP = 0,
53  STATECHANGE,
54  STARTUP
55};
56
57enum
58{
59  ARG_0,
60  ARG_PRIORITY
61};
62
63
64static void gst_thread_base_init (gpointer g_class);
65static void gst_thread_class_init (gpointer g_class, gpointer class_data);
66static void gst_thread_init (GTypeInstance * instance, gpointer g_class);
67
68static void gst_thread_dispose (GObject * object);
69
70static void gst_thread_set_property (GObject * object, guint prop_id,
71    const GValue * value, GParamSpec * pspec);
72static void gst_thread_get_property (GObject * object, guint prop_id,
73    GValue * value, GParamSpec * pspec);
74static GstElementStateReturn gst_thread_change_state (GstElement * element);
75static void gst_thread_child_state_change (GstBin * bin,
76    GstElementState oldstate, GstElementState newstate, GstElement * element);
77
78static void gst_thread_sync (GstThread * thread, gboolean is_self);
79
80#ifndef GST_DISABLE_LOADSAVE
81static xmlNodePtr gst_thread_save_thyself (GstObject * object,
82    xmlNodePtr parent);
83static void gst_thread_restore_thyself (GstObject * object, xmlNodePtr self);
84#endif
85
86static void *gst_thread_main_loop (void *arg);
87
88#define GST_TYPE_THREAD_PRIORITY (gst_thread_priority_get_type())
89static GType
90gst_thread_priority_get_type (void)
91{
92  static GType thread_priority_type = 0;
93  static GEnumValue thread_priority[] = {
94    {G_THREAD_PRIORITY_LOW, "LOW", "Low Priority Scheduling"},
95    {G_THREAD_PRIORITY_NORMAL, "NORMAL", "Normal Scheduling"},
96    {G_THREAD_PRIORITY_HIGH, "HIGH", "High Priority Scheduling"},
97    {G_THREAD_PRIORITY_URGENT, "URGENT", "Urgent Scheduling"},
98    {0, NULL, NULL},
99  };
100
101  if (!thread_priority_type) {
102    thread_priority_type =
103        g_enum_register_static ("GstThreadPriority", thread_priority);
104  }
105  return thread_priority_type;
106}
107
108static GstBinClass *parent_class = NULL;
109static guint gst_thread_signals[LAST_SIGNAL] = { 0 };
110GPrivate *gst_thread_current;
111
112GType
113gst_thread_get_type (void)
114{
115  static GType thread_type = 0;
116
117  if (!thread_type) {
118    static const GTypeInfo thread_info = {
119      sizeof (GstThreadClass),
120      gst_thread_base_init,
121      NULL,
122      gst_thread_class_init,
123      NULL,
124      NULL,
125      sizeof (GstThread),
126      0,
127      gst_thread_init,
128      NULL
129    };
130
131    thread_type = g_type_register_static (GST_TYPE_BIN, "GstThread",
132        &thread_info, 0);
133  }
134  return thread_type;
135}
136
137static void
138gst_thread_base_init (gpointer g_class)
139{
140  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
141
142  gst_element_class_set_details (gstelement_class, &gst_thread_details);
143}
144static void
145do_nothing (gpointer hi)
146{
147}
148static void
149gst_thread_class_init (gpointer g_class, gpointer class_data)
150{
151  GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
152
153#ifndef GST_DISABLE_LOADSAVE
154  GstObjectClass *gstobject_class = GST_OBJECT_CLASS (g_class);
155#endif
156  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
157  GstBinClass *gstbin_class = GST_BIN_CLASS (g_class);
158  GstThreadClass *klass = GST_THREAD_CLASS (g_class);
159
160  /* setup gst_thread_current */
161  gst_thread_current = g_private_new (do_nothing);
162
163  parent_class = g_type_class_peek_parent (g_class);
164
165  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PRIORITY,
166      g_param_spec_enum ("priority", "Scheduling Policy",
167          "The scheduling priority of the thread", GST_TYPE_THREAD_PRIORITY,
168          G_THREAD_PRIORITY_NORMAL, G_PARAM_READWRITE));
169
170  gst_thread_signals[SHUTDOWN] =
171      g_signal_new ("shutdown", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
172      G_STRUCT_OFFSET (GstThreadClass, shutdown), NULL, NULL,
173      gst_marshal_VOID__VOID, G_TYPE_NONE, 0);
174
175  gobject_class->dispose = gst_thread_dispose;
176
177#ifndef GST_DISABLE_LOADSAVE
178  gstobject_class->save_thyself = GST_DEBUG_FUNCPTR (gst_thread_save_thyself);
179  gstobject_class->restore_thyself =
180      GST_DEBUG_FUNCPTR (gst_thread_restore_thyself);
181#endif
182
183  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_thread_change_state);
184
185  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_thread_set_property);
186  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_thread_get_property);
187
188  gstbin_class->child_state_change =
189      GST_DEBUG_FUNCPTR (gst_thread_child_state_change);
190}
191
192static void
193gst_thread_init (GTypeInstance * instance, gpointer g_class)
194{
195  GstScheduler *scheduler;
196  GstThread *thread = GST_THREAD (instance);
197
198  GST_DEBUG ("initializing thread");
199
200  /* threads are managing bins and iterate themselves */
201  /* CR1: the GstBin code checks these flags */
202  GST_FLAG_SET (thread, GST_BIN_FLAG_MANAGER);
203  GST_FLAG_SET (thread, GST_BIN_SELF_SCHEDULABLE);
204
205  scheduler = gst_scheduler_factory_make (NULL, GST_ELEMENT (thread));
206  g_assert (scheduler);
207
208  thread->lock = g_mutex_new ();
209  thread->cond = g_cond_new ();
210  thread->iterate_lock = g_mutex_new ();
211
212  thread->thread_id = (GThread *) NULL; /* set in NULL -> READY */
213  thread->priority = G_THREAD_PRIORITY_NORMAL;
214}
215
216static void
217gst_thread_dispose (GObject * object)
218{
219  GstThread *thread = GST_THREAD (object);
220
221  GST_CAT_DEBUG (GST_CAT_REFCOUNTING, "GstThread: dispose");
222
223  /* if we get here, the thread is really stopped as it has released
224   * the last refcount to the thread object, so we can safely free the
225   * mutex and cond vars */
226  G_OBJECT_CLASS (parent_class)->dispose (object);
227
228  g_assert (GST_STATE (thread) == GST_STATE_NULL);
229
230  GST_CAT_DEBUG (GST_CAT_REFCOUNTING, "GstThread: dispose, freeing locks");
231
232  g_mutex_free (thread->lock);
233  g_cond_free (thread->cond);
234  g_mutex_free (thread->iterate_lock);
235
236  gst_object_replace ((GstObject **) & GST_ELEMENT_SCHED (thread), NULL);
237}
238
239/**
240 * gst_thread_set_priority:
241 * @thread: the thread to change
242 * @priority: the new priority for the thread
243 *
244 * change the thread's priority
245 */
246void
247gst_thread_set_priority (GstThread * thread, GThreadPriority priority)
248{
249  g_return_if_fail (GST_IS_THREAD (thread));
250
251  thread->priority = priority;
252}
253
254static void
255gst_thread_set_property (GObject * object, guint prop_id, const GValue * value,
256    GParamSpec * pspec)
257{
258  GstThread *thread;
259
260  thread = GST_THREAD (object);
261
262  switch (prop_id) {
263    case ARG_PRIORITY:
264      thread->priority = g_value_get_enum (value);
265      break;
266    default:
267      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
268      break;
269  }
270}
271
272static void
273gst_thread_get_property (GObject * object, guint prop_id, GValue * value,
274    GParamSpec * pspec)
275{
276  GstThread *thread;
277
278  thread = GST_THREAD (object);
279
280  switch (prop_id) {
281    case ARG_PRIORITY:
282      g_value_set_enum (value, thread->priority);
283      break;
284    default:
285      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
286      break;
287  }
288}
289
290
291/**
292 * gst_thread_new:
293 * @name: the name of the thread
294 *
295 * Create a new thread with the given name.
296 *
297 * Returns: The new thread
298 */
299GstElement *
300gst_thread_new (const gchar * name)
301{
302  return gst_element_factory_make ("thread", name);
303}
304
305/**
306 * gst_thread_get_current:
307 *
308 * Gets the current GstThread.
309 *
310 * Returns: The current GstThread or NULL if you are not running inside a
311 *          #GstThread.
312 */
313GstThread *
314gst_thread_get_current (void)
315{
316  return (GstThread *) g_private_get (gst_thread_current);
317}
318
319static inline void
320gst_thread_release_children_locks (GstThread * thread)
321{
322  GstRealPad *peer = NULL;
323  GstElement *peerelement;
324  GList *elements = (GList *) gst_bin_get_list (GST_BIN (thread));
325
326  while (elements) {
327    GstElement *element = GST_ELEMENT (elements->data);
328    GList *pads;
329
330    g_assert (element);
331    GST_DEBUG_OBJECT (thread, "waking element \"%s\"",
332        GST_ELEMENT_NAME (element));
333    elements = g_list_next (elements);
334
335    if (!gst_element_release_locks (element))
336      g_warning ("element %s could not release locks",
337          GST_ELEMENT_NAME (element));
338
339    pads = GST_ELEMENT_PADS (element);
340
341    while (pads) {
342      if (GST_PAD_PEER (pads->data)) {
343        peer = GST_REAL_PAD (GST_PAD_PEER (pads->data));
344        pads = g_list_next (pads);
345      } else {
346        pads = g_list_next (pads);
347        continue;
348      }
349
350      if (!peer)
351        continue;
352
353      peerelement = GST_PAD_PARENT (peer);
354      if (!peerelement)
355        continue;               /* FIXME: deal with case where there's no peer */
356
357      if (GST_ELEMENT_SCHED (peerelement) != GST_ELEMENT_SCHED (thread)) {
358        GST_LOG_OBJECT (thread, "element \"%s\" has pad cross sched boundary",
359            GST_ELEMENT_NAME (element));
360        GST_LOG_OBJECT (thread, "waking element \"%s\"",
361            GST_ELEMENT_NAME (peerelement));
362        if (!gst_element_release_locks (peerelement))
363          g_warning ("element %s could not release locks",
364              GST_ELEMENT_NAME (peerelement));
365      }
366    }
367  }
368}
369
370/* sync the main thread, if there is one and makes sure that the thread
371 * is not spinning and in the waiting state. We can only do this if
372 * this function is not called from the thread itself.
373 *
374 * This function should be called with the thread lock held.
375 */
376static void
377gst_thread_sync (GstThread * thread, gboolean is_self)
378{
379  if (thread->thread_id == NULL)
380    return;
381
382  /* need to stop spinning in any case */
383  GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
384
385  if (is_self) {
386    /* we're trying to sync ourself. Not much we can do here but hope
387     * that the thread will stop spinning and  end up in the waiting
388     * state */
389    GST_DEBUG_OBJECT (thread, "syncing itself");
390  } else {
391    GST_DEBUG_OBJECT (thread, "syncing thread, grabbing lock");
392    /* another thread is trying to sync us. The strategy is to
393     * repeadetly unlock the element locks until the thread is
394     * blocking in the waiting state. We use a timeout because
395     * unlocking all of the children at the same time is not
396     * atomic and might fail. */
397    while (!GST_FLAG_IS_SET (thread, GST_THREAD_STATE_WAITING)) {
398      GTimeVal tv;
399
400      GST_LOG_OBJECT (thread, "syncing thread...");
401
402      /* release child locks */
403      gst_thread_release_children_locks (thread);
404      g_get_current_time (&tv);
405      g_time_val_add (&tv, 1000);       /* wait a millisecond to sync the thread */
406      GST_DEBUG_OBJECT (thread, "wait");
407      g_cond_timed_wait (thread->cond, thread->lock, &tv);
408    }
409    GST_LOG_OBJECT (thread, "caught thread");
410    /* at this point we should be waiting */
411    g_assert (GST_FLAG_IS_SET (thread, GST_THREAD_STATE_WAITING));
412  }
413  g_assert (!GST_FLAG_IS_SET (thread, GST_THREAD_STATE_SPINNING));
414}
415
416static GstElementStateReturn
417gst_thread_change_state (GstElement * element)
418{
419  GstThread *thread;
420  GstElementStateReturn ret;
421  gint transition;
422  gboolean is_self;
423
424  g_return_val_if_fail (GST_IS_THREAD (element), GST_STATE_FAILURE);
425
426  GST_DEBUG_OBJECT (element, "changing state from %s to %s",
427      gst_element_state_get_name (GST_STATE (element)),
428      gst_element_state_get_name (GST_STATE_PENDING (element)));
429
430  thread = GST_THREAD (element);
431
432  /* boolean to check if we called the state change in the same thread as
433   * the iterate thread */
434  is_self = (thread == gst_thread_get_current ());
435
436  GST_LOG_OBJECT (thread, "grabbing lock");
437  g_mutex_lock (thread->lock);
438
439  gst_thread_sync (thread, is_self);
440
441  /* no iteration is allowed during this state change because an iteration
442   * can cause another state change conflicting with this one */
443  /* do not try to grab the lock if this method is called from the
444   * same thread as the iterate thread, the lock might be held and we
445   * might deadlock */
446  if (!is_self)
447    g_mutex_lock (thread->iterate_lock);
448
449  transition = GST_STATE_TRANSITION (element);
450
451  switch (transition) {
452    case GST_STATE_NULL_TO_READY:
453      /* create the thread */
454      GST_FLAG_UNSET (thread, GST_THREAD_STATE_REAPING);
455      GST_LOG_OBJECT (element, "grabbing lock");
456      thread->thread_id = g_thread_create_full (gst_thread_main_loop,
457          thread, STACK_SIZE, FALSE, TRUE, thread->priority, NULL);
458      if (!thread->thread_id) {
459        GST_ERROR_OBJECT (element, "g_thread_create_full failed");
460        goto error_out;
461      }
462      GST_LOG_OBJECT (element, "GThread created");
463
464      /* wait for it to 'spin up' */
465      g_cond_wait (thread->cond, thread->lock);
466      break;
467    case GST_STATE_READY_TO_PAUSED:
468      break;
469    case GST_STATE_PAUSED_TO_PLAYING:
470    {
471      /* FIXME: recurse into sub-bins */
472      GList *elements = (GList *) gst_bin_get_list (GST_BIN (thread));
473
474      while (elements) {
475        gst_element_enable_threadsafe_properties ((GstElement *) elements->
476            data);
477        elements = g_list_next (elements);
478      }
479      /* reset self to spinning */
480      GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING);
481      break;
482    }
483    case GST_STATE_PLAYING_TO_PAUSED:
484    {
485      GList *elements;
486
487      GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
488
489      elements = (GList *) gst_bin_get_list (GST_BIN (thread));
490
491      while (elements) {
492        gst_element_disable_threadsafe_properties ((GstElement *) elements->
493            data);
494        elements = g_list_next (elements);
495      }
496      break;
497    }
498    case GST_STATE_PAUSED_TO_READY:
499      GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
500      break;
501    case GST_STATE_READY_TO_NULL:
502      /* we can't join the threads here, because this could have been triggered
503         by ourself (ouch) */
504      GST_LOG_OBJECT (thread, "destroying GThread %p", thread->thread_id);
505      GST_FLAG_SET (thread, GST_THREAD_STATE_REAPING);
506      GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
507      /* thread was already gone */
508      if (thread->thread_id != NULL) {
509        thread->thread_id = NULL;
510        if (is_self) {
511          GST_LOG_OBJECT (thread,
512              "Thread %s is destroying itself. Returning to mainloop ASAP!",
513              GST_ELEMENT_NAME (thread));
514          break;
515        } else {
516          /* now wait for the thread to destroy itself */
517          GST_DEBUG_OBJECT (thread, "signal");
518          g_cond_signal (thread->cond);
519          GST_DEBUG_OBJECT (thread, "wait");
520          g_cond_wait (thread->cond, thread->lock);
521          GST_DEBUG_OBJECT (thread, "done");
522          /* it should be dead now */
523        }
524      }
525      break;
526    default:
527      break;
528  }
529  GST_LOG_OBJECT (thread, "unlocking lock");
530  g_mutex_unlock (thread->lock);
531
532  if (GST_ELEMENT_CLASS (parent_class)->change_state) {
533    ret = GST_ELEMENT_CLASS (parent_class)->change_state (GST_ELEMENT (thread));
534  } else {
535    ret = GST_STATE_SUCCESS;
536  }
537
538  g_mutex_lock (thread->lock);
539  if (GST_STATE (thread) == GST_STATE_PLAYING &&
540      GST_FLAG_IS_SET (thread, GST_THREAD_STATE_WAITING)) {
541    GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING);
542    if (!is_self) {
543      g_cond_signal (thread->cond);
544    }
545  }
546  g_mutex_unlock (thread->lock);
547
548  if (!is_self)
549    g_mutex_unlock (thread->iterate_lock);
550
551  return ret;
552
553error_out:
554  GST_CAT_DEBUG (GST_CAT_STATES, "changing state from %s to %s failed for %s",
555      gst_element_state_get_name (GST_STATE (element)),
556      gst_element_state_get_name (GST_STATE_PENDING (element)),
557      GST_ELEMENT_NAME (element));
558
559  g_mutex_unlock (thread->lock);
560
561  if (!is_self)
562    g_mutex_unlock (thread->iterate_lock);
563
564  return GST_STATE_FAILURE;
565}
566
567/* When a child changes its state the thread might have to start.
568 *
569 * When we are in the thread context we set the spinning flag.
570 * When we are not in the thread context, we grab the lock. The
571 * thread can then only be in the waiting or spinning state. If it's
572 * waiting we signal it to start. If it was spinning, we let it spin.
573 */
574static void
575gst_thread_child_state_change (GstBin * bin, GstElementState oldstate,
576    GstElementState newstate, GstElement * element)
577{
578  GstThread *thread = GST_THREAD (bin);
579  gboolean is_self;
580  GstThread *current;
581
582  current = gst_thread_get_current ();
583
584  is_self = (current == thread);
585
586  GST_LOG_OBJECT (bin, "(from thread %s) child %s changed state from %s to %s",
587      current ? GST_ELEMENT_NAME (current) :
588      "(none)", GST_ELEMENT_NAME (element),
589      gst_element_state_get_name (oldstate),
590      gst_element_state_get_name (newstate));
591
592  if (parent_class->child_state_change)
593    parent_class->child_state_change (bin, oldstate, newstate, element);
594
595  /* if we're changing from playing to paused, kids will one-by-one go
596   * to paused while we're playing, which is bad if we execute the code
597   * below. We should only do that when a child explicitely changed
598   * state outside our own context. */
599  if (GST_FLAG_IS_SET (bin, GST_BIN_STATE_LOCKED))
600    return;
601
602  /* see if we have to wake up the thread now. */
603  if (is_self) {
604    GST_LOG_OBJECT (element, "we are in the thread context");
605    /* we are the thread, set the spinning flag so that we start spinning
606     * next time */
607    if (newstate == GST_STATE_PLAYING) {
608      GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING);
609    }
610  } else {
611    g_mutex_lock (thread->lock);
612    /* thread is now spinning or waiting after grabbing the lock */
613    if (newstate == GST_STATE_PLAYING) {
614      if (!GST_FLAG_IS_SET (thread, GST_THREAD_STATE_WAITING)) {
615        /* its spinning, that's not really a problem, it will
616         * continue to do so */
617        GST_LOG_OBJECT (element, "thread is playing");
618      } else {
619        /* waiting, set spinning flag and trigger restart. */
620        GST_LOG_OBJECT (element, "signal playing");
621        GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING);
622        g_cond_signal (GST_THREAD (bin)->cond);
623      }
624    }
625    g_mutex_unlock (thread->lock);
626  }
627  GST_LOG_OBJECT (element, "done child state change");
628}
629
630/**
631 * gst_thread_main_loop:
632 * @arg: the thread to start
633 *
634 * The main loop of the thread. The thread will iterate
635 * while the state is GST_THREAD_STATE_SPINNING.
636 */
637static void *
638gst_thread_main_loop (void *arg)
639{
640  GstThread *thread = NULL;
641  GstElement *element = NULL;
642  GstScheduler *sched;
643
644  thread = GST_THREAD (arg);
645  GST_LOG_OBJECT (element, "grabbing lock");
646  g_mutex_lock (thread->lock);
647  element = GST_ELEMENT (arg);
648  GST_LOG_OBJECT (thread, "Started main loop");
649
650  /* initialize gst_thread_current */
651  g_private_set (gst_thread_current, thread);
652
653  /* set up the element's scheduler */
654  gst_scheduler_setup (GST_ELEMENT_SCHED (thread));
655  GST_FLAG_UNSET (thread, GST_THREAD_STATE_REAPING);
656  GST_FLAG_UNSET (thread, GST_THREAD_STATE_WAITING);
657
658  /* signals the startup of the thread */
659  GST_LOG_OBJECT (element, "signal");
660  g_cond_signal (thread->cond);
661  GST_LOG_OBJECT (element, "unlocking lock");
662
663  gst_object_ref (GST_OBJECT (thread));
664  /* as long as we're not dying we can continue */
665  while (!(GST_FLAG_IS_SET (thread, GST_THREAD_STATE_REAPING))) {
666    /* in the playing state we need to do something special */
667    if (GST_STATE (thread) == GST_STATE_PLAYING) {
668      GST_LOG_OBJECT (thread, "starting to iterate");
669      /* continue iteration while spinning */
670      while (GST_FLAG_IS_SET (thread, GST_THREAD_STATE_SPINNING)) {
671        gboolean status;
672
673        g_mutex_unlock (thread->lock);
674        g_mutex_lock (thread->iterate_lock);
675        status = gst_bin_iterate (GST_BIN (thread));
676        g_mutex_unlock (thread->iterate_lock);
677        g_mutex_lock (thread->lock);
678
679        if (!status) {
680          GST_DEBUG_OBJECT (thread, "iterate returned false");
681          if (GST_STATE (thread) != GST_STATE_PLAYING) {
682            GST_DEBUG_OBJECT (thread,
683                "stopping spinning as state is not playing");
684            GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
685          }
686        }
687        /* if we hold the last refcount, the app unreffed the
688         * thread and we should stop ASAP */
689        if (G_OBJECT (thread)->ref_count == 1) {
690          GST_DEBUG_OBJECT (thread, "reaping as refcount is only 1");
691          GST_FLAG_SET (thread, GST_THREAD_STATE_REAPING);
692          GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
693        }
694      }
695    }
696    /* do not try to sync when we are REAPING */
697    if (!(GST_FLAG_IS_SET (thread, GST_THREAD_STATE_REAPING))) {
698      /* sync section */
699      GST_LOG_OBJECT (thread, "entering sync");
700      GST_DEBUG_OBJECT (thread, "signal");
701      g_cond_signal (thread->cond);
702      GST_DEBUG_OBJECT (thread, "wait");
703      GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
704      GST_FLAG_SET (thread, GST_THREAD_STATE_WAITING);
705      g_cond_wait (thread->cond, thread->lock);
706      GST_FLAG_UNSET (thread, GST_THREAD_STATE_WAITING);
707      GST_LOG_OBJECT (thread, "wait done");
708    }
709  }
710  GST_LOG_OBJECT (thread, "unlocking lock");
711  thread->thread_id = NULL;
712  g_mutex_unlock (thread->lock);
713
714  /* we need to destroy the scheduler here because it might have
715   * mapped it's stack into the threads stack space */
716  sched = GST_ELEMENT_SCHED (thread);
717  if (sched)
718    gst_scheduler_reset (sched);
719
720  g_signal_emit (G_OBJECT (thread), gst_thread_signals[SHUTDOWN], 0);
721
722  /* signal - we are out */
723  GST_LOG_OBJECT (thread, "Thread %p exits main loop", g_thread_self ());
724  g_cond_signal (thread->cond);
725  gst_object_unref (GST_OBJECT (thread));
726  /* don't assume the GstThread object exists anymore now */
727
728  return NULL;
729}
730
731#ifndef GST_DISABLE_LOADSAVE
732static xmlNodePtr
733gst_thread_save_thyself (GstObject * object, xmlNodePtr self)
734{
735  if (GST_OBJECT_CLASS (parent_class)->save_thyself)
736    GST_OBJECT_CLASS (parent_class)->save_thyself (object, self);
737  return NULL;
738}
739
740static void
741gst_thread_restore_thyself (GstObject * object, xmlNodePtr self)
742{
743  GST_LOG_OBJECT (object, "restoring");
744
745  if (GST_OBJECT_CLASS (parent_class)->restore_thyself)
746    GST_OBJECT_CLASS (parent_class)->restore_thyself (object, self);
747}
748#endif /* GST_DISABLE_LOADSAVE */
Note: See TracBrowser for help on using the repository browser.