diff --git a/libraries/stdlib/js/src/kotlin/time/MonoClock.kt b/libraries/stdlib/js/src/kotlin/time/MonoClock.kt index 81f9b86a6b6..99d1ea9f4c7 100644 --- a/libraries/stdlib/js/src/kotlin/time/MonoClock.kt +++ b/libraries/stdlib/js/src/kotlin/time/MonoClock.kt @@ -21,7 +21,7 @@ public actual object MonoClock : Clock { } - override fun mark(initialElapsed: Duration): ClockMark = actualClock.mark(initialElapsed) + override fun mark(): ClockMark = actualClock.mark() } internal external interface Process { @@ -30,24 +30,21 @@ internal external interface Process { internal class HrTimeClock(val process: Process) : Clock { - override fun mark(initialElapsed: Duration): ClockMark = object : ClockMark { + override fun mark(): ClockMark = object : ClockMark { val startedAt = process.hrtime() - override val clock: Clock get() = this@HrTimeClock // delegation problem? - override val elapsedFrom: Duration - get() = process.hrtime(startedAt).let { (seconds, nanos) -> seconds.seconds + nanos.nanoseconds + initialElapsed } + override fun elapsed(): Duration = + process.hrtime(startedAt).let { (seconds, nanos) -> seconds.seconds + nanos.nanoseconds } } override fun toString(): String = "Clock(process.hrtime())" } -internal class PerformanceClock(val performance: Performance) : DoubleReadingClock() { - override val unit: DurationUnit get() = DurationUnit.MILLISECONDS - override fun reading(): Double = performance.now() +internal class PerformanceClock(val performance: Performance) : AbstractDoubleClock(unit = DurationUnit.MILLISECONDS) { + override fun read(): Double = performance.now() override fun toString(): String = "Clock(self.performance.now())" } -internal object DateNowClock : DoubleReadingClock() { - override val unit: DurationUnit get() = DurationUnit.MILLISECONDS - override fun reading(): Double = kotlin.js.Date.now() +internal object DateNowClock : AbstractDoubleClock(unit = DurationUnit.MILLISECONDS) { + override fun read(): Double = kotlin.js.Date.now() override fun toString(): String = "Clock(Date.now())" } \ No newline at end of file diff --git a/libraries/stdlib/jvm/src/kotlin/time/MonoClock.kt b/libraries/stdlib/jvm/src/kotlin/time/MonoClock.kt index 502426d4682..43ae652f0ad 100644 --- a/libraries/stdlib/jvm/src/kotlin/time/MonoClock.kt +++ b/libraries/stdlib/jvm/src/kotlin/time/MonoClock.kt @@ -5,8 +5,7 @@ package kotlin.time -public actual object MonoClock : LongReadingClock(), Clock { // TODO: interface should not be required here - override fun reading(): Long = System.nanoTime() +public actual object MonoClock : AbstractLongClock(unit = DurationUnit.NANOSECONDS), Clock { // TODO: interface should not be required here + override fun read(): Long = System.nanoTime() override fun toString(): String = "Clock(System.nanoTime())" - override val unit: DurationUnit = DurationUnit.NANOSECONDS } \ No newline at end of file diff --git a/libraries/stdlib/src/kotlin/time/Clock.kt b/libraries/stdlib/src/kotlin/time/Clock.kt index 8623f76c71f..dd250dbb337 100644 --- a/libraries/stdlib/src/kotlin/time/Clock.kt +++ b/libraries/stdlib/src/kotlin/time/Clock.kt @@ -5,27 +5,49 @@ package kotlin.time +/** + * A source of time for measuring time intervals. + * + * The only operation provided by the clock is [mark]. It returns a [ClockMark], which can be used to query the elapsed time later. + * + * @see [measureTime] + * @see [withMeasureTime] + */ public interface Clock { - /** * Marks a time point on this clock. - * - * @param initialElapsed An initial value of [ClockMark.elapsedFrom] property of the resulting [ClockMark]: - * - * - pass [Duration.ZERO] to mark a time point that is now. - * - pass a positive duration to mark a time point in the past. - * - pass a negative duration to mark a time point in the future. - * */ - fun mark(initialElapsed: Duration = Duration.ZERO): ClockMark - - companion object { - val Default: Clock get() = MonoClock - } - + fun mark(): ClockMark } +/** + * Represents a time point notched on a particular [Clock]. Remains bound to the clock it was taken from + * and allows querying for the duration of time elapsed from that point (see the function [elapsed]). + */ public interface ClockMark { - val clock: Clock - val elapsedFrom: Duration + /** + * Returns the amount of time passed from this clock mark on the clock from which this mark was taken. + */ + fun elapsed(): Duration + + /** + * Returns a clock mark on the same clock that is ahead of this clock mark by the specified [duration]. + * + * The returned clock mark is more _late_ when the [duration] is positive, and more _early_ when the [duration] is negative. + */ + operator fun plus(duration: Duration): ClockMark = AdjustedClockMark(this, duration) + + /** + * Returns a clock mark on the same clock that is behind this clock mark by the specified [duration]. + * + * The returned clock mark is more _early_ when the [duration] is positive, and more _late_ when the [duration] is negative. + */ + operator fun minus(duration: Duration): ClockMark = plus(-duration) +} + + +private class AdjustedClockMark(val mark: ClockMark, val adjustment: Duration) : ClockMark { + override fun elapsed(): Duration = mark.elapsed() - adjustment + + override fun plus(duration: Duration): ClockMark = AdjustedClockMark(mark, adjustment + duration) } \ No newline at end of file diff --git a/libraries/stdlib/src/kotlin/time/Clocks.kt b/libraries/stdlib/src/kotlin/time/Clocks.kt index c5198b98793..79bf4e97d88 100644 --- a/libraries/stdlib/src/kotlin/time/Clocks.kt +++ b/libraries/stdlib/src/kotlin/time/Clocks.kt @@ -7,49 +7,54 @@ package kotlin.time import kotlin.js.JsName +/** + * The most precise clock available in the platform, whose readings increase monotonically over time. + */ public expect object MonoClock : Clock +/** + * An abstract class used to implement clocks that return their readings as [Long] values in the specified [unit]. + */ +public abstract class AbstractLongClock(protected val unit: DurationUnit) : Clock { + protected abstract fun read(): Long -public abstract class LongReadingClock : Clock { - abstract fun reading(): Long - abstract val unit: DurationUnit - - override fun mark(initialElapsed: Duration): ClockMark = object : ClockMark { - val startedAt = reading() - override val clock: Clock get() = this@LongReadingClock - override val elapsedFrom: Duration get() = Duration(reading() - startedAt, this@LongReadingClock.unit) + initialElapsed + override fun mark(): ClockMark = object : ClockMark { + val startedAt = read() + override fun elapsed(): Duration = Duration(read() - startedAt, this@AbstractLongClock.unit) } } -public abstract class DoubleReadingClock : Clock { - abstract fun reading(): Double - abstract val unit: DurationUnit +/** + * An abstract class used to implement clocks that return their readings as [Double] values in the specified [unit]. + */ +public abstract class AbstractDoubleClock(protected val unit: DurationUnit) : Clock { + protected abstract fun read(): Double - override fun mark(initialElapsed: Duration): ClockMark = object : ClockMark { - val startedAt = reading() - override val clock: Clock get() = this@DoubleReadingClock - override val elapsedFrom: Duration get() = Duration(reading() - startedAt, this@DoubleReadingClock.unit) + initialElapsed + override fun mark(): ClockMark = object : ClockMark { + val startedAt = read() + override fun elapsed(): Duration = Duration(read() - startedAt, this@AbstractDoubleClock.unit) } } - - +/** + * A clock, whose readings can be preset and changed manually. It is useful as a predictable source of time in tests. + */ public class TestClock( @JsName("readingValue") var reading: Long = 0L, - override val unit: DurationUnit = DurationUnit.NANOSECONDS -) : LongReadingClock() { - override fun reading(): Long = reading + unit: DurationUnit = DurationUnit.NANOSECONDS +) : AbstractLongClock(unit) { + override fun read(): Long = reading } /* public interface WallClock { fun currentTimeMilliseconds(): Long - companion object : WallClock, LongReadingClock() { + companion object : WallClock, AbstractLongClock(unit = DurationUnit.MILLISECONDS) { override fun currentTimeMilliseconds(): Long = System.currentTimeMillis() - override fun reading(): Long = System.currentTimeMillis() - override val unit: DurationUnit get() = DurationUnit.MILLISECONDS + override fun read(): Long = System.currentTimeMillis() + override fun toString(): String = "WallClock(System.currentTimeMillis())" } } */ \ No newline at end of file diff --git a/libraries/stdlib/src/kotlin/time/measureTime.kt b/libraries/stdlib/src/kotlin/time/measureTime.kt index 4835cde7afe..e65c7847628 100644 --- a/libraries/stdlib/src/kotlin/time/measureTime.kt +++ b/libraries/stdlib/src/kotlin/time/measureTime.kt @@ -22,7 +22,7 @@ public inline fun Clock.measureTime(action: () -> Unit): Duration { val mark = mark() action() - return mark.elapsedFrom + return mark.elapsed() } @@ -44,5 +44,5 @@ public inline fun Clock.withMeasureTime(action: () -> T): DurationMeasured