summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/w32proc.c869
1 files changed, 523 insertions, 346 deletions
diff --git a/src/w32proc.c b/src/w32proc.c
index d13a5bca9e8..ee0f5f7986b 100644
--- a/src/w32proc.c
+++ b/src/w32proc.c
@@ -22,14 +22,21 @@ Boston, MA 02111-1307, USA.
Adapted from alarm.c by Tim Fleehart
*/
-#include <config.h>
-
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <io.h>
+#include <fcntl.h>
#include <signal.h>
+/* must include CRT headers *before* config.h */
+#include "config.h"
+#undef signal
+#undef wait
+#undef spawnve
+#undef select
+#undef kill
+
#include <windows.h>
#include "lisp.h"
@@ -42,32 +49,10 @@ Boston, MA 02111-1307, USA.
extern char *sys_siglist[];
#endif
-/* #define FULL_DEBUG */
-
-typedef void (_CALLBACK_ *signal_handler)(int);
-
-/* Defined in process.h which conflicts with the local copy */
-#define _P_NOWAIT 1
-
-typedef struct _child_process
-{
- int fd;
- HANDLE char_avail;
- HANDLE char_consumed;
- char chr;
- BOOL status;
- HANDLE process;
- DWORD pid;
- HANDLE thrd;
-} child_process;
-
-#define MAX_CHILDREN MAXDESC
-
#ifdef EMACSDEBUG
-void _CRTAPI1
-_DebPrint (char *fmt, ...)
+void _DebPrint (const char *fmt, ...)
{
- char buf[256];
+ char buf[1024];
va_list args;
va_start (args, fmt);
@@ -77,20 +62,14 @@ _DebPrint (char *fmt, ...)
}
#endif
-/* Child process management list. */
-static int child_proc_count = 0;
-static child_process child_procs[MAX_CHILDREN];
-static child_process *dead_child = NULL;
-
-#define CHILD_ACTIVE(cp) ((cp)->process != NULL)
-#define DEACTIVATE_CHILD(cp) ((cp)->process = NULL)
+typedef void (_CALLBACK_ *signal_handler)(int);
/* Signal handlers...SIG_DFL == 0 so this is initialized correctly. */
static signal_handler sig_handlers[NSIG];
/* Fake signal implementation to record the SIGCHLD handler. */
signal_handler
-win32_signal (int sig, signal_handler handler)
+sys_signal (int sig, signal_handler handler)
{
signal_handler old;
@@ -104,19 +83,109 @@ win32_signal (int sig, signal_handler handler)
return old;
}
+/* Defined in <process.h> which conflicts with the local copy */
+#define _P_NOWAIT 1
+
+/* Child process management list. */
+int child_proc_count = 0;
+child_process child_procs[ MAX_CHILDREN ];
+child_process *dead_child = NULL;
+
+DWORD WINAPI reader_thread (void *arg);
+
/* Find an unused process slot. */
-static child_process *
+child_process *
new_child (void)
{
child_process *cp;
-
- if (child_proc_count == MAX_CHILDREN)
- return NULL;
+ DWORD id;
for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
if (!CHILD_ACTIVE (cp))
- return cp;
- return &child_procs[child_proc_count++];
+ goto Initialise;
+ if (child_proc_count == MAX_CHILDREN)
+ return NULL;
+ cp = &child_procs[child_proc_count++];
+
+ Initialise:
+ memset (cp, 0, sizeof(*cp));
+ cp->fd = -1;
+ cp->pid = -1;
+ cp->procinfo.hProcess = NULL;
+ cp->status = STATUS_READ_ERROR;
+
+ /* use manual reset event so that select() will function properly */
+ cp->char_avail = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (cp->char_avail)
+ {
+ cp->char_consumed = CreateEvent (NULL, FALSE, FALSE, NULL);
+ if (cp->char_consumed)
+ {
+ cp->thrd = CreateThread (NULL, 1024, reader_thread, cp, 0, &id);
+ if (cp->thrd)
+ return cp;
+ }
+ }
+ delete_child (cp);
+ return NULL;
+}
+
+void
+delete_child (child_process *cp)
+{
+ int i;
+
+ /* Should not be deleting a child that is still needed. */
+ for (i = 0; i < MAXDESC; i++)
+ if (fd_info[i].cp == cp)
+ abort ();
+
+ if (!CHILD_ACTIVE (cp))
+ return;
+
+ /* reap thread if necessary */
+ if (cp->thrd)
+ {
+ DWORD rc;
+
+ if (GetExitCodeThread (cp->thrd, &rc) && rc == STILL_ACTIVE)
+ {
+ /* let the thread exit cleanly if possible */
+ cp->status = STATUS_READ_ERROR;
+ SetEvent (cp->char_consumed);
+ if (WaitForSingleObject (cp->thrd, 1000) != WAIT_OBJECT_0)
+ {
+ DebPrint (("delete_child.WaitForSingleObject (thread) failed "
+ "with %lu for fd %ld\n", GetLastError (), cp->fd));
+ TerminateThread (cp->thrd, 0);
+ }
+ }
+ CloseHandle (cp->thrd);
+ cp->thrd = NULL;
+ }
+ if (cp->char_avail)
+ {
+ CloseHandle (cp->char_avail);
+ cp->char_avail = NULL;
+ }
+ if (cp->char_consumed)
+ {
+ CloseHandle (cp->char_consumed);
+ cp->char_consumed = NULL;
+ }
+
+ /* update child_proc_count (highest numbered slot in use plus one) */
+ if (cp == child_procs + child_proc_count - 1)
+ {
+ for (i = child_proc_count-1; i >= 0; i--)
+ if (CHILD_ACTIVE (&child_procs[i]))
+ {
+ child_proc_count = i + 1;
+ break;
+ }
+ }
+ if (i < 0)
+ child_proc_count = 0;
}
/* Find a child by pid. */
@@ -124,31 +193,18 @@ static child_process *
find_child_pid (DWORD pid)
{
child_process *cp;
-
+
for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
if (CHILD_ACTIVE (cp) && pid == cp->pid)
return cp;
return NULL;
}
-/* Find a child by fd. */
-static child_process *
-find_child_fd (int fd)
-{
- child_process *cp;
-
- for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
- if (CHILD_ACTIVE (cp) && fd == cp->fd)
- return cp;
- return NULL;
-}
-/* Thread proc for child process reader threads
- The threads just sit in a loop waiting for input
- When they detect input, they signal the char_avail input to
- wake up the select emulator
- When the select emulator processes their input, it pulses
- char_consumed so that the reader thread goes back to reading. */
+/* Thread proc for child process and socket reader threads. Each thread
+ is normally blocked until woken by select() to check for input by
+ reading one char. When the read completes, char_avail is signalled
+ to wake up the select emulator and the thread blocks itself again. */
DWORD WINAPI
reader_thread (void *arg)
{
@@ -158,37 +214,30 @@ reader_thread (void *arg)
cp = (child_process *)arg;
/* We have to wait for the go-ahead before we can start */
- if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
- return 0;
- /* If something went wrong, quit */
- if (!cp->status)
- return 0;
-
+ if (cp == NULL ||
+ WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
+ return 1;
+
for (;;)
{
- /* Use read to get CRLF translation */
- if (read (cp->fd, &cp->chr, sizeof (char)) == sizeof (char))
- {
- cp->status = TRUE;
- }
- else
- {
-#ifdef FULL_DEBUG
- DebPrint (("reader_thread.read failed with %lu for fd %ld\n",
- GetLastError (), cp->fd));
-#endif
- cp->status = FALSE;
- }
-
+ int rc;
+
+ rc = _sys_read_ahead (cp->fd);
+
+ /* The name char_avail is a misnomer - it really just means the
+ read-ahead has completed, whether successfully or not. */
if (!SetEvent (cp->char_avail))
{
DebPrint (("reader_thread.SetEvent failed with %lu for fd %ld\n",
GetLastError (), cp->fd));
- break;
- }
+ return 1;
+ }
+
+ if (rc == STATUS_READ_ERROR)
+ return 1;
/* If the read died, the child has died so let the thread die */
- if (!cp->status)
+ if (rc == STATUS_READ_FAILED)
break;
/* Wait until our input is acknowledged before reading again */
@@ -204,31 +253,13 @@ reader_thread (void *arg)
static BOOL
create_child (char *exe, char *cmdline, char *env,
- PROCESS_INFORMATION *info)
+ int * pPid, child_process *cp)
{
- child_process *cp;
- DWORD id;
STARTUPINFO start;
SECURITY_ATTRIBUTES sec_attrs;
SECURITY_DESCRIPTOR sec_desc;
- cp = new_child ();
- if (cp == NULL)
- goto EH_Fail;
-
- cp->fd = -1;
-
- cp->char_avail = CreateEvent (NULL, FALSE, FALSE, NULL);
- if (cp->char_avail == NULL)
- goto EH_Fail;
-
- cp->char_consumed = CreateEvent (NULL, FALSE, FALSE, NULL);
- if (cp->char_consumed == NULL)
- goto EH_char_avail;
-
- cp->thrd = CreateThread (NULL, 1024, reader_thread, cp, 0, &id);
- if (cp->thrd == NULL)
- goto EH_char_consumed;
+ if (cp == NULL) abort ();
memset (&start, 0, sizeof (start));
start.cb = sizeof (start);
@@ -244,32 +275,35 @@ create_child (char *exe, char *cmdline, char *env,
/* Explicitly specify no security */
if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
- goto EH_thrd;
+ goto EH_Fail;
if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
- goto EH_thrd;
+ goto EH_Fail;
sec_attrs.nLength = sizeof (sec_attrs);
sec_attrs.lpSecurityDescriptor = &sec_desc;
sec_attrs.bInheritHandle = FALSE;
if (!CreateProcess (exe, cmdline, &sec_attrs, NULL, TRUE,
- CREATE_NEW_PROCESS_GROUP, env, NULL,
- &start, info))
- goto EH_thrd;
- cp->process = info->hProcess;
- cp->pid = info->dwProcessId;
+ CREATE_NEW_PROCESS_GROUP,
+ env, NULL,
+ &start, &cp->procinfo))
+ goto EH_Fail;
+
+ cp->pid = (int) cp->procinfo.dwProcessId;
+
+ /* Hack for Windows 95, which assigns large (ie negative) pids */
+ if (cp->pid < 0)
+ cp->pid = -cp->pid;
+
+ /* pid must fit in a Lisp_Int */
+ cp->pid = (cp->pid & VALMASK);
+
+
+ *pPid = cp->pid;
return TRUE;
- EH_thrd:
- id = GetLastError ();
-
- cp->status = FALSE;
- SetEvent (cp->char_consumed);
- EH_char_consumed:
- CloseHandle (cp->char_consumed);
- EH_char_avail:
- CloseHandle (cp->char_avail);
EH_Fail:
+ DebPrint (("create_child.CreateProcess failed: %ld\n", GetLastError()););
return FALSE;
}
@@ -295,14 +329,19 @@ register_child (int pid, int fd)
#endif
cp->fd = fd;
- cp->status = TRUE;
- /* Tell the reader thread to start */
- if (!SetEvent (cp->char_consumed))
+ /* thread is initially blocked until select is called; set status so
+ that select will release thread */
+ cp->status = STATUS_READ_ACKNOWLEDGED;
+
+ /* attach child_process to fd_info */
+ if (fd_info[fd].cp != NULL)
{
- DebPrint (("register_child.SetEvent failed with %lu for fd %ld\n",
- GetLastError (), cp->fd));
+ DebPrint (("register_child: fd_info[%d] apparently in use!\n", fd));
+ abort ();
}
+
+ fd_info[fd].cp = cp;
}
/* When a process dies its pipe will break so the reader thread will
@@ -310,27 +349,26 @@ register_child (int pid, int fd)
The select emulator then calls this routine to clean up.
Since the thread signaled failure we can assume it is exiting. */
static void
-remove_child (child_process *cp)
+reap_subprocess (child_process *cp)
{
- /* Reap the thread */
- if (WaitForSingleObject (cp->thrd, INFINITE) != WAIT_OBJECT_0)
- {
- DebPrint (("remove_child.WaitForSingleObject (thread) failed "
- "with %lu for fd %ld\n", GetLastError (), cp->fd));
- }
- CloseHandle (cp->thrd);
- CloseHandle (cp->char_consumed);
- CloseHandle (cp->char_avail);
-
- /* Reap the process */
- if (WaitForSingleObject (cp->process, INFINITE) != WAIT_OBJECT_0)
+ if (cp->procinfo.hProcess)
{
- DebPrint (("remove_child.WaitForSingleObject (process) failed "
- "with %lu for fd %ld\n", GetLastError (), cp->fd));
+ /* Reap the process */
+ if (WaitForSingleObject (cp->procinfo.hProcess, INFINITE) != WAIT_OBJECT_0)
+ DebPrint (("reap_subprocess.WaitForSingleObject (process) failed "
+ "with %lu for fd %ld\n", GetLastError (), cp->fd));
+ CloseHandle (cp->procinfo.hProcess);
+ cp->procinfo.hProcess = NULL;
+ CloseHandle (cp->procinfo.hThread);
+ cp->procinfo.hThread = NULL;
}
- CloseHandle (cp->process);
-
- DEACTIVATE_CHILD (cp);
+
+ /* For asynchronous children, the child_proc resources will be freed
+ when the last pipe read descriptor is closed; for synchronous
+ children, we must explicitly free the resources now because
+ register_child has not been called. */
+ if (cp->fd == -1)
+ delete_child (cp);
}
/* Wait for any of our existing child processes to die
@@ -338,10 +376,11 @@ remove_child (child_process *cp)
Return the pid and fill in the status if non-NULL. */
int
-win32_wait (int *status)
+sys_wait (int *status)
{
DWORD active, retval;
int nh;
+ int pid;
child_process *cp, *cps[MAX_CHILDREN];
HANDLE wait_hnd[MAX_CHILDREN];
@@ -349,17 +388,20 @@ win32_wait (int *status)
if (dead_child != NULL)
{
/* We want to wait for a specific child */
- wait_hnd[nh] = dead_child->process;
+ wait_hnd[nh] = dead_child->procinfo.hProcess;
cps[nh] = dead_child;
+ if (!wait_hnd[nh]) abort ();
nh++;
}
else
{
for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
- if (CHILD_ACTIVE (cp))
+ /* some child_procs might be sockets; ignore them */
+ if (CHILD_ACTIVE (cp) && cp->procinfo.hProcess)
{
- wait_hnd[nh] = cp->process;
+ wait_hnd[nh] = cp->procinfo.hProcess;
cps[nh] = cp;
+ if (!wait_hnd[nh]) abort ();
nh++;
}
}
@@ -416,8 +458,12 @@ win32_wait (int *status)
retval = SIGINT;
else
retval <<= 8;
-
+
cp = cps[active];
+ pid = cp->pid;
+#ifdef FULL_DEBUG
+ DebPrint (("Wait signaled with process pid %d\n", cp->pid));
+#endif
if (status)
{
@@ -445,15 +491,11 @@ win32_wait (int *status)
synch_process_death = signame;
}
- TerminateThread (cp->thrd, 0);
- CloseHandle (cp->thrd);
- CloseHandle (cp->char_consumed);
- CloseHandle (cp->char_avail);
- CloseHandle (cp->process);
- DEACTIVATE_CHILD (cp);
+
+ reap_subprocess (cp);
}
- return cp->pid;
+ return pid;
}
/* We pass our process ID to our children by setting up an environment
@@ -463,12 +505,20 @@ char ppid_env_var_buffer[64];
/* When a new child process is created we need to register it in our list,
so intercept spawn requests. */
int
-win32_spawnve (int mode, char *cmdname, char **argv, char **envp)
+sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
{
Lisp_Object program, full;
char *cmdline, *env, *parg, **targ;
int arglen;
- PROCESS_INFORMATION pi;
+ int pid;
+ child_process *cp;
+
+ /* We don't care about the other modes */
+ if (mode != _P_NOWAIT)
+ {
+ errno = EINVAL;
+ return -1;
+ }
/* Handle executable names without an executable suffix. */
program = make_string (cmdname, strlen (cmdname));
@@ -489,23 +539,24 @@ win32_spawnve (int mode, char *cmdname, char **argv, char **envp)
argv[0] = cmdname;
}
- if (child_proc_count == MAX_CHILDREN)
- {
- errno = EAGAIN;
- return -1;
- }
-
- /* We don't care about the other modes */
- if (mode != _P_NOWAIT)
- {
- errno = EINVAL;
- return -1;
- }
+ /* make sure cmdname is in DOS format */
+ strcpy (cmdname = alloca (strlen (cmdname) + 1), argv[0]);
+ unixtodos_filename (cmdname);
+ argv[0] = cmdname;
/* we have to do some conjuring here to put argv and envp into the
form CreateProcess wants... argv needs to be a space separated/null
terminated list of parameters, and envp is a null
separated/double-null terminated list of parameters.
+
+ Additionally, zero-length args and args containing whitespace need
+ to be wrapped in double quotes. Args containing embedded double
+ quotes (as opposed to enclosing quotes, which we leave alone) are
+ usually illegal (most Win32 programs do not implement escaping of
+ double quotes - sad but true, at least for programs compiled with
+ MSVC), but we will escape quotes anyway for those programs that can
+ handle it. The Win32 gcc library from Cygnus doubles quotes to
+ escape them, so we will use that convention.
Since I have no idea how large argv and envp are likely to be
we figure out list lengths on the fly and allocate them. */
@@ -515,21 +566,69 @@ win32_spawnve (int mode, char *cmdname, char **argv, char **envp)
targ = argv;
while (*targ)
{
+ char * p = *targ;
+ int add_quotes = 0;
+
+ if (*p == 0)
+ add_quotes = 1;
+ while (*p)
+ if (*p++ == '"')
+ {
+ /* allow for embedded quotes to be doubled - we won't
+ actually double quotes that aren't embedded though */
+ arglen++;
+ add_quotes = 1;
+ }
+ else if (*p == ' ' || *p == '\t')
+ add_quotes = 1;
+ if (add_quotes)
+ arglen += 2;
arglen += strlen (*targ++) + 1;
}
- cmdline = malloc (arglen);
- if (cmdline == NULL)
- {
- errno = ENOMEM;
- goto EH_Fail;
- }
+ cmdline = alloca (arglen);
targ = argv;
parg = cmdline;
while (*targ)
{
- strcpy (parg, *targ);
- parg += strlen (*targ++);
+ char * p = *targ;
+ int add_quotes = 0;
+
+ if (*p == 0)
+ add_quotes = 1;
+#if 0
+ /* Unfortunately, this causes more problems than it solves,
+ because argv arrays are not always carefully constructed.
+ grep, for instance, passes the whole command line as one
+ argument, so it becomes impossible to pass a regexp which
+ contains spaces. */
+ for ( ; *p; p++)
+ if (*p == ' ' || *p == '\t' || *p == '"')
+ add_quotes = 1;
+#endif
+ if (add_quotes)
+ {
+ char * first;
+ char * last;
+
+ p = *targ;
+ first = p;
+ last = p + strlen (p) - 1;
+ *parg++ = '"';
+ while (*p)
+ {
+ if (*p == '"' && p > first && p < last)
+ *parg++ = '"'; /* double up embedded quotes only */
+ *parg++ = *p++;
+ }
+ *parg++ = '"';
+ }
+ else
+ {
+ strcpy (parg, *targ);
+ parg += strlen (*targ);
+ }
*parg++ = ' ';
+ targ++;
}
*--parg = '\0';
@@ -544,12 +643,7 @@ win32_spawnve (int mode, char *cmdname, char **argv, char **envp)
GetCurrentProcessId ());
arglen += strlen (ppid_env_var_buffer) + 1;
- env = malloc (arglen);
- if (env == NULL)
- {
- errno = ENOMEM;
- goto EH_cmdline;
- }
+ env = alloca (arglen);
targ = envp;
parg = env;
while (*targ)
@@ -562,22 +656,23 @@ win32_spawnve (int mode, char *cmdname, char **argv, char **envp)
parg += strlen (ppid_env_var_buffer);
*parg++ = '\0';
*parg = '\0';
+
+ cp = new_child ();
+ if (cp == NULL)
+ {
+ errno = EAGAIN;
+ return -1;
+ }
/* Now create the process. */
- if (!create_child (cmdname, cmdline, env, &pi))
+ if (!create_child (cmdname, cmdline, env, &pid, cp))
{
+ delete_child (cp);
errno = ENOEXEC;
- goto EH_env;
+ return -1;
}
- return pi.dwProcessId;
-
- EH_env:
- free (env);
- EH_cmdline:
- free (cmdline);
- EH_Fail:
- return -1;
+ return pid;
}
/* Emulate the select call
@@ -598,20 +693,14 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
DWORD timeout_ms;
int i, nh, nr;
DWORD active;
- child_process *cp, *cps[MAX_CHILDREN + 1];
- HANDLE wait_hnd[MAX_CHILDREN + 1];
-#ifdef HAVE_NTGUI1
- BOOL keyboardwait = FALSE ;
-#endif /* HAVE_NTGUI */
+ child_process *cp;
+ HANDLE wait_hnd[MAXDESC];
+ int fdindex[MAXDESC]; /* mapping from wait handles back to descriptors */
/* If the descriptor sets are NULL but timeout isn't, then just Sleep. */
if (rfds == NULL && wfds == NULL && efds == NULL && timeout != NULL)
{
-#ifdef HAVE_TIMEVAL
Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
-#else
- Sleep ((*timeout) * 1000);
-#endif
return 0;
}
@@ -633,67 +722,96 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
{
if (i == 0)
{
-#ifdef HAVE_NTGUI1
- keyboardwait = TRUE ;
-#else
- /* Handle stdin specially */
- wait_hnd[nh] = keyboard_handle;
- cps[nh] = NULL;
- nh++;
-#endif /* HAVE_NTGUI */
+ if (keyboard_handle)
+ {
+ /* Handle stdin specially */
+ wait_hnd[nh] = keyboard_handle;
+ fdindex[nh] = i;
+ nh++;
+ }
/* Check for any emacs-generated input in the queue since
it won't be detected in the wait */
if (detect_input_pending ())
{
FD_SET (i, rfds);
- nr++;
+ return 1;
}
}
else
{
- /* Child process input */
- cp = find_child_fd (i);
+ /* Child process and socket input */
+ cp = fd_info[i].cp;
if (cp)
{
+ int current_status = cp->status;
+
+ if (current_status == STATUS_READ_ACKNOWLEDGED)
+ {
+ /* Tell reader thread which file handle to use. */
+ cp->fd = i;
+ /* Wake up the reader thread for this process */
+ cp->status = STATUS_READ_READY;
+ if (!SetEvent (cp->char_consumed))
+ DebPrint (("nt_select.SetEvent failed with "
+ "%lu for fd %ld\n", GetLastError (), i));
+ }
+
+#ifdef CHECK_INTERLOCK
+ /* slightly crude cross-checking of interlock between threads */
+
+ current_status = cp->status;
+ if (WaitForSingleObject (cp->char_avail, 0) == WAIT_OBJECT_0)
+ {
+ /* char_avail has been signalled, so status (which may
+ have changed) should indicate read has completed
+ but has not been acknowledged. */
+ current_status = cp->status;
+ if (current_status != STATUS_READ_SUCCEEDED &&
+ current_status != STATUS_READ_FAILED)
+ DebPrint (("char_avail set, but read not completed: status %d\n",
+ current_status));
+ }
+ else
+ {
+ /* char_avail has not been signalled, so status should
+ indicate that read is in progress; small possibility
+ that read has completed but event wasn't yet signalled
+ when we tested it (because a context switch occurred
+ or if running on separate CPUs). */
+ if (current_status != STATUS_READ_READY &&
+ current_status != STATUS_READ_IN_PROGRESS &&
+ current_status != STATUS_READ_SUCCEEDED &&
+ current_status != STATUS_READ_FAILED)
+ DebPrint (("char_avail reset, but read status is bad: %d\n",
+ current_status));
+ }
+#endif
+ wait_hnd[nh] = cp->char_avail;
+ fdindex[nh] = i;
+ if (!wait_hnd[nh]) abort ();
+ nh++;
#ifdef FULL_DEBUG
DebPrint (("select waiting on child %d fd %d\n",
cp-child_procs, i));
#endif
- wait_hnd[nh] = cp->char_avail;
- cps[nh] = cp;
- nh++;
}
else
{
- /* Unable to find something to wait on for this fd, fail */
- DebPrint (("select unable to find child process "
- "for fd %ld\n", i));
- nh = 0;
- break;
+ /* Unable to find something to wait on for this fd, skip */
+ DebPrint (("sys_select: fd %ld is invalid! ignoring\n", i));
+ abort ();
}
}
}
- /* Never do this in win32 since we will not get paint messages */
-
-#ifndef HAVE_NTGUI1
/* Nothing to look for, so we didn't find anything */
if (nh == 0)
{
if (timeout)
-#ifdef HAVE_TIMEVAL
Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
-#else
- Sleep ((*timeout) * 1000);
-#endif
return 0;
}
-#endif /* !HAVE_NTGUI */
-
- /* Check for immediate return without waiting */
- if (nr > 0)
- return nr;
/*
Wait for input
@@ -701,41 +819,25 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
so the reader thread will signal an error condition, thus, the wait
will wake up
*/
-#ifdef HAVE_TIMEVAL
timeout_ms = timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : INFINITE;
-#else
- timeout_ms = timeout ? *timeout*1000 : INFINITE;
-#endif
-#ifdef HAVE_NTGUI1
- active = MsgWaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms,QS_ALLINPUT);
-#else
+
active = WaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms);
-#endif /* HAVE_NTGUI */
+
if (active == WAIT_FAILED)
{
DebPrint (("select.WaitForMultipleObjects (%d, %lu) failed with %lu\n",
nh, timeout_ms, GetLastError ()));
- /* Is there a better error? */
- errno = EBADF;
+ /* don't return EBADF - this causes wait_reading_process_input to
+ abort; WAIT_FAILED is returned when single-stepping under
+ Windows 95 after switching thread focus in debugger, and
+ possibly at other times. */
+ errno = EINTR;
return -1;
}
else if (active == WAIT_TIMEOUT)
{
return 0;
}
-#ifdef HAVE_NTGUI1
- else if (active == WAIT_OBJECT_0 + nh)
- {
- /* Keyboard input available */
- FD_SET (0, rfds);
-
- /* This shouldn't be necessary, but apparently just setting the input
- fd is not good enough for emacs */
-// read_input_waiting ();
-
- return (1) ;
- }
-#endif /* HAVE_NTGUI */
else if (active >= WAIT_OBJECT_0 &&
active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
{
@@ -746,63 +848,82 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
{
active -= WAIT_ABANDONED_0;
}
-
- if (cps[active] == NULL)
- {
- /* Keyboard input available */
- FD_SET (0, rfds);
- nr++;
- /* This shouldn't be necessary, but apparently just setting the input
- fd is not good enough for emacs */
- read_input_waiting ();
- }
- else
+ /* Loop over all handles after active (now officially documented as
+ being the first signalled handle in the array). We do this to
+ ensure fairness, so that all channels with data available will be
+ processed - otherwise higher numbered channels could be starved. */
+ do
{
- /* Child process */
- cp = cps[active];
-
- /* If status is FALSE the read failed so don't report input */
- if (cp->status)
- {
- FD_SET (cp->fd, rfds);
- proc_buffered_char[cp->fd] = cp->chr;
+ if (fdindex[active] == 0)
+ {
+ /* Keyboard input available */
+ FD_SET (0, rfds);
nr++;
- }
+ }
else
- {
- /* The SIGCHLD handler will do a Wait so we know it won't
- return until the process is dead
- We force Wait to only wait for this process to avoid it
- picking up other children that happen to be dead but that
- we haven't noticed yet
- SIG_DFL for SIGCHLD is ignore? */
- if (sig_handlers[SIGCHLD] != SIG_DFL &&
- sig_handlers[SIGCHLD] != SIG_IGN)
- {
+ {
+ /* must be a socket or pipe */
+ int current_status;
+
+ cp = fd_info[ fdindex[active] ].cp;
+
+ /* Read ahead should have completed, either succeeding or failing. */
+ FD_SET (fdindex[active], rfds);
+ nr++;
+ current_status = cp->status;
+ if (current_status != STATUS_READ_SUCCEEDED)
+ {
+ if (current_status != STATUS_READ_FAILED)
+ DebPrint (("internal error: subprocess pipe signalled "
+ "at the wrong time (status %d)\n!", current_status));
+
+ /* The child_process entry for a socket or pipe will be
+ freed when the last descriptor using it is closed; for
+ pipes, we call the SIGCHLD handler. */
+ if (fd_info[ fdindex[active] ].flags & FILE_PIPE)
+ {
+ /* The SIGCHLD handler will do a Wait so we know it won't
+ return until the process is dead
+ We force Wait to only wait for this process to avoid it
+ picking up other children that happen to be dead but that
+ we haven't noticed yet
+ SIG_DFL for SIGCHLD is ignore? */
+ if (sig_handlers[SIGCHLD] != SIG_DFL &&
+ sig_handlers[SIGCHLD] != SIG_IGN)
+ {
#ifdef FULL_DEBUG
- DebPrint (("select calling SIGCHLD handler for pid %d\n",
- cp->pid));
+ DebPrint (("select calling SIGCHLD handler for pid %d\n",
+ cp->pid));
#endif
- dead_child = cp;
- sig_handlers[SIGCHLD](SIGCHLD);
- dead_child = NULL;
- }
-
- /* Clean up the child process entry in the table */
- remove_child (cp);
- }
- }
+ dead_child = cp;
+ sig_handlers[SIGCHLD] (SIGCHLD);
+ dead_child = NULL;
+ }
+
+ /* Clean up the child process entry in the table */
+ reap_subprocess (cp);
+ }
+ }
+ }
+
+ /* Test for input on remaining channels. */
+ while (++active < nh)
+ if (WaitForSingleObject (wait_hnd[active], 0) == WAIT_OBJECT_0)
+ break;
+ } while (active < nh);
+
return nr;
}
-/*
- Substitute for certain kill () operations
- */
+/* Substitute for certain kill () operations */
int
-win32_kill_process (int pid, int sig)
+sys_kill (int pid, int sig)
{
child_process *cp;
+ HANDLE proc_hand;
+ int need_to_free = 0;
+ int rc = 0;
/* Only handle signals that will result in the process dying */
if (sig != SIGINT && sig != SIGKILL && sig != SIGQUIT && sig != SIGHUP)
@@ -810,24 +931,33 @@ win32_kill_process (int pid, int sig)
errno = EINVAL;
return -1;
}
-
+
cp = find_child_pid (pid);
if (cp == NULL)
{
- DebPrint (("win32_kill_process didn't find a child with pid %lu\n", pid));
- errno = ECHILD;
- return -1;
+ proc_hand = OpenProcess (PROCESS_TERMINATE, 0, pid);
+ if (proc_hand == NULL)
+ {
+ errno = EPERM;
+ return -1;
+ }
+ need_to_free = 1;
+ }
+ else
+ {
+ proc_hand = cp->procinfo.hProcess;
+ pid = cp->procinfo.dwProcessId;
}
if (sig == SIGINT)
{
- /* Fake Ctrl-Break. */
+ /* Ctrl-Break is NT equivalent of SIGINT. */
if (!GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, pid))
{
- DebPrint (("win32_kill_process.GenerateConsoleCtrlEvent return %d "
+ DebPrint (("sys_kill.GenerateConsoleCtrlEvent return %d "
"for pid %lu\n", GetLastError (), pid));
errno = EINVAL;
- return -1;
+ rc = -1;
}
}
else
@@ -835,56 +965,103 @@ win32_kill_process (int pid, int sig)
/* Kill the process. On Win32 this doesn't kill child processes
so it doesn't work very well for shells which is why it's
not used in every case. */
- if (!TerminateProcess (cp->process, 0xff))
+ if (!TerminateProcess (proc_hand, 0xff))
{
- DebPrint (("win32_kill_process.TerminateProcess returned %d "
+ DebPrint (("sys_kill.TerminateProcess returned %d "
"for pid %lu\n", GetLastError (), pid));
errno = EINVAL;
- return -1;
+ rc = -1;
}
}
- return 0;
+
+ if (need_to_free)
+ CloseHandle (proc_hand);
+
+ return rc;
}
-/* If the channel is a pipe this read might block since we don't
- know how many characters are available, so check and read only
- what's there
- We also need to wake up the reader thread once we've read our data. */
-int
-read_child_output (int fd, char *buf, int max)
+extern int report_file_error (char *, Lisp_Object);
+
+/* The following two routines are used to manipulate stdin, stdout, and
+ stderr of our child processes.
+
+ Assuming that in, out, and err are *not* inheritable, we make them
+ stdin, stdout, and stderr of the child as follows:
+
+ - Save the parent's current standard handles.
+ - Set the std handles to inheritable duplicates of the ones being passed in.
+ (Note that _get_osfhandle() is an io.h procedure that retrieves the
+ NT file handle for a crt file descriptor.)
+ - Spawn the child, which inherits in, out, and err as stdin,
+ stdout, and stderr. (see Spawnve)
+ - Close the std handles passed to the child.
+ - Reset the parent's standard handles to the saved handles.
+ (see reset_standard_handles)
+ We assume that the caller closes in, out, and err after calling us. */
+
+void
+prepare_standard_handles (int in, int out, int err, HANDLE handles[3])
{
- HANDLE h;
- int to_read, nchars;
- DWORD waiting;
- child_process *cp;
-
- h = (HANDLE)_get_osfhandle (fd);
- if (GetFileType (h) == FILE_TYPE_PIPE)
- {
- PeekNamedPipe (h, NULL, 0, NULL, &waiting, NULL);
- to_read = min (waiting, (DWORD)max);
- }
- else
- to_read = max;
+ HANDLE parent;
+ HANDLE newstdin, newstdout, newstderr;
+
+ parent = GetCurrentProcess ();
+
+ handles[0] = GetStdHandle (STD_INPUT_HANDLE);
+ handles[1] = GetStdHandle (STD_OUTPUT_HANDLE);
+ handles[2] = GetStdHandle (STD_ERROR_HANDLE);
+
+ /* make inheritable copies of the new handles */
+ if (!DuplicateHandle (parent,
+ (HANDLE) _get_osfhandle (in),
+ parent,
+ &newstdin,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS))
+ report_file_error ("Duplicating input handle for child", Qnil);
- /* Use read to get CRLF translation */
- nchars = read (fd, buf, to_read);
+ if (!DuplicateHandle (parent,
+ (HANDLE) _get_osfhandle (out),
+ parent,
+ &newstdout,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS))
+ report_file_error ("Duplicating output handle for child", Qnil);
- if (GetFileType (h) == FILE_TYPE_PIPE)
- {
- /* Wake up the reader thread
- for this process */
- cp = find_child_fd (fd);
- if (cp)
- {
- if (!SetEvent (cp->char_consumed))
- DebPrint (("read_child_output.SetEvent failed with "
- "%lu for fd %ld\n", GetLastError (), fd));
- }
- else
- DebPrint (("read_child_output couldn't find a child with fd %d\n",
- fd));
- }
+ if (!DuplicateHandle (parent,
+ (HANDLE) _get_osfhandle (err),
+ parent,
+ &newstderr,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS))
+ report_file_error ("Duplicating error handle for child", Qnil);
+
+ /* and store them as our std handles */
+ if (!SetStdHandle (STD_INPUT_HANDLE, newstdin))
+ report_file_error ("Changing stdin handle", Qnil);
- return nchars;
+ if (!SetStdHandle (STD_OUTPUT_HANDLE, newstdout))
+ report_file_error ("Changing stdout handle", Qnil);
+
+ if (!SetStdHandle (STD_ERROR_HANDLE, newstderr))
+ report_file_error ("Changing stderr handle", Qnil);
+}
+
+void
+reset_standard_handles (int in, int out, int err, HANDLE handles[3])
+{
+ /* close the duplicated handles passed to the child */
+ CloseHandle (GetStdHandle (STD_INPUT_HANDLE));
+ CloseHandle (GetStdHandle (STD_OUTPUT_HANDLE));
+ CloseHandle (GetStdHandle (STD_ERROR_HANDLE));
+
+ /* now restore parent's saved std handles */
+ SetStdHandle (STD_INPUT_HANDLE, handles[0]);
+ SetStdHandle (STD_OUTPUT_HANDLE, handles[1]);
+ SetStdHandle (STD_ERROR_HANDLE, handles[2]);
}
+
+/* end of ntproc.c */