aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2018-08-17 23:14:37 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2018-08-20 17:37:44 +0300
commit41f958c4cf68541a48b20ceb608afe2308a6e6a7 (patch)
treec4e92865e98b7a3b911d355525e984151b44da22
parentc00321b94d6b952d59257b4a7373a1199e874d98 (diff)
Add timed_wait() and kill() to process class, change try_wait() signature
Review Backup
-rw-r--r--libbutl/pager.cxx3
-rw-r--r--libbutl/process.cxx69
-rw-r--r--libbutl/process.ixx20
-rw-r--r--libbutl/process.mxx29
-rw-r--r--tests/process/driver.cxx8
5 files changed, 101 insertions, 28 deletions
diff --git a/libbutl/pager.cxx b/libbutl/pager.cxx
index 12ad991..3013618 100644
--- a/libbutl/pager.cxx
+++ b/libbutl/pager.cxx
@@ -168,8 +168,7 @@ namespace butl
Sleep (50);
#endif
- bool r;
- if (p_.try_wait (r))
+ if (p_.try_wait ())
{
p_.out_fd.reset ();
diff --git a/libbutl/process.cxx b/libbutl/process.cxx
index 384af29..c0914b4 100644
--- a/libbutl/process.cxx
+++ b/libbutl/process.cxx
@@ -9,7 +9,7 @@
#include <errno.h>
#ifndef _WIN32
-# include <signal.h> // SIG*
+# include <signal.h> // SIG*, kill()
# include <unistd.h> // execvp, fork, dup2, pipe, chdir, *_FILENO, getpid
# include <sys/wait.h> // waitpid
# include <sys/types.h> // _stat
@@ -46,6 +46,7 @@
#ifndef __cpp_lib_modules
#include <string>
#include <vector>
+#include <chrono>
#include <cstdint>
#include <cstddef>
#include <system_error>
@@ -55,10 +56,11 @@
#include <utility> // move()
#include <ostream>
-#ifdef _WIN32
+#ifndef _WIN32
+#include <thread> // this_thread::sleep_for()
+#else
#include <map>
#include <ratio> // milli
-#include <chrono>
#include <cstdlib> // __argv[]
#include <algorithm> // find()
#endif
@@ -87,6 +89,10 @@ import butl.vector_view;
import butl.small_vector;
#endif
+#ifndef _WIN32
+import std.threading;
+#endif
+
import butl.utility; // casecmp()
import butl.fdstream; // fdnull()
#else
@@ -509,7 +515,7 @@ namespace butl
return exit && exit->normal () && exit->code () == 0;
}
- bool process::
+ optional<bool> process::
try_wait ()
{
if (handle != 0)
@@ -518,7 +524,7 @@ namespace butl
int r (waitpid (handle, &es, WNOHANG));
if (r == 0) // Not exited yet.
- return false;
+ return nullopt;
handle = 0; // We have tried.
@@ -528,7 +534,39 @@ namespace butl
exit = process_exit (es, process_exit::as_status);
}
- return true;
+ return exit ? static_cast<bool> (*exit) : optional<bool> ();
+ }
+
+ template <>
+ optional<bool> process::
+ timed_wait (const chrono::milliseconds& tm)
+ {
+ using namespace chrono;
+
+ // On POSIX this seems to be the best way for multi-threaded processes.
+ //
+ const milliseconds sd (10);
+ for (milliseconds d (tm); !try_wait (); d -= sd)
+ {
+ this_thread::sleep_for (d < sd ? d : sd);
+
+ if (d < sd)
+ break;
+ }
+
+ return try_wait ();
+ }
+
+ void process::
+ kill ()
+ {
+ if (handle != 0)
+ {
+ if (::kill (handle, SIGKILL) == -1)
+ throw process_error (errno);
+
+ wait ();
+ }
}
process::id_type process::
@@ -1665,14 +1703,14 @@ namespace butl
return exit && exit->normal () && exit->code () == 0;
}
- bool process::
+ optional<bool> process::
try_wait ()
{
if (handle != 0)
{
DWORD r (WaitForSingleObject (handle, 0));
if (r == WAIT_TIMEOUT)
- return false;
+ return nullopt;
DWORD es;
DWORD e (NO_ERROR);
@@ -1689,7 +1727,20 @@ namespace butl
exit->status = es;
}
- return true;
+ return exit ? static_cast<bool> (*exit) : optional<bool> ();
+ }
+
+ template <>
+ optional<bool> process::
+ timed_wait (const chrono::milliseconds&)
+ {
+ throw process_error (ENOTSUP);
+ }
+
+ void process::
+ kill ()
+ {
+ throw process_error (ENOTSUP);
}
process::id_type process::
diff --git a/libbutl/process.ixx b/libbutl/process.ixx
index c2cd892..df3b588 100644
--- a/libbutl/process.ixx
+++ b/libbutl/process.ixx
@@ -223,14 +223,18 @@ namespace butl
return *this;
}
- inline bool process::
- try_wait (bool& s)
- {
- bool r (try_wait ());
-
- if (r)
- s = exit && exit->normal () && exit->code () == 0;
+ // Implement timed_wait() function templates in terms of their milliseconds
+ // specialization.
+ //
+ template <>
+ optional<bool> process::
+ timed_wait (const std::chrono::milliseconds&);
- return r;
+ template <typename R, typename P>
+ inline optional<bool> process::
+ timed_wait (const std::chrono::duration<R, P>& d)
+ {
+ using namespace std::chrono;
+ return timed_wait (duration_cast<milliseconds> (d));
}
}
diff --git a/libbutl/process.mxx b/libbutl/process.mxx
index 0894515..a536fe9 100644
--- a/libbutl/process.mxx
+++ b/libbutl/process.mxx
@@ -15,6 +15,7 @@
#ifndef __cpp_lib_modules
#include <string>
#include <vector>
+#include <chrono>
#include <ostream>
#include <cstddef> // size_t
#include <cstdint> // uint32_t
@@ -337,14 +338,32 @@ LIBBUTL_MODEXPORT namespace butl
bool
wait (bool ignore_errors = false);
- // Return true if the process has already terminated in which case
- // optionally set the argument to the result of wait().
+ // Return the same result as wait() if the process has already terminated
+ // and nullopt otherwise.
//
- bool
+ optional<bool>
try_wait ();
- bool
- try_wait (bool&);
+ // Wait for the process to terminate for up to the specified time
+ // duration. Return the same result as wait() if the process has
+ // terminated in this timeframe and nullopt otherwise.
+ //
+ // Note: not yet implemented on Windows.
+ //
+ template <typename R, typename P>
+ optional<bool>
+ timed_wait (const std::chrono::duration<R, P>&);
+
+ // Terminate the process.
+ //
+ // On POSIX send SIGKILL to the process and wait until it terminates. The
+ // process exit information is available after the call returns. Noop for
+ // an already terminated process.
+ //
+ // Note: not yet implemented on Windows.
+ //
+ void
+ kill ();
// Note that the destructor will wait for the process but will ignore
// any errors and the exit status.
diff --git a/tests/process/driver.cxx b/tests/process/driver.cxx
index 6ca28a5..18017a7 100644
--- a/tests/process/driver.cxx
+++ b/tests/process/driver.cxx
@@ -49,6 +49,7 @@ exec (const path& p,
bool env = false) // Set environment variables for the child.
{
using cstrings = vector<const char*>;
+ using butl::optional;
assert (!in.empty () || (!out && !err)); // Nothing to output if no input.
assert (!pipeline || out); // To pipeline need to output something.
@@ -95,8 +96,7 @@ exec (const path& p,
{
if (!in.empty ())
{
- bool s;
- r = !pr.try_wait (s); // Couldn't exit as waiting for the input.
+ r = !pr.try_wait (); // Couldn't exit as waiting for the input.
auto bin_mode = [bin](auto_fd fd) -> auto_fd
{
@@ -170,8 +170,8 @@ exec (const path& p,
r = false;
}
- bool s;
- return pr.wait () && pr.try_wait (s) && s && r;
+ optional<bool> s;
+ return pr.wait () && (s = pr.try_wait ()) && *s && r;
}
catch (const process_error& e)
{