Skip to content

Commit

Permalink
ref: Use weak references for ANRTracker listeners (#2742)
Browse files Browse the repository at this point in the history
Replace NSMutableSet with NSHashTable and use weakObjectsHashTable to
avoid retaining strong references to ANR tracker listeners.
  • Loading branch information
philipphofmann authored Mar 3, 2023
1 parent dbc67d2 commit 78d5983
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 2 deletions.
4 changes: 2 additions & 2 deletions Sources/Sentry/SentryANRTracker.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ typedef NS_ENUM(NSInteger, SentryANRTrackerState) {
@property (nonatomic, strong) SentryCrashWrapper *crashWrapper;
@property (nonatomic, strong) SentryDispatchQueueWrapper *dispatchQueueWrapper;
@property (nonatomic, strong) SentryThreadWrapper *threadWrapper;
@property (nonatomic, strong) NSMutableSet<id<SentryANRTrackerDelegate>> *listeners;
@property (nonatomic, strong) NSHashTable<id<SentryANRTrackerDelegate>> *listeners;
@property (nonatomic, assign) NSTimeInterval timeoutInterval;

@end
Expand All @@ -42,7 +42,7 @@ - (instancetype)initWithTimeoutInterval:(NSTimeInterval)timeoutInterval
self.crashWrapper = crashWrapper;
self.dispatchQueueWrapper = dispatchQueueWrapper;
self.threadWrapper = threadWrapper;
self.listeners = [NSMutableSet set];
self.listeners = [NSHashTable weakObjectsHashTable];
threadLock = [[NSObject alloc] init];
state = kSentryANRTrackerNotRunning;
}
Expand Down
5 changes: 5 additions & 0 deletions Sources/Sentry/SentryANRTrackingIntegration.m
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ - (void)uninstall
[self.tracker removeListener:self];
}

- (void)dealloc
{
[self uninstall];
}

- (void)anrDetected
{
SentryThreadInspector *threadInspector = SentrySDK.currentHub.getClient.threadInspector;
Expand Down
23 changes: 23 additions & 0 deletions Tests/SentryTests/Integrations/ANR/SentryANRTrackerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,29 @@ class SentryANRTrackerTests: XCTestCase, SentryANRTrackerDelegate {

}

func testNotRemovingDeallocatedListener_DoesNotRetainListener_AndStopsTracking() {
anrDetectedExpectation.isInverted = true
anrStoppedExpectation.isInverted = true

// So ARC deallocates SentryANRTrackerTestDelegate
let addListenersCount = 10
func addListeners() {
for _ in 0..<addListenersCount {
self.sut.addListener(SentryANRTrackerTestDelegate())
}
}
addListeners()

sut.addListener(self)
sut.removeListener(self)

let listeners = Dynamic(sut).listeners.asObject as? NSHashTable<NSObject>

XCTAssertGreaterThan(addListenersCount, listeners?.count ?? addListenersCount)

wait(for: [anrDetectedExpectation, anrStoppedExpectation], timeout: 0.0)
}

func testClearDirectlyAfterStart() {
anrDetectedExpectation.isInverted = true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,25 @@ class SentryANRTrackingIntegrationTests: SentrySDKIntegrationTestsBase {

assertNoEventCaptured()
}

func testDealloc_CallsUninstall() {
givenInitializedTracker()

// // So ARC deallocates the SentryANRTrackingIntegration
func initIntegration() {
self.crashWrapper.internalIsBeingTraced = false
let sut = SentryANRTrackingIntegration()
sut.install(with: self.options)
}

initIntegration()

let tracker = SentryDependencyContainer.sharedInstance().getANRTracker(self.options.appHangTimeoutInterval)

let listeners = Dynamic(tracker).listeners.asObject as? NSHashTable<NSObject>

XCTAssertEqual(1, listeners?.count ?? 2)
}

private func givenInitializedTracker(isBeingTraced: Bool = false) {
givenSdkWithHub()
Expand Down

0 comments on commit 78d5983

Please sign in to comment.