ComparableTimeMark and specialized TimeSource KT-54082
- Introduce ComparableTimeMark interface extending TimeMark, and TimeSource.WithComparableMarks - a time source that returns such time marks. - Implement ComparableTimeMark in ValueTimeMark and AbstractLong/DoubleTimeMark classes.
This commit is contained in:
@@ -132,12 +132,6 @@ public inline fun measureTime(block: () -> kotlin.Unit): kotlin.time.Duration
|
||||
@kotlin.time.ExperimentalTime
|
||||
public inline fun <T> measureTimedValue(block: () -> T): kotlin.time.TimedValue<T>
|
||||
|
||||
@kotlin.time.ExperimentalTime
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
@kotlin.internal.InlineOnly
|
||||
@kotlin.Deprecated(level = DeprecationLevel.ERROR, message = "Comparing one TimeMark to another is not a well defined operation because these time marks could have been obtained from the different time sources.")
|
||||
public inline operator fun kotlin.time.TimeMark.compareTo(other: kotlin.time.TimeMark): kotlin.Int
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
@kotlin.time.ExperimentalTime
|
||||
public inline fun kotlin.time.TimeSource.measureTime(block: () -> kotlin.Unit): kotlin.time.Duration
|
||||
@@ -154,12 +148,6 @@ public inline fun <T> kotlin.time.TimeSource.measureTimedValue(block: () -> T):
|
||||
@kotlin.time.ExperimentalTime
|
||||
public inline fun <T> kotlin.time.TimeSource.Monotonic.measureTimedValue(block: () -> T): kotlin.time.TimedValue<T>
|
||||
|
||||
@kotlin.time.ExperimentalTime
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
@kotlin.internal.InlineOnly
|
||||
@kotlin.Deprecated(level = DeprecationLevel.ERROR, message = "Subtracting one TimeMark from another is not a well defined operation because these time marks could have been obtained from the different time sources.")
|
||||
public inline operator fun kotlin.time.TimeMark.minus(other: kotlin.time.TimeMark): kotlin.time.Duration
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.6")
|
||||
@kotlin.WasExperimental(markerClass = {kotlin.time.ExperimentalTime::class})
|
||||
@kotlin.internal.InlineOnly
|
||||
@@ -184,28 +172,44 @@ public fun kotlin.Long.toDuration(unit: kotlin.time.DurationUnit): kotlin.time.D
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
@kotlin.time.ExperimentalTime
|
||||
public abstract class AbstractDoubleTimeSource : kotlin.time.TimeSource {
|
||||
public abstract class AbstractDoubleTimeSource : kotlin.time.TimeSource.WithComparableMarks {
|
||||
public constructor AbstractDoubleTimeSource(unit: kotlin.time.DurationUnit)
|
||||
|
||||
protected final val unit: kotlin.time.DurationUnit { get; }
|
||||
|
||||
public open override fun markNow(): kotlin.time.TimeMark
|
||||
public open override fun markNow(): kotlin.time.ComparableTimeMark
|
||||
|
||||
protected abstract fun read(): kotlin.Double
|
||||
}
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
@kotlin.time.ExperimentalTime
|
||||
public abstract class AbstractLongTimeSource : kotlin.time.TimeSource {
|
||||
public abstract class AbstractLongTimeSource : kotlin.time.TimeSource.WithComparableMarks {
|
||||
public constructor AbstractLongTimeSource(unit: kotlin.time.DurationUnit)
|
||||
|
||||
protected final val unit: kotlin.time.DurationUnit { get; }
|
||||
|
||||
public open override fun markNow(): kotlin.time.TimeMark
|
||||
public open override fun markNow(): kotlin.time.ComparableTimeMark
|
||||
|
||||
protected abstract fun read(): kotlin.Long
|
||||
}
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.8")
|
||||
@kotlin.time.ExperimentalTime
|
||||
public interface ComparableTimeMark : kotlin.time.TimeMark, kotlin.Comparable<kotlin.time.ComparableTimeMark> {
|
||||
public open override operator fun compareTo(other: kotlin.time.ComparableTimeMark): kotlin.Int
|
||||
|
||||
public abstract override operator fun equals(other: kotlin.Any?): kotlin.Boolean
|
||||
|
||||
public abstract override fun hashCode(): kotlin.Int
|
||||
|
||||
public abstract operator fun minus(other: kotlin.time.ComparableTimeMark): kotlin.time.Duration
|
||||
|
||||
public open override operator fun minus(duration: kotlin.time.Duration): kotlin.time.ComparableTimeMark
|
||||
|
||||
public abstract override operator fun plus(duration: kotlin.time.Duration): kotlin.time.ComparableTimeMark
|
||||
}
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.6")
|
||||
@kotlin.WasExperimental(markerClass = {kotlin.time.ExperimentalTime::class})
|
||||
@kotlin.jvm.JvmInline
|
||||
@@ -587,7 +591,7 @@ public interface TimeSource {
|
||||
public companion object of TimeSource {
|
||||
}
|
||||
|
||||
public object Monotonic : kotlin.time.TimeSource {
|
||||
public object Monotonic : kotlin.time.TimeSource.WithComparableMarks {
|
||||
public open override fun markNow(): kotlin.time.TimeSource.Monotonic.ValueTimeMark
|
||||
|
||||
public open override fun toString(): kotlin.String
|
||||
@@ -595,7 +599,9 @@ public interface TimeSource {
|
||||
@kotlin.time.ExperimentalTime
|
||||
@kotlin.SinceKotlin(version = "1.7")
|
||||
@kotlin.jvm.JvmInline
|
||||
public final inline class ValueTimeMark : kotlin.time.TimeMark {
|
||||
public final inline class ValueTimeMark : kotlin.time.ComparableTimeMark {
|
||||
public final operator fun compareTo(other: kotlin.time.TimeSource.Monotonic.ValueTimeMark): kotlin.Int
|
||||
|
||||
public open override fun elapsedNow(): kotlin.time.Duration
|
||||
|
||||
public open override operator fun equals(other: kotlin.Any?): kotlin.Boolean
|
||||
@@ -606,13 +612,23 @@ public interface TimeSource {
|
||||
|
||||
public open override fun hashCode(): kotlin.Int
|
||||
|
||||
public open override operator fun minus(other: kotlin.time.ComparableTimeMark): kotlin.time.Duration
|
||||
|
||||
public open override operator fun minus(duration: kotlin.time.Duration): kotlin.time.TimeSource.Monotonic.ValueTimeMark
|
||||
|
||||
public final operator fun minus(other: kotlin.time.TimeSource.Monotonic.ValueTimeMark): kotlin.time.Duration
|
||||
|
||||
public open override operator fun plus(duration: kotlin.time.Duration): kotlin.time.TimeSource.Monotonic.ValueTimeMark
|
||||
|
||||
public open override fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.8")
|
||||
@kotlin.time.ExperimentalTime
|
||||
public interface WithComparableMarks : kotlin.time.TimeSource {
|
||||
public abstract override fun markNow(): kotlin.time.ComparableTimeMark
|
||||
}
|
||||
}
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
|
||||
@@ -132,12 +132,6 @@ public inline fun measureTime(block: () -> kotlin.Unit): kotlin.time.Duration
|
||||
@kotlin.time.ExperimentalTime
|
||||
public inline fun <T> measureTimedValue(block: () -> T): kotlin.time.TimedValue<T>
|
||||
|
||||
@kotlin.time.ExperimentalTime
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
@kotlin.internal.InlineOnly
|
||||
@kotlin.Deprecated(level = DeprecationLevel.ERROR, message = "Comparing one TimeMark to another is not a well defined operation because these time marks could have been obtained from the different time sources.")
|
||||
public inline operator fun kotlin.time.TimeMark.compareTo(other: kotlin.time.TimeMark): kotlin.Int
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
@kotlin.time.ExperimentalTime
|
||||
public inline fun kotlin.time.TimeSource.measureTime(block: () -> kotlin.Unit): kotlin.time.Duration
|
||||
@@ -154,12 +148,6 @@ public inline fun <T> kotlin.time.TimeSource.measureTimedValue(block: () -> T):
|
||||
@kotlin.time.ExperimentalTime
|
||||
public inline fun <T> kotlin.time.TimeSource.Monotonic.measureTimedValue(block: () -> T): kotlin.time.TimedValue<T>
|
||||
|
||||
@kotlin.time.ExperimentalTime
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
@kotlin.internal.InlineOnly
|
||||
@kotlin.Deprecated(level = DeprecationLevel.ERROR, message = "Subtracting one TimeMark from another is not a well defined operation because these time marks could have been obtained from the different time sources.")
|
||||
public inline operator fun kotlin.time.TimeMark.minus(other: kotlin.time.TimeMark): kotlin.time.Duration
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.6")
|
||||
@kotlin.WasExperimental(markerClass = {kotlin.time.ExperimentalTime::class})
|
||||
@kotlin.internal.InlineOnly
|
||||
@@ -184,28 +172,44 @@ public fun kotlin.Long.toDuration(unit: kotlin.time.DurationUnit): kotlin.time.D
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
@kotlin.time.ExperimentalTime
|
||||
public abstract class AbstractDoubleTimeSource : kotlin.time.TimeSource {
|
||||
public abstract class AbstractDoubleTimeSource : kotlin.time.TimeSource.WithComparableMarks {
|
||||
public constructor AbstractDoubleTimeSource(unit: kotlin.time.DurationUnit)
|
||||
|
||||
protected final val unit: kotlin.time.DurationUnit { get; }
|
||||
|
||||
public open override fun markNow(): kotlin.time.TimeMark
|
||||
public open override fun markNow(): kotlin.time.ComparableTimeMark
|
||||
|
||||
protected abstract fun read(): kotlin.Double
|
||||
}
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
@kotlin.time.ExperimentalTime
|
||||
public abstract class AbstractLongTimeSource : kotlin.time.TimeSource {
|
||||
public abstract class AbstractLongTimeSource : kotlin.time.TimeSource.WithComparableMarks {
|
||||
public constructor AbstractLongTimeSource(unit: kotlin.time.DurationUnit)
|
||||
|
||||
protected final val unit: kotlin.time.DurationUnit { get; }
|
||||
|
||||
public open override fun markNow(): kotlin.time.TimeMark
|
||||
public open override fun markNow(): kotlin.time.ComparableTimeMark
|
||||
|
||||
protected abstract fun read(): kotlin.Long
|
||||
}
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.8")
|
||||
@kotlin.time.ExperimentalTime
|
||||
public interface ComparableTimeMark : kotlin.time.TimeMark, kotlin.Comparable<kotlin.time.ComparableTimeMark> {
|
||||
public open override operator fun compareTo(other: kotlin.time.ComparableTimeMark): kotlin.Int
|
||||
|
||||
public abstract override operator fun equals(other: kotlin.Any?): kotlin.Boolean
|
||||
|
||||
public abstract override fun hashCode(): kotlin.Int
|
||||
|
||||
public abstract operator fun minus(other: kotlin.time.ComparableTimeMark): kotlin.time.Duration
|
||||
|
||||
public open override operator fun minus(duration: kotlin.time.Duration): kotlin.time.ComparableTimeMark
|
||||
|
||||
public abstract override operator fun plus(duration: kotlin.time.Duration): kotlin.time.ComparableTimeMark
|
||||
}
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.6")
|
||||
@kotlin.WasExperimental(markerClass = {kotlin.time.ExperimentalTime::class})
|
||||
@kotlin.jvm.JvmInline
|
||||
@@ -587,7 +591,7 @@ public interface TimeSource {
|
||||
public companion object of TimeSource {
|
||||
}
|
||||
|
||||
public object Monotonic : kotlin.time.TimeSource {
|
||||
public object Monotonic : kotlin.time.TimeSource.WithComparableMarks {
|
||||
public open override fun markNow(): kotlin.time.TimeSource.Monotonic.ValueTimeMark
|
||||
|
||||
public open override fun toString(): kotlin.String
|
||||
@@ -595,7 +599,9 @@ public interface TimeSource {
|
||||
@kotlin.time.ExperimentalTime
|
||||
@kotlin.SinceKotlin(version = "1.7")
|
||||
@kotlin.jvm.JvmInline
|
||||
public final inline class ValueTimeMark : kotlin.time.TimeMark {
|
||||
public final inline class ValueTimeMark : kotlin.time.ComparableTimeMark {
|
||||
public final operator fun compareTo(other: kotlin.time.TimeSource.Monotonic.ValueTimeMark): kotlin.Int
|
||||
|
||||
public open override fun elapsedNow(): kotlin.time.Duration
|
||||
|
||||
public open override operator fun equals(other: kotlin.Any?): kotlin.Boolean
|
||||
@@ -606,13 +612,23 @@ public interface TimeSource {
|
||||
|
||||
public open override fun hashCode(): kotlin.Int
|
||||
|
||||
public open override operator fun minus(other: kotlin.time.ComparableTimeMark): kotlin.time.Duration
|
||||
|
||||
public open override operator fun minus(duration: kotlin.time.Duration): kotlin.time.TimeSource.Monotonic.ValueTimeMark
|
||||
|
||||
public final operator fun minus(other: kotlin.time.TimeSource.Monotonic.ValueTimeMark): kotlin.time.Duration
|
||||
|
||||
public open override operator fun plus(duration: kotlin.time.Duration): kotlin.time.TimeSource.Monotonic.ValueTimeMark
|
||||
|
||||
public open override fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.8")
|
||||
@kotlin.time.ExperimentalTime
|
||||
public interface WithComparableMarks : kotlin.time.TimeSource {
|
||||
public abstract override fun markNow(): kotlin.time.ComparableTimeMark
|
||||
}
|
||||
}
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
|
||||
@@ -15,15 +15,16 @@ import kotlin.time.TimeSource.Monotonic.ValueTimeMark
|
||||
internal actual typealias ValueTimeMarkReading = Any
|
||||
|
||||
@ExperimentalTime
|
||||
internal interface DefaultTimeSource : TimeSource {
|
||||
internal interface DefaultTimeSource : TimeSource.WithComparableMarks {
|
||||
override fun markNow(): ValueTimeMark
|
||||
fun elapsedFrom(timeMark: ValueTimeMark): Duration
|
||||
fun differenceBetween(one: ValueTimeMark, another: ValueTimeMark): Duration
|
||||
fun adjustReading(timeMark: ValueTimeMark, duration: Duration): ValueTimeMark
|
||||
}
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalTime
|
||||
internal actual object MonotonicTimeSource : DefaultTimeSource, TimeSource { // TODO: interface should not be required here
|
||||
internal actual object MonotonicTimeSource : DefaultTimeSource, TimeSource.WithComparableMarks { // TODO: interface should not be required here
|
||||
|
||||
private val actualSource: DefaultTimeSource = run {
|
||||
val isNode: Boolean = js("typeof process !== 'undefined' && process.versions && !!process.versions.node")
|
||||
@@ -40,6 +41,8 @@ internal actual object MonotonicTimeSource : DefaultTimeSource, TimeSource { //
|
||||
|
||||
actual override fun markNow(): ValueTimeMark = actualSource.markNow()
|
||||
actual override fun elapsedFrom(timeMark: ValueTimeMark): Duration = actualSource.elapsedFrom(timeMark)
|
||||
actual override fun differenceBetween(one: ValueTimeMark, another: ValueTimeMark): Duration = actualSource.differenceBetween(one, another)
|
||||
|
||||
actual override fun adjustReading(timeMark: ValueTimeMark, duration: Duration): ValueTimeMark =
|
||||
actualSource.adjustReading(timeMark, duration)
|
||||
}
|
||||
@@ -58,11 +61,19 @@ internal class HrTimeSource(private val process: Process) : DefaultTimeSource {
|
||||
process.hrtime(timeMark.reading as Array<Double>)
|
||||
.let { (seconds, nanos) -> seconds.toDuration(DurationUnit.SECONDS) + nanos.toDuration(DurationUnit.NANOSECONDS) }
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun differenceBetween(one: ValueTimeMark, another: ValueTimeMark): Duration {
|
||||
val (s1, n1) = one.reading as Array<Double>
|
||||
val (s2, n2) = another.reading as Array<Double>
|
||||
return (if (s1 == s2 && n1 == n2) Duration.ZERO else (s1 - s2).toDuration(DurationUnit.SECONDS)) + (n1 - n2).toDuration(DurationUnit.NANOSECONDS)
|
||||
}
|
||||
|
||||
override fun adjustReading(timeMark: ValueTimeMark, duration: Duration): ValueTimeMark =
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(timeMark.reading as Array<Double>).let { (seconds, nanos) ->
|
||||
duration.toComponents { _, addNanos ->
|
||||
arrayOf<Double>(sumCheckNaN(seconds + truncate(duration.toDouble(DurationUnit.SECONDS))), nanos + addNanos)
|
||||
val resultSeconds = sumCheckNaN(seconds + truncate(duration.toDouble(DurationUnit.SECONDS)))
|
||||
arrayOf<Double>(resultSeconds, if (resultSeconds.isFinite()) nanos + addNanos else 0.0)
|
||||
}
|
||||
}.let(TimeSource.Monotonic::ValueTimeMark)
|
||||
|
||||
@@ -78,6 +89,13 @@ internal class PerformanceTimeSource(val performance: Performance) :
|
||||
|
||||
override fun markNow(): ValueTimeMark = ValueTimeMark(read())
|
||||
override fun elapsedFrom(timeMark: ValueTimeMark): Duration = (read() - timeMark.reading as Double).milliseconds
|
||||
|
||||
override fun differenceBetween(one: ValueTimeMark, another: ValueTimeMark): Duration {
|
||||
val ms1 = one.reading as Double
|
||||
val ms2 = another.reading as Double
|
||||
return if (ms1 == ms2) Duration.ZERO else (ms1 - ms2).milliseconds
|
||||
}
|
||||
|
||||
override fun adjustReading(timeMark: ValueTimeMark, duration: Duration): ValueTimeMark =
|
||||
ValueTimeMark(sumCheckNaN(timeMark.reading as Double + duration.toDouble(DurationUnit.MILLISECONDS)))
|
||||
|
||||
@@ -91,6 +109,13 @@ internal object DateNowTimeSource : DefaultTimeSource {
|
||||
|
||||
override fun markNow(): ValueTimeMark = ValueTimeMark(read())
|
||||
override fun elapsedFrom(timeMark: ValueTimeMark): Duration = (read() - timeMark.reading as Double).milliseconds
|
||||
|
||||
override fun differenceBetween(one: ValueTimeMark, another: ValueTimeMark): Duration {
|
||||
val ms1 = one.reading as Double
|
||||
val ms2 = another.reading as Double
|
||||
return if (ms1 == ms2) Duration.ZERO else (ms1 - ms2).milliseconds
|
||||
}
|
||||
|
||||
override fun adjustReading(timeMark: ValueTimeMark, duration: Duration): ValueTimeMark =
|
||||
ValueTimeMark(sumCheckNaN(timeMark.reading as Double + duration.toDouble(DurationUnit.MILLISECONDS)))
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import kotlin.time.TimeSource.Monotonic.ValueTimeMark
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalTime
|
||||
internal actual object MonotonicTimeSource : TimeSource {
|
||||
internal actual object MonotonicTimeSource : TimeSource.WithComparableMarks {
|
||||
private val zero: Long = System.nanoTime()
|
||||
private fun read(): Long = System.nanoTime() - zero
|
||||
override fun toString(): String = "TimeSource(System.nanoTime())"
|
||||
@@ -18,6 +18,9 @@ internal actual object MonotonicTimeSource : TimeSource {
|
||||
actual fun elapsedFrom(timeMark: ValueTimeMark): Duration =
|
||||
saturatingDiff(read(), timeMark.reading)
|
||||
|
||||
actual fun differenceBetween(one: ValueTimeMark, another: ValueTimeMark): Duration =
|
||||
saturatingOriginsDiff(one.reading, another.reading)
|
||||
|
||||
actual fun adjustReading(timeMark: ValueTimeMark, duration: Duration): ValueTimeMark =
|
||||
ValueTimeMark(saturatingAdd(timeMark.reading, duration))
|
||||
}
|
||||
|
||||
@@ -26,6 +26,15 @@ public interface TimeSource {
|
||||
*/
|
||||
public fun markNow(): TimeMark
|
||||
|
||||
/**
|
||||
* A [TimeSource] that returns [time marks][ComparableTimeMark] that can be compared for difference with each other.
|
||||
*/
|
||||
@SinceKotlin("1.8")
|
||||
@ExperimentalTime
|
||||
public interface WithComparableMarks : TimeSource {
|
||||
override fun markNow(): ComparableTimeMark
|
||||
}
|
||||
|
||||
/**
|
||||
* The most precise time source available in the platform.
|
||||
*
|
||||
@@ -35,28 +44,60 @@ public interface TimeSource {
|
||||
* The function [markNow] of this time source returns the specialized [ValueTimeMark] that is an inline value class
|
||||
* wrapping a platform-dependent time reading value.
|
||||
*/
|
||||
public object Monotonic : TimeSource {
|
||||
public object Monotonic : TimeSource.WithComparableMarks {
|
||||
override fun markNow(): ValueTimeMark = MonotonicTimeSource.markNow()
|
||||
override fun toString(): String = MonotonicTimeSource.toString()
|
||||
|
||||
/**
|
||||
* A specialized [kotlin.time.TimeMark] returned by [TimeSource.Monotonic].
|
||||
* A specialized [kotlin.time.TimeMark] returned by [TimeSource.Monotonic] time source.
|
||||
*
|
||||
* This time mark is implemented as an inline value class wrapping a platform-dependent
|
||||
* time reading value of the default monotonic time source, thus allowing to avoid additional boxing
|
||||
* of that value.
|
||||
*
|
||||
* The operations [plus] and [minus] are also specialized to return [ValueTimeMark] type.
|
||||
*
|
||||
* This time mark implements [ComparableTimeMark] and therefore is comparable with other time marks
|
||||
* obtained from the same [TimeSource.Monotonic] time source.
|
||||
*/
|
||||
@ExperimentalTime
|
||||
@SinceKotlin("1.7")
|
||||
@JvmInline
|
||||
public value class ValueTimeMark internal constructor(internal val reading: ValueTimeMarkReading) : TimeMark {
|
||||
public value class ValueTimeMark internal constructor(internal val reading: ValueTimeMarkReading) : ComparableTimeMark {
|
||||
override fun elapsedNow(): Duration = MonotonicTimeSource.elapsedFrom(this)
|
||||
override fun plus(duration: Duration): ValueTimeMark = MonotonicTimeSource.adjustReading(this, duration)
|
||||
override fun minus(duration: Duration): ValueTimeMark = MonotonicTimeSource.adjustReading(this, -duration)
|
||||
override fun hasPassedNow(): Boolean = !elapsedNow().isNegative()
|
||||
override fun hasNotPassedNow(): Boolean = elapsedNow().isNegative()
|
||||
|
||||
override fun minus(other: ComparableTimeMark): Duration {
|
||||
if (other !is ValueTimeMark)
|
||||
throw IllegalArgumentException("Subtracting or comparing time marks from different time sources is not possible: $this and $other")
|
||||
return this.minus(other)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the duration elapsed between the [other] time mark obtained from the same [TimeSource.Monotonic] time source and `this` time mark.
|
||||
*
|
||||
* The returned duration can be infinite if the time marks are far away from each other and
|
||||
* the result doesn't fit into [Duration] type,
|
||||
* or if one time mark is infinitely distant, or if both `this` and [other] time marks
|
||||
* lie infinitely distant on the opposite sides of the time scale.
|
||||
*
|
||||
* Two infinitely distant time marks on the same side of the time scale are considered equal and
|
||||
* the duration between them is [Duration.ZERO].
|
||||
*/
|
||||
public operator fun minus(other: ValueTimeMark): Duration = MonotonicTimeSource.differenceBetween(this, other)
|
||||
|
||||
/**
|
||||
* Compares this time mark with the [other] time mark for order.
|
||||
*
|
||||
* - Returns zero if this time mark represents *the same moment* of time as the [other] time mark.
|
||||
* - Returns a negative number if this time mark is *earlier* than the [other] time mark.
|
||||
* - Returns a positive number if this time mark is *later* than the [other] time mark.
|
||||
*/
|
||||
public operator fun compareTo(other: ValueTimeMark): Int =
|
||||
(this - other).compareTo(Duration.ZERO)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +139,7 @@ public interface TimeMark {
|
||||
* @throws IllegalArgumentException an implementation may throw if a positive infinite duration is added to an infinitely distant past time mark or
|
||||
* a negative infinite duration is added to an infinitely distant future time mark.
|
||||
*/
|
||||
public open operator fun plus(duration: Duration): TimeMark = AdjustedTimeMark(this, duration)
|
||||
public operator fun plus(duration: Duration): TimeMark = AdjustedTimeMark(this, duration)
|
||||
|
||||
/**
|
||||
* Returns a time mark on the same time source that is behind this time mark by the specified [duration].
|
||||
@@ -131,26 +172,53 @@ public interface TimeMark {
|
||||
public fun hasNotPassedNow(): Boolean = elapsedNow().isNegative()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A [TimeMark] that can be compared for difference with other time marks obtained from the same [TimeSource.WithComparableMarks] time source.
|
||||
*/
|
||||
@SinceKotlin("1.8")
|
||||
@ExperimentalTime
|
||||
@SinceKotlin("1.3")
|
||||
@kotlin.internal.InlineOnly
|
||||
@Deprecated(
|
||||
"Subtracting one TimeMark from another is not a well defined operation because these time marks could have been obtained from the different time sources.",
|
||||
level = DeprecationLevel.ERROR
|
||||
)
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
public inline operator fun TimeMark.minus(other: TimeMark): Duration = throw Error("Operation is disallowed.")
|
||||
public interface ComparableTimeMark : TimeMark, Comparable<ComparableTimeMark> {
|
||||
public abstract override operator fun plus(duration: Duration): ComparableTimeMark
|
||||
public open override operator fun minus(duration: Duration): ComparableTimeMark = plus(-duration)
|
||||
|
||||
@ExperimentalTime
|
||||
@SinceKotlin("1.3")
|
||||
@kotlin.internal.InlineOnly
|
||||
@Deprecated(
|
||||
"Comparing one TimeMark to another is not a well defined operation because these time marks could have been obtained from the different time sources.",
|
||||
level = DeprecationLevel.ERROR
|
||||
)
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
public inline operator fun TimeMark.compareTo(other: TimeMark): Int = throw Error("Operation is disallowed.")
|
||||
/**
|
||||
* Returns the duration elapsed between the [other] time mark and `this` time mark.
|
||||
*
|
||||
* The returned duration can be infinite if the time marks are far away from each other and
|
||||
* the result doesn't fit into [Duration] type,
|
||||
* or if one time mark is infinitely distant, or if both `this` and [other] time marks
|
||||
* lie infinitely distant on the opposite sides of the time scale.
|
||||
*
|
||||
* Two infinitely distant time marks on the same side of the time scale are considered equal and
|
||||
* the duration between them is [Duration.ZERO].
|
||||
*
|
||||
* Note that the other time mark must be obtained from the same time source as this one.
|
||||
*
|
||||
* @throws IllegalArgumentException if time marks were obtained from different time sources.
|
||||
*/
|
||||
public operator fun minus(other: ComparableTimeMark): Duration
|
||||
|
||||
/**
|
||||
* Compares this time mark with the [other] time mark for order.
|
||||
*
|
||||
* - Returns zero if this time mark represents *the same moment* of time as the [other] time mark.
|
||||
* - Returns a negative number if this time mark is *earlier* than the [other] time mark.
|
||||
* - Returns a positive number if this time mark is *later* than the [other] time mark.
|
||||
*
|
||||
* Note that the other time mark must be obtained from the same time source as this one.
|
||||
*
|
||||
* @throws IllegalArgumentException if time marks were obtained from different time sources.
|
||||
*/
|
||||
public override operator fun compareTo(other: ComparableTimeMark): Int =
|
||||
(this - other).compareTo(Duration.ZERO)
|
||||
|
||||
/**
|
||||
* Returns `true` if two time marks from the same time source represent the same moment of time, and `false` otherwise,
|
||||
* including the situation when the time marks were obtained from different time sources.
|
||||
*/
|
||||
override fun equals(other: Any?): Boolean
|
||||
override fun hashCode(): Int
|
||||
}
|
||||
|
||||
|
||||
@ExperimentalTime
|
||||
|
||||
@@ -5,11 +5,16 @@
|
||||
|
||||
package kotlin.time
|
||||
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.nanoseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalTime
|
||||
internal expect object MonotonicTimeSource : TimeSource {
|
||||
internal expect object MonotonicTimeSource : TimeSource.WithComparableMarks {
|
||||
override fun markNow(): TimeSource.Monotonic.ValueTimeMark
|
||||
fun elapsedFrom(timeMark: TimeSource.Monotonic.ValueTimeMark): Duration
|
||||
fun differenceBetween(one: TimeSource.Monotonic.ValueTimeMark, another: TimeSource.Monotonic.ValueTimeMark): Duration
|
||||
fun adjustReading(timeMark: TimeSource.Monotonic.ValueTimeMark, duration: Duration): TimeSource.Monotonic.ValueTimeMark
|
||||
}
|
||||
|
||||
@@ -20,19 +25,62 @@ internal expect object MonotonicTimeSource : TimeSource {
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalTime
|
||||
public abstract class AbstractLongTimeSource(protected val unit: DurationUnit) : TimeSource {
|
||||
public abstract class AbstractLongTimeSource(protected val unit: DurationUnit) : TimeSource.WithComparableMarks {
|
||||
/**
|
||||
* This protected method should be overridden to return the current reading of the time source expressed as a [Long] number
|
||||
* in the unit specified by the [unit] property.
|
||||
*/
|
||||
protected abstract fun read(): Long
|
||||
|
||||
private class LongTimeMark(private val startedAt: Long, private val timeSource: AbstractLongTimeSource, private val offset: Duration) : TimeMark {
|
||||
override fun elapsedNow(): Duration = (timeSource.read() - startedAt).toDuration(timeSource.unit) - offset
|
||||
override fun plus(duration: Duration): TimeMark = LongTimeMark(startedAt, timeSource, offset + duration)
|
||||
private class LongTimeMark(private val startedAt: Long, private val timeSource: AbstractLongTimeSource, private val offset: Duration) : ComparableTimeMark {
|
||||
override fun elapsedNow(): Duration = if (offset.isInfinite()) -offset else (timeSource.read() - startedAt).toDuration(timeSource.unit) - offset
|
||||
override fun plus(duration: Duration): ComparableTimeMark = LongTimeMark(startedAt, timeSource, offset + duration)
|
||||
override fun minus(other: ComparableTimeMark): Duration {
|
||||
if (other !is LongTimeMark || this.timeSource != other.timeSource)
|
||||
throw IllegalArgumentException("Subtracting or comparing time marks from different time sources is not possible: $this and $other")
|
||||
|
||||
// val thisValue = this.effectiveDuration()
|
||||
// val otherValue = other.effectiveDuration()
|
||||
// if (thisValue == otherValue && thisValue.isInfinite()) return Duration.ZERO
|
||||
// return thisValue - otherValue
|
||||
if (this.offset == other.offset && this.offset.isInfinite()) return Duration.ZERO
|
||||
val offsetDiff = this.offset - other.offset
|
||||
val startedAtDiff = (this.startedAt - other.startedAt).toDuration(timeSource.unit)
|
||||
// println("$startedAtDiff, $offsetDiff")
|
||||
return if (startedAtDiff == -offsetDiff) Duration.ZERO else startedAtDiff + offsetDiff
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is LongTimeMark && this.timeSource == other.timeSource && (this - other) == Duration.ZERO
|
||||
|
||||
internal fun effectiveDuration(): Duration {
|
||||
if (offset.isInfinite()) return offset
|
||||
val unit = timeSource.unit
|
||||
if (unit >= DurationUnit.MILLISECONDS) {
|
||||
return startedAt.toDuration(unit) + offset
|
||||
}
|
||||
val scale = convertDurationUnit(1L, DurationUnit.MILLISECONDS, unit)
|
||||
val startedAtMillis = startedAt / scale
|
||||
val startedAtRem = startedAt % scale
|
||||
|
||||
return offset.toComponents { offsetSeconds, offsetNanoseconds ->
|
||||
val offsetMillis = offsetNanoseconds / NANOS_IN_MILLIS
|
||||
val offsetRemNanos = offsetNanoseconds % NANOS_IN_MILLIS
|
||||
|
||||
// add component-wise
|
||||
(startedAtRem.toDuration(unit) + offsetRemNanos.nanoseconds) +
|
||||
(startedAtMillis + offsetMillis).milliseconds +
|
||||
offsetSeconds.seconds
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = effectiveDuration().hashCode()
|
||||
|
||||
override fun toString(): String = "LongTimeMark($startedAt${timeSource.unit.shortName()} + $offset (=${effectiveDuration()}), $timeSource)"
|
||||
}
|
||||
|
||||
override fun markNow(): TimeMark = LongTimeMark(read(), this, Duration.ZERO)
|
||||
override fun markNow(): ComparableTimeMark = LongTimeMark(read(), this, Duration.ZERO)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,19 +90,39 @@ public abstract class AbstractLongTimeSource(protected val unit: DurationUnit) :
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalTime
|
||||
public abstract class AbstractDoubleTimeSource(protected val unit: DurationUnit) : TimeSource {
|
||||
public abstract class AbstractDoubleTimeSource(protected val unit: DurationUnit) : TimeSource.WithComparableMarks {
|
||||
/**
|
||||
* This protected method should be overridden to return the current reading of the time source expressed as a [Double] number
|
||||
* in the unit specified by the [unit] property.
|
||||
*/
|
||||
protected abstract fun read(): Double
|
||||
|
||||
private class DoubleTimeMark(private val startedAt: Double, private val timeSource: AbstractDoubleTimeSource, private val offset: Duration) : TimeMark {
|
||||
private class DoubleTimeMark(private val startedAt: Double, private val timeSource: AbstractDoubleTimeSource, private val offset: Duration) : ComparableTimeMark {
|
||||
override fun elapsedNow(): Duration = (timeSource.read() - startedAt).toDuration(timeSource.unit) - offset
|
||||
override fun plus(duration: Duration): TimeMark = DoubleTimeMark(startedAt, timeSource, offset + duration)
|
||||
override fun plus(duration: Duration): ComparableTimeMark = DoubleTimeMark(startedAt, timeSource, offset + duration)
|
||||
|
||||
override fun minus(other: ComparableTimeMark): Duration {
|
||||
if (other !is DoubleTimeMark || this.timeSource != other.timeSource)
|
||||
throw IllegalArgumentException("Subtracting or comparing time marks from different time sources is not possible: $this and $other")
|
||||
|
||||
if (this.offset == other.offset && this.offset.isInfinite()) return Duration.ZERO
|
||||
val offsetDiff = this.offset - other.offset
|
||||
val startedAtDiff = (this.startedAt - other.startedAt).toDuration(timeSource.unit)
|
||||
return if (startedAtDiff == -offsetDiff) Duration.ZERO else startedAtDiff + offsetDiff
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is DoubleTimeMark && this.timeSource == other.timeSource && (this - other) == Duration.ZERO
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return (startedAt.toDuration(timeSource.unit) + offset).hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String = "DoubleTimeMark($startedAt${timeSource.unit.shortName()} + $offset, $timeSource)"
|
||||
}
|
||||
|
||||
override fun markNow(): TimeMark = DoubleTimeMark(read(), this, Duration.ZERO)
|
||||
override fun markNow(): ComparableTimeMark = DoubleTimeMark(read(), this, Duration.ZERO)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,6 +172,6 @@ public class TestTimeSource : AbstractLongTimeSource(unit = DurationUnit.NANOSEC
|
||||
}
|
||||
|
||||
private fun overflow(duration: Duration) {
|
||||
throw IllegalStateException("TestTimeSource will overflow if its reading ${reading}ns is advanced by $duration.")
|
||||
throw IllegalStateException("TestTimeSource will overflow if its reading ${reading}${unit.shortName()} is advanced by $duration.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,10 +45,25 @@ internal fun saturatingDiff(valueNs: Long, originNs: Long): Duration {
|
||||
if (originNs.isSaturated()) { // MIN_VALUE or MAX_VALUE
|
||||
return -(originNs.toDuration(DurationUnit.DAYS)) // saturate to infinity
|
||||
}
|
||||
val result = valueNs - originNs
|
||||
if ((result xor valueNs) and (result xor originNs).inv() < 0) {
|
||||
val resultMs = valueNs / NANOS_IN_MILLIS - originNs / NANOS_IN_MILLIS
|
||||
val resultNs = valueNs % NANOS_IN_MILLIS - originNs % NANOS_IN_MILLIS
|
||||
return saturatingFiniteDiff(valueNs, originNs)
|
||||
}
|
||||
|
||||
internal fun saturatingOriginsDiff(origin1Ns: Long, origin2Ns: Long): Duration {
|
||||
if (origin2Ns.isSaturated()) { // MIN_VALUE or MAX_VALUE
|
||||
if (origin1Ns == origin2Ns) return Duration.ZERO // saturated values of the same sign are considered equal
|
||||
return -(origin2Ns.toDuration(DurationUnit.DAYS)) // saturate to infinity
|
||||
}
|
||||
if (origin1Ns.isSaturated()) {
|
||||
return origin1Ns.toDuration(DurationUnit.DAYS)
|
||||
}
|
||||
return saturatingFiniteDiff(origin1Ns, origin2Ns)
|
||||
}
|
||||
|
||||
private fun saturatingFiniteDiff(value1Ns: Long, value2Ns: Long): Duration {
|
||||
val result = value1Ns - value2Ns
|
||||
if ((result xor value1Ns) and (result xor value2Ns).inv() < 0) {
|
||||
val resultMs = value1Ns / NANOS_IN_MILLIS - value2Ns / NANOS_IN_MILLIS
|
||||
val resultNs = value1Ns % NANOS_IN_MILLIS - value2Ns % NANOS_IN_MILLIS
|
||||
return resultMs.milliseconds + resultNs.nanoseconds
|
||||
}
|
||||
return result.nanoseconds
|
||||
|
||||
@@ -12,6 +12,7 @@ import kotlin.time.Duration.Companion.microseconds
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.nanoseconds
|
||||
|
||||
@OptIn(ExperimentalTime::class)
|
||||
class TimeMarkTest {
|
||||
|
||||
@Test
|
||||
@@ -32,12 +33,19 @@ class TimeMarkTest {
|
||||
val mark = timeSource.markNow()
|
||||
val markFuture1 = (mark + 1.milliseconds).apply { assertHasPassed(false) }
|
||||
val markFuture2 = (mark - (-1).milliseconds).apply { assertHasPassed(false) }
|
||||
assertTrue(markFuture1 > mark)
|
||||
assertTrue(markFuture2 > mark)
|
||||
|
||||
val markPast1 = (mark - 1.milliseconds).apply { assertHasPassed(true) }
|
||||
val markPast2 = (markFuture1 + (-2).milliseconds).apply { assertHasPassed(true) }
|
||||
assertTrue(markPast1 < mark)
|
||||
assertTrue(markPast2 < mark)
|
||||
|
||||
timeSource += 500_000.nanoseconds
|
||||
|
||||
val markElapsed = timeSource.markNow()
|
||||
val elapsedDiff = markElapsed - mark
|
||||
|
||||
val elapsed = mark.elapsedNow()
|
||||
val elapsedFromFuture = elapsed - 1.milliseconds
|
||||
val elapsedFromPast = elapsed + 1.milliseconds
|
||||
@@ -45,6 +53,10 @@ class TimeMarkTest {
|
||||
assertEquals(0.5.milliseconds, elapsed)
|
||||
assertEquals(elapsedFromFuture, markFuture1.elapsedNow())
|
||||
assertEquals(elapsedFromFuture, markFuture2.elapsedNow())
|
||||
assertEquals(elapsedDiff, elapsed)
|
||||
|
||||
val markToElapsed = mark + elapsedDiff
|
||||
assertEqualMarks(markElapsed, markToElapsed)
|
||||
|
||||
assertEquals(elapsedFromPast, markPast1.elapsedNow())
|
||||
assertEquals(elapsedFromPast, markPast2.elapsedNow())
|
||||
@@ -59,11 +71,21 @@ class TimeMarkTest {
|
||||
|
||||
}
|
||||
|
||||
fun testAdjustmentInfinite(timeSource: TimeSource) {
|
||||
fun testAdjustmentInfinite(timeSource: TimeSource.WithComparableMarks) {
|
||||
val baseMark = timeSource.markNow()
|
||||
val infiniteFutureMark = baseMark + Duration.INFINITE
|
||||
val infinitePastMark = baseMark - Duration.INFINITE
|
||||
|
||||
assertTrue(infinitePastMark < baseMark)
|
||||
assertTrue(baseMark < infiniteFutureMark)
|
||||
assertTrue(infinitePastMark < infiniteFutureMark)
|
||||
|
||||
assertEquals(Duration.INFINITE, infiniteFutureMark - infinitePastMark)
|
||||
assertEquals(Duration.INFINITE, infiniteFutureMark - baseMark)
|
||||
assertEquals(-Duration.INFINITE, infinitePastMark - baseMark)
|
||||
assertEqualMarks(infiniteFutureMark, infiniteFutureMark)
|
||||
assertEqualMarks(infinitePastMark, infinitePastMark)
|
||||
|
||||
assertEquals(-Duration.INFINITE, infiniteFutureMark.elapsedNow())
|
||||
assertTrue(infiniteFutureMark.hasNotPassedNow())
|
||||
|
||||
@@ -81,9 +103,15 @@ class TimeMarkTest {
|
||||
val futureMark = pastMark + long2Duration
|
||||
val sameMark = futureMark - (long2Duration - longDuration)
|
||||
|
||||
val elapsedMark = timeSource.markNow()
|
||||
val elapsedDiff = (sameMark.elapsedNow() - baseMark.elapsedNow()).absoluteValue
|
||||
val elapsedDiff2 = (baseMark.elapsedNow() - sameMark.elapsedNow()).absoluteValue
|
||||
assertTrue(maxOf(elapsedDiff, elapsedDiff2) < 1.milliseconds, "$elapsedDiff, $elapsedDiff2")
|
||||
// TODO: doesn't pass exactly for double-based value time marks in JS/WASM due to rounding
|
||||
// assertEquals(elapsedMark - baseMark, elapsedMark - sameMark, "$elapsedMark; $baseMark; $sameMark")
|
||||
val elapsedBaseDiff = elapsedMark - baseMark
|
||||
val elapsedSameDiff = elapsedMark - sameMark
|
||||
assertTrue((elapsedBaseDiff - elapsedSameDiff).absoluteValue < 1.milliseconds, "elapsedMark=$elapsedMark; baseMark=$baseMark; sameMark=$sameMark")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,15 +119,19 @@ class TimeMarkTest {
|
||||
testAdjustmentInfinite(TestTimeSource())
|
||||
}
|
||||
|
||||
fun testLongDisplacement(timeSource: TimeSource, wait: (Duration) -> Unit) {
|
||||
fun testLongDisplacement(timeSource: TimeSource.WithComparableMarks, wait: (Duration) -> Unit) {
|
||||
val baseMark = timeSource.markNow()
|
||||
val longDuration = Long.MAX_VALUE.nanoseconds
|
||||
val waitDuration = 20.milliseconds
|
||||
val pastMark = baseMark - longDuration
|
||||
wait(waitDuration)
|
||||
val elapsedMark = timeSource.markNow()
|
||||
val elapsed = pastMark.elapsedNow()
|
||||
val elapsedDiff = elapsedMark - pastMark
|
||||
assertTrue(elapsed > longDuration)
|
||||
assertTrue(elapsed >= longDuration + waitDuration, "$elapsed, $longDuration, $waitDuration")
|
||||
assertTrue(elapsedDiff >= longDuration + waitDuration)
|
||||
assertTrue(elapsed >= elapsedDiff)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -108,6 +140,137 @@ class TimeMarkTest {
|
||||
testLongDisplacement(timeSource, { waitDuration -> timeSource += waitDuration })
|
||||
}
|
||||
|
||||
private fun assertEqualMarks(mark1: ComparableTimeMark, mark2: ComparableTimeMark) {
|
||||
assertEquals(Duration.ZERO, mark1 - mark2)
|
||||
assertEquals(Duration.ZERO, mark2 - mark1)
|
||||
assertEquals(0, mark1 compareTo mark2)
|
||||
assertEquals(0, mark2 compareTo mark1)
|
||||
assertEquals(mark1, mark2)
|
||||
assertEquals(mark1.hashCode(), mark2.hashCode(), "hashCodes of: $mark1, $mark2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun timeMarkDifferenceAndComparison() {
|
||||
val timeSource = TestTimeSource()
|
||||
val timeSource2 = TestTimeSource()
|
||||
val baseMark = timeSource.markNow()
|
||||
|
||||
var markBefore = baseMark
|
||||
markBefore -= 100.microseconds
|
||||
markBefore -= 100.microseconds
|
||||
|
||||
val markAfter = baseMark + 100.microseconds
|
||||
|
||||
assertEquals(300.microseconds,markAfter - markBefore)
|
||||
assertTrue(markBefore < markAfter)
|
||||
assertFalse(markBefore > markAfter)
|
||||
assertEqualMarks(baseMark, baseMark)
|
||||
|
||||
timeSource += 100.microseconds
|
||||
val markElapsed = timeSource.markNow()
|
||||
assertEqualMarks(markElapsed, markAfter)
|
||||
|
||||
val differentSourceMark = TimeSource.Monotonic.markNow()
|
||||
assertFailsWith<IllegalArgumentException> { baseMark - differentSourceMark }
|
||||
assertFailsWith<IllegalArgumentException> { baseMark < differentSourceMark }
|
||||
|
||||
val differentSourceMark2 = timeSource2.markNow()
|
||||
assertFailsWith<IllegalArgumentException> { baseMark - differentSourceMark2 }
|
||||
assertFailsWith<IllegalArgumentException> { baseMark < differentSourceMark2 }
|
||||
}
|
||||
|
||||
private class LongTimeSource(unit: DurationUnit) : AbstractLongTimeSource(unit) {
|
||||
var reading: Long = 0L
|
||||
override fun read(): Long = reading
|
||||
}
|
||||
|
||||
private class DoubleTimeSource(unit: DurationUnit) : AbstractDoubleTimeSource(unit) {
|
||||
var reading: Double = 0.0
|
||||
override fun read(): Double = reading
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun longTimeMarkInfinities() {
|
||||
val timeSource = LongTimeSource(unit = DurationUnit.MILLISECONDS).apply { reading = Long.MIN_VALUE + 1 }
|
||||
|
||||
val mark1 = timeSource.markNow()
|
||||
timeSource.reading = 0
|
||||
val mark2 = timeSource.markNow() - Duration.INFINITE
|
||||
assertEquals(Duration.INFINITE, mark1.elapsedNow())
|
||||
assertEquals(Duration.INFINITE, mark2.elapsedNow())
|
||||
assertEqualMarks(mark1, mark2)
|
||||
|
||||
val mark3 = mark1 + Duration.INFINITE
|
||||
assertEquals(-Duration.INFINITE, mark3.elapsedNow(), "infinite offset should override distant past reading")
|
||||
val mark4 = timeSource.markNow() + Duration.INFINITE
|
||||
assertEquals(-Duration.INFINITE, mark4.elapsedNow())
|
||||
assertEqualMarks(mark3, mark4) // different readings, same infinite offset
|
||||
}
|
||||
|
||||
@Test
|
||||
fun doubleTimeMarkInfiniteEqualHashCode() {
|
||||
val timeSource = DoubleTimeSource(unit = DurationUnit.MILLISECONDS).apply { reading = -Double.MAX_VALUE }
|
||||
|
||||
val mark1 = timeSource.markNow()
|
||||
timeSource.reading = 0.0
|
||||
val mark2 = timeSource.markNow() - Duration.INFINITE
|
||||
assertEquals(Duration.INFINITE, mark1.elapsedNow())
|
||||
assertEquals(Duration.INFINITE, mark2.elapsedNow())
|
||||
assertEqualMarks(mark1, mark2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun longTimeMarkRoundingEqualHashCode() {
|
||||
// TODO: small reading, small offset
|
||||
run {
|
||||
val step = Long.MAX_VALUE / 4
|
||||
val timeSource = LongTimeSource(DurationUnit.NANOSECONDS)
|
||||
val mark0 = timeSource.markNow() + (step * 2).nanoseconds
|
||||
timeSource.reading += step
|
||||
val mark1 = timeSource.markNow() + step.nanoseconds
|
||||
timeSource.reading += step
|
||||
val mark2 = timeSource.markNow()
|
||||
assertEqualMarks(mark1, mark2)
|
||||
assertEqualMarks(mark0, mark2)
|
||||
// assertEqualMarks(mark0, mark1) // doesn't pass
|
||||
}
|
||||
|
||||
|
||||
// TODO: small reading, large offset
|
||||
|
||||
val unit = DurationUnit.MICROSECONDS
|
||||
val baseReading = Long.MAX_VALUE - 1000
|
||||
val timeSource = LongTimeSource(unit).apply { reading = baseReading }
|
||||
// large reading, small offset
|
||||
val baseMark = timeSource.markNow()
|
||||
for (delta in listOf((1..<500).random(), (500..<1000).random())) {
|
||||
val deltaDuration = delta.toDuration(unit)
|
||||
timeSource.reading = baseReading + delta
|
||||
val mark1e = timeSource.markNow()
|
||||
assertEquals(deltaDuration, mark1e - baseMark)
|
||||
val mark1d = baseMark + deltaDuration
|
||||
assertEqualMarks(mark1e, mark1d)
|
||||
}
|
||||
|
||||
// large reading, large offset
|
||||
run {
|
||||
val delta = 1000
|
||||
val deltaDuration = delta.toDuration(unit)
|
||||
timeSource.reading = baseReading + 1000
|
||||
val offset = Long.MAX_VALUE.nanoseconds
|
||||
val mark2 = timeSource.markNow()
|
||||
assertEquals(deltaDuration, mark2 - baseMark)
|
||||
val mark2e = mark2 + offset
|
||||
val mark2d = baseMark + offset + deltaDuration
|
||||
assertEqualMarks(mark2e, mark2d)
|
||||
}
|
||||
|
||||
// TODO: small offset, large offset
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
fun defaultTimeMarkAdjustment() {
|
||||
val baseMark = TimeSource.Monotonic.markNow()
|
||||
@@ -120,11 +283,16 @@ class TimeMarkTest {
|
||||
|
||||
MeasureTimeTest.longRunningCalc()
|
||||
|
||||
val elapsedMark = TimeSource.Monotonic.markNow()
|
||||
val elapsedDiff = elapsedMark - baseMark
|
||||
assertTrue(elapsedDiff > Duration.ZERO)
|
||||
|
||||
val elapsedAfter = markAfter.elapsedNow()
|
||||
val elapsedBase = baseMark.elapsedNow()
|
||||
val elapsedBefore = markBefore.elapsedNow()
|
||||
assertTrue(elapsedBefore >= elapsedBase + 200.microseconds)
|
||||
assertTrue(elapsedAfter <= elapsedBase - 100.microseconds)
|
||||
assertTrue(elapsedBase >= elapsedDiff)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -155,5 +323,32 @@ class TimeMarkTest {
|
||||
val elapsedDiff = (sameMark.elapsedNow() - baseMark.elapsedNow()).absoluteValue
|
||||
val elapsedDiff2 = (baseMark.elapsedNow() - sameMark.elapsedNow()).absoluteValue
|
||||
assertTrue(maxOf(elapsedDiff, elapsedDiff2) < 1.milliseconds, "$elapsedDiff, $elapsedDiff2")
|
||||
|
||||
val elapsedMark = TimeSource.Monotonic.markNow()
|
||||
val elapsedBaseDiff = elapsedMark - baseMark
|
||||
val elapsedSameDiff = elapsedMark - sameMark
|
||||
assertTrue((elapsedBaseDiff - elapsedSameDiff).absoluteValue < 1.milliseconds, "elapsedMark=$elapsedMark; baseMark=$baseMark; sameMark=$sameMark")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun defaultTimeMarkDifferenceAndComparison() {
|
||||
val baseMark = TimeSource.Monotonic.markNow()
|
||||
|
||||
var markBefore = baseMark
|
||||
markBefore -= 100.microseconds
|
||||
markBefore -= 100.microseconds
|
||||
|
||||
val markAfter = baseMark + 100.microseconds
|
||||
|
||||
assertEquals(300.microseconds,markAfter - markBefore)
|
||||
assertTrue(markBefore < markAfter)
|
||||
assertFalse(markBefore > markAfter)
|
||||
assertEquals(0,baseMark compareTo baseMark)
|
||||
assertEquals(baseMark as Any, baseMark as Any)
|
||||
assertEquals(baseMark.hashCode(), baseMark.hashCode())
|
||||
|
||||
val differentSourceMark = TestTimeSource().markNow()
|
||||
assertFailsWith<IllegalArgumentException> { baseMark - differentSourceMark }
|
||||
assertFailsWith<IllegalArgumentException> { baseMark < differentSourceMark }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ private external fun dateNow(): Double
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalTime
|
||||
internal actual object MonotonicTimeSource : TimeSource {
|
||||
internal actual object MonotonicTimeSource : TimeSource.WithComparableMarks {
|
||||
private val performance: ExternalInterfaceType? = tryGetPerformance()
|
||||
|
||||
private fun read(): Double =
|
||||
@@ -31,6 +31,12 @@ internal actual object MonotonicTimeSource : TimeSource {
|
||||
actual fun adjustReading(timeMark: ValueTimeMark, duration: Duration): ValueTimeMark =
|
||||
ValueTimeMark(sumCheckNaN(timeMark.reading as Double + duration.toDouble(DurationUnit.MILLISECONDS)))
|
||||
|
||||
actual fun differenceBetween(one: ValueTimeMark, another: ValueTimeMark): Duration {
|
||||
val ms1 = one.reading as Double
|
||||
val ms2 = another.reading as Double
|
||||
return if (ms1 == ms2) Duration.ZERO else (ms1 - ms2).milliseconds
|
||||
}
|
||||
|
||||
override fun toString(): String =
|
||||
if (performance != null) "TimeSource(globalThis.performance.now())" else "TimeSource(Date.now())"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user