aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2024-05-21 12:18:21 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2024-05-21 12:18:21 +0200
commit4fa6521c562ebf09343353717fb7c927df1eee86 (patch)
tree124ab05f06082905a77addcdfa39cda956b53358
parente5fe5459ffbc53504d46331c520f5bff90085a5b (diff)
Add notion of thread-specific current working directory
-rw-r--r--libbutl/builtin.cxx2
-rw-r--r--libbutl/path.cxx27
-rw-r--r--libbutl/path.hxx22
-rw-r--r--libbutl/process.cxx20
-rw-r--r--libbutl/utility.hxx2
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<error_record ()>& 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 <atomic>
#include <cstring> // strcpy()
+#include <libbutl/ft/lang.hxx> // thread_local
+
#include <libbutl/utility.hxx> // throw_*_error()
#include <libbutl/process.hxx> // process::current_id()
@@ -55,10 +57,21 @@ namespace butl
// char
//
+ static
+#ifdef __cpp_thread_local
+ thread_local
+#else
+ __thread
+#endif
+ const path_traits<char>::string_type* current_directory_ = nullptr;
+
template <>
LIBBUTL_SYMEXPORT path_traits<char>::string_type path_traits<char>::
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<char>::string_type* path_traits<char>::
+ thread_current_directory ()
+ {
+ return current_directory_;
+ }
+
+ template <>
+ LIBBUTL_SYMEXPORT void path_traits<char>::
+ thread_current_directory (const string_type* twd)
+ {
+ current_directory_ = twd;
+ }
+
#ifndef _WIN32
static const small_vector<string, 4> 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