[libgda] Corrected thread non ending problem and Added background thread to help manage GdaWorker objects
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] Corrected thread non ending problem and Added background thread to help manage GdaWorker objects
- Date: Sun, 18 May 2014 19:47:33 +0000 (UTC)
commit 31207c78eda5fbb66f2b7e1ee5c6961be0097873
Author: Vivien Malerba <malerba gnome-db org>
Date: Sun May 18 21:21:03 2014 +0200
Corrected thread non ending problem and Added background thread to help manage GdaWorker objects
doc/C/libgda-sections.txt | 2 +
doc/C/prov-writing-virtual-methods.xml | 29 ++
libgda/gda-server-provider-impl.h | 2 +-
libgda/gda-server-provider-private.h | 2 -
libgda/gda-server-provider.c | 58 ++--
libgda/sqlite/gda-sqlite-provider.c | 25 +-
libgda/thread-wrapper/Makefile.am | 27 +-
libgda/thread-wrapper/background.c | 492 ++++++++++++++++++++
libgda/thread-wrapper/background.h | 64 +++
libgda/thread-wrapper/gda-worker.c | 188 ++++++---
libgda/thread-wrapper/gda-worker.h | 11 +-
libgda/thread-wrapper/itsignaler.c | 136 ++----
libgda/thread-wrapper/itsignaler.h | 5 +
libgda/thread-wrapper/test-worker.c | 23 -
providers/firebird/gda-firebird-provider.c | 19 +-
providers/jdbc/gda-jdbc-provider.c | 11 +-
providers/mysql/gda-mysql-provider.c | 32 +-
providers/oracle/gda-oracle-provider.c | 9 +-
providers/postgres/gda-postgres-provider.c | 21 +-
.../skel-implementation/capi/gda-capi-provider.c | 22 +-
providers/web/gda-web-provider.c | 13 +-
21 files changed, 914 insertions(+), 277 deletions(-)
---
diff --git a/doc/C/libgda-sections.txt b/doc/C/libgda-sections.txt
index dd91e23..424ef9d 100644
--- a/doc/C/libgda-sections.txt
+++ b/doc/C/libgda-sections.txt
@@ -1913,8 +1913,10 @@ gda_sql_builder_get_type
<TITLE>GdaWorker</TITLE>
GdaWorker
gda_worker_new
+gda_worker_new_unique
gda_worker_ref
gda_worker_unref
+gda_worker_unref_timed
gda_worker_kill
<SUBSECTION>
GdaWorkerFunc
diff --git a/doc/C/prov-writing-virtual-methods.xml b/doc/C/prov-writing-virtual-methods.xml
index e5eeb09..eeb9a5c 100644
--- a/doc/C/prov-writing-virtual-methods.xml
+++ b/doc/C/prov-writing-virtual-methods.xml
@@ -66,6 +66,35 @@
no other code than the provider's code will ever be executed from within the worker thread (and some
code from
the GdaServerProvider object itself to do the binding).
</para>
+
+ <sect2 id="prov-create-worker">
+ <title>create_worker() - optional</title>
+ <para>
+ Regarding thread safety, each database API has one of the following behaviours:
+ <orderedlist>
+ <listitem><para>the API is enough thread safe to allow the API's objects related to a connection to
be used by a thread while
+ the API's objects related to another connection can be used by another thread. In this
+ case, each connection is used from its own "private" thread (and thus a <link
linkend="GdaWorker">GdaWorker</link> object
+ per connection)</para></listitem>
+ <listitem><para>the API is not enough thread safe to be in the 1st cathegory. However, it supports
being used from
+ several threads providing that at any time, all the API's objects are used from only one thread.
+ In this case it is possible for example at one point to use the API from a
+ thread A, and at a later time from thread B (in case thread A has finished for example). In this
case all the connections
+ are used from a single thread (and thus a single <link linkend="GdaWorker">GdaWorker</link>
object).</para></listitem>
+ <listitem><para>the API is not thread safe, and the thread which first uses (or initializes) the
API must be the one from which
+ all the API objects are manipulated. In this case, the thread which first uses the API must be the
only one which ever uses the API
+ and thus only a single <link linkend="GdaWorker">GdaWorker</link> object must ever exist for the
database provider.</para></listitem>
+ </orderedlist>
+ </para>
+ <para>
+ This method creates <link linkend="GdaWorker">GdaWorker</link> objects when the common database
provider implementation needs to have
+ a <link linkend="GdaWorker">GdaWorker</link> object.
+ </para>
+ <para>
+ If this function is not implemented, the database provider considers that the API is thread safe
enough to have a
+ <link linkend="GdaWorker">GdaWorker</link> for each connection (1st cathegory above).
+ </para>
+ </sect2>
</sect1>
<sect1>
diff --git a/libgda/gda-server-provider-impl.h b/libgda/gda-server-provider-impl.h
index 967f36c..a34d684 100644
--- a/libgda/gda-server-provider-impl.h
+++ b/libgda/gda-server-provider-impl.h
@@ -68,7 +68,7 @@ typedef struct {
const gchar *(* get_server_version) (GdaServerProvider *provider, GdaConnection *cnc);
gboolean (* supports_feature) (GdaServerProvider *provider, GdaConnection *cnc,
GdaConnectionFeature feature);
- GdaWorker *(* create_worker) (GdaServerProvider *provider); /* may be NULL */
+ GdaWorker *(* create_worker) (GdaServerProvider *provider, gboolean for_cnc);
GdaConnection *(* create_connection) (GdaServerProvider *provider); /* may be NULL */
GdaSqlParser *(* create_parser) (GdaServerProvider *provider, GdaConnection *cnc); /*
may be NULL */
GdaDataHandler *(* get_data_handler) (GdaServerProvider *provider, GdaConnection *cnc, /*
may be NULL */
diff --git a/libgda/gda-server-provider-private.h b/libgda/gda-server-provider-private.h
index 111026a..0c3ff43 100644
--- a/libgda/gda-server-provider-private.h
+++ b/libgda/gda-server-provider-private.h
@@ -34,8 +34,6 @@ struct _GdaServerProviderPrivate {
GdaSqlParser *parser;
GHashTable *jobs_hash; /* key = a job ID, value = a # */
- GdaWorker *gen_worker; /* worker used when no connection is specified, and yet the provider's
code need
- * to be executed */
};
diff --git a/libgda/gda-server-provider.c b/libgda/gda-server-provider.c
index 86876a3..ccf7673 100644
--- a/libgda/gda-server-provider.c
+++ b/libgda/gda-server-provider.c
@@ -150,7 +150,6 @@ gda_server_provider_init (GdaServerProvider *provider,
(GDestroyNotify)
gda_server_provider_handler_info_free,
(GDestroyNotify) g_object_unref);
provider->priv->jobs_hash = NULL;
- provider->priv->gen_worker = NULL;
}
static void
@@ -166,8 +165,6 @@ gda_server_provider_finalize (GObject *object)
g_hash_table_destroy (provider->priv->jobs_hash);
if (provider->priv->parser)
g_object_unref (provider->priv->parser);
- if (provider->priv->gen_worker)
- gda_worker_unref (provider->priv->gen_worker);
g_free (provider->priv);
provider->priv = NULL;
@@ -211,6 +208,8 @@ gda_server_provider_constructed (GObject *object)
g_warning ("Internal error after creation of %s: : %s() virtual function
missing", gtype_name, "statement_rewrite");
if (! fset->open_connection)
g_warning ("Internal error after creation of %s: : %s() virtual function
missing", gtype_name, "open_connection");
+ if (! fset->create_worker)
+ g_warning ("Internal error after creation of %s: : %s() virtual function
missing", gtype_name, "create_worker");
if (! fset->close_connection)
g_warning ("Internal error after creation of %s: : %s() virtual function
missing", gtype_name, "close_connection");
if (fset->escape_string && !fset->unescape_string)
@@ -473,7 +472,8 @@ gda_server_provider_get_impl_functions_for_class (GObjectClass *klass, GdaServer
/*
* _gda_server_provider_create_worker:
* @prov: a #GdaServerProvider
- * @for_new_cnc: if %FALSE, then we try to reuse a #GdaWorker, otherwise the GdaWorker will be used for a
new connection
+ * @for_cnc: if %TRUE, then the #GdaWorker will be used for a connection, and if %FALSE, it will be used for
the non connection code
+ * (in effect the returned #GdaWorker may be the same)
*
* Have @prov create a #GdaWorker. Any connection and C API will only be manipulated by the worker's working
thread,
* so if @prov can only be used by 1 thread, then it needs to always return the same object (increasing its
reference count).
@@ -483,26 +483,17 @@ gda_server_provider_get_impl_functions_for_class (GObjectClass *klass, GdaServer
* Returns: (transfer full): a new #GdaWorker
*/
static GdaWorker *
-_gda_server_provider_create_worker (GdaServerProvider *provider, gboolean for_new_cnc)
+_gda_server_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc)
{
g_return_val_if_fail (GDA_IS_SERVER_PROVIDER (provider), NULL);
- if (for_new_cnc) {
- GdaServerProviderBase *fset;
- fset = CLASS (provider)->functions_sets [GDA_SERVER_PROVIDER_FUNCTIONS_BASE]; /* rem: we
don't use
- *
_gda_server_provider_get_impl_functions()
- * because this
would fail if not
- * called from
the worker thread */
- if (fset->create_worker)
- return (fset->create_worker) (provider);
- else
- return gda_worker_new ();
- }
- else {
- if (!provider->priv->gen_worker)
- provider->priv->gen_worker = _gda_server_provider_create_worker (provider, TRUE);
- return gda_worker_ref (provider->priv->gen_worker);
- }
+ GdaServerProviderBase *fset;
+ fset = CLASS (provider)->functions_sets [GDA_SERVER_PROVIDER_FUNCTIONS_BASE]; /* rem: we don't use
+ *
_gda_server_provider_get_impl_functions()
+ * because this would
fail if not
+ * called from the
worker thread */
+ g_assert (fset->create_worker);
+ return (fset->create_worker) (provider, for_cnc);
}
/*
@@ -749,11 +740,12 @@ gda_server_provider_supports_operation (GdaServerProvider *provider, GdaConnecti
gda_worker_do_job (worker, context, 0, &retval, NULL,
(GdaWorkerFunc) worker_supports_operation, (gpointer) &data, NULL, NULL, NULL);
g_main_context_unref (context);
- gda_worker_unref (worker);
if (cnc)
gda_lockable_unlock ((GdaLockable*) cnc); /* CNC UNLOCK */
+ gda_worker_unref (worker);
+
return retval ? TRUE : FALSE;
}
@@ -959,11 +951,12 @@ gda_server_provider_create_operation (GdaServerProvider *provider, GdaConnection
gda_worker_do_job (worker, context, 0, (gpointer) &op, NULL,
(GdaWorkerFunc) worker_create_operation, (gpointer) &data, NULL, NULL, NULL);
g_main_context_unref (context);
- gda_worker_unref (worker);
if (cnc)
gda_lockable_unlock ((GdaLockable*) cnc); /* CNC UNLOCK */
+ gda_worker_unref (worker);
+
if (op) {
/* test op's conformance */
OpReq *opreq = op_req_table [type];
@@ -2027,13 +2020,16 @@ worker_open_connection (WorkerOpenConnectionData *data, GError **error)
}
else
cdata->worker = gda_worker_ref (data->worker);
+
if (fset->prepare_connection) {
result = fset->prepare_connection (data->provider, data->cnc, data->params,
data->auth);
if (!result) {
fset->close_connection (data->provider, data->cnc);
gda_connection_internal_set_provider_data (data->cnc, NULL, NULL);
- if (cdata->worker)
- gda_worker_unref (cdata->worker);
+
+ gda_worker_unref (cdata->worker);
+ cdata->worker = NULL;
+
if (cdata->provider_data_destroy_func)
cdata->provider_data_destroy_func (cdata);
}
@@ -2274,10 +2270,9 @@ stage2_close_connection (GdaConnection *cnc, gpointer result)
if (result) {
GdaServerProviderConnectionData *cdata;
cdata = gda_connection_internal_get_provider_data_error (cnc, NULL);
- if (cdata) {
+ if (cdata) {
gda_connection_internal_set_provider_data (cnc, NULL, NULL);
- if (cdata->worker)
- gda_worker_unref (cdata->worker);
+
if (cdata->provider_data_destroy_func)
cdata->provider_data_destroy_func (cdata);
}
@@ -2326,11 +2321,18 @@ _gda_server_provider_close_connection (GdaServerProvider *provider, GdaConnectio
jdata->cnc = g_object_ref (cnc);
_gda_connection_set_status (cnc, GDA_CONNECTION_STATUS_BUSY);
+
+ GdaWorker *worker;
+ worker = cdata->worker;
+
gpointer result;
gda_worker_do_job (cdata->worker, context, 0, &result, NULL,
(GdaWorkerFunc) worker_close_connection, jdata, (GDestroyNotify)
WorkerCloseConnectionData_free,
NULL, error);
g_main_context_unref (context);
+
+ gda_worker_unref (worker);
+
return stage2_close_connection (cnc, result);
}
diff --git a/libgda/sqlite/gda-sqlite-provider.c b/libgda/sqlite/gda-sqlite-provider.c
index 43fe452..9a82c77 100644
--- a/libgda/sqlite/gda-sqlite-provider.c
+++ b/libgda/sqlite/gda-sqlite-provider.c
@@ -8,7 +8,7 @@
* Copyright (C) 2004 J�rg Billeter <j bitron ch>
* Copyright (C) 2004 Nikolai Weibull <ruby-gnome2-devel-en-list pcppopper org>
* Copyright (C) 2005 Denis Fortin <denis fortin free fr>
- * Copyright (C) 2005 - 2013 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2005 - 2014 Vivien Malerba <malerba gnome-db org>
* Copyright (C) 2005 �lvaro Pe�a <alvaropg telefonica net>
* Copyright (C) 2008 - 2009 Bas Driessen <bas driessen xobas com>
* Copyright (C) 2008 - 2011 Murray Cumming <murrayc murrayc com>
@@ -295,7 +295,7 @@ static const gchar *gda_sqlite_provider_get_version (GdaServerProvider *p
static gboolean gda_sqlite_provider_supports_feature (GdaServerProvider *provider, GdaConnection
*cnc,
GdaConnectionFeature feature);
-static GdaWorker *gda_sqlite_provider_create_worker (GdaServerProvider *provider);
+static GdaWorker *gda_sqlite_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc);
static const gchar *gda_sqlite_provider_get_name (GdaServerProvider *provider);
static GdaDataHandler *gda_sqlite_provider_get_data_handler (GdaServerProvider *provider, GdaConnection
*cnc,
@@ -639,19 +639,22 @@ gda_sqlite_provider_get_type (void)
return type;
}
+
+
static GdaWorker *
-gda_sqlite_provider_create_worker (GdaServerProvider *provider)
+gda_sqlite_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc)
{
- static GdaWorker *unique_worker = NULL;
- if (unique_worker)
- return gda_worker_ref (unique_worker);
+ /* see http://www.sqlite.org/threadsafe.html */
- if (SQLITE3_CALL (sqlite3_threadsafe) ())
- return gda_worker_new ();
- else {
- unique_worker = gda_worker_new ();
- return gda_worker_ref (unique_worker);
+ static GdaWorker *unique_worker = NULL;
+ if (SQLITE3_CALL (sqlite3_threadsafe) ()) {
+ if (for_cnc)
+ return gda_worker_new ();
+ else
+ return gda_worker_new_unique (&unique_worker, TRUE);
}
+ else
+ return gda_worker_new_unique (&unique_worker, TRUE);
}
/*
diff --git a/libgda/thread-wrapper/Makefile.am b/libgda/thread-wrapper/Makefile.am
index d217548..95e3ead 100644
--- a/libgda/thread-wrapper/Makefile.am
+++ b/libgda/thread-wrapper/Makefile.am
@@ -29,6 +29,8 @@ libgda_threadwrapperinclude_HEADERS=$(libgda_threadwrapper_headers)
libgda_threadwrapper_6_0_la_SOURCES = \
$(libgda_threadwrapper_headers) \
+ background.h \
+ background.c \
itsignaler.h \
itsignaler.c \
gda-worker.c \
@@ -36,7 +38,8 @@ libgda_threadwrapper_6_0_la_SOURCES = \
# test_itsignaler
test_itsignaler_CFLAGS = \
- $(COREDEPS_CFLAGS)
+ $(COREDEPS_CFLAGS) \
+ -DNOBG
test_itsignaler_SOURCES = \
itsignaler.h \
@@ -49,7 +52,8 @@ test_itsignaler_LDADD = \
# test_raw_itsignaler
test_raw_itsignaler_CFLAGS = \
- $(COREDEPS_CFLAGS)
+ $(COREDEPS_CFLAGS) \
+ -DNOBG
test_raw_itsignaler_SOURCES = \
itsignaler.h \
@@ -64,7 +68,8 @@ test_raw_itsignaler_LDADD = \
# test_raw_itsignaler_no_eventfd
test_raw_itsignaler_no_eventfd_CFLAGS = \
-DFORCE_NO_EVENTFD \
- $(COREDEPS_CFLAGS)
+ $(COREDEPS_CFLAGS) \
+ -DNOBG
test_raw_itsignaler_no_eventfd_SOURCES = \
itsignaler.h \
@@ -76,7 +81,8 @@ test_raw_itsignaler_no_eventfd_LDADD = \
# test_blocking_itsignaler
test_blocking_itsignaler_CFLAGS = \
- $(COREDEPS_CFLAGS)
+ $(COREDEPS_CFLAGS) \
+ -DNOBG
test_blocking_itsignaler_SOURCES = \
itsignaler.h \
@@ -90,7 +96,8 @@ test_blocking_itsignaler_LDADD = \
# perf_itsignaler
perf_itsignaler_CFLAGS = \
-DPERF \
- $(COREDEPS_CFLAGS)
+ $(COREDEPS_CFLAGS) \
+ -DNOBG
perf_itsignaler_SOURCES = \
itsignaler.h \
@@ -106,7 +113,8 @@ perf_itsignaler_LDADD = \
perf_itsignaler_no_eventfd_CFLAGS = \
-DFORCE_NO_EVENTFD \
-DPERF \
- $(COREDEPS_CFLAGS)
+ $(COREDEPS_CFLAGS) \
+ -DNOBG
perf_itsignaler_no_eventfd_SOURCES = \
itsignaler.h \
@@ -121,6 +129,8 @@ test_worker_CFLAGS = \
$(COREDEPS_CFLAGS)
test_worker_SOURCES = \
+ background.h \
+ background.c \
itsignaler.h \
itsignaler.c \
gda-worker.h \
@@ -132,7 +142,8 @@ test_worker_LDADD = \
# test_connect
test_connect_CFLAGS = \
- $(COREDEPS_CFLAGS)
+ $(COREDEPS_CFLAGS) \
+ -DNOBG
test_connect_SOURCES = \
dummy-object.h \
@@ -144,4 +155,4 @@ test_connect_SOURCES = \
test-connect.c
test_connect_LDADD = \
- $(COREDEPS_LIBS)
\ No newline at end of file
+ $(COREDEPS_LIBS)
diff --git a/libgda/thread-wrapper/background.c b/libgda/thread-wrapper/background.c
new file mode 100644
index 0000000..5791918
--- /dev/null
+++ b/libgda/thread-wrapper/background.c
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2014 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * The "maintenance", or "background" thread is responsible to:
+ * - cache GdaWorker objects ready to be reused
+ * - join worker threads from GdaWorker
+ *
+ * - trim spare ITSignaler ?
+ */
+
+#include "gda-worker.h"
+#include "background.h"
+
+#define SPARE_WORKER_DELAY_MS 2000
+#define SPARE_ITS_DELAY_MS 2000
+#define MAKE_STATS
+#undef MAKE_STATS
+
+#ifdef MAKE_STATS
+ #ifndef G_OS_WIN32
+ #include <sys/types.h>
+ #include <unistd.h>
+ #endif
+#endif
+
+/*
+ * global data
+ */
+GMutex bg_mutex; /* protects:
+ * - the "background" thread creation, AND
+ * - the @spare_workers array
+ * - the @spare_its array */
+
+
+/*
+ * Job type: JOB_JOIN_THREAD
+ */
+typedef struct {
+ GThread *thread;
+} JoinThreadData;
+
+static void
+JoinThreadData_free (JoinThreadData *data)
+{
+ g_slice_free (JoinThreadData, data);
+}
+
+/*
+ * Job type: JOB_SPARE_WORKER
+ */
+typedef struct {
+ GdaWorker *worker;
+ guint ms;
+} WorkerSpareData;
+static GArray *spare_workers = NULL; /* array of WorkerSpareData pointers (ref held), only used from within
the "background" thread.
+ * the last entries have a higher value of @ms (because new elements are
appended) */
+static void
+WorkerSpareData_free (WorkerSpareData *data)
+{
+ if (data->worker) {
+ g_print ("[_gda_worker_bg_unref(%p)]\n", data->worker);
+ _gda_worker_bg_unref (data->worker);
+ }
+ g_slice_free (WorkerSpareData, data);
+}
+
+/*
+ * Job type: JOB_SPARE_ITS
+ */
+typedef struct {
+ ITSignaler *its;
+ guint ms;
+} ItsSpareData;
+static GArray *spare_its = NULL; /* array of ItsSpareData pointers (ref held), only used from within the
"background" thread.
+ * the last entries have a higher value of @ms (because new elements are
appended) */
+static void
+ItsSpareData_free (ItsSpareData *data)
+{
+ if (data->its) {
+ g_print ("[_its_bg_unref(%p)]\n", data->its);
+ _itsignaler_bg_unref (data->its);
+ }
+ g_slice_free (ItsSpareData, data);
+}
+
+/*
+ * Job transmission through a GAsyncQueue
+ */
+static GAsyncQueue *bgqueue = NULL; /* vector to pass jobs to "background" thread */
+typedef enum {
+ JOB_JOIN_THREAD,
+ JOB_SPARE_WORKER,
+ JOB_SPARE_ITS
+} JobType;
+
+typedef struct {
+ JobType type;
+ union {
+ JoinThreadData *u1;
+ WorkerSpareData *u2;
+ ItsSpareData *u3;
+ } u;
+} Job;
+
+/*
+ * Utility
+ */
+static guint
+compute_wait_delay (void)
+{
+ guint delay = 0;
+
+ g_mutex_lock (&bg_mutex);
+ /*
+ guint i;
+ for (i = 0; i < spare_workers->len; i++) {
+ WorkerSpareData *data;
+ data = g_array_index (spare_workers, WorkerSpareData*, i);
+ if (i == 0)
+ delay = data->ms;
+ else
+ delay = MIN (delay, data->ms);
+ }
+ */
+ if (spare_workers->len > 0) { /* we use here the fact that @spare_workers is ordered */
+ WorkerSpareData *data;
+ data = g_array_index (spare_workers, WorkerSpareData*, 0);
+ delay = data->ms;
+ }
+ if (spare_its->len > 0) { /* we use here the fact that @spare_its is ordered */
+ ItsSpareData *data;
+ data = g_array_index (spare_its, ItsSpareData*, 0);
+ if (delay == 0)
+ delay = data->ms;
+ else
+ delay = MIN (data->ms, delay);
+ }
+ g_mutex_unlock (&bg_mutex);
+
+ return delay;
+}
+
+/*
+ * MAIN part of the "background" thread
+ */
+static gpointer
+background_main (gpointer data)
+{
+ GTimer *timer;
+ guint elapsed_ms = 0;
+ timer = g_timer_new ();
+ g_timer_stop (timer);
+
+ while (1) {
+ /* honor delayed operations */
+ GSList *list;
+ g_mutex_lock (&bg_mutex);
+ guint i;
+ for (i = 0; i < spare_workers->len; ) {
+ WorkerSpareData *data;
+ data = g_array_index (spare_workers, WorkerSpareData*, i);
+
+ if (data->ms <= elapsed_ms) {
+ g_array_remove_index (spare_workers, 0);
+ WorkerSpareData_free (data);
+ }
+ else {
+ data->ms -= elapsed_ms;
+ i++;
+ }
+ }
+ for (i = 0; i < spare_its->len; ) {
+ ItsSpareData *data;
+ data = g_array_index (spare_its, ItsSpareData*, i);
+
+ if (data->ms <= elapsed_ms) {
+ g_array_remove_index (spare_its, 0);
+ ItsSpareData_free (data);
+ }
+ else {
+ data->ms -= elapsed_ms;
+ i++;
+ }
+ }
+ g_mutex_unlock (&bg_mutex);
+
+ /* compute maximum time to wait */
+ guint next_delay_ms;
+ next_delay_ms = compute_wait_delay ();
+
+ /* fetch new job submissions from the queue */
+ Job *job;
+ g_timer_start (timer);
+ if (next_delay_ms == 0)
+ job = g_async_queue_pop (bgqueue);
+ else
+ job = g_async_queue_timeout_pop (bgqueue, next_delay_ms * 1000);
+ g_timer_stop (timer);
+ elapsed_ms = (guint) (g_timer_elapsed (timer, NULL) * 1000.);
+
+ if (job) {
+ switch (job->type) {
+ case JOB_SPARE_WORKER:
+ g_mutex_lock (&bg_mutex);
+ g_array_append_val (spare_workers, job->u.u2);
+ g_print ("[Cached GdaWorker %p]\n", job->u.u2->worker);
+ g_mutex_unlock (&bg_mutex);
+ break;
+ case JOB_SPARE_ITS:
+ g_mutex_lock (&bg_mutex);
+ g_array_append_val (spare_its, job->u.u3);
+ g_print ("[Cached ITS %p]\n", job->u.u3->its);
+ g_mutex_unlock (&bg_mutex);
+ break;
+ case JOB_JOIN_THREAD: {
+ JoinThreadData *data;
+ data = job->u.u1;
+ g_print ("[g_thread_join(%p)]\n", data->thread);
+
+ bg_update_stats (BG_JOINED_THREADS);
+
+ g_thread_join (data->thread);
+ JoinThreadData_free (data);
+ break;
+ }
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_slice_free (Job, job); /* free the Job "shell" */
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ * _bg_start:
+ *
+ * Have the "background" thread start. May be called several times.
+ */
+static void
+_bg_start (void)
+{
+ static gboolean th_started = FALSE;
+ g_mutex_lock (&bg_mutex);
+
+ if (!bgqueue)
+ bgqueue = g_async_queue_new ();
+
+ if (!spare_its)
+ spare_its = g_array_new (FALSE, FALSE, sizeof (ItsSpareData*));
+
+ if (!spare_workers)
+ spare_workers = g_array_new (FALSE, FALSE, sizeof (WorkerSpareData*));
+
+ if (!th_started) {
+ GThread *th;
+ th = g_thread_new ("background", background_main, NULL);
+ if (th) {
+ th_started = TRUE;
+ }
+ else
+ g_print ("Failed to start BACKGROUND thread\n");
+ }
+
+ g_mutex_unlock (&bg_mutex);
+}
+
+/**
+ * bg_join_thread:
+ *
+ * This function must be called by a thread right before it exits, it tells the "background" thread that
+ * it can call g_thread_join() without having the risk of blocking. It is called by worker threads right
before
+ * they exit.
+ */
+void
+bg_join_thread ()
+{
+ _bg_start ();
+
+ Job *job;
+ job = g_slice_new (Job);
+ job->type = JOB_JOIN_THREAD;
+ job->u.u1 = g_slice_new (JoinThreadData);
+ job->u.u1->thread = g_thread_self ();
+
+ g_async_queue_push (bgqueue, job);
+}
+
+/**
+ * bg_set_spare_gda_worker:
+ * @worker: a #GdaWorker
+ *
+ * This function requests that the "background" handle the caching or the destruction of @worker. It is
intended to be called
+ * only from within the gda_worker_unref() method when the reference count is 0, but right before destroying
it.
+ *
+ * The caller (the GdaWorker's code) must first set the reference count to 1 (and not destroy the object).
+ */
+void
+bg_set_spare_gda_worker (GdaWorker *worker)
+{
+ g_return_if_fail (worker);
+ _bg_start ();
+
+ Job *job;
+ job = g_slice_new (Job);
+ job->type = JOB_SPARE_WORKER;
+ job->u.u2 = g_slice_new (WorkerSpareData);
+ job->u.u2->worker = worker;
+ job->u.u2->ms = SPARE_WORKER_DELAY_MS; /* fixed delay => array is ordered, see compute_wait_delay() */
+
+ bg_update_stats (BG_CACHED_WORKER_REQUESTS);
+
+ g_async_queue_push (bgqueue, job);
+}
+
+/**
+ * bg_get_spare_gda_worker:
+ *
+ * Requests the "background" thread to provide a #GdaWorker which it has kept as a cache (see
bg_set_spare_gda_worker()).
+ * The return value may be %NULL (if no #GdaWorker object is available), or a pointer to a #GdaWorker which
refcount is 1
+ * and which has a worker thread available immediately (no job pending or in process).
+ *
+ * Returns: (transfer full): a #GdaWorker, or %NULL
+ */
+GdaWorker *
+bg_get_spare_gda_worker (void)
+{
+ GdaWorker *worker = NULL;
+ _bg_start ();
+
+ g_mutex_lock (&bg_mutex);
+ if (spare_workers->len > 0) {
+ WorkerSpareData *data;
+ data = g_array_index (spare_workers, WorkerSpareData*, 0);
+
+ worker = data->worker;
+ data->worker = NULL;
+ WorkerSpareData_free (data);
+
+ g_array_remove_index (spare_workers, 0);
+ g_print ("[Fetched cached GdaWorker %p]\n", worker);
+
+ bg_update_stats (BG_REUSED_WORKER_FROM_CACHE);
+ }
+ g_mutex_unlock (&bg_mutex);
+
+ return worker;
+}
+
+/*
+ * ITSignaler caching
+ */
+/**
+ * bg_set_spare_its:
+ * @its: a #ITSignaler
+ *
+ * This function requests that the "background" handle the caching or the destruction of @its. It is
intended to be called
+ * only from within the itsignaler_unref() method when the reference count is 0, but right before destroying
it.
+ *
+ * The caller (the ITSIgnaler's code) must first set the reference count to 1 (and not destroy the object).
+ */
+void
+bg_set_spare_its (ITSignaler *its)
+{
+ g_return_if_fail (its);
+ _bg_start ();
+
+ Job *job;
+ job = g_slice_new (Job);
+ job->type = JOB_SPARE_ITS;
+ job->u.u3 = g_slice_new (ItsSpareData);
+ job->u.u3->its = its;
+ job->u.u3->ms = SPARE_ITS_DELAY_MS; /* fixed delay => array is ordered, see compute_wait_delay() */
+
+ bg_update_stats (BG_CACHED_ITS_REQUESTS);
+
+ g_async_queue_push (bgqueue, job);
+}
+
+/**
+ * bg_get_spare_its:
+ *
+ * Requests the "background" thread to provide a #ITSignaler which it has kept as a cache (see
bg_set_spare_its()).
+ * The return value may be %NULL (if no #ITSignaler object is available), or a pointer to a #ITSignaler
which refcount is 1.
+ *
+ * Returns: (transfer full): a #ITSignaler, or %NULL
+ */
+ITSignaler *
+bg_get_spare_its (void)
+{
+ ITSignaler *its = NULL;
+ _bg_start ();
+
+ g_mutex_lock (&bg_mutex);
+ if (spare_its->len > 0) {
+ ItsSpareData *data;
+ data = g_array_index (spare_its, ItsSpareData*, 0);
+
+ its = data->its;
+ data->its = NULL;
+ ItsSpareData_free (data);
+
+ g_array_remove_index (spare_its, 0);
+ g_print ("[Fetched cached ITS %p]\n", its);
+
+ bg_update_stats (BG_REUSED_ITS_FROM_CACHE);
+ }
+ g_mutex_unlock (&bg_mutex);
+
+ return its;
+}
+
+
+/*
+ * Statistics
+ */
+#ifdef MAKE_STATS
+guint stats [BG_STATS_MAX];
+
+static void
+write_stats (void)
+{
+ gchar *strings [] = {
+ "GdaWorker created",
+ "GdaWorker destroyed",
+ "GdaWorker cache requests",
+ "GdaWorker reused from cache",
+
+ "Started threads",
+ "Joined threads",
+
+ "ITS created",
+ "ITS destroyed",
+ "ITS cache requests",
+ "ITS reused from cache"
+ };
+ BackgroundStats i;
+ GString *string;
+ string = g_string_new ("=== stats start ===\n");
+ for (i = BG_STATS_MIN; i < BG_STATS_MAX; i++)
+ g_string_append_printf (string, "%s: %u\n", strings[i], stats [i]);
+ g_string_append (string, "=== stats end ===\n");
+
+ gchar *fname;
+#ifndef G_OS_WIN32
+ fname = g_strdup_printf ("gda_stats_%u", getpid ());
+#else
+ fname = g_strdup_printf ("gda_stats");
+#endif
+ g_file_set_contents (fname, string->str, -1, NULL);
+ g_free (fname);
+ g_string_free (string, TRUE);
+}
+
+/**
+ * bg_update_stats:
+ */
+void
+bg_update_stats (BackgroundStats type)
+{
+ g_assert ((type >= BG_STATS_MIN) && (type < BG_STATS_MAX));
+ stats [type] ++;
+ if ((type == BG_CREATED_WORKER) || (type == BG_DESTROYED_WORKER) ||
+ (type == BG_STARTED_THREADS) || (type == BG_JOINED_THREADS) ||
+ (type == BG_CREATED_ITS) || (type == BG_DESTROYED_ITS))
+ write_stats ();
+}
+#else
+void
+bg_update_stats (BackgroundStats type)
+{
+}
+#endif
diff --git a/libgda/thread-wrapper/background.h b/libgda/thread-wrapper/background.h
new file mode 100644
index 0000000..f19eb54
--- /dev/null
+++ b/libgda/thread-wrapper/background.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BACKGROUND_H__
+#define __BACKGROUND_H__
+
+#include "gda-worker.h"
+#include "itsignaler.h"
+
+G_BEGIN_DECLS
+
+/* IISignaler caching */
+void bg_set_spare_its (ITSignaler *its);
+ITSignaler *bg_get_spare_its (void);
+
+/* GdaWorker caching */
+void bg_set_spare_gda_worker (GdaWorker *worker);
+GdaWorker *bg_get_spare_gda_worker (void);
+
+/* threads joining */
+void bg_join_thread ();
+
+
+/* stats */
+typedef enum {
+ BG_STATS_MIN,
+
+ BG_CREATED_WORKER = BG_STATS_MIN,
+ BG_DESTROYED_WORKER,
+ BG_CACHED_WORKER_REQUESTS,
+ BG_REUSED_WORKER_FROM_CACHE,
+
+ BG_STARTED_THREADS,
+ BG_JOINED_THREADS,
+
+ BG_CREATED_ITS,
+ BG_DESTROYED_ITS,
+ BG_CACHED_ITS_REQUESTS,
+ BG_REUSED_ITS_FROM_CACHE,
+
+ BG_STATS_MAX
+} BackgroundStats;
+
+void bg_update_stats (BackgroundStats type);
+
+G_END_DECLS
+
+#endif
diff --git a/libgda/thread-wrapper/gda-worker.c b/libgda/thread-wrapper/gda-worker.c
index 85c3ec7..0e0a5d8 100644
--- a/libgda/thread-wrapper/gda-worker.c
+++ b/libgda/thread-wrapper/gda-worker.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2013 - 2014 Vivien Malerba <malerba gnome-db org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -19,11 +19,12 @@
#include "gda-worker.h"
#include "itsignaler.h"
+#include "background.h"
#include <gda-debug-macros.h>
#include <glib/gi18n-lib.h>
#define DEBUG_NOTIFICATION
-#undef DEBUG_NOTIFICATION
+//#undef DEBUG_NOTIFICATION
typedef struct {
ITSignaler *its; /* ref held */
@@ -74,12 +75,11 @@ declared_callback_free (DeclaredCallback *dc)
struct _GdaWorker {
GRecMutex rmutex; /* protects all the attributes in GdaWorker and any WorkerJob */
- guint refcount; /* +1 for any user
- * Object is destroyed when refcount reaches 0 */
+ guint ref_count; /* +1 for any user
+ * Object is destroyed when ref_count reaches 0 */
GThread *worker_thread; /* used to join thread when cleaning up, created when 1st job is submitted */
gint8 worker_must_quit; /* set to 1 if worker is requested to exit */
- gint8 worker_terminated; /* set to 1 by worker thread just before terminating */
ITSignaler *submit_its; /* used to submit jobs to worker thread, jobs IDs are pushed */
@@ -90,6 +90,8 @@ struct _GdaWorker {
GHashTable *jobs_hash; /* locked by @mutex when reading or writing, by any thread
* key = a #WorkerJob's job ID, value = the #WorkerJob */
+
+ GdaWorker **location;
};
@@ -184,7 +186,7 @@ worker_thread_main (GdaWorker *worker)
{
#define TIMER 150
#ifdef DEBUG_NOTIFICATION
- g_print ("[W] Worker %p thread for %p started!\n", worker, g_thread_self());
+ g_print ("[W] GdaWorker %p, worker thread %p started!\n", worker, g_thread_self());
#endif
itsignaler_ref (worker->submit_its);
while (1) {
@@ -205,7 +207,7 @@ worker_thread_main (GdaWorker *worker)
itsignaler_push_notification (job->reply_its, job, NULL);
}
- if (job->status & JOB_CANCELLED)
+ if ((job->status & JOB_CANCELLED) && worker->jobs_hash)
g_hash_table_remove (worker->jobs_hash, &job->id);
g_rec_mutex_unlock (&worker->rmutex);
}
@@ -214,8 +216,12 @@ worker_thread_main (GdaWorker *worker)
#ifdef DEBUG_NOTIFICATION
g_print ("[W] GdaWorker %p, worker thread %p finished!\n", worker, g_thread_self());
#endif
- worker->worker_terminated = 1;
itsignaler_unref (worker->submit_its);
+ g_rec_mutex_clear (& worker->rmutex);
+ g_slice_free (GdaWorker, worker);
+ bg_update_stats (BG_DESTROYED_WORKER);
+
+ bg_join_thread ();
return NULL;
}
}
@@ -235,6 +241,10 @@ GdaWorker *
gda_worker_new (void)
{
GdaWorker *worker;
+ worker = bg_get_spare_gda_worker ();
+ if (worker)
+ return worker;
+
worker = g_slice_new0 (GdaWorker);
worker->submit_its = itsignaler_new ();
if (!worker->submit_its) {
@@ -242,19 +252,19 @@ gda_worker_new (void)
return NULL;
}
- worker->refcount = 1;
+ worker->ref_count = 1;
worker->callbacks_hash = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) declared_callback_free);
worker->jobs_hash = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, (GDestroyNotify)
worker_job_free);
worker->worker_must_quit = 0;
- worker->worker_terminated = 0;
+ worker->location = NULL;
g_rec_mutex_init (& worker->rmutex);
gchar *str;
- str = g_strdup_printf ("Wkr%p", worker);
+ str = g_strdup_printf ("ForWkr%p", worker);
worker->worker_thread = g_thread_try_new (str, (GThreadFunc) worker_thread_main, worker, NULL);
g_free (str);
if (!worker->worker_thread) {
@@ -265,14 +275,58 @@ gda_worker_new (void)
g_slice_free (GdaWorker, worker);
return NULL;
}
+ else
+ bg_update_stats (BG_STARTED_THREADS);
#ifdef DEBUG_NOTIFICATION
g_print ("[W] created GdaWorker %p\n", worker);
#endif
+ bg_update_stats (BG_CREATED_WORKER);
return worker;
}
+static GMutex unique_worker_mutex;
+
+/**
+ * gda_worker_new_unique: (skip)
+ * @location: a place to store and test for existence, not %NULL
+ * @allow_destroy: defines if the created @GdaWorker (see case 1 below) will allow its reference to drop to
0 and be destroyed
+ *
+ * This function creates a new #GdaWorker, or reuses the one at @location. Specifically:
+ * <orderedlist>
+ * <listitem><para>if * location is %NULL, then a new #GdaWorker is created. In this case if
@allow_destroy is %FALSE, then the returned
+ * #GdaWorker's reference count is 2, thus preventing it form ever being destroyed (unless
gda_worker_unref() is called somewhere else)</para></listitem>
+ * <listitem><para>if * location is not %NULL, the the #GdaWorker it points to is returned, its reference
count increased by 1</para></listitem>
+ * </orderedlist>
+ *
+ * When the returned #GdaWorker's reference count reaches 0, then it is destroyed, and * location is set to
%NULL.
+ *
+ * In any case, the returned value is the same as * location
+ *
+ * Returns: (transfer full): a #GdaWorker
+ */
+GdaWorker *
+gda_worker_new_unique (GdaWorker **location, gboolean allow_destroy)
+{
+ g_return_val_if_fail (location, NULL);
+
+ g_mutex_lock (&unique_worker_mutex);
+ if (*location)
+ gda_worker_ref (*location);
+ else {
+ GdaWorker *worker;
+ worker = gda_worker_new ();
+ if (! allow_destroy)
+ gda_worker_ref (worker);
+ worker->location = location;
+ *location = worker;
+ }
+ g_mutex_unlock (&unique_worker_mutex);
+
+ return *location;
+}
+
/**
* gda_worker_ref: (skip)
* @worker: a #GdaWorker
@@ -288,80 +342,99 @@ gda_worker_ref (GdaWorker *worker)
{
g_return_val_if_fail (worker, NULL);
g_rec_mutex_lock (& worker->rmutex);
- worker->refcount ++;
+ worker->ref_count ++;
#ifdef DEBUG_NOTIFICATION
- g_print ("[W] GdaWorker %p reference increased to %u\n", worker, worker->refcount);
+ g_print ("[W] GdaWorker %p reference increased to %u\n", worker, worker->ref_count);
#endif
g_rec_mutex_unlock (& worker->rmutex);
return worker;
}
-/**
- * gda_worker_unref: (skip)
- * @worker: (allow-none): a #GdaWorker, or %NULL
- *
- * Decreases @worker's reference count. When reference count reaches %0, then the
- * object is destroyed, note that in this case this function only returns when the
- * worker thread actually has terminated, which can take some time if it's busy.
- *
- * If @worker is %NULL, then nothing happens.
- *
- * Since: 6.0
- */
void
-gda_worker_unref (GdaWorker *worker)
+_gda_worker_unref (GdaWorker *worker, gboolean give_to_bg)
{
- if (worker) {
- g_rec_mutex_lock (& worker->rmutex);
- worker->refcount --;
- g_rec_mutex_unlock (& worker->rmutex);
+ g_assert (worker);
+
+ gboolean unique_locked = FALSE;
+ if (worker->location) {
+ g_mutex_lock (&unique_worker_mutex);
+ unique_locked = TRUE;
+ }
#ifdef DEBUG_NOTIFICATION
- g_print ("[W] GdaWorker %p reference decreased to %u\n", worker, worker->refcount);
+ g_print ("[W] GdaWorker %p reference decreased to ", worker);
#endif
- if (worker->refcount == 0) {
- /* request the worker thread to exit */
- worker->worker_must_quit = 1;
- /* we need to call g_thread_join() to avoid zombie threads */
- g_thread_join (worker->worker_thread);
+ g_rec_mutex_lock (& worker->rmutex);
+ worker->ref_count --;
- /* free all the resources used by @worker */
- g_rec_mutex_lock (& worker->rmutex);
- g_hash_table_destroy (worker->callbacks_hash);
- worker->callbacks_hash = NULL;
- g_hash_table_destroy (worker->jobs_hash);
- worker->jobs_hash = NULL;
- g_rec_mutex_unlock (& worker->rmutex);
+#ifdef DEBUG_NOTIFICATION
+ g_print ("%u\n", worker->ref_count);
+#endif
- g_rec_mutex_clear (& worker->rmutex);
+ if (worker->ref_count == 0) {
+ /* destroy all the interal resources which will not be reused even if the GdaWorker is reused
*/
+ g_hash_table_destroy (worker->callbacks_hash);
+ worker->callbacks_hash = NULL;
+ g_hash_table_destroy (worker->jobs_hash);
+ worker->jobs_hash = NULL;
+ if (worker->location)
+ *(worker->location) = NULL;
+
+ if (give_to_bg) {
+ /* re-create the resources so the GdaWorker is ready to be used again */
+ worker->ref_count = 1;
+ worker->callbacks_hash = g_hash_table_new_full (NULL, NULL, NULL,
+ (GDestroyNotify)
declared_callback_free);
+ worker->jobs_hash = g_hash_table_new_full (g_int_hash, g_int_equal, NULL,
(GDestroyNotify) worker_job_free);
+ worker->worker_must_quit = 0;
+ worker->location = NULL;
+
+ bg_set_spare_gda_worker (worker);
+ }
+ else {
+ /* REM: we don't need to g_thread_join() the worker thread because the "background"
will do it */
+
+ /* free all the resources used by @worker */
itsignaler_unref (worker->submit_its);
- g_slice_free (GdaWorker, worker);
+ worker->worker_must_quit = 1; /* request the worker thread to exit */
}
+ g_rec_mutex_unlock (& worker->rmutex);
}
+ else
+ g_rec_mutex_unlock (& worker->rmutex);
+
+ if (unique_locked)
+ g_mutex_unlock (&unique_worker_mutex);
+}
+
+void
+_gda_worker_bg_unref (GdaWorker *worker)
+{
+ g_assert (worker);
+ g_assert (worker->ref_count == 1);
+
+ _gda_worker_unref (worker, FALSE);
}
/**
- * gda_worker_kill: (skip)
+ * gda_worker_unref: (skip)
* @worker: (allow-none): a #GdaWorker, or %NULL
*
- * Requests that @worker's worker thread be terminated. This is usefull before calling
- * gda_worker_unref() to make sure it won't lock.
+ * Decreases @worker's reference count. When reference count reaches %0, then the
+ * object is destroyed, note that in this case this function only returns when the
+ * worker thread actually has terminated, which can take some time if it's busy.
*
- * Returns: %TRUE if the worker thread has exited
+ * If @worker is %NULL, then nothing happens.
*
* Since: 6.0
*/
-gboolean
-gda_worker_kill (GdaWorker *worker)
+void
+gda_worker_unref (GdaWorker *worker)
{
- if (!worker)
- return TRUE;
- worker->worker_must_quit = 1;
- g_thread_yield ();
-
- return worker->worker_terminated == 1 ? TRUE : FALSE;
+ if (worker)
+ _gda_worker_unref (worker, TRUE);
}
/*
@@ -535,6 +608,7 @@ gda_worker_fetch_job_result (GdaWorker *worker, guint job_id, gpointer *out_resu
g_rec_mutex_unlock (&worker->rmutex);
gda_worker_unref (worker);
+
return retval;
}
diff --git a/libgda/thread-wrapper/gda-worker.h b/libgda/thread-wrapper/gda-worker.h
index 40d304e..86084eb 100644
--- a/libgda/thread-wrapper/gda-worker.h
+++ b/libgda/thread-wrapper/gda-worker.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2013 - 2014 Vivien Malerba <malerba gnome-db org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -20,7 +20,7 @@
#ifndef __GDA_WORKER_H__
#define __GDA_WORKER_H__
-#include <glib-object.h>
+#include <glib.h>
G_BEGIN_DECLS
@@ -70,9 +70,9 @@ typedef enum {
} GdaWorkerError;
GdaWorker *gda_worker_new (void);
+GdaWorker *gda_worker_new_unique (GdaWorker **location, gboolean allow_renew);
GdaWorker *gda_worker_ref (GdaWorker *worker);
void gda_worker_unref (GdaWorker *worker);
-gboolean gda_worker_kill (GdaWorker *worker);
/**
* GdaWorkerFunc:
@@ -108,6 +108,11 @@ gboolean gda_worker_set_callback (GdaWorker *worker, GMainContext *context, Gd
gboolean gda_worker_thread_is_worker (GdaWorker *worker);
GThread *gda_worker_get_worker_thread (GdaWorker *worker);
+/*
+ * Private
+ */
+void _gda_worker_bg_unref (GdaWorker *worker);
+
G_END_DECLS
#endif
diff --git a/libgda/thread-wrapper/itsignaler.c b/libgda/thread-wrapper/itsignaler.c
index 6db4167..241b1c0 100644
--- a/libgda/thread-wrapper/itsignaler.c
+++ b/libgda/thread-wrapper/itsignaler.c
@@ -34,6 +34,9 @@
#endif
#include "itsignaler.h"
+#ifndef NOBG
+ #include "background.h"
+#endif
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
@@ -42,11 +45,7 @@
#include <glib-object.h>
/* optimizations */
-static guint spare_size = 0; /* optimized spare size */
static guint created_objects = 0; /* counter of all the ITSignaler objects ever created */
-static GPtrArray *spare_array = NULL;
-static GMutex spare_mutex; /* to protect @spare_array */
-
#ifdef G_OS_WIN32
#define INVALID_SOCK INVALID_SOCKET
@@ -84,7 +83,6 @@ typedef struct {
*/
struct _ITSignaler {
guint8 broken; /* TRUE if the object has suffered an unrecoverable error */
- guint8 spared; /* TRUE if the object is in the spare array and should not be used */
#ifdef HAVE_FORK
/* detect forked process */
@@ -128,7 +126,6 @@ ITSignaler *
itsignaler_ref (ITSignaler *its)
{
if (its) {
- g_assert (!its->spared);
itsignaler_lock (its);
its->ref_count++;
#ifdef DEBUG_NOTIFICATION
@@ -177,6 +174,7 @@ cleanup_signaling (ITSignaler *its)
#endif
}
+#ifndef NOBG
static void
itsignaler_reset (ITSignaler *its)
{
@@ -211,6 +209,7 @@ itsignaler_reset (ITSignaler *its)
nd->destroy_func (nd->data);
}
}
+#endif
static void
itsignaler_free (ITSignaler *its)
@@ -227,23 +226,44 @@ itsignaler_free (ITSignaler *its)
#endif
g_mutex_unlock (m);
g_mutex_clear (m);
+#ifndef NOBG
+ bg_update_stats (BG_DESTROYED_ITS);
+#endif
g_free (its);
}
-static void
-trim_spares (void)
+void
+_itsignaler_unref (ITSignaler *its, gboolean give_to_bg)
{
- g_mutex_lock (&spare_mutex);
- for (;spare_array->len > spare_size + 1;) {
- ITSignaler *its;
- its = g_ptr_array_remove_index_fast (spare_array, spare_array->len - 1);
+ g_assert (its);
+
+ itsignaler_lock (its);
+ its->ref_count--;
#ifdef DEBUG_NOTIFICATION
- g_print ("[I] Trimming operation: get rid of ITSignaler %p\n", its);
+ g_print ("[I] ITSignaler %p --: %u\n", its, its->ref_count);
#endif
+ if (its->ref_count == 0) {
+ itsignaler_unlock (its);
+
+#ifndef NOBG
+ /* destroy or store as spare */
+ if (!its->broken && give_to_bg) {
+ itsignaler_reset (its);
+ its->ref_count++;
+ bg_set_spare_its (its);
+ }
+ else {
+ itsignaler_unlock (its);
+ itsignaler_free (its);
+ }
+#else
+ itsignaler_unlock (its);
itsignaler_free (its);
+#endif
}
- g_mutex_unlock (&spare_mutex);
+ else
+ itsignaler_unlock (its);
}
/**
@@ -258,35 +278,17 @@ trim_spares (void)
void
itsignaler_unref (ITSignaler *its)
{
- if (its) {
- g_assert (!its->spared);
- itsignaler_lock (its);
- its->ref_count--;
-#ifdef DEBUG_NOTIFICATION
- g_print ("[I] ITSignaler %p --: %u\n", its, its->ref_count);
-#endif
- if (its->ref_count == 0) {
- spare_size--;
- itsignaler_unlock (its);
+ if (its)
+ _itsignaler_unref (its, TRUE);
+}
- /* destroy or store as spare */
- g_mutex_lock (&spare_mutex);
- if (!its->broken && (spare_array->len < spare_size)) {
- itsignaler_reset (its);
- g_ptr_array_add (spare_array, its);
- its->spared = TRUE;
- }
- else {
- itsignaler_unlock (its);
- itsignaler_free (its);
- }
- g_mutex_unlock (&spare_mutex);
-
- trim_spares ();
- }
- else
- itsignaler_unlock (its);
- }
+void
+_itsignaler_bg_unref (ITSignaler *its)
+{
+ g_assert (its);
+ g_assert (its->ref_count == 1);
+
+ _itsignaler_unref (its, FALSE);
}
static void
@@ -308,39 +310,16 @@ ITSignaler *
itsignaler_new (void)
{
ITSignaler *its;
- gboolean err = FALSE;
-
- if (G_UNLIKELY (! spare_array)) {
- static GMutex registering;
- g_mutex_lock (®istering);
- g_mutex_init (&spare_mutex);
- if (!spare_array)
- spare_array = g_ptr_array_new ();
- g_mutex_unlock (®istering);
- }
-
- trim_spares ();
-
- g_mutex_lock (&spare_mutex);
- if (spare_array->len > 0) {
- /* pick up the 1st spare */
- its = g_ptr_array_remove_index_fast (spare_array, spare_array->len - 1);
- its->ref_count = 1;
- its->spared = FALSE;
- spare_size++;
-#ifdef DEBUG_NOTIFICATION
- g_print ("[I]: Reused ITS %p, total created ITSignaler objects: %u, spare size: %u, nb in
spare: %d\n", its, created_objects, spare_size, spare_array->len);
-#endif
- g_mutex_unlock (&spare_mutex);
-
+#ifndef NOBG
+ its = bg_get_spare_its ();
+ if (its)
return its;
- }
- g_mutex_unlock (&spare_mutex);
+#endif
+ gboolean err = FALSE;
its = g_new0 (ITSignaler, 1);
its->ref_count = 1;
its->broken = FALSE;
- its->spared = FALSE;
#ifdef G_OS_WIN32
SECURITY_DESCRIPTOR sd;
@@ -557,9 +536,8 @@ itsignaler_new (void)
#endif
created_objects++;
- spare_size++;
-#ifdef DEBUG_NOTIFICATION
- g_print ("[I]: total created ITSignaler objects: %u, spare size: %u\n", created_objects, spare_size);
+#ifndef NOBG
+ bg_update_stats (BG_CREATED_ITS);
#endif
return its;
}
@@ -577,7 +555,6 @@ SOCKET
itsignaler_windows_get_poll_fd (ITSignaler *its)
{
g_return_val_if_fail (its, -1);
- g_assert (!its->spared);
return its->socks[0];
}
@@ -595,7 +572,6 @@ int
itsignaler_unix_get_poll_fd (ITSignaler *its)
{
g_return_val_if_fail (its, -1);
- g_assert (!its->spared);
#ifdef HAVE_EVENTFD
return its->event_fd;
@@ -626,7 +602,6 @@ itsignaler_push_notification (ITSignaler *its, gpointer data, GDestroyNotify des
{
g_return_val_if_fail (its, FALSE);
g_return_val_if_fail (data, FALSE);
- g_assert (!its->spared);
if (its->broken)
return FALSE;
@@ -758,7 +733,6 @@ gpointer
itsignaler_pop_notification (ITSignaler *its, gint timeout_ms)
{
g_return_val_if_fail (its, NULL);
- g_assert (!its->spared);
if (timeout_ms == 0)
return itsignaler_pop_notification_non_block (its);
@@ -843,7 +817,6 @@ GSource *
itsignaler_create_source (ITSignaler *its)
{
g_return_val_if_fail (its, NULL);
- g_assert (!its->spared);
GSource *source;
ITSSource *isource;
@@ -876,7 +849,6 @@ static gboolean
its_source_prepare (GSource *source, gint *timeout_)
{
ITSSource *isource = (ITSSource*) source;
- g_assert (! isource->its->spared);
*timeout_ = -1;
return FALSE;
}
@@ -885,7 +857,6 @@ static gboolean
its_source_check (GSource *source)
{
ITSSource *isource = (ITSSource*) source;
- g_assert (! isource->its->spared);
#ifndef G_OS_WIN32
if (isource->pollfd.revents & G_IO_IN)
@@ -905,7 +876,6 @@ static gboolean
its_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
{
ITSSource *isource = (ITSSource*) source;
- g_assert (! isource->its->spared);
ITSignalerFunc func;
func = (ITSignalerFunc) callback;
@@ -920,7 +890,7 @@ static
void its_source_finalize (GSource *source)
{
ITSSource *isource = (ITSSource*) source;
- g_assert (! isource->its->spared);
+
itsignaler_unref (isource->its);
}
@@ -946,7 +916,6 @@ itsignaler_add (ITSignaler *its, GMainContext *context, ITSignalerFunc func, gpo
guint id;
g_return_val_if_fail (its, 0);
- g_assert (! its->spared);
source = itsignaler_create_source (its);
if (!source)
@@ -973,7 +942,6 @@ gboolean
itsignaler_remove (ITSignaler *its, GMainContext *context, guint id)
{
GSource *source;
- g_assert (! its->spared);
source = g_main_context_find_source_by_id (context, id);
if (source) {
g_source_destroy (source);
diff --git a/libgda/thread-wrapper/itsignaler.h b/libgda/thread-wrapper/itsignaler.h
index 490e189..ceba244 100644
--- a/libgda/thread-wrapper/itsignaler.h
+++ b/libgda/thread-wrapper/itsignaler.h
@@ -53,6 +53,11 @@ typedef gboolean (*ITSignalerFunc) (ITSignaler *its, gpointer user_data);
guint itsignaler_add (ITSignaler *its, GMainContext *context, ITSignalerFunc func, gpointer data,
GDestroyNotify notify);
gboolean itsignaler_remove (ITSignaler *its, GMainContext *context, guint id);
+/*
+ * Private
+ */
+void _itsignaler_bg_unref (ITSignaler *its);
+
G_END_DECLS
#endif
diff --git a/libgda/thread-wrapper/test-worker.c b/libgda/thread-wrapper/test-worker.c
index 544110c..5dabbb9 100644
--- a/libgda/thread-wrapper/test-worker.c
+++ b/libgda/thread-wrapper/test-worker.c
@@ -22,7 +22,6 @@
#include <stdlib.h>
int test1 (void);
-int test2 (void);
int test3 (void);
int test4 (void);
int test5 (void);
@@ -41,7 +40,6 @@ main (int argc, char** argv)
gint nfailed = 0;
nfailed += test1 ();
- nfailed += test2 ();
nfailed += test3 ();
nfailed += test4 ();
nfailed += test5 ();
@@ -72,27 +70,6 @@ test1 (void)
}
/*
- * Test 2: killing worker
- */
-int
-test2 (void)
-{
- g_print ("Test2 started\n");
- GdaWorker *worker;
-
- worker = gda_worker_new ();
- g_usleep (100000);
- guint i = 0;
- while (! gda_worker_kill (worker)) {
- g_print ("Wait %d\n", i++);
- g_usleep (100000);
- }
- gda_worker_unref (worker);
- return 0;
-}
-
-
-/*
* Test 3: fetching results
*/
typedef struct {
diff --git a/providers/firebird/gda-firebird-provider.c b/providers/firebird/gda-firebird-provider.c
index 16e0d9e..05430f9 100644
--- a/providers/firebird/gda-firebird-provider.c
+++ b/providers/firebird/gda-firebird-provider.c
@@ -3,7 +3,7 @@
* Copyright (C) 2003 Gonzalo Paniagua Javier <gonzalo gnome-db org>
* Copyright (C) 2004 Jeronimo Albi <jeronimoalbi yahoo com ar>
* Copyright (C) 2004 Julio M. Merino Vidal <jmmv menta net>
- * Copyright (C) 2004 - 2012 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2004 - 2014 Vivien Malerba <malerba gnome-db org>
* Copyright (C) 2008 Murray Cumming <murrayc murrayc com>
* Copyright (C) 2009 Bas Driessen <bas driessen xobas com>
*
@@ -99,7 +99,7 @@ static const gchar *gda_firebird_provider_get_version (GdaServerProvider
static gboolean gda_firebird_provider_supports_feature (GdaServerProvider *provider,
GdaConnection *cnc,
GdaConnectionFeature feature);
-static GdaWorker *gda_firebird_provider_create_worker (GdaServerProvider *provider);
+static GdaWorker *gda_firebird_provider_create_worker (GdaServerProvider *provider, gboolean
for_cnc);
static const gchar *gda_firebird_provider_get_name (GdaServerProvider *provider);
static GdaDataHandler *gda_firebird_provider_get_data_handler (GdaServerProvider *provider,
GdaConnection *cnc,
@@ -360,18 +360,13 @@ gda_firebird_provider_get_type (void)
}
static GdaWorker *
-gda_firebird_provider_create_worker (GdaServerProvider *provider)
+gda_firebird_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc)
{
- static GdaWorker *unique_worker = NULL;
- if (unique_worker)
- return gda_worker_ref (unique_worker);
+ /* For a start see
http://www.firebirdsql.org/file/documentation/drivers_documentation/python/3.3.0/thread-safety-overview.html
+ * For now we consider the client to be non thread safe */
- if (0) /* We need to determine if the Firebird API is thread safe */
- return gda_worker_new ();
- else {
- unique_worker = gda_worker_new ();
- return gda_worker_ref (unique_worker);
- }
+ static GdaWorker *unique_worker = NULL;
+ return gda_worker_new_unique (&unique_worker, TRUE);
}
/*
diff --git a/providers/jdbc/gda-jdbc-provider.c b/providers/jdbc/gda-jdbc-provider.c
index 4d4c130..cf4f71e 100644
--- a/providers/jdbc/gda-jdbc-provider.c
+++ b/providers/jdbc/gda-jdbc-provider.c
@@ -93,6 +93,7 @@ static const gchar *gda_jdbc_provider_get_version (GdaServerProvider *pro
static gboolean gda_jdbc_provider_supports_feature (GdaServerProvider *provider, GdaConnection
*cnc,
GdaConnectionFeature feature);
+static GdaWorker *gda_jdbc_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc);
static const gchar *gda_jdbc_provider_get_name (GdaServerProvider *provider);
static GdaDataHandler *gda_jdbc_provider_get_data_handler (GdaServerProvider *provider, GdaConnection
*cnc,
@@ -162,7 +163,7 @@ GdaServerProviderBase jdbc_base_functions = {
gda_jdbc_provider_get_version,
gda_jdbc_provider_get_server_version,
gda_jdbc_provider_supports_feature,
- NULL,
+ gda_jdbc_provider_create_worker,
NULL,
NULL,
gda_jdbc_provider_get_data_handler,
@@ -324,6 +325,14 @@ gda_jdbc_provider_get_type (void)
return type;
}
+static GdaWorker *
+gda_jdbc_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc)
+{
+ /* consider API not thread safe by default */
+
+ static GdaWorker *unique_worker = NULL;
+ return gda_worker_new_unique (&unique_worker, TRUE);
+}
/*
* Get provider name request
diff --git a/providers/mysql/gda-mysql-provider.c b/providers/mysql/gda-mysql-provider.c
index 8186ce4..b2d6204 100644
--- a/providers/mysql/gda-mysql-provider.c
+++ b/providers/mysql/gda-mysql-provider.c
@@ -154,7 +154,7 @@ static gboolean gda_mysql_provider_supports_feature (GdaServerProvide
GdaConnection *cnc,
GdaConnectionFeature feature);
-static GdaWorker *gda_mysql_provider_create_worker (GdaServerProvider *provider);
+static GdaWorker *gda_mysql_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc);
static const gchar *gda_mysql_provider_get_name (GdaServerProvider *provider);
@@ -415,21 +415,19 @@ gda_mysql_provider_get_property (GObject *object,
}
static GdaWorker *
-gda_mysql_provider_create_worker (GdaServerProvider *provider)
+gda_mysql_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc)
{
- /* If PostgreSQL was not compiled with the --enable-thread-safe-client, then it's
- * considered thread safe, and we limit the usage of the provider to one single thread */
+ /* see http://dev.mysql.com/doc/refman/5.1/en/c-api-threaded-clients.html */
static GdaWorker *unique_worker = NULL;
- if (unique_worker)
- return gda_worker_ref (unique_worker);
-
- if (mysql_thread_safe ())
- return gda_worker_new ();
- else {
- unique_worker = gda_worker_new ();
- return gda_worker_ref (unique_worker);
+ if (mysql_thread_safe ()) {
+ if (for_cnc)
+ return gda_worker_new ();
+ else
+ return gda_worker_new_unique (&unique_worker, TRUE);
}
+ else
+ return gda_worker_new_unique (&unique_worker, TRUE);
}
/*
@@ -498,8 +496,9 @@ real_open_connection (const gchar *host,
if (compress)
flags |= CLIENT_COMPRESS;
- MYSQL *mysql = NULL;
+ MYSQL *mysql;
mysql = mysql_init (NULL);
+ g_print ("mysql_init (NULL) ==> %p\n", mysql);
if ((port > 0) || proto) {
gint p = MYSQL_PROTOCOL_DEFAULT;
@@ -1029,9 +1028,9 @@ gda_mysql_provider_perform_operation (GdaServerProvider *provider,
g_free (sql);
if (res) {
- g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
- GDA_SERVER_PROVIDER_OPERATION_ERROR,
- "%s", mysql_error (mysql));
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_OPERATION_ERROR,
+ "%s", mysql_error (mysql));
mysql_close (mysql);
return FALSE;
}
@@ -3186,6 +3185,7 @@ gda_mysql_free_cnc_data (MysqlConnectionData *cdata)
return;
if (cdata->mysql) {
+ g_print ("mysql_close (%p)\n", cdata->mysql);
mysql_close (cdata->mysql);
cdata->mysql = NULL;
}
diff --git a/providers/oracle/gda-oracle-provider.c b/providers/oracle/gda-oracle-provider.c
index 69d3b92..a4059ab 100644
--- a/providers/oracle/gda-oracle-provider.c
+++ b/providers/oracle/gda-oracle-provider.c
@@ -6,7 +6,7 @@
* Copyright (C) 2003 Steve Fosdick <fozzy src gnome org>
* Copyright (C) 2004 Julio M. Merino Vidal <jmmv menta net>
* Copyright (C) 2005 Magnus Bergman <magnus bergman observer net>
- * Copyright (C) 2005 - 2013 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2005 - 2014 Vivien Malerba <malerba gnome-db org>
* Copyright (C) 2006 - 2009 Bas Driessen <bas driessen xobas com>
* Copyright (C) 2007 Murray Cumming <murrayc murrayc com>
*
@@ -367,12 +367,7 @@ gda_oracle_provider_create_worker (GdaServerProvider *provider)
{
/* See http://docs.oracle.com/cd/B10501_01/appdev.920/a96584/oci09adv.htm */
static GdaWorker *unique_worker = NULL;
- if (unique_worker)
- return gda_worker_ref (unique_worker);
- else {
- unique_worker = gda_worker_new ();
- return gda_worker_ref (unique_worker);
- }
+ return gda_worker_new_unique (&unique_worker, TRUE);
}
/*
diff --git a/providers/postgres/gda-postgres-provider.c b/providers/postgres/gda-postgres-provider.c
index cfa1653..968e395 100644
--- a/providers/postgres/gda-postgres-provider.c
+++ b/providers/postgres/gda-postgres-provider.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2001 - 2003 Gonzalo Paniagua Javier <gonzalo gnome-db org>
* Copyright (C) 2001 - 2004 Rodrigo Moya <rodrigo gnome-db org>
- * Copyright (C) 2002 - 2013 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2002 - 2014 Vivien Malerba <malerba gnome-db org>
* Copyright (C) 2002 Zbigniew Chyla <cyba gnome pl>
* Copyright (C) 2003 Akira TAGOH <tagoh gnome-db org>
* Copyright (C) 2004 - 2005 Alan Knowles <alank src gnome org>
@@ -106,7 +106,7 @@ static const gchar *gda_postgres_provider_get_version (GdaServerProvider
static gboolean gda_postgres_provider_supports_feature (GdaServerProvider *provider,
GdaConnection *cnc,
GdaConnectionFeature feature);
-static GdaWorker *gda_postgres_provider_create_worker (GdaServerProvider *provider);
+static GdaWorker *gda_postgres_provider_create_worker (GdaServerProvider *provider, gboolean
for_cnc);
static const gchar *gda_postgres_provider_get_name (GdaServerProvider *provider);
static GdaDataHandler *gda_postgres_provider_get_data_handler (GdaServerProvider *provider,
GdaConnection *cnc,
@@ -313,21 +313,20 @@ gda_postgres_provider_get_type (void)
}
static GdaWorker *
-gda_postgres_provider_create_worker (GdaServerProvider *provider)
+gda_postgres_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc)
{
/* If PostgreSQL was not compiled with the --enable-thread-safety flag, then libPQ is not
* considered thread safe, and we limit the usage of the provider to one single thread */
static GdaWorker *unique_worker = NULL;
- if (unique_worker)
- return gda_worker_ref (unique_worker);
-
- if (PQisthreadsafe ())
- return gda_worker_new ();
- else {
- unique_worker = gda_worker_new ();
- return gda_worker_ref (unique_worker);
+ if (PQisthreadsafe ()) {
+ if (for_cnc)
+ return gda_worker_new ();
+ else
+ return gda_worker_new_unique (&unique_worker, TRUE);
}
+ else
+ return gda_worker_new_unique (&unique_worker, TRUE);
}
/*
diff --git a/providers/skel-implementation/capi/gda-capi-provider.c
b/providers/skel-implementation/capi/gda-capi-provider.c
index 0217665..7065680 100644
--- a/providers/skel-implementation/capi/gda-capi-provider.c
+++ b/providers/skel-implementation/capi/gda-capi-provider.c
@@ -94,7 +94,7 @@ static gboolean gda_capi_provider_delete_savepoint (GdaServerProvider
static const gchar *gda_capi_provider_get_version (GdaServerProvider *provider);
static gboolean gda_capi_provider_supports_feature (GdaServerProvider *provider, GdaConnection
*cnc,
GdaConnectionFeature feature);
-static GdaWorker *gda_capi_provider_create_worker (GdaServerProvider *provider);
+static GdaWorker *gda_capi_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc);
static GdaConnection *gda_capi_provider_create_connection (GdaServerProvider *provider);
static const gchar *gda_capi_provider_get_name (GdaServerProvider *provider);
@@ -830,10 +830,24 @@ gda_capi_provider_supports_feature (GdaServerProvider *provider, GdaConnection *
* own connection, then this function should return a single GdaWorker for any request (using
gda_worker_ref()).
*/
static GdaWorker *
-gda_capi_provider_create_worker (GdaServerProvider *provider)
+gda_capi_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc)
{
- TO_IMPLEMENT;
- return gda_worker_new ();
+ TO_IMPLEMENT; /* We need to determine if the API is thread safe, 0 for now */
+
+ static GdaWorker *unique_worker = NULL;
+ if (0) {
+ /* API is thread safe */
+ if (for_cnc)
+ return gda_worker_new ();
+ else
+ return gda_worker_new_unique (&unique_worker, TRUE);
+ }
+ else {
+ gboolean onlyone = FALSE; /* if %TRUE then only 1 thread can ever access the API, if %FALSE,
only 1 thread can
+ * access the API _at any given time_ (i.e. several threads can
access the API but only one after
+ * another and never simultaneously) */
+ return gda_worker_new_unique (&unique_worker, !onlyone);
+ }
}
/*
diff --git a/providers/web/gda-web-provider.c b/providers/web/gda-web-provider.c
index 285f93d..71fc321 100644
--- a/providers/web/gda-web-provider.c
+++ b/providers/web/gda-web-provider.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2009 - 2014 Vivien Malerba <malerba gnome-db org>
* Copyright (C) 2010 David King <davidk openismus com>
* Copyright (C) 2011 Murray Cumming <murrayc murrayc com>
*
@@ -89,7 +89,7 @@ static const gchar *gda_web_provider_get_version (GdaServerProvider *prov
static gboolean gda_web_provider_supports_feature (GdaServerProvider *provider, GdaConnection
*cnc,
GdaConnectionFeature feature);
-static GdaWorker *gda_web_provider_create_worker (GdaServerProvider *provider);
+static GdaWorker *gda_web_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc);
static const gchar *gda_web_provider_get_name (GdaServerProvider *provider);
@@ -260,16 +260,11 @@ gda_web_provider_get_type (void)
}
static GdaWorker *
-gda_web_provider_create_worker (GdaServerProvider *provider)
+gda_web_provider_create_worker (GdaServerProvider *provider, gboolean for_cnc)
{
/* Let's assume for now that this provider is not thread safe... */
static GdaWorker *unique_worker = NULL;
- if (unique_worker)
- return gda_worker_ref (unique_worker);
- else {
- unique_worker = gda_worker_new ();
- return gda_worker_ref (unique_worker);
- }
+ return gda_worker_new_unique (&unique_worker, TRUE);
}
/*
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]