You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@stdcxx.apache.org by fa...@apache.org on 2006/11/08 13:27:17 UTC
svn commit: r472469 - in /incubator/stdcxx/trunk/tests: include/rw_process.h
self/0.process.cpp src/process.cpp
Author: faridz
Date: Wed Nov 8 04:27:17 2006
New Revision: 472469
URL: http://svn.apache.org/viewvc?view=rev&rev=472469
Log:
2006-11-08 Farid Zaripov <fa...@kyiv.vdiweb.com>
* rw_process.h (rw_pid_t): The type long changed to _RWSTD_SSIZE_T.
(rw_wait_pid): Added the timeout parameter.
(rw_process_kill): New function to terminate the specified process.
* process.cpp [_WIN32] (__rw_map_errno): New function to get errno
value from WinAPI last error code
(__rw_split_cmd): Moved to #ifndef _WIN32/#endif
[_WIN32] (_rw_vprocess_create): Used CreateProcess instead of
rw_process_create(char*, char* [])
[_WIN32] (rw_process_create): Used rw_process_create(char*, ...)
instead of spawnp
* 0.process.cpp: New test exercising the rw_process_create(),
rw_process_kill() and rw_waitpid() functions.
Added:
incubator/stdcxx/trunk/tests/self/0.process.cpp (with props)
Modified:
incubator/stdcxx/trunk/tests/include/rw_process.h
incubator/stdcxx/trunk/tests/src/process.cpp
Modified: incubator/stdcxx/trunk/tests/include/rw_process.h
URL: http://svn.apache.org/viewvc/incubator/stdcxx/trunk/tests/include/rw_process.h?view=diff&rev=472469&r1=472468&r2=472469
==============================================================================
--- incubator/stdcxx/trunk/tests/include/rw_process.h (original)
+++ incubator/stdcxx/trunk/tests/include/rw_process.h Wed Nov 8 04:27:17 2006
@@ -30,8 +30,7 @@
#include <testdefs.h>
-
-typedef long rw_pid_t;
+typedef _RWSTD_SSIZE_T rw_pid_t;
_TEST_EXPORT int
rw_system (const char*, ...);
@@ -45,15 +44,39 @@
// returns pid of the created process or -1 on error
// (in which case errno is set to an appropriate value)
_TEST_EXPORT rw_pid_t
-rw_process_create (const char*, char* const []);
+rw_process_create (const char* /*path*/, char* const /*argv*/[]);
// result is a pointer to a buffer where the result code
-// of the specified process will be stored, or NULL
-// returns pid of the specified process or -1 on error
-// (in which case errno is set to an appropriate value)
-// Errors:
+// of the specified process will be stored, or NULL
+//
+// the function suspends execution of the current process
+// until a child has exited or specified timeout is reached
+//
+// returns:
+// pid of the specified process if it has exited
+// 0 when process still active
+// -1 on error (in which case errno is set to an appropriate value)
+//
+// timeout is timeout interval in seconds.
+// if timeout > 0 the function returns if the interval elapses
+// if timeout == 0 the function returns immediately
+// if timeout < 0 the function's time-out interval never elapses
+//
+// errors:
// ECHILD: no specified process exists
_TEST_EXPORT rw_pid_t
-rw_waitpid (rw_pid_t, int*);
+rw_waitpid (rw_pid_t /*pid*/, int* /*result*/, int /*timeout*/ = -1);
+
+// returns:
+// 0 when process terminated successfully
+// 1 when signal was sent to the child process, but child process
+// not terminated within 1 second interval
+// -1 on error (in which case errno is set to an appropriate value)
+// errors:
+// ESRCH: the pid does not exist
+// EPERM: the calling process does not have permission
+// to terminate the specified process
+_TEST_EXPORT int
+rw_process_kill (rw_pid_t, int = -1);
#endif // RW_PROCESS_H_INCLUDED
Added: incubator/stdcxx/trunk/tests/self/0.process.cpp
URL: http://svn.apache.org/viewvc/incubator/stdcxx/trunk/tests/self/0.process.cpp?view=auto&rev=472469
==============================================================================
--- incubator/stdcxx/trunk/tests/self/0.process.cpp (added)
+++ incubator/stdcxx/trunk/tests/self/0.process.cpp Wed Nov 8 04:27:17 2006
@@ -0,0 +1,304 @@
+/************************************************************************
+*
+* 0.process.cpp - test exercising the rw_process_create(),
+* rw_process_kill() and rw_waitpid() functions
+*
+* $Id$
+*
+************************************************************************
+*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed
+* with this work for additional information regarding copyright
+* ownership. The ASF licenses this file to you under the Apache
+* License, Version 2.0 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* permissions and limitations under the License.
+*
+**************************************************************************/
+
+#include <string.h> // for strcmp()
+#include <errno.h> // for errno
+
+#include <rw_process.h> // for rw_process_create(), rw_waitpid()
+#include <driver.h> // for rw_test()
+
+static int _rw_child = 0;
+static int _rw_timeout = 5;
+
+static char arg1[] = "--child=1";
+static char arg2[] = "--no-stdout";
+
+static char* args [] = { 0, arg1, arg2, 0 };
+static const int nargs = sizeof (args) / sizeof (*args) - 1;
+
+static rw_pid_t _rw_pid = -1;
+
+static int join_test (rw_pid_t pid, bool should_hang)
+{
+ int result = 0;
+ const rw_pid_t ret = rw_waitpid (pid, &result, _rw_timeout);
+
+ rw_assert (-1 != ret, __FILE__, __LINE__,
+ "rw_waitpid() failed, errno = %{#m}");
+
+ if (-1 == ret)
+ return 1;
+
+ if (0 == ret) {
+ // set to ignore rw_error diagnostic
+ rw_enable (rw_error, false);
+
+ // time_out elapsed, kill the process
+ if (1 == rw_process_kill (pid)) {
+ // the process not yet terminated
+ // wait for process termination and remove the zombie process
+ rw_waitpid (pid, 0);
+ }
+
+ // enable rw_error diagnostic
+ rw_enable (rw_error);
+
+ rw_assert (should_hang, __FILE__, __LINE__,
+ "The child process unexpectedly deadlocked");
+
+ return should_hang ? 0 : 1;
+ }
+
+ rw_assert (!should_hang, __FILE__, __LINE__,
+ "Expected the deadlocked process, but process exited "
+ "with code: %d",
+ result);
+
+ if (!should_hang)
+ rw_assert (0 == result, __FILE__, __LINE__,
+ "Process exit code: expected 0, got %d", result);
+
+ return result;
+}
+
+static int test_process_create1 ()
+{
+ rw_info (0, 0, 0,
+ "Exercising the rw_process_create "
+ "(const char*, char* const []) overload");
+
+ const rw_pid_t pid = rw_process_create (args [0], args);
+
+ rw_assert (-1 != pid, __FILE__, __LINE__,
+ "rw_process_create() failed, errno = %{#m}");
+
+ if (-1 == pid)
+ return 1;
+
+ // save the pid for test exercising rw_waitpid() fail
+ if (-1 == _rw_pid)
+ _rw_pid = pid;
+
+ return join_test (pid, false);
+}
+
+static int test_process_create2 ()
+{
+ rw_info (0, 0, 0,
+ "Exercising the rw_process_create (const char*, ...) overload");
+
+ const rw_pid_t pid = rw_process_create ("\"%s\" %s %s",
+ args[0], args[1], args[2]);
+
+ rw_assert (-1 != pid, __FILE__, __LINE__,
+ "rw_process_create() failed, errno = %{#m}");
+
+ if (-1 == pid)
+ return 1;
+
+ // save the pid for test exercising rw_waitpid() fail
+ if (-1 == _rw_pid)
+ _rw_pid = pid;
+
+ return join_test (pid, false);
+}
+
+static int test_process_deadlocked ()
+{
+ rw_info (0, 0, 0,
+ "Exercising the rw_waitpid() with timeout and deadlocked process");
+
+ const rw_pid_t pid = rw_process_create ("\"%s\" --child=2 %s",
+ args[0], args[2]);
+
+ rw_assert (-1 != pid, __FILE__, __LINE__,
+ "rw_process_create() failed, errno = %{#m}");
+
+ return -1 == pid ? 1 : join_test (pid, true);
+}
+
+static int test_process_create_fail ()
+{
+ rw_info (0, 0, 0,
+ "Exercising the rw_process_create() behavior "
+ "when invalid path specified");
+
+ // set to ignore rw_error diagnostic
+ rw_enable (rw_error, false);
+
+ const rw_pid_t pid = rw_process_create ("/\\/\\/\\", args);
+
+ // enable rw_error diagnostic
+ rw_enable (rw_error);
+
+ rw_assert (-1 == pid, __FILE__, __LINE__,
+ "rw_process_create returns %ld, expected -1",
+ long (pid));
+
+ if (-1 != pid) {
+ rw_waitpid (pid, 0);
+ return 1;
+ }
+
+ rw_assert (ENOENT == errno, __FILE__, __LINE__,
+ "errno: expected ENOENT, got %{#m}");
+
+ return ENOENT == errno ? 0 : 1;
+}
+
+static int test_waitpid_fail ()
+{
+ if (-1 == _rw_pid) {
+ rw_info (0, 0, 0,
+ "The test, exercising the rw_waitpid() behavior "
+ "when invalid pid specified is disabled");
+
+ return 0;
+ }
+
+ rw_info (0, 0, 0,
+ "Exercising the rw_waitpid() behavior "
+ "when invalid pid specified");
+
+ // set to ignore rw_error diagnostic
+ rw_enable (rw_error, false);
+
+ const rw_pid_t pid = rw_waitpid (_rw_pid, 0);
+
+ // enable rw_error diagnostic
+ rw_enable (rw_error);
+
+ rw_assert (-1 == pid, __FILE__, __LINE__,
+ "rw_waitpid returns %ld, expected -1",
+ long (pid));
+
+ if (-1 != pid)
+ return 1;
+
+ rw_assert (ECHILD == errno, __FILE__, __LINE__,
+ "errno: expected ECHILD, got %{#m}");
+
+ return ECHILD == errno ? 0 : 1;
+}
+
+static int test_process_kill_fail ()
+{
+ if (-1 == _rw_pid) {
+ rw_info (0, 0, 0,
+ "The test, exercising the rw_process_kill() behavior "
+ "when invalid pid specified is disabled");
+
+ return 0;
+ }
+
+ rw_info (0, 0, 0,
+ "Exercising the rw_process_kill() behavior "
+ "when invalid pid specified");
+
+ // set to ignore rw_error diagnostic
+ rw_enable (rw_error, false);
+
+ const int res = rw_process_kill (_rw_pid);
+
+ // enable rw_error diagnostic
+ rw_enable (rw_error);
+
+ rw_assert (-1 == res, __FILE__, __LINE__,
+ "rw_process_kill returns %ld, expected -1",
+ long (res));
+
+ if (-1 != res)
+ return 1;
+
+ rw_assert (ESRCH == errno, __FILE__, __LINE__,
+ "errno: expected ESRCH, got %{#m}");
+
+ return ESRCH == errno ? 0 : 1;
+}
+
+static int
+run_test (int argc, char** argv)
+{
+ if (_rw_child) {
+
+ rw_info (0, 0, 0,
+ "The child process: _rw_child = %i", _rw_child);
+
+ if (2 == _rw_child) {
+ // simulate the deadlock
+ while (true) ;
+ }
+
+ // compare number of parameters with expected
+ if (nargs != argc)
+ return nargs;
+
+ // compare the parameters with expected
+ for (int i = 1; i < argc; ++i) {
+ if (0 != strcmp (argv [i], args [i]))
+ return i;
+ }
+
+ return 0;
+ }
+
+ args [0] = argv [0];
+
+ int fails = 0;
+
+ if (test_process_create1 ())
+ ++fails;
+
+ if (test_process_create2 ())
+ ++fails;
+
+ if (test_process_deadlocked ())
+ ++fails;
+
+ if (test_process_create_fail ())
+ ++fails;
+
+ if (test_waitpid_fail ())
+ ++fails;
+
+ if (test_process_kill_fail ())
+ ++fails;
+
+ return fails;
+}
+
+int main (int argc, char *argv[])
+{
+ return rw_test (argc, argv, __FILE__,
+ "0.process",
+ "", run_test,
+ "|-child#0 "
+ "|-timeout#",
+ &_rw_child,
+ &_rw_timeout,
+ 0 /*sentinel*/);
+}
Propchange: incubator/stdcxx/trunk/tests/self/0.process.cpp
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/stdcxx/trunk/tests/self/0.process.cpp
------------------------------------------------------------------------------
svn:keywords = Id
Modified: incubator/stdcxx/trunk/tests/src/process.cpp
URL: http://svn.apache.org/viewvc/incubator/stdcxx/trunk/tests/src/process.cpp?view=diff&rev=472469&r1=472468&r2=472469
==============================================================================
--- incubator/stdcxx/trunk/tests/src/process.cpp (original)
+++ incubator/stdcxx/trunk/tests/src/process.cpp Wed Nov 8 04:27:17 2006
@@ -29,7 +29,7 @@
#include <rw_process.h>
-#include <stddef.h>
+#include <stddef.h> // for size_t
#include <stdarg.h> // for va_copy, va_list, ...
#include <stdlib.h> // for free(), exit()
#include <string.h> // for strchr()
@@ -40,8 +40,9 @@
#include <driver.h> // for rw_note(), ...
#include <rw_printf.h> // for rw_fprintf()
-#ifndef ENOMEM
-# define ENOMEM 12 // e.g., Linux, Solaris
+#ifdef __CYGWIN__
+// use the Windows API on Cygwin
+# define _WIN32
#endif
/**************************************************************************/
@@ -51,13 +52,165 @@
/**************************************************************************/
-#if defined (_WIN32) || defined (_WIN64)
-# include <process.h> // for spawnvp(), cwait()
-#else
+#ifdef _WIN32
+
+# include <windows.h> // for WaitForSingleObject(), ...
+
+static int
+_rw_map_errno (DWORD err)
+{
+ if (ERROR_WRITE_PROTECT <= err && ERROR_SHARING_BUFFER_EXCEEDED >= err)
+ return EACCES;
+
+ if ( ERROR_INVALID_STARTING_CODESEG <= err
+ && ERROR_INFLOOP_IN_RELOC_CHAIN >= err) {
+
+ return ENOEXEC;
+ }
+
+ switch (err)
+ {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_INVALID_DRIVE:
+ case ERROR_NO_MORE_FILES:
+ case ERROR_BAD_NETPATH:
+ case ERROR_BAD_NET_NAME:
+ case ERROR_INVALID_NAME:
+ case ERROR_BAD_PATHNAME:
+ case ERROR_FILENAME_EXCED_RANGE:
+
+ return ENOENT;
+
+ case ERROR_TOO_MANY_OPEN_FILES:
+ return EMFILE;
+
+ case ERROR_ACCESS_DENIED:
+ case ERROR_CURRENT_DIRECTORY:
+ case ERROR_NETWORK_ACCESS_DENIED:
+ case ERROR_CANNOT_MAKE:
+ case ERROR_FAIL_I24:
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_SEEK_ON_DEVICE:
+ case ERROR_NOT_LOCKED:
+ case ERROR_LOCK_FAILED:
+ return EACCES;
+
+ case ERROR_INVALID_HANDLE:
+ case ERROR_INVALID_TARGET_HANDLE:
+ case ERROR_DIRECT_ACCESS_HANDLE:
+ return EBADF;
+
+ case ERROR_ARENA_TRASHED:
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_INVALID_BLOCK:
+ case ERROR_NOT_ENOUGH_QUOTA:
+ return ENOMEM;
+
+ case ERROR_BAD_ENVIRONMENT:
+ return E2BIG;
+
+ case ERROR_BAD_FORMAT:
+ return ENOEXEC;
+
+ case ERROR_NOT_SAME_DEVICE:
+ return EXDEV;
+
+ case ERROR_FILE_EXISTS:
+ return EEXIST;
+
+ case ERROR_NO_PROC_SLOTS:
+ case ERROR_MAX_THRDS_REACHED:
+ case ERROR_NESTING_NOT_ALLOWED:
+ return EAGAIN;
+
+ case ERROR_BROKEN_PIPE:
+ return EPIPE;
+
+ case ERROR_DISK_FULL:
+ return ENOSPC;
+
+ case ERROR_WAIT_NO_CHILDREN:
+ case ERROR_CHILD_NOT_COMPLETE:
+ return ECHILD;
+
+ case ERROR_DIR_NOT_EMPTY:
+ return ENOTEMPTY;
+
+ case ERROR_ALREADY_EXISTS:
+ return EEXIST;
+ }
+
+ return EINVAL;
+}
+
+#else // #if !defined (_WIN32)
+
# include <sys/types.h>
# include <sys/wait.h> // for waitpid()
# include <unistd.h> // for fork(), execv(), access()
-#endif // _WIN{32,64}
+# include <setjmp.h> // for setjmp(), longjmp()
+# include <signal.h> // for signal()
+# include <time.h> // for nanosleep()
+
+/**************************************************************************/
+
+// splits command line to the array of parameters
+// note: modifies the cmd string
+// returns the number of parameters in cmd
+// if argv != 0 fills argv up to size elements
+static size_t
+_rw_split_cmd (char* cmd, char** argv, size_t size)
+{
+ RW_ASSERT (0 != cmd);
+
+ size_t ret = 0;
+
+ for (char* end = cmd + strlen (cmd); cmd != end; /*do nothing*/) {
+ // skip the leading spaces
+ while (isspace (*cmd))
+ ++cmd;
+
+ if (end == cmd)
+ break;
+
+ if (argv && ret < size)
+ argv [ret] = cmd;
+
+ ++ret;
+
+ if ('\'' == *cmd || '\"' == *cmd) {
+ char* const cmd1 = cmd + 1;
+ // search the closing quote
+ char* const pos = strchr (cmd1, *cmd);
+ if (pos) {
+ // found, remove the quotes
+ // remove the opening quote
+ memmove (cmd, cmd1, pos - cmd1);
+ // remove the closing quote
+ cmd = pos - 1;
+ memmove (cmd, pos + 1, end - pos);
+ end -= 2;
+ }
+ else {
+ // not found
+ break;
+ }
+ }
+
+ // search the space
+ while (*cmd && !isspace (*cmd))
+ ++cmd;
+
+ if (cmd != end)
+ // found, replace to '\0'
+ *cmd++ = '\0';
+ }
+
+ return ret;
+}
+
+#endif // #if defined (_WIN32)
/**************************************************************************/
@@ -83,7 +236,7 @@
if (ret) {
-#if !defined (_WIN32) && !defined (_WIN64)
+#ifndef _WIN32
if (-1 == ret) {
// system() failed, e.g., because fork() failed
@@ -107,7 +260,7 @@
"the command \"%s\" exited with status %d",
buf, status);
}
-#else // if defined (_WIN32) || defined (_WIN64)
+#else // if defined (_WIN32)
// FIXME: make this more descriptive
rw_error (0, __FILE__, __LINE__,
@@ -140,86 +293,54 @@
/**************************************************************************/
-// splits command line to the array of parameters
-// note: modifies the cmd string
-// returns the number of parameters in cmd
-// if argv != 0 fills argv up to size elements
-static size_t
-_rw_split_cmd (char* cmd, char** argv, size_t size)
+static rw_pid_t
+_rw_vprocess_create (const char* cmd, va_list va)
{
RW_ASSERT (0 != cmd);
- size_t ret = 0;
+ char buffer [256];
+ char *buf = buffer;
- for (char* end = cmd + strlen (cmd); cmd != end; /*do nothing*/) {
- // skip the leading spaces
- while (isspace (*cmd))
- ++cmd;
+ size_t bufsize = sizeof (buffer);
- if (end == cmd)
- break;
+ rw_vasnprintf (&buf, &bufsize, cmd, va);
- if (argv && ret < size)
- argv [ret] = cmd;
+ rw_pid_t ret = -1;
- ++ret;
+#ifdef _WIN32
- if ('\'' == *cmd || '\"' == *cmd) {
- char* const cmd1 = cmd + 1;
- // search the closing quote
- if (char* pos = strchr (cmd1, *cmd)) {
- // found, remove the quotes
- // remove the opening quote
- memmove (cmd, cmd1, pos - cmd1);
- // remove the closing quote
- cmd = pos - 1;
- memmove (cmd, pos + 1, end - pos);
- end -= 2;
- } else {
- // not found
- break;
- }
- }
+ STARTUPINFO si = { sizeof (si) };
+ PROCESS_INFORMATION pi;
- // search the space
- while (*cmd && !isspace (*cmd))
- ++cmd;
+ if (CreateProcess (0, buf, 0, 0, FALSE,
+ CREATE_NEW_PROCESS_GROUP, 0, 0, &si, &pi)) {
- if (cmd != end)
- // found, replace to '\0'
- *cmd++ = '\0';
+ CloseHandle (pi.hThread);
+ ret = rw_pid_t (pi.hProcess);
}
+ else {
+ const DWORD err = GetLastError ();
- return ret;
-}
-
-/**************************************************************************/
-
-static rw_pid_t
-_rw_vprocess_create (const char* cmd, va_list va)
-{
- RW_ASSERT (0 != cmd);
-
- char buffer [256];
- char *buf = buffer;
+ rw_error (0, __FILE__, __LINE__,
+ "CreateProcess () failed: GetLastError() = %zu",
+ size_t (err));
- size_t bufsize = sizeof (buffer);
+ errno = _rw_map_errno (err);
+ }
- rw_vasnprintf (&buf, &bufsize, cmd, va);
+#else // #if !defined (_WIN32)
const size_t MAX_PARAMS = 63;
char* argv [MAX_PARAMS + 1] = { 0 };
- int ret;
-
size_t argc = _rw_split_cmd (buf, argv, MAX_PARAMS);
if (0 < argc && MAX_PARAMS >= argc)
ret = rw_process_create (argv [0], argv);
- else {
- ret = -1;
- errno = ENOMEM;
- }
+ else
+ errno = E2BIG;
+
+#endif // _WIN32
if (buf != buffer)
free (buf);
@@ -235,7 +356,7 @@
va_list va;
va_start (va, cmd);
- const int ret = _rw_vprocess_create (cmd, va);
+ const rw_pid_t ret = _rw_vprocess_create (cmd, va);
va_end (va);
return ret;
@@ -246,77 +367,194 @@
_TEST_EXPORT rw_pid_t
rw_process_create (const char* path, char* const argv[])
{
-#if defined (_WIN32) || defined (_WIN64)
-
- const rw_pid_t child_pid = spawnvp (P_NOWAIT, path, argv);
-
- if (-1 == child_pid)
- rw_error (0, __FILE__, __LINE__,
- "spawnp (P_NOWAIT, %#s, %{As}) failed: errno = %{#m} (%{m})",
- path);
+#if defined (_WIN32)
-#else
+ return rw_process_create ("\"%s\" %{ As}", path, argv + 1);
- rw_pid_t child_pid = -1;
+#else // #if !defined (_WIN32)
- const int res = access (path, X_OK);
+ if (0 == access (path, X_OK)) {
- if (0 == res) {
-
- child_pid = fork ();
+ const rw_pid_t child_pid = fork ();
if (0 == child_pid) {
// the child process
execvp (path, argv);
// the execvp returns only if an error occurs
- rw_fprintf (rw_stderr, "%s:%d execvp() failed: "
- "errno = %{#m} (%{m})\n");
+ rw_fprintf (rw_stderr, "%s:%d execvp (%#s, %{As}) failed: "
+ "errno = %{#m} (%{m})\n", path, argv);
exit (1);
}
else if (-1 == child_pid)
rw_error (0, __FILE__, __LINE__,
- "fork() failed: errno = %{#m} (%{m})");
+ "fork () failed: errno = %{#m} (%{m})");
+
+ return child_pid;
}
else
rw_error (0, __FILE__, __LINE__,
- "access(%#s, X_OK) failed: errno = %{#m} (%{m})",
+ "access (%#s, X_OK) failed: errno = %{#m} (%{m})",
path);
-#endif // #if defined (_WIN32) || defined (_WIN64)
+ return -1;
- return child_pid;
+#endif // #if defined (_WIN32)
}
/**************************************************************************/
+#if defined (_WIN32)
+
_TEST_EXPORT rw_pid_t
-rw_waitpid (rw_pid_t pid, int* result)
+rw_waitpid (rw_pid_t pid, int* result, int timeout/* = -1*/)
{
-#if defined (_WIN32) || defined (_WIN64)
+ /* Explicitly check for process_id being -1 or -2. In Windows NT,
+ * -1 is a handle on the current process, -2 is a handle on the
+ * current thread, and it is perfectly legal to to wait (forever)
+ * on either */
+ if (-1 == pid || -2 == pid) {
+ errno = ECHILD;
+ return -1;
+ }
- const rw_pid_t ret = cwait (result, pid, WAIT_CHILD);
+ const HANDLE handle = HANDLE (pid);
- if (-1 == ret)
+ const DWORD milliseconds =
+ 0 > timeout ? INFINITE : DWORD (timeout * 1000);
+
+ const DWORD res = WaitForSingleObject (handle, milliseconds);
+
+ DWORD err = ERROR_SUCCESS;
+
+ if (WAIT_OBJECT_0 == res) {
+
+ DWORD dwExitCode;
+ if (GetExitCodeProcess (handle, &dwExitCode)) {
+
+ CloseHandle (handle);
+
+ if (dwExitCode)
+ rw_error (0, __FILE__, __LINE__,
+ "the process (pid=%{P}) exited with return code %d",
+ pid, int (dwExitCode));
+
+ if (result)
+ *result = int (dwExitCode);
+
+ return pid;
+ }
+
+ err = GetLastError ();
+ rw_error (0, __FILE__, __LINE__,
+ "GetExitCodeProcess (%{P}, %#p) failed: GetLastError() = %zu",
+ pid, &dwExitCode, size_t (err));
+ }
+ else if (WAIT_FAILED == res) {
+ err = GetLastError ();
rw_error (0, __FILE__, __LINE__,
- "cwait(%#p, %li, WAIT_CHILD) failed: errno = %{#m} (%{m})",
- result, pid);
+ "WaitForSingleObject (%{P}, %{?}INFINITE%{:}%zu%{;}) failed: "
+ "GetLastError() = %zu",
+ pid, INFINITE == milliseconds,
+ size_t (milliseconds), size_t (err));
+ }
+ else {
+ // time-out elapsed
+ RW_ASSERT (WAIT_TIMEOUT == res);
+ return 0;
+ }
+
+ if (ERROR_INVALID_HANDLE == err)
+ errno = ECHILD;
+ else
+ errno = _rw_map_errno (err);
+
+ return -1;
+}
+
+#else // #if !defined (_WIN32)
+
+extern "C" {
+
+typedef void sig_handler_t (int);
+
+static void
+sig_handler (int)
+{
+ // restore the signal
+ signal (SIGCHLD, sig_handler);
+}
-#else
+}
+_TEST_EXPORT rw_pid_t
+rw_waitpid (rw_pid_t pid, int* result, int timeout/* = -1*/)
+{
int status = 0;
- const rw_pid_t ret = waitpid (pid, &status, 0);
+ const int options = 0 > timeout ? 0 : WNOHANG;
+
+ rw_pid_t ret = waitpid (pid, &status, options);
+ if (-1 == ret)
+ rw_error (0, __FILE__, __LINE__,
+ "waitpid (%{P}, %#p, %{?}WNOHANG%{:}%i%{;}) failed: "
+ "errno = %{#m} (%{m})",
+ pid, &status, WNOHANG == options, options);
+
+ if (0 < timeout && 0 == ret) {
+ // process still active, wait
+ sig_handler_t* old_handler = signal (SIGCHLD, sig_handler);
+ timespec rem = { timeout, 0 };
+
+ do {
+ timespec req = rem;
+ if (-1 == nanosleep (&req, &rem)) {
+ if (EINTR == errno) {
+ // possible that the child has exited
+ ret = waitpid (pid, &status, WNOHANG);
+ if (-1 == ret) {
+ rw_error (0, __FILE__, __LINE__,
+ "waitpid (%{P}, %#p, WNOHANG) failed: "
+ "errno = %{#m} (%{m})",
+ pid, &status);
+ }
+ else if (0 == ret) {
+ // child still active
+ continue;
+ }
+ else {
+ // child has exited
+ RW_ASSERT (pid == ret);
+ }
+ }
+ else {
+ rw_error (0, __FILE__, __LINE__,
+ "nanosleep (&{%i, 0}, %#p) failed: "
+ "errno = %{#m} (%{m})",
+ timeout, &rem);
+
+ ret = -1;
+ }
+ }
+ else {
+ // timeout elapsed
+ RW_ASSERT (0 == ret);
+ }
+ }
+ while (false);
+
+ signal (SIGCHLD, old_handler);
+ }
if (ret == pid) {
-
+
if (WIFSIGNALED (status)) {
// process exited with a signal
const int signo = WTERMSIG (status);
rw_error (0, __FILE__, __LINE__,
- "the process (pid=%ld) exited with signal %d (%{K})",
- long (ret), signo, signo);
+ "the process (pid=%{P}) exited with signal %d (%{K})",
+ pid, signo, signo);
if (result)
*result = signo;
@@ -327,8 +565,8 @@
if (retcode)
rw_error (0, __FILE__, __LINE__,
- "the process (pid=%ld) exited with return code %d",
- long (ret), retcode);
+ "the process (pid=%{P}) exited with return code %d",
+ pid, retcode);
if (result)
*result = retcode;
@@ -337,12 +575,90 @@
*result = -1;
}
+ return ret;
+}
+
+#endif // #if defined (_WIN32)
+
+
+_TEST_EXPORT int
+rw_process_kill (rw_pid_t pid, int signo)
+{
+ // timeout for rw_wait_pid
+ const int timeout = 1000;
+
+#if defined (_WIN32)
+
+ // send signal
+ if (!TerminateProcess (HANDLE (pid), DWORD (signo))) {
+
+ const DWORD err = GetLastError ();
+ rw_error (0, __FILE__, __LINE__,
+ "TerminateProcess (%{P}, %i) failed: GetLastError() = %zu",
+ pid, signo, size_t (err));
+
+ if (ERROR_INVALID_HANDLE == err)
+ errno = ESRCH;
+ else if (ERROR_ACCESS_DENIED == err)
+ errno = EPERM;
+ else
+ errno = _rw_map_errno (err);
+
+ return -1;
+ }
+
+ // wait for process termination
+ int ret = rw_waitpid (pid, 0, timeout);
+ if (pid == ret)
+ return 0;
+
if (-1 == ret)
rw_error (0, __FILE__, __LINE__,
- "waitpid(%li, %#p, 0) failed: errno = %{#m} (%{m})",
- pid, &status);
+ "rw_waitpid (%{P}, 0, %i) failed: errno = %{#m} (%{m})",
+ pid, timeout);
+
+ return 1;
+
+#else // #if !defined (_WIN32)
+
+ static const int signals_ [] = {
+ SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGKILL
+ };
+
+ const int* const signals = (-1 == signo) ? signals_ : &signo;
+
+ const unsigned sigcount =
+ (-1 == signo) ? sizeof (signals_) / sizeof (*signals_) : 1;
-#endif // #if defined (_WIN32) || defined (_WIN64)
+ int ret = -1;
+
+ for (unsigned i = 0; i < sigcount; ++i) {
+
+ // send signal
+ ret = kill (pid, signals [i]);
+
+ if (-1 == ret) {
+ rw_error (0, __FILE__, __LINE__,
+ "kill (%{P}, %{K}) failed: errno = %{#m} (%{m})",
+ pid, signals [i]);
+
+ continue;
+ }
+
+ // wait for process termination
+ ret = rw_waitpid (pid, 0, timeout);
+ if (pid == ret)
+ return 0;
+
+ if (-1 == ret)
+ rw_error (0, __FILE__, __LINE__,
+ "rw_waitpid (%{P}, 0, %i) failed: errno = %{#m} (%{m})",
+ pid, timeout);
+
+ ret = 1;
+ }
return ret;
+
+#endif // #if defined (_WIN32)
}
Re: svn commit: r472469 - in /incubator/stdcxx/trunk/tests: include/rw_process.h
self/0.process.cpp src/process.cpp
Posted by Martin Sebor <se...@roguewave.com>.
faridz@apache.org wrote:
> Author: faridz
> Date: Wed Nov 8 04:27:17 2006
> New Revision: 472469
>
> URL: http://svn.apache.org/viewvc?view=rev&rev=472469
[...]
> Modified: incubator/stdcxx/trunk/tests/src/process.cpp
> URL: http://svn.apache.org/viewvc/incubator/stdcxx/trunk/tests/src/process.cpp?view=diff&rev=472469&r1=472468&r2=472469
> ==============================================================================
> --- incubator/stdcxx/trunk/tests/src/process.cpp (original)
> +++ incubator/stdcxx/trunk/tests/src/process.cpp Wed Nov 8 04:27:17 2006
[...]
> +# include <time.h> // for nanosleep()
This change is causing link failures Solaris builds.
See for example (search for nanosleep):
http://people.apache.org/~sebor/stdcxx/results/solaris-10-sparcv9-sunpro-64b-5.8-11s-log.gz.txt
On Solaris, the function is defined in the realtime
library, librt.so, which we don't link with:
http://docs.sun.com/app/docs/doc/816-5171/6mbb6dcoe?a=view#indexterm-87
Please note that nanosleep() is an optional (REALTIME)
feature of POSIX that's not required to be provided by
conforming implementations, so we can't assume it will
be available everywhere:
http://www.opengroup.org/onlinepubs/009695399/functions/nanosleep.html
Martin