gst/gstbin.c: Cleanup the get_state() function a little, make sure it iterates the...
authorWim Taymans <wim.taymans@gmail.com>
Mon, 27 Jun 2005 18:11:24 +0000 (18:11 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Mon, 27 Jun 2005 18:11:24 +0000 (18:11 +0000)
Original commit message from CVS:
* gst/gstbin.c: (gst_bin_get_state), (gst_bin_iterate_state_order),
(gst_bin_change_state):
Cleanup the get_state() function a little, make sure it
iterates the same set of elements.
Added stub iterate_state_order().

ChangeLog
gst/gstbin.c

index 2959246..891346a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2005-06-27  Wim Taymans  <wim@fluendo.com>
+
+       * gst/gstbin.c: (gst_bin_get_state), (gst_bin_iterate_state_order),
+       (gst_bin_change_state):
+       Cleanup the get_state() function a little, make sure it
+       iterates the same set of elements.
+       Added stub iterate_state_order().
+
 2005-06-27  Thomas Vander Stichele  <thomas at apestaart dot org>
 
        * docs/gst/gstreamer-docs.sgml:
index 2e4f135..bcd27f3 100644 (file)
@@ -841,36 +841,58 @@ gst_bin_get_state (GstElement * element, GstElementState * state,
   GList *children;
   guint32 children_cookie;
   gboolean zero_timeout;
+  gboolean have_no_preroll;
 
   GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state");
 
   zero_timeout = timeout != NULL && timeout->tv_sec == 0
       && timeout->tv_usec == 0;
 
+  /* lock bin, no element can be added or removed between going into
+   * the quick scan and the blocking wait. */
+  GST_LOCK (bin);
+
+restart:
+  have_no_preroll = FALSE;
+
   /* if we have a non zero timeout we must make sure not to block
    * on the sinks when we have NO_PREROLL elements. This is why we do
    * a quick check if there are still NO_PREROLL elements. We also
    * catch the error elements this way. */
-  GST_STATE_LOCK (bin);
   if (!zero_timeout) {
-    GST_LOCK (bin);
     GTimeVal tv;
-    gboolean have_no_preroll = FALSE;
     gboolean have_async = FALSE;
 
     GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "checking for NO_PREROLL");
     /* use 0 timeout so we don't block on the sinks */
     GST_TIME_TO_TIMEVAL (0, tv);
     children = bin->children;
+    children_cookie = bin->children_cookie;
     while (children) {
       GstElement *child = GST_ELEMENT_CAST (children->data);
 
+      gst_object_ref (GST_OBJECT_CAST (child));
+      /* now we release the lock to enter a non blocking wait. We 
+       * release the lock anyway since we can. */
+      GST_UNLOCK (bin);
+
       ret = gst_element_get_state (child, NULL, NULL, &tv);
+
+      gst_object_unref (GST_OBJECT_CAST (child));
+
+      /* now grab the lock to iterate to the next child */
+      GST_LOCK (bin);
+      if (G_UNLIKELY (children_cookie != bin->children_cookie)) {
+        /* child added/removed during state change, restart. We need
+         * to restart with the quick check as a no-preroll element could
+         * have been added here and we don't want to block on sinks then.*/
+        goto restart;
+      }
+
       switch (ret) {
           /* report FAILURE or NO_PREROLL immediatly */
         case GST_STATE_FAILURE:
-          GST_UNLOCK (bin);
-          goto report;
+          goto done;
         case GST_STATE_NO_PREROLL:
           /* we have to continue scanning as there might be
            * ERRORS too */
@@ -884,47 +906,50 @@ gst_bin_get_state (GstElement * element, GstElementState * state,
       }
       children = g_list_next (children);
     }
-    GST_UNLOCK (bin);
     /* if we get here, we have no FAILURES, check for any NO_PREROLL
      * elements then. */
-    if (have_no_preroll)
-      goto report;
+    if (have_no_preroll) {
+      ret = GST_STATE_NO_PREROLL;
+      goto done;
+    }
 
     /* if we get here, no NO_PREROLL elements are in the pipeline */
     GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "no NO_PREROLL elements");
-    GST_STATE_NO_PREROLL (element) = FALSE;
 
     /* if no ASYNC elements exist we don't even have to poll with a
      * timeout again */
-    if (!have_async)
-      goto report;
+    if (!have_async) {
+      ret = GST_STATE_SUCCESS;
+      goto done;
+    }
   }
-  /* we have to release the state lock as we might block when querying
-   * the children, holding the lock for too long for no reason. */
-  GST_STATE_UNLOCK (bin);
 
   /* next we poll all children for their state to see if one of them
-   * is still busy with its state change. */
-  GST_LOCK (bin);
-restart:
+   * is still busy with its state change. We did not release the bin lock
+   * yet so the elements are the same as the ones from the quick scan. */
   children = bin->children;
   children_cookie = bin->children_cookie;
   while (children) {
     GstElement *child = GST_ELEMENT_CAST (children->data);
 
     gst_object_ref (GST_OBJECT_CAST (child));
+    /* now we release the lock to enter the potentialy blocking wait */
     GST_UNLOCK (bin);
 
-    /* ret is ASYNC if some child is still performing the state change */
+    /* ret is ASYNC if some child is still performing the state change
+     * ater the timeout. */
     ret = gst_element_get_state (child, NULL, NULL, timeout);
 
     gst_object_unref (GST_OBJECT_CAST (child));
 
     /* now grab the lock to iterate to the next child */
     GST_LOCK (bin);
-    if (G_UNLIKELY (children_cookie != bin->children_cookie))
-      /* child added/removed during state change, restart */
+    if (G_UNLIKELY (children_cookie != bin->children_cookie)) {
+      /* child added/removed during state change, restart. We need
+       * to restart with the quick check as a no-preroll element could
+       * have been added here and we don't want to block on sinks then.*/
       goto restart;
+    }
 
     switch (ret) {
       case GST_STATE_SUCCESS:
@@ -945,14 +970,17 @@ restart:
     }
     children = g_list_next (children);
   }
-  /* if we got here, all elements can to preroll */
-  GST_STATE_NO_PREROLL (element) = FALSE;
+  /* if we got here, all elements can do preroll */
+  have_no_preroll = FALSE;
+
 done:
   GST_UNLOCK (bin);
 
-  /* now we can take the state lock */
+  /* now we can take the state lock, it is possible that new elements
+   * are added now and we still report the old state. No problem though as
+   * the return is still consistent, the effect is as if the element was
+   * added after this function completed. */
   GST_STATE_LOCK (bin);
-report:
   switch (ret) {
     case GST_STATE_SUCCESS:
       /* we can commit the state */
@@ -973,6 +1001,8 @@ report:
   if (pending)
     *pending = GST_STATE_PENDING (element);
 
+  GST_STATE_NO_PREROLL (element) = have_no_preroll;
+
   GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
       "state current: %s, pending: %s, error: %d, no_preroll: %d, result: %d",
       gst_element_state_get_name (GST_STATE (element)),
@@ -990,6 +1020,33 @@ append_child (gpointer child, GQueue * queue)
   g_queue_push_tail (queue, child);
 }
 
+/**
+ * gst_bin_iterate_state_order:
+ * @bin: #Gstbin to iterate on
+ *
+ * Get an iterator for the elements in this bin in the order
+ * in which a state change should be performed on them. This 
+ * means that first the sinks and then the other elements will
+ * be returned.
+ * Each element will have its refcount increased, so unref
+ * after use.
+ *
+ * MT safe.
+ *
+ * Returns: a #GstIterator of #GstElements. gst_iterator_free after use.
+ */
+GstIterator *
+gst_bin_iterate_state_order (GstBin * bin)
+{
+  GstIterator *result;
+
+  g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+
+  result = NULL;
+
+  return result;
+}
+
 /* this function is called with the STATE_LOCK held. It works
  * as follows:
  *
@@ -1017,7 +1074,7 @@ gst_bin_change_state (GstElement * element)
   guint32 children_cookie;
   GQueue *elem_queue;           /* list of elements waiting for a state change */
   GQueue *semi_queue;           /* list of elements with no connected srcpads */
-  GQueue *temp;                 /* temp queue of leftovers */
+  GQueue *temp;                 /* queue of leftovers */
 
   bin = GST_BIN (element);
 
@@ -1033,7 +1090,7 @@ gst_bin_change_state (GstElement * element)
   if (pending == GST_STATE_VOID_PENDING)
     return GST_STATE_SUCCESS;
 
-  /* all elements added to this queue should have their refcount
+  /* all elements added to these queues should have their refcount
    * incremented */
   elem_queue = g_queue_new ();
   semi_queue = g_queue_new ();