[libgda] Added the GDA_CONNECTION_OPTIONS_THREAD_ISOLATED connection option



commit 7498a4239f27522af89fca6dcacfa3e44e0da438
Author: Vivien Malerba <malerba gnome-db org>
Date:   Tue Jun 1 18:39:13 2010 +0200

    Added the GDA_CONNECTION_OPTIONS_THREAD_ISOLATED connection option

 doc/C/tmpl/gda-connection.sgml                 |    2 +
 libgda/gda-connection.c                        |   25 ++++++---
 libgda/gda-connection.h                        |   63 ++++++++++++++++++++++--
 libgda/sqlite/gda-sqlite-provider.c            |   11 +++--
 libgda/sqlite/virtual/gda-virtual-connection.c |    7 ++-
 libgda/thread-wrapper/gda-thread-provider.c    |   21 ++++++--
 providers/mysql/gda-mysql-provider.c           |    2 +
 providers/postgres/gda-postgres-provider.c     |    2 +
 tests/multi-threading/check_threaded_cnc.c     |    4 +-
 9 files changed, 111 insertions(+), 26 deletions(-)
---
diff --git a/doc/C/tmpl/gda-connection.sgml b/doc/C/tmpl/gda-connection.sgml
index 6acd755..8e411cd 100644
--- a/doc/C/tmpl/gda-connection.sgml
+++ b/doc/C/tmpl/gda-connection.sgml
@@ -172,6 +172,7 @@ A connection to a database
 @GDA_CONNECTION_OPTIONS_READ_ONLY: 
 @GDA_CONNECTION_OPTIONS_SQL_IDENTIFIERS_CASE_SENSITIVE: 
 @GDA_CONNECTION_OPTIONS_THREAD_SAFE: 
+ GDA_CONNECTION_OPTIONS_THREAD_ISOLATED: 
 
 <!-- ##### ENUM GdaConnectionError ##### -->
 <para>
@@ -626,6 +627,7 @@ A connection to a database
 @GDA_CONNECTION_FEATURE_USERS: 
 @GDA_CONNECTION_FEATURE_VIEWS: 
 @GDA_CONNECTION_FEATURE_XA_TRANSACTIONS: 
+ GDA_CONNECTION_FEATURE_MULTI_THREADING: 
 @GDA_CONNECTION_FEATURE_LAST: 
 
 <!-- ##### FUNCTION gda_connection_supports_feature ##### -->
diff --git a/libgda/gda-connection.c b/libgda/gda-connection.c
index 3c29ae1..b6c7106 100644
--- a/libgda/gda-connection.c
+++ b/libgda/gda-connection.c
@@ -877,7 +877,8 @@ gda_connection_open_from_dsn (const gchar *dsn, const gchar *auth_string,
 
 	g_return_val_if_fail (dsn && *dsn, NULL);
 
-	if ((options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) && !g_thread_supported ()) {
+	if (((options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) || (options & GDA_CONNECTION_OPTIONS_THREAD_SAFE)) &&
+	    !g_thread_supported ()) {
 		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_UNSUPPORTED_THREADS_ERROR,
 			      "%s", _("Multi threading is not supported or enabled"));
 		return NULL;
@@ -923,10 +924,14 @@ gda_connection_open_from_dsn (const gchar *dsn, const gchar *auth_string,
 
 		pinfo = gda_config_get_provider_info (dsn_info->provider);
 		if (pinfo) {
-			if (options & GDA_CONNECTION_OPTIONS_THREAD_SAFE)
+			prov = gda_config_get_provider (dsn_info->provider, error);
+			if (((options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) &&
+			     !gda_server_provider_supports_feature (prov, NULL,
+								    GDA_CONNECTION_FEATURE_MULTI_THREADING)) ||
+			    (options & GDA_CONNECTION_OPTIONS_THREAD_ISOLATED)) {
+				options |= GDA_CONNECTION_OPTIONS_THREAD_ISOLATED;
 				prov = _gda_connection_get_internal_thread_provider ();
-			else
-				prov = gda_config_get_provider (dsn_info->provider, error);
+			}
 		}
 		else
 			g_set_error (error, GDA_CONFIG_ERROR, GDA_CONFIG_PROVIDER_NOT_FOUND_ERROR,
@@ -1022,7 +1027,8 @@ gda_connection_open_from_string (const gchar *provider_name, const gchar *cnc_st
 
 	g_return_val_if_fail (cnc_string && *cnc_string, NULL);
 
-	if ((options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) && !g_thread_supported ()) {
+	if (((options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) || (options & GDA_CONNECTION_OPTIONS_THREAD_SAFE)) &&
+		!g_thread_supported ()) {
 		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_UNSUPPORTED_THREADS_ERROR,
 			      "%s", _("Multi threading is not supported or enabled"));
 		return NULL;
@@ -1068,15 +1074,18 @@ gda_connection_open_from_string (const gchar *provider_name, const gchar *cnc_st
 
 		pinfo = gda_config_get_provider_info (provider_name ? provider_name : real_provider);
 		if (pinfo) {
-			if (options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) {
+			prov = gda_config_get_provider (provider_name ? provider_name : real_provider, error);
+			if (((options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) &&
+			     !gda_server_provider_supports_feature (prov, NULL,
+								    GDA_CONNECTION_FEATURE_MULTI_THREADING)) ||
+			    (options & GDA_CONNECTION_OPTIONS_THREAD_ISOLATED)) {
 				gchar *tmp;
 				tmp = g_strdup_printf ("%s;PROVIDER_NAME=%s", real_cnc, pinfo->id);
 				g_free (real_cnc);
 				real_cnc = tmp;
+				options |= GDA_CONNECTION_OPTIONS_THREAD_ISOLATED;
 				prov = _gda_connection_get_internal_thread_provider ();
 			}
-			else
-				prov = gda_config_get_provider (provider_name ? provider_name : real_provider, error);
 		}
 		else
 			g_set_error (error, GDA_CONFIG_ERROR, GDA_CONFIG_PROVIDER_NOT_FOUND_ERROR,
diff --git a/libgda/gda-connection.h b/libgda/gda-connection.h
index 1a74338..dfe9946 100644
--- a/libgda/gda-connection.h
+++ b/libgda/gda-connection.h
@@ -1,5 +1,5 @@
 /* GDA library
- * Copyright (C) 1998 - 2009 The GNOME Foundation.
+ * Copyright (C) 1998 - 2010 The GNOME Foundation.
  *
  * AUTHORS:
  *      Michael Lausch <michael lausch at>
@@ -77,6 +77,7 @@ struct _GdaConnectionClass {
 	void   (*dsn_changed)               (GdaConnection *obj);
 	void   (*transaction_status_changed)(GdaConnection *obj);
 
+	/*< private >*/
 	/* Padding for future expansion */
 	void (*_gda_reserved1) (void);
 	void (*_gda_reserved2) (void);
@@ -89,10 +90,13 @@ struct _GdaConnectionClass {
  * @GDA_CONNECTION_OPTIONS_NONE: no specific aspect
  * @GDA_CONNECTION_OPTIONS_READ_ONLY: this flag specifies that the connection to open should be in a read-only mode
  *                                    (this policy is not correctly enforced at the moment)
- * @GDA_CONNECTION_OPTIONS_THREAD_SAFE: this flag specifies that the connection to open will be used 
- *                                     by several threads at once so it has to be thread safe
  * @GDA_CONNECTION_OPTIONS_SQL_IDENTIFIERS_CASE_SENSITIVE: this flag specifies that SQL identifiers submitted as input
  *                                    to Libgda have to keep their case sensitivity. 
+ * @GDA_CONNECTION_OPTIONS_THREAD_SAFE: this flag specifies that the connection to open will be used 
+ *                                     by several threads at once so it has to be thread safe
+ * @GDA_CONNECTION_OPTIONS_THREAD_ISOLATED: this flag specifies that the connection to open will be used 
+ *                                     by several threads at once and requests that the real connection be used
+ *                                     only in a sub thread created specifically for it
  *                                    
  *
  * Specifies some aspects of a connection when opening it.
@@ -109,14 +113,55 @@ struct _GdaConnectionClass {
  *   <listitem><para>Libgda will not apply this rule when parsing SQL code, the SQL code being parsed
  *       has to be conform to the database it will be used with</para></listitem>
  * </itemizedlist>
+ *
+ * Additional information about the GDA_CONNECTION_OPTIONS_THREAD_SAFE and GDA_CONNECTION_OPTIONS_THREAD_ISOLATED flags:
+ * The GDA_CONNECTION_OPTIONS_THREAD_SAFE flag specifies that it has to be able to use the returned connection object from
+ * several threads at once (locking is ensured by the #GdaConnection itself). Depending on the database provider's
+ * implementation and on the native libraries it uses, the "normal" connection object might not respect this requirement,
+ * and in this case a specific thread is started and used as the unique thread which will manipulate the actual connection,
+ * while a "wrapper connection" is actually returned and used by the caller (that wrapper connection passes method calls
+ * from the calling thread to the actual connection's specific thread, and gets the results back).
+ *
+ * The GDA_CONNECTION_OPTIONS_THREAD_ISOLATED forces using a specific thread and a "wrapper connection" even if the
+ * "normal" connection would itself be thread safe; this is usefull for example to be sure the asynchronous API can
+ * always be used (see gda_connection_async_statement_execute()).
+ *
+ * Having a specific thread and a "wrapper connection" definitely has an impact on the performances (because it involves
+ * messages passing between threads for every method call), so using the
+ * GDA_CONNECTION_OPTIONS_THREAD_SAFE or GDA_CONNECTION_OPTIONS_THREAD_ISOLATED flags should be carefully considered.
  */
 typedef enum {
-        GDA_CONNECTION_OPTIONS_NONE = 0,
+	GDA_CONNECTION_OPTIONS_NONE = 0,
 	GDA_CONNECTION_OPTIONS_READ_ONLY = 1 << 0,
 	GDA_CONNECTION_OPTIONS_SQL_IDENTIFIERS_CASE_SENSITIVE = 1 << 1,
-	GDA_CONNECTION_OPTIONS_THREAD_SAFE = 1 << 2
+	GDA_CONNECTION_OPTIONS_THREAD_SAFE = 1 << 2,
+	GDA_CONNECTION_OPTIONS_THREAD_ISOLATED = 1 << 3
 } GdaConnectionOptions;
 
+
+/**
+ * GdaConnectionFeature
+ * @GDA_CONNECTION_FEATURE_AGGREGATES: test for aggregates support
+ * @GDA_CONNECTION_FEATURE_BLOBS: test for BLOBS (binary large objects) support
+ * @GDA_CONNECTION_FEATURE_INDEXES: test for indexes support
+ * @GDA_CONNECTION_FEATURE_INHERITANCE: test for tables inheritance support
+ * @GDA_CONNECTION_FEATURE_NAMESPACES: test for namespaces support
+ * @GDA_CONNECTION_FEATURE_PROCEDURES: test for functions support
+ * @GDA_CONNECTION_FEATURE_SEQUENCES: test for sequences support
+ * @GDA_CONNECTION_FEATURE_SQL: test for SQL language (even specific to the database) support
+ * @GDA_CONNECTION_FEATURE_TRANSACTIONS: test for transactions support
+ * @GDA_CONNECTION_FEATURE_SAVEPOINTS: test for savepoints within transactions support
+ * @GDA_CONNECTION_FEATURE_SAVEPOINTS_REMOVE: test if savepoints can be removed
+ * @GDA_CONNECTION_FEATURE_TRIGGERS: test for triggers support
+ * @GDA_CONNECTION_FEATURE_UPDATABLE_CURSOR: test for updatable cursors support
+ * @GDA_CONNECTION_FEATURE_USERS: test for users support
+ * @GDA_CONNECTION_FEATURE_VIEWS: test for views support
+ * @GDA_CONNECTION_FEATURE_XA_TRANSACTIONS: test for distributed transactions support
+ * @GDA_CONNECTION_FEATURE_MULTI_THREADING: test for native multi-threading support
+ *
+ * Used in gda_connection_supports_feature() and gda_server_provider_supports_feature() to test if a connection
+ * or a database provider supports some specific feature.
+ */
 typedef enum {
 	GDA_CONNECTION_FEATURE_AGGREGATES,
 	GDA_CONNECTION_FEATURE_BLOBS,
@@ -134,10 +179,18 @@ typedef enum {
 	GDA_CONNECTION_FEATURE_USERS,
 	GDA_CONNECTION_FEATURE_VIEWS,
 	GDA_CONNECTION_FEATURE_XA_TRANSACTIONS,
+	
+	GDA_CONNECTION_FEATURE_MULTI_THREADING,
 
 	GDA_CONNECTION_FEATURE_LAST
 } GdaConnectionFeature;
 
+
+/**
+ * GdaConnectionSchema
+ *
+ * Deprecated: 4.2: This was a leftover from the pre 4.0 area
+ */
 typedef enum {
 	GDA_CONNECTION_SCHEMA_AGGREGATES,
 	GDA_CONNECTION_SCHEMA_DATABASES,
diff --git a/libgda/sqlite/gda-sqlite-provider.c b/libgda/sqlite/gda-sqlite-provider.c
index cd767a9..3e96e09 100644
--- a/libgda/sqlite/gda-sqlite-provider.c
+++ b/libgda/sqlite/gda-sqlite-provider.c
@@ -764,9 +764,10 @@ gda_sqlite_provider_open_connection (GdaServerProvider *provider, GdaConnection
 		}
 	}
 	
-	/* Note: we don't need to set the thread owner because as stating with SQLite version 3.6.0
-	 * connections and prepared statement can be shared by threads */
-	g_object_set (G_OBJECT (cnc), "thread-owner", NULL, NULL);
+	if (sqlite3_threadsafe ())
+		g_object_set (G_OBJECT (cnc), "thread-owner", NULL, NULL);
+	else
+		g_object_set (G_OBJECT (cnc), "thread-owner", g_thread_self (), NULL);
 
 	g_static_rec_mutex_unlock (&cnc_mutex);
 	return TRUE;
@@ -844,7 +845,7 @@ gda_sqlite_provider_supports_operation (GdaServerProvider *provider, GdaConnecti
 
         case GDA_SERVER_OPERATION_CREATE_VIEW:
         case GDA_SERVER_OPERATION_DROP_VIEW:
-                return TRUE;
+		return TRUE;
         default:
                 return FALSE;
         }
@@ -1194,6 +1195,8 @@ gda_sqlite_provider_supports (GdaServerProvider *provider,
 	case GDA_CONNECTION_FEATURE_VIEWS :
 	case GDA_CONNECTION_FEATURE_PROCEDURES :
 		return TRUE;
+	case GDA_CONNECTION_FEATURE_MULTI_THREADING:
+		return sqlite3_threadsafe () ? TRUE : FALSE;
 	default: 
 		return FALSE;
 	}
diff --git a/libgda/sqlite/virtual/gda-virtual-connection.c b/libgda/sqlite/virtual/gda-virtual-connection.c
index 5124636..fab51be 100644
--- a/libgda/sqlite/virtual/gda-virtual-connection.c
+++ b/libgda/sqlite/virtual/gda-virtual-connection.c
@@ -161,7 +161,7 @@ gda_virtual_connection_open (GdaVirtualProvider *virtual_provider, GError **erro
  * @error: a place to store errors, or %NULL
  *
  * Creates and opens a new virtual connection using the @virtual_provider provider. If @options
- * contains the %GDA_CONNECTION_OPTIONS_THREAD_SAFE flag, then the returned connection will be
+ * contains the %GDA_CONNECTION_OPTIONS_THREAD_ISOLATED flag, then the returned connection will be
  * a thread wrapped connection, and the actual (wrapped) virtual connection can be obtained through
  * the "gda-virtual-connection" user property (use g_object_get_data() to get it).
  *
@@ -177,7 +177,8 @@ gda_virtual_connection_open_extended (GdaVirtualProvider *virtual_provider, GdaC
 		cnc = PROV_CLASS (virtual_provider)->create_connection ((GdaServerProvider*) virtual_provider);
 		if (cnc) {
 			g_object_set (G_OBJECT (cnc), "provider", virtual_provider, 
-				      "options", options & (~GDA_CONNECTION_OPTIONS_THREAD_SAFE), NULL);
+				      "options",
+				      options & (~ (GDA_CONNECTION_OPTIONS_THREAD_ISOLATED | GDA_CONNECTION_OPTIONS_THREAD_SAFE)), NULL);
 			if (!gda_connection_open (cnc, error)) {
 				g_object_unref (cnc);
 				cnc = NULL;
@@ -188,7 +189,7 @@ gda_virtual_connection_open_extended (GdaVirtualProvider *virtual_provider, GdaC
 		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_PROVIDER_ERROR, "%s", 
 			     _("Internal error: virtual provider does not implement the create_operation() virtual method"));
 
-	if (cnc && (options & GDA_CONNECTION_OPTIONS_THREAD_SAFE)) {
+	if (cnc && (options & GDA_CONNECTION_OPTIONS_THREAD_ISOLATED)) {
 		GdaConnection *wcnc;
 		wcnc = _gda_thread_provider_handle_virtual_connection (GDA_THREAD_PROVIDER (_gda_connection_get_internal_thread_provider ()),
 								       cnc);
diff --git a/libgda/thread-wrapper/gda-thread-provider.c b/libgda/thread-wrapper/gda-thread-provider.c
index a54fe64..e09c3ea 100644
--- a/libgda/thread-wrapper/gda-thread-provider.c
+++ b/libgda/thread-wrapper/gda-thread-provider.c
@@ -443,7 +443,7 @@ gda_thread_provider_open_connection (GdaServerProvider *provider, GdaConnection
 	guint jid;
 	g_assert (data);
 	data->auth_string = auth_string;
-	data->options = options & (~GDA_CONNECTION_OPTIONS_THREAD_SAFE);
+	data->options = options & (~(GDA_CONNECTION_OPTIONS_THREAD_ISOLATED | GDA_CONNECTION_OPTIONS_THREAD_SAFE));
 
 	jid = gda_thread_wrapper_execute (wr, (GdaThreadWrapperFunc) sub_thread_open_connection, data, NULL, NULL);
 	sub_cnc = gda_thread_wrapper_fetch_result (wr, TRUE, jid, &error);
@@ -2059,6 +2059,17 @@ gda_thread_provider_identifier_quote (GdaServerProvider *provider, GdaConnection
 	return res;
 }
 
+static gpointer
+sub_thread_unref_connection (GdaConnection *cnc, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	g_object_unref (cnc);
+#ifdef GDA_DEBUG_NO
+	g_print ("/%s()\n", __FUNCTION__);
+#endif
+	return NULL;
+}
+
 /*
  * Free connection's specific data
  */
@@ -2076,9 +2087,11 @@ gda_thread_free_cnc_data (ThreadConnectionData *cdata)
 	}
 
 	/* unref cdata->sub_connection in sub thread */
-	gda_thread_wrapper_execute_void (cdata->wrapper, 
-					 (GdaThreadWrapperVoidFunc) g_object_unref,
-					 cdata->sub_connection, NULL, NULL);
+	guint jid;
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperVoidFunc) sub_thread_unref_connection,
+					  cdata->sub_connection, NULL, NULL);
+	gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	g_object_unref (cdata->wrapper);
 
 	/* free async data */
diff --git a/providers/mysql/gda-mysql-provider.c b/providers/mysql/gda-mysql-provider.c
index d85a231..8975d62 100644
--- a/providers/mysql/gda-mysql-provider.c
+++ b/providers/mysql/gda-mysql-provider.c
@@ -1234,6 +1234,8 @@ gda_mysql_provider_supports_feature (GdaServerProvider     *provider,
 	switch (feature) {
 	case GDA_CONNECTION_FEATURE_SQL :
 		return TRUE;
+	case GDA_CONNECTION_FEATURE_MULTI_THREADING:
+		return mysql_thread_safe () ? TRUE : FALSE;
 	default: 
 		return FALSE;
 	}
diff --git a/providers/postgres/gda-postgres-provider.c b/providers/postgres/gda-postgres-provider.c
index 9c0ce6c..1966a9f 100644
--- a/providers/postgres/gda-postgres-provider.c
+++ b/providers/postgres/gda-postgres-provider.c
@@ -1263,6 +1263,8 @@ gda_postgres_provider_supports_feature (GdaServerProvider *provider, GdaConnecti
                 }
                 else
                         return TRUE;
+	case GDA_CONNECTION_FEATURE_MULTI_THREADING:
+		return PQisthreadsafe () ? TRUE : FALSE;
         default:
                 break;
         }
diff --git a/tests/multi-threading/check_threaded_cnc.c b/tests/multi-threading/check_threaded_cnc.c
index dacf061..5988012 100644
--- a/tests/multi-threading/check_threaded_cnc.c
+++ b/tests/multi-threading/check_threaded_cnc.c
@@ -52,7 +52,7 @@ main (int argc, char** argv)
                 return 1;
         }
         tcnc = gda_connection_open_from_string ("SQlite", "DB_DIR=.;DB_NAME=testdb", NULL,
-					       GDA_CONNECTION_OPTIONS_THREAD_SAFE, &error);
+					       GDA_CONNECTION_OPTIONS_THREAD_ISOLATED, &error);
         if (!tcnc) {
                 g_print ("ERROR opening connection in thread safe mode: %s\n",
                          error && error->message ? error->message : "No detail");
@@ -452,7 +452,7 @@ test_meta_store (GdaConnection *cnc)
 
 	g_print ("=== Starting test where threaded connection is used internally by a meta store\n");
 	tcnc = gda_connection_open_from_string ("SQlite", "DB_DIR=.;DB_NAME=storedb", NULL,
-						GDA_CONNECTION_OPTIONS_THREAD_SAFE, &error);
+						GDA_CONNECTION_OPTIONS_THREAD_ISOLATED, &error);
         if (!tcnc) {
                 g_print ("ERROR opening connection in thread safe mode: %s\n",
                          error && error->message ? error->message : "No detail");



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