Skip to content

Commit 206f7ed

Browse files
Switch MTRDevice to using MTRDeviceClusterData for its attribute cache.
Use these to store attribute values too, not just data versions.
1 parent 700e6d2 commit 206f7ed

File tree

3 files changed

+116
-137
lines changed

3 files changed

+116
-137
lines changed

src/darwin/Framework/CHIP/MTRDevice.mm

+92-96
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,23 @@ typedef NS_ENUM(NSUInteger, MTRDeviceWorkItemDuplicateTypeID) {
184184
MTRDeviceWorkItemDuplicateReadTypeID = 1,
185185
};
186186

187-
@implementation MTRDeviceClusterData
187+
@implementation MTRDeviceClusterData {
188+
NSMutableDictionary<NSNumber *, MTRDeviceDataValueDictionary> * _attributes;
189+
}
188190

189191
static NSString * const sDataVersionKey = @"dataVersion";
190192
static NSString * const sAttributesKey = @"attributes";
191193

194+
- (void)storeValue:(MTRDeviceDataValueDictionary _Nullable)value forAttribute:(NSNumber *)attribute
195+
{
196+
_attributes[attribute] = value;
197+
}
198+
199+
- (NSDictionary<NSNumber *, MTRDeviceDataValueDictionary> *)attributes
200+
{
201+
return _attributes;
202+
}
203+
192204
+ (BOOL)supportsSecureCoding
193205
{
194206
return YES;
@@ -199,6 +211,11 @@ - (NSString *)description
199211
return [NSString stringWithFormat:@"<MTRDeviceClusterData: dataVersion %@ attributes count %lu>", _dataVersion, static_cast<unsigned long>(_attributes.count)];
200212
}
201213

214+
- (nullable instancetype)init
215+
{
216+
return [self initWithDataVersion:nil attributes:nil];
217+
}
218+
202219
// Attributes dictionary is: attributeID => data-value dictionary
203220
- (nullable instancetype)initWithDataVersion:(NSNumber * _Nullable)dataVersion attributes:(NSDictionary<NSNumber *, MTRDeviceDataValueDictionary> * _Nullable)attributes
204221
{
@@ -208,7 +225,8 @@ - (nullable instancetype)initWithDataVersion:(NSNumber * _Nullable)dataVersion a
208225
}
209226

210227
_dataVersion = [dataVersion copy];
211-
_attributes = [attributes copy];
228+
_attributes = [NSMutableDictionary dictionaryWithCapacity:attributes.count];
229+
[_attributes addEntriesFromDictionary:attributes];
212230

213231
return self;
214232
}
@@ -299,10 +317,6 @@ @interface MTRDevice ()
299317
*/
300318
@property (nonatomic) BOOL reattemptingSubscription;
301319

302-
// Read cache is attributePath => NSDictionary of value.
303-
// See MTRDeviceResponseHandler definition for value dictionary details.
304-
@property (nonatomic) NSMutableDictionary<MTRAttributePath *, NSDictionary *> * readCache;
305-
306320
// Expected value cache is attributePath => NSArray of [NSDate of expiration time, NSDictionary of value, expected value ID]
307321
// - See MTRDeviceExpectedValueFieldIndex for the definitions of indices into this array.
308322
// See MTRDeviceResponseHandler definition for value dictionary details.
@@ -369,7 +383,6 @@ - (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceControlle
369383
_deviceController = controller;
370384
_queue
371385
= dispatch_queue_create("org.csa-iot.matter.framework.device.workqueue", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
372-
_readCache = [NSMutableDictionary dictionary];
373386
_expectedValueCache = [NSMutableDictionary dictionary];
374387
_asyncWorkQueue = [[MTRAsyncWorkQueue alloc] initWithContext:self];
375388
_state = MTRDeviceStateUnknown;
@@ -1021,30 +1034,12 @@ - (void)_handleReportBegin
10211034
}
10221035
}
10231036

1024-
- (NSDictionary<NSNumber *, MTRDeviceDataValueDictionary> *)_attributesForCluster:(MTRClusterPath *)clusterPath
1025-
{
1026-
os_unfair_lock_assert_owner(&self->_lock);
1027-
NSMutableDictionary * attributesToReturn = [NSMutableDictionary dictionary];
1028-
for (MTRAttributePath * attributePath in _readCache) {
1029-
if ([attributePath.endpoint isEqualToNumber:clusterPath.endpoint] && [attributePath.cluster isEqualToNumber:clusterPath.cluster]) {
1030-
attributesToReturn[attributePath.attribute] = _readCache[attributePath];
1031-
}
1032-
}
1033-
return attributesToReturn;
1034-
}
1035-
1036-
- (NSDictionary<MTRClusterPath *, MTRDeviceClusterData *> *)_clusterDataForPaths:(NSSet<MTRClusterPath *> *)clusterPaths
1037+
- (NSDictionary<MTRClusterPath *, MTRDeviceClusterData *> *)_copiedClusterDataForPaths:(NSSet<MTRClusterPath *> *)clusterPaths
10371038
{
10381039
os_unfair_lock_assert_owner(&self->_lock);
10391040
NSMutableDictionary * clusterDataToReturn = [NSMutableDictionary dictionary];
10401041
for (MTRClusterPath * clusterPath in clusterPaths) {
1041-
NSNumber * dataVersion = _clusterData[clusterPath].dataVersion;
1042-
NSDictionary<NSNumber *, MTRDeviceDataValueDictionary> * attributes = nil;
1043-
attributes = [self _attributesForCluster:clusterPath];
1044-
if (dataVersion || attributes) {
1045-
MTRDeviceClusterData * clusterData = [[MTRDeviceClusterData alloc] initWithDataVersion:dataVersion attributes:attributes];
1046-
clusterDataToReturn[clusterPath] = clusterData;
1047-
}
1042+
clusterDataToReturn[clusterPath] = [_clusterData[clusterPath] copy];
10481043
}
10491044

10501045
return clusterDataToReturn;
@@ -1060,7 +1055,10 @@ - (void)_handleReportEnd
10601055
BOOL dataStoreExists = _deviceController.controllerDataStore != nil;
10611056
if (dataStoreExists && _clustersToPersist.count) {
10621057
MTR_LOG_DEFAULT("%@ Storing cluster information (data version) count: %lu", self, static_cast<unsigned long>(_clustersToPersist.count));
1063-
NSDictionary<MTRClusterPath *, MTRDeviceClusterData *> * clusterData = [self _clusterDataForPaths:_clustersToPersist];
1058+
// We're going to hand out these MTRDeviceClusterData objects to our
1059+
// storage implementation, which will try to read them later. Make sure
1060+
// we snapshot the state here instead of handing out live copies.
1061+
NSDictionary<MTRClusterPath *, MTRDeviceClusterData *> * clusterData = [self _copiedClusterDataForPaths:_clustersToPersist];
10641062
[_deviceController.controllerDataStore storeClusterData:clusterData forNodeID:_nodeID];
10651063
_clustersToPersist = nil;
10661064
}
@@ -1221,6 +1219,42 @@ - (void)_handleEventReport:(NSArray<NSDictionary<NSString *, id> *> *)eventRepor
12211219
return dataVersions;
12221220
}
12231221

1222+
- (MTRDeviceDataValueDictionary _Nullable)_cachedAttributeValueForPath:(MTRAttributePath *)path
1223+
{
1224+
os_unfair_lock_assert_owner(&self->_lock);
1225+
1226+
// We need an MTRClusterPath to do the lookup in _clusterData.
1227+
auto * clusterPath = [MTRClusterPath clusterPathWithEndpointID:path.endpoint clusterID:path.cluster];
1228+
1229+
MTRDeviceClusterData * clusterData = _clusterData[clusterPath];
1230+
if (clusterData == nil) {
1231+
return nil;
1232+
}
1233+
1234+
return clusterData.attributes[path.attribute];
1235+
}
1236+
1237+
- (void)_setCachedAttributeValue:(MTRDeviceDataValueDictionary _Nullable)value forPath:(MTRAttributePath *)path
1238+
{
1239+
os_unfair_lock_assert_owner(&self->_lock);
1240+
1241+
// We need an MTRClusterPath to do the lookup in _clusterData.
1242+
auto * clusterPath = [MTRClusterPath clusterPathWithEndpointID:path.endpoint clusterID:path.cluster];
1243+
1244+
MTRDeviceClusterData * clusterData = _clusterData[clusterPath];
1245+
if (clusterData == nil) {
1246+
if (value == nil) {
1247+
// Nothing to do.
1248+
return;
1249+
}
1250+
1251+
clusterData = [[MTRDeviceClusterData alloc] init];
1252+
_clusterData[clusterPath] = clusterData;
1253+
}
1254+
1255+
[clusterData storeValue:value forAttribute:path.attribute];
1256+
}
1257+
12241258
- (void)_createDataVersionFilterListFromDictionary:(NSDictionary<MTRClusterPath *, NSNumber *> *)dataVersions dataVersionFilterList:(DataVersionFilter **)dataVersionFilterList count:(size_t *)count sizeReduction:(size_t)sizeReduction
12251259
{
12261260
size_t maxDataVersionFilterSize = dataVersions.count;
@@ -2192,7 +2226,7 @@ - (void)_checkExpiredExpectedValues
21922226
// compare with known value and mark for report if different
21932227
MTRAttributePath * attributePath = attributeInfo[0];
21942228
NSDictionary * attributeDataValue = attributeInfo[1];
2195-
NSDictionary * cachedAttributeDataValue = _readCache[attributePath];
2229+
NSDictionary * cachedAttributeDataValue = [self _cachedAttributeValueForPath:attributePath];
21962230
if (cachedAttributeDataValue
21972231
&& ![self _attributeDataValue:attributeDataValue isEqualToDataValue:cachedAttributeDataValue]) {
21982232
[attributesToReport addObject:@{ MTRAttributePathKey : attributePath, MTRDataKey : cachedAttributeDataValue, MTRPreviousDataKey : attributeDataValue }];
@@ -2249,7 +2283,7 @@ - (void)_performScheduledExpirationCheck
22492283
}
22502284

22512285
// Then check read cache
2252-
NSDictionary<NSString *, id> * cachedAttributeValue = _readCache[attributePath];
2286+
NSDictionary<NSString *, id> * cachedAttributeValue = [self _cachedAttributeValueForPath:attributePath];
22532287
if (cachedAttributeValue) {
22542288
return cachedAttributeValue;
22552289
} else {
@@ -2301,16 +2335,15 @@ - (void)_noteDataVersion:(NSNumber *)dataVersion forClusterPath:(MTRClusterPath
23012335
// Update data version used for subscription filtering
23022336
MTRDeviceClusterData * clusterData = _clusterData[clusterPath];
23032337
if (!clusterData) {
2304-
clusterData = [[MTRDeviceClusterData alloc] init];
2338+
clusterData = [[MTRDeviceClusterData alloc] initWithDataVersion:dataVersion attributes:nil];
23052339
_clusterData[clusterPath] = clusterData;
23062340
dataVersionChanged = YES;
23072341
} else if (![clusterData.dataVersion isEqualToNumber:dataVersion]) {
2342+
clusterData.dataVersion = dataVersion;
23082343
dataVersionChanged = YES;
23092344
}
23102345

23112346
if (dataVersionChanged) {
2312-
clusterData.dataVersion = dataVersion;
2313-
23142347
// Mark cluster path as needing persistence if needed
23152348
BOOL dataStoreExists = _deviceController.controllerDataStore != nil;
23162349
if (dataStoreExists) {
@@ -2361,11 +2394,12 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
23612394
// if this is an error, report and purge cache
23622395
if (attributeError) {
23632396
shouldReportAttribute = YES;
2397+
previousValue = [self _cachedAttributeValueForPath:attributePath];
23642398
MTR_LOG_INFO("%@ report %@ error %@ purge expected value %@ read cache %@", self, attributePath, attributeError,
2365-
_expectedValueCache[attributePath], _readCache[attributePath]);
2399+
_expectedValueCache[attributePath], previousValue);
23662400
_expectedValueCache[attributePath] = nil;
2367-
previousValue = _readCache[attributePath];
2368-
_readCache[attributePath] = nil;
2401+
// TODO: Is this clearing business really what we want?
2402+
[self _setCachedAttributeValue:nil forPath:attributePath];
23692403
} else {
23702404
// First separate data version and restore data value to a form without data version
23712405
NSNumber * dataVersion = attributeDataValue[MTRDataVersionKey];
@@ -2377,7 +2411,8 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
23772411
attributeDataValue = [self _dataValueWithoutDataVersion:attributeDataValue];
23782412
}
23792413

2380-
BOOL readCacheValueChanged = ![self _attributeDataValue:attributeDataValue isEqualToDataValue:_readCache[attributePath]];
2414+
previousValue = [self _cachedAttributeValueForPath:attributePath];
2415+
BOOL readCacheValueChanged = ![self _attributeDataValue:attributeDataValue isEqualToDataValue:previousValue];
23812416
// Check if attribute needs to be persisted - compare only to read cache and disregard expected values
23822417
if (dataStoreExists && readCacheValueChanged) {
23832418
[self _noteChangeForClusterPath:clusterPath];
@@ -2399,12 +2434,11 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
23992434
// Report the attribute if a read would get a changed value. This happens
24002435
// when our cached value changes and no expected value exists.
24012436
if (readCacheValueChanged && !expectedValue) {
2402-
previousValue = _readCache[attributePath];
24032437
shouldReportAttribute = YES;
24042438
}
24052439

2406-
// Now that we have grabbed previousValue, update the readCache with the attribute value.
2407-
_readCache[attributePath] = attributeDataValue;
2440+
// Now that we have grabbed previousValue, update our cache with the attribute value.
2441+
[self _setCachedAttributeValue:attributeDataValue forPath:attributePath];
24082442

24092443
if (!shouldReportAttribute) {
24102444
// If an expected value exists, the attribute will not be reported at this time.
@@ -2455,42 +2489,15 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
24552489
return attributesToReport;
24562490
}
24572491

2458-
- (void)_setAttributeValues:(NSArray<NSDictionary *> *)attributeValues reportChanges:(BOOL)reportChanges
2459-
{
2460-
os_unfair_lock_assert_owner(&self->_lock);
2461-
2462-
if (!attributeValues.count) {
2463-
return;
2464-
}
2465-
2466-
if (reportChanges) {
2467-
[self _reportAttributes:[self _getAttributesToReportWithReportedValues:attributeValues]];
2468-
} else {
2469-
for (NSDictionary * responseValue in attributeValues) {
2470-
MTRAttributePath * path = responseValue[MTRAttributePathKey];
2471-
NSDictionary * dataValue = responseValue[MTRDataKey];
2472-
_readCache[path] = dataValue;
2473-
}
2474-
}
2475-
2476-
// If cache is set from storage and is primed with initial configuration data, then assume the client had beeen informed in the past, and mark that the callback has been called
2477-
if ([self _isCachePrimedWithInitialConfigurationData]) {
2478-
_delegateDeviceCachePrimedCalled = YES;
2479-
}
2480-
}
2481-
2482-
- (void)setAttributeValues:(NSArray<NSDictionary *> *)attributeValues reportChanges:(BOOL)reportChanges
2483-
{
2484-
MTR_LOG_INFO("%@ setAttributeValues count: %lu reportChanges: %d", self, static_cast<unsigned long>(attributeValues.count), reportChanges);
2485-
std::lock_guard lock(_lock);
2486-
[self _setAttributeValues:attributeValues reportChanges:reportChanges];
2487-
}
2488-
24892492
#ifdef DEBUG
24902493
- (NSUInteger)unitTestAttributeCount
24912494
{
24922495
std::lock_guard lock(_lock);
2493-
return _readCache.count;
2496+
NSUInteger count = 0;
2497+
for (MTRClusterPath * path in _clusterData) {
2498+
count += _clusterData[path].attributes.count;
2499+
}
2500+
return count;
24942501
}
24952502
#endif
24962503

@@ -2503,25 +2510,12 @@ - (void)setClusterData:(NSDictionary<MTRClusterPath *, MTRDeviceClusterData *> *
25032510

25042511
std::lock_guard lock(_lock);
25052512

2506-
// For each cluster, extract and create the attribute response-value for the read cache
2507-
// TODO: consider some optimization in how the read cache is structured so there's fewer conversions from this format to what's in the cache
2508-
for (MTRClusterPath * clusterPath in clusterData) {
2509-
MTRDeviceClusterData * data = clusterData[clusterPath];
2510-
// Build and set attributes one cluster at a time to avoid creating a ton of temporary objects at a time
2511-
@autoreleasepool {
2512-
NSMutableArray * attributeValues = [NSMutableArray array];
2513-
for (NSNumber * attributeID in data.attributes) {
2514-
MTRAttributePath * attributePath = [MTRAttributePath attributePathWithEndpointID:clusterPath.endpoint clusterID:clusterPath.cluster attributeID:attributeID];
2515-
NSDictionary * responseValue = @{ MTRAttributePathKey : attributePath, MTRDataKey : data.attributes[attributeID] };
2516-
[attributeValues addObject:responseValue];
2517-
}
2518-
if (attributeValues.count) {
2519-
[self _setAttributeValues:attributeValues reportChanges:NO];
2520-
}
2521-
}
2522-
}
2523-
25242513
[_clusterData addEntriesFromDictionary:clusterData];
2514+
2515+
// If cache is set from storage and is primed with initial configuration data, then assume the client had beeen informed in the past, and mark that the callback has been called
2516+
if ([self _isCachePrimedWithInitialConfigurationData]) {
2517+
_delegateDeviceCachePrimedCalled = YES;
2518+
}
25252519
}
25262520

25272521
- (BOOL)deviceCachePrimed
@@ -2558,23 +2552,25 @@ - (void)_setExpectedValue:(NSDictionary<NSString *, id> *)expectedAttributeValue
25582552
// Remove previous expected value only if it's from the same setExpectedValues operation
25592553
NSNumber * previousExpectedValueID = previousExpectedValue[MTRDeviceExpectedValueFieldIDIndex];
25602554
if (previousExpectedValueID.unsignedLongLongValue == expectedValueID) {
2555+
MTRDeviceDataValueDictionary cachedValue = [self _cachedAttributeValueForPath:attributePath];
25612556
if (![self _attributeDataValue:previousExpectedValue[MTRDeviceExpectedValueFieldValueIndex]
2562-
isEqualToDataValue:_readCache[attributePath]]) {
2557+
isEqualToDataValue:cachedValue]) {
25632558
// Case of removing expected value that is different than read cache - report read cache value
25642559
*shouldReportValue = YES;
2565-
*attributeValueToReport = _readCache[attributePath];
2560+
*attributeValueToReport = cachedValue;
25662561
*previousValue = previousExpectedValue[MTRDeviceExpectedValueFieldValueIndex];
25672562
_expectedValueCache[attributePath] = nil;
25682563
}
25692564
}
25702565
}
25712566
} else {
2567+
MTRDeviceDataValueDictionary cachedValue = [self _cachedAttributeValueForPath:attributePath];
25722568
if (expectedAttributeValue
2573-
&& ![self _attributeDataValue:expectedAttributeValue isEqualToDataValue:_readCache[attributePath]]) {
2569+
&& ![self _attributeDataValue:expectedAttributeValue isEqualToDataValue:cachedValue]) {
25742570
// Case where new expected value is different than read cache - report new expected value
25752571
*shouldReportValue = YES;
25762572
*attributeValueToReport = expectedAttributeValue;
2577-
*previousValue = _readCache[attributePath];
2573+
*previousValue = cachedValue;
25782574
} else {
25792575
*previousValue = nil;
25802576
}
@@ -2708,7 +2704,7 @@ - (BOOL)_isCachePrimedWithInitialConfigurationData
27082704
os_unfair_lock_assert_owner(&self->_lock);
27092705

27102706
// Check if root node descriptor exists
2711-
NSDictionary * rootDescriptorPartsListDataValue = _readCache[[MTRAttributePath attributePathWithEndpointID:@(kRootEndpointId) clusterID:@(MTRClusterIDTypeDescriptorID) attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributePartsListID)]];
2707+
MTRDeviceDataValueDictionary rootDescriptorPartsListDataValue = [self _cachedAttributeValueForPath:[MTRAttributePath attributePathWithEndpointID:@(kRootEndpointId) clusterID:@(MTRClusterIDTypeDescriptorID) attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributePartsListID)]];
27122708
if (!rootDescriptorPartsListDataValue || ![MTRArrayValueType isEqualToString:rootDescriptorPartsListDataValue[MTRTypeKey]]) {
27132709
return NO;
27142710
}
@@ -2734,7 +2730,7 @@ - (BOOL)_isCachePrimedWithInitialConfigurationData
27342730
MTR_LOG_ERROR("%@ unexpected parts list item value class %@", self, [endpoint class]);
27352731
continue;
27362732
}
2737-
NSDictionary * descriptorDeviceTypeListDataValue = _readCache[[MTRAttributePath attributePathWithEndpointID:endpoint clusterID:@(MTRClusterIDTypeDescriptorID) attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributeDeviceTypeListID)]];
2733+
MTRDeviceDataValueDictionary descriptorDeviceTypeListDataValue = [self _cachedAttributeValueForPath:[MTRAttributePath attributePathWithEndpointID:endpoint clusterID:@(MTRClusterIDTypeDescriptorID) attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributeDeviceTypeListID)]];
27382734
if (![MTRArrayValueType isEqualToString:descriptorDeviceTypeListDataValue[MTRTypeKey]] || !descriptorDeviceTypeListDataValue[MTRValueKey]) {
27392735
return NO;
27402736
}

0 commit comments

Comments
 (0)