Skip to content

Commit 3502e48

Browse files
authored
Rollup merge of #103056 - beetrees:timespec-bug-fix, r=thomcc
Fix `checked_{add,sub}_duration` incorrectly returning `None` when `other` has more than `i64::MAX` seconds Use `checked_{add,sub}_unsigned` in `checked_{add,sub}_duration` so that the correct result is returned when adding/subtracting durations with more than `i64::MAX` seconds.
2 parents dd9a7bf + 5def753 commit 3502e48

File tree

4 files changed

+35
-24
lines changed

4 files changed

+35
-24
lines changed

library/std/src/sys/hermit/time.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,7 @@ impl Timespec {
4040
}
4141

4242
fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
43-
let mut secs = other
44-
.as_secs()
45-
.try_into() // <- target type would be `libc::time_t`
46-
.ok()
47-
.and_then(|secs| self.t.tv_sec.checked_add(secs))?;
43+
let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
4844

4945
// Nano calculations can't overflow because nanos are <1B which fit
5046
// in a u32.
@@ -57,11 +53,7 @@ impl Timespec {
5753
}
5854

5955
fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
60-
let mut secs = other
61-
.as_secs()
62-
.try_into() // <- target type would be `libc::time_t`
63-
.ok()
64-
.and_then(|secs| self.t.tv_sec.checked_sub(secs))?;
56+
let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
6557

6658
// Similar to above, nanos can't overflow.
6759
let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;

library/std/src/sys/solid/time.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ impl SystemTime {
4747
}
4848

4949
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
50-
Some(SystemTime(self.0.checked_add(other.as_secs().try_into().ok()?)?))
50+
Some(SystemTime(self.0.checked_add_unsigned(other.as_secs())?))
5151
}
5252

5353
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
54-
Some(SystemTime(self.0.checked_sub(other.as_secs().try_into().ok()?)?))
54+
Some(SystemTime(self.0.checked_sub_unsigned(other.as_secs())?))
5555
}
5656
}

library/std/src/sys/unix/time.rs

+4-12
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,7 @@ impl Timespec {
113113
}
114114

115115
pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
116-
let mut secs = other
117-
.as_secs()
118-
.try_into() // <- target type would be `i64`
119-
.ok()
120-
.and_then(|secs| self.tv_sec.checked_add(secs))?;
116+
let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
121117

122118
// Nano calculations can't overflow because nanos are <1B which fit
123119
// in a u32.
@@ -126,23 +122,19 @@ impl Timespec {
126122
nsec -= NSEC_PER_SEC as u32;
127123
secs = secs.checked_add(1)?;
128124
}
129-
Some(Timespec::new(secs, nsec as i64))
125+
Some(Timespec::new(secs, nsec.into()))
130126
}
131127

132128
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
133-
let mut secs = other
134-
.as_secs()
135-
.try_into() // <- target type would be `i64`
136-
.ok()
137-
.and_then(|secs| self.tv_sec.checked_sub(secs))?;
129+
let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
138130

139131
// Similar to above, nanos can't overflow.
140132
let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32;
141133
if nsec < 0 {
142134
nsec += NSEC_PER_SEC as i32;
143135
secs = secs.checked_sub(1)?;
144136
}
145-
Some(Timespec::new(secs, nsec as i64))
137+
Some(Timespec::new(secs, nsec.into()))
146138
}
147139

148140
#[allow(dead_code)]

library/std/src/time/tests.rs

+27
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::{Duration, Instant, SystemTime, UNIX_EPOCH};
2+
use core::fmt::Debug;
23
#[cfg(not(target_arch = "wasm32"))]
34
use test::{black_box, Bencher};
45

@@ -201,6 +202,32 @@ fn since_epoch() {
201202
assert!(a < hundred_twenty_years);
202203
}
203204

205+
#[test]
206+
fn big_math() {
207+
// Check that the same result occurs when adding/subtracting each duration one at a time as when
208+
// adding/subtracting them all at once.
209+
#[track_caller]
210+
fn check<T: Eq + Copy + Debug>(start: Option<T>, op: impl Fn(&T, Duration) -> Option<T>) {
211+
const DURATIONS: [Duration; 2] =
212+
[Duration::from_secs(i64::MAX as _), Duration::from_secs(50)];
213+
if let Some(start) = start {
214+
assert_eq!(
215+
op(&start, DURATIONS.into_iter().sum()),
216+
DURATIONS.into_iter().try_fold(start, |t, d| op(&t, d))
217+
)
218+
}
219+
}
220+
221+
check(SystemTime::UNIX_EPOCH.checked_sub(Duration::from_secs(100)), SystemTime::checked_add);
222+
check(SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(100)), SystemTime::checked_sub);
223+
224+
let instant = Instant::now();
225+
check(instant.checked_sub(Duration::from_secs(100)), Instant::checked_add);
226+
check(instant.checked_sub(Duration::from_secs(i64::MAX as _)), Instant::checked_add);
227+
check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub);
228+
check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub);
229+
}
230+
204231
macro_rules! bench_instant_threaded {
205232
($bench_name:ident, $thread_count:expr) => {
206233
#[bench]

0 commit comments

Comments
 (0)