From 41f958c4cf68541a48b20ceb608afe2308a6e6a7 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 17 Aug 2018 23:14:37 +0300 Subject: Add timed_wait() and kill() to process class, change try_wait() signature Review Backup --- libbutl/pager.cxx | 3 +-- libbutl/process.cxx | 69 +++++++++++++++++++++++++++++++++++++++++------- libbutl/process.ixx | 20 ++++++++------ libbutl/process.mxx | 29 ++++++++++++++++---- tests/process/driver.cxx | 8 +++--- 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 #ifndef _WIN32 -# include // SIG* +# include // SIG*, kill() # include // execvp, fork, dup2, pipe, chdir, *_FILENO, getpid # include // waitpid # include // _stat @@ -46,6 +46,7 @@ #ifndef __cpp_lib_modules #include #include +#include #include #include #include @@ -55,10 +56,11 @@ #include // move() #include -#ifdef _WIN32 +#ifndef _WIN32 +#include // this_thread::sleep_for() +#else #include #include // milli -#include #include // __argv[] #include // 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 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 (*exit) : optional (); + } + + template <> + optional 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 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 (*exit) : optional (); + } + + template <> + optional 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 process:: + timed_wait (const std::chrono::milliseconds&); - return r; + template + inline optional process:: + timed_wait (const std::chrono::duration& d) + { + using namespace std::chrono; + return timed_wait (duration_cast (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 #include +#include #include #include // size_t #include // 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 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 + optional + timed_wait (const std::chrono::duration&); + + // 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; + 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 s; + return pr.wait () && (s = pr.try_wait ()) && *s && r; } catch (const process_error& e) { -- cgit v1.1