Skip to content

Commit

Permalink
Moved MTRAsyncCallbackQueueTests additional queuing into readyHandler…
Browse files Browse the repository at this point in the history
… to avoid potential race
  • Loading branch information
jtung-apple committed Jun 1, 2023
1 parent 1a497cd commit bf6cff5
Showing 1 changed file with 104 additions and 107 deletions.
211 changes: 104 additions & 107 deletions src/darwin/Framework/CHIPTests/MTRAsyncCallbackQueueTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -269,55 +269,54 @@ - (void)testBatching
MTRAsyncCallbackQueueWorkItem * workItem0 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler0 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
// Give the following code a chance to enqueue and merge
sleep(1);
// While processing item 0, enqueue additional items to test batching
MTRAsyncCallbackQueueWorkItem * workItem1 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler1 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
[workItem1ReadyExpectation fulfill];
[workItem1 endWork];
};
workItem1.readyHandler = readyHandler1;
[workItem1 setBatchingID:1
data:@(1)
handler:^(id _Nonnull opaqueDataFirst, id _Nonnull opaqueDataSecond, BOOL * _Nonnull fullyMerged) {
XCTAssertEqualObjects(opaqueDataFirst, @(1));
XCTAssertEqualObjects(opaqueDataSecond, @(2));
*fullyMerged = YES;
}];
// No cancel handler on purpose.
[workQueue enqueueWorkItem:workItem1];

MTRAsyncCallbackQueueWorkItem * workItem2 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler2 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
workItem2ReadyCalled = YES;
[workItem2 endWork];
};
workItem2.readyHandler = readyHandler2;
[workItem2 setBatchingID:1
data:@(2)
handler:^(id _Nonnull opaqueDataFirst, id _Nonnull opaqueDataSecond, BOOL * _Nonnull fullyMerged) {
workItem2BatchingCalled = YES;
}];
// No cancel handler on purpose.
[workQueue enqueueWorkItem:workItem2];

MTRAsyncCallbackQueueWorkItem * workItem3 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler3 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
[workItem3ReadyExpectation fulfill];
[workItem3 endWork];
};
workItem3.readyHandler = readyHandler3;
[workQueue enqueueWorkItem:workItem3];

[workItem0 endWork];
};
workItem0.readyHandler = readyHandler0;
// No cancel handler on purpose.
[workQueue enqueueWorkItem:workItem0];

MTRAsyncCallbackQueueWorkItem * workItem1 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler1 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
[workItem1ReadyExpectation fulfill];
[workItem1 endWork];
};
workItem1.readyHandler = readyHandler1;
[workItem1 setBatchingID:1
data:@(1)
handler:^(id _Nonnull opaqueDataFirst, id _Nonnull opaqueDataSecond, BOOL * _Nonnull fullyMerged) {
XCTAssertEqualObjects(opaqueDataFirst, @(1));
XCTAssertEqualObjects(opaqueDataSecond, @(2));
*fullyMerged = YES;
}];
// No cancel handler on purpose.
[workQueue enqueueWorkItem:workItem1];

MTRAsyncCallbackQueueWorkItem * workItem2 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler2 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
workItem2ReadyCalled = YES;
[workItem2 endWork];
};
workItem2.readyHandler = readyHandler2;
[workItem2 setBatchingID:1
data:@(2)
handler:^(id _Nonnull opaqueDataFirst, id _Nonnull opaqueDataSecond, BOOL * _Nonnull fullyMerged) {
workItem2BatchingCalled = YES;
}];
// No cancel handler on purpose.
[workQueue enqueueWorkItem:workItem2];

MTRAsyncCallbackQueueWorkItem * workItem3 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler3 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
[workItem3ReadyExpectation fulfill];
[workItem3 endWork];
};
workItem3.readyHandler = readyHandler3;
[workQueue enqueueWorkItem:workItem3];

[self waitForExpectations:@[ workItem1ReadyExpectation, workItem3ReadyExpectation ] timeout:5];

XCTAssertFalse(workItem2BatchingCalled);
Expand All @@ -335,77 +334,75 @@ - (void)testDuplicate
MTRAsyncCallbackQueueWorkItem * workItem0 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler0 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
// Give the following code a chance to enqueue and check for duplicates
sleep(1);
// While processing item 0, enqueue additional items to test duplicate checking
MTRAsyncCallbackQueueWorkItem * workItem1 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler1 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
[workItem1 endWork];
};
workItem1.readyHandler = readyHandler1;
[workItem1 setDuplicateTypeID:1
handler:^(id _Nonnull opaqueItemData, BOOL * _Nonnull isDuplicate) {
if ([opaqueItemData isEqual:@(1)]) {
*isDuplicate = YES;
} else {
*isDuplicate = NO;
}
}];
// No cancel handler on purpose.
[workQueue enqueueWorkItem:workItem1];

MTRAsyncCallbackQueueWorkItem * workItem2 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler2 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
[workItem2 endWork];
};
workItem2.readyHandler = readyHandler2;
[workItem2 setDuplicateTypeID:1
handler:^(id _Nonnull opaqueItemData, BOOL * _Nonnull isDuplicate) {
if ([opaqueItemData isEqual:@(2)]) {
*isDuplicate = YES;
} else {
*isDuplicate = NO;
}
}];
// No cancel handler on purpose.
[workQueue enqueueWorkItem:workItem2];

MTRAsyncCallbackQueueWorkItem * workItem3 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler3 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
[workItem3 endWork];
};
workItem3.readyHandler = readyHandler3;
[workItem3 setDuplicateTypeID:2
handler:^(id _Nonnull opaqueItemData, BOOL * _Nonnull isDuplicate) {
if ([opaqueItemData isEqual:@(1)]) {
*isDuplicate = YES;
} else {
*isDuplicate = NO;
}
}];
[workQueue enqueueWorkItem:workItem3];

// At this point we should have duplicate type 1 with data @(1) and @(2), and type 2 with data @(1).
XCTAssertTrue([workQueue isDuplicateForTypeID:1 workItemData:@(1)]);
XCTAssertTrue([workQueue isDuplicateForTypeID:1 workItemData:@(2)]);
XCTAssertTrue([workQueue isDuplicateForTypeID:2 workItemData:@(1)]);

XCTAssertFalse([workQueue isDuplicateForTypeID:0 workItemData:@(1)]);
XCTAssertFalse([workQueue isDuplicateForTypeID:0 workItemData:@(2)]);
XCTAssertFalse([workQueue isDuplicateForTypeID:1 workItemData:@(0)]);
XCTAssertFalse([workQueue isDuplicateForTypeID:1 workItemData:@(3)]);
XCTAssertFalse([workQueue isDuplicateForTypeID:2 workItemData:@(2)]);

[workItem0 endWork];
[workItem0ReadyExpectation fulfill];
};
workItem0.readyHandler = readyHandler0;
// No cancel handler on purpose.
[workQueue enqueueWorkItem:workItem0];

MTRAsyncCallbackQueueWorkItem * workItem1 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler1 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
[workItem1 endWork];
};
workItem1.readyHandler = readyHandler1;
[workItem1 setDuplicateTypeID:1
handler:^(id _Nonnull opaqueItemData, BOOL * _Nonnull isDuplicate) {
if ([opaqueItemData isEqual:@(1)]) {
*isDuplicate = YES;
} else {
*isDuplicate = NO;
}
}];
// No cancel handler on purpose.
[workQueue enqueueWorkItem:workItem1];

MTRAsyncCallbackQueueWorkItem * workItem2 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler2 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
[workItem2 endWork];
};
workItem2.readyHandler = readyHandler2;
[workItem2 setDuplicateTypeID:1
handler:^(id _Nonnull opaqueItemData, BOOL * _Nonnull isDuplicate) {
if ([opaqueItemData isEqual:@(2)]) {
*isDuplicate = YES;
} else {
*isDuplicate = NO;
}
}];
// No cancel handler on purpose.
[workQueue enqueueWorkItem:workItem2];

MTRAsyncCallbackQueueWorkItem * workItem3 =
[[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)];
MTRAsyncCallbackReadyHandler readyHandler3 = ^(MTRDevice * _Nonnull device, NSUInteger retryCount) {
[workItem3 endWork];
};
workItem3.readyHandler = readyHandler3;
[workItem3 setDuplicateTypeID:2
handler:^(id _Nonnull opaqueItemData, BOOL * _Nonnull isDuplicate) {
if ([opaqueItemData isEqual:@(1)]) {
*isDuplicate = YES;
} else {
*isDuplicate = NO;
}
}];
[workQueue enqueueWorkItem:workItem3];

// At this point we should have duplicate type 1 with data @(1) and @(2), and type 2 with data @(1).

XCTAssertTrue([workQueue isDuplicateForTypeID:1 workItemData:@(1)]);
XCTAssertTrue([workQueue isDuplicateForTypeID:1 workItemData:@(2)]);
XCTAssertTrue([workQueue isDuplicateForTypeID:2 workItemData:@(1)]);

XCTAssertFalse([workQueue isDuplicateForTypeID:0 workItemData:@(1)]);
XCTAssertFalse([workQueue isDuplicateForTypeID:0 workItemData:@(2)]);
XCTAssertFalse([workQueue isDuplicateForTypeID:1 workItemData:@(0)]);
XCTAssertFalse([workQueue isDuplicateForTypeID:1 workItemData:@(3)]);
XCTAssertFalse([workQueue isDuplicateForTypeID:2 workItemData:@(2)]);

// double check that items 1~3 have not processed yet because of item 0's sleep()
XCTAssertFalse(workItem0ReadyCalled);

Expand Down

0 comments on commit bf6cff5

Please sign in to comment.