Use gmtime_r() and localtime_r() instead of gmtime() and localtime(),
for thread-safety.
There are a few affected calls in libpq and ecpg's libpgtypes, which
are probably effectively bugs, because those libraries already claim
to be thread-safe.
There is one affected call in the backend. Most of the backend
otherwise uses the custom functions pg_gmtime() and pg_localtime(),
which are implemented differently.
While we're here, change the call in the backend to gmtime*() instead
of localtime*(), since for that use time zone behavior is irrelevant,
and this side-steps any questions about when time zones are
initialized by localtime_r() vs localtime().
Portability: gmtime_r() and localtime_r() are in POSIX but are not
available on Windows. Windows has functions gmtime_s() and
localtime_s() that can fulfill the same purpose, so we add some small
wrappers around them. (Note that these *_s() functions are also
different from the *_s() functions in the bounds-checking extension of
C11. We are not using those here.)
On MinGW, you can get the POSIX-style *_r() functions by defining
_POSIX_C_SOURCE appropriately before including <time.h>. This leads
to a conflict at least in plpython because apparently _POSIX_C_SOURCE
gets defined in some header there, and then our replacement
definitions conflict with the system definitions. To avoid that sort
of thing, we now always define _POSIX_C_SOURCE on MinGW and use the
POSIX-style functions here.
Reviewed-by: Stepan Neretin <[email protected]>
Reviewed-by: Heikki Linnakangas <[email protected]>
Reviewed-by: Thomas Munro <[email protected]>
Discussion: https://www.postgresql.org/message-id/flat/
eba1dc75-298e-4c46-8869-
48ba8aad7d70@eisentraut.org
exesuffix = '.exe'
dlsuffix = '.dll'
library_path_var = ''
+ if cc.get_id() != 'msvc'
+ # define before including <time.h> for getting localtime_r() etc. on MinGW
+ cppflags += '-D_POSIX_C_SOURCE'
+ endif
export_file_format = 'win'
export_file_suffix = 'def'
char *bufptr;
time_t timenow;
struct tm *timeinfo;
+ struct tm timeinfobuf;
bool strftimefail = false;
int encoding;
int i;
/* We use times close to current time as data for strftime(). */
timenow = time(NULL);
- timeinfo = localtime(&timenow);
+ timeinfo = gmtime_r(&timenow, &timeinfobuf);
/* Store the strftime results in MAX_L10N_DATA-sized portions of buf[] */
bufptr = buf;
*/
#define strtok_r strtok_s
+/*
+ * Supplement to <time.h>.
+ */
+#ifdef _MSC_VER
+/*
+ * MinGW has these functions if _POSIX_C_SOURCE is defined. Third-party
+ * libraries might do that, so to avoid clashes we get ahead of it and define
+ * it ourselves and use the system functions provided by MinGW.
+ */
+#define gmtime_r(clock, result) (gmtime_s(result, clock) ? NULL : (result))
+#define localtime_r(clock, result) (localtime_s(result, clock) ? NULL : (result))
+#endif
+
/*
* Locale stuff.
*
GetEpochTime(struct tm *tm)
{
struct tm *t0;
+ struct tm tmbuf;
time_t epoch = 0;
- t0 = gmtime(&epoch);
+ t0 = gmtime_r(&epoch, &tmbuf);
if (t0)
{
{
time_t time = (time_t) _time;
struct tm *tx;
+ struct tm tmbuf;
errno = 0;
if (tzp != NULL)
- tx = localtime((time_t *) &time);
+ tx = localtime_r(&time, &tmbuf);
else
- tx = gmtime((time_t *) &time);
+ tx = gmtime_r(&time, &tmbuf);
if (!tx)
{
/* number of seconds in scan_val.luint_val */
{
struct tm *tms;
+ struct tm tmbuf;
time_t et = (time_t) scan_val.luint_val;
- tms = gmtime(&et);
+ tms = gmtime_r(&et, &tmbuf);
if (tms)
{
if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
{
#if defined(HAVE_STRUCT_TM_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
+ struct tm tmbuf;
utime = dt / USECS_PER_SEC +
((date0 - date2j(1970, 1, 1)) * INT64CONST(86400));
- tx = localtime(&utime);
+ tx = localtime_r(&utime, &tmbuf);
tm->tm_year = tx->tm_year + 1900;
tm->tm_mon = tx->tm_mon + 1;
tm->tm_mday = tx->tm_mday;
{
struct timeval tval;
time_t now;
+ struct tm tmbuf;
gettimeofday(&tval, NULL);
now = tval.tv_sec;
strftime(timestr, ts_len,
"%Y-%m-%d %H:%M:%S",
- localtime(&now));
+ localtime_r(&now, &tmbuf));
/* append microseconds */
snprintf(timestr + strlen(timestr), ts_len - strlen(timestr),
".%06u", (unsigned int) (tval.tv_usec));
# src/template/win32
+# define before including <time.h> for getting localtime_r() etc. on MinGW
+CPPFLAGS="$CPPFLAGS -D_POSIX_C_SOURCE"
+
# Extra CFLAGS for code that will go into a shared library
CFLAGS_SL=""