[libgda] Inter thread signal emissions corrections



commit 2cb1b4e19c7a876eda44d5de9f275dc833026472
Author: Vivien Malerba <malerba gnome-db org>
Date:   Wed Jun 25 15:11:35 2014 +0200

    Inter thread signal emissions corrections

 doc/C/libgda-6.0-docs.sgml                  |    1 +
 doc/C/libgda-sections.txt                   |    7 +
 libgda/libgda.symbols                       |    2 +
 libgda/thread-wrapper/gda-connect.c         |  101 ++++++---
 libgda/thread-wrapper/gda-connect.h         |   26 ++-
 libgda/thread-wrapper/itsignaler.c          |    2 +-
 libgda/thread-wrapper/test-connect.c        |  313 ++++++++++++++++++++++++++-
 libgda/thread-wrapper/test-raw-itsignaler.c |    2 +-
 8 files changed, 395 insertions(+), 59 deletions(-)
---
diff --git a/doc/C/libgda-6.0-docs.sgml b/doc/C/libgda-6.0-docs.sgml
index e4fba7a..3b2fc9f 100644
--- a/doc/C/libgda-6.0-docs.sgml
+++ b/doc/C/libgda-6.0-docs.sgml
@@ -1116,6 +1116,7 @@ g_object_unref (store);
       <title>Multi threading</title>
       <xi:include href="xml/gda-lockable.xml"/>
       <xi:include href="xml/gda-worker.xml"/>
+      <xi:include href="xml/gda-connect.xml"/>
     </chapter>
 
     <chapter id="misc">
diff --git a/doc/C/libgda-sections.txt b/doc/C/libgda-sections.txt
index 0bebbe8..74d0949 100644
--- a/doc/C/libgda-sections.txt
+++ b/doc/C/libgda-sections.txt
@@ -1921,6 +1921,13 @@ gda_worker_set_callback
 </SECTION>
 
 <SECTION>
+<FILE>gda-connect</FILE>
+<TITLE>Inter thread signal propagation</TITLE>
+gda_signal_connect
+gda_signal_handler_disconnect
+</SECTION>
+
+<SECTION>
 <FILE>gda-repetitive-statement</FILE>
 <TITLE>GdaRepetitiveStatement</TITLE>
 GdaRepetitiveStatement
diff --git a/libgda/libgda.symbols b/libgda/libgda.symbols
index 8255782..935b956 100644
--- a/libgda/libgda.symbols
+++ b/libgda/libgda.symbols
@@ -86,6 +86,7 @@
        gda_config_list_dsn
        gda_config_list_providers
        gda_config_remove_dsn
+       gda_signal_connect
        gda_connection_add_event
        gda_connection_add_event_string
        gda_connection_add_prepared_statement
@@ -340,6 +341,7 @@
        gda_default_escape_string
        gda_default_get_type
        gda_default_unescape_string
+       gda_signal_handler_disconnect
        gda_dsn_info_copy
        gda_dsn_info_free
        gda_dsn_info_get_type
diff --git a/libgda/thread-wrapper/gda-connect.c b/libgda/thread-wrapper/gda-connect.c
index dccd2ba..63d47b6 100644
--- a/libgda/thread-wrapper/gda-connect.c
+++ b/libgda/thread-wrapper/gda-connect.c
@@ -23,7 +23,7 @@
 #include <glib/gi18n-lib.h>
 
 #define DEBUG_NOTIFICATION
-//#undef DEBUG_NOTIFICATION
+#undef DEBUG_NOTIFICATION
 
 #ifdef DEBUG_NOTIFICATION
 static gchar *
@@ -77,10 +77,11 @@ typedef struct _SigClosure SigClosure;
 struct _SigClosure
 {
         GClosure      closure;
+       GMutex        mutex;
        GMainContext *context; /* ref held */
         gulong        ext_handler_id;
 
-        GCallback     callback;
+        GClosure     *rcl; /* real closure, used in the main loop context */
 
        ITSignaler   *signal_its;
        ITSignaler   *return_its;
@@ -92,10 +93,13 @@ sig_closure_finalize (G_GNUC_UNUSED gpointer notify_data, GClosure *closure)
 {
         SigClosure *sig_closure = (SigClosure *)closure;
 
-       g_print ("%s()\n", __FUNCTION__);
        itsignaler_remove (sig_closure->signal_its, sig_closure->context, sig_closure->its_add_id);
        itsignaler_unref (sig_closure->signal_its);
        itsignaler_unref (sig_closure->return_its);
+       if (sig_closure->rcl)
+               g_closure_unref (sig_closure->rcl);
+       g_mutex_clear (& (sig_closure->mutex));
+
        g_main_context_unref (sig_closure->context);
 }
 
@@ -104,15 +108,14 @@ typedef struct {
         GValue *return_value;
         guint n_param_values;
         GValue *param_values;
-        gpointer invocation_hint; /* FIXME: problem here to copy value! */
-
-        GClosure *rcl; /* real closure, used in the main loop context */
+        gpointer invocation_hint; /* value is never copied */
+       GClosure *rcl;
 } PassedData;
 
 /*
  * This function is called in the thread which emitted the signal
  *
- * In it we pack the data, push it through the ITSignaler to the thead from which the callback will be 
called, and
+ * In it we pack the data, push it through the ITSignaler to the thread in which the callback will be 
called, and
  * wait for the result before returning.
  */
 static void
@@ -141,14 +144,20 @@ sig_closure_marshal (GClosure *closure,
                 g_print ("Th%p   Return value expected: none\n", g_thread_self());
 #endif
 
+       gboolean is_owner;
+       is_owner = g_main_context_is_owner (sig_closure->context);
         if (g_main_context_acquire (sig_closure->context)) {
+               /* warning about not owner context */
+               if (!is_owner)
+                       g_warning (_("The GMainContext passed when gda_signal_connect() was called is not 
owned by any thread, you may "
+                                    "expect some undesired behaviours, use g_main_context_acquire()"));
+
                 /* the signal has to be propagated in the same thread */
                 GClosure *rcl;
-                rcl = g_cclosure_new (sig_closure->callback, closure->data, NULL);
-                g_closure_set_marshal (rcl, g_cclosure_marshal_generic);
+               rcl = g_closure_ref (sig_closure->rcl);
                 g_closure_invoke (rcl, return_value, n_param_values, param_values, invocation_hint);
                 g_closure_unref (rcl);
-                g_main_context_release (sig_closure->context);
+               g_main_context_release (sig_closure->context);
         }
         else {
                PassedData data;
@@ -156,15 +165,19 @@ sig_closure_marshal (GClosure *closure,
                 data.invocation_hint = invocation_hint;
                 data.return_value = return_value;
                data.param_values = (GValue*) param_values;
-                data.rcl = g_cclosure_new (sig_closure->callback, closure->data, NULL);
-                g_closure_set_marshal (data.rcl, g_cclosure_marshal_generic);
+               data.rcl = g_closure_ref (sig_closure->rcl);
+
+               g_mutex_lock (& (sig_closure->mutex)); /* This mutex ensures that push_notification() and
+                                                       * the following pop_notification() are treated as a 
single atomic operation
+                                                       * in case there are some return values */
                if (itsignaler_push_notification (sig_closure->signal_its, &data, NULL)) {
                        gpointer rdata;
                        rdata = itsignaler_pop_notification (sig_closure->return_its, -1);
                        g_assert (rdata == &data);
                }
                else
-                       g_warning ("Internal Gda Connect error, the signal will not be propagated, please 
report the bug");
+                       g_warning ("Internal Gda connect error, the signal will not be propagated, please 
report the bug");
+               g_mutex_unlock (& (sig_closure->mutex));
 
                g_closure_unref (data.rcl);
         }
@@ -190,14 +203,12 @@ propagate_signal (ITSignaler *its, SigClosure *sig_closure)
        return TRUE; /* don't remove the source! */
 }
 
-
-/*
- * FIXME: what is @data for ?
- */
 static SigClosure *
-sig_closure_new (gpointer instance, GMainContext *context, gpointer data)
+sig_closure_new (gpointer instance, GMainContext *context, gboolean swapped, GCallback c_handler, gpointer 
data)
 {
        g_assert (context);
+       g_assert (instance);
+       g_assert (c_handler);
 
        ITSignaler *its1, *its2;
        its1 = itsignaler_new ();
@@ -215,9 +226,15 @@ sig_closure_new (gpointer instance, GMainContext *context, gpointer data)
         sig_closure = (SigClosure *) closure;
        sig_closure->context = g_main_context_ref (context);
         sig_closure->ext_handler_id = 0;
-        sig_closure->callback = NULL;
        sig_closure->signal_its = its1;
        sig_closure->return_its = its2;
+       g_mutex_init (& (sig_closure->mutex));
+
+       if (swapped)
+               sig_closure->rcl = g_cclosure_new_swap (c_handler, closure->data, NULL);
+       else
+               sig_closure->rcl = g_cclosure_new (c_handler, closure->data, NULL);
+       g_closure_set_marshal (sig_closure->rcl, g_cclosure_marshal_generic);
 
        sig_closure->its_add_id = itsignaler_add (sig_closure->signal_its, context,
                                                  (ITSignalerFunc) propagate_signal, sig_closure, NULL);
@@ -244,32 +261,35 @@ ulong_equal (gconstpointer a, gconstpointer b)
 
 
 /**
- * gda_connect:
+ * gda_signal_connect:
  * @instance: the instance to connect to
  * @detailed_signal: a string of the form "signal-name::detail"
  * @c_handler: the GCallback to connect
  * @data: (allow-none): data to pass to @c_handler, or %NULL
  * @destroy_data: (allow-none): function to destroy @data when not needed anymore, or %NULL
- * @connect_flags:
+ * @connect_flags: a combination of #GConnectFlags.
  * @context: (allow-none): the #GMainContext in which signals will actually be treated, or %NULL for the 
default one
  *
- * Connects a GCallback function to a signal for a particular object.
+ * Connects a GCallback function to a signal for a particular object. The difference with g_signal_connect() 
is that the
+ * callback will be called from withing the thread which is the owner of @context. If needed you may have to 
use g_main_context_acquire()
+ * to ensure a specific thread is the owner of @context.
  *
  * Returns: the handler id, or %0 if an error occurred
  *
  * Since: 6.0
  */
 gulong
-gda_connect (gpointer instance,
-            const gchar *detailed_signal,
-            GCallback c_handler,
-            gpointer data,
-            GClosureNotify destroy_data,
-            GConnectFlags connect_flags,
-            GMainContext *context)
+gda_signal_connect (gpointer instance,
+                   const gchar *detailed_signal,
+                   GCallback c_handler,
+                   gpointer data,
+                   GClosureNotify destroy_data,
+                   GConnectFlags connect_flags,
+                   GMainContext *context)
 {
        static gulong ids = 1;
         g_return_val_if_fail (instance, 0);
+        g_return_val_if_fail (c_handler, 0);
 
         guint signal_id;
         if (! g_signal_parse_name (detailed_signal, G_TYPE_FROM_INSTANCE (instance), &signal_id, NULL, 
FALSE)) {
@@ -283,7 +303,8 @@ gda_connect (gpointer instance,
                 context = g_main_context_ref_thread_default ();
 
         SigClosure *sig_closure;
-        sig_closure = sig_closure_new (instance, context, data);
+        sig_closure = sig_closure_new (instance, context,
+                                      connect_flags & G_CONNECT_SWAPPED, c_handler, data);
        g_main_context_unref (context);
        if (!sig_closure)
                return 0;
@@ -292,10 +313,12 @@ gda_connect (gpointer instance,
         g_signal_query (signal_id, &query);
         g_assert (query.signal_id == signal_id);
 
-        gulong hid;
-        hid = g_signal_connect_closure (instance, detailed_signal, (GClosure *) sig_closure, 0);
+        gulong hid; /* REM: we don't need to keep a reference to @hid because when the closure is destroyed,
+                    * all the signals are "cleared", see gobject/gsignal.c in GLib's code
+                    */
+        hid = g_signal_connect_closure (instance, detailed_signal, (GClosure *) sig_closure,
+                                       (connect_flags & G_CONNECT_AFTER) ? TRUE : FALSE);
         g_closure_sink ((GClosure *) sig_closure);
-        sig_closure->callback = c_handler;
 
         if (hid > 0) {
                 if (!handlers_hash)
@@ -314,9 +337,19 @@ gda_connect (gpointer instance,
 
 }
 
+/**
+ * gda_signal_handler_disconnect:
+ * @instance: the instance to disconnect from
+ * @handler_id: the signal handler, as returned by gda_signal_connect()
+ *
+ * Disconnect a callback using the signal handler, see gda_signal_connect()
+ */
 void
-gda_disconnect (gpointer instance, gulong handler_id, GMainContext *context)
+gda_signal_handler_disconnect (gpointer instance, gulong handler_id)
 {
+       g_return_if_fail (instance);
+       g_return_if_fail (handler_id > 0);
+
        SigClosure *sig_closure = NULL;
        if (handlers_hash)
                sig_closure = g_hash_table_lookup (handlers_hash, &handler_id);
diff --git a/libgda/thread-wrapper/gda-connect.h b/libgda/thread-wrapper/gda-connect.h
index a2d9b70..e511b59 100644
--- a/libgda/thread-wrapper/gda-connect.h
+++ b/libgda/thread-wrapper/gda-connect.h
@@ -26,30 +26,32 @@ G_BEGIN_DECLS
 
 /**
  * SECTION:gda-connect
- * @short_description: Connect signals to objects and "forward" them to be handled from a specific 
#GMainContext
+ * @short_description: Inter thread signal propagation
  * @title: GdaConnect
  * @stability: Stable
  * @see_also:
  *
- * The purpose of the #GdaConnect object is to allow one to connect to a signal emitted by an object and be 
sure that
- * the acutal signal handling will occur _only_ when running a specific #GMainContext.
+ * The purpose of the gda_signal_connect() is to allow one to connect to a signal emitted by an object and 
be sure that
+ * the acutal signal handling will occur _only_ when running a specific #GMainContext. The 
gda_signal_handler_disconnect() actually
+ * disconnects the handler.
  *
  * Use these functions for exemple when you need to handle signals from objects which are emitted from 
within a thread
  * which is not the main UI thread.
  *
- * The #GdaConnect implements its own locking mechanism and can safely be used from multiple
+ * These function implement their own locking mechanism and can safely be used from multiple
  * threads at once without needing further locking.
  */
 
-gulong gda_connect (gpointer instance,
-                   const gchar *detailed_signal,
-                   GCallback c_handler,
-                   gpointer data,
-                   GClosureNotify destroy_data,
-                   GConnectFlags connect_flags,
-                   GMainContext *context);
+gulong gda_signal_connect    (gpointer instance,
+                             const gchar *detailed_signal,
+                             GCallback c_handler,
+                             gpointer data,
+                             GClosureNotify destroy_data,
+                             GConnectFlags connect_flags,
+                             GMainContext *context);
 
-void   gda_disconnect (gpointer instance, gulong handler_id, GMainContext *context);
+void   gda_signal_handler_disconnect (gpointer instance,
+                                     gulong handler_id);
 
 G_END_DECLS
 
diff --git a/libgda/thread-wrapper/itsignaler.c b/libgda/thread-wrapper/itsignaler.c
index 241b1c0..e4db446 100644
--- a/libgda/thread-wrapper/itsignaler.c
+++ b/libgda/thread-wrapper/itsignaler.c
@@ -897,7 +897,7 @@ void its_source_finalize (GSource *source)
 /**
  * itsignaler_add:
  * @its: a #ITSignaler object
- * @context: (allow-none): a GMainContext (if NULL, the default context will be used).
+ * @context: (allow-none): a GMainContext (if %NULL, the default context will be used).
  * @func: callback function to be called when a notification is ready
  * @data: data to pass to @func
  * @notify: (allow-none): a function to call when data is no longer in use, or NULL.
diff --git a/libgda/thread-wrapper/test-connect.c b/libgda/thread-wrapper/test-connect.c
index f94c875..09bfcd1 100644
--- a/libgda/thread-wrapper/test-connect.c
+++ b/libgda/thread-wrapper/test-connect.c
@@ -21,8 +21,14 @@
 #include "gda-connect.h"
 #include "dummy-object.h"
 #include <stdlib.h>
+#include <string.h>
+
+#define NB_THREADS 20
 
 int test1 (void);
+int test2 (void);
+int test3 (void);
+int test4 (void);
 
 int
 main (int argc, char** argv)
@@ -33,7 +39,18 @@ main (int argc, char** argv)
        g_type_init ();
 #endif
 
+       GMainContext *context;
+       context = g_main_context_ref_thread_default ();
+       g_main_context_acquire (context);
+
+       g_print ("Main thread is %p\n", g_thread_self ());
        nfailed += test1 ();
+       nfailed += test2 ();
+       nfailed += test3 ();
+       nfailed += test4 ();
+
+       g_main_context_release (context);
+       g_main_context_unref (context);
 
        return nfailed > 0 ? 1 : 0;
 }
@@ -45,25 +62,81 @@ idle_stop_main_loop (GMainLoop *loop)
        return FALSE; /* remove the source */
 }
 
-/***********************************************/
+#define EXP_INT 1234
+#define EXP_STR "Hell0"
 
 typedef struct {
        GThread *exp_thread;
+       gint     exp_int;
+       gchar   *exp_str;
        guint    counter;
 } SigData;
 
 static void
 sig0_cb (DummyObject *obj, SigData *data)
 {
-       g_print ("%s() called from thread %p\n", __FUNCTION__, g_thread_self());
+       if (data->exp_thread != g_thread_self ()) {
+               g_print ("%s() called from the wrong thread!\n", __FUNCTION__);
+               exit (1); /* failed! */
+       }
+       data->counter++;
+}
 
+static void
+sig1_cb (DummyObject *obj, gint i, SigData *data)
+{
        if (data->exp_thread != g_thread_self ()) {
                g_print ("%s() called from the wrong thread!\n", __FUNCTION__);
                exit (1); /* failed! */
        }
+       if (data->exp_int != i) {
+               g_print ("%s() called with the wrong integer argument!\n", __FUNCTION__);
+               exit (1); /* failed! */
+       }
        data->counter++;
 }
 
+static void
+sig2_cb (DummyObject *obj, gint i, gchar *str, SigData *data)
+{
+       if (data->exp_thread != g_thread_self ()) {
+               g_print ("%s() called from the wrong thread!\n", __FUNCTION__);
+               exit (1); /* failed! */
+       }
+       if (data->exp_int != i) {
+               g_print ("%s() called with the wrong integer argument!\n", __FUNCTION__);
+               exit (1); /* failed! */
+       }
+       if (strcmp (data->exp_str, str)) {
+               g_print ("%s() called with the wrong string argument!\n", __FUNCTION__);
+               exit (1); /* failed! */
+       }
+       data->counter++;
+}
+
+static gchar*
+sig3_cb (DummyObject *obj, gchar *str, gint i, SigData *data)
+{
+       g_print ("%s (%s, %d)\n", __FUNCTION__, str, i);
+       if (data->exp_thread != g_thread_self ()) {
+               g_print ("%s() called from the wrong thread!\n", __FUNCTION__);
+               exit (1); /* failed! */
+       }
+       if (data->exp_int != i) {
+               g_print ("%s() called with the wrong integer argument!\n", __FUNCTION__);
+               exit (1); /* failed! */
+       }
+       if (strcmp (data->exp_str, str)) {
+               g_print ("%s() called with the wrong string argument!\n", __FUNCTION__);
+               exit (1); /* failed! */
+       }
+       data->counter++;
+
+       return g_strdup_printf ("%d %s", i, str);
+}
+
+/***********************************************/
+
 /*
  * Emit signals from this thread, to be caught in the main thread
  */
@@ -71,6 +144,8 @@ static gpointer
 thread1_start (DummyObject *obj)
 {
        g_signal_emit_by_name (obj, "sig0");
+       g_thread_yield ();
+       g_signal_emit_by_name (obj, "sig0");
        return (gpointer) 0x01;
 }
 
@@ -88,9 +163,9 @@ test1 (void)
        sig_data.counter = 0;
 
        gulong hid;
-       hid = gda_connect (obj, "sig0",
-                          G_CALLBACK (sig0_cb),
-                          &sig_data, NULL, G_CONNECT_SWAPPED, NULL);
+       hid = gda_signal_connect (obj, "sig0",
+                                 G_CALLBACK (sig0_cb),
+                                 &sig_data, NULL, 0, NULL);
 
        /* prepare loop */
        GMainLoop *loop;
@@ -98,26 +173,242 @@ test1 (void)
        g_timeout_add (100, (GSourceFunc) idle_stop_main_loop, loop); /* stop main loop after 100 ms */
 
        /* run thread to emit signals */
-       GThread *th;
-       th = g_thread_new ("sub", (GThreadFunc) thread1_start, obj);
+       GThread *ths[NB_THREADS];
+       guint i;
+       for (i = 0 ; i < NB_THREADS; i++)
+               ths[i] = g_thread_new ("sub", (GThreadFunc) thread1_start, obj);
 
        g_signal_emit_by_name (obj, "sig0");
 
        /* run loop */
        g_main_loop_run (loop);
        g_main_loop_unref (loop);
-       g_thread_join (th);
+       for (i = 0 ; i < NB_THREADS; i++)
+               g_thread_join (ths[i]);
 
-       gda_disconnect (obj, hid, NULL);
+       gda_signal_handler_disconnect (obj, hid);
 
        g_signal_emit_by_name (obj, "sig0");
 
        g_object_unref (obj);
 
        /* results! */
-       if (sig_data.counter != 2) {
+       if (sig_data.counter != 2 * NB_THREADS + 1) {
+               g_print ("Error: callback has been called %u times when it should have been called %u 
times!\n",
+                        sig_data.counter, 2 * NB_THREADS + 1);
+               retval++;
+       }
+       g_print ("Test %s\n", retval ? "failed": "succeded");
+       return retval;
+}
+
+/***********************************************/
+
+/*
+ * Emit signals from this thread, to be caught in the main thread
+ */
+static gpointer
+thread2_start (DummyObject *obj)
+{
+       g_signal_emit_by_name (obj, "sig1", EXP_INT);
+       g_thread_yield ();
+       g_signal_emit_by_name (obj, "sig1", EXP_INT);
+       return (gpointer) 0x01;
+}
+
+int
+test2 (void)
+{
+       g_print ("Test2 started\n");
+       gint retval = 0;
+
+       DummyObject *obj;
+       obj = dummy_object_new ();
+
+       SigData sig_data;
+       sig_data.exp_thread = g_thread_self ();
+       sig_data.exp_int = EXP_INT;
+       sig_data.counter = 0;
+
+       gulong hid;
+       hid = gda_signal_connect (obj, "sig1",
+                                 G_CALLBACK (sig1_cb),
+                                 &sig_data, NULL, 0, NULL);
+
+       /* prepare loop */
+       GMainLoop *loop;
+       loop = g_main_loop_new (NULL, FALSE);
+       g_timeout_add (100, (GSourceFunc) idle_stop_main_loop, loop); /* stop main loop after 100 ms */
+
+       /* run thread to emit signals */
+       GThread *ths[NB_THREADS];
+       guint i;
+       for (i = 0 ; i < NB_THREADS; i++)
+               ths[i] = g_thread_new ("sub", (GThreadFunc) thread2_start, obj);
+
+       g_signal_emit_by_name (obj, "sig1", EXP_INT);
+
+       /* run loop */
+       g_main_loop_run (loop);
+       g_main_loop_unref (loop);
+       for (i = 0 ; i < NB_THREADS; i++)
+               g_thread_join (ths[i]);
+
+       gda_signal_handler_disconnect (obj, hid);
+
+       g_signal_emit_by_name (obj, "sig1", EXP_INT * 2);
+
+       g_object_unref (obj);
+
+       /* results! */
+       if (sig_data.counter != 2 * NB_THREADS + 1) {
+               g_print ("Error: callback has been called %u times when it should have been called %u 
times!\n",
+                        sig_data.counter, 2 * NB_THREADS + 1);
+               retval++;
+       }
+       g_print ("Test %s\n", retval ? "failed": "succeded");
+       return retval;
+}
+
+
+/***********************************************/
+
+/*
+ * Emit signals from this thread, to be caught in the main thread
+ */
+static gpointer
+thread3_start (DummyObject *obj)
+{
+       g_signal_emit_by_name (obj, "sig2", EXP_INT, EXP_STR);
+       g_thread_yield ();
+       g_signal_emit_by_name (obj, "sig2", EXP_INT, EXP_STR);
+       return (gpointer) 0x01;
+}
+
+int
+test3 (void)
+{
+       g_print ("Test3 started\n");
+       gint retval = 0;
+
+       DummyObject *obj;
+       obj = dummy_object_new ();
+
+       SigData sig_data;
+       sig_data.exp_thread = g_thread_self ();
+       sig_data.exp_int = EXP_INT;
+       sig_data.exp_str = EXP_STR;
+       sig_data.counter = 0;
+
+       gulong hid;
+       hid = gda_signal_connect (obj, "sig2",
+                                 G_CALLBACK (sig2_cb),
+                                 &sig_data, NULL, 0, NULL);
+
+       /* prepare loop */
+       GMainLoop *loop;
+       loop = g_main_loop_new (NULL, FALSE);
+       g_timeout_add (100, (GSourceFunc) idle_stop_main_loop, loop); /* stop main loop after 100 ms */
+
+       /* run thread to emit signals */
+       GThread *ths[NB_THREADS];
+       guint i;
+       for (i = 0 ; i < NB_THREADS; i++)
+               ths[i] = g_thread_new ("sub", (GThreadFunc) thread3_start, obj);
+
+       g_signal_emit_by_name (obj, "sig2", EXP_INT, EXP_STR);
+
+       /* run loop */
+       g_main_loop_run (loop);
+       g_main_loop_unref (loop);
+       for (i = 0 ; i < NB_THREADS; i++)
+               g_thread_join (ths[i]);
+
+       gda_signal_handler_disconnect (obj, hid);
+
+       g_signal_emit_by_name (obj, "sig2", EXP_INT * 2, NULL);
+
+       g_object_unref (obj);
+
+       /* results! */
+       if (sig_data.counter != 2 * NB_THREADS + 1) {
+               g_print ("Error: callback has been called %u times when it should have been called %u 
times!\n",
+                        sig_data.counter, 2 * NB_THREADS + 1);
+               retval++;
+       }
+       g_print ("Test %s\n", retval ? "failed": "succeded");
+       return retval;
+}
+
+/***********************************************/
+
+/*
+ * Emit signals from this thread, to be caught in the main thread
+ */
+static gpointer
+thread4_start (DummyObject *obj)
+{
+       gchar *tmp;
+       g_signal_emit_by_name (obj, "sig3", EXP_STR, EXP_INT, &tmp);
+       g_free (tmp);
+       g_thread_yield ();
+       g_signal_emit_by_name (obj, "sig3", EXP_STR, EXP_INT, &tmp);
+       g_free (tmp);
+       return (gpointer) 0x01;
+}
+
+int
+test4 (void)
+{
+       g_print ("Test4 started\n");
+       gint retval = 0;
+
+       DummyObject *obj;
+       obj = dummy_object_new ();
+
+       SigData sig_data;
+       sig_data.exp_thread = g_thread_self ();
+       sig_data.exp_int = EXP_INT;
+       sig_data.exp_str = EXP_STR;
+       sig_data.counter = 0;
+
+       gulong hid;
+       hid = gda_signal_connect (obj, "sig3",
+                                 G_CALLBACK (sig3_cb),
+                                 &sig_data, NULL, 0, NULL);
+
+       /* prepare loop */
+       GMainLoop *loop;
+       loop = g_main_loop_new (NULL, FALSE);
+       g_timeout_add (100, (GSourceFunc) idle_stop_main_loop, loop); /* stop main loop after 100 ms */
+
+       /* run thread to emit signals */
+       GThread *ths[NB_THREADS];
+       guint i;
+       for (i = 0 ; i < NB_THREADS; i++)
+               ths[i] = g_thread_new ("sub", (GThreadFunc) thread4_start, obj);
+
+       gchar *tmp;
+       g_signal_emit_by_name (obj, "sig3", EXP_STR, EXP_INT, &tmp);
+       g_print ("TMP=[%s]\n", tmp);
+       g_free (tmp);
+
+       /* run loop */
+       g_main_loop_run (loop);
+       g_main_loop_unref (loop);
+       for (i = 0 ; i < NB_THREADS; i++)
+               g_thread_join (ths[i]);
+
+       gda_signal_handler_disconnect (obj, hid);
+
+       g_signal_emit_by_name (obj, "sig2", EXP_INT * 2, NULL);
+
+       g_object_unref (obj);
+
+       /* results! */
+       if (sig_data.counter != 2 * NB_THREADS + 1) {
                g_print ("Error: callback has been called %u times when it should have been called %u 
times!\n",
-                        sig_data.counter, 2);
+                        sig_data.counter, 2 * NB_THREADS + 1);
                retval++;
        }
        g_print ("Test %s\n", retval ? "failed": "succeded");
diff --git a/libgda/thread-wrapper/test-raw-itsignaler.c b/libgda/thread-wrapper/test-raw-itsignaler.c
index cc93560..f881656 100644
--- a/libgda/thread-wrapper/test-raw-itsignaler.c
+++ b/libgda/thread-wrapper/test-raw-itsignaler.c
@@ -150,7 +150,7 @@ main (int argc, char** argv)
        if (counter == MAX_ITERATIONS * 2) {
                gdouble duration;
                duration = g_timer_elapsed (timer, NULL);
-               g_print ("Test Ok, got %u notification in %0.5f s\n", MAX_ITERATIONS, duration);
+               g_print ("Test Ok, got %u notification in %0.5f s\n", MAX_ITERATIONS * 2, duration);
                g_timer_destroy (timer);
                return EXIT_SUCCESS;
        }


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