Skip to content

Commit 1e40af0

Browse files
Make MTRServerCluster threadsafe.
If two API clients are both touching the same instance of MTRServerCluster on different threads, we should handle that correctly.
1 parent e8cf8f0 commit 1e40af0

File tree

3 files changed

+117
-17
lines changed

3 files changed

+117
-17
lines changed

src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm

+101-5
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
#import "MTRServerAttribute_Internal.h"
2121
#import "MTRServerCluster_Internal.h"
2222
#import "MTRServerEndpoint_Internal.h"
23+
#import "MTRUnfairLock.h"
24+
#import "NSDataSpanConversion.h"
25+
2326
#import <Matter/MTRClusterConstants.h>
2427
#import <Matter/MTRServerCluster.h>
2528

26-
#import "NSDataSpanConversion.h"
27-
2829
#include <app/AttributeAccessInterface.h>
2930
#include <app/clusters/descriptor/descriptor.h>
3031
#include <app/data-model/PreEncodedValue.h>
@@ -71,11 +72,28 @@ @implementation MTRServerCluster {
7172
std::unique_ptr<MTRServerAttributeAccessInterface> _attributeAccessInterface;
7273
// We can't use something like std::unique_ptr<EmberAfAttributeMetadata[]>
7374
// because EmberAfAttributeMetadata does not have a default constructor, so
74-
// we can't alloc and then initializer later.
75+
// we can't alloc and then initialize later.
7576
std::vector<EmberAfAttributeMetadata> _matterAttributeMetadata;
7677

7778
std::unique_ptr<CommandId[]> _matterAcceptedCommandList;
7879
std::unique_ptr<CommandId[]> _matterGeneratedCommandList;
80+
81+
NSSet<MTRAccessGrant *> * _matterAccessGrants;
82+
83+
chip::EndpointId _parentEndpoint;
84+
85+
// _acceptedCommands and _generatedCommands are touched directly by our API
86+
// consumer.
87+
NSArray<NSNumber *> * _acceptedCommands;
88+
NSArray<NSNumber *> * _generatedCommands;
89+
90+
/**
91+
* _lock always protects access to: _accessGrants, _attributes,
92+
* _deviceController, _attributeAccessInterface, _matterAttributeMetadata,
93+
* _matterAccessGrants, _parentEndpoint, _acceptedCommands,
94+
* _generatedCommands, _matterAcceptedCommandList, _matterGeneratedCommandList.
95+
*/
96+
os_unfair_lock _lock;
7997
}
8098

8199
- (nullable instancetype)initWithClusterID:(NSNumber *)clusterID revision:(NSNumber *)revision
@@ -117,6 +135,7 @@ - (instancetype)initInternalWithClusterID:(NSNumber *)clusterID revision:(NSNumb
117135
return nil;
118136
}
119137

138+
_lock = OS_UNFAIR_LOCK_INIT;
120139
_clusterID = [clusterID copy];
121140
_clusterRevision = [revision copy];
122141
_accessGrants = [[NSMutableSet alloc] init];
@@ -141,6 +160,8 @@ - (instancetype)initInternalWithClusterID:(NSNumber *)clusterID revision:(NSNumb
141160

142161
- (void)updateMatterAccessGrants
143162
{
163+
os_unfair_lock_assert_owner(&_lock);
164+
144165
MTRDeviceController * deviceController = _deviceController;
145166
if (deviceController == nil) {
146167
// _matterAccessGrants will be updated when we get bound to a controller.
@@ -149,27 +170,41 @@ - (void)updateMatterAccessGrants
149170

150171
NSSet * grants = [_accessGrants copy];
151172
[deviceController asyncDispatchToMatterQueue:^{
173+
std::lock_guard lock(self->_lock);
152174
self->_matterAccessGrants = grants;
153175
}
154176
errorHandler:nil];
155177
}
156178

157179
- (void)addAccessGrant:(MTRAccessGrant *)accessGrant
158180
{
181+
std::lock_guard lock(self->_lock);
182+
159183
[_accessGrants addObject:accessGrant];
160184

161185
[self updateMatterAccessGrants];
162186
}
163187

164188
- (void)removeAccessGrant:(MTRAccessGrant *)accessGrant;
165189
{
190+
std::lock_guard lock(self->_lock);
191+
166192
[_accessGrants removeObject:accessGrant];
167193

168194
[self updateMatterAccessGrants];
169195
}
170196

197+
- (NSArray<MTRAccessGrant *> *)matterAccessGrants
198+
{
199+
std::lock_guard lock(self->_lock);
200+
201+
return [_matterAccessGrants allObjects];
202+
}
203+
171204
- (BOOL)addAttribute:(MTRServerAttribute *)attribute
172205
{
206+
std::lock_guard lock(self->_lock);
207+
173208
MTRDeviceController * deviceController = _deviceController;
174209
if (deviceController != nil) {
175210
MTR_LOG_ERROR("Cannot add attribute on cluster %llx which is already in use", _clusterID.unsignedLongLongValue);
@@ -213,6 +248,8 @@ - (BOOL)addAttribute:(MTRServerAttribute *)attribute
213248

214249
- (BOOL)associateWithController:(nullable MTRDeviceController *)controller
215250
{
251+
std::lock_guard lock(self->_lock);
252+
216253
MTRDeviceController * existingController = _deviceController;
217254
if (existingController != nil) {
218255
#if MTR_PER_CONTROLLER_STORAGE_ENABLED
@@ -313,14 +350,16 @@ - (BOOL)associateWithController:(nullable MTRDeviceController *)controller
313350

314351
_deviceController = controller;
315352

316-
MTR_LOG_DEFAULT("Associated %@, attribute count %llu, with controller", self,
353+
MTR_LOG_DEFAULT("Associated %@, attribute count %llu, with controller", [self _descriptionWhileLocked],
317354
static_cast<unsigned long long>(attributeCount));
318355

319356
return YES;
320357
}
321358

322359
- (void)invalidate
323360
{
361+
std::lock_guard lock(_lock);
362+
324363
// Undo any work associateWithController did.
325364
for (MTRServerAttribute * attr in _attributes) {
326365
[attr invalidate];
@@ -342,6 +381,8 @@ - (void)registerMatterCluster
342381
{
343382
assertChipStackLockedByCurrentThread();
344383

384+
std::lock_guard lock(_lock);
385+
345386
if (!registerAttributeAccessOverride(_attributeAccessInterface.get())) {
346387
// This should only happen if we somehow managed to register an
347388
// AttributeAccessInterface for the same (endpoint, cluster) pair.
@@ -354,45 +395,93 @@ - (void)unregisterMatterCluster
354395
{
355396
assertChipStackLockedByCurrentThread();
356397

398+
std::lock_guard lock(_lock);
399+
357400
if (_attributeAccessInterface != nullptr) {
358401
unregisterAttributeAccessOverride(_attributeAccessInterface.get());
359402
}
360403
}
361404

362405
- (NSArray<MTRAccessGrant *> *)accessGrants
363406
{
407+
std::lock_guard lock(_lock);
408+
364409
return [_accessGrants allObjects];
365410
}
366411

367412
- (NSArray<MTRServerAttribute *> *)attributes
368413
{
414+
std::lock_guard lock(_lock);
415+
369416
return [_attributes copy];
370417
}
371418

372-
- (void)setParentEndpoint:(EndpointId)endpoint
419+
- (BOOL)addToEndpoint:(chip::EndpointId)endpoint
373420
{
421+
std::lock_guard lock(_lock);
422+
423+
if (_parentEndpoint != kInvalidEndpointId) {
424+
MTR_LOG_ERROR("Cannot add cluster " ChipLogFormatMEI " to endpoint %" PRIu32 "; already added to endpoint %" PRIu32,
425+
ChipLogValueMEI(_clusterID.unsignedLongLongValue), endpoint, _parentEndpoint);
426+
return NO;
427+
}
428+
374429
_parentEndpoint = endpoint;
375430
// Update it on all the attributes, in case the attributes were added to us
376431
// before we were added to the endpoint.
377432
for (MTRServerAttribute * attr in _attributes) {
378433
[attr updateParentCluster:ConcreteClusterPath(endpoint, static_cast<ClusterId>(_clusterID.unsignedLongLongValue))];
379434
}
435+
return YES;
436+
}
437+
438+
- (chip::EndpointId)parentEndpoint
439+
{
440+
std::lock_guard lock(_lock);
441+
return _parentEndpoint;
380442
}
381443

382444
- (Span<const EmberAfAttributeMetadata>)matterAttributeMetadata
383445
{
384446
// This is always called after our _matterAttributeMetadata has been set up
385447
// by associateWithController.
448+
std::lock_guard lock(_lock);
386449
return Span<const EmberAfAttributeMetadata>(_matterAttributeMetadata.data(), _matterAttributeMetadata.size());
387450
}
388451

452+
- (void)setAcceptedCommands:(NSArray<NSNumber *> *)acceptedCommands
453+
{
454+
std::lock_guard lock(_lock);
455+
_acceptedCommands = [acceptedCommands copy];
456+
}
457+
458+
- (NSArray<NSNumber *> *)acceptedCommands
459+
{
460+
std::lock_guard lock(_lock);
461+
return [_acceptedCommands copy];
462+
}
463+
464+
- (void)setGeneratedCommands:(NSArray<NSNumber *> *)generatedCommands
465+
{
466+
std::lock_guard lock(_lock);
467+
_generatedCommands = [generatedCommands copy];
468+
}
469+
470+
- (NSArray<NSNumber *> *)generatedCommands
471+
{
472+
std::lock_guard lock(_lock);
473+
return [_generatedCommands copy];
474+
}
475+
389476
- (CommandId *)matterAcceptedCommands
390477
{
478+
std::lock_guard lock(_lock);
391479
return _matterAcceptedCommandList.get();
392480
}
393481

394482
- (CommandId *)matterGeneratedCommands
395483
{
484+
std::lock_guard lock(_lock);
396485
return _matterGeneratedCommandList.get();
397486
}
398487

@@ -413,6 +502,13 @@ - (CommandId *)matterGeneratedCommands
413502

414503
- (NSString *)description
415504
{
505+
std::lock_guard lock(_lock);
506+
return [self _descriptionWhileLocked];
507+
}
508+
509+
- (NSString *)_descriptionWhileLocked
510+
{
511+
os_unfair_lock_assert_owner(&_lock);
416512
return [NSString stringWithFormat:@"<MTRServerCluster endpoint %u, id " ChipLogFormatMEI ">",
417513
_parentEndpoint, ChipLogValueMEI(_clusterID.unsignedLongLongValue)];
418514
}

src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster_Internal.h

+10-4
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,21 @@ NS_ASSUME_NONNULL_BEGIN
5757
- (void)invalidate;
5858

5959
/**
60-
* The access grants the Matter stack can observe. Only modified while in
61-
* Initializing state or on the Matter queue.
60+
* Add the cluster to an endpoint with the given endpoint ID. Will return NO
61+
* if the cluster is already added to an endpoint.
6262
*/
63-
@property (nonatomic, strong, readonly) NSSet<MTRAccessGrant *> * matterAccessGrants;
63+
- (BOOL)addToEndpoint:(chip::EndpointId)endpoint;
64+
65+
/**
66+
* The access grants the Matter stack can observe. Only modified while
67+
* associating with a controller or on the Matter queue.
68+
*/
69+
@property (nonatomic, copy, readonly) NSArray<MTRAccessGrant *> * matterAccessGrants;
6470

6571
/**
6672
* parentEndpoint will be kInvalidEndpointId until the cluster is added to an endpoint.
6773
*/
68-
@property (nonatomic, assign) chip::EndpointId parentEndpoint;
74+
@property (nonatomic, assign, readonly) chip::EndpointId parentEndpoint;
6975

7076
/**
7177
* The attribute metadata for the cluster. Only valid after associateWithController: has succeeded.

src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm

+6-8
Original file line numberDiff line numberDiff line change
@@ -154,20 +154,18 @@ - (BOOL)addServerCluster:(MTRServerCluster *)serverCluster
154154
return NO;
155155
}
156156

157-
if (serverCluster.parentEndpoint != kInvalidEndpointId) {
158-
MTR_LOG_ERROR("Cannot add cluster to endpoint %llu; already added to endpoint %" PRIu32, _endpointID.unsignedLongLongValue, serverCluster.parentEndpoint);
159-
return NO;
160-
}
161-
162157
for (MTRServerCluster * existingCluster in _serverClusters) {
163158
if ([existingCluster.clusterID isEqual:serverCluster.clusterID]) {
164-
MTR_LOG_ERROR("Cannot add second cluster with ID %llx on endpoint %llu", serverCluster.clusterID.unsignedLongLongValue, _endpointID.unsignedLongLongValue);
159+
MTR_LOG_ERROR("Cannot add second cluster with ID " ChipLogFormatMEI " on endpoint %llu", ChipLogValueMEI(serverCluster.clusterID.unsignedLongLongValue), _endpointID.unsignedLongLongValue);
165160
return NO;
166161
}
167162
}
168163

164+
if (![serverCluster addToEndpoint:static_cast<EndpointId>(_endpointID.unsignedLongLongValue)]) {
165+
return NO;
166+
}
169167
[_serverClusters addObject:serverCluster];
170-
serverCluster.parentEndpoint = static_cast<EndpointId>(_endpointID.unsignedLongLongValue);
168+
171169
return YES;
172170
}
173171

@@ -401,7 +399,7 @@ - (void)invalidate
401399
NSMutableArray<MTRAccessGrant *> * grants = [[_matterAccessGrants allObjects] mutableCopy];
402400
for (MTRServerCluster * cluster in _serverClusters) {
403401
if ([cluster.clusterID isEqual:clusterID]) {
404-
[grants addObjectsFromArray:[cluster.matterAccessGrants allObjects]];
402+
[grants addObjectsFromArray:cluster.matterAccessGrants];
405403
}
406404
}
407405

0 commit comments

Comments
 (0)