@@ -871,13 +871,7 @@ - (void)invalidate
871
871
// tear down the subscription. We will get no more callbacks from
872
872
// the subscription after this point.
873
873
std::lock_guard lock(self->_lock);
874
- self->_currentReadClient = nullptr;
875
- if (self->_currentSubscriptionCallback) {
876
- delete self->_currentSubscriptionCallback;
877
- }
878
- self->_currentSubscriptionCallback = nullptr;
879
-
880
- [self _changeInternalState:MTRInternalDeviceStateUnsubscribed];
874
+ [self _resetSubscription];
881
875
}
882
876
errorHandler:nil];
883
877
@@ -938,8 +932,8 @@ - (void)_triggerResubscribeWithReason:(NSString *)reason nodeLikelyReachable:(BO
938
932
}
939
933
readClientToResubscribe->TriggerResubscribeIfScheduled(reason.UTF8String);
940
934
} else if (_internalDeviceState == MTRInternalDeviceStateSubscribing && nodeLikelyReachable) {
941
- // If we have reason to suspect that the node is now reachable and we haven’ t established a
942
- // CASE session yet, let’ s consider it to be stalled and invalidate the pairing session.
935
+ // If we have reason to suspect that the node is now reachable and we haven' t established a
936
+ // CASE session yet, let' s consider it to be stalled and invalidate the pairing session.
943
937
[self._concreteController asyncGetCommissionerOnMatterQueue:^(Controller::DeviceCommissioner * commissioner) {
944
938
auto caseSessionMgr = commissioner->CASESessionMgr();
945
939
VerifyOrDie(caseSessionMgr != nullptr);
@@ -1164,7 +1158,7 @@ - (void)_handleSubscriptionError:(NSError *)error
1164
1158
[self _doHandleSubscriptionError:error];
1165
1159
}
1166
1160
1167
- - (void)_doHandleSubscriptionError:(NSError *)error
1161
+ - (void)_doHandleSubscriptionError:(nullable NSError *)error
1168
1162
{
1169
1163
assertChipStackLockedByCurrentThread();
1170
1164
@@ -1305,7 +1299,13 @@ - (void)_handleResubscriptionNeededWithDelay:(NSNumber *)resubscriptionDelayMs
1305
1299
1306
1300
// Change our state before going async.
1307
1301
[self _changeState:MTRDeviceStateUnknown];
1308
- [self _changeInternalState:MTRInternalDeviceStateResubscribing];
1302
+
1303
+ // If we have never had a subscription established, stay in the Subscribing
1304
+ // state; don't transition to Resubscribing just because our attempt at
1305
+ // subscribing failed.
1306
+ if (HadSubscriptionEstablishedOnce(self->_internalDeviceState)) {
1307
+ [self _changeInternalState:MTRInternalDeviceStateResubscribing];
1308
+ }
1309
1309
1310
1310
dispatch_async(self.queue, ^{
1311
1311
[self _handleResubscriptionNeededWithDelayOnDeviceQueue:resubscriptionDelayMs];
@@ -2444,19 +2444,29 @@ - (void)_resetSubscriptionWithReasonString:(NSString *)reasonString
2444
2444
MTR_LOG("%@ subscription reset disconnecting ReadClient and SubscriptionCallback", self);
2445
2445
2446
2446
std::lock_guard lock(self->_lock);
2447
- self->_currentReadClient = nullptr;
2448
- if (self->_currentSubscriptionCallback) {
2449
- delete self->_currentSubscriptionCallback;
2450
- }
2451
- self->_currentSubscriptionCallback = nullptr;
2452
2447
2453
- [self _doHandleSubscriptionError:nil];
2448
+ [self _resetSubscription];
2449
+
2454
2450
// Use nil reset delay so that this keeps existing backoff timing
2455
2451
[self _doHandleSubscriptionReset:nil];
2456
2452
}
2457
2453
errorHandler:nil];
2458
2454
}
2459
2455
2456
+ - (void)_resetSubscription
2457
+ {
2458
+ assertChipStackLockedByCurrentThread();
2459
+ os_unfair_lock_assert_owner(&_lock);
2460
+
2461
+ _currentReadClient = nullptr;
2462
+ if (_currentSubscriptionCallback) {
2463
+ delete _currentSubscriptionCallback;
2464
+ _currentSubscriptionCallback = nullptr;
2465
+ }
2466
+
2467
+ [self _doHandleSubscriptionError:nil];
2468
+ }
2469
+
2460
2470
#ifdef DEBUG
2461
2471
- (void)unitTestResetSubscription
2462
2472
{
@@ -4322,11 +4332,31 @@ - (BOOL)_deviceHasActiveSubscription
4322
4332
4323
4333
- (void)_deviceMayBeReachable
4324
4334
{
4325
- MTR_LOG("%@ _deviceMayBeReachable called", self);
4335
+ MTR_LOG("%@ _deviceMayBeReachable called, resetting subscription ", self);
4326
4336
// TODO: This should only be allowed for thread devices
4327
- [_deviceController asyncDispatchToMatterQueue:^{
4328
- [self _triggerResubscribeWithReason:@"SPI client indicated the device may now be reachable"
4329
- nodeLikelyReachable:YES];
4337
+ [self._concreteController asyncGetCommissionerOnMatterQueue:^(Controller::DeviceCommissioner * commissioner) {
4338
+ // Reset all of our subscription/session state and re-establish it all
4339
+ // from the start. Reset our subscription first, before tearing
4340
+ // down the session, so we don't have to worry about the
4341
+ // notifications from the latter coming through async and
4342
+ // complicating the situation. Unfortunately, we do not want to
4343
+ // hold the lock when destroying the session, just in case it still
4344
+ // ends up calling into us somehow, so we have to break the work up
4345
+ // into two separate locked sections...
4346
+ {
4347
+ std::lock_guard lock(self->_lock);
4348
+ [self _clearSubscriptionPoolWork];
4349
+ [self _resetSubscription];
4350
+ }
4351
+
4352
+ auto caseSessionMgr = commissioner->CASESessionMgr();
4353
+ VerifyOrDie(caseSessionMgr != nullptr);
4354
+ caseSessionMgr->ReleaseSession(commissioner->GetPeerScopedId(self->_nodeID.unsignedLongLongValue));
4355
+
4356
+ std::lock_guard lock(self->_lock);
4357
+ // Use _ensureSubscriptionForExistingDelegates so that the subscriptions
4358
+ // will go through the pool as needed, not necessarily happen immediately.
4359
+ [self _ensureSubscriptionForExistingDelegates:@"SPI client indicated the device may now be reachable"];
4330
4360
} errorHandler:nil];
4331
4361
}
4332
4362
0 commit comments