From 06687881c1877d389559bdcf4350dbf1c647fb70 Mon Sep 17 00:00:00 2001 From: q66 Date: Sat, 13 May 2017 00:00:44 +0200 Subject: [PATCH] implement subprocess detach and validity checks --- ostd/process.hh | 44 +++++++++++++++++++++++++++++++++++++++----- src/win32/process.cc | 9 ++------- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/ostd/process.hh b/ostd/process.hh index 1441c0a..f24ff84 100644 --- a/ostd/process.hh +++ b/ostd/process.hh @@ -284,7 +284,7 @@ struct OSTD_EXPORT subprocess { * * @throws ostd::subprocess_error on failure of any kind. * - * @see open_path(), open_command() + * @see open_path(), open_command(), valid() */ int close(); @@ -297,11 +297,13 @@ struct OSTD_EXPORT subprocess { * * If `path` is empty, the first element of `args` is used. * - * If this fails, ostd::subprocess_error will be thrown. + * If this fails for any reason, ostd::subprocess_error will be thrown. + * Having another child process running is considered a failure, so + * use close() or detach() before opening another. * * On success, a new subprocess will be created and this will return * without waiting for it to finish. Use close() to wait and get the - * return code. + * return code or detach() to give up ownership of the child process. * * @throws ostd::subprocess_error on failure of any kind. * @@ -329,11 +331,13 @@ struct OSTD_EXPORT subprocess { * * If `cmd` is empty, the first element of `args` is used. * - * If this fails, ostd::subprocess_error will be thrown. + * If this fails for any reason, ostd::subprocess_error will be thrown. + * Having another child process running is considered a failure, so + * use close() or detach() before opening another. * * On success, a new subprocess will be created and this will return * without waiting for it to finish. Use close() to wait and get the - * return code. + * return code or detach() to give up ownership of the child process. * * @throws ostd::subprocess_error on failure of any kind. * @@ -350,6 +354,31 @@ struct OSTD_EXPORT subprocess { open_command(nullptr, std::forward(args)); } + /** @brief Detaches the child process. + * + * If there is a running child process, makes it so that it's no + * longer owned by this subprocess object. The child process will + * keep running even after the subprocess object has died. + * + * @throws ostd::subprocess_error if there is no child process. + * + * @see valid() + */ + void detach() { + if (!p_current) { + throw subprocess_error{"no child process"}; + } + reset(); + } + + /** @brief Checks if there is a running child process. + * + * @returns If there is one, true, otherwise false. + */ + bool valid() const noexcept { + return !!p_current; + } + private: template void open_full(string_range cmd, InputRange args, bool use_path) { @@ -357,6 +386,11 @@ private: std::is_constructible_v>, "The arguments must be strings" ); + + if (p_current) { + throw subprocess_error{"another child process already running"}; + } + std::vector argv; for (; !args.empty(); args.pop_front()) { argv.emplace_back(args.front()); diff --git a/src/win32/process.cc b/src/win32/process.cc index af37eff..24a1f00 100644 --- a/src/win32/process.cc +++ b/src/win32/process.cc @@ -390,32 +390,27 @@ OSTD_EXPORT void subprocess::open_impl( } OSTD_EXPORT void subprocess::reset() { - p_current = nullptr; + CloseHandle(static_cast(std::exchange(p_current, nullptr))); } OSTD_EXPORT int subprocess::close() { if (!p_current) { throw subprocess_error{"no child process"}; } - - HANDLE *proc = static_cast(p_current); + HANDLE proc = static_cast(p_current); if (WaitForSingleObject(proc, INFINITE) == WAIT_FAILED) { - CloseHandle(proc); reset(); throw subprocess_error{"child process wait failed"}; } DWORD ec = 0; if (!GetExitCodeProcess(proc, &ec)) { - CloseHandle(proc); reset(); throw subprocess_error{"could not retrieve exit code"}; } - CloseHandle(proc); reset(); - return int(ec); }