aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2024-12-17 21:32:19 +0000
committerJonathan Wakely <redi@gcc.gnu.org>2025-01-08 12:45:37 +0000
commit8ade3c3ea77e166f2873fb7ae57f9690e2b8d0e0 (patch)
tree94eeaa00951c3b95a00164e16344ed8b69d5e9bb
parent5f44b1776e748a7528020557036740905a11b1df (diff)
downloadgcc-8ade3c3ea77e166f2873fb7ae57f9690e2b8d0e0.zip
gcc-8ade3c3ea77e166f2873fb7ae57f9690e2b8d0e0.tar.gz
gcc-8ade3c3ea77e166f2873fb7ae57f9690e2b8d0e0.tar.bz2
libstdc++: Fix std::future::wait_until for subsecond negative times [PR118093]
The current check for negative times (i.e. before the epoch) only checks for a negative number of seconds. For a time 1ms before the epoch the seconds part will be zero, but the futex syscall will still fail with an EINVAL error. Extend the check to handle this case. This change adds a redundant check in the headers too, so that we avoid even calling into the library for negative times. Both checks can be marked [[unlikely]]. The check in the headers avoids the cost of splitting the time into seconds and nanoseconds and then making a PLT call. The check inside the library matches where we were checking already, and fixes existing binaries that were compiled against older headers but use a newer libstdc++.so.6 at runtime. libstdc++-v3/ChangeLog: PR libstdc++/118093 * include/bits/atomic_futex.h (_M_load_and_test_until_impl): Return false for times before the epoch. * src/c++11/futex.cc (_M_futex_wait_until): Extend check for negative times to check for subsecond times. Add unlikely attribute. (_M_futex_wait_until_steady): Likewise. * testsuite/30_threads/future/members/118093.cc: New test.
-rw-r--r--libstdc++-v3/include/bits/atomic_futex.h20
-rw-r--r--libstdc++-v3/src/c++11/futex.cc4
-rw-r--r--libstdc++-v3/testsuite/30_threads/future/members/118093.cc26
3 files changed, 40 insertions, 10 deletions
diff --git a/libstdc++-v3/include/bits/atomic_futex.h b/libstdc++-v3/include/bits/atomic_futex.h
index 1146f0a..e69420d 100644
--- a/libstdc++-v3/include/bits/atomic_futex.h
+++ b/libstdc++-v3/include/bits/atomic_futex.h
@@ -172,11 +172,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
bool __equal, memory_order __mo,
const chrono::time_point<std::chrono::system_clock, _Dur>& __atime)
{
- auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
- auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
- // XXX correct?
+ auto __d = __atime.time_since_epoch();
+ if (__d < __d.zero()) [[__unlikely__]]
+ return false;
+ auto __s = chrono::duration_cast<chrono::seconds>(__d);
+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__d - __s);
return _M_load_and_test_until(__assumed, __operand, __equal, __mo,
- true, __s.time_since_epoch(), __ns);
+ true, __s, __ns);
}
template<typename _Dur>
@@ -185,11 +187,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
bool __equal, memory_order __mo,
const chrono::time_point<std::chrono::steady_clock, _Dur>& __atime)
{
- auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
- auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
- // XXX correct?
+ auto __d = __atime.time_since_epoch();
+ if (__d < __d.zero()) [[__unlikely__]]
+ return false;
+ auto __s = chrono::duration_cast<chrono::seconds>(__d);
+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__d - __s);
return _M_load_and_test_until_steady(__assumed, __operand, __equal, __mo,
- true, __s.time_since_epoch(), __ns);
+ true, __s, __ns);
}
public:
diff --git a/libstdc++-v3/src/c++11/futex.cc b/libstdc++-v3/src/c++11/futex.cc
index 902b821..d3a7ca3 100644
--- a/libstdc++-v3/src/c++11/futex.cc
+++ b/libstdc++-v3/src/c++11/futex.cc
@@ -128,7 +128,7 @@ namespace
if (!futex_clock_realtime_unavailable.load(std::memory_order_relaxed))
{
// futex sets errno=EINVAL for absolute timeouts before the epoch.
- if (__s.count() < 0)
+ if (__s.count() < 0 || __ns.count() < 0) [[unlikely]]
return false;
syscall_timespec rt;
@@ -204,7 +204,7 @@ namespace
if (!futex_clock_monotonic_unavailable.load(std::memory_order_relaxed))
{
// futex sets errno=EINVAL for absolute timeouts before the epoch.
- if (__s.count() < 0) [[unlikely]]
+ if (__s.count() < 0 || __ns.count() < 0) [[unlikely]]
return false;
syscall_timespec rt;
diff --git a/libstdc++-v3/testsuite/30_threads/future/members/118093.cc b/libstdc++-v3/testsuite/30_threads/future/members/118093.cc
new file mode 100644
index 0000000..2bb1e1c
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/future/members/118093.cc
@@ -0,0 +1,26 @@
+// { dg-do run { target c++11 } }
+
+#include <chrono>
+#include <future>
+
+void
+test_sys()
+{
+ std::promise<void> p;
+ std::chrono::system_clock::time_point tp(std::chrono::milliseconds{-10});
+ (void) p.get_future().wait_until(tp);
+}
+
+void
+test_steady()
+{
+ std::promise<void> p;
+ std::chrono::steady_clock::time_point tp(std::chrono::milliseconds{-10});
+ (void) p.get_future().wait_until(tp);
+}
+
+int main()
+{
+ test_sys();
+ test_steady();
+}