Changes to the GLib main loop



Hi Owen,

while trying to port ORBit-mt to the new GLib main loop I noticed, that the
one context per thread concept really isn't of great use. Instead I would like
the GMainContext to be a object of its own. There are several reasons:

1) I can add sources to a context and only then create a thread, where a 
   main loop for that context is running (not possible now).
2) I would want to be able to run two context's in the same thread, for
   example, if my GTK/ORBit program is inactive the context with both the 
   ORBit and the GTK fds is running, if a ORBit request is in progress
   a context with only the ORBit fds is running to just wait for the reply 
   and to avoid reentrance from GTK.

To achieve that the interface has changed as follows (gmain.h)

GMainContext *g_main_context_new       (void);
void          g_main_context_ref       (GMainContext *context);
void          g_main_context_unref     (GMainContext *context);
GMainContext *g_main_context_default   (void);

The default context handling work as before. And also for singlethreaded
programs this is not slower than the current solution.

For multithreaded programs there is the added advantage, that you can use the
usual idioms in other threads as well (though they will do nothing then, just
wait, whenever may_block is true.):

while (g_main_context_pending(context))
  g_main_context_iteration(context, may_block)

or:

while (g_main_loop_is_running(loop))
  g_main_context_iteration(context, may_block)

The patch also contains other changes:

- The loop is locked with the context lock always. This saves some
  locks/unlocks and because the main loop has exactly one context
  attached to it its whole lifetime it also works.
- g_main_context_iterate has an extra argument 'self' denonting
  g_thread_self to speed it up.
- g_main_context_iterate expects the context lock to be held.
- g_main_context_iterate handles cached_poll_array more inteligent. The
  old solution looked quite doubios to me.
- GStaticMutex is used instead of GMutex. This makes it possible to
  initialize a GMainContext before calling g_thread_init ();
- The context is refcounted too now.
- Added g_main_loop_get_context just to have it.
- Removed the context handling from gthread.c, as it is not needed
  anymore.
- Adapted and Extended tests/mainloop-test to new setup. Works ok here.
  The failures in the old versions seems to be caused by incorrect
  locking. I now use one big lock instead of the two before and now it
  works like a charm.

All in all I like this patch very much.... ;-) and I hope you do as well.
Really this patch (at least the API, I proposed) really would make life for
ORBit/ORBit-mt much easier.

Another thing, that really makes me wonder are the lines 
 
  'if (pollrec->fd->events)'

in g_main_context_qurey and g_main_context_check. Can't this be checked in
g_main_context_add_poll, or do we allow fd's with empty event fields to be
added. Actually this would indicate a programming mistake, doesn't it? If
'events' was to be changed during the lifetime of pollref->fd the thing would
fail anyway, because g_main_context_qurey and g_main_context_check assume the
same layout.

Bye,
Sebastian
-- 
Sebastian Wilhelmi
mailto:wilhelmi ira uka de
http://goethe.ira.uka.de/~wilhelmi
Index: gmain.c
===================================================================
RCS file: /cvs/gnome/glib/gmain.c,v
retrieving revision 1.59
diff -u -b -B -r1.59 gmain.c
--- gmain.c	2001/05/27 18:28:56	1.59
+++ gmain.c	2001/06/12 13:09:07
@@ -79,10 +78,14 @@
   /* The following lock is used for both the list of sources
    * and the list of poll records
    */
-  GMutex *mutex;
-  GThread *thread;
+  GStaticMutex mutex;
+  GCond *cond;
+  GThread *owner;
+  guint need_broadcast;
 #endif  
 
+  guint ref_count;
+
   GPtrArray *pending_dispatches;
   gint timeout;			/* Timeout for current iteration */
 
@@ -132,11 +135,6 @@
   GMainContext *context;
   gboolean is_running;
   guint ref_count;
-
-#ifdef G_THREADS_ENABLED
-  GMutex *mutex;
-  GCond *sem_cond;
-#endif /* G_THREADS_ENABLED */
 };
 
 struct _GTimeoutSource
@@ -154,15 +152,13 @@
 };
 
 #ifdef G_THREADS_ENABLED
-#define LOCK_CONTEXT(context) g_mutex_lock(context->mutex)
-#define UNLOCK_CONTEXT(context) g_mutex_unlock(context->mutex)
-#define LOCK_LOOP(loop) g_mutex_lock(loop->mutex)
-#define UNLOCK_LOOP(loop) g_mutex_unlock(loop->mutex)
+#define LOCK_CONTEXT(context) g_static_mutex_lock (&context->mutex)
+#define UNLOCK_CONTEXT(context) g_static_mutex_unlock (&context->mutex)
+#define G_THREAD_SELF g_thread_self ()
 #else
 #define LOCK_CONTEXT(context) (void)0
 #define UNLOCK_CONTEXT(context) (void)0
-#define LOCK_LOOP(context) (void)0
-#define UNLOCK_LOOP(context) (void)0
+#define G_THREAD_SELF NULL
 #endif
 
 #define SOURCE_DESTROYED(source) (((source)->flags & G_HOOK_FLAG_ACTIVE) == 0)
@@ -494,17 +490,38 @@
 
 #endif	/* !HAVE_POLL */
 
-/* Called to clean up when a thread terminates
- */
+/**
+ * g_main_context_ref:
+ * @loop: a #GMainContext
+ * 
+ * Increases the reference count on a #GMainContext object by one.
+ **/
 void
-g_main_context_destroy (GMainContext *context)
+g_main_context_ref (GMainContext *context)
 {
-  GSource *source;
+  g_return_if_fail (context != NULL);
+  g_return_if_fail (context->ref_count > 0); 
 
-  /* We need the lock here only because g_source_destroy expects
-   * to be able to unlock when destroying the source's data
-   */
   LOCK_CONTEXT (context);
+  
+  context->ref_count++;
+
+  UNLOCK_CONTEXT (context);
+}
+
+static void
+g_main_context_unref_and_unlock (GMainContext *context)
+{
+  GSource *source;
+
+  context->ref_count--;
+
+  if (context->ref_count != 0)
+    {
+      UNLOCK_CONTEXT (context);
+      return;
+    }
+
   source = context->source_list;
   while (source)
     {
@@ -515,7 +532,7 @@
   UNLOCK_CONTEXT (context);
 
 #ifdef G_THREADS_ENABLED  
-  g_mutex_free (context->mutex);
+  g_static_mutex_free (&context->mutex);
 #endif
 
   g_ptr_array_free (context->pending_dispatches, TRUE);
@@ -538,49 +555,44 @@
   g_free (context);
 }
 
-/* This is an imcomplete (only the members up till context) version of
- * GRealThread from gthread.h. Keep them in sync */
-typedef struct _GRealThread GRealThread;
-struct  _GRealThread
+/**
+ * g_main_context_unref:
+ * @loop: a #GMainContext
+ * 
+ * Decreases the reference count on a #GMainContext object by one. If
+ * the result is zero, free the context and free all associated memory.
+ **/
+void
+g_main_context_unref (GMainContext *context)
 {
-  GThread thread;
-  GMainContext *context;
-};
+  g_return_if_fail (context != NULL);
+  g_return_if_fail (context->ref_count > 0); 
+
+  LOCK_CONTEXT (context);
+  g_main_context_unref_and_unlock (context);
+}
 
 /**
- * g_main_context_get:
- * @thread: a #GThread
+ * g_main_context_new:
  * 
- * Retrieves the main loop context for a particular thread. This
- * will create the main context for the thread if none previously
- * existed. The context will exist until the thread terminates.
+ * Creates a new #GMainContext strcuture
  * 
- * Return value: the main loop context for @thread.
+ * Return value: the new #GMainContext
  **/
 GMainContext *
-g_main_context_get (GThread *thread)
+g_main_context_new ()
 {
-  GRealThread *real_thread = (GRealThread*)thread;
-  GMainContext *context;
+  GMainContext *context = g_new0 (GMainContext, 1);
 
-  g_return_val_if_fail (thread != NULL, NULL);
-
-  if (g_thread_supported ())
-    context = real_thread->context;
-  else
-    context = default_main_context;
-
-  if (!context)
-    {
-      context = g_new0 (GMainContext, 1);
-
 #ifdef G_THREADS_ENABLED
-      if (g_thread_supported ())
-	context->mutex = g_mutex_new();
+  g_static_mutex_init (&context->mutex);
 
-      context->thread = thread;
+  context->owner = NULL;
+  context->need_broadcast = 0;
 #endif
       
+  context->ref_count = 1;
+
       context->next_id = 1;
       
       context->source_list = NULL;
@@ -624,12 +636,6 @@
 	}
 #endif
 
-      if (g_thread_supported ())
-	real_thread->context = context;
-      else
-	default_main_context = context;
-    }
-
   return context;
 }
 
@@ -650,7 +656,7 @@
   G_LOCK (main_loop);
 
   if (!default_main_context)
-    default_main_context = g_main_context_get (g_thread_self ());
+    default_main_context = g_main_context_new ();
 
   G_UNLOCK (main_loop);
 
@@ -1943,63 +1949,91 @@
   UNLOCK_CONTEXT (context);
 }
 
+/* HOLDS context lock */
 static gboolean
 g_main_context_iterate (GMainContext *context,
 			gboolean      block,
-			gboolean      dispatch)
+			gboolean      dispatch,
+			GThread      *self)
 {
+#ifdef G_THREADS_ENABLED
+  gboolean reset_owner;
+#endif /* G_THREADS_ENABLED */    
   gint max_priority;
   gint timeout;
   gboolean some_ready;
-  gint nfds, new_nfds;
-  GPollFD *fds;
+  gint nfds, allocated_nfds;
+  GPollFD *fds = NULL;
   
-  some_ready = g_main_context_prepare (context, &max_priority);
-
-  do
+#ifdef G_THREADS_ENABLED
+  if (context->owner != NULL && context->owner != self)
     {
-      LOCK_CONTEXT (context);
+      g_return_val_if_fail (g_thread_supported (), FALSE);
+
+      if (!block)
+	return FALSE;
+      
+      if (!context->cond)
+	context->cond = g_cond_new ();
+          
+      context->need_broadcast++;
+      g_cond_wait (context->cond, g_static_mutex_get_mutex (&context->mutex));
+      context->need_broadcast--;
 
-      if (context->cached_poll_array)
+      return FALSE;
+    }
+
+  if (context->owner == self)
+    reset_owner = FALSE;
+  else /* loop->context->owner == NULL */
 	{
-	  nfds = context->cached_poll_array_size;
-	  fds = context->cached_poll_array;
-	  context->cached_poll_array = NULL;
+      context->owner = self;
+      reset_owner = TRUE;
 	}
-      else
+#endif /* G_THREADS_ENABLED */
+  
+  if (!context->cached_poll_array)
 	{
-	  nfds = context->cached_poll_array_size = context->n_poll_records;
-	  fds = g_new (GPollFD, nfds);
+      context->cached_poll_array_size = context->n_poll_records;
+      context->cached_poll_array = g_new (GPollFD, context->n_poll_records);
 	}
 
+  allocated_nfds = context->cached_poll_array_size;
+  fds = context->cached_poll_array;
+  
       UNLOCK_CONTEXT (context);
+
+  some_ready = g_main_context_prepare (context, &max_priority); 
   
-      new_nfds = g_main_context_query (context, max_priority,
-				       &timeout, fds, nfds);
+  while ((nfds = g_main_context_query (context, max_priority, &timeout, fds, 
+				       allocated_nfds)) > allocated_nfds)
+    {
+      LOCK_CONTEXT (context);
+      g_free (fds);
+      context->cached_poll_array_size = allocated_nfds = nfds;
+      context->cached_poll_array = fds = g_new (GPollFD, nfds);
+      UNLOCK_CONTEXT (context);
     }
-  while (new_nfds > nfds);
 
   if (!block)
     timeout = 0;
   
-  g_main_context_poll (context, timeout, max_priority,
-		       fds, new_nfds);
+  g_main_context_poll (context, timeout, max_priority, fds, nfds);
 
-  g_main_context_check (context,
-			max_priority,
-			fds, new_nfds);
+  g_main_context_check (context, max_priority, fds, nfds);
 
-  LOCK_CONTEXT (context);
-
-  g_assert (!context->cached_poll_array);
+  if (dispatch)
+    g_main_context_dispatch (context);
   
-  context->cached_poll_array = fds;
-  context->cached_poll_array_size = nfds;
+  LOCK_CONTEXT (context);
 
-  UNLOCK_CONTEXT (context);
+#ifdef G_THREADS_ENABLED
+  if (reset_owner)
+    context->owner = NULL;
   
-  if (dispatch)
-    g_main_context_dispatch (context);
+  if (context->need_broadcast > 0)
+    g_cond_broadcast (context->cond);
+#endif /* G_THREADS_ENABLED */    
 
   return some_ready;
 }
@@ -2015,10 +2049,16 @@
 gboolean 
 g_main_context_pending (GMainContext *context)
 {
+  gboolean retval;
+
   if (!context)
     context = g_main_context_default();
+
+  LOCK_CONTEXT (context);
+  retval = g_main_context_iterate (context, FALSE, FALSE, G_THREAD_SELF);
+  UNLOCK_CONTEXT (context);
   
-  return g_main_context_iterate (context, FALSE, FALSE);
+  return retval;
 }
 
 /**
@@ -2040,10 +2080,16 @@
 gboolean
 g_main_context_iteration (GMainContext *context, gboolean may_block)
 {
+  gboolean retval;
+
   if (!context)
     context = g_main_context_default();
   
-  return g_main_context_iterate (context, may_block, TRUE);
+  LOCK_CONTEXT (context);
+  retval = g_main_context_iterate (context, may_block, TRUE, G_THREAD_SELF);
+  UNLOCK_CONTEXT (context);
+  
+  return retval;
 }
 
 /**
@@ -2066,19 +2112,13 @@
   if (!context)
     context = g_main_context_default();
   
+  g_main_context_ref (context);
+
   loop = g_new0 (GMainLoop, 1);
   loop->context = context;
   loop->is_running = is_running != FALSE;
   loop->ref_count = 1;
   
-#ifdef G_THREADS_ENABLED
-  if (g_thread_supported ())
-    loop->mutex = g_mutex_new ();
-  else
-    loop->mutex = NULL;
-  loop->sem_cond = NULL;
-#endif /* G_THREADS_ENABLED */
-
   return loop;
 }
 
@@ -2094,24 +2134,29 @@
 g_main_loop_ref (GMainLoop *loop)
 {
   g_return_val_if_fail (loop != NULL, NULL);
+  g_return_val_if_fail (loop->ref_count > 0, NULL);
 
-  LOCK_LOOP (loop);
+  LOCK_CONTEXT (loop->context);
   loop->ref_count++;
-  UNLOCK_LOOP (loop);
+  UNLOCK_CONTEXT (loop->context);
 
   return loop;
 }
 
 static void
-main_loop_destroy (GMainLoop *loop)
+g_main_loop_unref_and_unlock (GMainLoop *loop)
 {
-#ifdef G_THREADS_ENABLED
-  g_mutex_free (loop->mutex);
-  if (loop->sem_cond)
-    g_cond_free (loop->sem_cond);
-#endif /* G_THREADS_ENABLED */  
-  
+  loop->ref_count--;
+  if (loop->ref_count == 0)
+    {
+      /* When the ref_count is 0, there can be nobody else using the
+       * loop, so it is safe to unlock before destroying.
+       */
+      g_main_context_unref_and_unlock (loop->context);
   g_free (loop);
+    }
+  else
+    UNLOCK_CONTEXT (loop->context);
 }
 
 /**
@@ -2127,19 +2172,9 @@
   g_return_if_fail (loop != NULL);
   g_return_if_fail (loop->ref_count > 0);
 
-  LOCK_LOOP (loop);
+  LOCK_CONTEXT (loop->context);
   
-  loop->ref_count--;
-  if (loop->ref_count == 0)
-    {
-      /* When the ref_count is 0, there can be nobody else using the
-       * loop, so it is safe to unlock before destroying.
-       */
-      UNLOCK_LOOP (loop);
-      main_loop_destroy (loop);
-    }
-  else
-    UNLOCK_LOOP (loop);
+  g_main_loop_unref_and_unlock (loop);
 }
 
 /**
@@ -2154,70 +2189,85 @@
 void 
 g_main_loop_run (GMainLoop *loop)
 {
+#ifdef G_THREADS_ENABLED
+  gboolean reset_owner;
+#endif /* G_THREADS_ENABLED */    
+  GThread *self = G_THREAD_SELF;
+
   g_return_if_fail (loop != NULL);
+  g_return_if_fail (loop->ref_count > 0);
 
-  /* The assumption here is that a reference is held to the loop
-   * until we recursively iterate
-   */
+  LOCK_CONTEXT (loop->context);
+
 #ifdef G_THREADS_ENABLED
-  if (loop->context->thread != g_thread_self ())
+  /* Another thread owns this context */
+  if (loop->context->owner != NULL && loop->context->owner != self)
     {
-      LOCK_LOOP (loop);
-
-      loop->ref_count++;
-      
       if (!g_thread_supported ())
 	{
 	  g_warning ("g_main_loop_run() was called from second thread but"
 		     "g_thread_init() was never called.");
+	  UNLOCK_CONTEXT (loop->context);
+	  return;
 	}
-      else
-	{
-	  if (!loop->sem_cond)
-	    loop->sem_cond = g_cond_new ();
 	  
+      loop->ref_count++;
+      
+      if (!loop->context->cond)
+	loop->context->cond = g_cond_new ();
+      
 	  if (!loop->is_running)
 	    loop->is_running = TRUE;
 	  
-	  while (loop->is_running)
-	    g_cond_wait (loop->sem_cond, loop->mutex);
+      loop->context->need_broadcast++;
+      
+      while (loop->is_running && loop->context->owner != NULL)
+	g_cond_wait (loop->context->cond, 
+		     g_static_mutex_get_mutex (&loop->context->mutex));
+      
+      loop->context->need_broadcast--;
+      
+      if (!loop->is_running)
+	{
+	  g_main_loop_unref_and_unlock (loop);
+	  return;
 	}
+
+      g_assert (loop->context->owner == NULL);
     }
-  else
-#endif /* G_THREADS_ENABLED */    
+
+  if (loop->context->owner == self)
+    reset_owner = FALSE;
+  else /* loop->context->owner == NULL */
     {
-      LOCK_CONTEXT (loop->context);
+      loop->context->owner = self;
+      reset_owner = TRUE;
+    }
+#endif /* G_THREADS_ENABLED */    
+
       if (loop->context->in_check_or_prepare)
 	{
 	  g_warning ("g_main_run(): called recursively from within a source's check() or "
 		     "prepare() member, iteration not possible.");
 	  return;
 	}
-      UNLOCK_CONTEXT (loop->context);
-      
-      LOCK_LOOP (loop);
 
       loop->ref_count++;
       loop->is_running = TRUE;
       while (loop->is_running)
 	{
-	  UNLOCK_LOOP (loop);
-	  g_main_context_iterate (loop->context, TRUE, TRUE);
-	  LOCK_LOOP (loop);
+      g_main_context_iterate (loop->context, TRUE, TRUE, self);
 	}
-    }
 
-  /* We inline this here rather than calling g_main_loop_unref() to
-   * avoid an extra unlock/lock.
-   */
-  loop->ref_count--;
-  if (loop->ref_count == 0)
-    {
-      UNLOCK_LOOP (loop);
-      main_loop_destroy (loop);
-    }
-  else
-    UNLOCK_LOOP (loop);
+#ifdef G_THREADS_ENABLED
+  if (reset_owner)
+    loop->context->owner = NULL;
+
+  if (loop->context->need_broadcast > 0)
+    g_cond_broadcast (loop->context->cond);
+#endif /* G_THREADS_ENABLED */    
+  
+  g_main_loop_unref_and_unlock (loop);
 }
 
 /**
@@ -2231,21 +2281,11 @@
 g_main_loop_quit (GMainLoop *loop)
 {
   g_return_if_fail (loop != NULL);
+  g_return_if_fail (loop->ref_count > 0);
 
-  LOCK_LOOP (loop);
   loop->is_running = FALSE;
 
-#ifdef G_THREADS_ENABLED
-  if (loop->sem_cond)
-    g_cond_broadcast (loop->sem_cond);
-#endif
-  
-  UNLOCK_LOOP (loop);
-
-  LOCK_CONTEXT (loop->context);
-  
   g_main_context_wakeup (loop->context);
-  UNLOCK_CONTEXT (loop->context);
 }
 
 /**
@@ -2259,15 +2299,27 @@
 gboolean
 g_main_loop_is_running (GMainLoop *loop)
 {
-  gboolean result;
-  
   g_return_val_if_fail (loop != NULL, FALSE);
+  g_return_val_if_fail (loop->ref_count > 0, FALSE);
 
-  LOCK_LOOP (loop);
-  result = loop->is_running;
-  UNLOCK_LOOP (loop);
+  return loop->is_running;
+}
 
-  return result;
+/**
+ * g_main_loop_get_context:
+ * @loop: a #GMainLoop.
+ * 
+ * Returns the #GMainContext of @loop.
+ * 
+ * Return value: the #GMainContext of @loop
+ **/
+GMainContext *
+g_main_loop_get_context (GMainLoop *loop)
+{
+  g_return_val_if_fail (loop != NULL, NULL);
+  g_return_val_if_fail (loop->ref_count > 0, NULL);
+ 
+  return loop->context;
 }
 
 /* HOLDS: context's lock */
@@ -2365,6 +2417,9 @@
   if (!context)
     context = g_main_context_default ();
   
+  g_return_if_fail (context->ref_count > 0);
+  g_return_if_fail (fd);
+
   LOCK_CONTEXT (context);
   g_main_context_add_poll_unlocked (context, priority, fd);
   UNLOCK_CONTEXT (context);
@@ -2440,6 +2495,9 @@
   if (!context)
     context = g_main_context_default ();
   
+  g_return_if_fail (context->ref_count > 0);
+  g_return_if_fail (fd);
+
   LOCK_CONTEXT (context);
 
   g_main_context_remove_poll_unlocked (context, fd);
@@ -2540,6 +2598,8 @@
   if (!context)
     context = g_main_context_default ();
   
+  g_return_if_fail (context->ref_count > 0);
+
   LOCK_CONTEXT (context);
   
   if (func)
@@ -2572,6 +2632,8 @@
   if (!context)
     context = g_main_context_default ();
   
+  g_return_val_if_fail (context->ref_count > 0, NULL);
+
   LOCK_CONTEXT (context);
   result = context->poll_func;
   UNLOCK_CONTEXT (context);
Index: gmain.h
===================================================================
RCS file: /cvs/gnome/glib/gmain.h,v
retrieving revision 1.8
diff -u -b -B -r1.8 gmain.h
--- gmain.h	2001/03/26 19:23:17	1.8
+++ gmain.h	2001/06/12 13:09:07
@@ -124,7 +124,9 @@
 
 /* GMainContext: */
 
-GMainContext *g_main_context_get       (GThread      *thread);
+GMainContext *g_main_context_new       (void);
+void          g_main_context_ref       (GMainContext *context);
+void          g_main_context_unref     (GMainContext *context);
 GMainContext *g_main_context_default   (void);
 
 gboolean      g_main_context_iteration (GMainContext *context,
@@ -177,6 +179,7 @@
 GMainLoop *g_main_loop_ref        (GMainLoop    *loop);
 void       g_main_loop_unref      (GMainLoop    *loop);
 gboolean   g_main_loop_is_running (GMainLoop    *loop);
+GMainContext *g_main_loop_get_context (GMainLoop    *loop);
 
 /* GSource: */
 
Index: gthread.c
===================================================================
RCS file: /cvs/gnome/glib/gthread.c,v
retrieving revision 1.25
diff -u -b -B -r1.25 gthread.c
--- gthread.c	2001/05/22 12:06:59	1.25
+++ gthread.c	2001/06/12 13:09:07
@@ -74,7 +74,6 @@
 struct  _GRealThread
 {
   GThread thread;
-  GMainContext *context;
   gpointer private_data;
   gpointer retval;
   GSystemThread system_thread;
@@ -465,8 +464,6 @@
   G_UNLOCK (g_thread);
 }
 
-void g_main_context_destroy (GMainContext *context);
-
 static void
 g_thread_cleanup (gpointer data)
 {
@@ -487,8 +484,6 @@
 	    }
 	  g_array_free (array, TRUE);
 	}
-      if (thread->context)
-	g_main_context_destroy (thread->context);
 
       /* We only free the thread structure, if it isn't joinable. If
          it is, the structure is freed in g_thread_join */
@@ -560,7 +555,6 @@
   result->thread.func = func;
   result->thread.data = data;
   result->private_data = NULL; 
-  result->context = NULL;
   G_LOCK (g_thread);
   G_THREAD_UF (thread_create, (g_thread_create_proxy, result, 
 			       stack_size, joinable, bound, priority,
@@ -657,7 +651,6 @@
       thread->thread.func = NULL;
       thread->thread.data = NULL;
       thread->private_data = NULL;
-      thread->context = NULL;
 
       if (g_thread_supported ())
 	G_THREAD_UF (thread_self, (&thread->system_thread));
Index: tests/mainloop-test.c
===================================================================
RCS file: /cvs/gnome/glib/tests/mainloop-test.c,v
retrieving revision 1.7
diff -u -b -B -r1.7 mainloop-test.c
--- tests/mainloop-test.c	2001/05/18 08:44:57	1.7
+++ tests/mainloop-test.c	2001/06/12 13:09:07
@@ -12,18 +12,16 @@
 
 #define ITERS 10000
 #define INCREMENT 10
-#define NTHREADS 4
+#define NUM_ADDER_THREADS 6
+#define NUM_CHECKER_THREADS 6
 #define NCRAWLERS 4
 #define CRAWLER_TIMEOUT_RANGE 40
 #define RECURSER_TIMEOUT 50
 
-GPtrArray *context_array;
-GMutex *context_array_mutex;
-GCond *context_array_cond;
+G_LOCK_DEFINE_STATIC (lock);
 
+GPtrArray *loop_array;
 GMainLoop *main_loop;
-
-G_LOCK_DEFINE_STATIC (crawler_array_lock);
 GPtrArray *crawler_array;
 
 typedef struct _AddrData AddrData;
@@ -132,22 +130,17 @@
 
   GIOChannel **channels = data;
   AddrData addr_data;
-
-  context = g_main_context_get (g_thread_self());
-
-  g_mutex_lock (context_array_mutex);
-  
-  g_ptr_array_add (context_array, context);
-
-  if (context_array->len == NTHREADS)
-    g_cond_broadcast (context_array_cond);
   
-  g_mutex_unlock (context_array_mutex);
+  context = g_main_context_new ();
 
   addr_data.dest = channels[1];
   addr_data.loop = g_main_loop_new (context, FALSE);
   addr_data.count = 0;
   
+  G_LOCK (lock);
+  g_ptr_array_add (loop_array, addr_data.loop);
+  G_UNLOCK (lock);
+
   adder_source = g_io_create_watch (channels[0], G_IO_IN | G_IO_HUP);
   g_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL);
   g_source_attach (adder_source, context);
@@ -172,11 +165,11 @@
 
   g_print ("Timeout run %d times\n", addr_data.count);
 
-  g_mutex_lock (context_array_mutex);
-  g_ptr_array_remove (context_array, context);
-  if (context_array->len == 0)
+  G_LOCK (lock);
+  g_ptr_array_remove (loop_array, addr_data.loop);
+  if (loop_array->len == 0)
     g_main_loop_quit (main_loop);
-  g_mutex_unlock (context_array_mutex);
+  G_UNLOCK (lock);
 
   cleanup_crawlers (context);
 
@@ -248,9 +241,10 @@
   return TRUE;
 }
 
-void
+static GThread *
 create_adder_thread (void)
 {
+  GThread *retval;
   GError *err = NULL;
   TestData *test_data;
   
@@ -267,7 +261,7 @@
   sub_channels[0] = in_channels[0];
   sub_channels[1] = out_channels[1];
 
-  g_thread_create (adder_thread, sub_channels, FALSE, &err);
+  retval = g_thread_create (adder_thread, sub_channels, TRUE, &err);
 
   if (err)
     {
@@ -284,6 +278,8 @@
 		  adder_response, test_data);
   
   do_add (test_data->in, test_data->current_val, INCREMENT);
+  
+  return retval;
 }
 
 static void create_crawler (void);
@@ -305,14 +301,13 @@
 crawler_callback (gpointer data)
 {
   GSource *source = data;
-
-  G_LOCK (crawler_array_lock);
   
+  G_LOCK (lock);
   if (!g_ptr_array_remove_fast (crawler_array, source))
     remove_crawler();
 
   remove_crawler();
-  G_UNLOCK (crawler_array_lock);
+  G_UNLOCK (lock);
 	    
   create_crawler();
   create_crawler();
@@ -326,14 +321,12 @@
   GSource *source = g_timeout_source_new (g_random_int_range (0, CRAWLER_TIMEOUT_RANGE));
   g_source_set_callback (source, (GSourceFunc)crawler_callback, source, NULL);
 
-  g_mutex_lock (context_array_mutex);
-  g_source_attach (source, context_array->pdata[g_random_int_range (0, context_array->len)]);
+  G_LOCK (lock);
+  g_source_attach (source, g_main_loop_get_context (loop_array->pdata[g_random_int_range (0, loop_array->len)]));
   g_source_unref (source);
-  g_mutex_unlock (context_array_mutex);
 
-  G_LOCK (crawler_array_lock);
   g_ptr_array_add (crawler_array, source);
-  G_UNLOCK (crawler_array_lock);
+  G_UNLOCK (lock);
 }
 
 static void
@@ -341,7 +334,7 @@
 {
   gint i;
   
-  G_LOCK (crawler_array_lock);
+  G_LOCK (lock);
   for (i=0; i < crawler_array->len; i++)
     {
       if (g_source_get_context (crawler_array->pdata[i]) == context)
@@ -350,7 +343,9 @@
 	  i--;
 	}
     }
-  G_UNLOCK (crawler_array_lock);
+  G_UNLOCK (lock);
+  
+  g_main_context_unref (context);
 }
 
 static gboolean
@@ -371,17 +366,53 @@
   GMainContext *context;
   GSource *source;
   
-  g_mutex_lock (context_array_mutex);
-  context = context_array->pdata[g_random_int_range (0, context_array->len)];
+  G_LOCK (lock);
+  context = g_main_loop_get_context (loop_array->pdata[g_random_int_range (0, loop_array->len)]);
   source = g_idle_source_new ();
   g_source_set_callback (source, recurser_idle, context, NULL);
   g_source_attach (source, context);
   g_source_unref (source);
-  g_mutex_unlock (context_array_mutex);
+  G_UNLOCK (lock);
 
   return TRUE;
 }
 
+gpointer
+checker_thread (gpointer ignore)
+{
+  GMainLoop *loop;
+
+  G_LOCK (lock);
+  loop = loop_array->pdata[g_random_int_range (0, loop_array->len)];
+  G_UNLOCK (lock);
+
+  if (g_random_boolean ())
+    {
+      g_main_loop_run (loop);
+    }
+  else
+    {
+      while (g_main_loop_is_running (loop))
+	g_main_context_iteration (g_main_loop_get_context (loop), TRUE);
+    }
+  return NULL;
+}
+
+static GThread *
+create_checker_thread (void)
+{
+  GError *err = NULL;
+  GThread *retval = g_thread_create (checker_thread, NULL, TRUE, &err);
+  
+  if (err)
+    {
+      g_warning ("Cannot create thread: %s", err->message);
+      exit (1);
+    }
+
+  return retval;
+}
+
 int 
 main (int   argc,
       char *argv[])
@@ -390,29 +421,26 @@
      implementation is available */
 #if defined(G_THREADS_ENABLED) && ! defined(G_THREADS_IMPL_NONE)
   gint i;
+  GThread *threads[NUM_ADDER_THREADS + NUM_CHECKER_THREADS];
 
   g_thread_init (NULL);
-
-  context_array = g_ptr_array_new ();
-  context_array_mutex = g_mutex_new ();
-  context_array_cond = g_cond_new (); 
 
+  loop_array = g_ptr_array_new ();
   crawler_array = g_ptr_array_new ();
 
   main_loop = g_main_loop_new (NULL, FALSE);
 
-  for (i = 0; i < NTHREADS; i++)
-    create_adder_thread ();
+  for (i = 0; i < NUM_ADDER_THREADS; i++)
+    threads [i] = create_adder_thread ();
 
-  /* Wait for all threads to start
+  /* Wait for all adder threads to start
    */
-  g_mutex_lock (context_array_mutex);
+  while (loop_array->len < NUM_ADDER_THREADS)
+    g_usleep (G_USEC_PER_SEC / 10);
   
-  if (context_array->len < NTHREADS)
-    g_cond_wait (context_array_cond, context_array_mutex);
+  for (i = 0; i < NUM_CHECKER_THREADS; i++)
+    threads [NUM_ADDER_THREADS + i] = create_checker_thread ();
   
-  g_mutex_unlock (context_array_mutex);
-  
   for (i = 0; i < NCRAWLERS; i++)
     create_crawler ();
 
@@ -420,6 +448,9 @@
 
   g_main_loop_run (main_loop);
   g_main_loop_unref (main_loop);
+
+  for (i = 0; i < NUM_ADDER_THREADS + NUM_CHECKER_THREADS; i++)
+    g_thread_join (threads [i]);
 
 #endif
   return 0;


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