Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.

Commit 4cbd8e4

Browse files
committed
chore: spawn process
1 parent 4df0704 commit 4cbd8e4

File tree

8 files changed

+178
-156
lines changed

8 files changed

+178
-156
lines changed

engine/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ add_executable(${TARGET_NAME} main.cc
147147
${CMAKE_CURRENT_SOURCE_DIR}/extensions/python-engine/python_engine.cc
148148

149149
${CMAKE_CURRENT_SOURCE_DIR}/utils/dylib_path_manager.cc
150+
${CMAKE_CURRENT_SOURCE_DIR}/utils/process/utils.cc
150151

151152
${CMAKE_CURRENT_SOURCE_DIR}/extensions/remote-engine/remote_engine.cc
152153

engine/cli/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ add_executable(${TARGET_NAME} main.cc
9595
${CMAKE_CURRENT_SOURCE_DIR}/../utils/file_manager_utils.cc
9696
${CMAKE_CURRENT_SOURCE_DIR}/../utils/curl_utils.cc
9797
${CMAKE_CURRENT_SOURCE_DIR}/../utils/system_info_utils.cc
98+
${CMAKE_CURRENT_SOURCE_DIR}/../utils/process/utils.cc
9899
)
99100

100101
target_link_libraries(${TARGET_NAME} PRIVATE CLI11::CLI11)

engine/cli/commands/server_start_cmd.cc

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "services/engine_service.h"
44
#include "utils/cortex_utils.h"
55
#include "utils/file_manager_utils.h"
6+
#include "utils/process/utils.h"
67

78
#if defined(_WIN32) || defined(_WIN64)
89
#include "utils/widechar_conv.h"
@@ -103,25 +104,26 @@ bool ServerStartCmd::Exec(const std::string& host, int port,
103104
}
104105

105106
#else
106-
// Unix-like system-specific code to fork a child process
107-
pid_t pid = fork();
107+
std::vector<std::string> commands;
108+
// Some engines requires to add lib search path before process being created
109+
auto download_srv = std::make_shared<DownloadService>();
110+
auto dylib_path_mng = std::make_shared<cortex::DylibPathManager>();
111+
auto db_srv = std::make_shared<DatabaseService>();
112+
EngineService(download_srv, dylib_path_mng, db_srv).RegisterEngineLibPath();
108113

114+
std::string p = cortex_utils::GetCurrentPath() + "/" + exe;
115+
commands.push_back(p);
116+
commands.push_back("--config_file_path");
117+
commands.push_back(get_config_file_path());
118+
commands.push_back("--data_folder_path");
119+
commands.push_back(get_data_folder_path());
120+
commands.push_back("--loglevel");
121+
commands.push_back(log_level_);
122+
auto pid = cortex::process::SpawnProcess(commands);
109123
if (pid < 0) {
110124
// Fork failed
111125
std::cerr << "Could not start server: " << std::endl;
112126
return false;
113-
} else if (pid == 0) {
114-
// Some engines requires to add lib search path before process being created
115-
auto download_srv = std::make_shared<DownloadService>();
116-
auto dylib_path_mng = std::make_shared<cortex::DylibPathManager>();
117-
auto db_srv = std::make_shared<DatabaseService>();
118-
EngineService(download_srv, dylib_path_mng, db_srv).RegisterEngineLibPath();
119-
120-
std::string p = cortex_utils::GetCurrentPath() + "/" + exe;
121-
execl(p.c_str(), exe.c_str(), "--start-server", "--config_file_path",
122-
get_config_file_path().c_str(), "--data_folder_path",
123-
get_data_folder_path().c_str(), "--loglevel", log_level_.c_str(),
124-
(char*)0);
125127
} else {
126128
// Parent process
127129
if (!TryConnectToServer(host, port)) {

engine/extensions/python-engine/python_engine.cc

Lines changed: 4 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <iostream>
44
#include <sstream>
55
#include <string>
6+
67
namespace python_engine {
78
namespace {
89
constexpr const int k200OK = 200;
@@ -61,33 +62,6 @@ static size_t WriteCallback(char* ptr, size_t size, size_t nmemb,
6162
return size * nmemb;
6263
}
6364

64-
std::string ConstructWindowsCommandLine(const std::vector<std::string>& args) {
65-
std::string cmd_line;
66-
for (const auto& arg : args) {
67-
// Simple escaping for Windows command line
68-
std::string escaped_arg = arg;
69-
if (escaped_arg.find(' ') != std::string::npos) {
70-
// Wrap in quotes and escape existing quotes
71-
for (char& c : escaped_arg) {
72-
if (c == '"')
73-
c = '\\';
74-
}
75-
escaped_arg = "\"" + escaped_arg + "\"";
76-
}
77-
cmd_line += escaped_arg + " ";
78-
}
79-
return cmd_line;
80-
}
81-
82-
std::vector<char*> ConvertToArgv(const std::vector<std::string>& args) {
83-
std::vector<char*> argv;
84-
for (const auto& arg : args) {
85-
argv.push_back(const_cast<char*>(arg.c_str()));
86-
}
87-
argv.push_back(nullptr);
88-
return argv;
89-
}
90-
9165
} // namespace
9266

9367
PythonEngine::PythonEngine() : q_(4 /*n_parallel*/, "python_engine") {}
@@ -106,80 +80,6 @@ config::PythonModelConfig* PythonEngine::GetModelConfig(
10680
return nullptr;
10781
}
10882

109-
// TODO(sang) move to utils to re-use
110-
pid_t PythonEngine::SpawnProcess(const std::string& model,
111-
const std::vector<std::string>& command) {
112-
try {
113-
#if defined(_WIN32)
114-
// Windows process creation
115-
STARTUPINFOA si = {0};
116-
PROCESS_INFORMATION pi = {0};
117-
si.cb = sizeof(si);
118-
119-
// Construct command line
120-
std::string cmd_line = ConstructWindowsCommandLine(command);
121-
122-
// Convert string to char* for Windows API
123-
char command_buffer[4096];
124-
strncpy_s(command_buffer, cmd_line.c_str(), sizeof(command_buffer));
125-
126-
if (!CreateProcessA(NULL, // lpApplicationName
127-
command_buffer, // lpCommandLine
128-
NULL, // lpProcessAttributes
129-
NULL, // lpThreadAttributes
130-
FALSE, // bInheritHandles
131-
0, // dwCreationFlags
132-
NULL, // lpEnvironment
133-
NULL, // lpCurrentDirectory
134-
&si, // lpStartupInfo
135-
&pi // lpProcessInformation
136-
)) {
137-
throw std::runtime_error("Failed to create process on Windows");
138-
}
139-
140-
// Store the process ID
141-
pid_t pid = pi.dwProcessId;
142-
process_map_[model] = pid;
143-
144-
// Close handles to avoid resource leaks
145-
CloseHandle(pi.hProcess);
146-
CloseHandle(pi.hThread);
147-
148-
return pid;
149-
150-
#elif defined(__APPLE__) || defined(__linux__)
151-
// POSIX process creation
152-
pid_t pid;
153-
154-
// Convert command vector to char*[]
155-
auto argv = ConvertToArgv(command);
156-
157-
// Use posix_spawn for cross-platform compatibility
158-
auto spawn_result = posix_spawn(&pid, // pid output
159-
command[0].c_str(), // executable path
160-
NULL, // file actions
161-
NULL, // spawn attributes
162-
argv.data(), // argument vector
163-
NULL // environment (inherit)
164-
);
165-
166-
if (spawn_result != 0) {
167-
throw std::runtime_error("Failed to spawn process");
168-
}
169-
170-
// Store the process ID
171-
process_map_[model] = pid;
172-
return pid;
173-
174-
#else
175-
#error Unsupported platform
176-
#endif
177-
} catch (const std::exception& e) {
178-
LOG_ERROR << "Process spawning error: " << e.what();
179-
return -1;
180-
}
181-
}
182-
18383
bool PythonEngine::TerminateModelProcess(const std::string& model) {
18484
auto it = process_map_.find(model);
18585
if (it == process_map_.end()) {
@@ -405,7 +305,8 @@ void PythonEngine::LoadModel(
405305

406306
// Add the parsed arguments to the command
407307
command.insert(command.end(), args.begin(), args.end());
408-
pid = SpawnProcess(model, command);
308+
pid = cortex::process::SpawnProcess(command);
309+
process_map_[model] = pid;
409310
if (pid == -1) {
410311
std::unique_lock lock(models_mutex_);
411312
if (models_.find(model) != models_.end()) {
@@ -467,7 +368,7 @@ void PythonEngine::UnloadModel(
467368

468369
auto model = (*json_body)["model"].asString();
469370

470-
{
371+
{
471372
if (TerminateModelProcess(model)) {
472373
std::unique_lock lock(models_mutex_);
473374
models_.erase(model);

engine/extensions/python-engine/python_engine.h

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,8 @@
1616
#include "utils/file_manager_utils.h"
1717

1818
#include "utils/curl_utils.h"
19-
#if defined(_WIN32)
20-
#include <process.h>
21-
#include <windows.h>
22-
using pid_t = DWORD;
23-
#elif defined(__APPLE__) || defined(__linux__)
24-
#include <signal.h>
25-
#include <spawn.h>
26-
#include <sys/types.h>
27-
#include <sys/wait.h>
28-
#include <unistd.h>
29-
#endif
19+
#include "utils/process/utils.h"
20+
3021
// Helper for CURL response
3122
namespace python_engine {
3223
struct StreamContext {
@@ -52,7 +43,6 @@ class PythonEngine : public EngineI {
5243
std::unordered_map<std::string, pid_t> process_map_;
5344
trantor::ConcurrentTaskQueue q_;
5445

55-
5646
// Helper functions
5747
CurlResponse MakePostRequest(const std::string& model,
5848
const std::string& path,
@@ -67,8 +57,6 @@ class PythonEngine : public EngineI {
6757
const std::function<void(Json::Value&&, Json::Value&&)>& callback);
6858

6959
// Process manager functions
70-
pid_t SpawnProcess(const std::string& model,
71-
const std::vector<std::string>& command);
7260
bool TerminateModelProcess(const std::string& model);
7361

7462
// Internal model management

engine/services/hardware_service.cc

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
#endif
1010
#include "cli/commands/cortex_upd_cmd.h"
1111
#include "database/hardware.h"
12+
#include "services/engine_service.h"
1213
#include "utils/cortex_utils.h"
14+
#include "utils/dylib_path_manager.h"
15+
#include "utils/process/utils.h"
1316

1417
namespace {
1518
bool TryConnectToServer(const std::string& host, int port) {
@@ -149,41 +152,36 @@ bool HardwareService::Restart(const std::string& host, int port) {
149152
}
150153

151154
#else
152-
// Unix-like system-specific code to fork a child process
153-
pid_t pid = fork();
154-
155+
std::vector<std::string> commands;
156+
// Some engines requires to add lib search path before process being created
157+
auto download_srv = std::make_shared<DownloadService>();
158+
auto dylib_path_mng = std::make_shared<cortex::DylibPathManager>();
159+
auto db_srv = std::make_shared<DatabaseService>();
160+
EngineService(download_srv, dylib_path_mng, db_srv).RegisterEngineLibPath();
161+
std::string p = cortex_utils::GetCurrentPath() + "/" + exe;
162+
commands.push_back(p);
163+
commands.push_back("--ignore_cout");
164+
commands.push_back("--config_file_path");
165+
commands.push_back(get_config_file_path());
166+
commands.push_back("--data_folder_path");
167+
commands.push_back(get_data_folder_path());
168+
commands.push_back("--loglevel");
169+
commands.push_back(luh::LogLevelStr(luh::global_log_level));
170+
auto pid = cortex::process::SpawnProcess(commands);
155171
if (pid < 0) {
156172
// Fork failed
157173
std::cerr << "Could not start server: " << std::endl;
158174
return false;
159-
} else if (pid == 0) {
160-
// No need to configure LD_LIBRARY_PATH for macOS
161-
#if !defined(__APPLE__) || !defined(__MACH__)
162-
const char* name = "LD_LIBRARY_PATH";
163-
auto data = getenv(name);
164-
std::string v;
165-
if (auto g = getenv(name); g) {
166-
v += g;
167-
}
168-
CTL_INF("LD_LIBRARY_PATH: " << v);
169-
auto llamacpp_path = file_manager_utils::GetCudaToolkitPath(kLlamaRepo);
170-
auto trt_path = file_manager_utils::GetCudaToolkitPath(kTrtLlmRepo);
171-
172-
auto new_v = trt_path.string() + ":" + llamacpp_path.string() + ":" + v;
173-
setenv(name, new_v.c_str(), true);
174-
CTL_INF("LD_LIBRARY_PATH: " << getenv(name));
175-
#endif
176-
std::string p = cortex_utils::GetCurrentPath() + "/" + exe;
177-
execl(p.c_str(), exe.c_str(), "--ignore_cout", "--config_file_path",
178-
get_config_file_path().c_str(), "--data_folder_path",
179-
get_data_folder_path().c_str(), "--loglevel",
180-
luh::LogLevelStr(luh::global_log_level).c_str(), (char*)0);
181175
} else {
182176
// Parent process
183177
if (!TryConnectToServer(host, port)) {
184178
return false;
185179
}
180+
std::cout << "Server started" << std::endl;
181+
std::cout << "API Documentation available at: http://" << host << ":"
182+
<< port << std::endl;
186183
}
184+
187185
#endif
188186
return true;
189187
}

0 commit comments

Comments
 (0)