From 4fa6521c562ebf09343353717fb7c927df1eee86 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 21 May 2024 12:18:21 +0200 Subject: Add notion of thread-specific current working directory --- libbutl/builtin.cxx | 2 +- libbutl/path.cxx | 27 +++++++++++++++++++++++++++ libbutl/path.hxx | 22 +++++++++++++++++++++- libbutl/process.cxx | 20 +++++++++++++++++++- libbutl/utility.hxx | 2 ++ 5 files changed, 70 insertions(+), 3 deletions(-) diff --git a/libbutl/builtin.cxx b/libbutl/builtin.cxx index 2755bf1..a5861d4 100644 --- a/libbutl/builtin.cxx +++ b/libbutl/builtin.cxx @@ -243,7 +243,7 @@ namespace butl // completed using the current directory if it is relative. Fail if // std::system_error is thrown by the underlying function call. // - dir_path + static dir_path current_directory (const dir_path& wd, const function& fail) { try diff --git a/libbutl/path.cxx b/libbutl/path.cxx index bd66f13..909971b 100644 --- a/libbutl/path.cxx +++ b/libbutl/path.cxx @@ -26,6 +26,8 @@ #include #include // strcpy() +#include // thread_local + #include // throw_*_error() #include // process::current_id() @@ -55,10 +57,21 @@ namespace butl // char // + static +#ifdef __cpp_thread_local + thread_local +#else + __thread +#endif + const path_traits::string_type* current_directory_ = nullptr; + template <> LIBBUTL_SYMEXPORT path_traits::string_type path_traits:: current_directory () { + if (const auto* twd = current_directory_) + return *twd; + #ifdef _WIN32 char cwd[_MAX_PATH]; if (_getcwd (cwd, _MAX_PATH) == 0) @@ -98,6 +111,20 @@ namespace butl #endif } + template <> + LIBBUTL_SYMEXPORT const path_traits::string_type* path_traits:: + thread_current_directory () + { + return current_directory_; + } + + template <> + LIBBUTL_SYMEXPORT void path_traits:: + thread_current_directory (const string_type* twd) + { + current_directory_ = twd; + } + #ifndef _WIN32 static const small_vector tmp_vars ( {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}); diff --git a/libbutl/path.hxx b/libbutl/path.hxx index a691812..7d8b862 100644 --- a/libbutl/path.hxx +++ b/libbutl/path.hxx @@ -451,11 +451,31 @@ namespace butl // Get/set current working directory. Throw std::system_error to report // underlying OS errors. // + // The curren_directory() accessor (as well as the relevant process + // startup functions) have a notion of a "thread working directory" which + // is implemented as a thread-specific override that can be added/removed + // with thread_current_directory() below. + // + // Note that the current_directory() modifier always sets the process-wide + // working directory. + // + // See also thread_env(). + // static string_type current_directory (); static void - current_directory (string_type const&); + current_directory (const string_type&); + + // Get/set thread working directory override. Note that the passed + // pointed-to string should be valid (and immutable) for as long as the + // override is in effect. + // + static const string_type* + thread_current_directory (); + + static void + thread_current_directory (const string_type*); // Return the user home directory. Throw std::system_error to report // underlying OS errors. diff --git a/libbutl/process.cxx b/libbutl/process.cxx index e416807..1b8da98 100644 --- a/libbutl/process.cxx +++ b/libbutl/process.cxx @@ -262,7 +262,7 @@ namespace butl { // Note that there is a similar version for Win32. - typedef path::traits_type traits; + using traits = path::traits_type; size_t fn (strlen (f)); @@ -454,6 +454,15 @@ namespace butl else if (err == -2) in_efd.out = open_null (); + // If there is no user-supplied CWD and we have thread-specific override, + // use that instead of defaulting to the process-wide value. + // + if (cwd == nullptr || *cwd == '\0') + { + if (const string* twd = path::traits_type::thread_current_directory ()) + cwd = twd->c_str (); + } + const char* const* tevars (thread_env ()); // The posix_spawn()-based implementation. @@ -1392,6 +1401,15 @@ namespace butl throw process_error (m == nullptr ? last_error_msg () : m); }; + // If there is no user-supplied CWD and we have thread-specific override, + // use that instead of defaulting to the process-wide value. + // + if (cwd == nullptr || *cwd == '\0') + { + if (const string* twd = path::traits_type::thread_current_directory ()) + cwd = twd->c_str (); + } + // (Un)set the environment variables for the child process. // // Note that we can not do it incrementally, as for POSIX implementation. diff --git a/libbutl/utility.hxx b/libbutl/utility.hxx index 779a0aa..9eb052d 100644 --- a/libbutl/utility.hxx +++ b/libbutl/utility.hxx @@ -301,6 +301,8 @@ namespace butl // of overrides over the process environment (sets and unsets), the same as // for the process startup. // + // See also path_traits::thread_current_directory(). + // extern #ifdef __cpp_thread_local thread_local -- cgit v1.1