From 815fcd050fbe18976c51af59116d60a6be5f3e41 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Sat, 27 Jul 2013 15:00:58 -0400 Subject: [PATCH] pg_upgrade: fix -j race condition on Windows Pg_Upgrade cannot write the command string to the log file and then call system() to write to the same file without causing occasional file-share errors on Windows. So instead, write the command string to the log file after system(), in those cases. Backpatch to 9.3. --- contrib/pg_upgrade/exec.c | 46 ++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/contrib/pg_upgrade/exec.c b/contrib/pg_upgrade/exec.c index ef123a8eef..44f6a756be 100644 --- a/contrib/pg_upgrade/exec.c +++ b/contrib/pg_upgrade/exec.c @@ -37,12 +37,14 @@ static int win32_check_directory_write_permissions(void); * If throw_error is true, this raises a PG_FATAL error and pg_upgrade * terminates; otherwise it is just reported as PG_REPORT and exec_prog() * returns false. + * + * The code requires it be called first from the primary thread on Windows. */ bool exec_prog(const char *log_file, const char *opt_log_file, bool throw_error, const char *fmt,...) { - int result; + int result = 0; int written; #define MAXCMDLEN (2 * MAXPGPATH) @@ -50,6 +52,14 @@ exec_prog(const char *log_file, const char *opt_log_file, FILE *log; va_list ap; +#ifdef WIN32 +static DWORD mainThreadId = 0; + + /* We assume we are called from the primary thread first */ + if (mainThreadId == 0) + mainThreadId = GetCurrentThreadId(); +#endif + written = strlcpy(cmd, SYSTEMQUOTE, sizeof(cmd)); va_start(ap, fmt); written += vsnprintf(cmd + written, MAXCMDLEN - written, fmt, ap); @@ -61,6 +71,22 @@ exec_prog(const char *log_file, const char *opt_log_file, if (written >= MAXCMDLEN) pg_log(PG_FATAL, "command too long\n"); + pg_log(PG_VERBOSE, "%s\n", cmd); + +#ifdef WIN32 + /* + * For some reason, Windows issues a file-in-use error if we write data + * to the log file from a non-primary thread just before we create a + * subprocess that also writes to the same log file. One fix is to + * sleep for 100ms. A cleaner fix is to write to the log file _after_ + * the subprocess has completed, so we do this only when writing from + * a non-primary thread. fflush(), running system() twice, and + * pre-creating the file do not see to help. + */ + if (mainThreadId != GetCurrentThreadId()) + result = system(cmd); +#endif + log = fopen(log_file, "a"); #ifdef WIN32 @@ -84,11 +110,18 @@ exec_prog(const char *log_file, const char *opt_log_file, if (log == NULL) pg_log(PG_FATAL, "cannot write to log file %s\n", log_file); + #ifdef WIN32 - fprintf(log, "\n\n"); + /* Are we printing "command:" before its output? */ + if (mainThreadId == GetCurrentThreadId()) + fprintf(log, "\n\n"); #endif - pg_log(PG_VERBOSE, "%s\n", cmd); fprintf(log, "command: %s\n", cmd); +#ifdef WIN32 + /* Are we printing "command:" after its output? */ + if (mainThreadId != GetCurrentThreadId()) + fprintf(log, "\n\n"); +#endif /* * In Windows, we must close the log file at this point so the file is not @@ -96,7 +129,11 @@ exec_prog(const char *log_file, const char *opt_log_file, */ fclose(log); - result = system(cmd); +#ifdef WIN32 + /* see comment above */ + if (mainThreadId == GetCurrentThreadId()) +#endif + result = system(cmd); if (result != 0) { @@ -118,7 +155,6 @@ exec_prog(const char *log_file, const char *opt_log_file, } #ifndef WIN32 - /* * We can't do this on Windows because it will keep the "pg_ctl start" * output filename open until the server stops, so we do the \n\n above on