diff options
author | crupest <crupest@outlook.com> | 2024-06-01 17:09:44 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2024-06-08 22:20:50 +0800 |
commit | b165ebcfb9c278dc7481abb5287e85672731c005 (patch) | |
tree | 58dd27a138fdd9ec656a79fa320081d95bed74cc | |
parent | d1f409530db6f9b712fd672c4c3154cac7eebad1 (diff) | |
download | cru-b165ebcfb9c278dc7481abb5287e85672731c005.tar.gz cru-b165ebcfb9c278dc7481abb5287e85672731c005.tar.bz2 cru-b165ebcfb9c278dc7481abb5287e85672731c005.zip |
HALF WORK: finish implementing PosixSpawnSubProcess.
-rw-r--r-- | include/cru/common/SubProcess.h | 47 | ||||
-rw-r--r-- | include/cru/common/platform/unix/PosixSpawnSubProcess.h | 7 | ||||
-rw-r--r-- | src/common/SubProcess.cpp | 38 | ||||
-rw-r--r-- | src/common/platform/unix/PosixSpawnSubProcess.cpp | 38 |
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 |