-
-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Description
As part of our (lately subpar) maintenance of the Windows 7 target, we run the library's tests. Since 1.90 while building for stable, we have noticed that the library/std/tests/thread.rs::sleep_until test started to fail quite frequently.
The test itself is quite simple:
rust/library/std/tests/thread.rs
Lines 23 to 31 in fa5eda1
| fn sleep_until() { | |
| let now = Instant::now(); | |
| let period = Duration::from_millis(100); | |
| let deadline = now + period; | |
| thread::sleep_until(deadline); | |
| let elapsed = now.elapsed(); | |
| assert!(elapsed >= period); | |
| } |
and fails on assertion misses such as:
running 1 test
[.../lib.rs:125:9] elapsed = 95.9499ms
[.../lib.rs:125:9] period = 100ms
thread 'tests::sleep_until' panicked at [...]/lib.rs:126:9:
assertion failed: elapsed >= period
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
test tests::sleep_until ... FAILED
These errors are appropriate, but there is still something not quite right somewhere.
The current implementation of sleep_until under Windows is still a proxy to sleep(deadline - Instant::now()) if I'm not mistaken, so the issue could come from sleep itself. In fact, the issue can also be reproduced with the slightly simpler:
#[test]
fn sleep() {
let now = Instant::now();
let period = Duration::from_millis(100);
thread::sleep(period);
let elapsed = now.elapsed();
dbg!(elapsed, period);
assert!(elapsed >= period);
}Under Windows 7, the high precision timer API is not available, so the standard Sleep function gets used with the given Duration's milliseconds. On the one hand, Rust's thread::sleep documents that "It will never sleep less [than the duration specified].", while on the other hand Window's Sleep only documents that "The system clock 'ticks' at a constant rate. If dwMilliseconds is less than the resolution of the system clock, the thread may sleep for less than the specified length of time. If dwMilliseconds is greater than one tick but less than two, the wait can be anywhere between one and two ticks, and so on.", but not more specifically that it assuringly cannot suspend for less than the specified length of time. Trying to get or set the "accuracy of the sleep interval" using the recommended methods for example to be 1ms did not help. Could it be that Windows 7's implementation of the function is just not that precise anyway?
The test actually has two facets: sleeping and measuring time. The latter could also be the cause of the errors here. As documented and implemented, the Instant::now bases itself on QueryPerformanceCounter under Windows. The function itself does not document much, but "Acquiring high-resolution time stamps" gives some lower-level details. Among these, it does mention a high adherence to hardware features. Could it therefore be the cause here? We use QEMU for running Windows 7 in VMs. I have tried to enable as much hardware-related options of QEMU to see if it could fix the problem, but it was to no avail. If it is indeed the cause, it is more a Windows 7 or QEMU issue?
We are having trouble answering these questions ourselves. Does anyone know what could be happening here? In particular, is it something that will never realistically work under Windows 7 or the testing facilities we use, while under actual hardware there would be no issue, and so the test should be simply ignored?
Note that I can reproduce these errors under our 1.89 build already, so considering the test landed in stable under 1.90, the issue might not be recent. Also, although it might be obvious, I cannot reproduce this under Windows 10+. This could only be a testing issue, so I'm not tagging this as a bug just yet.
cc @roblabla
@rustbot label F-sleep_until O-windows-7 T-libs I-flaky-test A-time A-thread E-help-wanted