Skip to content

Commit 19839d0

Browse files
jtung-applepull[bot]
authored andcommitted
[Darwin] Issue 26012 - MTRDevice should stream subscription reports (#29358)
* [Darwin] Issue 26012 - MTRDevice should stream subscription reports * Changed implementation to do per-packet batching * Remove test/redundant code * Added unit test protocol comment for readability * Address review comment
1 parent 3dfc703 commit 19839d0

File tree

4 files changed

+72
-5
lines changed

4 files changed

+72
-5
lines changed

src/darwin/Framework/CHIP/MTRBaseSubscriptionCallback.h

+10
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ class MTRBaseSubscriptionCallback : public chip::app::ClusterStateCache::Callbac
8484
// Ensure we release the ReadClient before we tear down anything else,
8585
// so it can call our OnDeallocatePaths properly.
8686
mReadClient = nullptr;
87+
88+
// Make sure the block isn't run after object destruction
89+
if (mInterimReportBlock) {
90+
dispatch_block_cancel(mInterimReportBlock);
91+
}
8792
}
8893

8994
chip::app::BufferedReadCallback & GetBufferedCallback() { return mBufferedReadAdapter; }
@@ -103,6 +108,10 @@ class MTRBaseSubscriptionCallback : public chip::app::ClusterStateCache::Callbac
103108
// be immediately followed by OnDone and we want to do the deletion there.
104109
void ReportError(CHIP_ERROR aError, bool aCancelSubscription = true);
105110

111+
// Called at attribute/event report time to queue a block to report on the Matter queue so that for multi-packet reports, this
112+
// block is run and reports in batch. No-op if the block is already queued.
113+
void QueueInterimReport();
114+
106115
private:
107116
void OnReportBegin() override;
108117

@@ -166,6 +175,7 @@ class MTRBaseSubscriptionCallback : public chip::app::ClusterStateCache::Callbac
166175
std::unique_ptr<chip::app::ClusterStateCache> mClusterStateCache;
167176
bool mHaveQueuedDeletion = false;
168177
OnDoneHandler _Nullable mOnDoneHandler = nil;
178+
dispatch_block_t mInterimReportBlock = nil;
169179
};
170180

171181
NS_ASSUME_NONNULL_END

src/darwin/Framework/CHIP/MTRBaseSubscriptionCallback.mm

+22
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,35 @@
4646
if (attributeCallback != nil && attributeReports.count) {
4747
attributeCallback(attributeReports);
4848
}
49+
4950
if (eventCallback != nil && eventReports.count) {
5051
eventCallback(eventReports);
5152
}
5253
}
5354

55+
void MTRBaseSubscriptionCallback::QueueInterimReport()
56+
{
57+
if (mInterimReportBlock) {
58+
return;
59+
}
60+
61+
mInterimReportBlock = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{
62+
mInterimReportBlock = nil;
63+
ReportData();
64+
// Allocate reports arrays to continue accumulation
65+
mAttributeReports = [NSMutableArray new];
66+
mEventReports = [NSMutableArray new];
67+
});
68+
69+
dispatch_async(DeviceLayer::PlatformMgrImpl().GetWorkQueue(), mInterimReportBlock);
70+
}
71+
5472
void MTRBaseSubscriptionCallback::OnReportEnd()
5573
{
74+
if (mInterimReportBlock) {
75+
dispatch_block_cancel(mInterimReportBlock);
76+
mInterimReportBlock = nil;
77+
}
5678
ReportData();
5779
if (mReportEndHandler) {
5880
mReportEndHandler();

src/darwin/Framework/CHIP/MTRDevice.mm

+26-2
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ @interface MTRDevice ()
187187

188188
@end
189189

190+
// Declaring selector so compiler won't complain about testing and calling it in _handleReportEnd
191+
#ifdef DEBUG
192+
@protocol MTRDeviceUnitTestDelegate <MTRDeviceDelegate>
193+
- (void)unitTestReportEndForDevice:(MTRDevice *)device;
194+
@end
195+
#endif
196+
190197
@implementation MTRDevice
191198

192199
- (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller
@@ -402,9 +409,11 @@ - (void)_handleUnsolicitedMessageFromPublisher
402409
[self _changeState:MTRDeviceStateReachable];
403410

404411
id<MTRDeviceDelegate> delegate = _weakDelegate.strongObject;
405-
if (delegate && [delegate respondsToSelector:@selector(deviceBecameActive:)]) {
412+
if (delegate) {
406413
dispatch_async(_delegateQueue, ^{
407-
[delegate deviceBecameActive:self];
414+
if ([delegate respondsToSelector:@selector(deviceBecameActive:)]) {
415+
[delegate deviceBecameActive:self];
416+
}
408417
});
409418
}
410419

@@ -429,6 +438,17 @@ - (void)_handleReportEnd
429438
{
430439
os_unfair_lock_lock(&self->_lock);
431440
_estimatedStartTimeFromGeneralDiagnosticsUpTime = nil;
441+
// For unit testing only
442+
#ifdef DEBUG
443+
id delegate = _weakDelegate.strongObject;
444+
if (delegate) {
445+
dispatch_async(_delegateQueue, ^{
446+
if ([delegate respondsToSelector:@selector(unitTestReportEndForDevice:)]) {
447+
[delegate unitTestReportEndForDevice:self];
448+
}
449+
});
450+
}
451+
#endif
432452
os_unfair_lock_unlock(&self->_lock);
433453
}
434454

@@ -1546,6 +1566,8 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
15461566
[mEventReports addObject:[MTRBaseDevice eventReportForHeader:aEventHeader andData:value]];
15471567
}
15481568
}
1569+
1570+
QueueInterimReport();
15491571
}
15501572

15511573
void SubscriptionCallback::OnAttributeData(
@@ -1582,5 +1604,7 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
15821604
[mAttributeReports addObject:@ { MTRAttributePathKey : attributePath, MTRDataKey : value }];
15831605
}
15841606
}
1607+
1608+
QueueInterimReport();
15851609
}
15861610
} // anonymous namespace

src/darwin/Framework/CHIPTests/MTRDeviceTests.m

+14-3
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ @interface MTRDeviceTestDelegate : NSObject <MTRDeviceDelegate>
122122
@property (nonatomic, nullable) dispatch_block_t onNotReachable;
123123
@property (nonatomic, nullable) MTRDeviceTestDelegateDataHandler onAttributeDataReceived;
124124
@property (nonatomic, nullable) MTRDeviceTestDelegateDataHandler onEventDataReceived;
125+
@property (nonatomic, nullable) dispatch_block_t onReportEnd;
125126
@end
126127

127128
@implementation MTRDeviceTestDelegate
@@ -148,6 +149,13 @@ - (void)device:(MTRDevice *)device receivedEventReport:(NSArray<NSDictionary<NSS
148149
}
149150
}
150151

152+
- (void)unitTestReportEndForDevice:(MTRDevice *)device
153+
{
154+
if (self.onReportEnd != nil) {
155+
self.onReportEnd();
156+
}
157+
}
158+
151159
@end
152160

153161
@interface MTRDeviceTests : XCTestCase
@@ -1457,6 +1465,8 @@ - (void)test017_TestMTRDeviceBasics
14571465
XCTAssertNotNil(eventDict[MTREventTimestampDateKey]);
14581466
}
14591467
}
1468+
};
1469+
delegate.onReportEnd = ^() {
14601470
[gotReportsExpectation fulfill];
14611471
};
14621472

@@ -1490,12 +1500,11 @@ - (void)test017_TestMTRDeviceBasics
14901500

14911501
[self waitForExpectations:@[ subscriptionExpectation, gotReportsExpectation ] timeout:60];
14921502

1503+
delegate.onReportEnd = nil;
1504+
14931505
XCTAssertNotEqual(attributeReportsReceived, 0);
14941506
XCTAssertNotEqual(eventReportsReceived, 0);
14951507

1496-
attributeReportsReceived = 0;
1497-
eventReportsReceived = 0;
1498-
14991508
// Before resubscribe, first test write failure and expected value effects
15001509
NSNumber * testEndpointID = @(1);
15011510
NSNumber * testClusterID = @(8);
@@ -1555,6 +1564,8 @@ - (void)test017_TestMTRDeviceBasics
15551564
};
15561565

15571566
// reset the onAttributeDataReceived to validate the following resubscribe test
1567+
attributeReportsReceived = 0;
1568+
eventReportsReceived = 0;
15581569
delegate.onAttributeDataReceived = ^(NSArray<NSDictionary<NSString *, id> *> * data) {
15591570
attributeReportsReceived += data.count;
15601571
};

0 commit comments

Comments
 (0)