GEvent - Proposal for a new threading structure for GLib



Working full-time on Evolution, I find myself doing a fair amount of
multi-threaded programming.  I've often wished GLib had something
equivalent to Python's threading.Event class [1], which is a simple but
useful mechanism for signaling an event to other threads.

I'd like to propose such a mechanism for GLib, which I call a GEvent.

A GEvent manages a mutex-guarded boolean and condition variable.
Threads can set, test, clear, and block on a GEvent without worrying
about the details of locking/unlocking or checking a condition variable
properly.

The most compelling use case I see for this in Evolution is for message
passing.  Threads that push messages to an asynchronous queue or a
thread pool are often interested in knowing when the message has been
processed and its output is available.  A simple way to coordinate this
would be to embed a GEvent in each message and have the thread
processing the message set the event when it is finished.  That would
then wake up any threads that are blocked on the event.

There's obviously many other use cases.  Any textbook on concurrent
programming should provide plenty of examples.

I've attached a patch that implements and documents GEvent.  It closely
mimics the Python class, so I won't bother describing the API here.

Would this be a worthwhile addition to GLib?

Matthew Barnes

[1] http://docs.python.org/lib/event-objects.html
Index: docs/reference/glib/glib-sections.txt
===================================================================
RCS file: /cvs/gnome/glib/docs/reference/glib/glib-sections.txt,v
retrieving revision 1.152
diff -u -p -r1.152 glib-sections.txt
--- docs/reference/glib/glib-sections.txt	9 Oct 2006 04:23:58 -0000	1.152
+++ docs/reference/glib/glib-sections.txt	16 Nov 2006 20:49:57 -0000
@@ -590,6 +590,16 @@ g_cond_timed_wait
 g_cond_free
 
 <SUBSECTION>
+GEvent
+g_event_new
+g_event_is_set
+g_event_set
+g_event_clear
+g_event_wait
+g_event_timed_wait
+g_event_free
+
+<SUBSECTION>
 GPrivate
 g_private_new
 g_private_get
Index: docs/reference/glib/tmpl/threads.sgml
===================================================================
RCS file: /cvs/gnome/glib/docs/reference/glib/tmpl/threads.sgml,v
retrieving revision 1.57
diff -u -p -r1.57 threads.sgml
--- docs/reference/glib/tmpl/threads.sgml	9 Aug 2006 21:32:12 -0000	1.57
+++ docs/reference/glib/tmpl/threads.sgml	16 Nov 2006 20:49:58 -0000
@@ -1397,6 +1397,95 @@ Destroys the #GCond.
 @cond: a #GCond.
 
 
+<!-- ##### STRUCT GEvent ##### -->
+<para>
+A #GEvent is a simple mechanism for communicating between threads: one
+thread signals an event and the others wait for it.  A #GEvent manages
+an internal flag that can be set to %TRUE or %FALSE.  Threads can block
+on a #GEvent until its flag becomes %TRUE.
+</para>
+
+
+<!-- ##### FUNCTION g_event_new ##### -->
+<para>
+Creates a new #GEvent object.  The internal flag is initially %FALSE.
+</para>
+
+<note>
+<para>
+This function will abort if g_thread_init() has not been called yet.
+</para>
+</note>
+
+ Returns: a new #GEvent
+
+
+<!-- ##### FUNCTION g_event_is_set ##### -->
+<para>
+Returns the state of the internal flag.
+</para>
+
+ event: a #GEvent
+ Returns: %TRUE if the internal flag is set
+
+
+<!-- ##### FUNCTION g_event_set ##### -->
+<para>
+Sets the internal flag to %TRUE.  All threads waiting on the event are
+woken up.  Threads that call g_event_wait() or g_event_timed_wait()
+once the flag is %TRUE will not block at all.
+</para>
+
+ event: a #GEvent
+
+
+<!-- ##### FUNCTION g_event_clear ##### -->
+<para>
+Resets the internal flag to %FALSE.  Subsequent calls to g_event_wait()
+or g_event_timed_wait() will block until the flag becomes %TRUE.
+</para>
+
+ event: a #GEvent
+
+
+<!-- ##### FUNCTION g_event_wait ##### -->
+<para>
+Block until the internal flag becomes %TRUE.  If the flag is %TRUE on
+entry, return immediately.
+</para>
+
+ event: a #GEvent
+
+
+<!-- ##### FUNCTION g_event_timed_wait ##### -->
+<para>
+Blocks until the internal flag becomes %TRUE, or until the time specified
+by @abs_time.  If the flag is %TRUE on entry, return immediately.  The
+return value indicates the state of the flag after waiting.
+</para>
+
+<para>
+If @abs_time is %NULL, g_event_timed_wait() acts like g_event_wait().
+</para>
+
+<para>
+To easily calculate @abs_time, a combination of g_get_current_time() and
+g_time_val_add() can be used.
+</para>
+
+ event: a #GEvent
+ abs_time: a #GTimeVal, determining the final time
+ Returns: %TRUE if the internal flag is now set
+
+
+<!-- ##### FUNCTION g_event_free ##### -->
+<para>
+Destroys @event.
+</para>
+
+ event: a #GEvent
+
+
 <!-- ##### STRUCT GPrivate ##### -->
 <para>
 The #GPrivate struct is an opaque data structure to represent a thread
Index: glib/gthread.c
===================================================================
RCS file: /cvs/gnome/glib/glib/gthread.c,v
retrieving revision 1.54
diff -u -p -r1.54 gthread.c
--- glib/gthread.c	23 May 2006 01:33:16 -0000	1.54
+++ glib/gthread.c	16 Nov 2006 20:50:03 -0000
@@ -843,6 +843,98 @@ g_static_rw_lock_free (GStaticRWLock* lo
   g_static_mutex_free (&lock->mutex);
 }
 
+struct _GEvent {
+	GMutex *mutex;
+	GCond *cond;
+	gboolean flag;
+};
+
+GEvent *
+g_event_new (void)
+{
+  GEvent *event;
+
+  event = g_slice_new (GEvent);
+  event->mutex = g_mutex_new ();
+  event->cond = g_cond_new ();
+  event->flag = FALSE;
+
+  return event;
+}
+
+gboolean
+g_event_is_set (GEvent *event)
+{
+  gboolean retval;
+
+  g_return_val_if_fail (event, FALSE);
+
+  g_mutex_lock (event->mutex);
+  retval = event->flag;
+  g_mutex_unlock (event->mutex);
+
+  return retval;
+}
+
+void
+g_event_set (GEvent *event)
+{
+  g_return_if_fail (event);
+
+  g_mutex_lock (event->mutex);
+  event->flag = TRUE;
+  g_cond_broadcast (event->cond);
+  g_mutex_unlock (event->mutex);
+}
+
+void
+g_event_clear (GEvent *event)
+{
+  g_return_if_fail (event);
+
+  g_mutex_lock (event->mutex);
+  event->flag = FALSE;
+  g_mutex_unlock (event->mutex);
+}
+
+void
+g_event_wait (GEvent *event)
+{
+  g_return_if_fail (event);
+
+  g_mutex_lock (event->mutex);
+  while (!event->flag)
+    g_cond_wait (event->cond, event->mutex);
+  g_mutex_unlock (event->mutex);
+}
+
+gboolean
+g_event_timed_wait (GEvent *event, GTimeVal *abs_time)
+{
+  gboolean retval;
+
+  g_return_val_if_fail (event, FALSE);
+
+  g_mutex_lock (event->mutex);
+  while (!event->flag)
+    if (!g_cond_timed_wait (event->cond, event->mutex, abs_time))
+      break;
+  retval = event->flag;
+  g_mutex_unlock (event->mutex);
+
+  return retval;
+}
+
+void
+g_event_free (GEvent *event)
+{
+  g_return_if_fail (event);
+
+  g_mutex_free (event->mutex);
+  g_cond_free (event->cond);
+  g_slice_free (GEvent, event);
+}
+
 /**
  * g_thread_foreach
  * @thread_func: function to call for all GThread structures
Index: glib/gthread.h
===================================================================
RCS file: /cvs/gnome/glib/glib/gthread.h,v
retrieving revision 1.27
diff -u -p -r1.27 gthread.h
--- glib/gthread.h	11 May 2006 00:18:46 -0000	1.27
+++ glib/gthread.h	16 Nov 2006 20:50:03 -0000
@@ -293,6 +293,17 @@ void      g_static_rw_lock_free         
 void	  g_thread_foreach         	  (GFunc    	  thread_func,
 					   gpointer 	  user_data);
 
+typedef struct _GEvent GEvent;
+
+GEvent *  g_event_new                   (void);
+gboolean  g_event_is_set                (GEvent *event);
+void      g_event_set                   (GEvent *event);
+void      g_event_clear                 (GEvent *event);
+void      g_event_wait                  (GEvent *event);
+gboolean  g_event_timed_wait            (GEvent *event,
+                                         GTimeVal *abs_time);
+void      g_event_free                  (GEvent *event);
+
 typedef enum
 {
   G_ONCE_STATUS_NOTCALLED,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]