This is an automated email from the ASF dual-hosted git repository. isapego pushed a commit to branch ignite-17424 in repository https://gitbox.apache.org/repos/asf/ignite-3.git
commit 4ca5c36ef09aff87b2b676e8ea14a267b473b3cd Author: Igor Sapego <[email protected]> AuthorDate: Tue Aug 30 17:14:32 2022 +0400 IGNITE-17424 Re-factoring --- modules/platforms/cpp/client-test/src/main.cpp | 3 + .../cpp/client/include/ignite/ignite_client.h | 38 +++++ modules/platforms/cpp/client/src/ignite_client.cpp | 23 +++ modules/platforms/cpp/common/Platform.h | 12 +- modules/platforms/cpp/test-common/CMakeLists.txt | 3 +- .../cpp/test-common/include/ignite_node.h | 80 +++++----- .../platforms/cpp/test-common/include/process.h | 76 ++++----- .../platforms/cpp/test-common/include/test_utils.h | 34 ++-- .../platforms/cpp/test-common/src/ignite_node.cpp | 77 ++++----- modules/platforms/cpp/test-common/src/process.cpp | 160 ++----------------- .../platforms/cpp/test-common/src/test_utils.cpp | 112 ++++++------- .../cpp/test-common/src/win/win_process.h | 173 +++++++++++++++++++++ 12 files changed, 461 insertions(+), 330 deletions(-) diff --git a/modules/platforms/cpp/client-test/src/main.cpp b/modules/platforms/cpp/client-test/src/main.cpp index 1f8eeae3d6..50dcdff9da 100644 --- a/modules/platforms/cpp/client-test/src/main.cpp +++ b/modules/platforms/cpp/client-test/src/main.cpp @@ -21,6 +21,9 @@ #include "ignite_node.h" +/** + * Run prior to any other tests. + */ void BeforeAll() { ignite::IgniteNode node; diff --git a/modules/platforms/cpp/client/include/ignite/ignite_client.h b/modules/platforms/cpp/client/include/ignite/ignite_client.h index e69de29bb2..03dbfe0ce3 100644 --- a/modules/platforms/cpp/client/include/ignite/ignite_client.h +++ b/modules/platforms/cpp/client/include/ignite/ignite_client.h @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace ignite +{ + +/** + * Ignite client. + */ +class IgniteClient +{ +public: + IgniteClient() = delete; + IgniteClient(IgniteClient&) = delete; + IgniteClient& operator=(IgniteClient&) = delete; + + + +private: +}; + +} // namespace ignite \ No newline at end of file diff --git a/modules/platforms/cpp/client/src/ignite_client.cpp b/modules/platforms/cpp/client/src/ignite_client.cpp index e69de29bb2..bcc60eb2c1 100644 --- a/modules/platforms/cpp/client/src/ignite_client.cpp +++ b/modules/platforms/cpp/client/src/ignite_client.cpp @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ignite/ignite_client.h" + +namespace ignite +{ + +} // namespace ignite diff --git a/modules/platforms/cpp/common/Platform.h b/modules/platforms/cpp/common/Platform.h index 2901abf986..4955529660 100644 --- a/modules/platforms/cpp/common/Platform.h +++ b/modules/platforms/cpp/common/Platform.h @@ -17,6 +17,15 @@ #pragma once +/** + * Macro SWITCH_WIN_OTHER that uses first option on Windows and second on any other OS. + */ +#ifdef WIN32 +# define SWITCH_WIN_OTHER(x, y) (x) +#else +# define SWITCH_WIN_OTHER(x, y) (y) +#endif + #define LITTLE_ENDIAN 1 #define BIG_ENDIAN 2 @@ -29,4 +38,5 @@ #else //TODO: Fix this # define BYTE_ORDER LITTLE_ENDIAN -#endif \ No newline at end of file +#endif + diff --git a/modules/platforms/cpp/test-common/CMakeLists.txt b/modules/platforms/cpp/test-common/CMakeLists.txt index a157d5ed98..92485f7f89 100644 --- a/modules/platforms/cpp/test-common/CMakeLists.txt +++ b/modules/platforms/cpp/test-common/CMakeLists.txt @@ -19,7 +19,6 @@ project(ignite-test-common) set(TARGET ${PROJECT_NAME}) -#include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${JNI_INCLUDE_DIRS}) include_directories(include) set(SOURCES @@ -32,4 +31,6 @@ add_library(${TARGET} OBJECT ${SOURCES}) set_target_properties(${TARGET} PROPERTIES VERSION ${CMAKE_PROJECT_VERSION}) set_target_properties(${TARGET} PROPERTIES POSITION_INDEPENDENT_CODE 1) + +target_link_libraries(${TARGET} ignite-common) target_include_directories(${TARGET} INTERFACE include) diff --git a/modules/platforms/cpp/test-common/include/ignite_node.h b/modules/platforms/cpp/test-common/include/ignite_node.h index 10bc130b2c..4850e07624 100644 --- a/modules/platforms/cpp/test-common/include/ignite_node.h +++ b/modules/platforms/cpp/test-common/include/ignite_node.h @@ -23,40 +23,48 @@ namespace ignite { - class IgniteNode - { - public: - /** - * Constructor. - */ - IgniteNode() = default; - - /** - * Destructor. - */ - ~IgniteNode() = default; - - /** - * Start node. - * - * @param dryRun Perform a dry run. Mostly used to ensure that code is compiled and all artifacts are downloaded. - */ - void start(bool dryRun = false); - - /** - * Stop node. - */ - void stop(); - - /** - * Join node process. - * - * @param timeout Timeout. - */ - void join(std::chrono::milliseconds timeout); - - private: - /** Underlying process. */ - std::unique_ptr<Process> process; - }; + +/** + * Represents Ignite server node process. + * + * Ignite node is started from command line. It is recommended to re-use + * a single Ignite node as much as possible to make tests as quick as possible. + */ +class IgniteNode +{ +public: + /** + * Constructor. + */ + IgniteNode() = default; + + /** + * Destructor. + */ + ~IgniteNode() = default; + + /** + * Start node. + * + * @param dryRun Perform a dry run. Mostly used to ensure that code is compiled and all artifacts are downloaded. + */ + void start(bool dryRun = false); + + /** + * Stop node. + */ + void stop(); + + /** + * Join node process. + * + * @param timeout Timeout. + */ + void join(std::chrono::milliseconds timeout); + +private: + /** Underlying process. */ + std::unique_ptr<Process> process; +}; + } // namespace ignite diff --git a/modules/platforms/cpp/test-common/include/process.h b/modules/platforms/cpp/test-common/include/process.h index ab45700ccb..4f4707c4fe 100644 --- a/modules/platforms/cpp/test-common/include/process.h +++ b/modules/platforms/cpp/test-common/include/process.h @@ -19,48 +19,54 @@ #include <chrono> #include <string> +#include <memory> namespace ignite { - class Process - { - public: - /** - * Destructor. - */ - virtual ~Process() = default; - /** - * Make new process instance. - * - * @param command Command. - * @param workDir Working directory. - * @return Process. - */ - static std::unique_ptr<Process> make(std::string command, std::string workDir); +/** + * Represents system process launched using commandline instruction. + */ +class Process +{ +public: + /** + * Destructor. + */ + virtual ~Process() = default; + + /** + * Make new process instance. + * + * @param command Command. + * @param workDir Working directory. + * @return Process. + */ + static std::unique_ptr<Process> make(std::string command, std::string workDir); + + /** + * Start process. + */ + virtual bool start() = 0; - /** - * Start process. - */ - virtual bool start() = 0; + /** + * Kill the process. + */ + virtual void kill() = 0; - /** - * Kill the process. - */ - virtual void kill() = 0; + /** + * Join process. + * + * @param timeout Timeout. + */ + virtual void join(std::chrono::milliseconds timeout) = 0; - /** - * Join process. - * - * @param timeout Timeout. - */ - virtual void join(std::chrono::milliseconds timeout) = 0; +protected: + /** + * Constructor. + */ + Process() = default; +}; - protected: - /** - * Constructor. - */ - Process() = default; - }; } // namespace ignite diff --git a/modules/platforms/cpp/test-common/include/test_utils.h b/modules/platforms/cpp/test-common/include/test_utils.h index 01626fd703..3857a321e4 100644 --- a/modules/platforms/cpp/test-common/include/test_utils.h +++ b/modules/platforms/cpp/test-common/include/test_utils.h @@ -23,21 +23,23 @@ namespace ignite { - /** - * Resolve IGNITE_HOME directory. Resolution is performed in several steps: - * 1) Check for path provided as argument. - * 2) Check for environment variable. - * 3) Check for current working directory. - * Result of these checks are evaluated based on existence of certain predefined folders inside possible Ignite - * home. If they are found, IGNITE_HOME is considered resolved. - * - * @param path Optional path to check. - * @return Resolved Ignite home. - */ - std::string resolveIgniteHome(const std::string& path = ""); - /** - * Get path to maven executable. - */ - std::string getMavenPath(); +/** + * Resolve IGNITE_HOME directory. Resolution is performed in several steps: + * 1) Check for path provided as argument. + * 2) Check for environment variable. + * 3) Check for current working directory. + * Result of these checks are evaluated based on existence of certain predefined folders inside possible Ignite + * home. If they are found, IGNITE_HOME is considered resolved. + * + * @param path Optional path to check. + * @return Resolved Ignite home. + */ +std::string resolveIgniteHome(const std::string& path = ""); + +/** + * Get path to maven executable. + */ +std::string getMavenPath(); + } // namespace ignite \ No newline at end of file diff --git a/modules/platforms/cpp/test-common/src/ignite_node.cpp b/modules/platforms/cpp/test-common/src/ignite_node.cpp index 560b9f54fd..3c6f7ab6a3 100644 --- a/modules/platforms/cpp/test-common/src/ignite_node.cpp +++ b/modules/platforms/cpp/test-common/src/ignite_node.cpp @@ -19,54 +19,61 @@ #include <stdexcept> #include <iostream> +#include "common/Platform.h" + #include "ignite_node.h" #include "test_utils.h" -namespace ignite +namespace { - void IgniteNode::start(bool dryRun) - { - std::string home = resolveIgniteHome(); - if (home.empty()) - throw std::runtime_error( - "Can not resolve Ignite home directory. Try setting IGNITE_HOME explicitly"); - std::string command = -#ifdef WIN32 - "cmd.exe /c "; -#else - "/bin/bash -c "; -#endif +/** + * System shell command string. + */ +constexpr std::string_view SYSTEM_SHELL = SWITCH_WIN_OTHER("cmd.exe /c ", "/bin/bash -c "); - command += getMavenPath() + " exec:java@platform-test-node-runner"; +} // anonymous namespace - if (dryRun) - command += " -Dexec.args=dry-run"; +namespace ignite +{ - auto workDir = std::filesystem::path(home) / "modules" / "runner"; +void IgniteNode::start(bool dryRun) +{ + std::string home = resolveIgniteHome(); + if (home.empty()) + throw std::runtime_error( + "Can not resolve Ignite home directory. Try setting IGNITE_HOME explicitly"); - std::cout << "IGNITE_HOME=" << home << std::endl; - std::cout << "working dir=" << workDir << std::endl; - std::cout << "command=" << command << std::endl; + std::string command = std::string(SYSTEM_SHELL) + getMavenPath() + " exec:java@platform-test-node-runner"; - process = Process::make(command, workDir.string()); - if (!process->start()) - { - process.reset(); + if (dryRun) + command += " -Dexec.args=dry-run"; - throw std::runtime_error("Failed to invoke Ignite command: '" + command + "'"); - } - } + auto workDir = std::filesystem::path(home) / "modules" / "runner"; - void IgniteNode::stop() - { - if (process) - process->kill(); - } + std::cout << "IGNITE_HOME=" << home << std::endl; + std::cout << "working dir=" << workDir << std::endl; + std::cout << "command=" << command << std::endl; - void IgniteNode::join(std::chrono::milliseconds timeout) + process = Process::make(command, workDir.string()); + if (!process->start()) { - if (process) - process->join(timeout); + process.reset(); + + throw std::runtime_error("Failed to invoke Ignite command: '" + command + "'"); } +} + +void IgniteNode::stop() +{ + if (process) + process->kill(); +} + +void IgniteNode::join(std::chrono::milliseconds timeout) +{ + if (process) + process->join(timeout); +} + } // namespace ignite \ No newline at end of file diff --git a/modules/platforms/cpp/test-common/src/process.cpp b/modules/platforms/cpp/test-common/src/process.cpp index 4911031bbc..f2ce10854a 100644 --- a/modules/platforms/cpp/test-common/src/process.cpp +++ b/modules/platforms/cpp/test-common/src/process.cpp @@ -16,8 +16,9 @@ */ #ifdef WIN32 -# include <windows.h> -# include <tlhelp32.h> +# include "win/win_process.h" +#else +# include "linux/linux_process.h" #endif #include <filesystem> @@ -26,159 +27,16 @@ #include "process.h" -namespace -{ -#ifdef WIN32 - /** - * Get process tree. - * @param processId ID of the parent process. - * @return Process tree. - */ - std::vector<DWORD> getProcessTree(DWORD processId) // NOLINT(misc-no-recursion) - { - std::vector<DWORD> children; - PROCESSENTRY32 pe; - - memset(&pe, 0, sizeof(PROCESSENTRY32)); - pe.dwSize = sizeof(PROCESSENTRY32); - - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - - if (Process32First(hSnap, &pe)) - { - BOOL bContinue = TRUE; - - while (bContinue) - { - if (pe.th32ParentProcessID == processId) - children.push_back(pe.th32ProcessID); - - bContinue = Process32Next(hSnap, &pe); - } - } - - std::vector<DWORD> tree(children); - for (auto procId : children) - { - std::vector<DWORD> childTree = getProcessTree(procId); - tree.insert(tree.end(), childTree.begin(), childTree.end()); - } - - return tree; - } - - /** - * Implementation of Process for Windows. - */ - class WinProcess : public ignite::Process - { - public: - /** - * Constructor. - * - * @param command Command. - * @param workDir Working directory. - */ - WinProcess(std::string command, std::string workDir) : - running(false), - command(std::move(command)), - workDir(std::move(workDir)), - info{} - { } - - /** - * Destructor. - */ - ~WinProcess() override = default; - - - /** - * Start process. - */ - bool start() override - { - if (running) - return false; - - STARTUPINFO si; - - std::memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - std::memset(&info, 0, sizeof(info)); - - std::vector<char> cmd(command.begin(), command.end()); - cmd.push_back(0); - - BOOL success = CreateProcess( - NULL, cmd.data(), NULL, NULL, - FALSE, 0, NULL, workDir.c_str(), - &si, &info); - - running = success == TRUE; - - return running; - } - - /** - * Kill the process. - */ - void kill() override - { - std::vector<DWORD> processTree = getProcessTree(info.dwProcessId); - for (auto procId : processTree) - { - HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, procId); - if (hChildProc) - { - TerminateProcess(hChildProc, 1); - CloseHandle(hChildProc); - } - } - - TerminateProcess(info.hProcess, 1); - - CloseHandle( info.hProcess ); - CloseHandle( info.hThread ); - } - - /** - * Join process. - * - * @param timeout Timeout. - */ - void join(std::chrono::milliseconds timeout) override - { - auto msecs = timeout.count() < 0 ? INFINITE : static_cast<DWORD>(timeout.count()); - - WaitForSingleObject(info.hProcess, msecs); - } - - private: - /** Running flag. */ - bool running; - - /** Command. */ - const std::string command; - - /** Working directory. */ - const std::string workDir; - - /** Process information. */ - PROCESS_INFORMATION info; - }; - -#else // #ifdef WIN32 - -#endif // #ifdef WIN32 -} namespace ignite { - std::unique_ptr<Process> Process::make(std::string command, std::string workDir) - { + +std::unique_ptr<Process> Process::make(std::string command, std::string workDir) +{ #ifdef WIN32 - return std::unique_ptr<Process>(new WinProcess(std::move(command), std::move(workDir))); + return std::unique_ptr<Process>(new win::WinProcess(std::move(command), std::move(workDir))); #else #endif - } +} + } // namespace ignite \ No newline at end of file diff --git a/modules/platforms/cpp/test-common/src/test_utils.cpp b/modules/platforms/cpp/test-common/src/test_utils.cpp index f6b0a46386..97e811e984 100644 --- a/modules/platforms/cpp/test-common/src/test_utils.cpp +++ b/modules/platforms/cpp/test-common/src/test_utils.cpp @@ -24,71 +24,73 @@ namespace ignite { - /** - * Checks if the path looks like binary release home directory. - * Internally checks for presence of core library. - * @return @c true if the path looks like binary release home directory. - */ - bool looksLikeBinaryReleaseHome(const std::filesystem::path& path) - { - std::filesystem::path coreLibPath = path / "libs"; - if (!is_directory(coreLibPath)) - return false; - - auto iter = std::filesystem::directory_iterator{coreLibPath}; - return std::any_of(iter, std::filesystem::end(iter), [](auto entry) { - const std::filesystem::path& entryPath = entry.path(); - if (entryPath.extension() != "jar") - return false; - std::string stem = entryPath.stem().string(); - return stem.find("ignite-core") == 0; - }); - } - - /** - * Checks if the path looks like source release home directory. - * Internally checks for presence of core source directory. - * @return @c true if the path looks like binary release home directory. - */ - bool looksLikeSourceReleaseHome(const std::filesystem::path& path) - { - std::filesystem::path coreSourcePath = - path / "modules" / "core" / "src" / "main" / "java" / "org" / "apache" / "ignite"; +/** + * Checks if the path looks like binary release home directory. + * Internally checks for presence of core library. + * @return @c true if the path looks like binary release home directory. + */ +bool looksLikeBinaryReleaseHome(const std::filesystem::path& path) +{ + std::filesystem::path coreLibPath = path / "libs"; + if (!is_directory(coreLibPath)) + return false; - return std::filesystem::is_directory(coreSourcePath); - } + auto iter = std::filesystem::directory_iterator{coreLibPath}; + return std::any_of(iter, std::filesystem::end(iter), [](auto entry) { + const std::filesystem::path& entryPath = entry.path(); + if (entryPath.extension() != "jar") + return false; - std::string resolveIgniteHome(const std::string& path) - { - std::error_code error; + std::string stem = entryPath.stem().string(); + return stem.find("ignite-core") == 0; + }); +} - std::filesystem::path home = std::filesystem::canonical(path, error); - if (!error && std::filesystem::is_directory(path)) - return home.string(); +/** + * Checks if the path looks like source release home directory. + * Internally checks for presence of core source directory. + * @return @c true if the path looks like binary release home directory. + */ +bool looksLikeSourceReleaseHome(const std::filesystem::path& path) +{ + std::filesystem::path coreSourcePath = + path / "modules" / "core" / "src" / "main" / "java" / "org" / "apache" / "ignite"; - const char *env = std::getenv("IGNITE_HOME"); - if (env) - { - home = std::filesystem::canonical(env, error); - if (!error && std::filesystem::is_directory(home)) - return home.string(); - } + return std::filesystem::is_directory(coreSourcePath); +} - home = std::filesystem::current_path(); - while (!home.empty() && home.has_relative_path()) - { - if (looksLikeBinaryReleaseHome(home) || looksLikeSourceReleaseHome(home)) - return home.string(); +std::string resolveIgniteHome(const std::string& path) +{ + std::error_code error; - home = home.parent_path(); - } + std::filesystem::path home = std::filesystem::canonical(path, error); + if (!error && std::filesystem::is_directory(path)) return home.string(); + + const char *env = std::getenv("IGNITE_HOME"); + if (env) + { + home = std::filesystem::canonical(env, error); + if (!error && std::filesystem::is_directory(home)) + return home.string(); } - std::string getMavenPath() + home = std::filesystem::current_path(); + while (!home.empty() && home.has_relative_path()) { - // Currently, we only support systems with "mvn" command in PATH - return "mvn"; + if (looksLikeBinaryReleaseHome(home) || looksLikeSourceReleaseHome(home)) + return home.string(); + + home = home.parent_path(); } + return home.string(); +} + +std::string getMavenPath() +{ + // Currently, we only support systems with "mvn" command in PATH + return "mvn"; +} + } // namespace ignite \ No newline at end of file diff --git a/modules/platforms/cpp/test-common/src/win/win_process.h b/modules/platforms/cpp/test-common/src/win/win_process.h new file mode 100644 index 0000000000..b0c29c5941 --- /dev/null +++ b/modules/platforms/cpp/test-common/src/win/win_process.h @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +// It's OK that this code is entirely in header as it only supposed to be included from a single file. + +#include <windows.h> +#include <tlhelp32.h> + +#include <vector> +#include <chrono> +#include <string> + +#include "process.h" + +namespace ignite::win +{ + +/** + * Get process tree. + * @param processId ID of the parent process. + * @return Process tree. + */ +std::vector<DWORD> getProcessTree(DWORD processId) // NOLINT(misc-no-recursion) +{ + std::vector<DWORD> children; + PROCESSENTRY32 pe; + + memset(&pe, 0, sizeof(PROCESSENTRY32)); + pe.dwSize = sizeof(PROCESSENTRY32); + + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + if (Process32First(hSnap, &pe)) + { + BOOL bContinue = TRUE; + + while (bContinue) + { + if (pe.th32ParentProcessID == processId) + children.push_back(pe.th32ProcessID); + + bContinue = Process32Next(hSnap, &pe); + } + } + + std::vector<DWORD> tree(children); + for (auto procId : children) + { + std::vector<DWORD> childTree = getProcessTree(procId); + tree.insert(tree.end(), childTree.begin(), childTree.end()); + } + + return tree; +} + +/** + * Implementation of Process for Windows. + */ +class WinProcess : public ignite::Process +{ +public: + /** + * Constructor. + * + * @param command Command. + * @param workDir Working directory. + */ + WinProcess(std::string command, std::string workDir) : + running(false), + command(std::move(command)), + workDir(std::move(workDir)), + info{} + { } + + /** + * Destructor. + */ + ~WinProcess() override = default; + + + /** + * Start process. + */ + bool start() override + { + if (running) + return false; + + STARTUPINFO si; + + std::memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + std::memset(&info, 0, sizeof(info)); + + std::vector<char> cmd(command.begin(), command.end()); + cmd.push_back(0); + + BOOL success = CreateProcess( + NULL, cmd.data(), NULL, NULL, + FALSE, 0, NULL, workDir.c_str(), + &si, &info); + + running = success == TRUE; + + return running; + } + + /** + * Kill the process. + */ + void kill() override + { + std::vector<DWORD> processTree = getProcessTree(info.dwProcessId); + for (auto procId : processTree) + { + HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, procId); + if (hChildProc) + { + TerminateProcess(hChildProc, 1); + CloseHandle(hChildProc); + } + } + + TerminateProcess(info.hProcess, 1); + + CloseHandle( info.hProcess ); + CloseHandle( info.hThread ); + } + + /** + * Join process. + * + * @param timeout Timeout. + */ + void join(std::chrono::milliseconds timeout) override + { + auto msecs = timeout.count() < 0 ? INFINITE : static_cast<DWORD>(timeout.count()); + + WaitForSingleObject(info.hProcess, msecs); + } + +private: + /** Running flag. */ + bool running; + + /** Command. */ + const std::string command; + + /** Working directory. */ + const std::string workDir; + + /** Process information. */ + PROCESS_INFORMATION info; +}; + +} // namespace ignite::win +
