forked from OctaForge/libostd
implement subprocess detach and validity checks
This commit is contained in:
parent
5bbee30f6c
commit
06687881c1
|
@ -284,7 +284,7 @@ struct OSTD_EXPORT subprocess {
|
||||||
*
|
*
|
||||||
* @throws ostd::subprocess_error on failure of any kind.
|
* @throws ostd::subprocess_error on failure of any kind.
|
||||||
*
|
*
|
||||||
* @see open_path(), open_command()
|
* @see open_path(), open_command(), valid()
|
||||||
*/
|
*/
|
||||||
int close();
|
int close();
|
||||||
|
|
||||||
|
@ -297,11 +297,13 @@ struct OSTD_EXPORT subprocess {
|
||||||
*
|
*
|
||||||
* If `path` is empty, the first element of `args` is used.
|
* 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
|
* 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
|
* 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.
|
* @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 `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
|
* 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
|
* 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.
|
* @throws ostd::subprocess_error on failure of any kind.
|
||||||
*
|
*
|
||||||
|
@ -350,6 +354,31 @@ struct OSTD_EXPORT subprocess {
|
||||||
open_command(nullptr, std::forward<InputRange>(args));
|
open_command(nullptr, std::forward<InputRange>(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:
|
private:
|
||||||
template<typename InputRange>
|
template<typename InputRange>
|
||||||
void open_full(string_range cmd, InputRange args, bool use_path) {
|
void open_full(string_range cmd, InputRange args, bool use_path) {
|
||||||
|
@ -357,6 +386,11 @@ private:
|
||||||
std::is_constructible_v<std::string, range_reference_t<InputRange>>,
|
std::is_constructible_v<std::string, range_reference_t<InputRange>>,
|
||||||
"The arguments must be strings"
|
"The arguments must be strings"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (p_current) {
|
||||||
|
throw subprocess_error{"another child process already running"};
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> argv;
|
std::vector<std::string> argv;
|
||||||
for (; !args.empty(); args.pop_front()) {
|
for (; !args.empty(); args.pop_front()) {
|
||||||
argv.emplace_back(args.front());
|
argv.emplace_back(args.front());
|
||||||
|
|
|
@ -390,32 +390,27 @@ OSTD_EXPORT void subprocess::open_impl(
|
||||||
}
|
}
|
||||||
|
|
||||||
OSTD_EXPORT void subprocess::reset() {
|
OSTD_EXPORT void subprocess::reset() {
|
||||||
p_current = nullptr;
|
CloseHandle(static_cast<HANDLE>(std::exchange(p_current, nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
OSTD_EXPORT int subprocess::close() {
|
OSTD_EXPORT int subprocess::close() {
|
||||||
if (!p_current) {
|
if (!p_current) {
|
||||||
throw subprocess_error{"no child process"};
|
throw subprocess_error{"no child process"};
|
||||||
}
|
}
|
||||||
|
HANDLE proc = static_cast<HANDLE>(p_current);
|
||||||
HANDLE *proc = static_cast<HANDLE *>(p_current);
|
|
||||||
|
|
||||||
if (WaitForSingleObject(proc, INFINITE) == WAIT_FAILED) {
|
if (WaitForSingleObject(proc, INFINITE) == WAIT_FAILED) {
|
||||||
CloseHandle(proc);
|
|
||||||
reset();
|
reset();
|
||||||
throw subprocess_error{"child process wait failed"};
|
throw subprocess_error{"child process wait failed"};
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD ec = 0;
|
DWORD ec = 0;
|
||||||
if (!GetExitCodeProcess(proc, &ec)) {
|
if (!GetExitCodeProcess(proc, &ec)) {
|
||||||
CloseHandle(proc);
|
|
||||||
reset();
|
reset();
|
||||||
throw subprocess_error{"could not retrieve exit code"};
|
throw subprocess_error{"could not retrieve exit code"};
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle(proc);
|
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
return int(ec);
|
return int(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue