This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/main by this push:
new 1b900a3868 [CPP_RPC] Replace legacy OS-specific API with std::
libraries (#19840)
1b900a3868 is described below
commit 1b900a386892408af6494e39014ca03bc8e2f1e6
Author: Balint Cristian <[email protected]>
AuthorDate: Fri Jun 19 16:55:16 2026 +0300
[CPP_RPC] Replace legacy OS-specific API with std:: libraries (#19840)
Replaces OS dependent API with standard cpp libraries in the
```tvm_rpc``` tool.
---
* Replace all filesystem calls with safe and portable std::filesystem
* Worker process remains fork()-ed (POSIX), due to kill() enforcement on
timeout
* Timer process is migrated from fork() to main with WNOHANG (nonblock)
pid watcher
* Watch a single fork()-ed worker, no more "guess" on which fork() will
"die/end" first
* Cleanup ambiguous doc entries, consistent default parameters across
various places
Currently tested with real tuning processes but limited to linux
machines.
---
apps/cpp_rpc/README.md | 8 +-
apps/cpp_rpc/main.cc | 14 ++--
apps/cpp_rpc/rpc_env.cc | 169 ++++++++++++--------------------------
apps/cpp_rpc/rpc_env.h | 13 ---
apps/cpp_rpc/rpc_server.cc | 76 +++++++----------
apps/cpp_rpc/rpc_server.h | 6 +-
apps/cpp_rpc/rpc_tracker_client.h | 2 +-
7 files changed, 97 insertions(+), 191 deletions(-)
diff --git a/apps/cpp_rpc/README.md b/apps/cpp_rpc/README.md
index d8d7eee377..a4557a3fd3 100644
--- a/apps/cpp_rpc/README.md
+++ b/apps/cpp_rpc/README.md
@@ -68,15 +68,15 @@ This folder contains a simple recipe to make RPC server in
c++.
```
Command line usage
server - Start the server
---host - The hostname of the server, Default=0.0.0.0
---port - The port of the RPC, Default=9090
---port-end - The end search port of the RPC, Default=9199
+--host - The listen address of the server, Default=0.0.0.0 (any)
+--port - The port of the RPC server, Default=9090
+--port-end - The end search port of the RPC server, Default=9099
--tracker - The RPC tracker address in host:port format e.g. 10.1.1.2:9190
Default=""
--key - The key used to identify the device type in tracker. Default=""
--custom-addr - Custom IP Address to Report to RPC Tracker. Default=""
--silent - Whether to run in silent mode. Default=False
Example
- ./tvm_rpc server --host=0.0.0.0 --port=9000 --port-end=9090
--tracker=127.0.0.1:9190 --key=rasp
+ ./tvm_rpc server --host=0.0.0.0 --port=9090 --port-end=9099
--tracker=127.0.0.1:9190 --key=rasp
```
## Note
diff --git a/apps/cpp_rpc/main.cc b/apps/cpp_rpc/main.cc
index b722202220..a349f66b92 100644
--- a/apps/cpp_rpc/main.cc
+++ b/apps/cpp_rpc/main.cc
@@ -49,9 +49,9 @@ using namespace tvm::support;
static const string kUsage =
"Command line usage\n"
" server - Start the server\n"
- "--host - The hostname of the server, Default=0.0.0.0\n"
- "--port - The port of the RPC, Default=9090\n"
- "--port-end - The end search port of the RPC, Default=9099\n"
+ "--host - The listen address of the server, Default=0.0.0.0 (any)\n"
+ "--port - The port of the RPC server, Default=9090\n"
+ "--port-end - The end search port of the RPC server, Default=9099\n"
"--tracker - The RPC tracker address in host:port format e.g.
10.1.1.2:9190 Default=\"\"\n"
"--key - The key used to identify the device type in tracker.
Default=\"\"\n"
"--custom-addr - Custom IP Address to Report to RPC Tracker.
Default=\"\"\n"
@@ -59,15 +59,15 @@ static const string kUsage =
"--silent - Whether to run in silent mode. Default=False\n"
"\n"
" Example\n"
- " ./tvm_rpc server --host=0.0.0.0 --port=9000 --port-end=9090 "
+ " ./tvm_rpc server --host=0.0.0.0 --port=9090 --port-end=9099 "
" --tracker=127.0.0.1:9190 --key=rasp"
"\n";
/*!
* \brief RpcServerArgs.
- * \arg host The hostname of the server, Default=0.0.0.0
- * \arg port The port of the RPC, Default=9090
- * \arg port_end The end search port of the RPC, Default=9099
+ * \arg host The listen address of the server, Default=0.0.0.0 (any)
+ * \arg port The port of the RPC server, Default=9090
+ * \arg port_end The end search port of the RPC server, Default=9099
* \arg tracker The address of RPC tracker in host:port format e.g.
10.77.1.234:9190 Default=""
* \arg key The key used to identify the device type in tracker. Default=""
* \arg custom_addr Custom IP Address to Report to RPC Tracker. Default=""
diff --git a/apps/cpp_rpc/rpc_env.cc b/apps/cpp_rpc/rpc_env.cc
index 4df5f87024..aae7eec683 100644
--- a/apps/cpp_rpc/rpc_env.cc
+++ b/apps/cpp_rpc/rpc_env.cc
@@ -20,30 +20,19 @@
* \file rpc_env.cc
* \brief Server environment of the RPC.
*/
+#include "rpc_env.h"
+
#include <tvm/ffi/extra/module.h>
#include <tvm/ffi/function.h>
#include <tvm/runtime/logging.h>
-#include <cerrno>
-#ifndef _WIN32
-#include <dirent.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#else
-#include <Windows.h>
-#include <direct.h>
-namespace {
-int mkdir(const char* path, int /* ignored */) { return _mkdir(path); }
-} // namespace
-#endif
-#include <cstring>
+#include <filesystem>
#include <fstream>
-#include <iostream>
#include <string>
+#include <system_error>
#include <vector>
#include "../../src/support/utils.h"
-#include "rpc_env.h"
namespace {
std::string GenerateUntarCommand(const std::string& tar_file, const
std::string& output_dir) {
@@ -69,37 +58,44 @@ namespace tvm {
namespace runtime {
RPCEnv::RPCEnv(const std::string& wd) {
- if (wd != "") {
+ std::error_code ec;
+ if (!wd.empty()) {
base_ = wd + "/.cache";
- mkdir(wd.c_str(), 0777);
- mkdir(base_.c_str(), 0777);
+ std::filesystem::create_directories(base_, ec);
+ if (ec) {
+ LOG(WARNING) << "Failed to create directory " << base_ << " : " <<
ec.message();
+ }
} else {
#if defined(ANDROID) || defined(__ANDROID__)
- char cwd[PATH_MAX];
- auto cmdline = fopen("/proc/self/cmdline", "r");
- fread(cwd, 1, sizeof(cwd), cmdline);
- fclose(cmdline);
- std::string android_base_ = "/data/data/" + std::string(cwd) + "/cache";
- struct stat statbuf;
+ std::string pkg_name;
+ if (std::ifstream cmdline("/proc/self/cmdline"); cmdline) {
+ std::getline(cmdline, pkg_name, '\0');
+ }
+ std::string android_base_ = "/data/data/" + pkg_name + "/cache";
// Check if application data directory exist. If not exist, usually means
we run tvm_rpc from
// adb shell terminal.
- if (stat(android_base_.data(), &statbuf) == -1 ||
!S_ISDIR(statbuf.st_mode)) {
+ if (!std::filesystem::is_directory(android_base_)) {
// Tmp directory is always writable for 'shell' user.
android_base_ = "/data/local/tmp";
}
base_ = android_base_ + "/rpc";
-
#elif !defined(_WIN32)
- char cwd[PATH_MAX];
- if (getcwd(cwd, sizeof(cwd))) {
- base_ = std::string(cwd) + "/rpc";
- } else {
+ base_ = std::filesystem::current_path(ec).string() + "/rpc";
+ if (ec) {
base_ = "./rpc";
}
#else
base_ = "./rpc";
#endif
- mkdir(base_.c_str(), 0777);
+ std::filesystem::create_directories(base_, ec);
+ if (ec) {
+ LOG(WARNING) << "Failed to create directory " << base_ << " : " <<
ec.message();
+ }
+ }
+ std::filesystem::permissions(base_, std::filesystem::perms::all,
+ std::filesystem::perm_options::replace, ec);
+ if (ec) {
+ LOG(WARNING) << "Failed to grant permissions to " << base_ << " : " <<
ec.message();
}
ffi::Function::SetGlobal(
@@ -157,11 +153,10 @@ std::string RPCEnv::GetPath(const std::string& file_name)
const {
* \brief Remove The RPC Environment cleanup function
*/
void RPCEnv::CleanUp() const {
- CleanDir(base_);
- if (!CheckPath(base_)) return;
- const int ret = rmdir(base_.c_str());
- if (ret != 0) {
- LOG(WARNING) << "Remove directory " << base_ << " failed";
+ std::error_code ec;
+ std::filesystem::remove_all(base_, ec);
+ if (ec) {
+ LOG(WARNING) << "Cleanup " << base_ << " failed: " << ec.message();
}
}
@@ -171,55 +166,16 @@ void RPCEnv::CleanUp() const {
* \return vector Files in directory.
*/
std::vector<std::string> ListDir(const std::string& dirname) {
+ std::error_code ec;
std::vector<std::string> vec;
-#ifndef _WIN32
- DIR* dp = opendir(dirname.c_str());
- if (dp == nullptr) {
- int errsv = errno;
- if (errsv == ENOENT) {
- return vec;
- }
- TVM_FFI_THROW(InternalError) << "ListDir " << dirname << " error: " <<
strerror(errsv);
+ auto iter = std::filesystem::directory_iterator(dirname, ec);
+ if (ec) {
+ if (ec == std::errc::no_such_file_or_directory) return vec;
+ TVM_FFI_THROW(InternalError) << "ListDir " << dirname << " error: " <<
ec.message();
}
- dirent* d;
- while ((d = readdir(dp)) != nullptr) {
- std::string filename = d->d_name;
- if (filename != "." && filename != "..") {
- std::string f = dirname;
- if (f[f.length() - 1] != '/') {
- f += '/';
- }
- f += d->d_name;
- vec.push_back(f);
- }
+ for (const auto& entry : iter) {
+ vec.push_back(entry.path().generic_string());
}
- closedir(dp);
-#elif defined(_WIN32)
- WIN32_FIND_DATAA fd;
- const std::string pattern = dirname + "/*";
- HANDLE handle = FindFirstFileA(pattern.c_str(), &fd);
- if (handle == INVALID_HANDLE_VALUE) {
- const int errsv = GetLastError();
- if (errsv == ERROR_FILE_NOT_FOUND || errsv == ERROR_PATH_NOT_FOUND) {
- return vec;
- }
- TVM_FFI_THROW(InternalError) << "ListDir " << dirname << " error: " <<
strerror(errsv);
- }
- do {
- std::string filename = fd.cFileName;
- if (filename != "." && filename != "..") {
- std::string f = dirname;
- if (f[f.length() - 1] != '/') {
- f += '/';
- }
- f += filename;
- vec.push_back(f);
- }
- } while (FindNextFileA(handle, &fd));
- FindClose(handle);
-#else
- TVM_FFI_THROW(InternalError) << "Operating system not supported";
-#endif
return vec;
}
@@ -299,7 +255,16 @@ std::string BuildSharedLibrary(std::string file) {
CreateShared(file_name, {file});
} else if (support::EndsWith(file, ".tar")) {
const std::string tmp_dir = "./rpc/tmp/";
- mkdir(tmp_dir.c_str(), 0777);
+ std::error_code ec;
+ std::filesystem::create_directories(tmp_dir, ec);
+ if (ec) {
+ LOG(WARNING) << "Failed to create directory " << tmp_dir << " : " <<
ec.message();
+ }
+ std::filesystem::permissions(tmp_dir, std::filesystem::perms::all,
+ std::filesystem::perm_options::replace, ec);
+ if (ec) {
+ LOG(WARNING) << "Failed to grant permissions to " << tmp_dir << " : " <<
ec.message();
+ }
const std::string cmd = GenerateUntarCommand(file, tmp_dir);
@@ -309,45 +274,15 @@ std::string BuildSharedLibrary(std::string file) {
TVM_FFI_THROW(InternalError) << err_msg;
}
CreateShared(file_name, ListDir(tmp_dir));
- CleanDir(tmp_dir);
- (void)rmdir(tmp_dir.c_str());
+ std::filesystem::remove_all(tmp_dir, ec);
+ if (ec) {
+ LOG(WARNING) << "Remove " << tmp_dir << " failed: " << ec.message();
+ }
} else {
file_name = file;
}
return file_name;
}
-/*!
- * \brief CheckPath Checks file or directory if exists
- * \param dirname The name of the directory
- * \return True if path exists.
- */
-bool CheckPath(const std::string& pathname) {
-#if defined(_WIN32)
- DWORD attribs = GetFileAttributesA(pathname.c_str());
- return (attribs != INVALID_FILE_ATTRIBUTES);
-#else
- struct stat info;
- return (stat(pathname.c_str(), &info) == 0);
-#endif
-}
-
-/*!
- * \brief CleanDir Removes the files from the directory
- * \param dirname The name of the directory
- */
-void CleanDir(const std::string& dirname) {
- if (!CheckPath(dirname)) return;
- auto files = ListDir(dirname);
- for (const auto& filename : files) {
- std::string file_path = dirname + "/";
- file_path += filename;
- const int ret = std::remove(filename.c_str());
- if (ret != 0) {
- LOG(WARNING) << "Remove file " << filename << " failed";
- }
- }
-}
-
} // namespace runtime
} // namespace tvm
diff --git a/apps/cpp_rpc/rpc_env.h b/apps/cpp_rpc/rpc_env.h
index bd5e2f9422..6a35109b22 100644
--- a/apps/cpp_rpc/rpc_env.h
+++ b/apps/cpp_rpc/rpc_env.h
@@ -31,19 +31,6 @@
namespace tvm {
namespace runtime {
-/*!
- * \brief CheckPath Checks if file or directory exists
- * \param dirname The name of the directory
- * \return True if path exists.
- */
-bool CheckPath(const std::string& pathname);
-
-/*!
- * \brief CleanDir Removes the files from the directory
- * \param dirname The name of the directory
- */
-void CleanDir(const std::string& dirname);
-
/*!
* \brief ListDir Get the list of files in a directory
* \param dirname The root directory name
diff --git a/apps/cpp_rpc/rpc_server.cc b/apps/cpp_rpc/rpc_server.cc
index 88971cc34c..db9d1fb82b 100644
--- a/apps/cpp_rpc/rpc_server.cc
+++ b/apps/cpp_rpc/rpc_server.cc
@@ -34,6 +34,7 @@
#include <set>
#include <string>
#include <thread>
+#include <utility>
#include "../../src/runtime/rpc/rpc_endpoint.h"
#include "../../src/runtime/rpc/rpc_socket_impl.h"
@@ -50,25 +51,6 @@ using namespace std::chrono;
namespace tvm {
namespace runtime {
-/*!
- * \brief wait the child process end.
- * \param status status value
- */
-#if defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__)
-static pid_t waitPidEintr(int* status) {
- pid_t pid = 0;
- while ((pid = waitpid(-1, status, 0)) == -1) {
- if (errno == EINTR) {
- continue;
- } else {
- perror("waitpid");
- abort();
- }
- }
- return pid;
-}
-#endif
-
#ifdef __ANDROID__
static std::string getNextString(std::stringstream* iss) {
std::string str = iss->str();
@@ -88,7 +70,7 @@ static std::string getNextString(std::stringstream* iss) {
/*!
* \brief RPCServer RPC Server class.
*
- * \param host The hostname of the server, Default=0.0.0.0
+ * \param host The listen address of the server, Default=0.0.0.0 (any)
*
* \param port_search_start The low end of the search range for an
* available port for the RPC, Default=9090
@@ -137,7 +119,7 @@ class RPCServer {
void Start() {
listen_sock_.Create();
my_port_ = listen_sock_.TryBindHost(host_, port_search_start_,
port_search_end_);
- LOG(INFO) << "bind to " << host_ << ":" << my_port_;
+ LOG(INFO) << "Bind to " << host_ << ":" << my_port_;
listen_sock_.Listen(1);
std::future<void> proc(std::async(std::launch::async,
&RPCServer::ListenLoopProc, this));
proc.get();
@@ -176,13 +158,6 @@ class RPCServer {
#if defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__)
// step 3: serving
if (timeout != 0) {
- const pid_t timer_pid = fork();
- if (timer_pid == 0) {
- // Timer process
- sleep(timeout);
- _exit(0);
- }
-
const pid_t worker_pid = fork();
if (worker_pid == 0) {
// Worker process
@@ -190,27 +165,36 @@ class RPCServer {
_exit(0);
}
- int status_first = 0;
- const pid_t finished_first = waitPidEintr(&status_first);
- if (finished_first == timer_pid) {
- kill(worker_pid, SIGTERM);
- } else if (finished_first == worker_pid) {
- kill(timer_pid, SIGTERM);
- } else {
- LOG(INFO) << "Child pid=" << finished_first << " unexpected, but
still continue.";
+ int status = 0;
+ bool timed_out = false;
+ auto start_timer = std::chrono::steady_clock::now();
+ while (true) {
+ // Check worker pid (non-blocking)
+ int ret = waitpid(worker_pid, &status, WNOHANG);
+ if (ret == worker_pid) {
+ break;
+ } else if (ret == -1) {
+ if (errno == EINTR) continue;
+ break;
+ }
+ // Check worker timeout
+ if (std::chrono::steady_clock::now() - start_timer >=
std::chrono::seconds(timeout)) {
+ timed_out = true;
+ kill(worker_pid, SIGTERM);
+ waitpid(worker_pid, &status, 0);
+ break;
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
- int status_second = 0;
- waitPidEintr(&status_second);
-
// Logging.
- if (finished_first == timer_pid) {
+ if (timed_out) {
LOG(INFO) << "Child pid=" << worker_pid << " killed"
<< " (timeout = " << timeout << " sec)"
- << ", status = " << status_second;
- } else if (finished_first == worker_pid) {
+ << ", status = " << status;
+ } else {
LOG(INFO) << "Child pid=" << worker_pid << " finished"
- << ", status = " << status_first;
+ << ", status = " << status;
}
} else {
auto pid = fork();
@@ -384,9 +368,9 @@ void ServerLoopFromChild(SOCKET socket) {
/*!
* \brief RPCServerCreate Creates the RPC Server.
- * \param host The hostname of the server, Default=0.0.0.0
- * \param port The port of the RPC, Default=9090
- * \param port_end The end search port of the RPC, Default=9099
+ * \param host The listen address of the server, Default=0.0.0.0 (any)
+ * \param port The port of the RPC server, Default=9090
+ * \param port_end The end search port of the RPC server, Default=9099
* \param tracker_addr The address of RPC tracker in host:port format e.g.
10.77.1.234:9190
* Default="" \param key The key used to identify the device type in tracker.
Default="" \param
* custom_addr Custom IP Address to Report to RPC Tracker. Default="" \param
silent Whether run in
diff --git a/apps/cpp_rpc/rpc_server.h b/apps/cpp_rpc/rpc_server.h
index 9bb61065c5..4da43ba43a 100644
--- a/apps/cpp_rpc/rpc_server.h
+++ b/apps/cpp_rpc/rpc_server.h
@@ -42,9 +42,9 @@ void ServerLoopFromChild(SOCKET socket);
/*!
* \brief RPCServerCreate Creates the RPC Server.
- * \param host The hostname of the server, Default=0.0.0.0
- * \param port The port of the RPC, Default=9090
- * \param port_end The end search port of the RPC, Default=9099
+ * \param host The listen address of the server, Default=0.0.0.0 (any)
+ * \param port The port of the RPC server, Default=9090
+ * \param port_end The end search port of the RPC server, Default=9099
* \param tracker The address of RPC tracker in host:port format e.g.
10.77.1.234:9190 Default=""
* \param key The key used to identify the device type in tracker. Default=""
* \param custom_addr Custom IP Address to Report to RPC Tracker. Default=""
diff --git a/apps/cpp_rpc/rpc_tracker_client.h
b/apps/cpp_rpc/rpc_tracker_client.h
index 4b1e36b70d..63eb7b6427 100644
--- a/apps/cpp_rpc/rpc_tracker_client.h
+++ b/apps/cpp_rpc/rpc_tracker_client.h
@@ -206,7 +206,7 @@ class TrackerClient {
auto period = (std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now() - tbegin))
.count();
- TVM_FFI_ICHECK(period < timeout) << "Failed to connect to server" <<
addr.AsString();
+ TVM_FFI_ICHECK(period < timeout) << "Failed to connect to tracker " <<
addr.AsString();
LOG(WARNING) << "Cannot connect to tracker " << addr.AsString() << "
retry in "
<< retry_period << " seconds.";
std::this_thread::sleep_for(std::chrono::seconds(retry_period));