Tested x86_64-linux. Pushed to trunk.

Probably worth backporting after some time on trunk.

-- >8 --

Use strerror_r instead of strerror when available, due to the latter not
being thread-safe. This is complicated by Glibc providing a GNU-specific
strerror_r which is not compatible with POSIX strerror_r, so we need to
dispatch on the return type.

We can use the recently-added std::string::__resize_and_overwrite to
write directly into the string buffer when possible. Because we estimate
the initial buffer size we might end up with excess capacity in the
returned std::string. We can slightly tweak the std::system_error
constructors to make use of that excess capacity, so that in some cases
we require fewer allocations to construct the std::system_error::what()
string.

libstdc++-v3/ChangeLog:

        PR libstdc++/110133
        * include/std/system_error (system_error::system_error): Group
        arguments so that concatenation can reuse rvalue's capacity.
        * src/c++11/system_error.cc (strerror_string): New function.
        [_GLIBCXX_HAVE_STRERROR_R] (use_strerror_result): New functions.
        (generic_error_category::message): Use strerror_string.
        (system_error_category::message): Likewise.
---
 libstdc++-v3/include/std/system_error  |  4 +-
 libstdc++-v3/src/c++11/system_error.cc | 78 +++++++++++++++++++++++---
 2 files changed, 72 insertions(+), 10 deletions(-)

diff --git a/libstdc++-v3/include/std/system_error 
b/libstdc++-v3/include/std/system_error
index d71abc9766b..e26472bb0bf 100644
--- a/libstdc++-v3/include/std/system_error
+++ b/libstdc++-v3/include/std/system_error
@@ -563,7 +563,7 @@ namespace __adl_only
     : runtime_error(__ec.message()), _M_code(__ec) { }
 
     system_error(error_code __ec, const string& __what)
-    : runtime_error(__what + ": " + __ec.message()), _M_code(__ec) { }
+    : runtime_error(__what + (": " + __ec.message())), _M_code(__ec) { }
 
     system_error(error_code __ec, const char* __what)
     : runtime_error(__what + (": " + __ec.message())), _M_code(__ec) { }
@@ -576,7 +576,7 @@ namespace __adl_only
       _M_code(__v, __ecat) { }
 
     system_error(int __v, const error_category& __ecat, const string& __what)
-    : runtime_error(__what + ": " + error_code(__v, __ecat).message()),
+    : runtime_error(__what + (": " + error_code(__v, __ecat).message())),
       _M_code(__v, __ecat) { }
 
 #if __cplusplus >= 201103L
diff --git a/libstdc++-v3/src/c++11/system_error.cc 
b/libstdc++-v3/src/c++11/system_error.cc
index 748eee94168..876e2bb44fc 100644
--- a/libstdc++-v3/src/c++11/system_error.cc
+++ b/libstdc++-v3/src/c++11/system_error.cc
@@ -46,6 +46,74 @@ namespace
 {
   using std::string;
 
+#if _GLIBCXX_HAVE_STRERROR_R
+  // Handle the result of POSIX strerror_r.
+  inline std::size_t
+  use_strerror_result(int res, char* buf, std::size_t bufsz,
+                     std::size_t& nextbufsz)
+  {
+    if (res == 0) // Success.
+      return std::strlen(buf);
+
+    if (res == ERANGE) // Buffer too small to hold result string.
+      {
+       nextbufsz = 2 * bufsz;
+       return 0;
+      }
+    // else res == EINVAL, unknown error.
+    if (*buf == '\0') // No result string written to buffer.
+      {
+       const char msg[] = "Unknown error";
+       std::memcpy(buf, msg, sizeof(msg) - 1);
+       return sizeof(msg) - 1;
+      }
+    else // An "unknown error" string was already written to the buffer.
+      return std::strlen(buf);
+  }
+
+  // Handle the result of GNU strerror_r.
+  inline std::size_t
+  use_strerror_result(char* res, char* buf, std::size_t bufsz,
+                     std::size_t& nextbufsz)
+  {
+    if (res == buf) // Result string written to the buffer.
+      return std::strlen(res);
+
+    // Static result string returned, must be copied to the buffer.
+    std::size_t len = std::strlen(res);
+    if (len <= bufsz)
+      {
+       std::strcpy(buf, res);
+       return len;
+      }
+
+    // Reallocate and try again:
+    nextbufsz = len;
+    return 0;
+  }
+
+  string strerror_string(int err)
+  {
+    // Estimate maximum length of strerror strings (including "Unknown error").
+    // Any excess capacity here can be used by std::system_error constructors
+    // when concatenating strings.
+    std::size_t len = 60;
+    string s;
+    do
+      s.__resize_and_overwrite(len, [err, &len](char* p, std::size_t n) {
+       *p = '\0';
+       return use_strerror_result(strerror_r(err, p, n), p, n, len);
+      });
+    while (s.empty());
+    return s;
+  }
+#else
+  string strerror_string(int err)
+  {
+    return strerror(err); // XXX Not thread-safe.
+  }
+#endif
+
   template<typename T>
     struct constant_init
     {
@@ -66,11 +134,7 @@ namespace
     _GLIBCXX_DEFAULT_ABI_TAG
     string
     message(int i) const final
-    {
-      // XXX locale issues: how does one get or set loc.
-      // _GLIBCXX_HAVE_STRERROR_L, strerror_l(i, cloc)
-      return string(strerror(i));
-    }
+    { return strerror_string(i); }
 
     // Override this to avoid a virtual call to default_error_condition(i).
     bool
@@ -113,9 +177,7 @@ namespace
       }
       return string("Unknown error code");
 #else
-      // XXX locale issues: how does one get or set loc.
-      // _GLIBCXX_HAVE_STRERROR_L, strerror_l(i, cloc)
-      return string(strerror(i));
+      return strerror_string(i);
 #endif
     }
 
-- 
2.41.0

Reply via email to