@@ -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