[libgda] Inter thread signal emissions corrections
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] Inter thread signal emissions corrections
- Date: Thu, 26 Jun 2014 05:31:18 +0000 (UTC)
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]