Buildfarm failures on woodlouse (in ecpg-check)
Hello,
my buildfarm animal woodlouse (Visual Studio 2013 on Windows 7) stopped
working correctly some months ago. After Tom kindly prodded me into
fixing it, I noticed that I had configured it to skip the ecpg-check
step because one of the tests in the "thread" section (not always the
same) nearly always failed.
I had set it up in circa 2015, so I reenabled the step to see whether
anything had changed since, and it started failing again.
Through some trial and error, and with the help of Microsoft's
Application Verifier tool, I found what I think is the cause: It looks
like the VS 2013 CRT's setlocale() function is not entirely thread-safe;
the heap debugging options make it crash when manipulating per-thread
locale state, and according to the comments surrounding that spot in the
CRT source, the developers were aware the code is fragile.
I crudely hacked a critical section around the setlocale() call in
pgwin32_setlocale(). After this change, the test does not crash, while
without it, it crashes every time.
If there is interest in fixing this issue that is apparently limited to
VS 2013, I will try and produce a proper patch. I notice, however, that
there is a pthread compatibility layer available that I have no
experience with at all, and I assume using it is preferred over straight
Win32?
My hack is attached; please feel free to tell me I'm on the wrong track.
To build correctly, it requires defining _WIN32_WINNT to be 0x600 or
above (and using an SDK that knows about InitOnceExecuteOnce()).
--
Christian
Attachments:
setlocale.difftext/plain; charset=UTF-8; name=setlocale.diffDownload
diff --git a/src/port/win32setlocale.c b/src/port/win32setlocale.c
index cbf109836b..278d836b4d 100644
--- a/src/port/win32setlocale.c
+++ b/src/port/win32setlocale.c
@@ -164,19 +164,33 @@ map_locale(const struct locale_map * map, const char *locale)
return locale;
}
+static CRITICAL_SECTION setlocale_cs;
+static INIT_ONCE init_once;
+static BOOL CALLBACK init_setlocale_cs(PINIT_ONCE pInitOnce, PVOID pParam, PVOID pCtx)
+{
+ InitializeCriticalSection((PCRITICAL_SECTION)pParam);
+ return TRUE;
+}
+
char *
pgwin32_setlocale(int category, const char *locale)
{
const char *argument;
char *result;
+ if (InitOnceExecuteOnce(&init_once, init_setlocale_cs, (PVOID)&setlocale_cs, NULL) == 0) {
+ abort();
+ }
+
if (locale == NULL)
argument = NULL;
else
argument = map_locale(locale_map_argument, locale);
/* Call the real setlocale() function */
+ EnterCriticalSection(&setlocale_cs);
result = setlocale(category, argument);
+ LeaveCriticalSection(&setlocale_cs);
/*
* setlocale() is specified to return a "char *" that the caller is
On 06/11/2017 11:33 AM, Christian Ullrich wrote:
Hello,
my buildfarm animal woodlouse (Visual Studio 2013 on Windows 7)
stopped working correctly some months ago. After Tom kindly prodded me
into fixing it, I noticed that I had configured it to skip the
ecpg-check step because one of the tests in the "thread" section (not
always the same) nearly always failed.I had set it up in circa 2015, so I reenabled the step to see whether
anything had changed since, and it started failing again.Through some trial and error, and with the help of Microsoft's
Application Verifier tool, I found what I think is the cause: It looks
like the VS 2013 CRT's setlocale() function is not entirely
thread-safe; the heap debugging options make it crash when
manipulating per-thread locale state, and according to the comments
surrounding that spot in the CRT source, the developers were aware the
code is fragile.I crudely hacked a critical section around the setlocale() call in
pgwin32_setlocale(). After this change, the test does not crash, while
without it, it crashes every time.If there is interest in fixing this issue that is apparently limited
to VS 2013, I will try and produce a proper patch. I notice, however,
that there is a pthread compatibility layer available that I have no
experience with at all, and I assume using it is preferred over
straight Win32?My hack is attached; please feel free to tell me I'm on the wrong track.
To build correctly, it requires defining _WIN32_WINNT to be 0x600 or
above (and using an SDK that knows about InitOnceExecuteOnce()).
It's certainly worth doing.
I turned off testing ecpg ages ago on bowerbird because I was getting
errors. That's an older version of the toolset.
We already define _WIN32_WINNT to be 0x0600 on all appropriate platforms
(Vista/2008 and above), so I think you could probably just check for
that value.
I have no opinion on the pthread question.
cheers
andrew
--
Andrew Dunstan https://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
* Andrew Dunstan wrote:
On 06/11/2017 11:33 AM, Christian Ullrich wrote:
To build correctly, it requires defining _WIN32_WINNT to be 0x600 or
above (and using an SDK that knows about InitOnceExecuteOnce()).
We already define _WIN32_WINNT to be 0x0600 on all appropriate platforms
(Vista/2008 and above), so I think you could probably just check for
that value.
Not quite; the definition depends on the build toolset, not the build
platform. When building with VS 2015 and above, _WIN32_WINNT is set to
0x600 (Vista/2008), while with older compilers, the value is 0x501
(XP/2003). This is also due to locale issues, but of a different kind,
and is apparently coincidental.
The build platform does not figure into the target platform, which is
clearly a good idea for binary distribution reasons.
--
Christian
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
* I wrote:
If there is interest in fixing this issue that is apparently limited
to VS 2013, I will try and produce a proper patch.
Patch.
Perhaps surprisingly, the bug is in the failing test cases themselves,
not ecpg. The CRT has two modes for its locale implementation: When
called in a "global" mode thread, setlocale() affects all global mode
threads; when called from a per-thread mode thread, it affects that
thread only. [1]https://msdn.microsoft.com/en-us/library/ms235302(v=vs.120).aspx
In the threaded test cases, many global mode threads call setlocale()
simultaneously, getting in each other's way. The fix is to switch the
worker threads to per-thread mode first.
Without this patch, I see crashes in approximately 25 percent of runs
(four crashes in 16 cycles of vcregress ecpgcheck, ~10 in 50 runs of
thread.exe alone). With the patch, I have no crashes in 100 ecpgcheck runs.
On the other hand, this affects only the buildfarm VM I mentioned
earlier. I have another VM where it does not ever crash even without the
patch -- such are the joys of multithreading. Both of them should have
plenty of cores, both physical and virtual.
There are some alternatives to fixing it this way, but I think this is
the best approach:
- Selecting per-thread mode in ecpglib takes the choice away from the
developer who might want shared locale.
- Adding locking around setlocale() is difficult because ecpglib already
uses a wrapper around the CRT function, provided by libpgport.
These test cases are the only consumers of the wrapper that have any
concept of multithreading. Supporting it in libpgport for the sole
benefit of threaded ECPG applications on Windows does not seem to
be a good idea, and re-wrapping the wrapper in ecpglib is not only
beyond my abilities to write, but is also going to be unmaintainable.
- Adding locking around every setlocale() call in ecpglib is just ugly.
While working on this, I also noticed that there seem to be two separate
partial implementations of a pthread synchronization emulation for
Win32. One is in ecpglib, uses mutexes and provides
PTHREAD_MUTEX_INITIALIZER and pthread_once(), the other has the header
in src/port and the implementation in libpq, uses critical sections and
does not cover either feature.
Should the two be merged at some point?
[1]: https://msdn.microsoft.com/en-us/library/ms235302(v=vs.120).aspx
--
Christian
Attachments:
0001-Make-setlocale-aware-of-multithreading-to-avoid-cras.patchtext/plain; charset=UTF-8; name=0001-Make-setlocale-aware-of-multithreading-to-avoid-cras.patchDownload
From 5dee698f4cef684a320ced59b19cd4fea61319fb Mon Sep 17 00:00:00 2001
From: Christian Ullrich <chris@chrullrich.net>
Date: Wed, 14 Jun 2017 22:18:18 +0200
Subject: [PATCH] Make setlocale() aware of multithreading to avoid crash.
---
src/interfaces/ecpg/test/expected/thread-alloc.c | 39 +++++++------
.../ecpg/test/expected/thread-descriptor.c | 19 +++---
src/interfaces/ecpg/test/expected/thread-prep.c | 67 ++++++++++++----------
src/interfaces/ecpg/test/expected/thread-thread.c | 60 ++++++++++---------
.../ecpg/test/expected/thread-thread_implicit.c | 60 ++++++++++---------
src/interfaces/ecpg/test/thread/alloc.pgc | 5 ++
src/interfaces/ecpg/test/thread/descriptor.pgc | 5 ++
src/interfaces/ecpg/test/thread/prep.pgc | 5 ++
src/interfaces/ecpg/test/thread/thread.pgc | 6 ++
.../ecpg/test/thread/thread_implicit.pgc | 6 ++
10 files changed, 163 insertions(+), 109 deletions(-)
diff --git a/src/interfaces/ecpg/test/expected/thread-alloc.c b/src/interfaces/ecpg/test/expected/thread-alloc.c
index 49f1cd1..1312580 100644
--- a/src/interfaces/ecpg/test/expected/thread-alloc.c
+++ b/src/interfaces/ecpg/test/expected/thread-alloc.c
@@ -22,6 +22,7 @@ main(void)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>
+#include <locale.h>
#else
#include <pthread.h>
#endif
@@ -99,7 +100,7 @@ struct sqlca_t *ECPGget_sqlca(void);
#endif
-#line 24 "alloc.pgc"
+#line 25 "alloc.pgc"
#line 1 "regression.h"
@@ -109,14 +110,14 @@ struct sqlca_t *ECPGget_sqlca(void);
-#line 25 "alloc.pgc"
+#line 26 "alloc.pgc"
/* exec sql whenever sqlerror sqlprint ; */
-#line 27 "alloc.pgc"
+#line 28 "alloc.pgc"
/* exec sql whenever not found sqlprint ; */
-#line 28 "alloc.pgc"
+#line 29 "alloc.pgc"
#ifdef WIN32
@@ -127,59 +128,63 @@ static void* fn(void* arg)
{
int i;
+#ifdef WIN32
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+#endif
+
/* exec sql begin declare section */
-#line 39 "alloc.pgc"
+#line 44 "alloc.pgc"
int value ;
-#line 40 "alloc.pgc"
+#line 45 "alloc.pgc"
char name [ 100 ] ;
-#line 41 "alloc.pgc"
+#line 46 "alloc.pgc"
char ** r = NULL ;
/* exec sql end declare section */
-#line 42 "alloc.pgc"
+#line 47 "alloc.pgc"
value = (long)arg;
sprintf(name, "Connection: %d", value);
{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , name, 0);
-#line 47 "alloc.pgc"
+#line 52 "alloc.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 47 "alloc.pgc"
+#line 52 "alloc.pgc"
{ ECPGsetcommit(__LINE__, "on", NULL);
-#line 48 "alloc.pgc"
+#line 53 "alloc.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 48 "alloc.pgc"
+#line 53 "alloc.pgc"
for (i = 1; i <= REPEATS; ++i)
{
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select relname from pg_class where relname = 'pg_class'", ECPGt_EOIT,
ECPGt_char,&(r),(long)0,(long)0,(1)*sizeof(char),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
-#line 51 "alloc.pgc"
+#line 56 "alloc.pgc"
if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 51 "alloc.pgc"
+#line 56 "alloc.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 51 "alloc.pgc"
+#line 56 "alloc.pgc"
free(r);
r = NULL;
}
{ ECPGdisconnect(__LINE__, name);
-#line 55 "alloc.pgc"
+#line 60 "alloc.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 55 "alloc.pgc"
+#line 60 "alloc.pgc"
return 0;
diff --git a/src/interfaces/ecpg/test/expected/thread-descriptor.c b/src/interfaces/ecpg/test/expected/thread-descriptor.c
index e2be89d..8698db7 100644
--- a/src/interfaces/ecpg/test/expected/thread-descriptor.c
+++ b/src/interfaces/ecpg/test/expected/thread-descriptor.c
@@ -12,6 +12,7 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>
+#include <locale.h>
#else
#include <pthread.h>
#endif
@@ -90,13 +91,13 @@ struct sqlca_t *ECPGget_sqlca(void);
#endif
-#line 15 "descriptor.pgc"
+#line 16 "descriptor.pgc"
/* exec sql whenever sqlerror sqlprint ; */
-#line 16 "descriptor.pgc"
+#line 17 "descriptor.pgc"
/* exec sql whenever not found sqlprint ; */
-#line 17 "descriptor.pgc"
+#line 18 "descriptor.pgc"
#if defined(ENABLE_THREAD_SAFETY) && defined(WIN32)
@@ -107,19 +108,23 @@ static void* fn(void* arg)
{
int i;
+#ifdef WIN32
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+#endif
+
for (i = 1; i <= REPEATS; ++i)
{
ECPGallocate_desc(__LINE__, "mydesc");
-#line 29 "descriptor.pgc"
+#line 34 "descriptor.pgc"
if (sqlca.sqlcode < 0) sqlprint();
-#line 29 "descriptor.pgc"
+#line 34 "descriptor.pgc"
ECPGdeallocate_desc(__LINE__, "mydesc");
-#line 30 "descriptor.pgc"
+#line 35 "descriptor.pgc"
if (sqlca.sqlcode < 0) sqlprint();
-#line 30 "descriptor.pgc"
+#line 35 "descriptor.pgc"
}
diff --git a/src/interfaces/ecpg/test/expected/thread-prep.c b/src/interfaces/ecpg/test/expected/thread-prep.c
index 4d06b90..1b023e6 100644
--- a/src/interfaces/ecpg/test/expected/thread-prep.c
+++ b/src/interfaces/ecpg/test/expected/thread-prep.c
@@ -22,6 +22,7 @@ main(void)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>
+#include <locale.h>
#else
#include <pthread.h>
#endif
@@ -99,7 +100,7 @@ struct sqlca_t *ECPGget_sqlca(void);
#endif
-#line 24 "prep.pgc"
+#line 25 "prep.pgc"
#line 1 "regression.h"
@@ -109,14 +110,14 @@ struct sqlca_t *ECPGget_sqlca(void);
-#line 25 "prep.pgc"
+#line 26 "prep.pgc"
/* exec sql whenever sqlerror sqlprint ; */
-#line 27 "prep.pgc"
+#line 28 "prep.pgc"
/* exec sql whenever not found sqlprint ; */
-#line 28 "prep.pgc"
+#line 29 "prep.pgc"
#ifdef WIN32
@@ -127,69 +128,73 @@ static void* fn(void* arg)
{
int i;
+#ifdef WIN32
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+#endif
+
/* exec sql begin declare section */
-#line 39 "prep.pgc"
+#line 44 "prep.pgc"
int value ;
-#line 40 "prep.pgc"
+#line 45 "prep.pgc"
char name [ 100 ] ;
-#line 41 "prep.pgc"
+#line 46 "prep.pgc"
char query [ 256 ] = "INSERT INTO T VALUES ( ? )" ;
/* exec sql end declare section */
-#line 42 "prep.pgc"
+#line 47 "prep.pgc"
value = (long)arg;
sprintf(name, "Connection: %d", value);
{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , name, 0);
-#line 47 "prep.pgc"
+#line 52 "prep.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 47 "prep.pgc"
+#line 52 "prep.pgc"
{ ECPGsetcommit(__LINE__, "on", NULL);
-#line 48 "prep.pgc"
+#line 53 "prep.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 48 "prep.pgc"
+#line 53 "prep.pgc"
for (i = 1; i <= REPEATS; ++i)
{
{ ECPGprepare(__LINE__, NULL, 0, "i", query);
-#line 51 "prep.pgc"
+#line 56 "prep.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 51 "prep.pgc"
+#line 56 "prep.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "i",
ECPGt_int,&(value),(long)1,(long)1,sizeof(int),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
-#line 52 "prep.pgc"
+#line 57 "prep.pgc"
if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 52 "prep.pgc"
+#line 57 "prep.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 52 "prep.pgc"
+#line 57 "prep.pgc"
}
{ ECPGdeallocate(__LINE__, 0, NULL, "i");
-#line 54 "prep.pgc"
+#line 59 "prep.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 54 "prep.pgc"
+#line 59 "prep.pgc"
{ ECPGdisconnect(__LINE__, name);
-#line 55 "prep.pgc"
+#line 60 "prep.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 55 "prep.pgc"
+#line 60 "prep.pgc"
return 0;
@@ -205,34 +210,34 @@ int main ()
#endif
{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0);
-#line 69 "prep.pgc"
+#line 74 "prep.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 69 "prep.pgc"
+#line 74 "prep.pgc"
{ ECPGsetcommit(__LINE__, "on", NULL);
-#line 70 "prep.pgc"
+#line 75 "prep.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 70 "prep.pgc"
+#line 75 "prep.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table if exists T", ECPGt_EOIT, ECPGt_EORT);
-#line 71 "prep.pgc"
+#line 76 "prep.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 71 "prep.pgc"
+#line 76 "prep.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table T ( i int )", ECPGt_EOIT, ECPGt_EORT);
-#line 72 "prep.pgc"
+#line 77 "prep.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 72 "prep.pgc"
+#line 77 "prep.pgc"
{ ECPGdisconnect(__LINE__, "CURRENT");
-#line 73 "prep.pgc"
+#line 78 "prep.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 73 "prep.pgc"
+#line 78 "prep.pgc"
#ifdef WIN32
diff --git a/src/interfaces/ecpg/test/expected/thread-thread.c b/src/interfaces/ecpg/test/expected/thread-thread.c
index 61d3c5c..470fbb2 100644
--- a/src/interfaces/ecpg/test/expected/thread-thread.c
+++ b/src/interfaces/ecpg/test/expected/thread-thread.c
@@ -26,6 +26,7 @@ main(void)
#include <pthread.h>
#else
#include <windows.h>
+#include <locale.h>
#endif
@@ -36,7 +37,7 @@ main(void)
-#line 22 "thread.pgc"
+#line 23 "thread.pgc"
void *test_thread(void *arg);
@@ -55,10 +56,10 @@ int main()
/* exec sql begin declare section */
-#line 38 "thread.pgc"
+#line 39 "thread.pgc"
int l_rows ;
/* exec sql end declare section */
-#line 39 "thread.pgc"
+#line 40 "thread.pgc"
/* Do not switch on debug output for regression tests. The threads get executed in
@@ -67,22 +68,22 @@ int main()
/* setup test_thread table */
{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 46 "thread.pgc"
+#line 47 "thread.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test_thread", ECPGt_EOIT, ECPGt_EORT);}
-#line 47 "thread.pgc"
+#line 48 "thread.pgc"
/* DROP might fail */
{ ECPGtrans(__LINE__, NULL, "commit");}
-#line 48 "thread.pgc"
+#line 49 "thread.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table test_thread ( tstamp timestamp not null default cast ( timeofday ( ) as timestamp ) , thread text not null , iteration integer not null , primary key ( thread , iteration ) )", ECPGt_EOIT, ECPGt_EORT);}
-#line 53 "thread.pgc"
+#line 54 "thread.pgc"
{ ECPGtrans(__LINE__, NULL, "commit");}
-#line 54 "thread.pgc"
+#line 55 "thread.pgc"
{ ECPGdisconnect(__LINE__, "CURRENT");}
-#line 55 "thread.pgc"
+#line 56 "thread.pgc"
/* create, and start, threads */
@@ -114,18 +115,18 @@ int main()
/* and check results */
{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 85 "thread.pgc"
+#line 86 "thread.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from test_thread", ECPGt_EOIT,
ECPGt_int,&(l_rows),(long)1,(long)1,sizeof(int),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
-#line 86 "thread.pgc"
+#line 87 "thread.pgc"
{ ECPGtrans(__LINE__, NULL, "commit");}
-#line 87 "thread.pgc"
+#line 88 "thread.pgc"
{ ECPGdisconnect(__LINE__, "CURRENT");}
-#line 88 "thread.pgc"
+#line 89 "thread.pgc"
if( l_rows == (nthreads * iterations) )
printf("Success.\n");
@@ -138,17 +139,22 @@ int main()
void *test_thread(void *arg)
{
long threadnum = (long)arg;
+
+#ifdef WIN32
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+#endif
+
/* exec sql begin declare section */
-#line 101 "thread.pgc"
+#line 107 "thread.pgc"
int l_i ;
-#line 102 "thread.pgc"
+#line 108 "thread.pgc"
char l_connection [ 128 ] ;
/* exec sql end declare section */
-#line 103 "thread.pgc"
+#line 109 "thread.pgc"
/* build up connection name, and connect to database */
@@ -158,13 +164,13 @@ void *test_thread(void *arg)
_snprintf(l_connection, sizeof(l_connection), "thread_%03ld", threadnum);
#endif
/* exec sql whenever sqlerror sqlprint ; */
-#line 111 "thread.pgc"
+#line 117 "thread.pgc"
{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , l_connection, 0);
-#line 112 "thread.pgc"
+#line 118 "thread.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 112 "thread.pgc"
+#line 118 "thread.pgc"
if( sqlca.sqlcode != 0 )
{
@@ -172,10 +178,10 @@ if (sqlca.sqlcode < 0) sqlprint();}
return( NULL );
}
{ ECPGtrans(__LINE__, l_connection, "begin");
-#line 118 "thread.pgc"
+#line 124 "thread.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 118 "thread.pgc"
+#line 124 "thread.pgc"
/* insert into test_thread table */
@@ -186,10 +192,10 @@ if (sqlca.sqlcode < 0) sqlprint();}
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
ECPGt_int,&(l_i),(long)1,(long)1,sizeof(int),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
-#line 123 "thread.pgc"
+#line 129 "thread.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 123 "thread.pgc"
+#line 129 "thread.pgc"
if( sqlca.sqlcode != 0 )
printf("%s: ERROR: insert failed!\n", l_connection);
@@ -197,16 +203,16 @@ if (sqlca.sqlcode < 0) sqlprint();}
/* all done */
{ ECPGtrans(__LINE__, l_connection, "commit");
-#line 129 "thread.pgc"
+#line 135 "thread.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 129 "thread.pgc"
+#line 135 "thread.pgc"
{ ECPGdisconnect(__LINE__, l_connection);
-#line 130 "thread.pgc"
+#line 136 "thread.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 130 "thread.pgc"
+#line 136 "thread.pgc"
return( NULL );
}
diff --git a/src/interfaces/ecpg/test/expected/thread-thread_implicit.c b/src/interfaces/ecpg/test/expected/thread-thread_implicit.c
index c43c1ad..854549e 100644
--- a/src/interfaces/ecpg/test/expected/thread-thread_implicit.c
+++ b/src/interfaces/ecpg/test/expected/thread-thread_implicit.c
@@ -27,6 +27,7 @@ main(void)
#include <pthread.h>
#else
#include <windows.h>
+#include <locale.h>
#endif
@@ -37,7 +38,7 @@ main(void)
-#line 23 "thread_implicit.pgc"
+#line 24 "thread_implicit.pgc"
void *test_thread(void *arg);
@@ -56,10 +57,10 @@ int main()
/* exec sql begin declare section */
-#line 39 "thread_implicit.pgc"
+#line 40 "thread_implicit.pgc"
int l_rows ;
/* exec sql end declare section */
-#line 40 "thread_implicit.pgc"
+#line 41 "thread_implicit.pgc"
/* Do not switch on debug output for regression tests. The threads get executed in
@@ -68,22 +69,22 @@ int main()
/* setup test_thread table */
{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 47 "thread_implicit.pgc"
+#line 48 "thread_implicit.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test_thread", ECPGt_EOIT, ECPGt_EORT);}
-#line 48 "thread_implicit.pgc"
+#line 49 "thread_implicit.pgc"
/* DROP might fail */
{ ECPGtrans(__LINE__, NULL, "commit");}
-#line 49 "thread_implicit.pgc"
+#line 50 "thread_implicit.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table test_thread ( tstamp timestamp not null default cast ( timeofday ( ) as timestamp ) , thread text not null , iteration integer not null , primary key ( thread , iteration ) )", ECPGt_EOIT, ECPGt_EORT);}
-#line 54 "thread_implicit.pgc"
+#line 55 "thread_implicit.pgc"
{ ECPGtrans(__LINE__, NULL, "commit");}
-#line 55 "thread_implicit.pgc"
+#line 56 "thread_implicit.pgc"
{ ECPGdisconnect(__LINE__, "CURRENT");}
-#line 56 "thread_implicit.pgc"
+#line 57 "thread_implicit.pgc"
/* create, and start, threads */
@@ -115,18 +116,18 @@ int main()
/* and check results */
{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 86 "thread_implicit.pgc"
+#line 87 "thread_implicit.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from test_thread", ECPGt_EOIT,
ECPGt_int,&(l_rows),(long)1,(long)1,sizeof(int),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
-#line 87 "thread_implicit.pgc"
+#line 88 "thread_implicit.pgc"
{ ECPGtrans(__LINE__, NULL, "commit");}
-#line 88 "thread_implicit.pgc"
+#line 89 "thread_implicit.pgc"
{ ECPGdisconnect(__LINE__, "CURRENT");}
-#line 89 "thread_implicit.pgc"
+#line 90 "thread_implicit.pgc"
if( l_rows == (nthreads * iterations) )
printf("Success.\n");
@@ -139,17 +140,22 @@ int main()
void *test_thread(void *arg)
{
long threadnum = (long)arg;
+
+#ifdef WIN32
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+#endif
+
/* exec sql begin declare section */
-#line 102 "thread_implicit.pgc"
+#line 108 "thread_implicit.pgc"
int l_i ;
-#line 103 "thread_implicit.pgc"
+#line 109 "thread_implicit.pgc"
char l_connection [ 128 ] ;
/* exec sql end declare section */
-#line 104 "thread_implicit.pgc"
+#line 110 "thread_implicit.pgc"
/* build up connection name, and connect to database */
@@ -159,13 +165,13 @@ void *test_thread(void *arg)
_snprintf(l_connection, sizeof(l_connection), "thread_%03ld", threadnum);
#endif
/* exec sql whenever sqlerror sqlprint ; */
-#line 112 "thread_implicit.pgc"
+#line 118 "thread_implicit.pgc"
{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , l_connection, 0);
-#line 113 "thread_implicit.pgc"
+#line 119 "thread_implicit.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 113 "thread_implicit.pgc"
+#line 119 "thread_implicit.pgc"
if( sqlca.sqlcode != 0 )
{
@@ -173,10 +179,10 @@ if (sqlca.sqlcode < 0) sqlprint();}
return( NULL );
}
{ ECPGtrans(__LINE__, NULL, "begin");
-#line 119 "thread_implicit.pgc"
+#line 125 "thread_implicit.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 119 "thread_implicit.pgc"
+#line 125 "thread_implicit.pgc"
/* insert into test_thread table */
@@ -187,10 +193,10 @@ if (sqlca.sqlcode < 0) sqlprint();}
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
ECPGt_int,&(l_i),(long)1,(long)1,sizeof(int),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
-#line 124 "thread_implicit.pgc"
+#line 130 "thread_implicit.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 124 "thread_implicit.pgc"
+#line 130 "thread_implicit.pgc"
if( sqlca.sqlcode != 0 )
printf("%s: ERROR: insert failed!\n", l_connection);
@@ -198,16 +204,16 @@ if (sqlca.sqlcode < 0) sqlprint();}
/* all done */
{ ECPGtrans(__LINE__, NULL, "commit");
-#line 130 "thread_implicit.pgc"
+#line 136 "thread_implicit.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 130 "thread_implicit.pgc"
+#line 136 "thread_implicit.pgc"
{ ECPGdisconnect(__LINE__, l_connection);
-#line 131 "thread_implicit.pgc"
+#line 137 "thread_implicit.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 131 "thread_implicit.pgc"
+#line 137 "thread_implicit.pgc"
return( NULL );
}
diff --git a/src/interfaces/ecpg/test/thread/alloc.pgc b/src/interfaces/ecpg/test/thread/alloc.pgc
index ea98495..8e6d042 100644
--- a/src/interfaces/ecpg/test/thread/alloc.pgc
+++ b/src/interfaces/ecpg/test/thread/alloc.pgc
@@ -13,6 +13,7 @@ main(void)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>
+#include <locale.h>
#else
#include <pthread.h>
#endif
@@ -35,6 +36,10 @@ static void* fn(void* arg)
{
int i;
+#ifdef WIN32
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+#endif
+
EXEC SQL BEGIN DECLARE SECTION;
int value;
char name[100];
diff --git a/src/interfaces/ecpg/test/thread/descriptor.pgc b/src/interfaces/ecpg/test/thread/descriptor.pgc
index e07a5e2..c88c05a 100644
--- a/src/interfaces/ecpg/test/thread/descriptor.pgc
+++ b/src/interfaces/ecpg/test/thread/descriptor.pgc
@@ -3,6 +3,7 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>
+#include <locale.h>
#else
#include <pthread.h>
#endif
@@ -24,6 +25,10 @@ static void* fn(void* arg)
{
int i;
+#ifdef WIN32
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+#endif
+
for (i = 1; i <= REPEATS; ++i)
{
EXEC SQL ALLOCATE DESCRIPTOR mydesc;
diff --git a/src/interfaces/ecpg/test/thread/prep.pgc b/src/interfaces/ecpg/test/thread/prep.pgc
index 45205dd..1ec9676 100644
--- a/src/interfaces/ecpg/test/thread/prep.pgc
+++ b/src/interfaces/ecpg/test/thread/prep.pgc
@@ -13,6 +13,7 @@ main(void)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>
+#include <locale.h>
#else
#include <pthread.h>
#endif
@@ -35,6 +36,10 @@ static void* fn(void* arg)
{
int i;
+#ifdef WIN32
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+#endif
+
EXEC SQL BEGIN DECLARE SECTION;
int value;
char name[100];
diff --git a/src/interfaces/ecpg/test/thread/thread.pgc b/src/interfaces/ecpg/test/thread/thread.pgc
index cc23b82..f08aacd 100644
--- a/src/interfaces/ecpg/test/thread/thread.pgc
+++ b/src/interfaces/ecpg/test/thread/thread.pgc
@@ -17,6 +17,7 @@ main(void)
#include <pthread.h>
#else
#include <windows.h>
+#include <locale.h>
#endif
exec sql include ../regression;
@@ -97,6 +98,11 @@ int main()
void *test_thread(void *arg)
{
long threadnum = (long)arg;
+
+#ifdef WIN32
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+#endif
+
EXEC SQL BEGIN DECLARE SECTION;
int l_i;
char l_connection[128];
diff --git a/src/interfaces/ecpg/test/thread/thread_implicit.pgc b/src/interfaces/ecpg/test/thread/thread_implicit.pgc
index 96e0e99..aab758e 100644
--- a/src/interfaces/ecpg/test/thread/thread_implicit.pgc
+++ b/src/interfaces/ecpg/test/thread/thread_implicit.pgc
@@ -18,6 +18,7 @@ main(void)
#include <pthread.h>
#else
#include <windows.h>
+#include <locale.h>
#endif
exec sql include ../regression;
@@ -98,6 +99,11 @@ int main()
void *test_thread(void *arg)
{
long threadnum = (long)arg;
+
+#ifdef WIN32
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+#endif
+
EXEC SQL BEGIN DECLARE SECTION;
int l_i;
char l_connection[128];
--
2.10.0.windows.1