问题
This question on sleeping forever has an answer that mentions this:
std::this_thread::sleep_until(
std::chrono::time_point<std::chrono::system_clock>::max());
and this:
std::this_thread::sleep_for(
std::chrono::system_clock::duration::max());
Running this code on Visual C++ 2017 RC actually doesn't sleep at all. I haven't checked out the sleep_until()
case, so I'm not sure what's going on there.
In the sleep_for()
case, the given duration
seems to be converted to an absolute time by adding it to system_clock::now()
which is then forwarded to sleep_until()
. The problem is that the addition overflows, giving a time in the past.
Looking at the C++17 draft in 30.3.2, neither sleep_until()
nor sleep_for()
seem to mention limits. There is nothing relevant in Timing specifications (30.2.4). As for duration::max()
, it is described in duration_values
(20.17.4.3) as: "The value returned shall compare greater than zero()
", which isn't helpful at all.
Honestly, I was rather surprised to see sleep_for()
fail for system_clock::duration::max()
, as it is a construct that make perfect sense to me.
What is the highest value I can pass to those functions that has a well-defined behaviour?
回答1:
Technically speaking std::chrono::system_clock::duration::max()
should sleep for a very long time (longer than you will or your grandchildren will live). And the standard enforces that.
But practically, implementors are still learning how to deal with overflow induced by chrono
conversions among durations of different precisions. So bugs are common.
It might be more practical to sleep for 9'000h
(a little over a year). There's no way this is going to cause overflow. And it is surely "forever" for your application.
However, don't hesitate to send a bug report to your vendor complaining that std::chrono::system_clock::duration::max()
doesn't work. It should. It is just tricky to make it work correctly. And making it work isn't portable, so it isn't reasonable to ask you to write some wrapper to do it.
Motivated by isanae's excellent comment below which asks for references:
30.3.3 [thread.thread.this]/p7 which describes sleep_for
says:
Effects: Blocks the calling thread for the relative timeout (30.2.4) specified by
rel_time
.
30.2.4 [thread.req.timing] which is a specification of all the timing requirements in the thread support library, says:
2 Implementations necessarily have some delay in returning from a timeout. Any overhead in interrupt response, function return, and scheduling induces a “quality of implementation” delay, expressed as duration
D
i. Ideally, this delay would be zero. Further, any contention for processor and memory resources induces a “quality of management” delay, expressed as durationD
m. The delay durations may vary from timeout to timeout, but in all cases shorter is better.3 The member functions whose names end in
_for
take an argument that specifies a duration. These functions produce relative timeouts. Implementations should use a steady clock to measure time for these functions.330 Given a duration argumentD
t, the real-time duration of the timeout isD
t+ D
i+ D
m.
Ok, so now I'm amused, because we aren't talking about a member function. We're talking about a namespace-scope function. This is a defect. Feel free to submit one.
But the spec provides no grace to overflow. The spec (nearly) clearly says that the implementation can't return until after the specified delay. It is vague on how much after, but clear on that it can't return before.
If you "bug" STL and he isn't cooperative, just refer him to me, and we will work it out. :-) Perhaps there is a standards bug I'm not seeing, and should be fixed. If so, I can help you file the bug against the standard instead of against VS. Or maybe VS has already addressed this issue, and the fix is available in an upgrade.
If this is a bug in VS, please let STL know that I am more than happy to assist in fixing it. There are different tradeoffs in addressing this issue on different platforms.
At the moment, I can't swear that there isn't a bug of this class in my own implementation (libc++). So no high-horse here. It is a difficult area for a std::lib to get right.
Update
I've looked at the libc++ sleep_for
and sleep_until
. sleep_for
correctly handles the overflow by sleeping for a "long time" (as much as the OS can handle). sleep_until
has the overflow bug.
Here is a very lightly tested fixed sleep_until
:
template <class _Clock, class _Duration>
void
sleep_until(const chrono::time_point<_Clock, _Duration>& __t)
{
using namespace chrono;
using __ldsec = duration<long double>;
_LIBCPP_CONSTEXPR time_point<_Clock, __ldsec> _Max =
time_point<_Clock, nanoseconds>::max();
time_point<_Clock, nanoseconds> __ns;
if (__t < _Max)
{
__ns = time_point_cast<nanoseconds>(__t);
if (__ns < __t)
__ns += nanoseconds{1};
}
else
__ns = time_point<_Clock, nanoseconds>::max();
mutex __mut;
condition_variable __cv;
unique_lock<mutex> __lk(__mut);
while (_Clock::now() < __ns)
__cv.wait_until(__lk, __ns);
}
The basic strategy is to do the overflow check using a long double
representation which not only has a very large maximum representable value, but also uses saturation arithmetic (has an infinity). If the input value is too big for the OS to handle, truncate it down to something the OS can handle.
On some platforms it might not be desirable to resort to floating point arithmetic. One might use __int128_t
instead. Or there is a more involved trick of converting to the "least common multiple" of the input and the native duration before doing the comparison. That conversion will only involve division (not multiplication) and so can't overflow. However it will not always give accurate answers for two values that are nearly equal. But it should work well enough for this use case.
For those interested in the latter (lcm
) strategy, here is how to compute that type:
namespace detail
{
template <class Duration0, class ...Durations>
struct lcm_type;
template <class Duration>
struct lcm_type<Duration>
{
using type = Duration;
};
template <class Duration1, class Duration2>
struct lcm_type<Duration1, Duration2>
{
template <class D>
using invert = std::chrono::duration
<
typename D::rep,
std::ratio_divide<std::ratio<1>, typename D::period>
>;
using type = invert<typename std::common_type<invert<Duration1>,
invert<Duration2>>::type>;
};
template <class Duration0, class Duration1, class Duration2, class ...Durations>
struct lcm_type<Duration0, Duration1, Duration2, Durations...>
{
using type = typename lcm_type<
typename lcm_type<Duration0, Duration1>::type,
Duration2, Durations...>::type;
};
} // namespace detail
One can think of lcm_type<duration1, duration2>
as the opposite of common_type<duration1, duration2>
. The former finds a duration which the conversion to only divides. The latter finds a duration which the conversion to only multiplies.
回答2:
It's unspecified, and it will overflow
I've had discussions with Billy O'Neal, one of the Visual C++ standard library developers, and Howard Hinnant, lead author of libc++. My conclusion is that the _for
and _until
family from the threading library will overflow in unspecified ways and you should not try to pass largish values to them. Whether the standard is under-specified on that subject is unclear to me.
The problem
All timed functions1 take either a duration
or a time_point
. Both are defined by their underlying type (representation) and ratio (period). The period can also be considered a "unit", such as a second or nanosecond.
There are two main places where overflow can happen:
- Before the platform-specific call, and
- During the conversion to a platform-specific type
Before the call
It is possible to avoid overflow in this situation, like Howard mentions in his answer, but "implementors are still learning how to deal with overflow induced by chrono
conversions among durations of different precisions".
Visual C++ 2017, for example, implements sleep_for()
in terms of sleep_until()
by adding the given duration to the current time returned by
system_clock::now()
. If the duration is too large, this will overflow. Other libraries, such as libstdc++, don't seem to have this problem.
The system call
Once you go deep enough, you'll have to interact with whatever platform you're on to do the actual work. This is where it gets messy.
On libstdc++, for example, the call to sleep_for()
ends up in nanosleep(), which takes a timespec
. This is a simplified version of it:
auto s = duration_cast<seconds>(time);
auto ns = duration_cast<nanoseconds>(time - s);
timespec ts = { s.count(), ns.count() };
nanosleep(&ts, &ts);
It's easy to overflow this: you just have to pass a time that is longer than LLONG_MAX seconds:
std::this_thread::sleep_for(hours::max());
This overflows the duration_cast
into seconds
and sets ts.tv_sec
to -3600, which doesn't sleep at all because nanosleep()
fails on negative values. It gets even better with sleep_until()
, which tries to call nanosleep()
in a loop, but it keeps failing, so it takes 100% of the processor for the duration of the wait.
The same thing happens in the Visual C++ 2017 library. Ignoring the overflow in sleep_for()
because it adds the duration to the current time, it ends up calling Sleep, which takes an unsigned 32-bit value in milliseconds.
Even if it called something more flexible like NtWaitForSingleObject() (which it might in the future), it's still only a signed 64-bit value in 100-nanosecond increments and can still overflow.
Bugs and limitations
I personally consider an overflow in the <chrono>
library itself to be a bug, such as Visual C++'s implementation of sleep_for()
in terms of sleep_until()
. I think whatever value you give should end up untouched right up to the final conversion before calling into a platform-specific function.
Once you get there though, if the platform doesn't support sleeping for the duration you're asking for, there is no real solution. As <chrono>
is prohibited from throwing exceptions, I accept than overflowing is a possibility. Although this then becomes undefined behaviour, I wish implementations would be a bit more careful treating overflows, such as libstdc++'s various failings of handling EINVAL and spinning in a tight loop.
Visual C++
I'm quoting a few things from the emails I got from Billy O'Neal because they add the point of view of a standard library developer:
Are you saying that this:
this_thread::sleep_for(system_clock::duration::max());
is undefined behaviour by the standard?
As far as I can tell, yes. It's kind of a grey area -- no maximum allowable range is really specified for these functions, but given their nature of accepting arbitrary
time_point
/duration
, which may be backed by some user-supplied bignum type of which the standard library has no knowledge, a conversion to some underlyingtime_point
/duration
type is essentially mandated.<chrono>
's design treats dealing with overflows as a non-goal (see duration_cast, for example, which outright prohibits implementing "as if infinity" and similar).The standard [...] doesn't give us any way to report failure to convert here -- the behavior is literally undefined. We are explicitly prohibited from throwing exceptions, we have no way of reasoning about what happens if you exceed
LLONG_MAX
, and so our only possible responses are "as if infinity" or go directly tostd::terminate()
, do not pass go, do not collect $200.libstdc++ and libc++ are targeting platforms for which
system_clock
actually maps to something the platform understands, where Unix timestamps are the law of the land. We are not targeting such a platform, and are obligated to map to/from "DWORD
milliseconds" and/orFILETIME
.About the only thing I can think of might be a reasonable use case for this thing would be to have some kind of sentinel value which means "infinity," but if we want to go there the standard should introduce a named constant and describe the behavior thereof.
I'd rather solve your direct problem (wanting a time value to be a sentinel for infinity) rather than attempting to mandate overflow checking. Overflow checking when you don't know anything about the types involved can get really expensive (in both complexity and run time), but checking for a magic constant (e.g.
chrono::duration<rep, period>::max()
orchrono::time_point<clock, duration>::max()
) should be cheap.
It also looks like a future update (ABI incompatible) would make major changes to <thread>
so it doesn't overflow in sleep_for()
anymore, but it is still limited by what the Windows API supports. Something like NtWaitForSingleObject() does support 64-bit values, but signed, because it supports both relative (negative) and absolute (positive) times.
1 By "timed functions", I mean any function for which 30.2.4 [thread.req.timing] applies, such as this_thread::sleep_for()
and this_thread::sleep_until()
, but also stuff in timed_mutex
, recursive_timed_mutex
, condition_variable
, etc.
来源:https://stackoverflow.com/questions/42638847/what-is-the-maximum-value-i-can-pass-to-stdthreadsleep-for-and-sleep-until