licenses, ugg
[platform/upstream/gstreamer.git] / gst / gstthread.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstthread.c: Threaded container object
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <unistd.h>
24
25 /* #define GST_DEBUG_ENABLED */
26 #include "gst_private.h"
27
28 #include "gst.h"
29 #include "gstthread.h"
30 #include "gstscheduler.h"
31 #include "gstqueue.h"
32
33 GstElementDetails gst_thread_details = {
34   "Threaded container",
35   "Generic/Bin",
36   "LGPL",
37   "Container that creates/manages a thread",
38   VERSION,
39   "Erik Walthinsen <omega@cse.ogi.edu>",
40   "(C) 1999, 2000",
41 };
42
43
44 /* Thread signals and args */
45 enum {
46   SHUTDOWN,
47   /* FILL ME */
48   LAST_SIGNAL
49 };
50
51 enum {
52   SPINUP=0,
53   STATECHANGE,
54   STARTUP
55 };
56
57 enum {
58   ARG_0,
59   ARG_SCHEDPOLICY,
60   ARG_PRIORITY,
61 };
62
63
64
65 static void                     gst_thread_class_init           (GstThreadClass *klass);
66 static void                     gst_thread_init                 (GstThread *thread);
67
68 static void                     gst_thread_dispose      (GObject *object);
69
70 static void                     gst_thread_set_property         (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
71 static void                     gst_thread_get_property         (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
72
73 static GstElementStateReturn    gst_thread_change_state         (GstElement *element);
74
75 #ifndef GST_DISABLE_LOADSAVE
76 static xmlNodePtr               gst_thread_save_thyself         (GstObject *object, xmlNodePtr parent);
77 static void                     gst_thread_restore_thyself      (GstObject *object, xmlNodePtr self);
78 #endif
79
80 static void*                    gst_thread_main_loop            (void *arg);
81
82 #define GST_TYPE_THREAD_SCHEDPOLICY (gst_thread_schedpolicy_get_type())
83 static GType
84 gst_thread_schedpolicy_get_type(void) {
85   static GType thread_schedpolicy_type = 0;
86   static GEnumValue thread_schedpolicy[] = {
87     {SCHED_OTHER, "SCHED_OTHER", "Normal Scheduling"},
88     {SCHED_FIFO,  "SCHED_FIFO",  "FIFO Scheduling (requires root)"},
89     {SCHED_RR,    "SCHED_RR",    "Round-Robin Scheduling (requires root)"},
90     {0, NULL, NULL},
91   };
92   if (!thread_schedpolicy_type) {
93     thread_schedpolicy_type = g_enum_register_static("GstThreadSchedPolicy", thread_schedpolicy);
94   }
95   return thread_schedpolicy_type;
96 }
97
98 static GstBinClass *parent_class = NULL;
99 static guint gst_thread_signals[LAST_SIGNAL] = { 0 }; 
100
101 GType
102 gst_thread_get_type(void) {
103   static GType thread_type = 0;
104
105   if (!thread_type) {
106     static const GTypeInfo thread_info = {
107       sizeof(GstThreadClass),
108       NULL,
109       NULL,
110       (GClassInitFunc)gst_thread_class_init,
111       NULL,
112       NULL,
113       sizeof(GstThread),
114       4,
115       (GInstanceInitFunc)gst_thread_init,
116       NULL
117     };
118     thread_type = g_type_register_static(GST_TYPE_BIN, "GstThread", &thread_info, 0);
119   }
120   return thread_type;
121 }
122
123 static void
124 gst_thread_class_init (GstThreadClass *klass)
125 {
126   GObjectClass *gobject_class;
127   GstObjectClass *gstobject_class;
128   GstElementClass *gstelement_class;
129   GstBinClass *gstbin_class;
130
131   gobject_class =       (GObjectClass*)klass;
132   gstobject_class =     (GstObjectClass*)klass;
133   gstelement_class =    (GstElementClass*)klass;
134   gstbin_class =        (GstBinClass*)klass;
135
136   parent_class = g_type_class_ref (GST_TYPE_BIN);
137
138   g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_SCHEDPOLICY,
139     g_param_spec_enum("schedpolicy", "Scheduling Policy", "The scheduling policy of the thread",
140                       GST_TYPE_THREAD_SCHEDPOLICY, SCHED_OTHER, G_PARAM_READWRITE));
141   g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_PRIORITY,
142     g_param_spec_int("priority", "Scheduling Priority", "The scheduling priority of the thread",
143                      0, 99, 0, G_PARAM_READWRITE));
144
145   gst_thread_signals[SHUTDOWN] =
146     g_signal_new ("shutdown", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
147                   G_STRUCT_OFFSET (GstThreadClass, shutdown), NULL, NULL,
148                   gst_marshal_VOID__VOID, G_TYPE_NONE, 0);
149
150   gobject_class->dispose =              gst_thread_dispose;
151
152 #ifndef GST_DISABLE_LOADSAVE
153   gstobject_class->save_thyself =       GST_DEBUG_FUNCPTR (gst_thread_save_thyself);
154   gstobject_class->restore_thyself =    GST_DEBUG_FUNCPTR (gst_thread_restore_thyself);
155 #endif
156
157   gstelement_class->change_state =      GST_DEBUG_FUNCPTR (gst_thread_change_state);
158
159   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_thread_set_property);
160   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_thread_get_property);
161
162 }
163
164 static void
165 gst_thread_init (GstThread *thread)
166 {
167   GstScheduler *scheduler;
168
169   GST_DEBUG (GST_CAT_THREAD, "initializing thread");
170
171   /* threads are managing bins and iterate themselves */
172   /* CR1: the GstBin code checks these flags */
173   GST_FLAG_SET (thread, GST_BIN_FLAG_MANAGER);
174   GST_FLAG_SET (thread, GST_BIN_SELF_SCHEDULABLE);
175
176   scheduler = gst_scheduler_factory_make (NULL, GST_ELEMENT (thread));
177
178   thread->lock = g_mutex_new ();
179   thread->cond = g_cond_new ();
180
181   thread->ppid = getpid ();
182   thread->thread_id = (pthread_t) -1;
183   thread->sched_policy = SCHED_OTHER;
184   thread->priority = 0;
185   thread->stack = NULL;
186 }
187
188 static void
189 gst_thread_dispose (GObject *object)
190 {
191   GstThread *thread = GST_THREAD (object);
192
193   GST_DEBUG (GST_CAT_REFCOUNTING, "dispose");
194
195   g_mutex_free (thread->lock);
196   g_cond_free (thread->cond);
197
198   G_OBJECT_CLASS (parent_class)->dispose (object);
199
200   if (GST_ELEMENT_SCHED (thread)) {
201     gst_object_unref (GST_OBJECT (GST_ELEMENT_SCHED (thread)));
202   }
203 }
204
205 static void
206 gst_thread_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
207 {
208   GstThread *thread;
209
210   /* it's not null if we got it, but it might not be ours */
211   g_return_if_fail (GST_IS_THREAD (object));
212
213   thread = GST_THREAD (object);
214
215   switch (prop_id) {
216     case ARG_SCHEDPOLICY:
217       thread->sched_policy = g_value_get_enum (value);
218       break;
219     case ARG_PRIORITY:
220       thread->priority = g_value_get_int (value);
221       break;
222     default:
223       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224       break;
225   }
226 }
227
228 static void
229 gst_thread_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
230 {
231   GstThread *thread;
232
233   /* it's not null if we got it, but it might not be ours */
234   g_return_if_fail (GST_IS_THREAD (object));
235
236   thread = GST_THREAD (object);
237
238   switch (prop_id) {
239     case ARG_SCHEDPOLICY:
240       g_value_set_enum (value, thread->sched_policy);
241       break;
242     case ARG_PRIORITY:
243       g_value_set_int (value, thread->priority);
244       break;
245     default:
246       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
247       break;
248   }
249 }
250
251
252 /**
253  * gst_thread_new:
254  * @name: the name of the thread
255  *
256  * Create a new thread with the given name.
257  *
258  * Returns: The new thread
259  */
260 GstElement*
261 gst_thread_new (const gchar *name)
262 {
263   return gst_element_factory_make ("thread", name);
264 }
265
266 /* these two macros are used for debug/info from the state_change function */
267
268 #define THR_INFO(format,args...) \
269   GST_INFO_ELEMENT(GST_CAT_THREAD, thread, "sync(" GST_DEBUG_THREAD_FORMAT "): " format , \
270   GST_DEBUG_THREAD_ARGS(thread->pid) , ## args )
271   
272 #define THR_DEBUG(format,args...) \
273   GST_DEBUG_ELEMENT(GST_CAT_THREAD, thread, "sync(" GST_DEBUG_THREAD_FORMAT "): " format , \
274   GST_DEBUG_THREAD_ARGS(thread->pid) , ## args )
275
276 /* these two macros are used for debug/info from the gst_thread_main_loop
277  * function
278  */
279
280 #define THR_INFO_MAIN(format,args...) \
281   GST_INFO_ELEMENT(GST_CAT_THREAD, thread, "sync-main(" GST_DEBUG_THREAD_FORMAT "): " format , \
282   GST_DEBUG_THREAD_ARGS(thread->ppid) , ## args )
283
284 #define THR_DEBUG_MAIN(format,args...) \
285   GST_DEBUG_ELEMENT(GST_CAT_THREAD, thread, "sync-main(" GST_DEBUG_THREAD_FORMAT "): " format , \
286   GST_DEBUG_THREAD_ARGS(thread->ppid) , ## args )
287
288 static GstElementStateReturn 
289 gst_thread_update_state (GstThread *thread)
290 {
291   GST_DEBUG_ELEMENT (GST_CAT_THREAD, thread, "updating state of thread");
292   /* check for state change */
293   if (GST_STATE_PENDING (thread) != GST_STATE_VOID_PENDING) {
294     /* punt and change state on all the children */
295     if (GST_ELEMENT_CLASS (parent_class)->change_state)
296       return GST_ELEMENT_CLASS (parent_class)->change_state (GST_ELEMENT (thread));
297   }
298
299   /* FIXME: in the case of no change_state function in the parent's class,
300    * shouldn't we actually change the thread's state ? */
301   g_warning ("thread's parent doesn't have change_state, returning success");
302   return GST_STATE_SUCCESS;
303 }
304
305
306 static GstElementStateReturn
307 gst_thread_change_state (GstElement * element)
308 {
309   GstThread *thread;
310   gboolean stateset = GST_STATE_SUCCESS;
311   gint transition;
312   pthread_t self = pthread_self ();
313   glong stacksize;
314
315   g_return_val_if_fail (GST_IS_THREAD (element), GST_STATE_FAILURE);
316   g_return_val_if_fail (gst_has_threads (), GST_STATE_FAILURE);
317
318   thread = GST_THREAD (element);
319
320   transition = GST_STATE_TRANSITION (element);
321
322   THR_INFO ("changing state from %s to %s",
323             gst_element_state_get_name (GST_STATE (element)),
324             gst_element_state_get_name (GST_STATE_PENDING (element)));
325
326   if (pthread_equal (self, thread->thread_id)) {
327     GST_DEBUG (GST_CAT_THREAD,
328                "no sync(" GST_DEBUG_THREAD_FORMAT "): setting own thread's state to spinning",
329                GST_DEBUG_THREAD_ARGS (thread->pid));
330     return gst_thread_update_state (thread);
331   }
332
333   switch (transition) {
334     case GST_STATE_NULL_TO_READY:
335       /* set the state to idle */
336       GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
337
338       THR_DEBUG ("creating thread \"%s\"", GST_ELEMENT_NAME (element));
339
340       /* this bit of code handles creation of pthreads
341        * this is therefor tricky code
342        * compare it with the block of code that handles the destruction
343        * in GST_STATE_READY_TO_NULL below
344        */
345       g_mutex_lock (thread->lock);
346
347       /* create attribute struct for pthread
348        * and assign stack pointer and size to it
349        *
350        * the default state of a pthread is PTHREAD_CREATE_JOINABLE
351        * (see man pthread_attr_init)
352        * - other thread can sync on termination
353        * - thread resources are kept allocated until other thread performs
354        *   pthread_join
355        */
356
357       if (pthread_attr_init (&thread->attr) != 0)
358         g_warning ("pthread_attr_init returned an error !");
359
360       /* this function should return a newly allocated stack
361        * (using whatever method)
362        * which we can initiate the pthreads with
363        * the stack should be freed in 
364        */
365       if (gst_scheduler_get_preferred_stack (GST_ELEMENT_SCHED (element), 
366                                              &thread->stack, &stacksize)) {
367 #ifdef HAVE_PTHREAD_ATTR_SETSTACK
368         if (pthread_attr_setstack (&thread->attr, 
369                                    thread->stack, stacksize) != 0) {
370           g_warning ("pthread_attr_setstack failed\n");
371           return GST_STATE_FAILURE;
372         }
373 #else
374         if (pthread_attr_setstackaddr (&thread->attr, thread->stack) != 0) {
375           g_warning ("pthread_attr_setstackaddr failed\n");
376           return GST_STATE_FAILURE;
377         }
378         if (pthread_attr_setstacksize (&thread->attr, stacksize) != 0) {
379           g_warning ("pthread_attr_setstacksize failed\n");
380           return GST_STATE_FAILURE;
381         }
382 #endif
383         GST_DEBUG (GST_CAT_THREAD, "pthread attr set stack at %p of size %ld", 
384                    thread->stack, stacksize);
385       }
386       else {
387         g_warning ("scheduler did not return a preferred stack");
388       }
389
390       /* create a new pthread
391        * use the specified attributes
392        * make it execute gst_thread_main_loop (thread)
393        */
394       GST_DEBUG (GST_CAT_THREAD, "going to pthread_create...");
395       if (pthread_create (&thread->thread_id, &thread->attr, 
396           gst_thread_main_loop, thread) != 0) {
397         GST_DEBUG (GST_CAT_THREAD, "pthread_create failed");
398         g_mutex_unlock (thread->lock);
399         GST_DEBUG (GST_CAT_THREAD, "could not create thread \"%s\"", 
400                    GST_ELEMENT_NAME (element));
401         return GST_STATE_FAILURE;
402       }
403       GST_DEBUG (GST_CAT_THREAD, "pthread created");
404
405       /* wait for it to 'spin up' */
406       THR_DEBUG ("waiting for child thread spinup");
407       g_cond_wait (thread->cond, thread->lock);
408       THR_DEBUG ("thread claims to be up");
409       g_mutex_unlock (thread->lock);
410       break;
411     case GST_STATE_READY_TO_PAUSED:
412       THR_INFO ("readying thread");
413       g_mutex_lock (thread->lock);
414       THR_DEBUG ("signaling");
415       g_cond_signal (thread->cond);
416       THR_DEBUG ("waiting for ack");
417       g_cond_wait (thread->cond, thread->lock);
418       THR_DEBUG ("got ack");
419       g_mutex_unlock (thread->lock);
420       break;
421     case GST_STATE_PAUSED_TO_PLAYING:
422     {
423       /* fixme: recurse into sub-bins */
424       const GList *elements = gst_bin_get_list (GST_BIN (thread));
425       while (elements) {
426         gst_element_enable_threadsafe_properties ((GstElement*)elements->data);
427         elements = g_list_next (elements);
428       }
429       
430       THR_DEBUG ("telling thread to start spinning");
431       g_mutex_lock (thread->lock);
432       THR_DEBUG ("signaling");
433       g_cond_signal (thread->cond);
434       THR_DEBUG ("waiting for ack");
435       g_cond_wait (thread->cond, thread->lock);
436       THR_DEBUG ("got ack");
437       g_mutex_unlock (thread->lock);
438       break;
439     }
440     case GST_STATE_PLAYING_TO_PAUSED:
441     {
442       const GList *elements = (GList *) gst_bin_get_list (GST_BIN (thread));
443
444       THR_INFO ("pausing thread");
445
446       /* the following code ensures that the bottom half of thread will run
447        * to perform each elements' change_state() (by calling gstbin.c::
448        * change_state()).
449        * + the pending state was already set by gstelement.c::set_state()
450        * + unlock all elements so the bottom half can start the state change.
451        */ 
452       g_mutex_lock (thread->lock);
453
454       GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
455
456       while (elements) {
457         GstElement *element = GST_ELEMENT (elements->data);
458         GList *pads;
459         
460
461         g_assert (element);
462         THR_DEBUG ("  waking element \"%s\"", GST_ELEMENT_NAME (element));
463         elements = g_list_next (elements);
464
465         if (!gst_element_release_locks (element)) {
466           g_warning ("element %s could not release locks", GST_ELEMENT_NAME (element));
467         }
468
469         pads = GST_ELEMENT_PADS (element);
470
471         while (pads) {
472           GstRealPad *peer = NULL;
473           GstElement *peerelement;
474
475           if (GST_PAD_PEER (pads->data))
476             peer = GST_REAL_PAD (GST_PAD_PEER (pads->data));
477
478           pads = g_list_next (pads);
479
480           if (!peer)
481             continue;
482
483           peerelement = GST_PAD_PARENT (peer);
484           if (!peerelement)
485             continue;           /* deal with case where there's no peer */
486
487           if (!GST_FLAG_IS_SET (peerelement, GST_ELEMENT_DECOUPLED)) {
488             GST_DEBUG (GST_CAT_THREAD, "peer element isn't DECOUPLED");
489             continue;
490           }
491
492           if (GST_ELEMENT_SCHED (peerelement) != GST_ELEMENT_SCHED (thread)) {
493             THR_DEBUG ("  element \"%s\" has pad cross sched boundary", GST_ELEMENT_NAME (element));
494             THR_DEBUG ("  waking element \"%s\"", GST_ELEMENT_NAME (peerelement));
495             if (!gst_element_release_locks (peerelement)) {
496               g_warning ("element %s could not release locks", GST_ELEMENT_NAME (peerelement));
497             }
498           }
499         }
500
501       }
502       THR_DEBUG ("telling thread to pause, signaling");
503       g_cond_signal (thread->cond);
504       THR_DEBUG ("waiting for ack");
505       g_cond_wait (thread->cond, thread->lock);
506       THR_DEBUG ("got ack");
507       g_mutex_unlock (thread->lock);
508
509       elements = gst_bin_get_list (GST_BIN (thread));
510       while (elements) {
511         gst_element_disable_threadsafe_properties ((GstElement*)elements->data);
512         elements = g_list_next (elements);
513       }
514       break;
515     }
516     case GST_STATE_READY_TO_NULL:
517       THR_DEBUG ("telling thread to pause (null) - and joining");
518       /* MattH FIXME revisit */
519       g_mutex_lock (thread->lock);
520       THR_DEBUG ("signaling");
521       g_cond_signal (thread->cond);
522       THR_DEBUG ("waiting for ack");
523       g_cond_wait (thread->cond, thread->lock);
524       THR_DEBUG ("got ack");
525
526       /* this block of code is very tricky
527        * basically, we try to clean up the whole thread and
528        * everything related to it in the right order without
529        * triggering segfaults
530        * compare this block to the block
531        */
532
533       
534       /* in glibc 2.2.5, pthread_attr_destroy does nothing more
535        * than return 0 */
536       if (pthread_attr_destroy (&thread->attr) != 0)
537         g_warning ("pthread_attr_destroy has failed !");
538
539       GST_DEBUG (GST_CAT_THREAD, "joining pthread %ld", thread->thread_id);
540       if (pthread_join (thread->thread_id, NULL) != 0)
541         g_warning ("pthread_join has failed !\n");
542
543       thread->thread_id = -1;
544       
545       /* the stack was allocated when we created the thread
546        * using scheduler->get_preferred_stack */
547       if (thread->stack) {
548         GST_DEBUG (GST_CAT_THREAD, "freeing allocated stack (%p)", 
549                    thread->stack);
550         free (thread->stack);
551         thread->stack = NULL;
552       }
553      
554       THR_DEBUG ("unlocking mutex");
555       g_mutex_unlock (thread->lock);
556
557       GST_FLAG_UNSET (thread, GST_THREAD_STATE_REAPING);
558       GST_FLAG_UNSET (thread, GST_THREAD_STATE_STARTED);
559       GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
560
561       break;
562     case GST_STATE_PAUSED_TO_READY:
563       THR_DEBUG ("telling thread to stop spinning");
564       g_mutex_lock (thread->lock);
565       THR_DEBUG ("signaling");
566       g_cond_signal (thread->cond);
567       THR_DEBUG ("waiting for ack");
568       g_cond_wait (thread->cond, thread->lock);
569       THR_DEBUG ("got ack");
570       g_mutex_unlock (thread->lock);
571
572       break;
573     default:
574       GST_DEBUG_ELEMENT (GST_CAT_THREAD, element, "UNHANDLED STATE CHANGE! %x", transition);
575       break;
576   }
577
578   return stateset;
579 }
580
581 /**
582  * gst_thread_main_loop:
583  * @arg: the thread to start
584  *
585  * The main loop of the thread. The thread will iterate
586  * while the state is GST_THREAD_STATE_SPINNING.
587  */
588 static void *
589 gst_thread_main_loop (void *arg)
590 {
591   GstThread *thread = NULL;
592   gint stateset;
593
594   GST_DEBUG (GST_CAT_THREAD, "gst_thread_main_loop started");
595   thread = GST_THREAD (arg);
596   g_mutex_lock (thread->lock);
597
598   /* handle scheduler policy; do stuff if not the normal scheduler */
599   if (thread->sched_policy != SCHED_OTHER) {
600     struct sched_param sched_param;
601
602     memset (&sched_param, 0, sizeof (sched_param));
603     if (thread->priority == 0) {
604       thread->priority = sched_get_priority_max (thread->sched_policy);
605     }
606     sched_param.sched_priority = thread->priority;
607
608     if (sched_setscheduler (0, thread->sched_policy, &sched_param) != 0) {
609       GST_DEBUG (GST_CAT_THREAD, "not running with real-time priority");
610     }
611   }
612
613   /* set up the element's scheduler */
614   gst_scheduler_setup (GST_ELEMENT_SCHED (thread));
615   GST_FLAG_UNSET (thread, GST_THREAD_STATE_REAPING);
616
617   thread->pid = getpid();
618   THR_INFO_MAIN ("thread is running");
619
620   /* first we need to change the state of all the children */
621   if (GST_ELEMENT_CLASS (parent_class)->change_state) {
622     stateset = GST_ELEMENT_CLASS (parent_class)->change_state (GST_ELEMENT(thread));
623
624     if (stateset != GST_STATE_SUCCESS) {
625       THR_DEBUG_MAIN ("state change of children failed");
626     }
627   }
628
629   THR_DEBUG_MAIN ("indicating spinup");
630   g_cond_signal (thread->cond);
631   /* don't unlock the mutex because we hold it into the top of the while loop */
632   THR_DEBUG_MAIN ("thread has indicated spinup to parent process");
633
634   /***** THREAD IS NOW IN READY STATE *****/
635
636   /* CR1: most of this code is handshaking */
637   /* do this while the thread lives */
638   while (!GST_FLAG_IS_SET (thread, GST_THREAD_STATE_REAPING)) {
639     /* NOTE we hold the thread lock at this point */
640     /* what we do depends on what state we're in */
641     switch (GST_STATE (thread)) {
642       /* NOTE: cannot be in NULL, we're not running in that state at all */
643       case GST_STATE_READY:
644         /* wait to be set to either the NULL or PAUSED states */
645         THR_DEBUG_MAIN ("thread in %s state, waiting for either %s or %s",
646                         gst_element_state_get_name (GST_STATE_READY),
647                         gst_element_state_get_name (GST_STATE_NULL),
648                         gst_element_state_get_name (GST_STATE_PAUSED));
649         g_cond_wait (thread->cond, thread->lock);
650         
651         /* this must have happened by a state change in the thread context */
652         if (GST_STATE_PENDING (thread) != GST_STATE_NULL &&
653             GST_STATE_PENDING (thread) != GST_STATE_PAUSED) {
654           g_cond_signal (thread->cond);
655           continue;
656         }
657
658         /* been signaled, we need to state transition now and signal back */
659         gst_thread_update_state (thread);
660         THR_DEBUG_MAIN ("done with state transition, "
661                         "signaling back to parent process");
662         g_cond_signal (thread->cond);
663         /* now we decide what to do next */
664         if (GST_STATE (thread) == GST_STATE_NULL) {
665           /* REAPING must be set, we can simply break this iteration */
666           THR_DEBUG_MAIN ("set GST_THREAD_STATE_REAPING");
667           GST_FLAG_SET (thread, GST_THREAD_STATE_REAPING);
668         }
669         continue;
670
671       case GST_STATE_PAUSED:
672         /* wait to be set to either the READY or PLAYING states */
673         THR_DEBUG_MAIN ("thread in %s state, waiting for either %s or %s",
674                         gst_element_state_get_name (GST_STATE_PAUSED),
675                         gst_element_state_get_name (GST_STATE_READY),
676                         gst_element_state_get_name (GST_STATE_PLAYING));
677         g_cond_wait (thread->cond, thread->lock);
678
679         /* this must have happened by a state change in the thread context */
680         if (GST_STATE_PENDING (thread) != GST_STATE_READY &&
681             GST_STATE_PENDING (thread) != GST_STATE_PLAYING) {
682           g_cond_signal (thread->cond);
683           continue;
684         }
685
686         /* been signaled, we need to state transition now and signal back */
687         gst_thread_update_state (thread);
688         /* now we decide what to do next */
689         if (GST_STATE (thread) != GST_STATE_PLAYING) {
690           /* either READY or the state change failed for some reason */
691           g_cond_signal (thread->cond);
692           continue;
693         } 
694         else {
695           GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING);
696           /* PLAYING is coming up, so we can now start spinning */
697           while (GST_FLAG_IS_SET (thread, GST_THREAD_STATE_SPINNING)) {
698             gboolean status;
699
700             g_cond_signal (thread->cond);
701             g_mutex_unlock (thread->lock);
702             status = gst_bin_iterate (GST_BIN (thread));
703             g_mutex_lock (thread->lock);
704             /* g_cond_signal(thread->cond); */
705
706             if (!status || GST_STATE_PENDING (thread) != GST_STATE_VOID_PENDING)
707               GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
708           }
709           /* looks like we were stopped because of a statechange */
710           if (GST_STATE_PENDING (thread)) {
711             gst_thread_update_state (thread);
712           }
713           /* once we're here, SPINNING has stopped, we should signal that we're done */
714           THR_DEBUG_MAIN ("SPINNING stopped, signaling back to parent process");
715           g_cond_signal (thread->cond);
716           /* now we can wait for PAUSED */
717           continue;
718         }
719       case GST_STATE_PLAYING:
720         /* wait to be set to PAUSED */
721         THR_DEBUG_MAIN ("thread in %s state, waiting for %s",
722                         gst_element_state_get_name (GST_STATE_PLAYING),
723                         gst_element_state_get_name (GST_STATE_PAUSED));
724         g_cond_wait (thread->cond,thread->lock);
725
726         /* been signaled, we need to state transition now and signal back */
727         gst_thread_update_state (thread);
728         g_cond_signal (thread->cond);
729         /* now we decide what to do next */
730         /* there's only PAUSED, we we just wait for it */
731         continue;
732       case GST_STATE_NULL:
733         THR_DEBUG_MAIN ("thread in %s state, preparing to die",
734                         gst_element_state_get_name (GST_STATE_NULL));
735         GST_FLAG_SET (thread, GST_THREAD_STATE_REAPING);
736         break;
737       default:
738         g_assert_not_reached ();
739         break;
740     }
741   }
742
743   /* THREAD HAS STOPPED RUNNING */
744   
745   /* we need to destroy the scheduler here because it has mapped it's
746    * stack into the threads stack space */
747   gst_scheduler_reset (GST_ELEMENT_SCHED (thread));
748
749   /* since we don't unlock at the end of the while loop, do it here */
750   g_mutex_unlock (thread->lock);
751
752   GST_INFO (GST_CAT_THREAD, "gstthread: thread \"%s\" is stopped",
753                   GST_ELEMENT_NAME (thread));
754
755   g_signal_emit (G_OBJECT (thread), gst_thread_signals[SHUTDOWN], 0);
756
757   return NULL;
758 }
759
760 #ifndef GST_DISABLE_LOADSAVE
761 static xmlNodePtr
762 gst_thread_save_thyself (GstObject *object,
763                          xmlNodePtr self)
764 {
765   if (GST_OBJECT_CLASS (parent_class)->save_thyself)
766     GST_OBJECT_CLASS (parent_class)->save_thyself (object, self);
767   return NULL;
768 }
769
770 static void
771 gst_thread_restore_thyself (GstObject *object,
772                             xmlNodePtr self)
773 {
774   GST_DEBUG (GST_CAT_THREAD,"gstthread: restore");
775
776   if (GST_OBJECT_CLASS (parent_class)->restore_thyself)
777     GST_OBJECT_CLASS (parent_class)->restore_thyself (object, self);
778 }
779 #endif /* GST_DISABLE_LOADSAVE */