Skip to content

Commit dfa3570

Browse files
authored
Merge pull request #2576 from readdle/windows-thread-sleep
[Windows] Thread.sleep(...) time interval precision loss
2 parents 5e27d97 + e0b9a6a commit dfa3570

File tree

2 files changed

+46
-2
lines changed

2 files changed

+46
-2
lines changed

Foundation/Thread.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ open class Thread : NSObject {
119119

120120
// the timeout is in 100ns units
121121
var liTimeout: LARGE_INTEGER =
122-
LARGE_INTEGER(QuadPart: LONGLONG(date.timeIntervalSinceNow) * -10000000)
122+
LARGE_INTEGER(QuadPart: LONGLONG(date.timeIntervalSinceNow * -10_000_000))
123123
if !SetWaitableTimer(hTimer, &liTimeout, 0, nil, nil, false) {
124124
return
125125
}
@@ -156,7 +156,7 @@ open class Thread : NSObject {
156156

157157
// the timeout is in 100ns units
158158
var liTimeout: LARGE_INTEGER =
159-
LARGE_INTEGER(QuadPart: LONGLONG(interval) * -10000000)
159+
LARGE_INTEGER(QuadPart: LONGLONG(interval * -10_000_000))
160160
if !SetWaitableTimer(hTimer, &liTimeout, 0, nil, nil, false) {
161161
return
162162
}

TestFoundation/TestThread.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class TestThread : XCTestCase {
2727
("test_mainThread", test_mainThread),
2828
("test_callStackSymbols", testExpectedToFailOnAndroid(test_callStackSymbols, "Android doesn't support backtraces at the moment.")),
2929
("test_callStackReturnAddresses", testExpectedToFailOnAndroid(test_callStackReturnAddresses, "Android doesn't support backtraces at the moment.")),
30+
("test_sleepForTimeInterval", test_sleepForTimeInterval),
31+
("test_sleepUntilDate", test_sleepUntilDate),
3032
]
3133

3234
#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT
@@ -151,4 +153,46 @@ class TestThread : XCTestCase {
151153
XCTAssertTrue(addresses.count > 0)
152154
XCTAssertTrue(addresses.count <= 128)
153155
}
156+
157+
func test_sleepForTimeInterval() {
158+
let measureOversleep = { (timeInterval: TimeInterval) -> TimeInterval in
159+
let start = Date()
160+
Thread.sleep(forTimeInterval: timeInterval)
161+
162+
// Measures time Thread.sleep spends over specified timeInterval value
163+
return -(start.timeIntervalSinceNow + timeInterval)
164+
}
165+
166+
// Allow a little early wake-ups. Sleep timer on Windows
167+
// is more precise than timer used in Date implementation.
168+
let allowedOversleepRange = -0.00001..<0.1
169+
170+
let oversleep1 = measureOversleep(TimeInterval(0.9))
171+
XCTAssertTrue(allowedOversleepRange.contains(oversleep1), "Oversleep \(oversleep1) is not in expected range \(allowedOversleepRange)")
172+
173+
let oversleep2 = measureOversleep(TimeInterval(1.2))
174+
XCTAssertTrue(allowedOversleepRange.contains(oversleep2), "Oversleep \(oversleep2) is not in expected range \(allowedOversleepRange)")
175+
176+
let oversleep3 = measureOversleep(TimeInterval(1.0))
177+
XCTAssertTrue(allowedOversleepRange.contains(oversleep3), "Oversleep \(oversleep3) is not in expected range \(allowedOversleepRange)")
178+
}
179+
180+
func test_sleepUntilDate() {
181+
let measureOversleep = { (date: Date) -> TimeInterval in
182+
Thread.sleep(until: date)
183+
return -date.timeIntervalSinceNow
184+
}
185+
186+
let allowedOversleepRange = -0.00001..<0.1
187+
188+
let oversleep1 = measureOversleep(Date(timeIntervalSinceNow: 0.8))
189+
XCTAssertTrue(allowedOversleepRange.contains(oversleep1), "Oversleep \(oversleep1) is not in expected range \(allowedOversleepRange)")
190+
191+
let oversleep2 = measureOversleep(Date(timeIntervalSinceNow: 1.1))
192+
XCTAssertTrue(allowedOversleepRange.contains(oversleep2), "Oversleep \(oversleep2) is not in expected range \(allowedOversleepRange)")
193+
194+
let oversleep3 = measureOversleep(Date(timeIntervalSinceNow: 1.0))
195+
XCTAssertTrue(allowedOversleepRange.contains(oversleep3), "Oversleep \(oversleep3) is not in expected range \(allowedOversleepRange)")
196+
}
197+
154198
}

0 commit comments

Comments
 (0)