mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-05 19:11:50 +00:00
Merge pull request #899 from MihailRis/app-new-instance
engine sub-instance spawn function
This commit is contained in:
commit
40f8fc620d
18 changed files with 386 additions and 52 deletions
|
|
@ -180,14 +180,34 @@ app.get_content_sources() -> table<string>
|
|||
|
||||
Returns a list of content sources (paths), in descending priority order.
|
||||
|
||||
``lua
|
||||
```lua
|
||||
app.set_content_sources(sources: table<string>)
|
||||
```
|
||||
|
||||
Sets a list of content sources (paths). Specified in descending priority order.
|
||||
|
||||
``lua
|
||||
```lua
|
||||
app.reset_content_sources()
|
||||
```
|
||||
|
||||
Resets content sources.
|
||||
|
||||
## Sub-instances
|
||||
|
||||
```lua
|
||||
-- Creates a headless engine instance with the current project and the specified application script.
|
||||
-- Returns the instance ID. The number of active sub-instances is currently limited to one.
|
||||
app.start_background_instance(
|
||||
-- script file
|
||||
app_script: string,
|
||||
-- log file
|
||||
output_file: string | nil
|
||||
) -> int
|
||||
|
||||
-- Checks if the engine sub-instance is alive.
|
||||
app.is_instance_alive(handle: int) -> boolean
|
||||
|
||||
-- Stops the engine sub-instance.
|
||||
-- Returns true if the instance was alive at the time of the call.
|
||||
app.terminate(handle: int) -> boolean
|
||||
```
|
||||
|
|
|
|||
|
|
@ -142,4 +142,24 @@ app.set_content_sources(sources: table<string>)
|
|||
|
||||
-- Сбрасывает список источников контента.
|
||||
app.reset_content_sources()
|
||||
```
|
||||
```
|
||||
|
||||
## Под-экземпляры
|
||||
|
||||
```lua
|
||||
-- Создаёт headless-экземпляр движка с текущим проектом и указанным сценарием.
|
||||
-- Возвращает id экземпляра. Число живых под-экземпляров, на данный момент, ограничено одним.
|
||||
app.start_background_instance(
|
||||
-- файл сценария
|
||||
app_script: string,
|
||||
-- файл лога
|
||||
output_file: string | nil
|
||||
) -> int
|
||||
|
||||
-- Проверяет, жив ли под-экземпляр движка.
|
||||
app.is_instance_alive(handle: int) -> boolean
|
||||
|
||||
-- Останавливает под-экземпляр движка.
|
||||
-- Возвращает true если экземпляр был жив в момент вызова.
|
||||
app.terminate_instance(handle: int) -> boolean
|
||||
```
|
||||
|
|
|
|||
|
|
@ -3,5 +3,6 @@ name = "default"
|
|||
base_packs = ["base"]
|
||||
permissions = [
|
||||
"network",
|
||||
"record-audio"
|
||||
"record-audio",
|
||||
"sub-instances",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -140,7 +140,20 @@ local __vc_named_coroutines = {}
|
|||
local __vc_next_coroutine = 1
|
||||
|
||||
function __vc_start_coroutine(chunk)
|
||||
local co = coroutine.create(chunk)
|
||||
local co = coroutine.create(function()
|
||||
local _, err = xpcall(chunk, function(msg)
|
||||
local traceback = debug.get_traceback(0)
|
||||
local s = string.format("%s:", msg)
|
||||
for i=1,#traceback - 2 do
|
||||
local frame = traceback[i]
|
||||
s = s .. "\n\t"..tb_frame_tostring(frame)
|
||||
end
|
||||
return s
|
||||
end)
|
||||
if err then
|
||||
error(err)
|
||||
end
|
||||
end)
|
||||
local id = __vc_next_coroutine
|
||||
__vc_next_coroutine = __vc_next_coroutine + 1
|
||||
__vc_coroutines[id] = co
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ vc = {
|
|||
is_client = function()
|
||||
return not _vc_headless
|
||||
end,
|
||||
get_version = __vc_app.get_version,
|
||||
get_setting = __vc_app.get_setting,
|
||||
get_setting_str = __vc_app.get_setting_str,
|
||||
str_setting = __vc_app.str_setting,
|
||||
get_setting_info = __vc_app.get_setting_info,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ inline constexpr bool ENGINE_DEBUG_BUILD = true;
|
|||
|
||||
inline const std::string ENGINE_VERSION_STRING = "0.32";
|
||||
|
||||
inline constexpr uint MAX_SUBPROCESSES = 1;
|
||||
inline constexpr uint MAX_SUBPROCESS_DEPTH = 2;
|
||||
|
||||
/// @brief world regions format version
|
||||
inline constexpr uint REGION_FORMAT_VERSION = 3;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,7 @@
|
|||
|
||||
static debug::Logger logger("app-scripts");
|
||||
|
||||
AppScriptsControl::AppScriptsControl(
|
||||
const CoreParameters& params, const Project& project
|
||||
)
|
||||
: project(project) {
|
||||
|
||||
AppScriptsControl::AppScriptsControl(const CoreParameters& params) {
|
||||
io::path scriptFile =
|
||||
params.scriptFile.empty()
|
||||
? "project:start.lua"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ struct Project;
|
|||
|
||||
class AppScriptsControl {
|
||||
public:
|
||||
AppScriptsControl(const CoreParameters& params, const Project& project);
|
||||
AppScriptsControl(const CoreParameters& params);
|
||||
|
||||
void tick();
|
||||
void loadProjectClientScript();
|
||||
|
|
@ -24,7 +24,6 @@ public:
|
|||
|
||||
bool isFinished() const;
|
||||
private:
|
||||
const Project& project;
|
||||
std::unique_ptr<scripting::IClientProjectScript> clientScript;
|
||||
std::unique_ptr<Process> scriptCoroutine;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "interfaces/Serializable.hpp"
|
||||
#include "io/path.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
|
@ -12,6 +13,7 @@ struct Permissions {
|
|||
static inline std::string NETWORK = "network";
|
||||
static inline std::string RECORD_AUDIO = "record-audio";
|
||||
static inline std::string WRITE_TO_USER = "write-to-user";
|
||||
static inline std::string SUB_INSTANCES = "sub-instances";
|
||||
|
||||
std::set<std::string> permissions;
|
||||
|
||||
|
|
@ -21,6 +23,7 @@ struct Permissions {
|
|||
struct Project : Serializable {
|
||||
std::string name;
|
||||
std::string title;
|
||||
io::path path;
|
||||
std::vector<std::string> basePacks;
|
||||
Permissions permissions;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,5 +14,6 @@ struct CoreParameters {
|
|||
std::filesystem::path projectFolder;
|
||||
std::string debugServerString;
|
||||
int tps = 20;
|
||||
int subProcessDepth = 0;
|
||||
std::unordered_map<std::string, std::string> projectArgs;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ void Engine::initialize(CoreParameters coreParameters) {
|
|||
audio::set_input_device(name == "auto" ? "" : name);
|
||||
}));
|
||||
|
||||
appScripts = std::make_unique<AppScriptsControl>(params, *project);
|
||||
appScripts = std::make_unique<AppScriptsControl>(params);
|
||||
|
||||
if (params.stdinCommands) {
|
||||
cmd::start_stdin_cmd_reader(*this);
|
||||
|
|
@ -380,6 +380,7 @@ void Engine::loadAssets() {
|
|||
void Engine::loadProject() {
|
||||
io::path projectFile = "project:project.toml";
|
||||
project = std::make_unique<Project>();
|
||||
project->path = "project:";
|
||||
project->deserialize(io::read_object(projectFile));
|
||||
logger.info() << "loaded project " << util::quote(project->name);
|
||||
}
|
||||
|
|
@ -412,6 +413,7 @@ void Engine::onWorldClosed() {
|
|||
}
|
||||
|
||||
void Engine::quit() {
|
||||
logger.info() << "quitSignal set to true";
|
||||
quitSignal = true;
|
||||
if (!isHeadless()) {
|
||||
window->setShouldClose(true);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ void Mainloop::run() {
|
|||
auto& appScripts = engine.getAppScripts();
|
||||
|
||||
logger.info() << "main loop started";
|
||||
while (!window.isShouldClose()) {
|
||||
while (!window.isShouldClose() && !engine.isQuitSignal()) {
|
||||
time.update(window.time());
|
||||
engine.applicationTick();
|
||||
engine.updateFrontend();
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ namespace io {
|
|||
}
|
||||
}
|
||||
|
||||
path(std::string_view str) : path(std::string(str)) {}
|
||||
|
||||
path(const char* str) : path(std::string(str)) {}
|
||||
|
||||
bool operator==(const std::string& other) const {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,14 @@
|
|||
#include "window/Window.hpp"
|
||||
#include "world/Level.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
namespace {
|
||||
static std::array<std::unique_ptr<Process>, MAX_SUBPROCESSES> processes;
|
||||
}
|
||||
|
||||
/// @brief Check if content is loaded
|
||||
static int l_is_content_loaded(lua::State* L) {
|
||||
return lua::pushboolean(L, content != nullptr);
|
||||
|
|
@ -337,6 +343,10 @@ static int l_start_debug_instance(lua::State* L) {
|
|||
if (!engine->getProject().permissions.has(Permissions::DEBUGGING)) {
|
||||
throw std::runtime_error("project has no debugging permission");
|
||||
}
|
||||
const auto& params = engine->getCoreParameters();
|
||||
if (params.subProcessDepth >= MAX_SUBPROCESS_DEPTH) {
|
||||
throw std::runtime_error("max subprocess depth exceeded");
|
||||
}
|
||||
|
||||
int port = lua::tointeger(L, 1);
|
||||
if (port == 0) {
|
||||
|
|
@ -357,6 +367,8 @@ static int l_start_debug_instance(lua::State* L) {
|
|||
"--res", paths.getResourcesFolder().u8string(),
|
||||
"--dir", paths.getUserFilesFolder().u8string(),
|
||||
"--dbg-server", "tcp:" + std::to_string(port),
|
||||
"--sub-depth", std::to_string(engine->getCoreParameters()
|
||||
.subProcessDepth + 1),
|
||||
};
|
||||
if (!projectPath.empty()) {
|
||||
args.emplace_back("--project");
|
||||
|
|
@ -365,11 +377,77 @@ static int l_start_debug_instance(lua::State* L) {
|
|||
|
||||
platform::new_engine_instance(
|
||||
std::move(args),
|
||||
outputPath.empty() ? "" : io::resolve(std::string(outputPath))
|
||||
outputPath.empty() ? "" : io::resolve(std::string(outputPath)),
|
||||
false
|
||||
);
|
||||
return lua::pushinteger(L, port);
|
||||
}
|
||||
|
||||
static int l_start_background_instance(lua::State* L) {
|
||||
if (!engine->getProject().permissions.has(Permissions::SUB_INSTANCES)) {
|
||||
throw std::runtime_error("project has no sub-instances permission");
|
||||
}
|
||||
const auto& params = engine->getCoreParameters();
|
||||
if (params.subProcessDepth >= MAX_SUBPROCESS_DEPTH) {
|
||||
throw std::runtime_error("max subprocess depth exceeded");
|
||||
}
|
||||
|
||||
auto scriptPath = lua::require_lstring(L, 1);
|
||||
io::path outputPath = lua::isstring(L, 2) ? lua::require_lstring(L, 2) : "";
|
||||
const auto& paths = engine->getPaths();
|
||||
|
||||
std::vector<std::string> args {
|
||||
"--headless",
|
||||
"--res", paths.getResourcesFolder().u8string(),
|
||||
"--dir", paths.getUserFilesFolder().u8string(),
|
||||
"--script", io::resolve(scriptPath).u8string(),
|
||||
"--sub-depth", std::to_string(engine->getCoreParameters()
|
||||
.subProcessDepth + 1),
|
||||
};
|
||||
args.emplace_back("--project");
|
||||
args.emplace_back(io::resolve(engine->getProject().path).u8string());
|
||||
|
||||
int handle = -1;
|
||||
for (int i = 0; i < ::processes.size(); i++) {
|
||||
if (!::processes[i] || !::processes[i]->isActive()) {
|
||||
handle = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (handle == -1) {
|
||||
throw std::runtime_error("sub-processes limit exceeded");
|
||||
}
|
||||
::processes[handle] = platform::new_engine_instance(
|
||||
std::move(args),
|
||||
outputPath.empty() ? "" : io::resolve(outputPath),
|
||||
true
|
||||
);
|
||||
return lua::pushinteger(L, handle);
|
||||
}
|
||||
|
||||
static int l_is_instance_alive(lua::State* L) {
|
||||
int handle = lua::tointeger(L, 1);
|
||||
if (handle < 0 || handle >= ::processes.size()) {
|
||||
throw std::runtime_error("invalid process handle");
|
||||
}
|
||||
auto& process = ::processes[handle];
|
||||
return lua::pushboolean(L, process && process->isActive());
|
||||
}
|
||||
|
||||
static int l_terminate_instance(lua::State* L) {
|
||||
int handle = lua::tointeger(L, 1);
|
||||
if (handle < 0 || handle >= ::processes.size()) {
|
||||
throw std::runtime_error("invalid process handle");
|
||||
}
|
||||
auto& process = ::processes[handle];
|
||||
if (process == nullptr) {
|
||||
return lua::pushboolean(L, false);
|
||||
}
|
||||
bool active = process->isActive();
|
||||
process.reset();
|
||||
return lua::pushboolean(L, active);
|
||||
}
|
||||
|
||||
const luaL_Reg applib[] = {
|
||||
/// content
|
||||
{"is_content_loaded", lua::wrap<l_is_content_loaded>},
|
||||
|
|
@ -402,5 +480,8 @@ const luaL_Reg applib[] = {
|
|||
{"get_version", lua::wrap<l_get_version>},
|
||||
{"create_memory_device", lua::wrap<l_create_memory_device>},
|
||||
{"start_debug_instance", lua::wrap<l_start_debug_instance>},
|
||||
{"start_background_instance", lua::wrap<l_start_background_instance>},
|
||||
{"is_instance_alive", lua::wrap<l_is_instance_alive>},
|
||||
{"terminate_instance", lua::wrap<l_terminate_instance>},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
static debug::Logger logger("main");
|
||||
|
||||
static void sigterm_handler(int signum) {
|
||||
logger.info() << (signum == SIGTERM ? "SIGTERM" : "SIGINT") << " received";
|
||||
Engine::getInstance().quit();
|
||||
}
|
||||
|
||||
|
|
@ -25,12 +26,16 @@ int main(int argc, char** argv) {
|
|||
if (!parse_cmdline(argc, argv, coreParameters)) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
logger.debug() << "sub-process depth: "
|
||||
<< coreParameters.subProcessDepth;
|
||||
} catch (const std::runtime_error& err) {
|
||||
std::cerr << err.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::signal(SIGTERM, sigterm_handler);
|
||||
|
||||
#ifdef NDEBUG
|
||||
std::signal(SIGINT, sigterm_handler);
|
||||
#endif
|
||||
debug::Logger::init(coreParameters.userFolder.string() + "/latest.log");
|
||||
platform::configure_encoding();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,22 +13,22 @@
|
|||
namespace fs = std::filesystem;
|
||||
|
||||
class ArgC {
|
||||
public:
|
||||
std::string keyword;
|
||||
std::function<bool()> execute;
|
||||
std::string args;
|
||||
std::string help;
|
||||
ArgC(
|
||||
const std::string& keyword,
|
||||
std::function<bool()> execute,
|
||||
const std::string& args,
|
||||
const std::string& help
|
||||
) {
|
||||
this->keyword = keyword;
|
||||
this->execute = execute;
|
||||
this->args = args;
|
||||
this->help = help;
|
||||
}
|
||||
public:
|
||||
std::string keyword;
|
||||
std::function<bool(CoreParameters&, util::ArgsReader&)> execute;
|
||||
std::string args;
|
||||
std::string help;
|
||||
ArgC(
|
||||
const std::string& keyword,
|
||||
std::function<bool(CoreParameters&, util::ArgsReader&)> execute,
|
||||
const std::string& args,
|
||||
const std::string& help
|
||||
) {
|
||||
this->keyword = keyword;
|
||||
this->execute = execute;
|
||||
this->args = args;
|
||||
this->help = help;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -36,54 +36,62 @@ static bool perform_keyword(
|
|||
util::ArgsReader& reader, const std::string& keyword, CoreParameters& params
|
||||
) {
|
||||
static const std::vector<ArgC> argumentsCommandline = {
|
||||
ArgC("--res", [¶ms, &reader]() -> bool {
|
||||
ArgC("--res", [](auto& params, auto& reader) -> bool {
|
||||
params.resFolder = reader.next();
|
||||
return true;
|
||||
}, "<path>", "set resources directory."),
|
||||
ArgC("--dir", [¶ms, &reader]() -> bool {
|
||||
ArgC("--dir", [](auto& params, auto& reader) -> bool {
|
||||
params.userFolder = reader.next();
|
||||
return true;
|
||||
}, "<path>", "set userfiles directory."),
|
||||
ArgC("--project", [¶ms, &reader]() -> bool {
|
||||
ArgC("--project", [](auto& params, auto& reader) -> bool {
|
||||
params.projectFolder = reader.next();
|
||||
return true;
|
||||
}, "<path>", "set project directory."),
|
||||
ArgC("--test", [¶ms, &reader]() -> bool {
|
||||
ArgC("--test", [](auto& params, auto& reader) -> bool {
|
||||
params.testMode = true;
|
||||
params.scriptFile = reader.next();
|
||||
return true;
|
||||
}, "<path>", "test script file."),
|
||||
ArgC("--script", [¶ms, &reader]() -> bool {
|
||||
ArgC("--script", [](auto& params, auto& reader) -> bool {
|
||||
params.testMode = false;
|
||||
params.scriptFile = reader.next();
|
||||
return true;
|
||||
}, "<path>", "main script file."),
|
||||
ArgC("--headless", [¶ms]() -> bool {
|
||||
ArgC("--headless", [](auto& params, auto& reader) -> bool {
|
||||
params.headless = true;
|
||||
return true;
|
||||
}, "", "run in headless mode."),
|
||||
ArgC("--stdin-cmd", [¶ms]() -> bool {
|
||||
ArgC("--stdin-cmd", [](auto& params, auto& reader) -> bool {
|
||||
params.stdinCommands = true;
|
||||
return true;
|
||||
}, "", "run commands from stdin."),
|
||||
ArgC("--tps", [¶ms, &reader]() -> bool {
|
||||
ArgC("--tps", [](auto& params, auto& reader) -> bool {
|
||||
params.tps = reader.nextInt();
|
||||
return true;
|
||||
}, "<tps>", "headless mode tick-rate (default - 20)."),
|
||||
ArgC("--version", []() -> bool {
|
||||
ArgC("--version", [](auto&, auto&) -> bool {
|
||||
std::cout << ENGINE_VERSION_STRING << std::endl;
|
||||
return false;
|
||||
}, "", "display the engine version."),
|
||||
ArgC("--dbg-server", [¶ms, &reader]() -> bool {
|
||||
ArgC("--dbg-server", [](auto& params, auto& reader) -> bool {
|
||||
params.debugServerString = reader.next();
|
||||
return true;
|
||||
}, "<serv>", "open debugging server where <serv> is {transport}:{port}"),
|
||||
ArgC("--help", []() -> bool {
|
||||
ArgC("--sub-depth", [](auto& params, auto& reader) -> bool {
|
||||
params.subProcessDepth = reader.nextInt();
|
||||
return true;
|
||||
}, "<depth>", "sub-process depth"),
|
||||
ArgC("--help", [](auto&, auto&) -> bool {
|
||||
std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n";
|
||||
std::cout << "Command-line arguments:\n";
|
||||
for (auto& a : argumentsCommandline) {
|
||||
std::cout << std::setw(24) << std::left << (a.keyword + " " + a.args);
|
||||
std::cout << "- " << a.help << std::endl;
|
||||
for (auto& arg : argumentsCommandline) {
|
||||
if (arg.help.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::cout << std::setw(24) << std::left
|
||||
<< (arg.keyword + " " + arg.args);
|
||||
std::cout << "- " << arg.help << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return false;
|
||||
|
|
@ -91,7 +99,7 @@ static bool perform_keyword(
|
|||
};
|
||||
for (auto& a : argumentsCommandline) {
|
||||
if (a.keyword == keyword) {
|
||||
return a.execute();
|
||||
return a.execute(params, reader);
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("unknown argument " + keyword);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,13 @@
|
|||
#else
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
namespace platform::internal {
|
||||
|
|
@ -215,8 +222,102 @@ std::filesystem::path platform::get_executable_path() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void platform::new_engine_instance(
|
||||
const std::vector<std::string>& args, std::filesystem::path outputFile
|
||||
class SystemProcess final : public Process {
|
||||
public:
|
||||
#ifdef __linux__
|
||||
SystemProcess(pid_t pid) : pid(pid) {
|
||||
}
|
||||
|
||||
~SystemProcess() {
|
||||
terminate();
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
SystemProcess(const PROCESS_INFORMATION& pi)
|
||||
: processHandle(pi.hProcess), pid(pi.dwProcessId) {
|
||||
if (pi.hThread != nullptr) {
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
}
|
||||
|
||||
~SystemProcess() {
|
||||
terminate();
|
||||
}
|
||||
#endif
|
||||
SystemProcess(const SystemProcess&) = delete;
|
||||
SystemProcess(SystemProcess&&) noexcept = default;
|
||||
SystemProcess& operator=(const SystemProcess&) = delete;
|
||||
|
||||
SystemProcess& operator=(SystemProcess&& other) noexcept {
|
||||
if (this != &other) {
|
||||
#ifdef __linux__
|
||||
pid = other.pid;
|
||||
#elif defined(_WIN32)
|
||||
if (processHandle != nullptr) {
|
||||
CloseHandle(processHandle);
|
||||
}
|
||||
processHandle = other.processHandle;
|
||||
pid = other.pid;
|
||||
other.processHandle = nullptr;
|
||||
other.pid = 0;
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isActive() const override {
|
||||
#ifdef __linux__
|
||||
if (kill(pid, 0) == 0) {
|
||||
return true;
|
||||
}
|
||||
return errno != ESRCH;
|
||||
#elif defined(_WIN32)
|
||||
if (processHandle == nullptr) {
|
||||
return false;
|
||||
}
|
||||
DWORD exitCode;
|
||||
if (GetExitCodeProcess(processHandle, &exitCode)) {
|
||||
return exitCode == STILL_ACTIVE;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void update() override {}
|
||||
|
||||
void waitForEnd() override {
|
||||
#ifdef __linux__
|
||||
waitpid(pid, nullptr, 0);
|
||||
#elif defined(_WIN32)
|
||||
if (processHandle != nullptr) {
|
||||
WaitForSingleObject(processHandle, INFINITE);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
void terminate() final override {
|
||||
#ifdef __linux__
|
||||
logger.info() << "terminating " << pid;
|
||||
kill(pid, SIGTERM);
|
||||
#elif defined(_WIN32)
|
||||
if (processHandle != nullptr) {
|
||||
TerminateProcess(processHandle, 1);
|
||||
WaitForSingleObject(processHandle, INFINITE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
#ifdef __linux__
|
||||
pid_t pid;
|
||||
#elif defined(_WIN32)
|
||||
HANDLE processHandle = nullptr;
|
||||
DWORD pid = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
std::unique_ptr<Process> platform::new_engine_instance(
|
||||
const std::vector<std::string>& args,
|
||||
std::filesystem::path outputFile,
|
||||
bool subProcess
|
||||
) {
|
||||
auto executable = get_executable_path();
|
||||
|
||||
|
|
@ -285,6 +386,38 @@ void platform::new_engine_instance(
|
|||
&si,
|
||||
&pi
|
||||
);
|
||||
if (subProcess) {
|
||||
HANDLE job = CreateJobObjectW(nullptr, nullptr);
|
||||
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION info{};
|
||||
info.BasicLimitInformation.LimitFlags =
|
||||
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
|
||||
try {
|
||||
if (!SetInformationJobObject(
|
||||
job,
|
||||
JobObjectExtendedLimitInformation,
|
||||
&info,
|
||||
sizeof(info))) {
|
||||
throw std::runtime_error(
|
||||
"SetInformationJobObject failed with code: " +
|
||||
std::to_string(GetLastError())
|
||||
);
|
||||
}
|
||||
if (!AssignProcessToJobObject(job, pi.hProcess)) {
|
||||
throw std::runtime_error(
|
||||
"SetInformationJobObject failed with code: " +
|
||||
std::to_string(GetLastError())
|
||||
);
|
||||
}
|
||||
return std::make_unique<SystemProcess>(pi);
|
||||
} catch (const std::exception& err) {
|
||||
CloseHandle(job);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
|
@ -295,6 +428,46 @@ void platform::new_engine_instance(
|
|||
);
|
||||
}
|
||||
#else
|
||||
|
||||
// TODO: implement
|
||||
#ifndef __APPLE__
|
||||
if (subProcess) {
|
||||
pid_t pid = fork();
|
||||
|
||||
int fd = open(
|
||||
outputFile.string().c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644
|
||||
);
|
||||
|
||||
if (pid == 0) {
|
||||
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||||
|
||||
if (getppid() == 1) {
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
|
||||
std::vector<const char*> argv(args.size() + 2);
|
||||
argv[0] = const_cast<char*>(executable.c_str());
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
argv[i + 1] = args[i].c_str();
|
||||
}
|
||||
argv[args.size() + 1] = nullptr;
|
||||
|
||||
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||
execvp(executable.c_str(), const_cast<char**>(argv.data()));
|
||||
|
||||
_exit(127);
|
||||
} else {
|
||||
close(fd);
|
||||
usleep(1000); // waiting for execvp call
|
||||
return std::make_unique<SystemProcess>(pid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::stringstream ss;
|
||||
ss << executable;
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
|
|
@ -315,6 +488,7 @@ void platform::new_engine_instance(
|
|||
);
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool platform::stdin_has_data() {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
|
||||
#include "interfaces/Process.hpp"
|
||||
|
||||
namespace platform {
|
||||
void configure_encoding();
|
||||
/// @brief Get Environment locale in ISO format ll_CC
|
||||
|
|
@ -18,8 +21,10 @@ namespace platform {
|
|||
/// @brief Get current process running executable path
|
||||
std::filesystem::path get_executable_path();
|
||||
/// @brief Run a separate engine instance with specified arguments
|
||||
void new_engine_instance(
|
||||
const std::vector<std::string>& args, std::filesystem::path outputFile
|
||||
std::unique_ptr<Process> new_engine_instance(
|
||||
const std::vector<std::string>& args,
|
||||
std::filesystem::path outputFile,
|
||||
bool subProcess
|
||||
);
|
||||
/// @brief Open URL in web browser
|
||||
bool open_url(const std::string& url);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue