Skip to content

Commit e1e6946

Browse files
committed
Fix race conditions
1 parent c75be29 commit e1e6946

File tree

2 files changed

+29
-25
lines changed

2 files changed

+29
-25
lines changed

sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
import android.view.Window;
1515
import androidx.annotation.RequiresApi;
1616
import io.sentry.ILogger;
17+
import io.sentry.ISentryLifecycleToken;
1718
import io.sentry.SentryLevel;
1819
import io.sentry.SentryOptions;
1920
import io.sentry.SentryUUID;
2021
import io.sentry.android.core.BuildInfoProvider;
2122
import io.sentry.android.core.ContextUtils;
23+
import io.sentry.util.AutoClosableReentrantLock;
2224
import io.sentry.util.Objects;
2325
import java.lang.ref.WeakReference;
2426
import java.lang.reflect.Field;
@@ -38,6 +40,8 @@ public final class SentryFrameMetricsCollector implements Application.ActivityLi
3840

3941
private final @NotNull BuildInfoProvider buildInfoProvider;
4042
private final @NotNull Set<Window> trackedWindows = new CopyOnWriteArraySet<>();
43+
private final @NotNull AutoClosableReentrantLock trackedWindowsLock =
44+
new AutoClosableReentrantLock();
4145
private final @NotNull ILogger logger;
4246
private @Nullable Handler handler;
4347
private @Nullable WeakReference<Window> currentWindow;
@@ -282,23 +286,24 @@ public void stopCollection(final @Nullable String listenerId) {
282286

283287
@SuppressLint("NewApi")
284288
private void stopTrackingWindow(final @NotNull Window window) {
285-
final boolean wasTracked = trackedWindows.remove(window);
286-
if (wasTracked) {
287-
new Handler(Looper.getMainLooper())
288-
.post(
289-
() -> {
290-
try {
291-
// Re-check if we should still stop tracking this window
292-
if (!trackedWindows.contains(window)) {
293-
windowFrameMetricsManager.removeOnFrameMetricsAvailableListener(
294-
window, frameMetricsAvailableListener);
295-
}
296-
} catch (Throwable e) {
297-
logger.log(
298-
SentryLevel.ERROR, "Failed to remove frameMetricsAvailableListener", e);
289+
new Handler(Looper.getMainLooper())
290+
.post(
291+
() -> {
292+
try {
293+
// Re-check if we should still remove the listener for this window
294+
// in case trackCurrentWindow was called in the meantime
295+
final boolean shouldRemove;
296+
try (final @NotNull ISentryLifecycleToken ignored = trackedWindowsLock.acquire()) {
297+
shouldRemove = trackedWindows.contains(window) && trackedWindows.remove(window);
299298
}
300-
});
301-
}
299+
if (shouldRemove) {
300+
windowFrameMetricsManager.removeOnFrameMetricsAvailableListener(
301+
window, frameMetricsAvailableListener);
302+
}
303+
} catch (Throwable e) {
304+
logger.log(SentryLevel.ERROR, "Failed to remove frameMetricsAvailableListener", e);
305+
}
306+
});
302307
}
303308

304309
private void setCurrentWindow(final @NotNull Window window) {
@@ -316,23 +321,22 @@ private void trackCurrentWindow() {
316321
return;
317322
}
318323

319-
if (trackedWindows.contains(window)) {
320-
return;
321-
}
322-
323324
if (listenerMap.isEmpty()) {
324325
return;
325326
}
326327

327328
if (handler != null) {
328-
trackedWindows.add(window);
329329
// Ensure the addOnFrameMetricsAvailableListener is called on the main thread
330330
new Handler(Looper.getMainLooper())
331331
.post(
332332
() -> {
333333
// Re-check if we should still track this window
334-
// in case stopTrackingWindow was called in the meantime
335-
if (trackedWindows.contains(window)) {
334+
// in case stopTrackingWindow was called for the same Window in the meantime
335+
final boolean shouldAdd;
336+
try (final @NotNull ISentryLifecycleToken ignored = trackedWindowsLock.acquire()) {
337+
shouldAdd = !trackedWindows.contains(window) && trackedWindows.add(window);
338+
}
339+
if (shouldAdd) {
336340
try {
337341
windowFrameMetricsManager.addOnFrameMetricsAvailableListener(
338342
window, frameMetricsAvailableListener, handler);

sentry-android-core/src/test/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollectorTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -571,8 +571,8 @@ class SentryFrameMetricsCollectorTest {
571571
// Now execute all pending operations
572572
Shadows.shadowOf(Looper.getMainLooper()).idle()
573573

574-
assertEquals(0, fixture.addOnFrameMetricsAvailableListenerCounter)
575-
// remove will still execute as it has no clue that add bailed on the main thread
574+
// as the listeners are posted to the main thread, we expect an add followed by a remove
575+
assertEquals(1, fixture.addOnFrameMetricsAvailableListenerCounter)
576576
assertEquals(1, fixture.removeOnFrameMetricsAvailableListenerCounter)
577577
assertEquals(0, collector.getProperty<Set<Window>>("trackedWindows").size)
578578
}

0 commit comments

Comments
 (0)