aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2016-10-21 20:02:35 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2016-10-24 11:27:19 +0300
commita128eb0961ccf820ff2142897795b48723d842bd (patch)
tree291351ef0ed9f428b3f5c8a1bf22483d3350bc15
parent070d99ca6d714e8789e67e2e797a1bf16ba35528 (diff)
Make process status optional
-rw-r--r--butl/pager.cxx4
-rw-r--r--butl/process32
-rw-r--r--butl/process.cxx35
-rw-r--r--butl/process.ixx6
-rw-r--r--tests/process/driver.cxx20
5 files changed, 64 insertions, 33 deletions
diff --git a/butl/pager.cxx b/butl/pager.cxx
index 3d429f5..b154a5c 100644
--- a/butl/pager.cxx
+++ b/butl/pager.cxx
@@ -28,6 +28,10 @@ namespace butl
bool verbose,
const string* pager,
const vector<string>* pager_options)
+ // Create successfully exited process. Will "wait" for it if fallback to
+ // non-interactive execution path.
+ //
+ : p_ (optional<process::status_type> (0))
{
// If we are using the default pager, try to get the terminal width
// so that we can center the output.
diff --git a/butl/process b/butl/process
index e1eea29..c69c306 100644
--- a/butl/process
+++ b/butl/process
@@ -16,6 +16,7 @@
#include <butl/path>
#include <butl/export>
+#include <butl/optional>
namespace butl
{
@@ -106,6 +107,16 @@ namespace butl
class LIBBUTL_EXPORT process
{
public:
+#ifndef _WIN32
+ using handle_type = pid_t;
+ using id_type = pid_t;
+ using status_type = int;
+#else
+ using handle_type = void*; // Win32 HANDLE
+ using id_type = std::uint32_t; // Win32 DWORD
+ using status_type = std::uint32_t; // Win32 DWORD
+#endif
+
// Start another process using the specified command line. The default
// values to the in, out and err arguments indicate that the child process
// should inherit the parent process stdin, stdout, and stderr,
@@ -202,10 +213,13 @@ namespace butl
process (const process&) = delete;
process& operator= (const process&) = delete;
- // Create an empty or "already terminated" process. That is, handle is 0
- // and exit status is 0.
+ // Create an empty or "already terminated" process. By default the
+ // termination status is abnormal but you can change that.
+ //
+ // @@ Need to audit all calls (__attribute__((deprecated))).
//
- process ();
+ explicit
+ process (optional<status_type> status = nullopt);
// Resolve process' paths based on the initial path in args0. If recall
// differs from initial, adjust args0 to point to the recall path. If
@@ -274,22 +288,12 @@ namespace butl
print (std::ostream&, const char* const args[], size_t n = 0);
public:
-#ifndef _WIN32
- using handle_type = pid_t;
- using id_type = pid_t;
- using status_type = int;
-#else
- using handle_type = void*; // Win32 HANDLE
- using id_type = std::uint32_t; // Win32 DWORD
- using status_type = std::uint32_t; // Win32 DWORD
-#endif
-
static id_type
current_id ();
public:
handle_type handle;
- status_type status;
+ optional<status_type> status; // Absence means terminated abnormally.
int out_fd; // Write to this fd to send to the new process' stdin.
int in_ofd; // Read from this fd to receive from the new process' stdout.
diff --git a/butl/process.cxx b/butl/process.cxx
index 64366ba..3af42cf 100644
--- a/butl/process.cxx
+++ b/butl/process.cxx
@@ -385,25 +385,23 @@ namespace butl
{
if (handle != 0)
{
- int r (waitpid (handle, &status, 0));
+ status_type es;
+ int r (waitpid (handle, &es, 0));
handle = 0; // We have tried.
if (r == -1)
{
+ // If ignore errors then just leave status nullopt, so it has the same
+ // semantics as for abnormally terminated process.
+ //
if (!ie)
throw process_error (errno, false);
- else
- // Fold into status, so this and subsequent wait() calls return
- // false. There is no portable way to update the status bits
- // representing a process exit code specifically. So we set all bits
- // to 1 and recon on getting non-zero exit status wherever the exact
- // bits are.
- //
- status = ~0;
}
+ else if (WIFEXITED (es))
+ status = WEXITSTATUS (es);
}
- return WIFEXITED (status) && WEXITSTATUS (status) == 0;
+ return status && *status == 0;
}
bool process::
@@ -411,7 +409,8 @@ namespace butl
{
if (handle != 0)
{
- int r (waitpid (handle, &status, WNOHANG));
+ status_type es;
+ int r (waitpid (handle, &es, WNOHANG));
if (r == 0) // Not exited yet.
return false;
@@ -420,9 +419,12 @@ namespace butl
if (r == -1)
throw process_error (errno, false);
+
+ if (WIFEXITED (es))
+ status = WEXITSTATUS (es);
}
- s = WIFEXITED (status) && WEXITSTATUS (status) == 0;
+ s = status && *status == 0;
return true;
}
@@ -890,14 +892,15 @@ namespace butl
status = s;
else
{
+ // If ignore errors then just leave status nullopt, so it has the same
+ // semantics as for abnormally terminated process.
+ //
if (!ie)
throw process_error (error_msg (e));
- else
- status = 1; // Fold into status.
}
}
- return status == 0;
+ return status && *status == 0;
}
bool process::
@@ -923,7 +926,7 @@ namespace butl
status = s;
}
- s = status == 0;
+ s = status && *status == 0;
return true;
}
diff --git a/butl/process.ixx b/butl/process.ixx
index 83918bf..6d4dd2f 100644
--- a/butl/process.ixx
+++ b/butl/process.ixx
@@ -100,9 +100,9 @@ namespace butl
}
inline process::
- process ()
+ process (optional<status_type> s)
: handle (0),
- status (0), // This is a bit of an assumption.
+ status (s),
out_fd (-1),
in_ofd (-1),
in_efd (-1)
@@ -155,7 +155,7 @@ namespace butl
wait ();
handle = p.handle;
- status = p.status;
+ status = std::move (p.status);
out_fd = p.out_fd;
in_ofd = p.in_ofd;
in_efd = p.in_efd;
diff --git a/tests/process/driver.cxx b/tests/process/driver.cxx
index 0aa9177..d59b1a7 100644
--- a/tests/process/driver.cxx
+++ b/tests/process/driver.cxx
@@ -255,6 +255,26 @@ main (int argc, const char* argv[])
return 0;
}
+ // Test processes created as "already terminated".
+ //
+ {
+ process p;
+ assert (!p.wait ()); // "Terminated" abnormally.
+ }
+
+ {
+ // Note that if to create as just process(0) then the
+ // process(const char* args[], int=0, int=1, int=2) ctor is being called.
+ //
+ process p (optional<process::status_type> (0));
+ assert (p.wait ()); // "Exited" successfully.
+ }
+
+ {
+ process p (optional<process::status_type> (1));
+ assert (!p.wait ()); // "Exited" with an error.
+ }
+
const char* s ("ABC\nXYZ");
assert (exec (p));