aboutsummaryrefslogtreecommitdiff
path: root/libbutl
diff options
context:
space:
mode:
Diffstat (limited to 'libbutl')
-rw-r--r--libbutl/process.cxx57
-rw-r--r--libbutl/process.mxx30
2 files changed, 63 insertions, 24 deletions
diff --git a/libbutl/process.cxx b/libbutl/process.cxx
index 0695493..3383a73 100644
--- a/libbutl/process.cxx
+++ b/libbutl/process.cxx
@@ -805,13 +805,15 @@ namespace butl
void process::
kill ()
{
- if (handle != 0)
- {
- if (::kill (handle, SIGKILL) == -1)
- throw process_error (errno);
+ if (handle != 0 && ::kill (handle, SIGKILL) == -1)
+ throw process_error (errno);
+ }
- wait ();
- }
+ void process::
+ term ()
+ {
+ if (handle != 0 && ::kill (handle, SIGTERM) == -1)
+ throw process_error (errno);
}
process::id_type process::
@@ -1958,9 +1960,16 @@ namespace butl
optional<bool> process::
try_wait ()
{
+ return timed_wait (chrono::milliseconds (0));
+ }
+
+ template <>
+ optional<bool> process::
+ timed_wait (const chrono::milliseconds& t)
+ {
if (handle != 0)
{
- DWORD r (WaitForSingleObject (handle, 0));
+ DWORD r (WaitForSingleObject (handle, static_cast<DWORD> (t.count ())));
if (r == WAIT_TIMEOUT)
return nullopt;
@@ -1982,17 +1991,33 @@ namespace butl
return exit ? static_cast<bool> (*exit) : optional<bool> ();
}
- template <>
- optional<bool> process::
- timed_wait (const chrono::milliseconds&)
+ void process::
+ kill ()
{
- throw process_error (ENOTSUP);
+ // Note that TerminateProcess() requires an exit code the process will be
+ // terminated with. We could probably craft a custom exit code that will
+ // be treated by the normal() function as an abnormal termination.
+ // However, let's keep it simple and reuse the existing (semantically
+ // close) error code.
+ //
+ if (handle != 0 && !TerminateProcess (handle, DBG_TERMINATE_PROCESS))
+ {
+ DWORD e (GetLastError ());
+ if (e != ERROR_ACCESS_DENIED)
+ throw process_error (error_msg (e));
+
+ // Handle the case when the process has already terminated or is still
+ // exiting (potentially after being killed).
+ //
+ if (!try_wait ())
+ throw process_error (error_msg (e), EPERM);
+ }
}
void process::
- kill ()
+ term ()
{
- throw process_error (ENOTSUP);
+ kill ();
}
process::id_type process::
@@ -2023,7 +2048,7 @@ namespace butl
// [ 0, 16) - program exit code or exception code
// [16, 29) - facility
// [29, 30) - flag indicating if the status value is customer-defined
- // [30, 31) - severity (00 -success, 01 - informational, 10 - warning,
+ // [30, 31] - severity (00 -success, 01 - informational, 10 - warning,
// 11 - error)
//
: status (c)
@@ -2094,6 +2119,10 @@ namespace butl
case STATUS_STACK_BUFFER_OVERRUN: return "aborted";
case STATUS_STACK_OVERFLOW: return "stack overflow";
+ // Presumably the kill() function was called for the process.
+ //
+ case DBG_TERMINATE_PROCESS: return "killed";
+
default:
{
string desc ("unknown error 0x");
diff --git a/libbutl/process.mxx b/libbutl/process.mxx
index 54abdec..9106549 100644
--- a/libbutl/process.mxx
+++ b/libbutl/process.mxx
@@ -360,27 +360,37 @@ LIBBUTL_MODEXPORT namespace butl
// 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.
+ // Note that the destructor will wait for the process but will ignore
+ // any errors and the exit status.
+ //
+ ~process () {if (handle != 0) wait (true);}
+
+ // Process termination.
//
- // 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.
+
+ // Send SIGKILL to the process on POSIX and call TerminateProcess() with
+ // DBG_TERMINATE_PROCESS exit code on Windows. Noop for an already
+ // terminated process.
+ //
+ // Note that if the process is killed, it terminates as if it has called
+ // abort() (functions registered with atexit() are not called, etc).
//
- // Note: not yet implemented on Windows.
+ // Also note that on Windows calling this function for a terminating
+ // process results in the EPERM process_error exception.
//
void
kill ();
- // Note that the destructor will wait for the process but will ignore
- // any errors and the exit status.
+ // Send SIGTERM to the process on POSIX and call kill() on Windows (where
+ // there is no general way to terminate a console process gracefully).
+ // Noop for an already terminated process.
//
- ~process () {if (handle != 0) wait (true);}
+ void
+ term ();
// Moveable-only type.
//