Description:
There are a number calls to setlocale() post the initialisation phase. The following Visual Studio bug report has a good synopsis of the problem:
https://connect.microsoft.com/VisualStudio/feedback/details/790530/crt-function-setlocale-...
Basically it is not safe to "just" call setlocale() in a multi-threaded applications.
How to repeat:
1. Compile with the Visual Studio 2013 compiler
2. Execute statements from multiple threads
Suggested fix:
Use _confighreadlocale to limit the locale change to the current thread. The following patch has worked for us:
diff -u -r mysql-connector-odbc-5.1.13-src-old/driver/execute.c mysql-connector-odbc-5.1.13-src/driver/execute.c
--- driver/execute.c 2016-10-07 07:09:40.000000000 +0200
+++ driver/execute.c 2016-10-07 08:47:25.000000000 +0200
@@ -188,6 +188,7 @@
uint i,length, had_info= 0;
NET *net;
SQLRETURN rc= SQL_SUCCESS;
+ int loc;
int mutex_was_locked= pthread_mutex_trylock(&stmt->dbc->lock);
@@ -196,6 +197,10 @@
if (!stmt->dbc->ds->dont_use_set_locale)
{
+ #ifdef _WIN32
+ loc = _configthreadlocale(0);
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+ #endif
setlocale(LC_NUMERIC, "C"); /* force use of '.' as decimal point */
}
@@ -274,6 +279,9 @@
if (!stmt->dbc->ds->dont_use_set_locale)
{
setlocale(LC_NUMERIC,default_locale);
+ #ifdef _WIN32
+ _configthreadlocale(loc);
+ #endif
}
if (finalquery!=NULL)
@@ -289,8 +297,12 @@
/* ! was _already_ locked, when we tried to lock */
if (!mutex_was_locked)
pthread_mutex_unlock(&stmt->dbc->lock);
- if (!stmt->dbc->ds->dont_use_set_locale)
+ if (!stmt->dbc->ds->dont_use_set_locale) {
setlocale(LC_NUMERIC,default_locale);
+ #ifdef _WIN32
+ _configthreadlocale(loc);
+ #endif
+ }
return rc;
}
diff -u -r mysql-connector-odbc-5.1.13-src-old/driver/results.c mysql-connector-odbc-5.1.13-src/driver/results.c
--- driver/results.c 2016-10-07 07:09:40.000000000 +0200
+++ driver/results.c 2016-10-07 08:46:15.000000000 +0200
@@ -1202,6 +1202,7 @@
SQLRETURN result;
ulong length= 0;
DESCREC *irrec;
+ int loc;
if (!stmt->result || !stmt->current_values)
{
@@ -1230,16 +1231,25 @@
if (!length && stmt->current_values[ColumnNumber])
length= strlen(stmt->current_values[ColumnNumber]);
- if (!stmt->dbc->ds->dont_use_set_locale)
+ if (!stmt->dbc->ds->dont_use_set_locale) {
+ #ifdef _WIN32
+ loc = _configthreadlocale(0);
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+ #endif
setlocale(LC_NUMERIC, "C");
+ }
result= sql_get_data(stmt, TargetType, ColumnNumber,
TargetValuePtr, BufferLength, StrLen_or_IndPtr,
stmt->current_values[ColumnNumber], length,
desc_get_rec(stmt->ard, ColumnNumber, FALSE));
- if (!stmt->dbc->ds->dont_use_set_locale)
+ if (!stmt->dbc->ds->dont_use_set_locale) {
setlocale(LC_NUMERIC,default_locale);
+ #ifdef _WIN32
+ _configthreadlocale(loc);
+ #endif
+ }
return result;
}
@@ -1493,6 +1503,7 @@
MYSQL_ROW_OFFSET save_position;
SQLULEN dummy_pcrow;
BOOL disconnected= FALSE;
+ int loc;
LINT_INIT(save_position);
@@ -1633,6 +1644,10 @@
if (!stmt->dbc->ds->dont_use_set_locale)
{
+ #ifdef _WIN32
+ loc = _configthreadlocale(0);
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+ #endif
setlocale(LC_NUMERIC, "C");
}
@@ -1783,9 +1798,12 @@
stmt->end_of_set= mysql_row_seek(stmt->result,save_position);
}
- if (!stmt->dbc->ds->dont_use_set_locale)
-
+ if (!stmt->dbc->ds->dont_use_set_locale) {
setlocale(LC_NUMERIC,default_locale);
+ #ifdef _WIN32
+ _configthreadlocale(loc);
+ #endif
+ }
if (SQL_SUCCEEDED(res)
&& stmt->rows_found_in_set < stmt->ard->array_size)