]> granicus.if.org Git - postgresql/commitdiff
pg_upgrade: fix -j race condition on Windows
authorBruce Momjian <bruce@momjian.us>
Sat, 27 Jul 2013 19:00:58 +0000 (15:00 -0400)
committerBruce Momjian <bruce@momjian.us>
Sat, 27 Jul 2013 19:00:58 +0000 (15:00 -0400)
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

index ef123a8eef3e8845b3ece12a5338fae2bbf7ac81..44f6a756be30f6bf71b4cc3468944afce8dec886 100644 (file)
@@ -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