Skip to content

Commit

Permalink
Stop using InvokeInteraction APIs for Darwin framework invokes. (#23640)
Browse files Browse the repository at this point in the history
This reduces Darwin build CI times from ~1 hour 20 mins to ~1 hour and
reduces the size of a release unstripped framework from ~300MB to ~250MB.
  • Loading branch information
bzbarsky-apple authored and pull[bot] committed Jul 21, 2023
1 parent 6bd6164 commit 9843908
Show file tree
Hide file tree
Showing 3 changed files with 703 additions and 374 deletions.
143 changes: 143 additions & 0 deletions src/darwin/Framework/CHIP/MTRBaseClustersCpp_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
#import "MTRCluster_internal.h"
#import "zap-generated/MTRCallbackBridge_internal.h"

#include <app/CommandSender.h>
#include <app/ReadClient.h>
#include <app/data-model/NullObject.h>
#include <lib/core/CHIPTLV.h>
#include <lib/core/DataModelTypes.h>
#include <lib/support/CHIPMem.h>
Expand Down Expand Up @@ -260,4 +262,145 @@ void MTRReadAttribute(MTRReadParams * _Nonnull params,
std::move(*callbackBridge).DispatchAction(device);
}

/**
* Utility functions base clusters use for doing commands.
*/
template <typename InvokeBridgeType, typename ResponseType> class MTRInvokeCallback : public chip::app::CommandSender::Callback {
public:
MTRInvokeCallback(InvokeBridgeType * _Nonnull bridge, typename InvokeBridgeType::SuccessCallbackType _Nonnull onResponse,
MTRErrorCallback _Nonnull onError)
: mBridge(bridge)
, mOnResponse(onResponse)
, mOnError(onError)
{
}

~MTRInvokeCallback() {}

void AdoptCommandSender(chip::Platform::UniquePtr<chip::app::CommandSender> commandSender)
{
mCommandSender = std::move(commandSender);
}

protected:
// We need to have different OnResponse implementations depending on whether
// ResponseType is DataModel::NullObjectType or not. Since template class methods
// can't be partially specialized (either you have to partially specialize
// the class template, or you have to fully specialize the method), use
// enable_if to deal with this.
void OnResponse(chip::app::CommandSender * commandSender, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::StatusIB & status, chip::TLV::TLVReader * reader) override
{
HandleResponse(commandSender, commandPath, status, reader);
}

/**
* Response handler for data responses.
*/
template <typename T = ResponseType, std::enable_if_t<!std::is_same<T, chip::app::DataModel::NullObjectType>::value, int> = 0>
void HandleResponse(chip::app::CommandSender * commandSender, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::StatusIB & status, chip::TLV::TLVReader * reader)
{
if (mCalledCallback) {
return;
}
mCalledCallback = true;

ResponseType response;
CHIP_ERROR err = CHIP_NO_ERROR;

//
// We're expecting response data in this variant of OnResponse. Consequently, reader should always be
// non-null. If it is, it means we received a success status code instead, which is not what was expected.
//
VerifyOrExit(reader != nullptr, err = CHIP_ERROR_SCHEMA_MISMATCH);

//
// Validate that the data response we received matches what we expect in terms of its cluster and command IDs.
//
VerifyOrExit(
commandPath.mClusterId == ResponseType::GetClusterId() && commandPath.mCommandId == ResponseType::GetCommandId(),
err = CHIP_ERROR_SCHEMA_MISMATCH);

err = chip::app::DataModel::Decode(*reader, response);
SuccessOrExit(err);

mOnResponse(mBridge, response);

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

/**
* Response handler for status responses.
*/
template <typename T = ResponseType, std::enable_if_t<std::is_same<T, chip::app::DataModel::NullObjectType>::value, int> = 0>
void HandleResponse(chip::app::CommandSender * commandSender, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::StatusIB & status, chip::TLV::TLVReader * reader)
{
if (mCalledCallback) {
return;
}
mCalledCallback = true;

//
// If we got a valid reader, it means we received response data that we were not expecting to receive.
//
if (reader != nullptr) {
mOnError(mBridge, CHIP_ERROR_SCHEMA_MISMATCH);
return;
}

chip::app::DataModel::NullObjectType nullResp;
mOnResponse(mBridge, nullResp);
}

void OnError(const chip::app::CommandSender * commandSender, CHIP_ERROR error) override
{
if (mCalledCallback) {
return;
}
mCalledCallback = true;

mOnError(mBridge, error);
}

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

InvokeBridgeType * _Nonnull mBridge;

typename InvokeBridgeType::SuccessCallbackType mOnResponse;
MTRErrorCallback mOnError;
chip::Platform::UniquePtr<chip::app::CommandSender> mCommandSender;
// For reads, we ensure that we make only one data/error callback to our consumer.
bool mCalledCallback = false;
};

template <typename BridgeType, typename RequestDataType>
CHIP_ERROR MTRStartInvokeInteraction(BridgeType * _Nonnull bridge, const RequestDataType & requestData,
chip::Messaging::ExchangeManager & exchangeManager, const chip::SessionHandle & session,
typename BridgeType::SuccessCallbackType successCb, MTRErrorCallback failureCb, chip::EndpointId endpoint,
chip::Optional<uint16_t> timedInvokeTimeoutMs)
{
auto callback = chip::Platform::MakeUnique<MTRInvokeCallback<BridgeType, typename RequestDataType::ResponseType>>(
bridge, successCb, failureCb);
VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY);

auto commandSender
= chip::Platform::MakeUnique<chip::app::CommandSender>(callback.get(), &exchangeManager, timedInvokeTimeoutMs.HasValue());
VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY);

chip::app::CommandPathParams commandPath(endpoint, 0, RequestDataType::GetClusterId(), RequestDataType::GetCommandId(),
chip::app::CommandPathFlags::kEndpointIdValid);
ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestData, timedInvokeTimeoutMs));
ReturnErrorOnFailure(commandSender->SendCommandRequest(session));

callback->AdoptCommandSender(std::move(commandSender));
callback.release();

return CHIP_NO_ERROR;
};

NS_ASSUME_NONNULL_END
4 changes: 2 additions & 2 deletions src/darwin/Framework/CHIP/templates/MTRBaseClusters-src.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ using chip::SessionHandle;
},
{{/if}}
^(ExchangeManager & exchangeManager, const SessionHandle & session, {{>callbackName}}CallbackType successCb, MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) {
auto * typedBridge = static_cast<MTR{{>callbackName}}CallbackBridge *>(bridge);
chip::Optional<uint16_t> timedInvokeTimeoutMs;
ListFreer listFreer;
{{asUpperCamelCase parent.name}}::Commands::{{asUpperCamelCase name}}::Type request;
Expand Down Expand Up @@ -94,8 +95,7 @@ using chip::SessionHandle;
{{/last}}
{{/chip_cluster_command_arguments}}

chip::Controller::{{asUpperCamelCase parent.name}}Cluster cppCluster(exchangeManager, session, self->_endpoint);
return cppCluster.InvokeCommand(request, bridge, successCb, failureCb, timedInvokeTimeoutMs);
return MTRStartInvokeInteraction(typedBridge, request, exchangeManager, session, successCb, failureCb, self->_endpoint, timedInvokeTimeoutMs);
});
std::move(*bridge).DispatchAction(self.device);
}
Expand Down
Loading

0 comments on commit 9843908

Please sign in to comment.