Skip to content

Commit 361c74d

Browse files
Stop using InvokeInteraction APIs for Darwin framework invokes. (project-chip#23640)
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.
1 parent 0d9014d commit 361c74d

File tree

3 files changed

+703
-374
lines changed

3 files changed

+703
-374
lines changed

src/darwin/Framework/CHIP/MTRBaseClustersCpp_Internal.h

+143
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
#import "MTRCluster_internal.h"
2020
#import "zap-generated/MTRCallbackBridge_internal.h"
2121

22+
#include <app/CommandSender.h>
2223
#include <app/ReadClient.h>
24+
#include <app/data-model/NullObject.h>
2325
#include <lib/core/CHIPTLV.h>
2426
#include <lib/core/DataModelTypes.h>
2527
#include <lib/support/CHIPMem.h>
@@ -260,4 +262,145 @@ void MTRReadAttribute(MTRReadParams * _Nonnull params,
260262
std::move(*callbackBridge).DispatchAction(device);
261263
}
262264

265+
/**
266+
* Utility functions base clusters use for doing commands.
267+
*/
268+
template <typename InvokeBridgeType, typename ResponseType> class MTRInvokeCallback : public chip::app::CommandSender::Callback {
269+
public:
270+
MTRInvokeCallback(InvokeBridgeType * _Nonnull bridge, typename InvokeBridgeType::SuccessCallbackType _Nonnull onResponse,
271+
MTRErrorCallback _Nonnull onError)
272+
: mBridge(bridge)
273+
, mOnResponse(onResponse)
274+
, mOnError(onError)
275+
{
276+
}
277+
278+
~MTRInvokeCallback() {}
279+
280+
void AdoptCommandSender(chip::Platform::UniquePtr<chip::app::CommandSender> commandSender)
281+
{
282+
mCommandSender = std::move(commandSender);
283+
}
284+
285+
protected:
286+
// We need to have different OnResponse implementations depending on whether
287+
// ResponseType is DataModel::NullObjectType or not. Since template class methods
288+
// can't be partially specialized (either you have to partially specialize
289+
// the class template, or you have to fully specialize the method), use
290+
// enable_if to deal with this.
291+
void OnResponse(chip::app::CommandSender * commandSender, const chip::app::ConcreteCommandPath & commandPath,
292+
const chip::app::StatusIB & status, chip::TLV::TLVReader * reader) override
293+
{
294+
HandleResponse(commandSender, commandPath, status, reader);
295+
}
296+
297+
/**
298+
* Response handler for data responses.
299+
*/
300+
template <typename T = ResponseType, std::enable_if_t<!std::is_same<T, chip::app::DataModel::NullObjectType>::value, int> = 0>
301+
void HandleResponse(chip::app::CommandSender * commandSender, const chip::app::ConcreteCommandPath & commandPath,
302+
const chip::app::StatusIB & status, chip::TLV::TLVReader * reader)
303+
{
304+
if (mCalledCallback) {
305+
return;
306+
}
307+
mCalledCallback = true;
308+
309+
ResponseType response;
310+
CHIP_ERROR err = CHIP_NO_ERROR;
311+
312+
//
313+
// We're expecting response data in this variant of OnResponse. Consequently, reader should always be
314+
// non-null. If it is, it means we received a success status code instead, which is not what was expected.
315+
//
316+
VerifyOrExit(reader != nullptr, err = CHIP_ERROR_SCHEMA_MISMATCH);
317+
318+
//
319+
// Validate that the data response we received matches what we expect in terms of its cluster and command IDs.
320+
//
321+
VerifyOrExit(
322+
commandPath.mClusterId == ResponseType::GetClusterId() && commandPath.mCommandId == ResponseType::GetCommandId(),
323+
err = CHIP_ERROR_SCHEMA_MISMATCH);
324+
325+
err = chip::app::DataModel::Decode(*reader, response);
326+
SuccessOrExit(err);
327+
328+
mOnResponse(mBridge, response);
329+
330+
exit:
331+
if (err != CHIP_NO_ERROR) {
332+
mOnError(mBridge, err);
333+
}
334+
}
335+
336+
/**
337+
* Response handler for status responses.
338+
*/
339+
template <typename T = ResponseType, std::enable_if_t<std::is_same<T, chip::app::DataModel::NullObjectType>::value, int> = 0>
340+
void HandleResponse(chip::app::CommandSender * commandSender, const chip::app::ConcreteCommandPath & commandPath,
341+
const chip::app::StatusIB & status, chip::TLV::TLVReader * reader)
342+
{
343+
if (mCalledCallback) {
344+
return;
345+
}
346+
mCalledCallback = true;
347+
348+
//
349+
// If we got a valid reader, it means we received response data that we were not expecting to receive.
350+
//
351+
if (reader != nullptr) {
352+
mOnError(mBridge, CHIP_ERROR_SCHEMA_MISMATCH);
353+
return;
354+
}
355+
356+
chip::app::DataModel::NullObjectType nullResp;
357+
mOnResponse(mBridge, nullResp);
358+
}
359+
360+
void OnError(const chip::app::CommandSender * commandSender, CHIP_ERROR error) override
361+
{
362+
if (mCalledCallback) {
363+
return;
364+
}
365+
mCalledCallback = true;
366+
367+
mOnError(mBridge, error);
368+
}
369+
370+
void OnDone(chip::app::CommandSender * commandSender) override { chip::Platform::Delete(this); }
371+
372+
InvokeBridgeType * _Nonnull mBridge;
373+
374+
typename InvokeBridgeType::SuccessCallbackType mOnResponse;
375+
MTRErrorCallback mOnError;
376+
chip::Platform::UniquePtr<chip::app::CommandSender> mCommandSender;
377+
// For reads, we ensure that we make only one data/error callback to our consumer.
378+
bool mCalledCallback = false;
379+
};
380+
381+
template <typename BridgeType, typename RequestDataType>
382+
CHIP_ERROR MTRStartInvokeInteraction(BridgeType * _Nonnull bridge, const RequestDataType & requestData,
383+
chip::Messaging::ExchangeManager & exchangeManager, const chip::SessionHandle & session,
384+
typename BridgeType::SuccessCallbackType successCb, MTRErrorCallback failureCb, chip::EndpointId endpoint,
385+
chip::Optional<uint16_t> timedInvokeTimeoutMs)
386+
{
387+
auto callback = chip::Platform::MakeUnique<MTRInvokeCallback<BridgeType, typename RequestDataType::ResponseType>>(
388+
bridge, successCb, failureCb);
389+
VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY);
390+
391+
auto commandSender
392+
= chip::Platform::MakeUnique<chip::app::CommandSender>(callback.get(), &exchangeManager, timedInvokeTimeoutMs.HasValue());
393+
VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY);
394+
395+
chip::app::CommandPathParams commandPath(endpoint, 0, RequestDataType::GetClusterId(), RequestDataType::GetCommandId(),
396+
chip::app::CommandPathFlags::kEndpointIdValid);
397+
ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestData, timedInvokeTimeoutMs));
398+
ReturnErrorOnFailure(commandSender->SendCommandRequest(session));
399+
400+
callback->AdoptCommandSender(std::move(commandSender));
401+
callback.release();
402+
403+
return CHIP_NO_ERROR;
404+
};
405+
263406
NS_ASSUME_NONNULL_END

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ using chip::SessionHandle;
6767
},
6868
{{/if}}
6969
^(ExchangeManager & exchangeManager, const SessionHandle & session, {{>callbackName}}CallbackType successCb, MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) {
70+
auto * typedBridge = static_cast<MTR{{>callbackName}}CallbackBridge *>(bridge);
7071
chip::Optional<uint16_t> timedInvokeTimeoutMs;
7172
ListFreer listFreer;
7273
{{asUpperCamelCase parent.name}}::Commands::{{asUpperCamelCase name}}::Type request;
@@ -94,8 +95,7 @@ using chip::SessionHandle;
9495
{{/last}}
9596
{{/chip_cluster_command_arguments}}
9697

97-
chip::Controller::{{asUpperCamelCase parent.name}}Cluster cppCluster(exchangeManager, session, self->_endpoint);
98-
return cppCluster.InvokeCommand(request, bridge, successCb, failureCb, timedInvokeTimeoutMs);
98+
return MTRStartInvokeInteraction(typedBridge, request, exchangeManager, session, successCb, failureCb, self->_endpoint, timedInvokeTimeoutMs);
9999
});
100100
std::move(*bridge).DispatchAction(self.device);
101101
}

0 commit comments

Comments
 (0)