Skip to content

Commit 1609260

Browse files
bzbarsky-applepull[bot]
authored andcommitted
Make queue handling consistent for commands in MTRDevice/MTRClusters. (#23959)
invokeCommandWithEndpointID on MTRDevice ran the async work item management logic on the MTRDevice's work queue, and queued an async callback to the client queue for the command response. This was different from what MTRClusters did: that ran the management logic on the client queue as well. This PR aligns on the invokeCommandWithEndpointID behavior, because the MTRClusters behavior allows a poorly-behaved client that blocks inside the completion handler to completely block the device's async callback queue processing.
1 parent 7f01e71 commit 1609260

File tree

4 files changed

+944
-568
lines changed

4 files changed

+944
-568
lines changed

src/darwin/Framework/CHIP/MTRDevice.mm

+5-7
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ - (id)strongObject
130130
#pragma mark - MTRDevice
131131
@interface MTRDevice ()
132132
@property (nonatomic, readonly) os_unfair_lock lock; // protects the caches and device state
133-
@property (nonatomic) dispatch_queue_t queue;
134133
@property (nonatomic) MTRWeakReference<id<MTRDeviceDelegate>> * weakDelegate;
135134
@property (nonatomic) dispatch_queue_t delegateQueue;
136135
@property (nonatomic) NSArray<NSDictionary<NSString *, id> *> * unreportedEvents;
@@ -156,8 +155,7 @@ - (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceControlle
156155
_lock = OS_UNFAIR_LOCK_INIT;
157156
_nodeID = [nodeID copy];
158157
_deviceController = controller;
159-
_queue = dispatch_queue_create("com.apple.matter.framework.xpc.workqueue", DISPATCH_QUEUE_SERIAL);
160-
;
158+
_queue = dispatch_queue_create("com.apple.matter.framework.device.workqueue", DISPATCH_QUEUE_SERIAL);
161159
_readCache = [NSMutableDictionary dictionary];
162160
_expectedValueCache = [NSMutableDictionary dictionary];
163161
_asyncCallbackWorkQueue = [[MTRAsyncCallbackWorkQueue alloc] initWithContext:self queue:_queue];
@@ -413,7 +411,7 @@ - (void)setupSubscription
413411
NSString * logPrefix = [NSString
414412
stringWithFormat:@"MTRDevice read %u %@ %@ %@", _deviceController.fabricIndex, endpointID, clusterID, attributeID];
415413
// Create work item, set ready handler to perform task, then enqueue the work
416-
MTRAsyncCallbackQueueWorkItem * workItem = [[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:_queue];
414+
MTRAsyncCallbackQueueWorkItem * workItem = [[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:self.queue];
417415
MTRAsyncCallbackReadyHandler readyHandler = ^(MTRDevice * device, NSUInteger retryCount) {
418416
MTR_LOG_INFO("%@ dequeueWorkItem %@", logPrefix, self->_asyncCallbackWorkQueue);
419417
MTRBaseDevice * baseDevice = [self newBaseDevice];
@@ -468,7 +466,7 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID
468466
timeout = MTRClampedNumber(timeout, @(1), @(UINT16_MAX));
469467
}
470468
expectedValueInterval = MTRClampedNumber(expectedValueInterval, @(1), @(UINT32_MAX));
471-
MTRAsyncCallbackQueueWorkItem * workItem = [[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:_queue];
469+
MTRAsyncCallbackQueueWorkItem * workItem = [[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:self.queue];
472470
MTRAsyncCallbackReadyHandler readyHandler = ^(MTRDevice * device, NSUInteger retryCount) {
473471
MTR_LOG_INFO("%@ dequeueWorkItem %@", logPrefix, self->_asyncCallbackWorkQueue);
474472
MTRBaseDevice * baseDevice = [self newBaseDevice];
@@ -517,7 +515,7 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
517515
} else {
518516
expectedValueInterval = MTRClampedNumber(expectedValueInterval, @(1), @(UINT32_MAX));
519517
}
520-
MTRAsyncCallbackQueueWorkItem * workItem = [[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:_queue];
518+
MTRAsyncCallbackQueueWorkItem * workItem = [[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:self.queue];
521519
MTRAsyncCallbackReadyHandler readyHandler = ^(MTRDevice * device, NSUInteger retryCount) {
522520
MTR_LOG_INFO("%@ dequeueWorkItem %@", logPrefix, self->_asyncCallbackWorkQueue);
523521
MTRBaseDevice * baseDevice = [self newBaseDevice];
@@ -611,7 +609,7 @@ - (void)_checkExpiredExpectedValues
611609
waitTime = MTR_DEVICE_EXPIRATION_CHECK_TIMER_MINIMUM_WAIT_TIME;
612610
}
613611
MTRWeakReference<MTRDevice *> * weakSelf = [MTRWeakReference weakReferenceWithObject:self];
614-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(waitTime * NSEC_PER_SEC)), _queue, ^{
612+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(waitTime * NSEC_PER_SEC)), self.queue, ^{
615613
MTRDevice * strongSelf = weakSelf.strongObject;
616614
[strongSelf _performScheduledExpirationCheck];
617615
});

src/darwin/Framework/CHIP/MTRDevice_Internal.h

+4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ typedef void (^MTRDevicePerformAsyncBlock)(MTRBaseDevice * baseDevice);
3535

3636
@property (nonatomic, readonly) MTRDeviceController * deviceController;
3737
@property (nonatomic, readonly, copy) NSNumber * nodeID;
38+
// Queue used for various internal bookkeeping work. In general endWork calls
39+
// on work items should happen on this queue, so we don't block progress of the
40+
// asyncCallbackWorkQueue on any client code.
41+
@property (nonatomic) dispatch_queue_t queue;
3842
@property (nonatomic, readonly) MTRAsyncCallbackWorkQueue * asyncCallbackWorkQueue;
3943

4044
@end

src/darwin/Framework/CHIP/templates/MTRClusters-src.zapt

+5-3
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,14 @@ static void MTRClustersLogCompletion(NSString *logPrefix, id value, NSError *err
7373
if (timedInvokeTimeoutMsParam) {
7474
timedInvokeTimeoutMsParam = MTRClampedNumber(timedInvokeTimeoutMsParam, @(1), @(UINT16_MAX));
7575
}
76-
MTRAsyncCallbackQueueWorkItem * workItem = [[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:self.callbackQueue];
76+
MTRAsyncCallbackQueueWorkItem * workItem = [[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:self.device.queue];
7777
MTRAsyncCallbackReadyHandler readyHandler = ^(MTRDevice * device, NSUInteger retryCount) {
7878
MTRClustersLogDequeue(logPrefix, self.device.asyncCallbackWorkQueue);
7979
MTRBaseDevice *baseDevice = [[MTRBaseDevice alloc] initWithNodeID:self.device.nodeID controller:self.device.deviceController];
80-
auto * bridge = new MTR{{>callbackName}}CallbackBridge(self.callbackQueue,
80+
auto * bridge = new MTR{{>callbackName}}CallbackBridge(self.device.queue,
8181
^(id _Nullable value, NSError * _Nullable error) {
82+
MTRClustersLogCompletion(logPrefix, value, error);
83+
dispatch_async(self.callbackQueue, ^{
8284
{{#if hasSpecificResponse}}
8385
{{! This treats completion as taking an id for the data. This is
8486
not great from a type-safety perspective, of course. }}
@@ -89,7 +91,7 @@ static void MTRClustersLogCompletion(NSString *logPrefix, id value, NSError *err
8991
type-safety perspective, of course. }}
9092
completion(error);
9193
{{/if}}
92-
MTRClustersLogCompletion(logPrefix, value, error);
94+
});
9395
[workItem endWork];
9496
},
9597
^(ExchangeManager & exchangeManager, const SessionHandle & session, {{>callbackName}}CallbackType successCb, MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) {

0 commit comments

Comments
 (0)