summaryrefslogtreecommitdiff
path: root/libguile/posix-w32.c
diff options
context:
space:
mode:
authorEli Zaretskii <eliz@gnu.org>2016-07-16 19:58:25 +0300
committerAndy Wingo <wingo@pobox.com>2016-07-25 11:49:27 +0200
commit62843d5475c31214c8c06d4946dcd75ada951bf9 (patch)
treeef34f01dbac4a7fda2301843ecf0363e69027f99 /libguile/posix-w32.c
parentaae356158412662c97b7178768bfe4be41749a3b (diff)
Improve process handling on MS-Windows
* libguile/posix-w32.c: Include gc.h and threads.h. (proc_record): New structure tag. <procs, proc_size>: New static variables. (find_proc, proc_handle, record_proc, delete_proc): New utility functions. (start_child): Return value is now pid_t, as it is on Posix platforms. Record the new process and returns its PID, instead of returning a handle. Fix the recursive call. (waitpid, kill, getpriority, setpriority, sched_getaffinity) (sched_setaffinity): Look up the PID in the recorded subprocesses before trying to open a process that is not our subprocess. Make sure any open handle is closed before returning, unless it's our subprocess.
Diffstat (limited to 'libguile/posix-w32.c')
-rw-r--r--libguile/posix-w32.c233
1 files changed, 216 insertions, 17 deletions
diff --git a/libguile/posix-w32.c b/libguile/posix-w32.c
index 21b779e4e..a3669e4fb 100644
--- a/libguile/posix-w32.c
+++ b/libguile/posix-w32.c
@@ -29,8 +29,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <io.h>
+#include <fcntl.h>
#include "posix-w32.h"
+#include "libguile/gc.h" /* for scm_*alloc, scm_strdup */
+#include "libguile/threads.h" /* for scm_i_scm_pthread_mutex_lock */
/*
* Get name and information about current kernel.
@@ -168,6 +174,80 @@ uname (struct utsname *uts)
return 0;
}
+/* Utility functions for maintaining the list of subprocesses launched
+ by Guile. */
+
+struct proc_record {
+ DWORD pid;
+ HANDLE handle;
+};
+
+static struct proc_record *procs;
+static ptrdiff_t proc_size;
+
+/* Find the process slot that corresponds to PID. Return the index of
+ the slot, or -1 if not found. */
+static ptrdiff_t
+find_proc (pid_t pid)
+{
+ ptrdiff_t found = -1, i;
+
+ for (i = 0; i < proc_size; i++)
+ {
+ if (procs[i].pid == pid && procs[i].handle != INVALID_HANDLE_VALUE)
+ found = i;
+ }
+
+ return found;
+}
+
+/* Return the process handle corresponding to its PID. If not found,
+ return invalid handle value. */
+static HANDLE
+proc_handle (pid_t pid)
+{
+ ptrdiff_t idx = find_proc (pid);
+
+ if (idx < 0)
+ return INVALID_HANDLE_VALUE;
+ return procs[idx].handle;
+}
+
+/* Store a process record in the procs[] array. */
+static void
+record_proc (pid_t proc_pid, HANDLE proc_handle)
+{
+ ptrdiff_t i;
+
+ /* Find a vacant slot. */
+ for (i = 0; i < proc_size; i++)
+ {
+ if (procs[i].handle == INVALID_HANDLE_VALUE)
+ break;
+ }
+
+ /* If no vacant slot, enlarge the array. */
+ if (i == proc_size)
+ {
+ proc_size++;
+ procs = scm_realloc (procs, proc_size * sizeof(procs[0]));
+ }
+
+ /* Store the process data. */
+ procs[i].pid = proc_pid;
+ procs[i].handle = proc_handle;
+}
+
+/* Delete a process record for process PID. */
+static void
+delete_proc (pid_t pid)
+{
+ ptrdiff_t idx = find_proc (pid);
+
+ if (0 <= idx && idx < proc_size)
+ procs[idx].handle = INVALID_HANDLE_VALUE;
+}
+
/* Run a child process with redirected standard handles, without
redirecting standard handles of the parent. This is required in
multithreaded programs, where redirecting a standard handle affects
@@ -522,7 +602,7 @@ prepare_cmdline (const char *cmd, const char * const *argv, int bin_sh_replaced)
Return the PID of the child process, or -1 if couldn't start a
process. */
-int
+pid_t
start_child (const char *exec_file, char **argv,
int reading, int c2p[2], int writing, int p2c[2],
int infd, int outfd, int errfd)
@@ -642,7 +722,12 @@ start_child (const char *exec_file, char **argv,
}
}
else
- pid = (intptr_t)pi.hProcess;
+ {
+ scm_i_scm_pthread_mutex_lock (&scm_i_misc_mutex);
+ record_proc (pi.dwProcessId, pi.hProcess);
+ scm_i_pthread_mutex_unlock (&scm_i_misc_mutex);
+ pid = pi.dwProcessId;
+ }
errno_save = errno;
@@ -666,7 +751,8 @@ start_child (const char *exec_file, char **argv,
if (c_strcasecmp (exec_file, shell) != 0)
{
argv[0] = (char *)exec_file;
- return start_child (shell, argv, reading, c2p, writing, p2c, errfd);
+ return start_child (shell, argv, reading, c2p, writing, p2c,
+ infd, outfd, errfd);
}
}
@@ -677,13 +763,33 @@ start_child (const char *exec_file, char **argv,
/* Emulation of waitpid which only supports WNOHANG, since _cwait doesn't. */
int
-waitpid (intptr_t pid, int *status, int options)
+waitpid (pid_t pid, int *status, int options)
{
+ HANDLE ph;
+
+ /* Not supported on MS-Windows. */
+ if (pid <= 0)
+ {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ scm_i_scm_pthread_mutex_lock (&scm_i_misc_mutex);
+ ph = proc_handle (pid);
+ scm_i_pthread_mutex_unlock (&scm_i_misc_mutex);
+ /* Since scm_waitpid is documented to work only on child processes,
+ being unable to find a process in our records means failure. */
+ if (ph == INVALID_HANDLE_VALUE)
+ {
+ errno = ECHILD;
+ return -1;
+ }
+
if ((options & WNOHANG) != 0)
{
DWORD st;
- if (!GetExitCodeProcess ((HANDLE)pid, &st))
+ if (!GetExitCodeProcess (ph, &st))
{
errno = ECHILD;
return -1;
@@ -692,10 +798,16 @@ waitpid (intptr_t pid, int *status, int options)
return 0;
if (status)
*status = st;
- return (int)pid;
+ CloseHandle (ph);
}
+ else
+ _cwait (status, (intptr_t)ph, WAIT_CHILD);
+
+ scm_i_scm_pthread_mutex_lock (&scm_i_misc_mutex);
+ delete_proc (pid);
+ scm_i_pthread_mutex_unlock (&scm_i_misc_mutex);
- return (int)_cwait (status, pid, WAIT_CHILD);
+ return pid;
}
@@ -757,8 +869,25 @@ w32_status_to_termsig (DWORD status)
int
kill (int pid, int sig)
{
- HANDLE ph = OpenProcess (PROCESS_TERMINATE, 0, pid);
+ HANDLE ph;
+ int child_proc = 0;
+ if (pid == getpid ())
+ {
+ if (raise (sig) == 0)
+ errno = ENOSYS;
+ return -1;
+ }
+
+ scm_i_scm_pthread_mutex_lock (&scm_i_misc_mutex);
+ ph = proc_handle (pid);
+ scm_i_pthread_mutex_unlock (&scm_i_misc_mutex);
+ /* If not found among our subprocesses, look elsewhere in the
+ system. */
+ if (ph == INVALID_HANDLE_VALUE)
+ ph = OpenProcess (PROCESS_TERMINATE, 0, pid);
+ else
+ child_proc = 1;
if (!ph)
{
errno = EPERM;
@@ -766,10 +895,23 @@ kill (int pid, int sig)
}
if (!TerminateProcess (ph, w32_signal_to_status (sig)))
{
- errno = EINVAL;
+ /* If it's our subprocess, it could have already exited. In
+ that case, waitpid will handily delete the process from our
+ records, and we should return a more meaningful ESRCH to the
+ caller. */
+ if (child_proc && waitpid (pid, NULL, WNOHANG) == pid)
+ errno = ESRCH;
+ else
+ errno = EINVAL;
return -1;
}
CloseHandle (ph);
+ if (child_proc)
+ {
+ scm_i_scm_pthread_mutex_lock (&scm_i_misc_mutex);
+ delete_proc (pid);
+ scm_i_pthread_mutex_unlock (&scm_i_misc_mutex);
+ }
return 0;
}
@@ -783,6 +925,7 @@ getpriority (int which, int who)
HANDLE hp;
int nice_value = -1;
int error = 0;
+ int child_proc = 0;
/* We don't support process groups and users. */
if (which != PRIO_PROCESS)
@@ -794,12 +937,27 @@ getpriority (int which, int who)
if (who == 0)
hp = GetCurrentProcess ();
else
- hp = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, who);
+ {
+ scm_i_scm_pthread_mutex_lock (&scm_i_misc_mutex);
+ hp = proc_handle (who);
+ scm_i_pthread_mutex_unlock (&scm_i_misc_mutex);
+ /* If not found among our subprocesses, look elsewhere in the
+ system. */
+ if (hp == INVALID_HANDLE_VALUE)
+ hp = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, who);
+ else
+ child_proc = 1;
+ }
if (hp)
{
DWORD pri_class = GetPriorityClass (hp);
+ /* The pseudo-handle returned by GetCurrentProcess doesn't need
+ to be closed. */
+ if (who > 0 && !child_proc)
+ CloseHandle (hp);
+
if (pri_class > 0)
{
switch (pri_class)
@@ -888,6 +1046,7 @@ setpriority (int which, int who, int nice_val)
{
HANDLE hp;
DWORD err;
+ int child_proc = 0, retval = -1;
if (which != PRIO_PROCESS)
{
@@ -898,7 +1057,17 @@ setpriority (int which, int who, int nice_val)
if (who == 0)
hp = GetCurrentProcess ();
else
- hp = OpenProcess (PROCESS_SET_INFORMATION, FALSE, who);
+ {
+ scm_i_scm_pthread_mutex_lock (&scm_i_misc_mutex);
+ hp = proc_handle (who);
+ scm_i_pthread_mutex_unlock (&scm_i_misc_mutex);
+ /* If not found among our subprocesses, look elsewhere in the
+ system. */
+ if (hp == INVALID_HANDLE_VALUE)
+ hp = OpenProcess (PROCESS_SET_INFORMATION, FALSE, who);
+ else
+ child_proc = 1;
+ }
if (hp)
{
@@ -920,7 +1089,7 @@ setpriority (int which, int who, int nice_val)
pri_class = REALTIME_PRIORITY_CLASS;
if (SetPriorityClass (hp, pri_class))
- return 0;
+ retval = 0;
}
err = GetLastError ();
@@ -934,8 +1103,12 @@ setpriority (int which, int who, int nice_val)
errno = EPERM;
break;
}
+ /* The pseudo-handle returned by GetCurrentProcess doesn't
+ need to be closed. */
+ if (hp && who > 0 && !child_proc)
+ CloseHandle (hp);
- return -1;
+ return retval;
}
/* Emulation of sched_getaffinity and sched_setaffinity. */
@@ -944,6 +1117,7 @@ sched_getaffinity (int pid, size_t mask_size, cpu_set_t *mask)
{
HANDLE hp;
DWORD err;
+ int child_proc = 0;
if (mask == NULL)
{
@@ -954,14 +1128,26 @@ sched_getaffinity (int pid, size_t mask_size, cpu_set_t *mask)
if (pid == 0)
hp = GetCurrentProcess ();
else
- hp = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, pid);
+ {
+ scm_i_scm_pthread_mutex_lock (&scm_i_misc_mutex);
+ hp = proc_handle (pid);
+ scm_i_pthread_mutex_unlock (&scm_i_misc_mutex);
+ /* If not found among our subprocesses, look elsewhere in the
+ system. */
+ if (hp == INVALID_HANDLE_VALUE)
+ hp = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, pid);
+ else
+ child_proc = 1;
+ }
if (hp)
{
DWORD_PTR ignored;
BOOL result = GetProcessAffinityMask (hp, (DWORD_PTR *)mask, &ignored);
- if (pid != 0)
+ /* The pseudo-handle returned by GetCurrentProcess doesn't
+ need to be closed. */
+ if (pid > 0 && !child_proc)
CloseHandle (hp);
if (result)
return 0;
@@ -988,6 +1174,7 @@ sched_setaffinity (int pid, size_t mask_size, cpu_set_t *mask)
{
HANDLE hp;
DWORD err;
+ int child_proc = 0;
if (mask == NULL)
{
@@ -998,13 +1185,25 @@ sched_setaffinity (int pid, size_t mask_size, cpu_set_t *mask)
if (pid == 0)
hp = GetCurrentProcess ();
else
- hp = OpenProcess (PROCESS_SET_INFORMATION, FALSE, pid);
+ {
+ scm_i_scm_pthread_mutex_lock (&scm_i_misc_mutex);
+ hp = proc_handle (pid);
+ scm_i_pthread_mutex_unlock (&scm_i_misc_mutex);
+ /* If not found among our subprocesses, look elsewhere in the
+ system. */
+ if (hp == INVALID_HANDLE_VALUE)
+ hp = OpenProcess (PROCESS_SET_INFORMATION, FALSE, pid);
+ else
+ child_proc = 1;
+ }
if (hp)
{
BOOL result = SetProcessAffinityMask (hp, *(DWORD_PTR *)mask);
- if (pid != 0)
+ /* The pseudo-handle returned by GetCurrentProcess doesn't
+ need to be closed. */
+ if (pid > 0 && !child_proc)
CloseHandle (hp);
if (result)
return 0;