Skip to content

Commit 7dd8463

Browse files
runningcodeclaude
andcommitted
refactor(core): Hand out AutoCloseable token from re-entrancy guard
Replace the manual enter()/finally-exit() pattern at every callback site with an ISentryLifecycleToken returned from enter(), used via try-with-resources. This removes nine copies of the finally boilerplate and the risk of forgetting the exit() call. The depth counter stays as the guard's state model - the token's close() just decrements it - so the nesting correctness guarantee is unchanged. The token is a shared static singleton, so no allocation happens per callback, matching the AutoClosableReentrantLock idiom already in the codebase. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 9606aba commit 7dd8463

3 files changed

Lines changed: 24 additions & 40 deletions

File tree

sentry/src/main/java/io/sentry/Scope.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -465,8 +465,7 @@ public Queue<Breadcrumb> getBreadcrumbs() {
465465
final @NotNull SentryOptions.BeforeBreadcrumbCallback callback,
466466
@NotNull Breadcrumb breadcrumb,
467467
final @NotNull Hint hint) {
468-
try {
469-
SentryCallbackReentrancyGuard.enter();
468+
try (final @NotNull ISentryLifecycleToken ignored = SentryCallbackReentrancyGuard.enter()) {
470469
breadcrumb = callback.execute(breadcrumb, hint);
471470
} catch (Throwable e) {
472471
options
@@ -479,8 +478,6 @@ public Queue<Breadcrumb> getBreadcrumbs() {
479478
if (e.getMessage() != null) {
480479
breadcrumb.setData("sentry:message", e.getMessage());
481480
}
482-
} finally {
483-
SentryCallbackReentrancyGuard.exit();
484481
}
485482
return breadcrumb;
486483
}

sentry/src/main/java/io/sentry/SentryClient.java

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,7 @@ private boolean shouldApplyScopeData(final @NotNull CheckIn event, final @NotNul
245245
final SentryReplayOptions.BeforeErrorSamplingCallback beforeErrorSampling =
246246
options.getSessionReplay().getBeforeErrorSampling();
247247
if (beforeErrorSampling != null) {
248-
try {
249-
SentryCallbackReentrancyGuard.enter();
248+
try (final @NotNull ISentryLifecycleToken ignored = SentryCallbackReentrancyGuard.enter()) {
250249
shouldCaptureReplay = beforeErrorSampling.execute(event, hint);
251250
} catch (Throwable e) {
252251
options
@@ -256,8 +255,6 @@ private boolean shouldApplyScopeData(final @NotNull CheckIn event, final @NotNul
256255
"The beforeErrorSampling callback threw an exception. Proceeding with replay capture.",
257256
e);
258257
shouldCaptureReplay = true;
259-
} finally {
260-
SentryCallbackReentrancyGuard.exit();
261258
}
262259
}
263260
if (shouldCaptureReplay) {
@@ -961,15 +958,12 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint
961958
final @Nullable SentryOptions.BeforeEnvelopeCallback beforeEnvelopeCallback =
962959
options.getBeforeEnvelopeCallback();
963960
if (beforeEnvelopeCallback != null) {
964-
try {
965-
SentryCallbackReentrancyGuard.enter();
961+
try (final @NotNull ISentryLifecycleToken ignored = SentryCallbackReentrancyGuard.enter()) {
966962
beforeEnvelopeCallback.execute(envelope, hint);
967963
} catch (Throwable e) {
968964
options
969965
.getLogger()
970966
.log(SentryLevel.ERROR, "The BeforeEnvelope callback threw an exception.", e);
971-
} finally {
972-
SentryCallbackReentrancyGuard.exit();
973967
}
974968
}
975969

@@ -1672,8 +1666,7 @@ private void sortBreadcrumbsByDate(
16721666
@NotNull SentryEvent event, final @NotNull Hint hint) {
16731667
final SentryOptions.BeforeSendCallback beforeSend = options.getBeforeSend();
16741668
if (beforeSend != null) {
1675-
try {
1676-
SentryCallbackReentrancyGuard.enter();
1669+
try (final @NotNull ISentryLifecycleToken ignored = SentryCallbackReentrancyGuard.enter()) {
16771670
event = beforeSend.execute(event, hint);
16781671
} catch (Throwable e) {
16791672
options
@@ -1685,8 +1678,6 @@ private void sortBreadcrumbsByDate(
16851678

16861679
// drop event in case of an error in beforeSend due to PII concerns
16871680
event = null;
1688-
} finally {
1689-
SentryCallbackReentrancyGuard.exit();
16901681
}
16911682
}
16921683
return event;
@@ -1697,8 +1688,7 @@ private void sortBreadcrumbsByDate(
16971688
final SentryOptions.BeforeSendTransactionCallback beforeSendTransaction =
16981689
options.getBeforeSendTransaction();
16991690
if (beforeSendTransaction != null) {
1700-
try {
1701-
SentryCallbackReentrancyGuard.enter();
1691+
try (final @NotNull ISentryLifecycleToken ignored = SentryCallbackReentrancyGuard.enter()) {
17021692
transaction = beforeSendTransaction.execute(transaction, hint);
17031693
} catch (Throwable e) {
17041694
options
@@ -1710,8 +1700,6 @@ private void sortBreadcrumbsByDate(
17101700

17111701
// drop transaction in case of an error in beforeSend due to PII concerns
17121702
transaction = null;
1713-
} finally {
1714-
SentryCallbackReentrancyGuard.exit();
17151703
}
17161704
}
17171705
return transaction;
@@ -1721,8 +1709,7 @@ private void sortBreadcrumbsByDate(
17211709
@NotNull SentryEvent event, final @NotNull Hint hint) {
17221710
final SentryOptions.BeforeSendCallback beforeSendFeedback = options.getBeforeSendFeedback();
17231711
if (beforeSendFeedback != null) {
1724-
try {
1725-
SentryCallbackReentrancyGuard.enter();
1712+
try (final @NotNull ISentryLifecycleToken ignored = SentryCallbackReentrancyGuard.enter()) {
17261713
event = beforeSendFeedback.execute(event, hint);
17271714
} catch (Throwable e) {
17281715
options
@@ -1731,8 +1718,6 @@ private void sortBreadcrumbsByDate(
17311718

17321719
// drop feedback in case of an error in beforeSend due to PII concerns
17331720
event = null;
1734-
} finally {
1735-
SentryCallbackReentrancyGuard.exit();
17361721
}
17371722
}
17381723
return event;
@@ -1742,8 +1727,7 @@ private void sortBreadcrumbsByDate(
17421727
@NotNull SentryReplayEvent event, final @NotNull Hint hint) {
17431728
final SentryOptions.BeforeSendReplayCallback beforeSendReplay = options.getBeforeSendReplay();
17441729
if (beforeSendReplay != null) {
1745-
try {
1746-
SentryCallbackReentrancyGuard.enter();
1730+
try (final @NotNull ISentryLifecycleToken ignored = SentryCallbackReentrancyGuard.enter()) {
17471731
event = beforeSendReplay.execute(event, hint);
17481732
} catch (Throwable e) {
17491733
options
@@ -1755,8 +1739,6 @@ private void sortBreadcrumbsByDate(
17551739

17561740
// drop event in case of an error in beforeSend due to PII concerns
17571741
event = null;
1758-
} finally {
1759-
SentryCallbackReentrancyGuard.exit();
17601742
}
17611743
}
17621744
return event;
@@ -1766,8 +1748,7 @@ private void sortBreadcrumbsByDate(
17661748
final SentryOptions.Logs.BeforeSendLogCallback beforeSendLog =
17671749
options.getLogs().getBeforeSend();
17681750
if (beforeSendLog != null) {
1769-
try {
1770-
SentryCallbackReentrancyGuard.enter();
1751+
try (final @NotNull ISentryLifecycleToken ignored = SentryCallbackReentrancyGuard.enter()) {
17711752
event = beforeSendLog.execute(event);
17721753
} catch (Throwable e) {
17731754
options
@@ -1779,8 +1760,6 @@ private void sortBreadcrumbsByDate(
17791760

17801761
// drop event in case of an error in beforeSendLog due to PII concerns
17811762
event = null;
1782-
} finally {
1783-
SentryCallbackReentrancyGuard.exit();
17841763
}
17851764
}
17861765
return event;
@@ -1791,8 +1770,7 @@ private void sortBreadcrumbsByDate(
17911770
final SentryOptions.Metrics.BeforeSendMetricCallback beforeSendMetric =
17921771
options.getMetrics().getBeforeSend();
17931772
if (beforeSendMetric != null) {
1794-
try {
1795-
SentryCallbackReentrancyGuard.enter();
1773+
try (final @NotNull ISentryLifecycleToken ignored = SentryCallbackReentrancyGuard.enter()) {
17961774
event = beforeSendMetric.execute(event, hint);
17971775
} catch (Throwable e) {
17981776
options
@@ -1804,8 +1782,6 @@ private void sortBreadcrumbsByDate(
18041782

18051783
// drop event in case of an error in beforeSendMetric due to PII concerns
18061784
event = null;
1807-
} finally {
1808-
SentryCallbackReentrancyGuard.exit();
18091785
}
18101786
}
18111787
return event;

sentry/src/main/java/io/sentry/util/SentryCallbackReentrancyGuard.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.sentry.util;
22

3+
import io.sentry.ISentryLifecycleToken;
34
import org.jetbrains.annotations.ApiStatus;
5+
import org.jetbrains.annotations.NotNull;
46
import org.jetbrains.annotations.Nullable;
57

68
/**
@@ -22,12 +24,18 @@
2224
* clear it while an outer callback is still running. Capture entry points drop while a callback is
2325
* active, so callbacks should never nest — but a capture path lacking an entry check must not
2426
* silently disarm the guard for the rest of the outer callback.
27+
*
28+
* <p>{@link #enter()} returns an {@link ISentryLifecycleToken} so callers can use try-with-resources
29+
* instead of a manual {@code finally exit()}. The token is a shared singleton (its {@code close()}
30+
* just decrements the counter), so no allocation happens per callback.
2531
*/
2632
@ApiStatus.Internal
2733
public final class SentryCallbackReentrancyGuard {
2834

2935
private static final ThreadLocal<Integer> depth = new ThreadLocal<>();
3036

37+
private static final ISentryLifecycleToken TOKEN = SentryCallbackReentrancyGuard::exit;
38+
3139
private SentryCallbackReentrancyGuard() {}
3240

3341
/** Whether a user callback is currently executing on this thread. */
@@ -36,14 +44,17 @@ public static boolean isActive() {
3644
return current != null && current > 0;
3745
}
3846

39-
/** Marks that a user callback is starting to execute on this thread. */
40-
public static void enter() {
47+
/**
48+
* Marks that a user callback is starting to execute on this thread. Close the returned token (via
49+
* try-with-resources) once the callback returns.
50+
*/
51+
public static @NotNull ISentryLifecycleToken enter() {
4152
final @Nullable Integer current = depth.get();
4253
depth.set(current == null ? 1 : current + 1);
54+
return TOKEN;
4355
}
4456

45-
/** Marks that the user callback finished executing on this thread. */
46-
public static void exit() {
57+
private static void exit() {
4758
final @Nullable Integer current = depth.get();
4859
if (current == null || current <= 1) {
4960
depth.remove();

0 commit comments

Comments
 (0)