@@ -908,12 +908,17 @@ - (void)invalidate
908
908
_reattemptingSubscription = NO;
909
909
910
910
[_deviceController asyncDispatchToMatterQueue:^{
911
+ MTR_LOG("%@ invalidate disconnecting ReadClient and SubscriptionCallback", self);
912
+
911
913
// Destroy the read client and callback (has to happen on the Matter
912
914
// queue, to avoid deleting objects that are being referenced), to
913
915
// tear down the subscription. We will get no more callbacks from
914
916
// the subscrption after this point.
915
917
std::lock_guard lock(self->_lock);
916
918
self->_currentReadClient = nullptr;
919
+ if (self->_currentSubscriptionCallback) {
920
+ delete self->_currentSubscriptionCallback;
921
+ }
917
922
self->_currentSubscriptionCallback = nullptr;
918
923
919
924
[self _changeInternalState:MTRInternalDeviceStateUnsubscribed];
@@ -940,6 +945,7 @@ - (void)nodeMayBeAdvertisingOperational
940
945
// whether it might be.
941
946
- (void)_triggerResubscribeWithReason:(NSString *)reason nodeLikelyReachable:(BOOL)nodeLikelyReachable
942
947
{
948
+ MTR_LOG("%@ _triggerResubscribeWithReason called with reason %@", self, reason);
943
949
assertChipStackLockedByCurrentThread();
944
950
945
951
// We might want to trigger a resubscribe on our existing ReadClient. Do
@@ -1235,6 +1241,12 @@ - (void)_handleSubscriptionEstablished
1235
1241
- (void)_handleSubscriptionError:(NSError *)error
1236
1242
{
1237
1243
std::lock_guard lock(_lock);
1244
+ [self _doHandleSubscriptionError:error];
1245
+ }
1246
+
1247
+ - (void)_doHandleSubscriptionError:(NSError *)error
1248
+ {
1249
+ os_unfair_lock_assert_owner(&_lock);
1238
1250
1239
1251
[self _changeInternalState:MTRInternalDeviceStateUnsubscribed];
1240
1252
_unreportedEvents = nil;
@@ -1400,6 +1412,12 @@ - (void)_handleResubscriptionNeededWithDelay:(NSNumber *)resubscriptionDelayMs
1400
1412
- (void)_handleSubscriptionReset:(NSNumber * _Nullable)retryDelay
1401
1413
{
1402
1414
std::lock_guard lock(_lock);
1415
+ [self _doHandleSubscriptionReset:retryDelay];
1416
+ }
1417
+
1418
+ - (void)_doHandleSubscriptionReset:(NSNumber * _Nullable)retryDelay
1419
+ {
1420
+ os_unfair_lock_assert_owner(&_lock);
1403
1421
1404
1422
// If we are here, then either we failed to establish initial CASE, or we
1405
1423
// failed to send the initial SubscribeRequest message, or our ReadClient
@@ -1471,7 +1489,7 @@ - (void)_reattemptSubscriptionNowIfNeededWithReason:(NSString *)reason
1471
1489
return;
1472
1490
}
1473
1491
1474
- MTR_LOG("%@ reattempting subscription", self);
1492
+ MTR_LOG("%@ reattempting subscription with reason %@ ", self, reason );
1475
1493
self.reattemptingSubscription = NO;
1476
1494
[self _setupSubscriptionWithReason:reason];
1477
1495
}
@@ -2100,6 +2118,22 @@ - (void)unitTestClearClusterData
2100
2118
}
2101
2119
#endif
2102
2120
2121
+ - (void)_reconcilePersistedClustersWithStorage
2122
+ {
2123
+ os_unfair_lock_assert_owner(&self->_lock);
2124
+
2125
+ NSMutableSet * clusterPathsToRemove = [NSMutableSet set];
2126
+ for (MTRClusterPath * clusterPath in _persistedClusters) {
2127
+ MTRDeviceClusterData * data = [_deviceController.controllerDataStore getStoredClusterDataForNodeID:_nodeID endpointID:clusterPath.endpoint clusterID:clusterPath.cluster];
2128
+ if (!data) {
2129
+ [clusterPathsToRemove addObject:clusterPath];
2130
+ }
2131
+ }
2132
+
2133
+ MTR_LOG_ERROR("%@ Storage missing %lu / %lu clusters - reconciling in-memory records", self, static_cast<unsigned long>(clusterPathsToRemove.count), static_cast<unsigned long>(_persistedClusters.count));
2134
+ [_persistedClusters minusSet:clusterPathsToRemove];
2135
+ }
2136
+
2103
2137
- (nullable MTRDeviceClusterData *)_clusterDataForPath:(MTRClusterPath *)clusterPath
2104
2138
{
2105
2139
os_unfair_lock_assert_owner(&self->_lock);
@@ -2132,8 +2166,16 @@ - (nullable MTRDeviceClusterData *)_clusterDataForPath:(MTRClusterPath *)cluster
2132
2166
2133
2167
// Page in the stored value for the data.
2134
2168
MTRDeviceClusterData * data = [_deviceController.controllerDataStore getStoredClusterDataForNodeID:_nodeID endpointID:clusterPath.endpoint clusterID:clusterPath.cluster];
2169
+ MTR_LOG("%@ cluster path %@ cache miss - load from storage success %@", self, clusterPath, YES_NO(data));
2135
2170
if (data != nil) {
2136
2171
[_persistedClusterData setObject:data forKey:clusterPath];
2172
+ } else {
2173
+ // If clusterPath is in _persistedClusters and the data store returns nil for it, then the in-memory cache is now not dependable, and subscription should be reset and reestablished to reload cache from device
2174
+
2175
+ // First make sure _persistedClusters is consistent with storage, so repeated calls don't immediately re-trigger this
2176
+ [self _reconcilePersistedClustersWithStorage];
2177
+
2178
+ [self _resetSubscriptionWithReasonString:[NSString stringWithFormat:@"Data store has no data for cluster %@", clusterPath]];
2137
2179
}
2138
2180
2139
2181
return data;
@@ -2303,13 +2345,43 @@ - (void)_stopConnectivityMonitoring
2303
2345
}
2304
2346
}
2305
2347
2348
+ - (void)_resetSubscriptionWithReasonString:(NSString *)reasonString
2349
+ {
2350
+ os_unfair_lock_assert_owner(&self->_lock);
2351
+ MTR_LOG_ERROR("%@ %@ - resetting subscription", self, reasonString);
2352
+
2353
+ [_deviceController asyncDispatchToMatterQueue:^{
2354
+ MTR_LOG("%@ subscription reset disconnecting ReadClient and SubscriptionCallback", self);
2355
+
2356
+ std::lock_guard lock(self->_lock);
2357
+ self->_currentReadClient = nullptr;
2358
+ if (self->_currentSubscriptionCallback) {
2359
+ delete self->_currentSubscriptionCallback;
2360
+ }
2361
+ self->_currentSubscriptionCallback = nullptr;
2362
+
2363
+ [self _doHandleSubscriptionError:nil];
2364
+ // Use nil reset delay so that this keeps existing backoff timing
2365
+ [self _doHandleSubscriptionReset:nil];
2366
+ }
2367
+ errorHandler:nil];
2368
+ }
2369
+
2370
+ #ifdef DEBUG
2371
+ - (void)unitTestResetSubscription
2372
+ {
2373
+ std::lock_guard lock(self->_lock);
2374
+ [self _resetSubscriptionWithReasonString:@"Unit test reset subscription"];
2375
+ }
2376
+ #endif
2377
+
2306
2378
// assume lock is held
2307
2379
- (void)_setupSubscriptionWithReason:(NSString *)reason
2308
2380
{
2309
2381
os_unfair_lock_assert_owner(&self->_lock);
2310
2382
2311
2383
if (![self _subscriptionsAllowed]) {
2312
- MTR_LOG("%@ _setupSubscription: Subscriptions not allowed. Do not set up subscription", self);
2384
+ MTR_LOG("%@ _setupSubscription: Subscriptions not allowed. Do not set up subscription (reason: %@) ", self, reason );
2313
2385
return;
2314
2386
}
2315
2387
@@ -2328,6 +2400,7 @@ - (void)_setupSubscriptionWithReason:(NSString *)reason
2328
2400
2329
2401
// for now just subscribe once
2330
2402
if (!NeedToStartSubscriptionSetup(_internalDeviceState)) {
2403
+ MTR_LOG("%@ setupSubscription: no need to subscribe due to internal state %lu (reason: %@)", self, static_cast<unsigned long>(_internalDeviceState), reason);
2331
2404
return;
2332
2405
}
2333
2406
@@ -3758,14 +3831,21 @@ - (void)_storePersistedDeviceData
3758
3831
}
3759
3832
3760
3833
#ifdef DEBUG
3761
- - (MTRDeviceClusterData *)_getClusterDataForPath :(MTRClusterPath *)path
3834
+ - (MTRDeviceClusterData *)unitTestGetClusterDataForPath :(MTRClusterPath *)path
3762
3835
{
3763
3836
std::lock_guard lock(_lock);
3764
3837
3765
3838
return [[self _clusterDataForPath:path] copy];
3766
3839
}
3767
3840
3768
- - (BOOL)_clusterHasBeenPersisted:(MTRClusterPath *)path
3841
+ - (NSSet<MTRClusterPath *> *)unitTestGetPersistedClusters
3842
+ {
3843
+ std::lock_guard lock(_lock);
3844
+
3845
+ return [_persistedClusters copy];
3846
+ }
3847
+
3848
+ - (BOOL)unitTestClusterHasBeenPersisted:(MTRClusterPath *)path
3769
3849
{
3770
3850
std::lock_guard lock(_lock);
3771
3851
0 commit comments