You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by is...@apache.org on 2022/09/09 20:00:50 UTC
[ignite-3] 09/17: IGNITE-17424 Re-factoring
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 <is...@apache.org>
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
+