aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cru/common/SubProcess.h47
-rw-r--r--include/cru/common/platform/unix/PosixSpawnSubProcess.h7
-rw-r--r--src/common/SubProcess.cpp38
-rw-r--r--src/common/platform/unix/PosixSpawnSubProcess.cpp38
4 files changed, 94 insertions, 36 deletions
diff --git a/include/cru/common/SubProcess.h b/include/cru/common/SubProcess.h
index ba46d049..9a56b726 100644
--- a/include/cru/common/SubProcess.h
+++ b/include/cru/common/SubProcess.h
@@ -13,7 +13,7 @@
#include <vector>
namespace cru {
-enum class PlatformSubProcessStatus {
+enum class SubProcessStatus {
/**
* @brief The process has not been created and started.
*/
@@ -43,14 +43,40 @@ class CRU_BASE_API SubProcessFailedToStartException
using SubProcessException::SubProcessException;
};
-struct PlatformSubProcessStartInfo {
+class CRU_BASE_API SubProcessInternalException : public SubProcessException {
+ public:
+ using SubProcessException::SubProcessException;
+};
+
+struct SubProcessStartInfo {
String program;
std::vector<String> arguments;
std::unordered_map<String, String> environments;
};
-struct PlatformSubProcessExitResult {
+enum class SubProcessExitType {
+ Unknown,
+ Normal,
+ Signal,
+};
+
+struct SubProcessExitResult {
+ SubProcessExitType exit_type;
int exit_code;
+ int exit_signal;
+ bool has_core_dump;
+
+ static SubProcessExitResult Unknown() {
+ return {SubProcessExitType::Unknown, 0, 0, false};
+ }
+
+ static SubProcessExitResult Normal(int exit_code) {
+ return {SubProcessExitType::Normal, exit_code, 0, false};
+ }
+
+ static SubProcessExitResult Signal(int exit_signal, bool has_core_dump) {
+ return {SubProcessExitType::Normal, 0, exit_signal, has_core_dump};
+ }
};
/**
@@ -62,8 +88,7 @@ struct PlatformSubProcessExitResult {
*/
class PlatformSubProcessBase : public Object {
public:
- explicit PlatformSubProcessBase(
- const PlatformSubProcessStartInfo& start_info);
+ explicit PlatformSubProcessBase(const SubProcessStartInfo& start_info);
~PlatformSubProcessBase() override;
@@ -104,13 +129,13 @@ class PlatformSubProcessBase : public Object {
* actually running. Because there might be a window that the process exits
* already but status is not updated.
*/
- PlatformSubProcessStatus GetStatus();
+ SubProcessStatus GetStatus();
/**
* @brief Get the exit result. If the process is not started, failed to start
* or running, `SubProcessException` will be thrown.
*/
- PlatformSubProcessExitResult GetExitResult();
+ SubProcessExitResult GetExitResult();
virtual io::Stream* GetStdinStream() = 0;
virtual io::Stream* GetStdoutStream() = 0;
@@ -137,7 +162,7 @@ class PlatformSubProcessBase : public Object {
* This method will be called only once on another thread after
* `PlatformCreateProcess` returns successfully
*/
- virtual PlatformSubProcessExitResult PlatformWaitForProcess() = 0;
+ virtual SubProcessExitResult PlatformWaitForProcess() = 0;
/**
* @brief Kill the process immediately.
@@ -150,11 +175,11 @@ class PlatformSubProcessBase : public Object {
virtual void PlatformKillProcess() = 0;
protected:
- PlatformSubProcessStartInfo start_info_;
- PlatformSubProcessExitResult exit_result_;
+ SubProcessStartInfo start_info_;
+ SubProcessExitResult exit_result_;
private:
- PlatformSubProcessStatus status_;
+ SubProcessStatus status_;
std::thread process_thread_;
std::mutex process_mutex_;
diff --git a/include/cru/common/platform/unix/PosixSpawnSubProcess.h b/include/cru/common/platform/unix/PosixSpawnSubProcess.h
index 9a08daa6..e8e23738 100644
--- a/include/cru/common/platform/unix/PosixSpawnSubProcess.h
+++ b/include/cru/common/platform/unix/PosixSpawnSubProcess.h
@@ -4,6 +4,7 @@
#ifdef CRU_PLATFORM_UNIX
+#include "../../Base.h"
#include "../../SubProcess.h"
#include "../../io/AutoReadStream.h"
@@ -14,8 +15,10 @@
namespace cru::platform::unix {
class PosixSpawnSubProcess : public PlatformSubProcessBase {
+ CRU_DEFINE_CLASS_LOG_TAG(u"PosixSpawnSubProcess")
+
public:
- explicit PosixSpawnSubProcess(const PlatformSubProcessStartInfo& start_info);
+ explicit PosixSpawnSubProcess(const SubProcessStartInfo& start_info);
~PosixSpawnSubProcess();
io::Stream* GetStdinStream() override;
@@ -24,7 +27,7 @@ class PosixSpawnSubProcess : public PlatformSubProcessBase {
protected:
void PlatformCreateProcess() override;
- PlatformSubProcessExitResult PlatformWaitForProcess() override;
+ SubProcessExitResult PlatformWaitForProcess() override;
void PlatformKillProcess() override;
private:
diff --git a/src/common/SubProcess.cpp b/src/common/SubProcess.cpp
index eea83acd..ae1c5375 100644
--- a/src/common/SubProcess.cpp
+++ b/src/common/SubProcess.cpp
@@ -5,7 +5,7 @@
namespace cru {
PlatformSubProcessBase::PlatformSubProcessBase(
- const PlatformSubProcessStartInfo& start_info)
+ const SubProcessStartInfo& start_info)
: start_info_(start_info), process_lock_(process_mutex_, std::defer_lock) {}
PlatformSubProcessBase::~PlatformSubProcessBase() {}
@@ -13,28 +13,28 @@ PlatformSubProcessBase::~PlatformSubProcessBase() {}
void PlatformSubProcessBase::Start() {
std::lock_guard lock_guard(process_lock_);
- if (status_ != PlatformSubProcessStatus::Prepare) {
+ if (status_ != SubProcessStatus::Prepare) {
throw SubProcessException(u"The process has already tried to start.");
}
try {
PlatformCreateProcess();
- status_ = PlatformSubProcessStatus::Running;
+ status_ = SubProcessStatus::Running;
process_thread_ = std::thread([this] {
auto exit_result = PlatformWaitForProcess();
{
std::lock_guard lock_guard(process_lock_);
exit_result_ = std::move(exit_result);
- status_ = PlatformSubProcessStatus::Exited;
+ status_ = SubProcessStatus::Exited;
}
this->process_condition_variable_.notify_all();
});
process_thread_.detach();
} catch (const std::exception& e) {
- status_ = PlatformSubProcessStatus::FailedToStart;
+ status_ = SubProcessStatus::FailedToStart;
throw SubProcessFailedToStartException(u"Sub-process failed to start. " +
String::FromUtf8(e.what()));
}
@@ -44,23 +44,21 @@ void PlatformSubProcessBase::Wait(
std::optional<std::chrono::milliseconds> wait_time) {
std::lock_guard lock_guard(process_lock_);
- if (status_ == PlatformSubProcessStatus::Prepare) {
+ if (status_ == SubProcessStatus::Prepare) {
throw SubProcessException(
u"The process does not start. Can't wait for it.");
}
- if (status_ == PlatformSubProcessStatus::FailedToStart) {
+ if (status_ == SubProcessStatus::FailedToStart) {
throw SubProcessException(
u"The process failed to start. Can't wait for it.");
}
- if (status_ == PlatformSubProcessStatus::Exited) {
+ if (status_ == SubProcessStatus::Exited) {
return;
}
- auto predicate = [this] {
- return status_ == PlatformSubProcessStatus::Exited;
- };
+ auto predicate = [this] { return status_ == SubProcessStatus::Exited; };
if (wait_time) {
process_condition_variable_.wait_for(process_lock_, *wait_time, predicate);
@@ -70,42 +68,42 @@ void PlatformSubProcessBase::Wait(
}
void PlatformSubProcessBase::Kill() {
- auto status = GetStatus();
+ std::lock_guard data_lock_guard(process_lock_);
- if (status == PlatformSubProcessStatus::Prepare) {
+ if (status_ == SubProcessStatus::Prepare) {
throw SubProcessException(u"The process does not start. Can't kill it.");
}
- if (status == PlatformSubProcessStatus::FailedToStart) {
+ if (status_ == SubProcessStatus::FailedToStart) {
throw SubProcessException(u"The process failed to start. Can't kill it.");
}
- if (status == PlatformSubProcessStatus::Exited) {
+ if (status_ == SubProcessStatus::Exited) {
return;
}
PlatformKillProcess();
}
-PlatformSubProcessStatus PlatformSubProcessBase::GetStatus() {
+SubProcessStatus PlatformSubProcessBase::GetStatus() {
std::lock_guard data_lock_guard(process_lock_);
return status_;
}
-PlatformSubProcessExitResult PlatformSubProcessBase::GetExitResult() {
+SubProcessExitResult PlatformSubProcessBase::GetExitResult() {
std::lock_guard lock_guard(process_lock_);
- if (status_ == PlatformSubProcessStatus::Prepare) {
+ if (status_ == SubProcessStatus::Prepare) {
throw SubProcessException(
u"The process does not start. Can't get exit result.");
}
- if (status_ == PlatformSubProcessStatus::FailedToStart) {
+ if (status_ == SubProcessStatus::FailedToStart) {
throw SubProcessException(
u"The process failed to start. Can't get exit result.");
}
- if (status_ == PlatformSubProcessStatus::Running) {
+ if (status_ == SubProcessStatus::Running) {
throw SubProcessException(
u"The process is running. Can't get exit result.");
}
diff --git a/src/common/platform/unix/PosixSpawnSubProcess.cpp b/src/common/platform/unix/PosixSpawnSubProcess.cpp
index 8b9a9a47..2528c5ec 100644
--- a/src/common/platform/unix/PosixSpawnSubProcess.cpp
+++ b/src/common/platform/unix/PosixSpawnSubProcess.cpp
@@ -4,15 +4,18 @@
#include "cru/common/Guard.h"
#include "cru/common/String.h"
#include "cru/common/SubProcess.h"
+#include "cru/common/log/Logger.h"
+#include <signal.h>
#include <spawn.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <memory>
#include <unordered_map>
namespace cru::platform::unix {
PosixSpawnSubProcess::PosixSpawnSubProcess(
- const PlatformSubProcessStartInfo& start_info)
+ const SubProcessStartInfo& start_info)
: PlatformSubProcessBase(start_info),
pid_(0),
exit_code_(0),
@@ -128,7 +131,36 @@ void PosixSpawnSubProcess::PlatformCreateProcess() {
check_error(u"Failed to call posix_spawnp.");
}
-PlatformSubProcessExitResult PosixSpawnSubProcess::PlatformWaitForProcess() {}
+SubProcessExitResult PosixSpawnSubProcess::PlatformWaitForProcess() {
+ int wstatus;
-void PosixSpawnSubProcess::PlatformKillProcess() {}
+ while (waitpid(pid_, &wstatus, 0) == -1) {
+ if (errno == EINTR) {
+ CRU_LOG_INFO(u"Waitpid is interrupted by a signal. Call it again.");
+ continue;
+ }
+
+ std::unique_ptr<ErrnoException> inner(new ErrnoException({}, errno));
+
+ throw SubProcessInternalException(
+ u"Failed to call waitpid on a subprocess.", std::move(inner));
+ }
+
+ if (WIFEXITED(wstatus)) {
+ return SubProcessExitResult::Normal(WEXITSTATUS(wstatus));
+ } else if (WIFEXITED(wstatus)) {
+ return SubProcessExitResult::Signal(WTERMSIG(wstatus), WCOREDUMP(wstatus));
+ } else {
+ return SubProcessExitResult::Unknown();
+ }
+}
+
+void PosixSpawnSubProcess::PlatformKillProcess() {
+ int error = kill(pid_, SIGKILL);
+ if (error != 0) {
+ std::unique_ptr<ErrnoException> inner(new ErrnoException({}, errno));
+ throw SubProcessInternalException(u"Failed to call kill on a subprocess.",
+ std::move(inner));
+ }
+}
} // namespace cru::platform::unix