[freetds] dblib and thread safety

liam at inodes.org liam at inodes.org
Wed Sep 15 23:05:25 EDT 2004


On Tue, Sep 14, 2004 at 04:05:38PM +0200, ZIGLIO, Frediano, VF-IT wrote:

> I think you are right. Do you know a small and portable library to do
> this job (using mutex) ??

Well, for POSIX hosts, POSIX 1003.1c threads is the right answer. For Win32, 
another set of ifdefs that add critical sections would be fine.  However, 
as I'm not a Win32 person, I can't code/test this.

Please find attached my patch to the latest CVS snapshot for dblib.c.
I've done some functional and load tests on Solaris and Linux and so
far, so good.

Appreciate it if you could apply it to CVS.

Cheers,
Liam
-------------- next part --------------
--- ../../../freetds-0.63.dev.20040824/src/dblib/dblib.c	2004-07-29 20:22:40.000000000 +1000
+++ dblib.c	2004-09-16 12:57:45.000000000 +1000
@@ -21,6 +21,10 @@
 #include <config.h>
 #endif /* HAVE_CONFIG_H */
 
+#ifdef _THREAD_SAFE
+#include <pthread.h>
+#endif
+
 #if TIME_WITH_SYS_TIME
 # include <sys/time.h>
 # include <time.h>
@@ -109,9 +113,27 @@
 	int connection_list_size_represented;
 	char *recftos_filename;
 	int recftos_filenum;
+#ifdef _THREAD_SAFE
+	pthread_mutex_t mutex;
+#endif
 }
 DBLIBCONTEXT;
 
+typedef struct dblib_timeout_context {
+	int g_dblib_login_timeout; /* not used unless > -1 */
+	int g_dblib_query_timeout; /* not used unless > -1 */
+#ifdef _THREAD_SAFE
+	pthread_mutex_t mutex;
+#endif
+}
+DBLIBTIMEOUTCONTEXT;
+
+#ifdef _THREAD_SAFE
+static DBLIBTIMEOUTCONTEXT g_dblib_timeout_ctx = { -1, -1, PTHREAD_MUTEX_INITIALIZER };
+#else
+static DBLIBTIMEOUTCONTEXT g_dblib_timeout_ctx = { -1, -1 };
+#endif
+
 static DBLIBCONTEXT g_dblib_ctx;
 
 static int g_dblib_version =
@@ -131,8 +153,6 @@
 DBVERSION_80;
 #endif
 
-static int g_dblib_login_timeout = -1;	/* not used unless positive */
-static int g_dblib_query_timeout = -1;	/* not used unless positive */
 
 static int
 dblib_add_connection(DBLIBCONTEXT * ctx, TDSSOCKET * tds)
@@ -516,15 +536,27 @@
 RETCODE
 dbinit(void)
 {
+#ifdef _THREAD_SAFE
+	pthread_mutexattr_t attr;
+#endif
 	/* 
 	 * DBLIBCONTEXT stores a list of current connections so they may be closed
 	 * with dbexit() 
 	 */
 	memset(&g_dblib_ctx, '\0', sizeof(DBLIBCONTEXT));
+#ifdef _THREAD_SAFE
+	pthread_mutexattr_init(&attr);
+	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+	pthread_mutex_init(&g_dblib_ctx.mutex, &attr);
+	pthread_mutex_lock(&g_dblib_ctx.mutex);
+#endif
 
 	g_dblib_ctx.connection_list = (TDSSOCKET **) calloc(TDS_MAX_CONN, sizeof(TDSSOCKET *));
 	if (g_dblib_ctx.connection_list == NULL) {
 		tdsdump_log(TDS_DBG_FUNC, "dbinit: out of memory\n");
+#ifdef _THREAD_SAFE
+		pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
 		return FAIL;
 	}
 	g_dblib_ctx.connection_list_size = TDS_MAX_CONN;
@@ -547,6 +579,10 @@
 		g_dblib_ctx.tds_ctx->locale->date_fmt = strdup("%b %e %Y %I:%M:%S:%z%p");
 	}
 
+#ifdef _THREAD_SAFE
+	pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
+
 	return SUCCEED;
 }
 
@@ -948,6 +984,10 @@
 
 	tds_set_server(login->tds_login, server);
 
+#ifdef _THREAD_SAFE
+	pthread_mutex_lock(&g_dblib_ctx.mutex);
+#endif
+
 	dbproc->tds_socket = tds_alloc_socket(g_dblib_ctx.tds_ctx, 512);
 	tds_set_parent(dbproc->tds_socket, (void *) dbproc);
 	dbproc->tds_socket->option_flag2 &= ~0x02;	/* we're not an ODBC driver */
@@ -957,18 +997,27 @@
 	dbproc->servcharset[0] = '\0';
 
 	connection = tds_read_config_info(NULL, login->tds_login, g_dblib_ctx.tds_ctx->locale);
-	if (!connection)
+	if (!connection) {
+#ifdef _THREAD_SAFE
+		pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
 		return NULL;
-
+	}
 	/* override connection timeout if dbsetlogintime() was called */
-	if (g_dblib_login_timeout >= 0) {
-		connection->connect_timeout = g_dblib_login_timeout;
+#ifdef _THREAD_SAFE
+		pthread_mutex_lock(&g_dblib_timeout_ctx.mutex);
+#endif
+	if (g_dblib_timeout_ctx.g_dblib_login_timeout >= 0) {
+		connection->connect_timeout = g_dblib_timeout_ctx.g_dblib_login_timeout;
 	}
 
 	/* override query timeout if dbsettime() was called */
-	if (g_dblib_query_timeout >= 0) {
-		connection->timeout = g_dblib_query_timeout;
+	if (g_dblib_timeout_ctx.g_dblib_query_timeout >= 0) {
+		connection->timeout = g_dblib_timeout_ctx.g_dblib_query_timeout;
 	}
+#ifdef _THREAD_SAFE
+		pthread_mutex_unlock(&g_dblib_timeout_ctx.mutex);
+#endif
 
 	dbproc->dbchkintr = NULL;
 	dbproc->dbhndlintr = NULL;
@@ -978,6 +1027,9 @@
 	if (tds_connect(dbproc->tds_socket, connection) == TDS_FAIL) {
 		dbproc->tds_socket = NULL;
 		tds_free_connection(connection);
+#ifdef _THREAD_SAFE
+		pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
 		return NULL;
 	}
 	tds_free_connection(connection);
@@ -990,6 +1042,9 @@
 	} else {
 		fprintf(stderr, "DB-Library: Login incorrect.\n");
 		free(dbproc);	/* memory leak fix (mlilback, 11/17/01) */
+#ifdef _THREAD_SAFE
+		pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
 		return NULL;
 	}
 
@@ -1010,6 +1065,9 @@
 			g_dblib_ctx.recftos_filenum++;
 		}
 	}
+#ifdef _THREAD_SAFE
+	pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
 	return dbproc;
 }
 
@@ -1227,7 +1285,13 @@
 	dbstring_free(&(dbproc->dboptcmd));
 
 	dbfreebuf(dbproc);
+#ifdef _THREAD_SAFE
+	pthread_mutex_lock(&g_dblib_ctx.mutex);
+#endif
 	dblib_del_connection(&g_dblib_ctx, dbproc->tds_socket);
+#ifdef _THREAD_SAFE
+	pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
 	free(dbproc);
 
 	return;
@@ -1246,8 +1310,12 @@
 	TDSSOCKET *tds;
 	DBPROCESS *dbproc;
 	int i;
-	const int list_size = g_dblib_ctx.connection_list_size;
+	int list_size;
 
+#ifdef _THREAD_SAFE
+	pthread_mutex_lock(&g_dblib_ctx.mutex);
+#endif
+	list_size = g_dblib_ctx.connection_list_size;
 
 	/* FIX ME -- this breaks if ctlib/dblib used in same process */
 	for (i = 0; i < list_size; i++) {
@@ -1267,6 +1335,9 @@
 		tds_free_context(g_dblib_ctx.tds_ctx);
 		g_dblib_ctx.tds_ctx = NULL;
 	}
+#ifdef _THREAD_SAFE
+	pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
 }
 
 /** \internal
@@ -1819,7 +1890,13 @@
 
 	tdsdump_log(TDS_DBG_INFO1, "dbconvert() calling tds_convert\n");
 
+#ifdef _THREAD_SAFE
+	pthread_mutex_lock(&g_dblib_ctx.mutex);
+#endif
 	len = tds_convert(g_dblib_ctx.tds_ctx, srctype, (const TDS_CHAR *) src, srclen, desttype, &dres);
+#ifdef _THREAD_SAFE
+	pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
 	tdsdump_log(TDS_DBG_INFO1, "dbconvert() called tds_convert returned %d\n", len);
 
 	switch (len) {
@@ -3230,7 +3307,13 @@
 dbsetmaxprocs(int maxprocs)
 {
 	int i;
-	TDSSOCKET **old_list = g_dblib_ctx.connection_list;
+	TDSSOCKET **old_list = NULL;
+
+#ifdef _THREAD_SAFE
+	pthread_mutex_lock(&g_dblib_ctx.mutex);
+#endif
+
+	old_list = g_dblib_ctx.connection_list;
 
 	tdsdump_log(TDS_DBG_FUNC, "UNTESTED dbsetmaxprocs()\n");
 	/*
@@ -3241,6 +3324,9 @@
 	 */
 	if (maxprocs < g_dblib_ctx.connection_list_size) {
 		g_dblib_ctx.connection_list_size_represented = maxprocs;
+#ifdef _THREAD_SAFE
+		pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
 		return SUCCEED;
 	}
 
@@ -3248,6 +3334,10 @@
 
 	if (g_dblib_ctx.connection_list == NULL) {
 		g_dblib_ctx.connection_list = old_list;
+
+#ifdef _THREAD_SAFE
+		pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
 		return FAIL;
 	}
 
@@ -3258,6 +3348,10 @@
 	g_dblib_ctx.connection_list_size = maxprocs;
 	g_dblib_ctx.connection_list_size_represented = maxprocs;
 
+#ifdef _THREAD_SAFE
+	pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
+
 	return SUCCEED;
 }
 
@@ -3271,7 +3365,15 @@
 int
 dbgetmaxprocs(void)
 {
-	return g_dblib_ctx.connection_list_size_represented;
+	int c;
+#ifdef _THREAD_SAFE
+	pthread_mutex_lock(&g_dblib_ctx.mutex);
+#endif
+	c = g_dblib_ctx.connection_list_size_represented;
+#ifdef _THREAD_SAFE
+	pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
+	return c;
 }
 
 /**
@@ -3287,7 +3389,13 @@
 dbsettime(int seconds)
 {
 	tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsettime()\n");
-	g_dblib_query_timeout = seconds;
+#ifdef _THREAD_SAFE
+	pthread_mutex_lock(&g_dblib_timeout_ctx.mutex);
+#endif
+	g_dblib_timeout_ctx.g_dblib_query_timeout = seconds;
+#ifdef _THREAD_SAFE
+	pthread_mutex_unlock(&g_dblib_timeout_ctx.mutex);
+#endif
 	return SUCCEED;
 }
 
@@ -3302,7 +3410,13 @@
 RETCODE
 dbsetlogintime(int seconds)
 {
-	g_dblib_login_timeout = seconds;
+#ifdef _THREAD_SAFE
+	pthread_mutex_lock(&g_dblib_timeout_ctx.mutex);
+#endif
+	g_dblib_timeout_ctx.g_dblib_login_timeout = seconds;
+#ifdef _THREAD_SAFE
+	pthread_mutex_unlock(&g_dblib_timeout_ctx.mutex);
+#endif
 	return SUCCEED;
 }
 
@@ -5473,11 +5587,17 @@
 void
 dbrecftos(char *filename)
 {
+#ifdef _THREAD_SAFE
+	pthread_mutex_lock(&g_dblib_ctx.mutex);
+#endif
 	g_dblib_ctx.recftos_filename = malloc(strlen(filename) + 1);
 	if (g_dblib_ctx.recftos_filename != (char *) NULL) {
 		strcpy(g_dblib_ctx.recftos_filename, filename);
 		g_dblib_ctx.recftos_filenum = 0;
 	}
+#ifdef _THREAD_SAFE
+	pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
 }
 
 /** \internal
@@ -6047,10 +6167,18 @@
 _dblib_client_msg(DBPROCESS * dbproc, int dberr, int severity, const char *dberrstr)
 {
 	TDSSOCKET *tds = NULL;
+	int r;
 
 	if (dbproc)
 		tds = dbproc->tds_socket;
-	return tds_client_msg(g_dblib_ctx.tds_ctx, tds, dberr, severity, -1, -1, dberrstr);
+#ifdef _THREAD_SAFE
+	pthread_mutex_lock(&g_dblib_ctx.mutex);
+#endif
+	r = tds_client_msg(g_dblib_ctx.tds_ctx, tds, dberr, severity, -1, -1, dberrstr);
+#ifdef _THREAD_SAFE
+	pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
+	return r;
 }
 
 static char *
@@ -6273,7 +6401,13 @@
 			dres.n.scale = num->scale;
 	}
 
+#ifdef _THREAD_SAFE
+	pthread_mutex_lock(&g_dblib_ctx.mutex);
+#endif
 	len = tds_convert(g_dblib_ctx.tds_ctx, srctype, (const TDS_CHAR *) src, srclen, desttype, &dres);
+#ifdef _THREAD_SAFE
+	pthread_mutex_unlock(&g_dblib_ctx.mutex);
+#endif
 	tdsdump_log(TDS_DBG_INFO1, "copy_data_to_host_var() called tds_convert returned %d\n", len);
 
 	switch (len) {


More information about the FreeTDS mailing list