top-level invocation of GNU make, or via MAKEFLAGS or GNUMAKEFLAGS.
To detect this change search for 'jobserver-fifo' in the .FEATURES variable.
+* Some POSIX systems (*BSD) do not allow locks to be taken on pipes, which
+ caused the output sync feature to not work properly there. Also multiple
+ invocations of make redirecting to the same output file (e.g., /dev/null)
+ would cause hangs. Instead of locking stdout (which does have some useful
+ performance characteristics, but is not portable) create a temporary file
+ and lock that. Windows continues to use a mutex as before.
+
* GNU make has sometimes chosen unexpected, and sub-optimal, chains of
implicit rules due to the definition of "ought to exist" in the implicit
rule search algorithm, which considered any prerequisite mentioned in the
# include <process.h>
#endif
+#if defined (HAVE_FCNTL_H)
+# include <fcntl.h>
+#endif
+
#if defined (HAVE_SYS_WAIT_H) || defined (HAVE_UNION_WAIT)
# include <sys/wait.h>
#endif
}
else
{
-#ifndef NO_OUTPUT_SYNC
/* If we're sync'ing per line, write the previous line's
output before starting the next one. */
if (output_sync == OUTPUT_SYNC_LINE)
output_dump (&c->output);
-#endif
/* Check again whether to start remotely.
Whether or not we want to changes over time.
Also, start_remote_job may need state set up
/* When we get here, all the commands for c->file are finished. */
-#ifndef NO_OUTPUT_SYNC
/* Synchronize any remaining parallel output. */
output_dump (&c->output);
-#endif
/* At this point c->file->update_status is success or failed. But
c->file->command_state is still cs_running if all the commands
OUTPUT_SET (&child->output);
-#ifndef NO_OUTPUT_SYNC
if (! child->output.syncout)
/* We don't want to sync this command: to avoid misordered
output ensure any already-synced content is written. */
output_dump (&child->output);
-#endif
/* Print the command if appropriate. */
if (just_print_flag || ISDB (DB_PRINT)
{
HANDLE hPID;
char* arg0;
- int outfd = FD_STDOUT;
- int errfd = FD_STDERR;
+ int outfd = -1;
+ int errfd = -1;
/* make UNC paths safe for CreateProcess -- backslash format */
arg0 = argv[0];
/* make sure CreateProcess() has Path it needs */
sync_Path_environment ();
-#ifndef NO_OUTPUT_SYNC
/* Divert child output if output_sync in use. */
if (child->output.syncout)
{
if (child->output.err >= 0)
errfd = child->output.err;
}
-#else
- outfd = errfd = -1;
-#endif
+
hPID = process_easy (argv, child->environment, outfd, errfd);
if (hPID != INVALID_HANDLE_VALUE)
static char *shuffle_mode = NULL;
-/* Handle for the mutex used on Windows to synchronize output of our
- children under -O. */
+/* Handle for the mutex to synchronize output of our children under -O. */
static char *sync_mutex = NULL;
}
if (sync_mutex)
- RECORD_SYNC_MUTEX (sync_mutex);
+ osync_parse_mutex (sync_mutex);
#endif
}
#ifdef WINDOWS32
-#ifndef NO_OUTPUT_SYNC
-
-/* This is called from start_job_command when it detects that
- output_sync option is in effect. The handle to the synchronization
- mutex is passed, as a string, to sub-makes via the --sync-mutex
- command-line argument. */
-void
-prepare_mutex_handle_string (sync_handle_t handle)
-{
- if (!sync_mutex)
- {
- /* Prepare the mutex handle string for our children. */
- /* 2 hex digits per byte + 2 characters for "0x" + null. */
- sync_mutex = xmalloc ((2 * sizeof (sync_handle_t)) + 2 + 1);
- sprintf (sync_mutex, "0x%Ix", handle);
- define_makeflags (1, 0);
- }
-}
-
-#endif /* NO_OUTPUT_SYNC */
-
/*
* HANDLE runtime exceptions by avoiding a requestor on the GUI. Capture
* exception and print it to stderr instead.
#endif
#ifdef MAKE_JOBSERVER
" jobserver"
-#ifdef HAVE_MKFIFO
+# ifdef HAVE_MKFIFO
" jobserver-fifo"
-#endif
+# endif
#endif
#ifndef NO_OUTPUT_SYNC
" output-sync"
output_sync = OUTPUT_SYNC_NONE;
}
+ if (syncing)
+ {
+ /* If there a mutex we're the child, else we're the origin. */
+ if (!sync_mutex)
+ {
+ osync_setup ();
+ sync_mutex = osync_get_mutex ();
+ }
+ else if (!osync_parse_mutex (sync_mutex))
+ {
+ osync_clear ();
+ free (sync_mutex);
+ sync_mutex = NULL;
+ }
+ }
+
#ifndef MAKE_SYMLINKS
if (check_symlink_flag)
{
output_close (NULL);
+ osync_clear ();
+
/* Try to move back to the original directory. This is essential on
MS-DOS (where there is really only one process), and on Unix it
puts core files in the original directory instead of the -C
# define WIN32_LEAN_AND_MEAN
#endif /* WINDOWS32 */
+/* ALL_SET() evaluates the second argument twice. */
#define ANY_SET(_v,_m) (((_v)&(_m)) != 0)
#define NONE_SET(_v,_m) (! ANY_SET ((_v),(_m)))
+#define ALL_SET(_v,_m) (((_v)&(_m)) == (_m))
#define MAP_NUL 0x0001
#define MAP_BLANK 0x0002 /* space, TAB */
#ifdef VMS
# define DEFAULT_TMPFILE "sys$scratch:gnv$make_cmdXXXXXX.com"
-#else
-# define DEFAULT_TMPFILE "GmXXXXXX"
-#endif
-
-#ifdef VMS
# define DEFAULT_TMPDIR "/sys$scratch/"
#else
+# define DEFAULT_TMPFILE "GmXXXXXX"
# ifdef P_tmpdir
# define DEFAULT_TMPDIR P_tmpdir
# else
this program. If not, see <http://www.gnu.org/licenses/>. */
+#define IO_UNKNOWN 0x0001
+#define IO_COMBINED_OUTERR 0x0002
+#define IO_STDIN_OK 0x0004
+#define IO_STDOUT_OK 0x0008
+#define IO_STDERR_OK 0x0010
+
+#if defined(VMS) || defined(_AMIGA) || defined(__MSDOS__)
+# define check_io_state() (IO_STDIN_OK|IO_STDOUT_OK|IO_STDERR_OK)
+# define fd_inherit(_i) (0)
+# define fd_noinherit(_i) (0)
+# define fd_set_append(_i) (void)(0)
+#else
+
+/* Determine the state of stdin/stdout/stderr. */
+unsigned int check_io_state ();
+
+/* Set a file descriptor to close/not close in a subprocess. */
+void fd_inherit (int);
+void fd_noinherit (int);
+
+/* If the file descriptor is for a file put it into append mode. */
+void fd_set_append (int);
+#endif
+
/* Return a file descriptor for a new anonymous temp file, or -1. */
#if defined(WINDOWS32)
int os_anontmp ();
/* Returns 1 if the jobserver is enabled, else 0. */
unsigned int jobserver_enabled ();
-/* Called in the master instance to set up the jobserver initially. */
+/* Called in the parent make to set up the jobserver initially. */
unsigned int jobserver_setup (int job_slots, const char *style);
/* Called in a child instance to connect to the jobserver.
#define jobserver_setup(_slots, _style) (0)
#define jobserver_parse_auth(_auth) (0)
#define jobserver_get_auth() (NULL)
+#define jobserver_get_invalid_auth() (NULL)
#define jobserver_clear() (void)(0)
#define jobserver_release(_fatal) (void)(0)
#define jobserver_acquire_all() (0)
#define jobserver_pre_acquire() (void)(0)
#define jobserver_acquire(_tmout) (0)
-#endif
+#endif /* MAKE_JOBSERVER */
+
+#ifndef NO_OUTPUT_SYNC
+
+/* Returns 1 if output sync is enabled, else 0. */
+unsigned int osync_enabled ();
+
+/* Called in the parent make to set up output sync initially. */
+void osync_setup ();
+
+/* Returns an allocated buffer containing output sync info to pass to child
+ instances, or NULL if not needed. */
+char *osync_get_mutex ();
+
+/* Called in a child instance to obtain info on the output sync mutex.
+ Return 1 if we got a valid mutex, else 0. */
+unsigned int osync_parse_mutex (const char *mutex);
+
+/* Clean up this instance's output sync facilities. */
+void osync_clear ();
+
+/* Acquire the output sync lock. This will wait until available.
+ Returns 0 if there was an error getting the semaphore. */
+unsigned int osync_acquire ();
+
+/* Release the output sync lock. */
+void osync_release ();
+
+#else
+
+#define osync_enabled() (0)
+#define osync_setup() (void)(0)
+#define osync_get_mutex() (0)
+#define osync_parse_mutex(_s) (0)
+#define osync_clear() (void)(0)
+#define osync_acquire() (1)
+#define osync_release() (void)(0)
+
+#endif /* NO_OUTPUT_SYNC */
/* Create a "bad" file descriptor for stdin when parallel jobs are run. */
#if defined(VMS) || defined(WINDOWS32) || defined(_AMIGA) || defined(__MSDOS__)
#else
int get_bad_stdin ();
#endif
-
-/* Set a file descriptor to close/not close in a subprocess. */
-#if defined(VMS) || defined(_AMIGA) || defined(__MSDOS__)
-# define fd_inherit(_i) 0
-# define fd_noinherit(_i) 0
-#else
-void fd_inherit (int);
-void fd_noinherit (int);
-#endif
#define OUTPUT_ISSET(_out) ((_out)->out >= 0 || (_out)->err >= 0)
-#ifdef HAVE_FCNTL_H
-# define STREAM_OK(_s) ((fcntl (fileno (_s), F_GETFD) != -1) || (errno != EBADF))
-#else
-# define STREAM_OK(_s) 1
-#endif
-
/* Write a string to the current STDOUT or STDERR. */
static void
_outputs (struct output *out, int is_err, const char *msg)
return 1;
}
-
-/* Set a file descriptor referring to a regular file
- to be in O_APPEND mode. If it fails, just ignore it. */
-
-static void
-set_append_mode (int fd)
-{
-#if defined(F_GETFL) && defined(F_SETFL) && defined(O_APPEND)
- struct stat stbuf;
- int flags;
- if (fstat (fd, &stbuf) != 0 || !S_ISREG (stbuf.st_mode))
- return;
- flags = fcntl (fd, F_GETFL, 0);
- if (flags >= 0)
- {
- int r;
- EINTRLOOP(r, fcntl (fd, F_SETFL, flags | O_APPEND));
- }
-#endif
-}
\f
#ifndef NO_OUTPUT_SYNC
-/* Semaphore for use in -j mode with output_sync. */
-static sync_handle_t sync_handle = -1;
-
-#define FD_NOT_EMPTY(_f) ((_f) != OUTPUT_NONE && lseek ((_f), 0, SEEK_END) > 0)
-
-/* Set up the sync handle. Disables output_sync on error. */
-static int
-sync_init (void)
-{
- int combined_output = 0;
-
-#ifdef WINDOWS32
- if ((!STREAM_OK (stdout) && !STREAM_OK (stderr))
- || (sync_handle = create_mutex ()) == -1)
- {
- perror_with_name ("output-sync suppressed: ", "stderr");
- output_sync = 0;
- }
- else
- {
- combined_output = same_stream (stdout, stderr);
- prepare_mutex_handle_string (sync_handle);
- }
-
-#else
- if (STREAM_OK (stdout))
- {
- struct stat stbuf_o, stbuf_e;
-
- sync_handle = fileno (stdout);
- combined_output = (fstat (fileno (stdout), &stbuf_o) == 0
- && fstat (fileno (stderr), &stbuf_e) == 0
- && stbuf_o.st_dev == stbuf_e.st_dev
- && stbuf_o.st_ino == stbuf_e.st_ino);
- }
- else if (STREAM_OK (stderr))
- sync_handle = fileno (stderr);
- else
- {
- perror_with_name ("output-sync suppressed: ", "stderr");
- output_sync = 0;
- }
-#endif
-
- return combined_output;
-}
-
/* Support routine for output_sync() */
static void
pump_from_tmp (int from, FILE *to)
#endif
}
-/* Obtain the lock for writing output. */
-static void *
-acquire_semaphore (void)
-{
- static struct flock fl;
-
- fl.l_type = F_WRLCK;
- fl.l_whence = SEEK_SET;
- fl.l_start = 0;
- fl.l_len = 1;
- if (fcntl (sync_handle, F_SETLKW, &fl) != -1)
- return &fl;
- perror ("fcntl()");
- return NULL;
-}
-
-/* Release the lock for writing output. */
-static void
-release_semaphore (void *sem)
-{
- struct flock *flp = (struct flock *)sem;
- flp->l_type = F_UNLCK;
- if (fcntl (sync_handle, F_SETLKW, flp) == -1)
- perror ("fcntl()");
-}
-
-/* Returns a file descriptor to a temporary file. The file is automatically
- closed/deleted on exit. Don't use a FILE* stream. */
+/* Returns a file descriptor to a temporary file, that will be automatically
+ deleted on exit. */
int
output_tmpfd (void)
{
int fd = get_tmpfd (NULL);
- set_append_mode (fd);
+ fd_set_append (fd);
return fd;
}
static void
setup_tmpfile (struct output *out)
{
- /* Is make's stdout going to the same place as stderr? */
- static int combined_output = -1;
+ unsigned int io_state = check_io_state ();
- if (combined_output < 0)
- combined_output = sync_init ();
+ if (NONE_SET (io_state, IO_STDOUT_OK|IO_STDERR_OK))
+ {
+ /* This is probably useless since stdout/stderr aren't working. */
+ perror_with_name ("output-sync suppressed: ", "stderr");
+ goto error;
+ }
- if (STREAM_OK (stdout))
+ if (ANY_SET (io_state, IO_STDOUT_OK))
{
int fd = output_tmpfd ();
if (fd < 0)
out->out = fd;
}
- if (STREAM_OK (stderr))
+ if (ANY_SET (io_state, IO_STDERR_OK))
{
- if (out->out != OUTPUT_NONE && combined_output)
+ if (out->out != OUTPUT_NONE && ANY_SET (io_state, IO_COMBINED_OUTERR))
out->err = out->out;
else
{
error:
output_close (out);
output_sync = OUTPUT_SYNC_NONE;
+ osync_clear ();
}
/* Synchronize the output of jobs in -j mode to keep the results of
void
output_dump (struct output *out)
{
+#define FD_NOT_EMPTY(_f) ((_f) != OUTPUT_NONE && lseek ((_f), 0, SEEK_END) > 0)
+
int outfd_not_empty = FD_NOT_EMPTY (out->out);
int errfd_not_empty = FD_NOT_EMPTY (out->err);
/* Try to acquire the semaphore. If it fails, dump the output
unsynchronized; still better than silently discarding it.
We want to keep this lock for as little time as possible. */
- void *sem = acquire_semaphore ();
+ if (!osync_acquire ())
+ {
+ O (error, NILF,
+ _("warning: Cannot acquire output lock, disabling output sync."));
+ osync_clear ();
+ }
/* Log the working directory for this dump. */
if (print_directory && output_sync != OUTPUT_SYNC_RECURSE)
log_working_directory (0);
/* Exit the critical section. */
- if (sem)
- release_semaphore (sem);
+ osync_release ();
/* Truncate and reset the output, in case we use it again. */
if (out->out != OUTPUT_NONE)
/* Force stdout/stderr into append mode. This ensures parallel jobs won't
lose output due to overlapping writes. */
- set_append_mode (fileno (stdout));
- set_append_mode (fileno (stderr));
+ fd_set_append (fileno (stdout));
+ fd_set_append (fileno (stderr));
#ifdef HAVE_ATEXIT
- if (STREAM_OK (stdout))
+ if (ANY_SET (check_io_state (), IO_STDOUT_OK))
atexit (close_stdout);
#endif
}
/* Show a message on stdout or stderr. Will start the output if needed. */
void outputs (int is_err, const char *msg);
-#if defined(HAVE_FCNTL_H)
-# include <fcntl.h>
-#elif defined(HAVE_SYS_FILE_H)
-# include <sys/file.h>
-#endif
-
-#ifdef NO_OUTPUT_SYNC
-# define RECORD_SYNC_MUTEX(m) \
- O (error, NILF, \
- _("-O[TYPE] (--output-sync[=TYPE]) is not configured for this build."));
+#if defined(NO_OUTPUT_SYNC)
+# define output_dump(_o) (void)(0)
#else
-int output_tmpfd (void);
/* Dump any child output content to stdout, and reset it. */
void output_dump (struct output *out);
-
-# ifdef WINDOWS32
-/* For emulations in w32/compat/posixfcn.c. */
-# ifndef F_GETFD
-# define F_GETFD 1
-# endif
-# ifndef F_SETLKW
-# define F_SETLKW 2
-# endif
-/* Implementation note: None of the values of l_type below can be zero
- -- they are compared with a static instance of the struct, so zero
- means unknown/invalid, see w32/compat/posixfcn.c. */
-# ifndef F_WRLCK
-# define F_WRLCK 1
-# endif
-# ifndef F_UNLCK
-# define F_UNLCK 2
-# endif
-struct flock
- {
- short l_type;
- short l_whence;
- off_t l_start;
- off_t l_len;
- pid_t l_pid;
- };
-
-/* This type is actually a HANDLE, but we want to avoid including
- windows.h as much as possible. */
-typedef intptr_t sync_handle_t;
-
-/* Public functions emulated/provided in posixfcn.c. */
-# if !defined(GNULIB_defined_rpl_fcntl) && !defined(GNULIB_defined_fcntl)
-int fcntl (intptr_t fd, int cmd, ...);
-# endif
-intptr_t create_mutex (void);
-int same_stream (FILE *f1, FILE *f2);
-
-# define RECORD_SYNC_MUTEX(m) record_sync_mutex(m)
-void record_sync_mutex (const char *str);
-void prepare_mutex_handle_string (intptr_t hdl);
-# else /* !WINDOWS32 */
-
-typedef int sync_handle_t; /* file descriptor */
-
-# define RECORD_SYNC_MUTEX(m) (void)(m)
-
-# endif
-#endif /* !NO_OUTPUT_SYNC */
+#endif
#include "job.h"
#include "os.h"
-#ifdef MAKE_JOBSERVER
+#define STREAM_OK(_s) ((fcntl (fileno (_s), F_GETFD) != -1) || (errno != EBADF))
+
+unsigned int
+check_io_state ()
+{
+ static unsigned int state = IO_UNKNOWN;
+
+ /* We only need to compute this once per process. */
+ if (state != IO_UNKNOWN)
+ return state;
+
+ if (STREAM_OK (stdin))
+ state |= IO_STDIN_OK;
+ if (STREAM_OK (stdout))
+ state |= IO_STDOUT_OK;
+ if (STREAM_OK (stderr))
+ state |= IO_STDERR_OK;
+
+ if (ALL_SET (state, IO_STDOUT_OK|IO_STDERR_OK))
+ {
+ struct stat stbuf_o, stbuf_e;
+
+ if (fstat (fileno (stdout), &stbuf_o) == 0
+ && fstat (fileno (stderr), &stbuf_e) == 0
+ && stbuf_o.st_dev == stbuf_e.st_dev
+ && stbuf_o.st_ino == stbuf_e.st_ino)
+ state |= IO_COMBINED_OUTERR;
+ }
+
+ return state;
+}
+
+#if defined(MAKE_JOBSERVER)
#define FIFO_PREFIX "fifo:"
pfatal_with_name (_("read jobs pipe"));
}
- /* read() should never return 0: only the master make can reap all the
+ /* read() should never return 0: only the parent make can reap all the
tokens and close the write side...?? */
return r > 0;
}
#endif /* MAKE_JOBSERVER */
+#if !defined(NO_OUTPUT_SYNC)
+
+#define MUTEX_PREFIX "fnm:"
+
+static int osync_handle = -1;
+
+static char *osync_tmpfile = NULL;
+
+static unsigned int sync_parent = 0;
+
+unsigned int
+osync_enabled ()
+{
+ return osync_handle >= 0;
+}
+
+void
+osync_setup ()
+{
+ osync_handle = get_tmpfd (&osync_tmpfile);
+ if (osync_handle >= 0)
+ sync_parent = 1;
+}
+
+char *
+osync_get_mutex ()
+{
+ char *mutex = NULL;
+
+ if (osync_enabled ())
+ {
+ /* Prepare the mutex handle string for our children. */
+ mutex = xmalloc (strlen (osync_tmpfile) + CSTRLEN (MUTEX_PREFIX) + 1);
+ sprintf (mutex, MUTEX_PREFIX "%s", osync_tmpfile);
+ }
+
+ return mutex;
+}
+
+unsigned int
+osync_parse_mutex (const char *mutex)
+{
+ if (strncmp (mutex, MUTEX_PREFIX, CSTRLEN (MUTEX_PREFIX)) != 0)
+ {
+ OS (error, NILF, _("invalid --sync-mutex string '%s'"), mutex);
+ return 0;
+ }
+
+ osync_tmpfile = xstrdup (mutex + CSTRLEN (MUTEX_PREFIX));
+
+ EINTRLOOP (osync_handle, open (osync_tmpfile, O_WRONLY));
+ if (osync_handle < 0)
+ OSS (fatal, NILF, _("cannot open output sync mutex %s: %s"),
+ osync_tmpfile, strerror (errno));
+
+ return 1;
+}
+
+void
+osync_clear ()
+{
+ if (osync_handle)
+ {
+ close (osync_handle);
+ osync_handle = -1;
+ }
+
+ if (sync_parent && osync_tmpfile)
+ {
+ unlink (osync_tmpfile);
+ osync_tmpfile = NULL;
+ }
+}
+
+unsigned int
+osync_acquire ()
+{
+ if (osync_enabled())
+ {
+ struct flock fl;
+
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 1;
+ if (fcntl (osync_handle, F_SETLKW, &fl) == -1)
+ {
+ perror ("fcntl()");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+void
+osync_release ()
+{
+ if (osync_enabled())
+ {
+ struct flock fl;
+
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 1;
+ if (fcntl (osync_handle, F_SETLKW, &fl) == -1)
+ perror ("fcntl()");
+ }
+}
+
+#endif
+
/* Create a "bad" file descriptor for stdin when parallel jobs are run. */
int
get_bad_stdin ()
fd_noinherit (int fd)
{
int flags;
- EINTRLOOP(flags, fcntl(fd, F_GETFD));
+ EINTRLOOP (flags, fcntl(fd, F_GETFD));
if (flags >= 0)
{
int r;
flags |= FD_CLOEXEC;
- EINTRLOOP(r, fcntl(fd, F_SETFD, flags));
+ EINTRLOOP (r, fcntl(fd, F_SETFD, flags));
}
}
#endif
+
+/* Set a file descriptor referring to a regular file to be in O_APPEND mode.
+ If it fails, just ignore it. */
+
+void
+fd_set_append (int fd)
+{
+#if defined(F_GETFL) && defined(F_SETFL) && defined(O_APPEND)
+ struct stat stbuf;
+ int flags;
+ if (fstat (fd, &stbuf) == 0 && S_ISREG (stbuf.st_mode))
+ {
+ flags = fcntl (fd, F_GETFL, 0);
+ if (flags >= 0)
+ {
+ int r;
+ EINTRLOOP(r, fcntl (fd, F_SETFL, flags | O_APPEND));
+ }
+ }
+#endif
+}
#include "job.h"
-#ifndef NO_OUTPUT_SYNC
-/* Support for OUTPUT_SYNC and related functionality. */
-
-#if !defined(GNULIB_defined_rpl_fcntl) && !defined(GNULIB_defined_fcntl)
-/* Emulation of fcntl that supports only F_GETFD and F_SETLKW. */
-int
-fcntl (intptr_t fd, int cmd, ...)
-{
- va_list ap;
-
- va_start (ap, cmd);
-
- switch (cmd)
- {
- case F_GETFD:
- va_end (ap);
- /* Could have used GetHandleInformation, but that isn't
- supported on Windows 9X. */
- if (_get_osfhandle (fd) == -1)
- return -1;
- return 0;
- case F_SETLKW:
- {
- void *buf = va_arg (ap, void *);
- struct flock *fl = (struct flock *)buf;
- HANDLE hmutex = (HANDLE)fd;
- static struct flock last_fl;
- short last_type = last_fl.l_type;
-
- va_end (ap);
-
- if (hmutex == INVALID_HANDLE_VALUE || !hmutex)
- return -1;
-
- last_fl = *fl;
-
- switch (fl->l_type)
- {
-
- case F_WRLCK:
- {
- DWORD result;
-
- if (last_type == F_WRLCK)
- {
- /* Don't call WaitForSingleObject if we already
- own the mutex, because doing so will require
- us to call ReleaseMutex an equal number of
- times, before the mutex is actually
- released. */
- return 0;
- }
-
- result = WaitForSingleObject (hmutex, INFINITE);
- switch (result)
- {
- case WAIT_OBJECT_0:
- /* We don't care if the mutex owner crashed or
- exited. */
- case WAIT_ABANDONED:
- return 0;
- case WAIT_FAILED:
- case WAIT_TIMEOUT: /* cannot happen, really */
- {
- DWORD err = GetLastError ();
-
- /* Invalidate the last command. */
- memset (&last_fl, 0, sizeof (last_fl));
-
- switch (err)
- {
- case ERROR_INVALID_HANDLE:
- case ERROR_INVALID_FUNCTION:
- errno = EINVAL;
- return -1;
- default:
- errno = EDEADLOCK;
- return -1;
- }
- }
- }
- }
- case F_UNLCK:
- {
- /* FIXME: Perhaps we should call ReleaseMutex
- repatedly until it errors out, to make sure the
- mutext is released even if we somehow managed to
- to take ownership multiple times? */
- BOOL status = ReleaseMutex (hmutex);
-
- if (status)
- return 0;
- else
- {
- DWORD err = GetLastError ();
-
- if (err == ERROR_NOT_OWNER)
- errno = EPERM;
- else
- {
- memset (&last_fl, 0, sizeof (last_fl));
- errno = EINVAL;
- }
- return -1;
- }
- }
- default:
- errno = ENOSYS;
- return -1;
- }
- }
- default:
- errno = ENOSYS;
- va_end (ap);
- return -1;
- }
-}
-#endif /* GNULIB_defined_fcntl */
-
-static intptr_t mutex_handle = -1;
-
-/* Record in a static variable the mutex handle we were requested to
- use. That nameless mutex was created by the top-level Make, and
- its handle was passed to us via inheritance. The value of that
- handle is passed via the command-line arguments, so that we know
- which handle to use. */
-void
-record_sync_mutex (const char *str)
-{
- char *endp;
- intptr_t hmutex = strtol (str, &endp, 16);
-
- if (*endp == '\0')
- mutex_handle = hmutex;
- else
- {
- mutex_handle = -1;
- errno = EINVAL;
- }
-}
-
-/* Create a new mutex or reuse one created by our parent. */
-intptr_t
-create_mutex (void)
-{
- SECURITY_ATTRIBUTES secattr;
- intptr_t hmutex = -1;
-
- /* If we have a mutex handle passed from the parent Make, just use
- that. */
- if (mutex_handle > 0)
- return mutex_handle;
-
- /* We are the top-level Make, and we want the handle to be inherited
- by our child processes. */
- secattr.nLength = sizeof (secattr);
- secattr.lpSecurityDescriptor = NULL; /* use default security descriptor */
- secattr.bInheritHandle = TRUE;
-
- hmutex = (intptr_t)CreateMutex (&secattr, FALSE, NULL);
- if (!hmutex)
- {
- DWORD err = GetLastError ();
-
- fprintf (stderr, "CreateMutex: error %lu\n", err);
- errno = ENOLCK;
- hmutex = -1;
- }
-
- mutex_handle = hmutex;
- return hmutex;
-}
-
-/* Return non-zero if F1 and F2 are 2 streams representing the same
- file or pipe or device. */
-int
-same_stream (FILE *f1, FILE *f2)
-{
- HANDLE fh1 = (HANDLE)_get_osfhandle (fileno (f1));
- HANDLE fh2 = (HANDLE)_get_osfhandle (fileno (f2));
-
- /* Invalid file descriptors get treated as different streams. */
- if (fh1 && fh1 != INVALID_HANDLE_VALUE
- && fh2 && fh2 != INVALID_HANDLE_VALUE)
- {
- if (fh1 == fh2)
- return 1;
- else
- {
- DWORD ftyp1 = GetFileType (fh1), ftyp2 = GetFileType (fh2);
-
- if (ftyp1 != ftyp2
- || ftyp1 == FILE_TYPE_UNKNOWN || ftyp2 == FILE_TYPE_UNKNOWN)
- return 0;
- else if (ftyp1 == FILE_TYPE_CHAR)
- {
- /* For character devices, check if they both refer to a
- console. This loses if both handles refer to the
- null device (FIXME!), but in that case we don't care
- in the context of Make. */
- DWORD conmode1, conmode2;
-
- /* Each process on Windows can have at most 1 console,
- so if both handles are for the console device, they
- are the same. We also compare the console mode to
- distinguish between stdin and stdout/stderr. */
- if (GetConsoleMode (fh1, &conmode1)
- && GetConsoleMode (fh2, &conmode2)
- && conmode1 == conmode2)
- return 1;
- }
- else
- {
- /* For disk files and pipes, compare their unique
- attributes. */
- BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2;
-
- /* Pipes get zero in the volume serial number, but do
- appear to have meaningful information in file index
- attributes. We test file attributes as well, for a
- good measure. */
- if (GetFileInformationByHandle (fh1, &bhfi1)
- && GetFileInformationByHandle (fh2, &bhfi2))
- return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber
- && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow
- && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh
- && bhfi1.dwFileAttributes == bhfi2.dwFileAttributes);
- }
- }
- }
- return 0;
-}
-
-#endif /* !NO_OUTPUT_SYNC */
-
#if MAKE_LOAD
/* Support for dynamic loading of objects. */
-
static DWORD last_err;
void *
#include <windows.h>
#include <process.h>
#include <io.h>
+#include <synchapi.h>
#include "pathstuff.h"
#include "sub_proc.h"
#include "w32err.h"
#include "os.h"
#include "debug.h"
+unsigned int
+check_io_state ()
+{
+ static unsigned int state = IO_UNKNOWN;
+
+ /* We only need to compute this once per process. */
+ if (state != IO_UNKNOWN)
+ return state;
+
+ /* Could have used GetHandleInformation, but that isn't supported
+ on Windows 9X. */
+ HANDLE outfd = (HANDLE)_get_osfhandle (fileno (stdout));
+ HANDLE errfd = (HANDLE)_get_osfhandle (fileno (stderr));
+
+ if ((HANDLE)_get_osfhandle (fileno (stdin)) != INVALID_HANDLE_VALUE)
+ state |= IO_STDIN_OK;
+ if (outfd != INVALID_HANDLE_VALUE)
+ state |= IO_STDOUT_OK;
+ if (errfd != INVALID_HANDLE_VALUE)
+ state |= IO_STDERR_OK;
+
+ if (ALL_SET (state, IO_STDOUT_OK|IO_STDERR_OK))
+ {
+ unsigned int combined = 0;
+
+ if (outfd == errfd)
+ combined = IO_COMBINED_OUTERR;
+ else
+ {
+ DWORD outtype = GetFileType (outfd), errtype = GetFileType (errfd);
+
+ if (outtype == errtype
+ && outtype != FILE_TYPE_UNKNOWN && errtype != FILE_TYPE_UNKNOWN)
+ {
+ if (outtype == FILE_TYPE_CHAR)
+ {
+ /* For character devices, check if they both refer to a
+ console. This loses if both handles refer to the
+ null device (FIXME!), but in that case we don't care
+ in the context of Make. */
+ DWORD outmode, errmode;
+
+ /* Each process on Windows can have at most 1 console,
+ so if both handles are for the console device, they
+ are the same. We also compare the console mode to
+ distinguish between stdin and stdout/stderr. */
+ if (GetConsoleMode (outfd, &outmode)
+ && GetConsoleMode (errfd, &errmode)
+ && outmode == errmode)
+ combined = IO_COMBINED_OUTERR;
+ }
+ else
+ {
+ /* For disk files and pipes, compare their unique
+ attributes. */
+ BY_HANDLE_FILE_INFORMATION outfi, errfi;
+
+ /* Pipes get zero in the volume serial number, but do
+ appear to have meaningful information in file index
+ attributes. We test file attributes as well, for a
+ good measure. */
+ if (GetFileInformationByHandle (outfd, &outfi)
+ && GetFileInformationByHandle (errfd, &errfi)
+ && outfi.dwVolumeSerialNumber == errfi.dwVolumeSerialNumber
+ && outfi.nFileIndexLow == errfi.nFileIndexLow
+ && outfi.nFileIndexHigh == errfi.nFileIndexHigh
+ && outfi.dwFileAttributes == errfi.dwFileAttributes)
+ combined = IO_COMBINED_OUTERR;
+ }
+ }
+ }
+ state |= combined;
+ }
+
+ return state;
+}
+
/* A replacement for tmpfile, since the MSVCRT implementation creates
the file in the root directory of the current drive, which might
not be writable by our user, and also it returns a FILE* and we want a file
return -1;
}
+#if defined(MAKE_JOBSERVER)
+
/* This section provides OS-specific functions to support the jobserver. */
static char jobserver_semaphore_name[MAX_PATH + 1];
return dwEvent == WAIT_OBJECT_0;
}
+#endif /* MAKE_JOBSERVER */
+
+#if !defined(NO_OUTPUT_SYNC)
+
+#define MUTEX_PREFIX "fnm:"
+
+/* Since we're using this with CreateMutex, NULL is invalid. */
+static HANDLE osync_handle = NULL;
+
+unsigned int
+osync_enabled ()
+{
+ return osync_handle != NULL;
+}
+
+void
+osync_setup ()
+{
+ SECURITY_ATTRIBUTES secattr;
+
+ /* We are the top-level make, and we want the handle to be inherited
+ by our child processes. */
+ secattr.nLength = sizeof (secattr);
+ secattr.lpSecurityDescriptor = NULL; /* use default security descriptor */
+ secattr.bInheritHandle = TRUE;
+
+ osync_handle = CreateMutex (&secattr, FALSE, NULL);
+ if (!osync_handle)
+ {
+ DWORD err = GetLastError ();
+ fprintf (stderr, "CreateMutex: error %lu\n", err);
+ errno = ENOLCK;
+ }
+}
+
+char *
+osync_get_mutex ()
+{
+ char *mutex = NULL;
+
+ if (osync_enabled ())
+ {
+ /* Prepare the mutex handle string for our children.
+ 2 hex digits per byte + 2 characters for "0x" + null. */
+ mutex = xmalloc ((2 * sizeof (osync_handle)) + 2 + 1);
+ sprintf (mutex, "0x%Ix", (unsigned long long)osync_handle);
+ }
+
+ return mutex;
+}
+
+unsigned int
+osync_parse_mutex (const char *mutex)
+{
+ char *endp;
+ unsigned long long i;
+
+ errno = 0;
+ i = strtoull (mutex, &endp, 16);
+ if (errno != 0)
+ OSS (fatal, NILF, _("cannot parse output sync mutex %s: %s"),
+ mutex, strerror (errno));
+ if (endp[0] != '\0')
+ OS (fatal, NILF, _("invalid output sync mutex: %s"), mutex);
+
+ osync_handle = (HANDLE) i;
+
+ return 1;
+}
+
+void
+osync_clear ()
+{
+ if (osync_handle)
+ {
+ CloseHandle (osync_handle);
+ osync_handle = NULL;
+ }
+}
+
+unsigned int
+osync_acquire ()
+{
+ if (osync_enabled())
+ {
+ DWORD result = WaitForSingleObject (osync_handle, INFINITE);
+ if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+osync_release ()
+{
+ if (osync_enabled())
+ /* FIXME: Perhaps we should call ReleaseMutex repatedly until it errors
+ out, to make sure the mutext is released even if we somehow managed to
+ to take ownership multiple times? */
+ ReleaseMutex (osync_handle);
+}
+
+#endif /* NO_OUTPUT_SYNC */
+
void
fd_inherit(int fd)
{
if (fh && fh != INVALID_HANDLE_VALUE)
SetHandleInformation(fh, HANDLE_FLAG_INHERIT, 0);
}
+
+void
+fd_set_append (int fd)
+{}
# If we're using pipes for jobserver, then we will close them and not
# allow them to be available to sub-makes invoked via $(shell ...)
- run_make_test(q!
+ if (exists $FEATURES{'jobserver'}) {
+ run_make_test(q!
ifeq ($(ELT),)
default:; @$(MAKE) -f #MAKEFILE# ELT=1
else ifeq ($(ELT),1)
default:;: $(ELT)
endif
!,
- '--no-print-directory -j2 --jobserver-style=pipe', "#MAKE#[2]: warning: jobserver unavailable: using -j1. Add '+' to parent make rule.\n: 2\n: 1");
+ '--no-print-directory -j2 --jobserver-style=pipe', "#MAKE#[2]: warning: jobserver unavailable: using -j1. Add '+' to parent make rule.\n: 2\n: 1");
+ }
}
# If we're not using pipes for jobserver, then they are available in sub-makes