diff options
author | crupest <crupest@outlook.com> | 2024-10-06 13:57:39 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2024-10-06 13:57:39 +0800 |
commit | dfe62dcf8bcefc523b466e127c3edc4dc2756629 (patch) | |
tree | 1c751a14ba0da07ca2ff805633f97568060aa4c9 /src/base/SubProcess.cpp | |
parent | f51eb955e188858272230a990565931e7403f23b (diff) | |
download | cru-dfe62dcf8bcefc523b466e127c3edc4dc2756629.tar.gz cru-dfe62dcf8bcefc523b466e127c3edc4dc2756629.tar.bz2 cru-dfe62dcf8bcefc523b466e127c3edc4dc2756629.zip |
Rename common to base.
Diffstat (limited to 'src/base/SubProcess.cpp')
-rw-r--r-- | src/base/SubProcess.cpp | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/src/base/SubProcess.cpp b/src/base/SubProcess.cpp new file mode 100644 index 00000000..1133b848 --- /dev/null +++ b/src/base/SubProcess.cpp @@ -0,0 +1,209 @@ +#include "cru/base/SubProcess.h" + +#include <thread> + +#ifdef CRU_PLATFORM_UNIX +#include "cru/base/platform/unix/PosixSpawnSubProcess.h" +#endif + +namespace cru { + +#ifdef CRU_PLATFORM_UNIX +using ThisPlatformSubProcessImpl = platform::unix::PosixSpawnSubProcessImpl; +#endif + +PlatformSubProcess::PlatformSubProcess( + SubProcessStartInfo start_info, + std::shared_ptr<IPlatformSubProcessImpl> impl) + : state_(new State(std::move(start_info), std::move(impl))), + lock_(state_->mutex, std::defer_lock) {} + +PlatformSubProcess::~PlatformSubProcess() {} + +void PlatformSubProcess::Start() { + std::lock_guard lock_guard(this->lock_); + + if (this->state_->status != SubProcessStatus::Prepare) { + throw SubProcessException(u"The process has already tried to start."); + } + + try { + this->state_->impl->PlatformCreateProcess(this->state_->start_info); + this->state_->status = SubProcessStatus::Running; + + auto thread = std::thread([state = state_] { + std::unique_lock lock(state->mutex); + state->exit_result = state->impl->PlatformWaitForProcess(); + state->status = SubProcessStatus::Exited; + state->condition_variable.notify_all(); + }); + + thread.detach(); + } catch (const std::exception& e) { + this->state_->status = SubProcessStatus::FailedToStart; + throw SubProcessFailedToStartException(u"Sub-process failed to start. " + + String::FromUtf8(e.what())); + } +} + +void PlatformSubProcess::Wait( + std::optional<std::chrono::milliseconds> wait_time) { + std::lock_guard lock_guard(this->lock_); + + if (this->state_->status == SubProcessStatus::Prepare) { + throw SubProcessException( + u"The process does not start. Can't wait for it."); + } + + if (this->state_->status == SubProcessStatus::FailedToStart) { + throw SubProcessException( + u"The process failed to start. Can't wait for it."); + } + + if (this->state_->status == SubProcessStatus::Exited) { + return; + } + + auto predicate = [this] { + return this->state_->status == SubProcessStatus::Exited; + }; + + if (wait_time) { + this->state_->condition_variable.wait_for(this->lock_, *wait_time, + predicate); + } else { + this->state_->condition_variable.wait(this->lock_, predicate); + } +} + +void PlatformSubProcess::Kill() { + std::lock_guard lock_guard(this->lock_); + + if (this->state_->status == SubProcessStatus::Prepare) { + throw SubProcessException(u"The process does not start. Can't kill it."); + } + + if (this->state_->status == SubProcessStatus::FailedToStart) { + throw SubProcessException(u"The process failed to start. Can't kill it."); + } + + if (this->state_->status == SubProcessStatus::Exited) { + return; + } + + if (this->state_->killed) { + return; + } + + this->state_->impl->PlatformKillProcess(); + this->state_->killed = true; +} + +SubProcessStatus PlatformSubProcess::GetStatus() { + std::lock_guard lock_guard(this->lock_); + return this->state_->status; +} + +SubProcessExitResult PlatformSubProcess::GetExitResult() { + std::lock_guard lock_guard(this->lock_); + + if (this->state_->status == SubProcessStatus::Prepare) { + throw SubProcessException( + u"The process does not start. Can't get exit result."); + } + + if (this->state_->status == SubProcessStatus::FailedToStart) { + throw SubProcessException( + u"The process failed to start. Can't get exit result."); + } + + if (this->state_->status == SubProcessStatus::Running) { + throw SubProcessException( + u"The process is running. Can't get exit result."); + } + + return this->state_->exit_result; +} + +io::Stream* PlatformSubProcess::GetStdinStream() { + return this->state_->impl->GetStdinStream(); +} + +io::Stream* PlatformSubProcess::GetStdoutStream() { + return this->state_->impl->GetStdoutStream(); +} + +io::Stream* PlatformSubProcess::GetStderrStream() { + return this->state_->impl->GetStderrStream(); +} + +SubProcess SubProcess::Create(String program, std::vector<String> arguments, + std::unordered_map<String, String> environments) { + SubProcessStartInfo start_info; + start_info.program = std::move(program); + start_info.arguments = std::move(arguments); + start_info.environments = std::move(environments); + return SubProcess(std::move(start_info)); +} + +SubProcessExitResult SubProcess::Call( + String program, std::vector<String> arguments, + std::unordered_map<String, String> environments) { + auto process = + Create(std::move(program), std::move(arguments), std::move(environments)); + process.Wait(); + return process.GetExitResult(); +} + +SubProcess::SubProcess(SubProcessStartInfo start_info) { + platform_process_.reset(new PlatformSubProcess( + std::move(start_info), std::make_shared<ThisPlatformSubProcessImpl>())); + platform_process_->Start(); +} + +SubProcess::~SubProcess() {} + +void SubProcess::Wait(std::optional<std::chrono::milliseconds> wait_time) { + CheckValid(); + platform_process_->Wait(wait_time); +} + +void SubProcess::Kill() { + CheckValid(); + platform_process_->Kill(); +} + +SubProcessStatus SubProcess::GetStatus() { + CheckValid(); + return platform_process_->GetStatus(); +} + +SubProcessExitResult SubProcess::GetExitResult() { + CheckValid(); + return platform_process_->GetExitResult(); +} + +io::Stream* SubProcess::GetStdinStream() { + CheckValid(); + return platform_process_->GetStdinStream(); +} + +io::Stream* SubProcess::GetStdoutStream() { + CheckValid(); + return platform_process_->GetStdoutStream(); +} + +io::Stream* SubProcess::GetStderrStream() { + CheckValid(); + return platform_process_->GetStderrStream(); +} + +void SubProcess::Detach() { auto p = platform_process_.release(); } + +void SubProcess::CheckValid() const { + if (!IsValid()) { + throw SubProcessException(u"SubProcess instance is invalid."); + } +} + +} // namespace cru |