Index: gst/gstpad.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstpad.c,v
retrieving revision 1.515
diff -u -r1.515 gstpad.c
--- gst/gstpad.c	12 Jun 2006 09:28:35 -0000	1.515
+++ gst/gstpad.c	1 Jul 2006 11:22:58 -0000
@@ -887,12 +887,14 @@
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
 
-  if (GST_IS_GHOST_PAD (pad)) {
-    pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
-    if (!pad) {
+  while (GST_IS_GHOST_PAD (pad)) {
+    GstPad * tpad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
+    if (was_ghost)
+      gst_object_unref (pad);
+    if (!tpad)
       return FALSE;
-    }
     was_ghost = TRUE;
+    pad = tpad;
   }
 
   GST_OBJECT_LOCK (pad);
@@ -3070,7 +3072,28 @@
 #endif /* GST_DISABLE_LOADSAVE */
 
 /*
- * should be called with pad lock held
+ * should be called with pad OBJECT_LOCK and STREAM_LOCK helt. 
+ * GST_PAD_IS_BLOCK (pad) == TRUE when this function is
+ * called.
+ *
+ * This function perform the pad blocking when an event, buffer push
+ * or buffer_alloc is performed on a _SRC_ pad. It blocks the
+ * streaming thread after informing the pad has been blocked. 
+ *
+ * An application can with this method wait and block any streaming
+ * thread and perform operations such as seeking or linking.
+ *
+ * Two methods are available for notifying the application of the
+ * block: 
+ * - the callback method, which happens in the STREAMING thread with
+ *   the STREAM_LOCK helt. With this method, the most usefull way of
+ *   dealing with the callback is to post a message to the main thread
+ *   where the pad block can then be handled outside of the streaming
+ *   thread. With the last method one can perform all operations such
+ *   as doing a state change, linking, unblocking, seeking etc on the
+ *   pad.
+ * - the GCond signal method, which makes any thread unblock when
+ *   the pad block happens.
  *
  * MT safe.
  */
@@ -3084,36 +3107,67 @@
   GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
       "signal block taken on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
 
-  /* need to grab extra ref for the callbacks */
+  /* flushing, don't bother trying to block and return WRONG_STATE
+   * right away */
+  if (GST_PAD_IS_FLUSHING (pad))
+    goto flushingnonref;
+
+  /* we grab an extra ref for the callbacks, this is probably not
+   * needed (callback code does not have a ref and cannot unref). I
+   * think this was done to make it possible to unref the element in
+   * the callback, which is in the end totally impossible as it 
+   * requires grabbing the STREAM_LOCK and OBJECT_LOCK which are
+   * all taken when calling this function. */
   gst_object_ref (pad);
 
+  /* we either have a callback installed to notify the block or
+   * some other thread is doing a GCond wait. */
   callback = pad->block_callback;
   if (callback) {
+    /* there is a callback installed, call it. We release the
+     * lock so that the callback can do something usefull with the
+     * pad */
     user_data = pad->block_data;
     GST_OBJECT_UNLOCK (pad);
     callback (pad, TRUE, user_data);
     GST_OBJECT_LOCK (pad);
   } else {
+    /* no callback, signal the thread that is doing a GCond wait
+     * if any. */
     GST_PAD_BLOCK_SIGNAL (pad);
   }
 
+  /* OBJECT_LOCK could have been released when we did the callback, which
+   * then could have made the pad unblock so we need to check the blocking
+   * condition again.   */
   while (GST_PAD_IS_BLOCKED (pad)) {
     if (GST_PAD_IS_FLUSHING (pad))
       goto flushing;
+
+    /* now we block the streaming thread. It can be unlocked when we 
+     * deactivate the pad (which will also set the FLUSHING flag) or
+     * when the pad is unblocked. A flushing event will also unblock
+     * the pad after setting the FLUSHING flag. */
     GST_PAD_BLOCK_WAIT (pad);
+
+    /* see if we got unlocked by a flush or not */
     if (GST_PAD_IS_FLUSHING (pad))
       goto flushing;
   }
 
   GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "got unblocked");
 
+  /* when we get here, the pad is unblocked again and we perform
+   * the needed unblock code. */
   callback = pad->block_callback;
   if (callback) {
+    /* we need to call the callback */
     user_data = pad->block_data;
     GST_OBJECT_UNLOCK (pad);
     callback (pad, FALSE, user_data);
     GST_OBJECT_LOCK (pad);
   } else {
+    /* we need to signal the thread waiting on the GCond */
     GST_PAD_BLOCK_SIGNAL (pad);
   }
 
@@ -3121,8 +3175,16 @@
 
   return ret;
 
+flushingnonref:
+ {
+   GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+      "pad %s:%s was flushing", GST_DEBUG_PAD_NAME (pad));
+   return GST_FLOW_WRONG_STATE;
+ }
 flushing:
   {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+      "pad %s:%s became flushing", GST_DEBUG_PAD_NAME (pad));
     gst_object_unref (pad);
     return GST_FLOW_WRONG_STATE;
   }
@@ -3634,7 +3696,10 @@
   g_return_val_if_fail (event != NULL, FALSE);
   g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
 
+  GST_LOG_OBJECT (pad, "event:%s", GST_EVENT_TYPE_NAME (event));
+
   GST_OBJECT_LOCK (pad);
+  /* first update the FLUSHING flag in case of flushing events */
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_FLUSH_START:
       GST_PAD_SET_FLUSHING (pad);
@@ -3647,9 +3712,21 @@
   }
 
   if (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad))) {
+    /* when the pad is blocked, we handle the FLUSH_START and FLUSH_STOP
+     * events specially */
     if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START) {
+      /* flush start will have set the FLUSHING flag and will then
+       * unlock all threads doing a GCond wait on the blocked pad. This
+       * will typically unblock the STREAMING thread blocked on a pad. */
       GST_PAD_BLOCK_SIGNAL (pad);
-    }
+      goto flushed;
+    } else if (GST_PAD_IS_FLUSHING (pad) || (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP)) {
+      /* the pad is flushing, discard the events including the FLUSH_STOP */
+      goto flushed;
+    } else while (GST_PAD_IS_BLOCKED (pad))
+      /* else block the event as long as the pad is blocked */
+      if (handle_pad_block (pad) != GST_FLOW_OK)
+        goto flushed;
   }
 
   if (G_UNLIKELY (GST_PAD_DO_EVENT_SIGNALS (pad) > 0)) {
@@ -3688,6 +3765,14 @@
     GST_OBJECT_UNLOCK (pad);
     return FALSE;
   }
+
+ flushed:
+  {
+    GST_DEBUG_OBJECT (pad, "Not forwarding event since we're flushing and blocking");
+    gst_event_unref (event);
+    GST_OBJECT_UNLOCK (pad);
+    return TRUE;
+  }
 }
 
 /**
Index: tests/check/gst/gstevent.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/tests/check/gst/gstevent.c,v
retrieving revision 1.14
diff -u -r1.14 gstevent.c
--- tests/check/gst/gstevent.c	8 May 2006 09:52:33 -0000	1.14
+++ tests/check/gst/gstevent.c	1 Jul 2006 11:22:58 -0000
@@ -274,6 +274,7 @@
     gboolean expect_before_q, GstPad * fake_srcpad)
 {
   GstEvent *event;
+  GstPad *peer;
   gint i;
 
   got_event_before_q = got_event_after_q = NULL;
@@ -288,8 +289,15 @@
   got_event_time.tv_sec = 0;
   got_event_time.tv_usec = 0;
 
+  /* We block the pad and send the event on the peerpad */
   fail_unless (gst_pad_set_blocked (fake_srcpad, TRUE) == TRUE);
-  gst_pad_push_event (pad, event);
+
+  fail_if ((peer = gst_pad_get_peer (pad)) == NULL);
+
+  gst_pad_send_event (peer, event);
+
+  gst_object_unref (peer);
+
   fail_unless (gst_pad_set_blocked (fake_srcpad, FALSE) == TRUE);
 
   /* Wait up to 5 seconds for the event to appear */
