Skip to content

Commit 271cf66

Browse files
committed
ref(time): Collapse the clocks into a single MonotonicClock (JAVA-571)
Two interfaces existed so that a call site could name which suspend behaviour it needed, but only ANR detection wanted the one that excludes deep sleep, and neither ANR path can be fooled by a suspend: ANRWatchDog reports only once ActivityManager confirms NOT_RESPONDING, and AnrProfilingIntegration parks its thread while backgrounded and resets its baseline on wake. That leaves one guarantee worth naming. Ticker, UptimeClock and JavaUptimeClock are gone, and ElapsedRealtimeClock becomes MonotonicClock, backed by elapsedRealtimeNanos() on Android and nanoTime() on the JVM.
1 parent 5c664f3 commit 271cf66

17 files changed

Lines changed: 80 additions & 153 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
### Internal
66

7-
- Add internal `UptimeClock` and `ElapsedRealtimeClock` abstractions with `Deadline` and `Stopwatch` primitives ([#6028](https://github.com/getsentry/sentry-java/pull/6028))
7+
- Add an internal `MonotonicClock` abstraction with `Deadline` and `Stopwatch` primitives ([#6028](https://github.com/getsentry/sentry-java/pull/6028))
88

99
## 8.55.0
1010

sentry-android-core/api/sentry-android-core.api

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,8 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
413413
public fun getBeforeScreenshotCaptureCallback ()Lio/sentry/android/core/SentryAndroidOptions$BeforeCaptureCallback;
414414
public fun getBeforeViewHierarchyCaptureCallback ()Lio/sentry/android/core/SentryAndroidOptions$BeforeCaptureCallback;
415415
public fun getDebugImagesLoader ()Lio/sentry/android/core/IDebugImagesLoader;
416-
public fun getElapsedRealtimeClock ()Lio/sentry/time/ElapsedRealtimeClock;
417416
public fun getFrameMetricsCollector ()Lio/sentry/android/core/internal/util/SentryFrameMetricsCollector;
417+
public fun getMonotonicClock ()Lio/sentry/time/MonotonicClock;
418418
public fun getNativeSdkName ()Ljava/lang/String;
419419
public fun getNdkAppHangTimeoutIntervalMillis ()J
420420
public fun getNdkHandlerStrategy ()I

sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
import io.sentry.SentryLevel;
1313
import io.sentry.SentryOptions;
1414
import io.sentry.SpanStatus;
15-
import io.sentry.android.core.internal.time.AndroidElapsedRealtimeClock;
15+
import io.sentry.android.core.internal.time.AndroidMonotonicClock;
1616
import io.sentry.android.core.internal.util.RootChecker;
1717
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
1818
import io.sentry.protocol.Mechanism;
1919
import io.sentry.protocol.SdkVersion;
2020
import io.sentry.protocol.SentryId;
21-
import io.sentry.time.ElapsedRealtimeClock;
21+
import io.sentry.time.MonotonicClock;
2222
import io.sentry.util.SampleRateUtils;
2323
import org.jetbrains.annotations.ApiStatus;
2424
import org.jetbrains.annotations.NotNull;
@@ -893,8 +893,8 @@ public void setEnableAnrFingerprinting(final boolean enableAnrFingerprinting) {
893893

894894
@Override
895895
@ApiStatus.Internal
896-
public @NotNull ElapsedRealtimeClock getElapsedRealtimeClock() {
897-
return AndroidElapsedRealtimeClock.getInstance();
896+
public @NotNull MonotonicClock getMonotonicClock() {
897+
return AndroidMonotonicClock.getInstance();
898898
}
899899

900900
static class AndroidUserFeedbackFormHandler implements SentryFeedbackOptions.IFormHandler {

sentry-android-core/src/main/java/io/sentry/android/core/internal/time/AndroidElapsedRealtimeClock.java renamed to sentry-android-core/src/main/java/io/sentry/android/core/internal/time/AndroidMonotonicClock.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
package io.sentry.android.core.internal.time;
22

33
import android.os.SystemClock;
4-
import io.sentry.time.ElapsedRealtimeClock;
4+
import io.sentry.time.MonotonicClock;
55
import org.jetbrains.annotations.ApiStatus;
66
import org.jetbrains.annotations.NotNull;
77

88
/**
9-
* {@link ElapsedRealtimeClock} backed by {@link SystemClock#elapsedRealtimeNanos()}.
9+
* {@link MonotonicClock} backed by {@link SystemClock#elapsedRealtimeNanos()}.
1010
*
1111
* <p>That is {@code CLOCK_BOOTTIME}, so it keeps counting while the device is suspended — unlike
1212
* {@link System#nanoTime()}, which the core module falls back to and which stops in deep sleep.
1313
*/
1414
@ApiStatus.Internal
15-
public final class AndroidElapsedRealtimeClock implements ElapsedRealtimeClock {
15+
public final class AndroidMonotonicClock implements MonotonicClock {
1616

17-
private static final AndroidElapsedRealtimeClock instance = new AndroidElapsedRealtimeClock();
17+
private static final AndroidMonotonicClock instance = new AndroidMonotonicClock();
1818

19-
public static @NotNull ElapsedRealtimeClock getInstance() {
19+
public static @NotNull MonotonicClock getInstance() {
2020
return instance;
2121
}
2222

23-
private AndroidElapsedRealtimeClock() {}
23+
private AndroidMonotonicClock() {}
2424

2525
@Override
2626
public long tickNanos() {

sentry-test-support/src/main/kotlin/io/sentry/time/TestTicker.kt renamed to sentry-test-support/src/main/kotlin/io/sentry/time/TestMonotonicClock.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,13 @@ package io.sentry.time
33
import java.util.concurrent.TimeUnit
44

55
/**
6-
* A [Ticker] that only moves when a test tells it to.
6+
* A [MonotonicClock] that only moves when a test tells it to.
77
*
88
* Advancing by an amount *and a unit* is the point: a stubbed `thenReturn(1001)` against a
99
* nanosecond clock is off by a factor of a million and still compiles, whereas `advance(1001,
1010
* MILLISECONDS)` cannot be.
11-
*
12-
* Implements both clock guarantees so a test can inject it wherever either is declared. Production
13-
* code must never do this — the whole purpose of the two interfaces is that one object cannot
14-
* honestly promise both.
1511
*/
16-
class TestTicker(private var nanos: Long = 0) : UptimeClock, ElapsedRealtimeClock {
12+
class TestMonotonicClock(private var nanos: Long = 0) : MonotonicClock {
1713
override fun tickNanos(): Long = nanos
1814

1915
fun advance(amount: Long, unit: TimeUnit) {

sentry/api/sentry.api

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3704,7 +3704,6 @@ public class io/sentry/SentryOptions {
37043704
public fun getDistributionController ()Lio/sentry/IDistributionApi;
37053705
public fun getDsn ()Ljava/lang/String;
37063706
public fun getEffectiveOrgId ()Ljava/lang/String;
3707-
public fun getElapsedRealtimeClock ()Lio/sentry/time/ElapsedRealtimeClock;
37083707
public fun getEnvelopeDiskCache ()Lio/sentry/cache/IEnvelopeCache;
37093708
public fun getEnvelopeReader ()Lio/sentry/IEnvelopeReader;
37103709
public fun getEnvironment ()Ljava/lang/String;
@@ -3741,6 +3740,7 @@ public class io/sentry/SentryOptions {
37413740
public fun getMaxTraceFileSize ()J
37423741
public fun getMetrics ()Lio/sentry/SentryOptions$Metrics;
37433742
public fun getModulesLoader ()Lio/sentry/internal/modules/IModulesLoader;
3743+
public fun getMonotonicClock ()Lio/sentry/time/MonotonicClock;
37443744
public fun getOnDiscard ()Lio/sentry/SentryOptions$OnDiscardCallback;
37453745
public fun getOnOversizedEvent ()Lio/sentry/SentryOptions$OnOversizedEventCallback;
37463746
public fun getOpenTelemetryMode ()Lio/sentry/SentryOpenTelemetryMode;
@@ -7599,37 +7599,26 @@ public final class io/sentry/rrweb/RRWebVideoEvent$JsonKeys {
75997599
}
76007600

76017601
public final class io/sentry/time/Deadline {
7602-
public static fun after (Lio/sentry/time/Ticker;JLjava/util/concurrent/TimeUnit;)Lio/sentry/time/Deadline;
7602+
public static fun after (Lio/sentry/time/MonotonicClock;JLjava/util/concurrent/TimeUnit;)Lio/sentry/time/Deadline;
76037603
public fun hasPassed ()Z
76047604
public fun isAfter (Lio/sentry/time/Deadline;)Z
7605-
public static fun passed (Lio/sentry/time/Ticker;)Lio/sentry/time/Deadline;
7605+
public static fun passed (Lio/sentry/time/MonotonicClock;)Lio/sentry/time/Deadline;
76067606
public fun remaining (Ljava/util/concurrent/TimeUnit;)J
76077607
}
76087608

7609-
public abstract interface class io/sentry/time/ElapsedRealtimeClock : io/sentry/time/Ticker {
7610-
}
7611-
7612-
public final class io/sentry/time/JavaElapsedRealtimeClock : io/sentry/time/ElapsedRealtimeClock {
7613-
public static fun getInstance ()Lio/sentry/time/ElapsedRealtimeClock;
7609+
public final class io/sentry/time/JavaMonotonicClock : io/sentry/time/MonotonicClock {
7610+
public static fun getInstance ()Lio/sentry/time/MonotonicClock;
76147611
public fun tickNanos ()J
76157612
}
76167613

7617-
public final class io/sentry/time/JavaUptimeClock : io/sentry/time/UptimeClock {
7618-
public static fun getInstance ()Lio/sentry/time/UptimeClock;
7619-
public fun tickNanos ()J
7614+
public abstract interface class io/sentry/time/MonotonicClock {
7615+
public abstract fun tickNanos ()J
76207616
}
76217617

76227618
public final class io/sentry/time/Stopwatch {
76237619
public fun elapsed (Ljava/util/concurrent/TimeUnit;)J
76247620
public fun elapsedNanos ()J
7625-
public static fun started (Lio/sentry/time/Ticker;)Lio/sentry/time/Stopwatch;
7626-
}
7627-
7628-
public abstract interface class io/sentry/time/Ticker {
7629-
public abstract fun tickNanos ()J
7630-
}
7631-
7632-
public abstract interface class io/sentry/time/UptimeClock : io/sentry/time/Ticker {
7621+
public static fun started (Lio/sentry/time/MonotonicClock;)Lio/sentry/time/Stopwatch;
76337622
}
76347623

76357624
public final class io/sentry/transport/AsyncHttpTransport : io/sentry/transport/ITransport {

sentry/src/main/java/io/sentry/SentryOptions.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
import io.sentry.metrics.IMetricsBatchProcessorFactory;
2222
import io.sentry.protocol.SdkVersion;
2323
import io.sentry.protocol.SentryTransaction;
24-
import io.sentry.time.ElapsedRealtimeClock;
25-
import io.sentry.time.JavaElapsedRealtimeClock;
24+
import io.sentry.time.JavaMonotonicClock;
25+
import io.sentry.time.MonotonicClock;
2626
import io.sentry.transport.ITransport;
2727
import io.sentry.transport.ITransportGate;
2828
import io.sentry.transport.NoOpEnvelopeCache;
@@ -3062,15 +3062,15 @@ public void setDateProvider(final @NotNull SentryDateProvider dateProvider) {
30623062
}
30633063

30643064
/**
3065-
* Returns the clock used to measure intervals that must include deep sleep, such as rate-limit
3066-
* windows and cache expiry.
3065+
* Returns the clock used to measure elapsed time, such as rate-limit windows, cache expiry and
3066+
* ANR thresholds.
30673067
*
30683068
* <p>Android overrides this with a {@code SystemClock.elapsedRealtimeNanos()}-backed clock, which
30693069
* this module cannot reference. On the JVM there is no suspend state to account for.
30703070
*/
30713071
@ApiStatus.Internal
3072-
public @NotNull ElapsedRealtimeClock getElapsedRealtimeClock() {
3073-
return JavaElapsedRealtimeClock.getInstance();
3072+
public @NotNull MonotonicClock getMonotonicClock() {
3073+
return JavaMonotonicClock.getInstance();
30743074
}
30753075

30763076
/**

sentry/src/main/java/io/sentry/time/Deadline.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import org.jetbrains.annotations.NotNull;
66

77
/**
8-
* A point in the future, measured on a {@link Ticker}.
8+
* A point in the future, measured on a {@link MonotonicClock}.
99
*
1010
* <p>Exists so that callers never do arithmetic on raw ticks. A tick carries no unit and no epoch,
1111
* so spelling out {@code now - then < ttl} at every call site is where unit mix-ups, sentinels that
@@ -15,17 +15,17 @@
1515
@ApiStatus.Internal
1616
public final class Deadline {
1717

18-
private final @NotNull Ticker clock;
18+
private final @NotNull MonotonicClock clock;
1919
private final long deadlineNanos;
2020

21-
private Deadline(final @NotNull Ticker clock, final long deadlineNanos) {
21+
private Deadline(final @NotNull MonotonicClock clock, final long deadlineNanos) {
2222
this.clock = clock;
2323
this.deadlineNanos = deadlineNanos;
2424
}
2525

2626
/** A deadline {@code amount} of {@code unit} from now. */
2727
public static @NotNull Deadline after(
28-
final @NotNull Ticker clock, final long amount, final @NotNull TimeUnit unit) {
28+
final @NotNull MonotonicClock clock, final long amount, final @NotNull TimeUnit unit) {
2929
return new Deadline(clock, clock.tickNanos() + unit.toNanos(amount));
3030
}
3131

@@ -34,7 +34,7 @@ private Deadline(final @NotNull Ticker clock, final long deadlineNanos) {
3434
* "never set" needs no numeric sentinel and cannot be mistaken for fresh — {@code 0} is a real
3535
* and very recent instant on any boot-relative clock.
3636
*/
37-
public static @NotNull Deadline passed(final @NotNull Ticker clock) {
37+
public static @NotNull Deadline passed(final @NotNull MonotonicClock clock) {
3838
return new Deadline(clock, clock.tickNanos());
3939
}
4040

sentry/src/main/java/io/sentry/time/ElapsedRealtimeClock.java

Lines changed: 0 additions & 16 deletions
This file was deleted.

sentry/src/main/java/io/sentry/time/JavaElapsedRealtimeClock.java

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)