stdcxx-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From far...@apache.org
Subject svn commit: r472469 - in /incubator/stdcxx/trunk/tests: include/rw_process.h self/0.process.cpp src/process.cpp
Date Wed, 08 Nov 2006 12:27:17 GMT
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 <faridz@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)
 }



Mime
View raw message