Skip to content

Commit 1866812

Browse files
Move delegate management into shared MTRDevice super-class. (#35084)
* Move delegate management into shared MTRDevice super-class. MTRDevice_XPC and MTRDevice_Concrete can then share that code. * Address review comments, fix TAPI build.
1 parent b367512 commit 1866812

File tree

4 files changed

+114
-351
lines changed

4 files changed

+114
-351
lines changed

src/darwin/Framework/CHIP/MTRDevice.mm

+20-110
Original file line numberDiff line numberDiff line change
@@ -64,43 +64,6 @@
6464
// Disabling pending crashes
6565
#define ENABLE_CONNECTIVITY_MONITORING 0
6666

67-
// Consider moving utility classes to their own file
68-
#pragma mark - Utility Classes
69-
70-
// container of MTRDevice delegate weak reference, its queue, and its interested paths for attribute reports
71-
MTR_DIRECT_MEMBERS
72-
@interface MTRDeviceDelegateInfo : NSObject {
73-
@private
74-
void * _delegatePointerValue;
75-
__weak id _delegate;
76-
dispatch_queue_t _queue;
77-
NSArray * _Nullable _interestedPathsForAttributes;
78-
NSArray * _Nullable _interestedPathsForEvents;
79-
}
80-
81-
// Array of interested cluster paths, attribute paths, or endpointID, for attribute report filtering.
82-
@property (readonly, nullable) NSArray * interestedPathsForAttributes;
83-
84-
// Array of interested cluster paths, attribute paths, or endpointID, for event report filtering.
85-
@property (readonly, nullable) NSArray * interestedPathsForEvents;
86-
87-
// Expose delegate
88-
@property (readonly) id delegate;
89-
90-
// Pointer value for logging purpose only
91-
@property (readonly) void * delegatePointerValue;
92-
93-
- (instancetype)initWithDelegate:(id<MTRDeviceDelegate>)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents;
94-
95-
// Returns YES if delegate and queue are both non-null, and the block is scheduled to run.
96-
- (BOOL)callDelegateWithBlock:(void (^)(id<MTRDeviceDelegate>))block;
97-
98-
#ifdef DEBUG
99-
// Only used for unit test purposes - normal delegate should not expect or handle being called back synchronously.
100-
- (BOOL)callDelegateSynchronouslyWithBlock:(void (^)(id<MTRDeviceDelegate>))block;
101-
#endif
102-
@end
103-
10467
@implementation MTRDeviceDelegateInfo
10568
- (instancetype)initWithDelegate:(id<MTRDeviceDelegate>)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents
10669
{
@@ -371,7 +334,6 @@ - (BOOL)isEqual:(id)object
371334
#define MTRDEVICE_SUBSCRIPTION_LATENCY_NEW_VALUE_WEIGHT (1.0 / 3.0)
372335

373336
@interface MTRDevice ()
374-
@property (nonatomic, readonly) os_unfair_lock lock; // protects the caches and device state
375337
// protects against concurrent time updates by guarding timeUpdateScheduled flag which manages time updates scheduling,
376338
// and protects device calls to setUTCTime and setDSTOffset. This can't just be replaced with "lock", because the time
377339
// update code calls public APIs like readAttributeWithEndpointID:.. (which attempt to take "lock") while holding
@@ -502,8 +464,6 @@ @implementation MTRDevice {
502464
// System time change observer reference
503465
id _systemTimeChangeObserverToken;
504466

505-
NSMutableSet<MTRDeviceDelegateInfo *> * _delegates;
506-
507467
// Protects mutable state used by our description getter. This is a separate lock from "lock"
508468
// so that we don't need to worry about getting our description while holding "lock" (e.g due to
509469
// logging self). This lock _must_ be held narrowly, with no other lock acquisitions allowed
@@ -526,10 +486,11 @@ @implementation MTRDevice {
526486
NSDate * _Nullable _lastSubscriptionFailureTimeForDescription;
527487
}
528488

529-
- (instancetype)initForSubclasses
489+
- (instancetype)initForSubclassesWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller
530490
{
531491
if (self = [super init]) {
532-
// nothing, as superclass of MTRDevice is NSObject
492+
_lock = OS_UNFAIR_LOCK_INIT;
493+
_delegates = [NSMutableSet set];
533494
}
534495

535496
return self;
@@ -875,6 +836,8 @@ - (BOOL)_subscriptionsAllowed
875836
{
876837
os_unfair_lock_assert_owner(&self->_lock);
877838

839+
// TODO: XPC: This function and all its callsites should go away from this class.
840+
878841
// We should not allow a subscription for device controllers over XPC.
879842
return ![_deviceController isKindOfClass:MTRDeviceControllerOverXPC.class];
880843
}
@@ -923,34 +886,14 @@ - (void)_addDelegate:(id<MTRDeviceDelegate>)delegate queue:(dispatch_queue_t)que
923886
[_delegates addObject:newDelegateInfo];
924887
MTR_LOG("%@ added delegate info %@", self, newDelegateInfo);
925888

926-
__block BOOL shouldSetUpSubscription = [self _subscriptionsAllowed];
927-
928-
// For unit testing only. If this ever changes to not being for unit testing purposes,
929-
// we would need to move the code outside of where we acquire the lock above.
930-
#ifdef DEBUG
931-
[self _callFirstDelegateSynchronouslyWithBlock:^(id testDelegate) {
932-
if ([testDelegate respondsToSelector:@selector(unitTestShouldSetUpSubscriptionForDevice:)]) {
933-
shouldSetUpSubscription = [testDelegate unitTestShouldSetUpSubscriptionForDevice:self];
934-
}
935-
}];
936-
#endif
889+
// Call hook to allow subclasses to act on delegate addition.
890+
[self _delegateAdded];
891+
}
937892

938-
if (shouldSetUpSubscription) {
939-
MTR_LOG("%@ - starting subscription setup", self);
940-
// Record the time of first addDelegate call that triggers initial subscribe, and do not reset this value on subsequent addDelegate calls
941-
if (!_initialSubscribeStart) {
942-
_initialSubscribeStart = [NSDate now];
943-
}
944-
if ([self _deviceUsesThread]) {
945-
MTR_LOG(" => %@ - device is a thread device, scheduling in pool", self);
946-
[self _scheduleSubscriptionPoolWork:^{
947-
std::lock_guard lock(self->_lock);
948-
[self _setupSubscriptionWithReason:@"delegate is set and scheduled subscription is happening"];
949-
} inNanoseconds:0 description:@"MTRDevice setDelegate first subscription"];
950-
} else {
951-
[self _setupSubscriptionWithReason:@"delegate is set and subscription is needed"];
952-
}
953-
}
893+
- (void)_delegateAdded
894+
{
895+
// Nothing to do; this is a hook for subclasses. If that ever changes for
896+
// some reason, subclasses need to start calling this hook on their super.
954897
}
955898

956899
- (void)removeDelegate:(id<MTRDeviceDelegate>)delegate
@@ -962,7 +905,10 @@ - (void)removeDelegate:(id<MTRDeviceDelegate>)delegate
962905
NSMutableSet<MTRDeviceDelegateInfo *> * delegatesToRemove = [NSMutableSet set];
963906
[self _iterateDelegatesWithBlock:^(MTRDeviceDelegateInfo * delegateInfo) {
964907
id<MTRDeviceDelegate> strongDelegate = delegateInfo.delegate;
965-
if (strongDelegate == delegate) {
908+
if (!strongDelegate) {
909+
[delegatesToRemove addObject:delegateInfo];
910+
MTR_LOG("%@ removing delegate info for nil delegate %p", self, delegateInfo.delegatePointerValue);
911+
} else if (strongDelegate == delegate) {
966912
[delegatesToRemove addObject:delegateInfo];
967913
MTR_LOG("%@ removing delegate info %@ for %p", self, delegateInfo, delegate);
968914
}
@@ -976,45 +922,9 @@ - (void)removeDelegate:(id<MTRDeviceDelegate>)delegate
976922

977923
- (void)invalidate
978924
{
979-
MTR_LOG("%@ invalidate", self);
980-
981-
[_asyncWorkQueue invalidate];
982-
983-
os_unfair_lock_lock(&self->_timeSyncLock);
984-
_timeUpdateScheduled = NO;
985-
os_unfair_lock_unlock(&self->_timeSyncLock);
986-
987-
os_unfair_lock_lock(&self->_lock);
988-
989-
_state = MTRDeviceStateUnknown;
925+
std::lock_guard lock(_lock);
990926

991927
[_delegates removeAllObjects];
992-
993-
// Make sure we don't try to resubscribe if we have a pending resubscribe
994-
// attempt, since we now have no delegate.
995-
_reattemptingSubscription = NO;
996-
997-
[_deviceController asyncDispatchToMatterQueue:^{
998-
MTR_LOG("%@ invalidate disconnecting ReadClient and SubscriptionCallback", self);
999-
1000-
// Destroy the read client and callback (has to happen on the Matter
1001-
// queue, to avoid deleting objects that are being referenced), to
1002-
// tear down the subscription. We will get no more callbacks from
1003-
// the subscription after this point.
1004-
std::lock_guard lock(self->_lock);
1005-
self->_currentReadClient = nullptr;
1006-
if (self->_currentSubscriptionCallback) {
1007-
delete self->_currentSubscriptionCallback;
1008-
}
1009-
self->_currentSubscriptionCallback = nullptr;
1010-
1011-
[self _changeInternalState:MTRInternalDeviceStateUnsubscribed];
1012-
}
1013-
errorHandler:nil];
1014-
1015-
[self _stopConnectivityMonitoring];
1016-
1017-
os_unfair_lock_unlock(&self->_lock);
1018928
}
1019929

1020930
- (void)nodeMayBeAdvertisingOperational
@@ -1145,8 +1055,7 @@ - (BOOL)_delegateExists
11451055
return [self _iterateDelegatesWithBlock:nil];
11461056
}
11471057

1148-
// Returns YES if any non-null delegates were found
1149-
- (BOOL)_iterateDelegatesWithBlock:(void(NS_NOESCAPE ^)(MTRDeviceDelegateInfo * delegateInfo)_Nullable)block
1058+
- (BOOL)_iterateDelegatesWithBlock:(void(NS_NOESCAPE ^ _Nullable)(MTRDeviceDelegateInfo * delegateInfo))block
11501059
{
11511060
os_unfair_lock_assert_owner(&self->_lock);
11521061

@@ -1198,7 +1107,6 @@ - (BOOL)_callDelegatesWithBlock:(void (^)(id<MTRDeviceDelegate> delegate))block
11981107

11991108
#ifdef DEBUG
12001109
// Only used for unit test purposes - normal delegate should not expect or handle being called back synchronously
1201-
// Returns YES if a delegate is called
12021110
- (void)_callFirstDelegateSynchronouslyWithBlock:(void (^)(id<MTRDeviceDelegate> delegate))block
12031111
{
12041112
os_unfair_lock_assert_owner(&self->_lock);
@@ -2496,6 +2404,8 @@ - (void)unitTestResetSubscription
24962404
// assume lock is held
24972405
- (void)_setupSubscriptionWithReason:(NSString *)reason
24982406
{
2407+
// TODO: XPC: This is not really called anymore in this class. Should
2408+
// remove this function and anything only reachable from it.
24992409
os_unfair_lock_assert_owner(&self->_lock);
25002410

25012411
if (![self _subscriptionsAllowed]) {

0 commit comments

Comments
 (0)