Simplify Clock/ClockMark design

- Do not require initialElapsed parameter
- Introduce instead ClockMark +- Duration operators
- Do not require ClockMark to expose its originating Clock back
- Turn elapsedFrom property into elapsed() function
- Rename abstract clock classes
- Fix unit of abstract clocks when constructing them

Add top-level docs for Clocks
This commit is contained in:
Ilya Gorbunov
2019-04-15 22:27:02 +03:00
parent ca29cfb88d
commit b5d16f1a9b
5 changed files with 78 additions and 55 deletions
@@ -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 { internal external interface Process {
@@ -30,24 +30,21 @@ internal external interface Process {
internal class HrTimeClock(val process: Process) : Clock { 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() val startedAt = process.hrtime()
override val clock: Clock get() = this@HrTimeClock // delegation problem? override fun elapsed(): Duration =
override val elapsedFrom: Duration process.hrtime(startedAt).let { (seconds, nanos) -> seconds.seconds + nanos.nanoseconds }
get() = process.hrtime(startedAt).let { (seconds, nanos) -> seconds.seconds + nanos.nanoseconds + initialElapsed }
} }
override fun toString(): String = "Clock(process.hrtime())" override fun toString(): String = "Clock(process.hrtime())"
} }
internal class PerformanceClock(val performance: Performance) : DoubleReadingClock() { internal class PerformanceClock(val performance: Performance) : AbstractDoubleClock(unit = DurationUnit.MILLISECONDS) {
override val unit: DurationUnit get() = DurationUnit.MILLISECONDS override fun read(): Double = performance.now()
override fun reading(): Double = performance.now()
override fun toString(): String = "Clock(self.performance.now())" override fun toString(): String = "Clock(self.performance.now())"
} }
internal object DateNowClock : DoubleReadingClock() { internal object DateNowClock : AbstractDoubleClock(unit = DurationUnit.MILLISECONDS) {
override val unit: DurationUnit get() = DurationUnit.MILLISECONDS override fun read(): Double = kotlin.js.Date.now()
override fun reading(): Double = kotlin.js.Date.now()
override fun toString(): String = "Clock(Date.now())" override fun toString(): String = "Clock(Date.now())"
} }
@@ -5,8 +5,7 @@
package kotlin.time package kotlin.time
public actual object MonoClock : LongReadingClock(), Clock { // TODO: interface should not be required here public actual object MonoClock : AbstractLongClock(unit = DurationUnit.NANOSECONDS), Clock { // TODO: interface should not be required here
override fun reading(): Long = System.nanoTime() override fun read(): Long = System.nanoTime()
override fun toString(): String = "Clock(System.nanoTime())" override fun toString(): String = "Clock(System.nanoTime())"
override val unit: DurationUnit = DurationUnit.NANOSECONDS
} }
+38 -16
View File
@@ -5,27 +5,49 @@
package kotlin.time 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 { public interface Clock {
/** /**
* Marks a time point on this 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 fun mark(): ClockMark
companion object {
val Default: Clock get() = MonoClock
}
} }
/**
* 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 { 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)
} }
+28 -23
View File
@@ -7,49 +7,54 @@ package kotlin.time
import kotlin.js.JsName import kotlin.js.JsName
/**
* The most precise clock available in the platform, whose readings increase monotonically over time.
*/
public expect object MonoClock : Clock 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 { override fun mark(): ClockMark = object : ClockMark {
abstract fun reading(): Long val startedAt = read()
abstract val unit: DurationUnit override fun elapsed(): Duration = Duration(read() - startedAt, this@AbstractLongClock.unit)
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
} }
} }
public abstract class DoubleReadingClock : Clock { /**
abstract fun reading(): Double * An abstract class used to implement clocks that return their readings as [Double] values in the specified [unit].
abstract val unit: DurationUnit */
public abstract class AbstractDoubleClock(protected val unit: DurationUnit) : Clock {
protected abstract fun read(): Double
override fun mark(initialElapsed: Duration): ClockMark = object : ClockMark { override fun mark(): ClockMark = object : ClockMark {
val startedAt = reading() val startedAt = read()
override val clock: Clock get() = this@DoubleReadingClock override fun elapsed(): Duration = Duration(read() - startedAt, this@AbstractDoubleClock.unit)
override val elapsedFrom: Duration get() = Duration(reading() - startedAt, this@DoubleReadingClock.unit) + initialElapsed
} }
} }
/**
* A clock, whose readings can be preset and changed manually. It is useful as a predictable source of time in tests.
*/
public class TestClock( public class TestClock(
@JsName("readingValue") @JsName("readingValue")
var reading: Long = 0L, var reading: Long = 0L,
override val unit: DurationUnit = DurationUnit.NANOSECONDS unit: DurationUnit = DurationUnit.NANOSECONDS
) : LongReadingClock() { ) : AbstractLongClock(unit) {
override fun reading(): Long = reading override fun read(): Long = reading
} }
/* /*
public interface WallClock { public interface WallClock {
fun currentTimeMilliseconds(): Long fun currentTimeMilliseconds(): Long
companion object : WallClock, LongReadingClock() { companion object : WallClock, AbstractLongClock(unit = DurationUnit.MILLISECONDS) {
override fun currentTimeMilliseconds(): Long = System.currentTimeMillis() override fun currentTimeMilliseconds(): Long = System.currentTimeMillis()
override fun reading(): Long = System.currentTimeMillis() override fun read(): Long = System.currentTimeMillis()
override val unit: DurationUnit get() = DurationUnit.MILLISECONDS override fun toString(): String = "WallClock(System.currentTimeMillis())"
} }
} }
*/ */
@@ -22,7 +22,7 @@ public inline fun Clock.measureTime(action: () -> Unit): Duration {
val mark = mark() val mark = mark()
action() action()
return mark.elapsedFrom return mark.elapsed()
} }
@@ -44,5 +44,5 @@ public inline fun <T> Clock.withMeasureTime(action: () -> T): DurationMeasured<T
val mark = mark() val mark = mark()
val result = action() val result = action()
return DurationMeasured(result, mark.elapsedFrom) return DurationMeasured(result, mark.elapsed())
} }