Add guards for Int64 overflow in `Timer` methods (#29)
Motivation: `Timer.record*` methods are generic to cover all cases of `BinaryInteger` values and converts the passed value into `Int64` before doing time unit conversions and overflow checks. Modifications: All methods that accept `BinaryInteger` types and converts to `Int64` now guards against values higher than `Int64.max` Result: Users can reliably use the `Timer.record*` APIs with `BinaryInteger` types without concern for unknowing fatal errors.
This commit is contained in:
parent
4a9e0de637
commit
e4883dab79
|
|
@ -273,6 +273,8 @@ public class Timer {
|
|||
/// - value: Duration to record.
|
||||
@inlinable
|
||||
public func recordMicroseconds<DataType: BinaryInteger>(_ duration: DataType) {
|
||||
guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) }
|
||||
|
||||
let result = Int64(duration).multipliedReportingOverflow(by: 1000)
|
||||
if result.overflow {
|
||||
self.recordNanoseconds(Int64.max)
|
||||
|
|
@ -296,6 +298,8 @@ public class Timer {
|
|||
/// - value: Duration to record.
|
||||
@inlinable
|
||||
public func recordMilliseconds<DataType: BinaryInteger>(_ duration: DataType) {
|
||||
guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) }
|
||||
|
||||
let result = Int64(duration).multipliedReportingOverflow(by: 1_000_000)
|
||||
if result.overflow {
|
||||
self.recordNanoseconds(Int64.max)
|
||||
|
|
@ -319,6 +323,8 @@ public class Timer {
|
|||
/// - value: Duration to record.
|
||||
@inlinable
|
||||
public func recordSeconds<DataType: BinaryInteger>(_ duration: DataType) {
|
||||
guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) }
|
||||
|
||||
let result = Int64(duration).multipliedReportingOverflow(by: 1_000_000_000)
|
||||
if result.overflow {
|
||||
self.recordNanoseconds(Int64.max)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ extension MetricsTests {
|
|||
("testTimerBlock", testTimerBlock),
|
||||
("testTimerVariants", testTimerVariants),
|
||||
("testTimerOverflow", testTimerOverflow),
|
||||
("testTimerHandlesUnsignedOverflow", testTimerHandlesUnsignedOverflow),
|
||||
("testGauge", testGauge),
|
||||
("testGaugeBlock", testGaugeBlock),
|
||||
("testMUX", testMUX),
|
||||
|
|
|
|||
|
|
@ -217,6 +217,27 @@ class MetricsTests: XCTestCase {
|
|||
XCTAssertEqual(testTimer.values[3].1, Int64.max, "expected value to match")
|
||||
}
|
||||
|
||||
func testTimerHandlesUnsignedOverflow() throws {
|
||||
// bootstrap with our test metrics
|
||||
let metrics = TestMetrics()
|
||||
MetricsSystem.bootstrapInternal(metrics)
|
||||
// run the test
|
||||
let timer = Timer(label: "test-timer")
|
||||
let testTimer = timer.handler as! TestTimer
|
||||
// micro
|
||||
timer.recordMicroseconds(UInt64.max)
|
||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||
XCTAssertEqual(testTimer.values[0].1, Int64.max, "expected value to match")
|
||||
// milli
|
||||
timer.recordMilliseconds(UInt64.max)
|
||||
XCTAssertEqual(testTimer.values.count, 2, "expected number of entries to match")
|
||||
XCTAssertEqual(testTimer.values[1].1, Int64.max, "expected value to match")
|
||||
// seconds
|
||||
timer.recordSeconds(UInt64.max)
|
||||
XCTAssertEqual(testTimer.values.count, 3, "expected number of entries to match")
|
||||
XCTAssertEqual(testTimer.values[2].1, Int64.max, "expected value to match")
|
||||
}
|
||||
|
||||
func testGauge() throws {
|
||||
// bootstrap with our test metrics
|
||||
let metrics = TestMetrics()
|
||||
|
|
|
|||
Loading…
Reference in New Issue