64
64
// Disabling pending crashes
65
65
#define ENABLE_CONNECTIVITY_MONITORING 0
66
66
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
-
104
67
@implementation MTRDeviceDelegateInfo
105
68
- (instancetype)initWithDelegate:(id<MTRDeviceDelegate>)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents
106
69
{
@@ -371,7 +334,6 @@ - (BOOL)isEqual:(id)object
371
334
#define MTRDEVICE_SUBSCRIPTION_LATENCY_NEW_VALUE_WEIGHT (1.0 / 3.0)
372
335
373
336
@interface MTRDevice ()
374
- @property (nonatomic, readonly) os_unfair_lock lock; // protects the caches and device state
375
337
// protects against concurrent time updates by guarding timeUpdateScheduled flag which manages time updates scheduling,
376
338
// and protects device calls to setUTCTime and setDSTOffset. This can't just be replaced with "lock", because the time
377
339
// update code calls public APIs like readAttributeWithEndpointID:.. (which attempt to take "lock") while holding
@@ -502,8 +464,6 @@ @implementation MTRDevice {
502
464
// System time change observer reference
503
465
id _systemTimeChangeObserverToken;
504
466
505
- NSMutableSet<MTRDeviceDelegateInfo *> * _delegates;
506
-
507
467
// Protects mutable state used by our description getter. This is a separate lock from "lock"
508
468
// so that we don't need to worry about getting our description while holding "lock" (e.g due to
509
469
// logging self). This lock _must_ be held narrowly, with no other lock acquisitions allowed
@@ -526,10 +486,11 @@ @implementation MTRDevice {
526
486
NSDate * _Nullable _lastSubscriptionFailureTimeForDescription;
527
487
}
528
488
529
- - (instancetype)initForSubclasses
489
+ - (instancetype)initForSubclassesWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller
530
490
{
531
491
if (self = [super init]) {
532
- // nothing, as superclass of MTRDevice is NSObject
492
+ _lock = OS_UNFAIR_LOCK_INIT;
493
+ _delegates = [NSMutableSet set];
533
494
}
534
495
535
496
return self;
@@ -875,6 +836,8 @@ - (BOOL)_subscriptionsAllowed
875
836
{
876
837
os_unfair_lock_assert_owner(&self->_lock);
877
838
839
+ // TODO: XPC: This function and all its callsites should go away from this class.
840
+
878
841
// We should not allow a subscription for device controllers over XPC.
879
842
return ![_deviceController isKindOfClass:MTRDeviceControllerOverXPC.class];
880
843
}
@@ -923,34 +886,14 @@ - (void)_addDelegate:(id<MTRDeviceDelegate>)delegate queue:(dispatch_queue_t)que
923
886
[_delegates addObject:newDelegateInfo];
924
887
MTR_LOG("%@ added delegate info %@", self, newDelegateInfo);
925
888
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
+ }
937
892
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.
954
897
}
955
898
956
899
- (void)removeDelegate:(id<MTRDeviceDelegate>)delegate
@@ -962,7 +905,10 @@ - (void)removeDelegate:(id<MTRDeviceDelegate>)delegate
962
905
NSMutableSet<MTRDeviceDelegateInfo *> * delegatesToRemove = [NSMutableSet set];
963
906
[self _iterateDelegatesWithBlock:^(MTRDeviceDelegateInfo * delegateInfo) {
964
907
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) {
966
912
[delegatesToRemove addObject:delegateInfo];
967
913
MTR_LOG("%@ removing delegate info %@ for %p", self, delegateInfo, delegate);
968
914
}
@@ -976,45 +922,9 @@ - (void)removeDelegate:(id<MTRDeviceDelegate>)delegate
976
922
977
923
- (void)invalidate
978
924
{
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);
990
926
991
927
[_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);
1018
928
}
1019
929
1020
930
- (void)nodeMayBeAdvertisingOperational
@@ -1145,8 +1055,7 @@ - (BOOL)_delegateExists
1145
1055
return [self _iterateDelegatesWithBlock:nil];
1146
1056
}
1147
1057
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
1150
1059
{
1151
1060
os_unfair_lock_assert_owner(&self->_lock);
1152
1061
@@ -1198,7 +1107,6 @@ - (BOOL)_callDelegatesWithBlock:(void (^)(id<MTRDeviceDelegate> delegate))block
1198
1107
1199
1108
#ifdef DEBUG
1200
1109
// 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
1202
1110
- (void)_callFirstDelegateSynchronouslyWithBlock:(void (^)(id<MTRDeviceDelegate> delegate))block
1203
1111
{
1204
1112
os_unfair_lock_assert_owner(&self->_lock);
@@ -2496,6 +2404,8 @@ - (void)unitTestResetSubscription
2496
2404
// assume lock is held
2497
2405
- (void)_setupSubscriptionWithReason:(NSString *)reason
2498
2406
{
2407
+ // TODO: XPC: This is not really called anymore in this class. Should
2408
+ // remove this function and anything only reachable from it.
2499
2409
os_unfair_lock_assert_owner(&self->_lock);
2500
2410
2501
2411
if (![self _subscriptionsAllowed]) {
0 commit comments