19
19
// Importing MTRBaseDevice.h for the MTRAttributePath class. Needs to change when https://github.com/project-chip/connectedhomeip/issues/31247 is fixed.
20
20
#import " MTRBaseDevice.h"
21
21
#import " MTRLogging_Internal.h"
22
+ #import " MTRUnfairLock.h"
22
23
23
24
#include < lib/core/CASEAuthTag.h>
24
25
#include < lib/core/NodeId.h>
@@ -111,6 +112,11 @@ @implementation MTRDeviceControllerDataStore {
111
112
__weak MTRDeviceController * _controller;
112
113
// Array of nodes with resumption info, oldest-stored first.
113
114
NSMutableArray <NSNumber *> * _nodesWithResumptionInfo;
115
+ // Array of nodes with attribute info.
116
+ NSMutableArray <NSNumber *> * _nodesWithAttributeInfo;
117
+ // Lock protecting access to the _nodesWithAttributeInfo and
118
+ // _nodesWithResumptionInfo arrays.
119
+ os_unfair_lock _nodeArrayLock;
114
120
}
115
121
116
122
- (nullable instancetype )initWithController : (MTRDeviceController *)controller
@@ -124,8 +130,10 @@ - (nullable instancetype)initWithController:(MTRDeviceController *)controller
124
130
_controller = controller;
125
131
_storageDelegate = storageDelegate;
126
132
_storageDelegateQueue = storageDelegateQueue;
133
+ _nodeArrayLock = OS_UNFAIR_LOCK_INIT;
127
134
128
135
__block id resumptionNodeList;
136
+ __block NSArray <NSNumber *> * nodesWithAttributeInfo;
129
137
dispatch_sync (_storageDelegateQueue, ^{
130
138
@autoreleasepool {
131
139
// NOTE: controller, not our weak ref, since we know it's still
@@ -134,6 +142,8 @@ - (nullable instancetype)initWithController:(MTRDeviceController *)controller
134
142
valueForKey: sResumptionNodeListKey
135
143
securityLevel: MTRStorageSecurityLevelSecure
136
144
sharingType: MTRStorageSharingTypeNotShared];
145
+
146
+ nodesWithAttributeInfo = [self _fetchNodeIndex ];
137
147
}
138
148
});
139
149
if (resumptionNodeList != nil ) {
@@ -152,6 +162,12 @@ - (nullable instancetype)initWithController:(MTRDeviceController *)controller
152
162
_nodesWithResumptionInfo = [[NSMutableArray alloc ] init ];
153
163
}
154
164
165
+ if (nodesWithAttributeInfo != nil ) {
166
+ _nodesWithAttributeInfo = [nodesWithAttributeInfo mutableCopy ];
167
+ } else {
168
+ _nodesWithAttributeInfo = [[NSMutableArray alloc ] init ];
169
+ }
170
+
155
171
return self;
156
172
}
157
173
@@ -196,6 +212,8 @@ - (void)storeResumptionInfo:(MTRCASESessionResumptionInfo *)resumptionInfo
196
212
removeValueForKey: ResumptionByResumptionIDKey (oldInfo.resumptionID)
197
213
securityLevel: MTRStorageSecurityLevelSecure
198
214
sharingType: MTRStorageSharingTypeNotShared];
215
+
216
+ std::lock_guard lock (self->_nodeArrayLock );
199
217
[_nodesWithResumptionInfo removeObject: resumptionInfo.nodeID];
200
218
}
201
219
@@ -211,9 +229,14 @@ - (void)storeResumptionInfo:(MTRCASESessionResumptionInfo *)resumptionInfo
211
229
sharingType: MTRStorageSharingTypeNotShared];
212
230
213
231
// Update our resumption info node list.
214
- [_nodesWithResumptionInfo addObject: resumptionInfo.nodeID];
232
+ NSArray <NSNumber *> * valueToStore;
233
+ {
234
+ std::lock_guard lock (self->_nodeArrayLock );
235
+ [_nodesWithResumptionInfo addObject: resumptionInfo.nodeID];
236
+ valueToStore = [_nodesWithResumptionInfo copy ];
237
+ }
215
238
[_storageDelegate controller: controller
216
- storeValue: [_nodesWithResumptionInfo copy ]
239
+ storeValue: valueToStore
217
240
forKey: sResumptionNodeListKey
218
241
securityLevel: MTRStorageSecurityLevelSecure
219
242
sharingType: MTRStorageSharingTypeNotShared];
@@ -226,7 +249,9 @@ - (void)clearAllResumptionInfo
226
249
VerifyOrReturn (controller != nil ); // No way to call delegate without controller.
227
250
228
251
// Can we do less dispatch? We would need to have a version of
229
- // _findResumptionInfoWithKey that assumes we are already on the right queue.
252
+ // _findResumptionInfoWithKey that assumes we are already on the right
253
+ // queue.
254
+ std::lock_guard lock (_nodeArrayLock);
230
255
for (NSNumber * nodeID in _nodesWithResumptionInfo) {
231
256
[self _clearResumptionInfoForNodeID: nodeID controller: controller];
232
257
}
@@ -240,6 +265,8 @@ - (void)clearResumptionInfoForNodeID:(NSNumber *)nodeID
240
265
VerifyOrReturn (controller != nil ); // No way to call delegate without controller.
241
266
242
267
[self _clearResumptionInfoForNodeID: nodeID controller: controller];
268
+
269
+ std::lock_guard lock (_nodeArrayLock);
243
270
[_nodesWithResumptionInfo removeObject: nodeID];
244
271
}
245
272
@@ -588,6 +615,20 @@ - (void)unitTestPruneEmptyStoredClusterDataBranches
588
615
[self _pruneEmptyStoredClusterDataBranches ];
589
616
});
590
617
}
618
+
619
+ - (void )unitTestRereadNodeIndex
620
+ {
621
+ dispatch_sync (_storageDelegateQueue, ^{
622
+ auto * newIndex = [self _fetchNodeIndex ];
623
+
624
+ std::lock_guard lock (self->_nodeArrayLock );
625
+ if (newIndex != nil ) {
626
+ self->_nodesWithAttributeInfo = [newIndex mutableCopy ];
627
+ } else {
628
+ self->_nodesWithAttributeInfo = [[NSMutableArray alloc ] init ];
629
+ }
630
+ });
631
+ }
591
632
#endif
592
633
593
634
- (void )_pruneEmptyStoredClusterDataBranches
@@ -596,9 +637,8 @@ - (void)_pruneEmptyStoredClusterDataBranches
596
637
597
638
NSUInteger storeFailures = 0 ;
598
639
599
- // Fetch node index
600
- NSArray <NSNumber *> * nodeIndex = [self _fetchNodeIndex ];
601
- NSMutableArray <NSNumber *> * nodeIndexCopy = [nodeIndex mutableCopy ];
640
+ std::lock_guard lock (self->_nodeArrayLock );
641
+ NSArray <NSNumber *> * nodeIndex = [_nodesWithAttributeInfo copy ];
602
642
603
643
for (NSNumber * nodeID in nodeIndex) {
604
644
// Fetch endpoint index
@@ -638,7 +678,7 @@ - (void)_pruneEmptyStoredClusterDataBranches
638
678
if (endpointIndexCopy.count ) {
639
679
success = [self _storeEndpointIndex: endpointIndexCopy forNodeID: nodeID];
640
680
} else {
641
- [nodeIndexCopy removeObject: nodeID];
681
+ [_nodesWithAttributeInfo removeObject: nodeID];
642
682
success = [self _deleteEndpointIndexForNodeID: nodeID];
643
683
}
644
684
if (!success) {
@@ -648,16 +688,16 @@ - (void)_pruneEmptyStoredClusterDataBranches
648
688
}
649
689
}
650
690
651
- if (nodeIndex.count != nodeIndexCopy .count ) {
691
+ if (nodeIndex.count != _nodesWithAttributeInfo .count ) {
652
692
BOOL success;
653
- if (nodeIndexCopy .count ) {
654
- success = [self _storeNodeIndex: nodeIndexCopy ];
693
+ if (_nodesWithAttributeInfo .count ) {
694
+ success = [self _storeNodeIndex: _nodesWithAttributeInfo ];
655
695
} else {
656
696
success = [self _deleteNodeIndex ];
657
697
}
658
698
if (!success) {
659
699
storeFailures++;
660
- MTR_LOG_ERROR (" Store failed in _pruneEmptyStoredClusterDataBranches for nodeIndex (%lu)" , static_cast <unsigned long >(nodeIndexCopy .count ));
700
+ MTR_LOG_ERROR (" Store failed in _pruneEmptyStoredClusterDataBranches for nodeIndex (%lu)" , static_cast <unsigned long >(_nodesWithAttributeInfo .count ));
661
701
}
662
702
}
663
703
@@ -713,18 +753,19 @@ - (void)clearStoredClusterDataForNodeID:(NSNumber *)nodeID
713
753
{
714
754
dispatch_async (_storageDelegateQueue, ^{
715
755
[self _clearStoredClusterDataForNodeID: nodeID];
716
- NSArray <NSNumber *> * nodeIndex = [self _fetchNodeIndex ];
717
- NSMutableArray <NSNumber *> * nodeIndexCopy = [nodeIndex mutableCopy ];
718
- [nodeIndexCopy removeObject: nodeID];
719
- if (nodeIndex.count != nodeIndexCopy.count ) {
756
+
757
+ std::lock_guard lock (self->_nodeArrayLock );
758
+ auto oldCount = self->_nodesWithAttributeInfo .count ;
759
+ [self ->_nodesWithAttributeInfo removeObject: nodeID];
760
+ if (self->_nodesWithAttributeInfo .count != oldCount) {
720
761
BOOL success;
721
- if (nodeIndexCopy .count ) {
722
- success = [self _storeNodeIndex: nodeIndexCopy ];
762
+ if (self-> _nodesWithAttributeInfo .count ) {
763
+ success = [self _storeNodeIndex: self ->_nodesWithAttributeInfo ];
723
764
} else {
724
765
success = [self _deleteNodeIndex ];
725
766
}
726
767
if (!success) {
727
- MTR_LOG_ERROR (" Store failed in clearStoredAttributesForNodeID for nodeIndex (%lu)" , static_cast <unsigned long >(nodeIndexCopy .count ));
768
+ MTR_LOG_ERROR (" Store failed in clearStoredAttributesForNodeID for nodeIndex (%lu)" , static_cast <unsigned long >(self-> _nodesWithAttributeInfo .count ));
728
769
}
729
770
}
730
771
});
@@ -804,12 +845,12 @@ - (void)clearAllStoredClusterData
804
845
{
805
846
dispatch_async (_storageDelegateQueue, ^{
806
847
// Fetch node index
807
- NSArray <NSNumber *> * nodeIndex = [self _fetchNodeIndex ];
808
-
809
- for (NSNumber * nodeID in nodeIndex) {
848
+ std::lock_guard lock (self->_nodeArrayLock );
849
+ for (NSNumber * nodeID in self->_nodesWithAttributeInfo ) {
810
850
[self _clearStoredClusterDataForNodeID: nodeID];
811
851
}
812
852
853
+ [self ->_nodesWithAttributeInfo removeAllObjects ];
813
854
BOOL success = [self _deleteNodeIndex ];
814
855
if (!success) {
815
856
MTR_LOG_ERROR (" Delete failed for nodeIndex" );
@@ -826,14 +867,13 @@ - (void)clearAllStoredClusterData
826
867
827
868
__block NSMutableDictionary <MTRClusterPath *, MTRDeviceClusterData *> * clusterDataToReturn = nil ;
828
869
dispatch_sync (_storageDelegateQueue, ^{
829
- // Fetch node index
830
- NSArray <NSNumber *> * nodeIndex = [self _fetchNodeIndex ];
870
+ std::lock_guard lock (self->_nodeArrayLock );
831
871
832
872
#if ATTRIBUTE_CACHE_VERBOSE_LOGGING
833
- MTR_LOG (" Fetch got %lu values for nodeIndex" , static_cast <unsigned long >(nodeIndex .count ));
873
+ MTR_LOG (" Fetch got %lu values for nodeIndex" , static_cast <unsigned long >(self-> _nodesWithAttributeInfo .count ));
834
874
#endif
835
875
836
- if (![nodeIndex containsObject: nodeID]) {
876
+ if (![self ->_nodesWithAttributeInfo containsObject: nodeID]) {
837
877
// Sanity check and delete if nodeID exists in index
838
878
NSArray <NSNumber *> * endpointIndex = [self _fetchEndpointIndexForNodeID: nodeID];
839
879
if (endpointIndex) {
@@ -1086,14 +1126,13 @@ - (void)storeClusterData:(NSDictionary<MTRClusterPath *, MTRDeviceClusterData *>
1086
1126
}
1087
1127
1088
1128
// Check if node index needs updating / creation
1089
- NSArray <NSNumber *> * nodeIndex = [self _fetchNodeIndex ];
1090
1129
NSArray <NSNumber *> * nodeIndexToStore = nil ;
1091
- if (!nodeIndex) {
1092
- // Ensure node index exists
1093
- MTR_LOG ( " No entry found for for nodeIndex - creating for node 0x%016llX " , nodeID. unsignedLongLongValue );
1094
- nodeIndexToStore = [ NSArray arrayWithObject : nodeID];
1095
- } else if (![nodeIndex containsObject: nodeID]) {
1096
- nodeIndexToStore = [nodeIndex arrayByAddingObject: nodeID];
1130
+ {
1131
+ std::lock_guard lock (self-> _nodeArrayLock );
1132
+ if (![ self ->_nodesWithAttributeInfo containsObject: nodeID]) {
1133
+ [ self ->_nodesWithAttributeInfo addObject : nodeID];
1134
+ nodeIndexToStore = [ self ->_nodesWithAttributeInfo copy ];
1135
+ }
1097
1136
}
1098
1137
1099
1138
if (nodeIndexToStore) {
@@ -1206,6 +1245,27 @@ - (void)synchronouslyPerformBlock:(void (^_Nullable)(void))block
1206
1245
});
1207
1246
}
1208
1247
1248
+ - (NSArray <NSNumber *> *)nodesWithStoredData
1249
+ {
1250
+ // We have three types of stored data:
1251
+ //
1252
+ // 1) Attribute data
1253
+ // 2) Session resumption data
1254
+ // 3) Device data.
1255
+ //
1256
+ // Items 1 and 2 come with node indices. Item 3 does not, but in practice
1257
+ // we should have device data if and only if we have attribute data for that
1258
+ // node ID, barring odd error conditions.
1259
+ //
1260
+ // TODO: Consider changing how we store device data so we can easily recover
1261
+ // the relevant set of node IDs.
1262
+ NSMutableSet <NSNumber *> * nodeSet = [NSMutableSet set ];
1263
+ std::lock_guard lock (_nodeArrayLock);
1264
+ [nodeSet addObjectsFromArray: _nodesWithResumptionInfo];
1265
+ [nodeSet addObjectsFromArray: _nodesWithAttributeInfo];
1266
+ return [nodeSet allObjects ];
1267
+ }
1268
+
1209
1269
@end
1210
1270
1211
1271
@implementation MTRCASESessionResumptionInfo
0 commit comments