Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor controller startup a bit to share more code. #28385

Merged
merged 1 commit into from
Jul 29, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 97 additions & 120 deletions src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm
Original file line number Diff line number Diff line change
Expand Up @@ -553,16 +553,21 @@ - (void)stopControllerFactory
_running = NO;
}

- (MTRDeviceController * _Nullable)createControllerOnExistingFabric:(MTRDeviceControllerStartupParams *)startupParams
error:(NSError * __autoreleasing *)error
/**
* Helper function to start a device controller with the given startup params.
* The fabricChecker block will run on the Matter queue, and is expected to
* return nil if pre-startup fabric table checks fail, and set fabricError to
* the right error value in that situation.
*/
- (MTRDeviceController * _Nullable)_startDeviceController:(MTRDeviceControllerStartupParams *)startupParams
fabricChecker:(MTRDeviceControllerStartupParamsInternal * (^)(
FabricTable * fabricTable, CHIP_ERROR & fabricError))fabricChecker
error:(NSError * __autoreleasing *)error
{
[self _assertCurrentQueueIsNotMatterQueue];

if (![self checkIsRunning:error]) {
MTR_LOG_ERROR("Trying to start controller while Matter controller factory is not running");
if (error != nil) {
*error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INCORRECT_STATE];
}
return nil;
}

Expand All @@ -578,53 +583,14 @@ - (MTRDeviceController * _Nullable)createControllerOnExistingFabric:(MTRDeviceCo

__block MTRDeviceControllerStartupParamsInternal * params = nil;
__block CHIP_ERROR fabricError = CHIP_NO_ERROR;

// We want the block to end up with just a pointer to the fabric table,
// since we know our on-stack instance will outlive the block.
FabricTable fabricTableInstance;
FabricTable * fabricTable = &fabricTableInstance;
dispatch_sync(_chipWorkQueue, ^{
const FabricInfo * fabric = nullptr;
BOOL ok = [self findMatchingFabric:*fabricTable params:startupParams fabric:&fabric];
if (!ok) {
MTR_LOG_ERROR("Can't start on existing fabric: fabric matching failed");
fabricError = CHIP_ERROR_INTERNAL;
return;
}

if (fabric == nullptr) {
MTR_LOG_ERROR("Can't start on existing fabric: fabric not found");
fabricError = CHIP_ERROR_NOT_FOUND;
return;
}

os_unfair_lock_lock(&_controllersLock);
NSArray<MTRDeviceController *> * controllersCopy = [_controllers copy];
os_unfair_lock_unlock(&_controllersLock);

for (MTRDeviceController * existing in controllersCopy) {
BOOL isRunning = YES; // assume the worst
if ([existing isRunningOnFabric:fabricTable fabricIndex:fabric->GetFabricIndex() isRunning:&isRunning]
!= CHIP_NO_ERROR) {
MTR_LOG_ERROR("Can't tell what fabric a controller is running on. Not safe to start.");
fabricError = CHIP_ERROR_INTERNAL;
return;
}

if (isRunning) {
MTR_LOG_ERROR("Can't start on existing fabric: another controller is running on it");
fabricError = CHIP_ERROR_INCORRECT_STATE;
return;
}
}

params = [[MTRDeviceControllerStartupParamsInternal alloc] initForExistingFabric:fabricTable
fabricIndex:fabric->GetFabricIndex()
keystore:_keystore
advertiseOperational:self.advertiseOperational
params:startupParams];
if (params == nil) {
fabricError = CHIP_ERROR_NO_MEMORY;
}
dispatch_sync(_chipWorkQueue, ^{
params = fabricChecker(fabricTable, fabricError);
});

if (params == nil) {
Expand Down Expand Up @@ -653,19 +619,68 @@ - (MTRDeviceController * _Nullable)createControllerOnExistingFabric:(MTRDeviceCo
return controller;
}

- (MTRDeviceController * _Nullable)createControllerOnExistingFabric:(MTRDeviceControllerStartupParams *)startupParams
error:(NSError * __autoreleasing *)error
{
[self _assertCurrentQueueIsNotMatterQueue];

return [self
_startDeviceController:startupParams
fabricChecker:^MTRDeviceControllerStartupParamsInternal *(FabricTable * fabricTable, CHIP_ERROR & fabricError) {
const FabricInfo * fabric = nullptr;
BOOL ok = [self findMatchingFabric:*fabricTable params:startupParams fabric:&fabric];
if (!ok) {
MTR_LOG_ERROR("Can't start on existing fabric: fabric matching failed");
fabricError = CHIP_ERROR_INTERNAL;
return nil;
}

if (fabric == nullptr) {
MTR_LOG_ERROR("Can't start on existing fabric: fabric not found");
fabricError = CHIP_ERROR_NOT_FOUND;
return nil;
}

os_unfair_lock_lock(&self->_controllersLock);
NSArray<MTRDeviceController *> * controllersCopy = [self->_controllers copy];
os_unfair_lock_unlock(&self->_controllersLock);

for (MTRDeviceController * existing in controllersCopy) {
BOOL isRunning = YES; // assume the worst
if ([existing isRunningOnFabric:fabricTable fabricIndex:fabric->GetFabricIndex() isRunning:&isRunning]
!= CHIP_NO_ERROR) {
MTR_LOG_ERROR("Can't tell what fabric a controller is running on. Not safe to start.");
fabricError = CHIP_ERROR_INTERNAL;
return nil;
}

if (isRunning) {
MTR_LOG_ERROR("Can't start on existing fabric: another controller is running on it");
fabricError = CHIP_ERROR_INCORRECT_STATE;
return nil;
}
}

auto * params =
[[MTRDeviceControllerStartupParamsInternal alloc] initForExistingFabric:fabricTable
fabricIndex:fabric->GetFabricIndex()
keystore:self->_keystore
advertiseOperational:self.advertiseOperational
params:startupParams];
if (params == nil) {
fabricError = CHIP_ERROR_NO_MEMORY;
}

return params;
}
error:error];
}

- (MTRDeviceController * _Nullable)createControllerOnNewFabric:(MTRDeviceControllerStartupParams *)startupParams
error:(NSError * __autoreleasing *)error
{
[self _assertCurrentQueueIsNotMatterQueue];

if (![self isRunning]) {
MTR_LOG_ERROR("Trying to start controller while Matter controller factory is not running");
if (error != nil) {
*error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INCORRECT_STATE];
}
return nil;
}

if (startupParams.vendorID == nil) {
MTR_LOG_ERROR("Must provide vendor id when starting controller on new fabric");
if (error != nil) {
Expand All @@ -682,71 +697,33 @@ - (MTRDeviceController * _Nullable)createControllerOnNewFabric:(MTRDeviceControl
return nil;
}

// Create the controller, so we start the event loop, since we plan to do
// our fabric table operations there.
auto * controller = [self createController];
if (controller == nil) {
if (error != nil) {
*error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_NO_MEMORY];
}
return nil;
}

__block MTRDeviceControllerStartupParamsInternal * params = nil;
__block CHIP_ERROR fabricError = CHIP_NO_ERROR;
// We want the block to end up with just a pointer to the fabric table,
// since we know our on-stack instance will outlive the block.
FabricTable fabricTableInstance;
FabricTable * fabricTable = &fabricTableInstance;
dispatch_sync(_chipWorkQueue, ^{
const FabricInfo * fabric = nullptr;
BOOL ok = [self findMatchingFabric:*fabricTable params:startupParams fabric:&fabric];
if (!ok) {
MTR_LOG_ERROR("Can't start on new fabric: fabric matching failed");
fabricError = CHIP_ERROR_INTERNAL;
return;
}

if (fabric != nullptr) {
MTR_LOG_ERROR("Can't start on new fabric that matches existing fabric");
fabricError = CHIP_ERROR_INCORRECT_STATE;
return;
}

params = [[MTRDeviceControllerStartupParamsInternal alloc] initForNewFabric:fabricTable
keystore:_keystore
advertiseOperational:self.advertiseOperational
params:startupParams];
if (params == nil) {
fabricError = CHIP_ERROR_NO_MEMORY;
}
});

if (params == nil) {
[self controllerShuttingDown:controller];
if (error != nil) {
*error = [MTRError errorForCHIPErrorCode:fabricError];
}
return nil;
}

BOOL ok = [controller startup:params];
if (ok == NO) {
// TODO: get error from controller's startup.
if (error != nil) {
*error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INTERNAL];
}
return nil;
}

// TODO: Need better error propagation.
controller = [self maybeInitializeOTAProvider:controller];
if (controller == nil) {
if (error != nil) {
*error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INTERNAL];
}
}
return controller;
return [self
_startDeviceController:startupParams
fabricChecker:^MTRDeviceControllerStartupParamsInternal *(FabricTable * fabricTable, CHIP_ERROR & fabricError) {
const FabricInfo * fabric = nullptr;
BOOL ok = [self findMatchingFabric:*fabricTable params:startupParams fabric:&fabric];
if (!ok) {
MTR_LOG_ERROR("Can't start on new fabric: fabric matching failed");
fabricError = CHIP_ERROR_INTERNAL;
return nil;
}

if (fabric != nullptr) {
MTR_LOG_ERROR("Can't start on new fabric that matches existing fabric");
fabricError = CHIP_ERROR_INCORRECT_STATE;
return nil;
}

auto * params = [[MTRDeviceControllerStartupParamsInternal alloc] initForNewFabric:fabricTable
keystore:self->_keystore
advertiseOperational:self.advertiseOperational
params:startupParams];
if (params == nil) {
fabricError = CHIP_ERROR_NO_MEMORY;
}
return params;
}
error:error];
}

- (MTRDeviceController * _Nullable)createController
Expand Down