[libgda] Corrected thread non ending problem and Added background thread to help manage GdaWorker objects



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 (&registering);
-               g_mutex_init (&spare_mutex);
-               if (!spare_array)
-                       spare_array = g_ptr_array_new ();
-               g_mutex_unlock (&registering);
-       }
-
-       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]