Skip to content

Commit cbf92b7

Browse files
authored
[Darwin] MTRDevice can get stuck using subscription pool if not reset properly (#36741)
* [Darwin] MTRDevice can get stuck using subscription pool if not reset properly * Restyled
1 parent fa9d005 commit cbf92b7

File tree

2 files changed

+98
-17
lines changed

2 files changed

+98
-17
lines changed

src/darwin/Framework/CHIP/MTRDevice_Concrete.mm

+20-2
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,12 @@ - (void)_ensureSubscriptionForExistingDelegates:(NSString *)reason
821821
[self->_deviceController asyncDispatchToMatterQueue:^{
822822
std::lock_guard lock(self->_lock);
823823
[self _setupSubscriptionWithReason:[NSString stringWithFormat:@"%@ and scheduled subscription is happening", reason]];
824-
} errorHandler:nil];
824+
} errorHandler:^(NSError * _Nonnull error) {
825+
// If controller is not running, clear work item from the subscription queue
826+
MTR_LOG_ERROR("%@ could not dispatch to matter queue for resubscription - error %@", self, error);
827+
std::lock_guard lock(self->_lock);
828+
[self _clearSubscriptionPoolWork];
829+
}];
825830
} inNanoseconds:0 description:@"MTRDevice setDelegate first subscription"];
826831
} else {
827832
[_deviceController asyncDispatchToMatterQueue:^{
@@ -850,6 +855,10 @@ - (void)invalidate
850855
// attempt, since we now have no delegate.
851856
_reattemptingSubscription = NO;
852857

858+
// Clear subscription pool work item if it's in progress, to avoid forever
859+
// taking up a slot in the controller's work queue.
860+
[self _clearSubscriptionPoolWork];
861+
853862
[_deviceController asyncDispatchToMatterQueue:^{
854863
MTR_LOG("%@ invalidate disconnecting ReadClient and SubscriptionCallback", self);
855864

@@ -1380,6 +1389,7 @@ - (void)_doHandleSubscriptionReset:(NSNumber * _Nullable)retryDelay
13801389

13811390
if (self.suspended) {
13821391
MTR_LOG("%@ ignoring expected subscription reset on controller suspend", self);
1392+
[self _clearSubscriptionPoolWork];
13831393
return;
13841394
}
13851395

@@ -1397,6 +1407,7 @@ - (void)_doHandleSubscriptionReset:(NSNumber * _Nullable)retryDelay
13971407
if (![self _delegateExists]) {
13981408
// NOTE: Do not log anything here: we have been invalidated, and the
13991409
// Matter stack might already be torn down.
1410+
[self _clearSubscriptionPoolWork];
14001411
return;
14011412
}
14021413

@@ -1445,7 +1456,12 @@ - (void)_doHandleSubscriptionReset:(NSNumber * _Nullable)retryDelay
14451456
std::lock_guard lock(self->_lock);
14461457
[self _reattemptSubscriptionNowIfNeededWithReason:@"got subscription reset"];
14471458
}
1448-
errorHandler:nil];
1459+
errorHandler:^(NSError * _Nonnull error) {
1460+
// If controller is not running, clear work item from the subscription queue
1461+
MTR_LOG_ERROR("%@ could not dispatch to matter queue for resubscription - error %@", self, error);
1462+
std::lock_guard lock(self->_lock);
1463+
[self _clearSubscriptionPoolWork];
1464+
}];
14491465
};
14501466

14511467
int64_t resubscriptionDelayNs = static_cast<int64_t>(secondsToWait * NSEC_PER_SEC);
@@ -2456,6 +2472,7 @@ - (void)_setupSubscriptionWithReason:(NSString *)reason
24562472

24572473
if (![self _subscriptionsAllowed]) {
24582474
MTR_LOG("%@ _setupSubscription: Subscriptions not allowed. Do not set up subscription (reason: %@)", self, reason);
2475+
[self _clearSubscriptionPoolWork];
24592476
return;
24602477
}
24612478

@@ -2475,6 +2492,7 @@ - (void)_setupSubscriptionWithReason:(NSString *)reason
24752492
// for now just subscribe once
24762493
if (!NeedToStartSubscriptionSetup(_internalDeviceState)) {
24772494
MTR_LOG("%@ setupSubscription: no need to subscribe due to internal state %lu (reason: %@)", self, static_cast<unsigned long>(_internalDeviceState), reason);
2495+
[self _clearSubscriptionPoolWork];
24782496
return;
24792497
}
24802498

src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m

+78-15
Original file line numberDiff line numberDiff line change
@@ -2641,7 +2641,7 @@ - (void)doTestSubscriptionPoolWithSize:(NSInteger)subscriptionPoolSize deviceOnb
26412641

26422642
XCTAssertEqualObjects(controller.controllerNodeID, nodeID);
26432643

2644-
NSArray<NSNumber *> * orderedDeviceIDs = @[ @(101), @(102), @(103), @(104), @(105) ];
2644+
NSArray<NSNumber *> * orderedDeviceIDs = [deviceOnboardingPayloads allKeys];
26452645

26462646
// Commission 5 devices
26472647
for (NSNumber * deviceID in orderedDeviceIDs) {
@@ -2669,21 +2669,16 @@ - (void)doTestSubscriptionPoolWithSize:(NSInteger)subscriptionPoolSize deviceOnb
26692669

26702670
// Set up expectations and delegates
26712671

2672-
NSDictionary<NSNumber *, XCTestExpectation *> * subscriptionExpectations = @{
2673-
@(101) : [self expectationWithDescription:@"Subscription 1 has been set up"],
2674-
@(102) : [self expectationWithDescription:@"Subscription 2 has been set up"],
2675-
@(103) : [self expectationWithDescription:@"Subscription 3 has been set up"],
2676-
@(104) : [self expectationWithDescription:@"Subscription 4 has been set up"],
2677-
@(105) : [self expectationWithDescription:@"Subscription 5 has been set up"],
2678-
};
2672+
NSMutableDictionary<NSNumber *, XCTestExpectation *> * subscriptionExpectations = [NSMutableDictionary dictionary];
2673+
for (NSNumber * deviceID in orderedDeviceIDs) {
2674+
NSString * expectationDescription = [NSString stringWithFormat:@"Subscription 1 has been set up %@", deviceID];
2675+
subscriptionExpectations[deviceID] = [self expectationWithDescription:expectationDescription];
2676+
}
26792677

2680-
NSDictionary<NSNumber *, MTRDeviceTestDelegate *> * deviceDelegates = @{
2681-
@(101) : [[MTRDeviceTestDelegate alloc] init],
2682-
@(102) : [[MTRDeviceTestDelegate alloc] init],
2683-
@(103) : [[MTRDeviceTestDelegate alloc] init],
2684-
@(104) : [[MTRDeviceTestDelegate alloc] init],
2685-
@(105) : [[MTRDeviceTestDelegate alloc] init],
2686-
};
2678+
NSMutableDictionary<NSNumber *, MTRDeviceTestDelegate *> * deviceDelegates = [NSMutableDictionary dictionary];
2679+
for (NSNumber * deviceID in orderedDeviceIDs) {
2680+
deviceDelegates[deviceID] = [[MTRDeviceTestDelegate alloc] init];
2681+
}
26872682

26882683
// Test with counters
26892684
__block os_unfair_lock counterLock = OS_UNFAIR_LOCK_INIT;
@@ -2786,6 +2781,74 @@ - (void)testSubscriptionPool
27862781
[self doTestSubscriptionPoolWithSize:2 deviceOnboardingPayloads:deviceOnboardingPayloads];
27872782
}
27882783

2784+
- (void)testSubscriptionPoolManyDevices
2785+
{
2786+
// QRCodes generated for discriminators 1111~1150 and passcodes 1001~1050
2787+
NSDictionary<NSNumber *, NSString *> * deviceOnboardingPayloads = @{
2788+
@(101) : @"MT:00000I9K17U0D900000",
2789+
@(102) : @"MT:0000000000BED900000",
2790+
@(103) : @"MT:000008C801TRD900000",
2791+
@(104) : @"MT:00000GOG0293E900000",
2792+
@(105) : @"MT:00000O-O03RGE900000",
2793+
@(106) : @"MT:00000WAX047UE900000",
2794+
@(107) : @"MT:000002N315P5F900000",
2795+
@(108) : @"MT:00000AZB165JF900000",
2796+
@(109) : @"MT:00000I9K17NWF900000",
2797+
@(110) : @"MT:000000000048G900000",
2798+
@(111) : @"MT:000008C801MLG900000",
2799+
@(112) : @"MT:00000GOG022ZG900000",
2800+
@(113) : @"MT:00000O-O03KAH900000",
2801+
@(114) : @"MT:00000WAX040OH900000",
2802+
@(115) : @"MT:000002N315I.H900000",
2803+
@(116) : @"MT:00000AZB16-CI900000",
2804+
@(117) : @"MT:00000I9K17GQI900000",
2805+
@(118) : @"MT:0000000000Z1J900000",
2806+
@(119) : @"MT:000008C801FFJ900000",
2807+
@(120) : @"MT:00000GOG02XSJ900000",
2808+
@(121) : @"MT:00000O-O03D4K900000",
2809+
@(122) : @"MT:00000WAX04VHK900000",
2810+
@(123) : @"MT:000002N315BVK900000",
2811+
@(124) : @"MT:00000AZB16T6L900000",
2812+
@(125) : @"MT:00000I9K179KL900000",
2813+
@(126) : @"MT:0000000000SXL900000",
2814+
@(127) : @"MT:000008C80189M900000",
2815+
@(128) : @"MT:00000GOG02QMM900000",
2816+
@(129) : @"MT:00000O-O036-M900000",
2817+
@(130) : @"MT:00000WAX04OBN900000",
2818+
@(131) : @"MT:000002N3154PN900000",
2819+
@(132) : @"MT:00000AZB16M0O900000",
2820+
@(133) : @"MT:00000I9K172EO900000",
2821+
@(134) : @"MT:0000000000LRO900000",
2822+
@(135) : @"MT:000008C80113P900000",
2823+
@(136) : @"MT:00000GOG02JGP900000",
2824+
@(137) : @"MT:00000O-O03.TP900000",
2825+
@(138) : @"MT:00000WAX04H5Q900000",
2826+
@(139) : @"MT:000002N315ZIQ900000",
2827+
@(140) : @"MT:00000AZB16FWQ900000",
2828+
@(141) : @"MT:00000I9K17X7R900000",
2829+
@(142) : @"MT:0000000000ELR900000",
2830+
@(143) : @"MT:000008C801WYR900000",
2831+
@(144) : @"MT:00000GOG02CAS900000",
2832+
@(145) : @"MT:00000O-O03UNS900000",
2833+
@(146) : @"MT:00000WAX04A.S900000",
2834+
@(147) : @"MT:000002N315SCT900000",
2835+
@(148) : @"MT:00000AZB168QT900000",
2836+
@(149) : @"MT:00000I9K17Q1U900000",
2837+
@(150) : @"MT:00000000007FU900000",
2838+
};
2839+
2840+
// Start our helper apps.
2841+
__auto_type * sortedKeys = [[deviceOnboardingPayloads allKeys] sortedArrayUsingSelector:@selector(compare:)];
2842+
for (NSNumber * deviceID in sortedKeys) {
2843+
BOOL started = [self startAppWithName:@"all-clusters"
2844+
arguments:@[]
2845+
payload:deviceOnboardingPayloads[deviceID]];
2846+
XCTAssertTrue(started);
2847+
}
2848+
2849+
[self doTestSubscriptionPoolWithSize:3 deviceOnboardingPayloads:deviceOnboardingPayloads];
2850+
}
2851+
27892852
- (MTRDevice *)getMTRDevice:(NSNumber *)deviceID
27902853
{
27912854
__auto_type * factory = [MTRDeviceControllerFactory sharedInstance];

0 commit comments

Comments
 (0)