[committed] libstdc++: Use strerror_r in std::generic_category()::message(int) [PR110133]
Checks
Commit Message
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(-)
@@ -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
@@ -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
}