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));

Reply via email to