commit afa52465d56bd563a7007fd10f0a2bc42ca22050
Author: Wim Taymans <wim.taymans@collabora.co.uk>
Date:   Wed Jan 28 18:11:37 2009 +0100

    Add methods to use GstPoll for timeouts
    
    Add API to create a GstPoll for handling timeouts. For this we only need a
    controllable GstPoll and we allow multiple threads to wait(). We also need
    methods to read and write the control socket to unlock pending _wait()
    operations.
    API: GstPoll::gst_poll_new_timer()
    API: GstPoll::gst_poll_read_control()
    API: GstPoll::gst_poll_write_control()

diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt
index 5da67ce..0e79ca0 100644
--- a/docs/gst/gstreamer-sections.txt
+++ b/docs/gst/gstreamer-sections.txt
@@ -1718,11 +1718,14 @@ gst_poll_fd_ignored
 gst_poll_fd_init
 gst_poll_free
 gst_poll_new
+gst_poll_new_timer
 gst_poll_remove_fd
 gst_poll_restart
 gst_poll_set_controllable
 gst_poll_set_flushing
 gst_poll_wait
+gst_poll_read_control
+gst_poll_write_control
 </SECTION>
 
 <SECTION>
diff --git a/gst/gstpoll.c b/gst/gstpoll.c
index 6e77c2b..2b8f819 100644
--- a/gst/gstpoll.c
+++ b/gst/gstpoll.c
@@ -91,17 +91,19 @@
 /* FIXME: Shouldn't we check or return the return value
  * of write()?
  */
-#define SEND_COMMAND(set, command)                   \
+#define SEND_COMMAND(set, command, result)           \
 G_STMT_START {                                       \
   unsigned char c = command;                         \
-  ssize_t res;                                       \
-  res = write (set->control_write_fd.fd, &c, 1);     \
-  set->control_pending = TRUE;                       \
+  result = write (set->control_write_fd.fd, &c, 1);  \
+  if (result > 0)                                    \
+    set->control_pending = TRUE;                     \
 } G_STMT_END
 
 #define READ_COMMAND(set, command, res)              \
 G_STMT_START {                                       \
   res = read (set->control_read_fd.fd, &command, 1); \
+  if (res <= 0)                                      \
+    set->control_pending = FALSE;                    \
 } G_STMT_END
 
 #define GST_POLL_CMD_WAKEUP  'W'        /* restart the poll/select call */
@@ -149,9 +151,10 @@ struct _GstPoll
 
   gboolean controllable;
   gboolean new_controllable;
-  gboolean waiting;
+  gint waiting;
   gboolean flushing;
   gboolean control_pending;
+  gboolean timer;
 };
 
 static gint
@@ -498,6 +501,34 @@ not_controllable:
 }
 
 /**
+ * gst_poll_new_timer:
+ *
+ * Create a new poll object that can be used for scheduling cancelable timeouts.
+ * A timeout is performed with gst_poll_wait(). Multiple timeouts can be
+ * performed from different threads. 
+ *
+ * Returns: a new #GstPoll, or %NULL in case of an error. Free with
+ * gst_poll_free().
+ *
+ * Since: 0.10.23
+ */
+GstPoll *
+gst_poll_new_timer (void)
+{
+  GstPoll *poll;
+
+  /* make a new controllable poll set */
+  if (!(poll = gst_poll_new (TRUE)))
+    goto done;
+
+  /* we are a timer */
+  poll->timer = TRUE;
+
+done:
+  return poll;
+}
+
+/**
  * gst_poll_free:
  * @set: a file descriptor set.
  *
@@ -995,7 +1026,6 @@ gst_poll_check_ctrl_commands (GstPoll * set, gint res, gboolean * restarting)
       READ_COMMAND (set, cmd, result);
       if (result <= 0) {
         /* no more commands, quit the loop */
-        set->control_pending = FALSE;
         break;
       }
 
@@ -1042,15 +1072,16 @@ gst_poll_wait (GstPoll * set, GstClockTime timeout)
 
   g_mutex_lock (set->lock);
 
-  /* we cannot wait from multiple threads */
-  if (G_UNLIKELY (set->waiting))
+  /* we cannot wait from multiple threads unless we are a timer */
+  if (G_UNLIKELY (set->waiting > 0 && !set->timer))
     goto already_waiting;
 
   /* flushing, exit immediatly */
   if (G_UNLIKELY (set->flushing))
     goto flushing;
 
-  set->waiting = TRUE;
+  /* add one more waiter */
+  set->waiting++;
 
   do {
     GstPollMode mode;
@@ -1213,8 +1244,8 @@ gst_poll_wait (GstPoll * set, GstClockTime timeout)
 
     g_mutex_lock (set->lock);
 
-    /* FIXME, can we only do this check when (res > 0)? */
-    gst_poll_check_ctrl_commands (set, res, &restarting);
+    if (!set->timer)
+      gst_poll_check_ctrl_commands (set, res, &restarting);
 
     /* update the controllable state if needed */
     set->controllable = set->new_controllable;
@@ -1227,7 +1258,7 @@ gst_poll_wait (GstPoll * set, GstClockTime timeout)
     }
   } while (G_UNLIKELY (restarting));
 
-  set->waiting = FALSE;
+  set->waiting--;
 
   g_mutex_unlock (set->lock);
 
@@ -1297,7 +1328,7 @@ gst_poll_set_controllable (GstPoll * set, gboolean controllable)
 
   /* delay the change of the controllable state if we are waiting */
   set->new_controllable = controllable;
-  if (!set->waiting)
+  if (set->waiting == 0)
     set->controllable = controllable;
 
   g_mutex_unlock (set->lock);
@@ -1332,11 +1363,13 @@ gst_poll_restart (GstPoll * set)
 
   g_mutex_lock (set->lock);
 
-  if (set->controllable && set->waiting) {
+  if (set->controllable && set->waiting > 0) {
 #ifndef G_OS_WIN32
+    gint result;
+
     /* if we are waiting, we can send the command, else we do not have to
      * bother, future calls will automatically pick up the new fdset */
-    SEND_COMMAND (set, GST_POLL_CMD_WAKEUP);
+    SEND_COMMAND (set, GST_POLL_CMD_WAKEUP, result);
 #else
     SetEvent (set->wakeup_event);
 #endif
@@ -1367,12 +1400,14 @@ gst_poll_set_flushing (GstPoll * set, gboolean flushing)
   /* update the new state first */
   set->flushing = flushing;
 
-  if (flushing && set->controllable && set->waiting) {
+  if (flushing && set->controllable && set->waiting > 0) {
     /* we are flushing, controllable and waiting, wake up the waiter. When we
      * stop the flushing operation we don't clear the wakeup fd here, this will
      * happen in the _wait() thread. */
 #ifndef G_OS_WIN32
-    SEND_COMMAND (set, GST_POLL_CMD_WAKEUP);
+    gint result;
+
+    SEND_COMMAND (set, GST_POLL_CMD_WAKEUP, result);
 #else
     SetEvent (set->wakeup_event);
 #endif
@@ -1380,3 +1415,80 @@ gst_poll_set_flushing (GstPoll * set, gboolean flushing)
 
   g_mutex_unlock (set->lock);
 }
+
+/**
+ * gst_poll_write_control:
+ * @set: a #GstPoll.
+ *
+ * Write a byte to the control socket of the controllable @set.
+ * This function is mostly usefull for timer #GstPoll objects created with
+ * gst_poll_new_timer(). 
+ *
+ * It will make any current and future gst_poll_wait() function return with
+ * 1, meaning the control socket is set. After an equal amount of calls to
+ * gst_poll_read_control() have been performed, calls to gst_poll_wait() will
+ * block again until their timeout expired.
+ *
+ * Returns: %TRUE on success. %FALSE when @set is not controllable or when the
+ * byte could not be written.
+ *
+ * Since: 0.10.23
+ */
+gboolean
+gst_poll_write_control (GstPoll * set)
+{
+  gboolean res = FALSE;
+
+  g_return_val_if_fail (set != NULL, FALSE);
+
+  g_mutex_lock (set->lock);
+  if (set->controllable) {
+#ifndef G_OS_WIN32
+    gint result;
+
+    SEND_COMMAND (set, GST_POLL_CMD_WAKEUP, result);
+    res = (result > 0);
+#else
+    SetEvent (set->wakeup_event);
+#endif
+  }
+  g_mutex_unlock (set->lock);
+
+  return res;
+}
+
+/**
+ * gst_poll_read_control:
+ * @set: a #GstPoll.
+ *
+ * Read a byte from the control socket of the controllable @set.
+ * This function is mostly usefull for timer #GstPoll objects created with
+ * gst_poll_new_timer(). 
+ *
+ * Returns: %TRUE on success. %FALSE when @set is not controllable or when there
+ * was no byte to read.
+ *
+ * Since: 0.10.23
+ */
+gboolean
+gst_poll_read_control (GstPoll * set)
+{
+  gboolean res = FALSE;
+
+  g_return_val_if_fail (set != NULL, FALSE);
+
+  g_mutex_lock (set->lock);
+  if (set->controllable) {
+#ifndef G_OS_WIN32
+    guchar cmd;
+    gint result;
+    READ_COMMAND (set, cmd, result);
+    res = (result > 0);
+#else
+    ResetEvent (set->wakeup_event);
+#endif
+  }
+  g_mutex_unlock (set->lock);
+
+  return res;
+}
diff --git a/gst/gstpoll.h b/gst/gstpoll.h
index 78a9f39..5503fbe 100644
--- a/gst/gstpoll.h
+++ b/gst/gstpoll.h
@@ -64,6 +64,7 @@ typedef struct {
 #define GST_POLL_FD_INIT  { -1, -1 }
 
 GstPoll*        gst_poll_new              (gboolean controllable);
+GstPoll*        gst_poll_new_timer        (void);
 void            gst_poll_free             (GstPoll *set);
 
 void            gst_poll_fd_init          (GstPollFD *fd);
@@ -86,6 +87,9 @@ gboolean        gst_poll_set_controllable (GstPoll *set, gboolean controllable);
 void            gst_poll_restart          (GstPoll *set);
 void            gst_poll_set_flushing     (GstPoll *set, gboolean flushing);
 
+gboolean        gst_poll_write_control    (GstPoll *set);
+gboolean        gst_poll_read_control     (GstPoll *set);
+
 G_END_DECLS
 
 #endif /* __GST_POLL_H__ */
