Skip to content

Commit

Permalink
Stop using ReadInteraction APIs for Darwin framework read/subscribe. (#…
Browse files Browse the repository at this point in the history
…23534)

This reduces Darwin build CI times from ~2 hours 30 mins to ~1 hour 20 mins and
reduces the size of a release unstripped framework from ~430MB to ~300MB.

Also fixes support for setting resubscribeIfLost to false in the Darwin
"subscribe an attribute" APIs.
  • Loading branch information
bzbarsky-apple committed Nov 16, 2022
1 parent d2ae082 commit bbf3111
Show file tree
Hide file tree
Showing 13 changed files with 12,308 additions and 47,218 deletions.
263 changes: 263 additions & 0 deletions src/darwin/Framework/CHIP/MTRBaseClustersCpp_Internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
/*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>

#import "MTRBaseDevice.h"
#import "MTRCluster_internal.h"
#import "zap-generated/MTRCallbackBridge_internal.h"

#include <app/ReadClient.h>
#include <lib/core/CHIPTLV.h>
#include <lib/core/DataModelTypes.h>
#include <lib/support/CHIPMem.h>

NS_ASSUME_NONNULL_BEGIN

/**
* Utility functions base clusters use for doing reads and subscribes.
*/
template <typename BridgeType, typename DecodableAttributeType>
class MTRAttributeReportCallback : public chip::app::ReadClient::Callback {
public:
MTRAttributeReportCallback(BridgeType * _Nonnull bridge, typename BridgeType::SuccessCallbackType _Nonnull onAttributeReport,
MTRErrorCallback _Nonnull onError, chip::ClusterId clusterID, chip::AttributeId attributeID)
: mBridge(bridge)
, mOnAttributeReport(onAttributeReport)
, mOnError(onError)
, mClusterID(clusterID)
, mAttributeID(attributeID)
, mBufferedReadAdapter(*this)
{
}

~MTRAttributeReportCallback() {}

chip::app::BufferedReadCallback & GetBufferedCallback() { return mBufferedReadAdapter; }

void AdoptReadClient(chip::Platform::UniquePtr<chip::app::ReadClient> readClient) { mReadClient = std::move(readClient); }

protected:
void OnAttributeData(
const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data, const chip::app::StatusIB & status) override
{
if (mCalledCallback && mReadClient->IsReadType()) {
return;
}
mCalledCallback = true;

CHIP_ERROR err = CHIP_NO_ERROR;
DecodableAttributeType value;

//
// We shouldn't be getting list item operations in the provided path since that should be handled by the buffered read
// callback. If we do, that's a bug.
//
VerifyOrDie(!path.IsListItemOperation());

VerifyOrExit(status.IsSuccess(), err = status.ToChipError());
VerifyOrExit(path.mClusterId == mClusterID && path.mAttributeId == mAttributeID, err = CHIP_ERROR_SCHEMA_MISMATCH);
VerifyOrExit(data != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);

SuccessOrExit(err = chip::app::DataModel::Decode(*data, value));

mOnAttributeReport(mBridge, value);

exit:
if (err != CHIP_NO_ERROR) {
mOnError(mBridge, err);
}
}

void OnError(CHIP_ERROR error) override
{
if (mCalledCallback && mReadClient->IsReadType()) {
return;
}
mCalledCallback = true;

mOnError(mBridge, error);
}

void OnDone(chip::app::ReadClient *) override { chip::Platform::Delete(this); }

BridgeType * _Nonnull mBridge;

chip::ClusterId mClusterID;
chip::AttributeId mAttributeID;
typename BridgeType::SuccessCallbackType mOnAttributeReport;
MTRErrorCallback mOnError;
chip::app::BufferedReadCallback mBufferedReadAdapter;
chip::Platform::UniquePtr<chip::app::ReadClient> mReadClient;
// For reads, we ensure that we make only one data/error callback to our consumer.
bool mCalledCallback = false;
};

template <typename SubscriptionBridgeType, typename DecodableAttributeType>
class MTRAttributeSubscriptionCallback : public MTRAttributeReportCallback<SubscriptionBridgeType, DecodableAttributeType> {
public:
MTRAttributeSubscriptionCallback(SubscriptionBridgeType * _Nonnull bridge,
typename SubscriptionBridgeType::SuccessCallbackType onAttributeReport, MTRErrorCallback onError, chip::ClusterId clusterID,
chip::AttributeId attributeID)
: MTRAttributeReportCallback<SubscriptionBridgeType, DecodableAttributeType>(
bridge, onAttributeReport, onError, clusterID, attributeID)
{
}

~MTRAttributeSubscriptionCallback()
{
// Ensure we release the ReadClient before we tear down anything else,
// so it can call our OnDeallocatePaths properly.
this->mReadClient = nullptr;
}

private:
// The superclass OnResubscriptionNeeded is fine for our purposes.

void OnDeallocatePaths(chip::app::ReadPrepareParams && readPrepareParams) override
{
VerifyOrDie(readPrepareParams.mAttributePathParamsListSize == 1 && readPrepareParams.mpAttributePathParamsList != nullptr);
chip::Platform::Delete<chip::app::AttributePathParams>(readPrepareParams.mpAttributePathParamsList);

if (readPrepareParams.mDataVersionFilterListSize == 1 && readPrepareParams.mpDataVersionFilterList != nullptr) {
chip::Platform::Delete<chip::app::DataVersionFilter>(readPrepareParams.mpDataVersionFilterList);
}
}

void OnSubscriptionEstablished(chip::SubscriptionId subscriptionId) override { this->mBridge->OnSubscriptionEstablished(); }

void OnDone(chip::app::ReadClient * readClient) override
{
this->mBridge->OnDone();
MTRAttributeReportCallback<SubscriptionBridgeType, DecodableAttributeType>::OnDone(readClient);
}
};

template <typename DecodableAttributeType, typename BridgeType>
CHIP_ERROR MTRStartReadInteraction(BridgeType * _Nonnull bridge, MTRReadParams * params,
chip::Messaging::ExchangeManager & exchangeManager, const chip::SessionHandle & session,
typename BridgeType::SuccessCallbackType successCb, MTRErrorCallback failureCb, chip::EndpointId endpoint,
chip::ClusterId clusterID, chip::AttributeId attributeID)
{
auto readPaths = chip::Platform::MakeUnique<chip::app::AttributePathParams>(endpoint, clusterID, attributeID);
VerifyOrReturnError(readPaths != nullptr, CHIP_ERROR_NO_MEMORY);

chip::app::ReadPrepareParams readPrepareParams(session);
[params toReadPrepareParams:readPrepareParams];
readPrepareParams.mpAttributePathParamsList = readPaths.get();
readPrepareParams.mAttributePathParamsListSize = 1;

auto callback = chip::Platform::MakeUnique<MTRAttributeReportCallback<BridgeType, DecodableAttributeType>>(
bridge, successCb, failureCb, clusterID, attributeID);
VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY);

auto readClient = chip::Platform::MakeUnique<chip::app::ReadClient>(chip::app::InteractionModelEngine::GetInstance(),
&exchangeManager, callback->GetBufferedCallback(), chip::app::ReadClient::InteractionType::Read);
VerifyOrReturnError(readClient != nullptr, CHIP_ERROR_NO_MEMORY);

CHIP_ERROR err = readClient->SendRequest(readPrepareParams);
ReturnErrorOnFailure(err);

callback->AdoptReadClient(std::move(readClient));
callback.release();

return CHIP_NO_ERROR;
}

template <typename DecodableAttributeType, typename BridgeType>
CHIP_ERROR MTRStartSubscribeInteraction(BridgeType * _Nonnull bridge, MTRSubscribeParams * params,
chip::Messaging::ExchangeManager & exchangeManager, const chip::SessionHandle & session,
typename BridgeType::SuccessCallbackType successCb, MTRErrorCallback failureCb, chip::EndpointId endpoint,
chip::ClusterId clusterID, chip::AttributeId attributeID)
{
auto readPaths = chip::Platform::MakeUnique<chip::app::AttributePathParams>(endpoint, clusterID, attributeID);
VerifyOrReturnError(readPaths != nullptr, CHIP_ERROR_NO_MEMORY);

chip::app::ReadPrepareParams readPrepareParams(session);
[params toReadPrepareParams:readPrepareParams];
readPrepareParams.mpAttributePathParamsList = readPaths.get();
readPrepareParams.mAttributePathParamsListSize = 1;

auto callback = chip::Platform::MakeUnique<MTRAttributeSubscriptionCallback<BridgeType, DecodableAttributeType>>(
bridge, successCb, failureCb, clusterID, attributeID);
VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY);

auto readClient = chip::Platform::MakeUnique<chip::app::ReadClient>(chip::app::InteractionModelEngine::GetInstance(),
&exchangeManager, callback->GetBufferedCallback(), chip::app::ReadClient::InteractionType::Subscribe);
VerifyOrReturnError(readClient != nullptr, CHIP_ERROR_NO_MEMORY);

CHIP_ERROR err;
if (params.resubscribeIfLost) {
readPaths.release();

err = readClient->SendAutoResubscribeRequest(std::move(readPrepareParams));
} else {
err = readClient->SendRequest(readPrepareParams);
}
ReturnErrorOnFailure(err);

bridge->KeepAliveOnCallback();

callback->AdoptReadClient(std::move(readClient));
callback.release();

return CHIP_NO_ERROR;
}

template <typename SubscriptionBridgeType, typename AttributeObjCType, typename DecodableAttributeType>
void MTRSubscribeAttribute(MTRSubscribeParams * _Nonnull params,
MTRSubscriptionEstablishedHandler _Nullable subscriptionEstablished,
void (^reportHandler)(AttributeObjCType * _Nullable value, NSError * _Nullable error), dispatch_queue_t callbackQueue,
MTRBaseDevice * device, chip::EndpointId endpoint, chip::ClusterId clusterID, chip::AttributeId attributeID)
{
// Make a copy of params before we go async.
params = [params copy];
auto * callbackBridge = new SubscriptionBridgeType(
callbackQueue,
// This treats reportHandler as taking an id for the data. This is
// not great from a type-safety perspective, of course.
reportHandler,
^(chip::Messaging::ExchangeManager & exchangeManager, const chip::SessionHandle & session,
typename SubscriptionBridgeType::SuccessCallbackType successCb, MTRErrorCallback failureCb,
MTRCallbackBridgeBase * bridge) {
auto * subscriptionBridge = static_cast<SubscriptionBridgeType *>(bridge);
return MTRStartSubscribeInteraction<DecodableAttributeType>(
subscriptionBridge, params, exchangeManager, session, successCb, failureCb, endpoint, clusterID, attributeID);
},
subscriptionEstablished);
std::move(*callbackBridge).DispatchAction(device);
}

template <typename ReadBridgeType, typename AttributeObjCType, typename DecodableAttributeType>
void MTRReadAttribute(MTRReadParams * _Nonnull params,
void (^reportHandler)(AttributeObjCType * _Nullable value, NSError * _Nullable error), dispatch_queue_t callbackQueue,
MTRBaseDevice * device, chip::EndpointId endpoint, chip::ClusterId clusterID, chip::AttributeId attributeID)
{
// Make a copy of params before we go async.
params = [params copy];
auto * callbackBridge = new ReadBridgeType(callbackQueue,
// This treats reportHandler as taking an id for the data. This is
// not great from a type-safety perspective, of course.
reportHandler,
^(chip::Messaging::ExchangeManager & exchangeManager, const chip::SessionHandle & session,
typename ReadBridgeType::SuccessCallbackType successCb, MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) {
auto * readBridge = static_cast<ReadBridgeType *>(bridge);
return MTRStartReadInteraction<DecodableAttributeType>(
readBridge, params, exchangeManager, session, successCb, failureCb, endpoint, clusterID, attributeID);
});
std::move(*callbackBridge).DispatchAction(device);
}

NS_ASSUME_NONNULL_END
13 changes: 4 additions & 9 deletions src/darwin/Framework/CHIP/MTRBaseDevice.mm
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#import "MTRBaseSubscriptionCallback.h"
#import "MTRCallbackBridgeBase_internal.h"
#import "MTRCluster.h"
#import "MTRCluster_internal.h"
#import "MTRError_Internal.h"
#import "MTREventTLVValueDecoder_Internal.h"
#import "MTRLogging.h"
Expand Down Expand Up @@ -302,14 +303,11 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue
// We want to get event reports at the minInterval, not the maxInterval.
eventPath->mIsUrgentEvent = true;
ReadPrepareParams readParams(session.Value());
readParams.mMinIntervalFloorSeconds = [params.minInterval unsignedShortValue];
readParams.mMaxIntervalCeilingSeconds = [params.maxInterval unsignedShortValue];
[params toReadPrepareParams:readParams];
readParams.mpAttributePathParamsList = attributePath.get();
readParams.mAttributePathParamsListSize = 1;
readParams.mpEventPathParamsList = eventPath.get();
readParams.mEventPathParamsListSize = 1;
readParams.mIsFabricFiltered = params.filterByFabric;
readParams.mKeepSubscriptions = !params.replaceExistingSubscriptions;

std::unique_ptr<ClusterStateCache> attributeCache;
ReadClient::Callback * callbackForReadClient = nullptr;
Expand Down Expand Up @@ -831,9 +829,9 @@ - (void)readAttributesWithEndpointID:(NSNumber * _Nullable)endpointID
CHIP_ERROR err = CHIP_NO_ERROR;

chip::app::ReadPrepareParams readParams(session);
[params toReadPrepareParams:readParams];
readParams.mpAttributePathParamsList = &attributePath;
readParams.mAttributePathParamsListSize = 1;
readParams.mIsFabricFiltered = params.filterByFabric;

auto onDone = [resultArray, resultSuccess, resultFailure, bridge, successCb, failureCb](
BufferedReadAttributeCallback<MTRDataValueDictionaryDecodableType> * callback) {
Expand Down Expand Up @@ -1200,12 +1198,9 @@ - (void)subscribeToAttributesWithEndpointID:(NSNumber * _Nullable)endpointID
CHIP_ERROR err = CHIP_NO_ERROR;

chip::app::ReadPrepareParams readParams(session.Value());
[params toReadPrepareParams:readParams];
readParams.mpAttributePathParamsList = container.pathParams;
readParams.mAttributePathParamsListSize = 1;
readParams.mMinIntervalFloorSeconds = static_cast<uint16_t>([params.minInterval unsignedShortValue]);
readParams.mMaxIntervalCeilingSeconds = static_cast<uint16_t>([params.maxInterval unsignedShortValue]);
readParams.mIsFabricFiltered = params.filterByFabric;
readParams.mKeepSubscriptions = !params.replaceExistingSubscriptions;

auto onDone = [container](BufferedReadAttributeCallback<MTRDataValueDictionaryDecodableType> * callback) {
[container onDone];
Expand Down
1 change: 1 addition & 0 deletions src/darwin/Framework/CHIP/MTRCallbackBridgeBase_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ template <class T> class MTRCallbackBridge : public MTRCallbackBridgeBase {
public:
using MTRActionBlock = MTRActionBlockT<T>;
using MTRLocalActionBlock = MTRLocalActionBlockT<T>;
using SuccessCallbackType = T;

/**
* Construct a callback bridge, which can then have DispatcLocalAction() called
Expand Down
13 changes: 13 additions & 0 deletions src/darwin/Framework/CHIP/MTRCluster.mm
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ - (id)copyWithZone:(NSZone * _Nullable)zone
return other;
}

- (void)toReadPrepareParams:(chip::app::ReadPrepareParams &)readPrepareParams
{
readPrepareParams.mIsFabricFiltered = self.filterByFabric;
}

@end

@implementation MTRSubscribeParams
Expand All @@ -100,6 +105,14 @@ - (id)copyWithZone:(NSZone * _Nullable)zone
return other;
}

- (void)toReadPrepareParams:(chip::app::ReadPrepareParams &)readPrepareParams
{
[super toReadPrepareParams:readPrepareParams];
readPrepareParams.mMinIntervalFloorSeconds = self.minInterval.unsignedShortValue;
readPrepareParams.mMaxIntervalCeilingSeconds = self.maxInterval.unsignedShortValue;
readPrepareParams.mKeepSubscriptions = !self.replaceExistingSubscriptions;
}

@end

@implementation MTRReadParams (Deprecated)
Expand Down
16 changes: 16 additions & 0 deletions src/darwin/Framework/CHIP/MTRCluster_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#import "zap-generated/CHIPClusters.h"
#import "zap-generated/MTRBaseClusters.h"

#include <app/ReadPrepareParams.h>

NS_ASSUME_NONNULL_BEGIN

@interface MTRCluster ()
Expand All @@ -33,4 +35,18 @@ NS_ASSUME_NONNULL_BEGIN
- (chip::CharSpan)asCharSpan:(NSString *)value;
@end

@interface MTRReadParams ()
/**
* Copy state from this MTRReadParams to the ReadPreparaParams.
*/
- (void)toReadPrepareParams:(chip::app::ReadPrepareParams &)readPrepareParams;
@end

@interface MTRSubscribeParams ()
/**
* Copy state from this MTRReadParams to the ReadPreparaParams.
*/
- (void)toReadPrepareParams:(chip::app::ReadPrepareParams &)readPrepareParams;
@end

NS_ASSUME_NONNULL_END
Loading

0 comments on commit bbf3111

Please sign in to comment.