diff --git a/src/applications/bmqtool/m_bmqtool_application.cpp b/src/applications/bmqtool/m_bmqtool_application.cpp index 4b27be27b..09cdd8509 100644 --- a/src/applications/bmqtool/m_bmqtool_application.cpp +++ b/src/applications/bmqtool/m_bmqtool_application.cpp @@ -71,6 +71,54 @@ namespace { BALL_LOG_SET_NAMESPACE_CATEGORY("BMQTOOL.APPLICATION"); +/// Stack-built functor to pass to `bmqp::ProtocolUtil::buildEvent` +struct BuildConfirmFunctor +: public bmqp::ProtocolUtil::BuildEventActionFunctor { + // DATA + bmqa::ConfirmEventBuilder& d_confirmBuilder; + const bmqa::Message& d_message; + + // CREATORS + inline explicit BuildConfirmFunctor( + bmqa::ConfirmEventBuilder& confirmBuilder, + const bmqa::Message& message) + : d_confirmBuilder(confirmBuilder) + , d_message(message) + { + // NOTHING + } + + // MANIPULATORS + inline bmqt::EventBuilderResult::Enum operator()() BSLS_KEYWORD_OVERRIDE + { + return d_confirmBuilder.addMessageConfirmation(d_message); + } +}; + +/// Stack-built functor to pass to `bmqp::ProtocolUtil::buildEvent` +struct BuildConfirmOverflowFunctor +: public bmqp::ProtocolUtil::BuildEventOverflowFunctor { + // DATA + bmqa::Session& d_session; + bmqa::ConfirmEventBuilder& d_builder; + + // CREATORS + inline explicit BuildConfirmOverflowFunctor( + bmqa::Session& session, + bmqa::ConfirmEventBuilder& builder) + : d_session(session) + , d_builder(builder) + { + // NOTHING + } + + // MANIPULATORS + inline void operator()() BSLS_KEYWORD_OVERRIDE + { + d_session.confirmMessages(&d_builder); + } +}; + } // close unnamed namespace // ----------------- @@ -685,10 +733,9 @@ void Application::onMessageEvent(const bmqa::MessageEvent& event) bmqt::EventBuilderResult::Enum rc = bmqp::ProtocolUtil::buildEvent( - bdlf::BindUtil::bind(f, &confirmBuilder, message), - bdlf::BindUtil::bind(&bmqa::Session::confirmMessages, - d_session_mp.get(), - &confirmBuilder)); + BuildConfirmFunctor(confirmBuilder, message), + BuildConfirmOverflowFunctor(*d_session_mp.get(), + confirmBuilder)); BSLS_ASSERT_SAFE(rc == 0); (void)rc; // compiler happiness diff --git a/src/groups/bmq/bmqp/bmqp_protocolutil.cpp b/src/groups/bmq/bmqp/bmqp_protocolutil.cpp index b429b5f96..33bfc5fcb 100644 --- a/src/groups/bmq/bmqp/bmqp_protocolutil.cpp +++ b/src/groups/bmq/bmqp/bmqp_protocolutil.cpp @@ -105,6 +105,24 @@ const char k_HEX_TO_INT_TABLE[24] = {0, 1, 2, 3, 4, 5, 6, 7, 99, 10, 11, 12, 13, 14, 15, 99}; } // close unnamed namespace +// -------------------------------------------- +// struct ProtocolUtil::BuildEventActionFunctor +// -------------------------------------------- + +ProtocolUtil::BuildEventActionFunctor::~BuildEventActionFunctor() +{ + // NOTHING +} + +// ---------------------------------------------- +// struct ProtocolUtil::BuildEventOverflowFunctor +// ---------------------------------------------- + +ProtocolUtil::BuildEventOverflowFunctor::~BuildEventOverflowFunctor() +{ + // NOTHING +} + // ------------------- // struct ProtocolUtil // ------------------- diff --git a/src/groups/bmq/bmqp/bmqp_protocolutil.h b/src/groups/bmq/bmqp/bmqp_protocolutil.h index b8c2af80f..d05cca3dd 100644 --- a/src/groups/bmq/bmqp/bmqp_protocolutil.h +++ b/src/groups/bmq/bmqp/bmqp_protocolutil.h @@ -84,6 +84,32 @@ namespace bmqp { struct ProtocolUtil { // PUBLIC TYPES + /// The common interface for functors passed to `buildEvent`. + /// These functors exist as an alternative of using `bsl::function` on + /// critical performance paths, since they could be built on stack without + /// arguments copying and memory allocations/deallocations. The price of + /// using virtual table is small compared to `bsl::function`. + /// Usage: inherit from this functor locally, override the () operator and + /// store some arguments in the custom functor if needed. + struct BuildEventActionFunctor { + virtual ~BuildEventActionFunctor(); + + virtual bmqt::EventBuilderResult::Enum operator()() = 0; + }; + + /// The common interface for functors passed to `buildEvent`. + /// These functors exist as an alternative of using `bsl::function` on + /// critical performance paths, since they could be built on stack without + /// arguments copying and memory allocations/deallocations. The price of + /// using virtual table is small compared to `bsl::function`. + /// Usage: inherit from this functor locally, override the () operator and + /// store some arguments in the custom functor if needed. + struct BuildEventOverflowFunctor { + virtual ~BuildEventOverflowFunctor(); + + virtual void operator()() = 0; + }; + template struct QueueInfo { struct StreamInfo { @@ -399,10 +425,19 @@ struct ProtocolUtil { /// Invoke the specified `action` and if it returns e_EVENT_TOO_BIG then /// invoke the specified `overflowCb` and call `action` again. Return /// result code returned from `action` + /// DEPRECATED: use `buildEvent` with functors instead, to avoid loss of + /// performance from `bsl::function` on critical paths. static bmqt::EventBuilderResult::Enum buildEvent( const bsl::function& action, const bsl::function& overflowCb); + /// Invoke the specified `action` and if it returns e_EVENT_TOO_BIG then + /// invoke the specified `overflowCb` and call `action` again. Return + /// result code returned from `action` + static bmqt::EventBuilderResult::Enum + buildEvent(BuildEventActionFunctor& action, + BuildEventOverflowFunctor& overflowCb); + /// Encode Receipt into the specified `blob` for the specified /// `partitionId`, `primaryLeaseId`, and `sequenceNumber`. static void buildReceipt(bdlbb::Blob* blob, @@ -708,6 +743,21 @@ inline bmqt::EventBuilderResult::Enum ProtocolUtil::buildEvent( return rc; }; +inline bmqt::EventBuilderResult::Enum +ProtocolUtil::buildEvent(BuildEventActionFunctor& action, + BuildEventOverflowFunctor& overflowCb) +{ + bmqt::EventBuilderResult::Enum rc = action(); + if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY( + bmqt::EventBuilderResult::e_EVENT_TOO_BIG == rc)) { + BSLS_PERFORMANCEHINT_UNLIKELY_HINT; + + overflowCb(); + rc = action(); + } + return rc; +}; + inline void ProtocolUtil::buildReceipt(bdlbb::Blob* blob, int partitionId, unsigned int primaryLeaseId, diff --git a/src/groups/mqb/mqba/mqba_clientsession.cpp b/src/groups/mqb/mqba/mqba_clientsession.cpp index 0d1d5674c..4ddc14961 100644 --- a/src/groups/mqb/mqba/mqba_clientsession.cpp +++ b/src/groups/mqb/mqba/mqba_clientsession.cpp @@ -287,6 +287,57 @@ void finalizeClosedHandle(bsl::string description, << handle->handleParameters(); } +/// Stack-built functor to pass to `bmqp::ProtocolUtil::buildEvent` +struct BuildAckFunctor : public bmqp::ProtocolUtil::BuildEventActionFunctor { + // DATA + bmqp::AckEventBuilder& d_ackBuilder; + int d_status; + int d_correlationId; + const bmqt::MessageGUID& d_messageGUID; + int d_queueId; + + // CREATORS + inline explicit BuildAckFunctor(bmqp::AckEventBuilder& ackBuilder, + int status, + int correlationId, + const bmqt::MessageGUID& messageGUID, + int queueId) + : d_ackBuilder(ackBuilder) + , d_status(status) + , d_correlationId(correlationId) + , d_messageGUID(messageGUID) + , d_queueId(queueId) + { + // NOTHING + } + + // MANIPULATORS + inline bmqt::EventBuilderResult::Enum operator()() BSLS_KEYWORD_OVERRIDE + { + return d_ackBuilder.appendMessage(d_status, + d_correlationId, + d_messageGUID, + d_queueId); + } +}; + +/// Stack-built functor to pass to `bmqp::ProtocolUtil::buildEvent` +struct BuildAckOverflowFunctor +: public bmqp::ProtocolUtil::BuildEventOverflowFunctor { + // DATA + mqba::ClientSession& d_session; + + // CREATORS + inline explicit BuildAckOverflowFunctor(mqba::ClientSession& session) + : d_session(session) + { + // NOTHING + } + + // MANIPULATORS + inline void operator()() BSLS_KEYWORD_OVERRIDE { d_session.flush(); } +}; + } // close unnamed namespace // ------------------------- @@ -560,13 +611,12 @@ void ClientSession::sendAck(bmqt::AckResult::Enum status, // Append the ACK to the ackBuilder bmqt::EventBuilderResult::Enum rc = bmqp::ProtocolUtil::buildEvent( - bdlf::BindUtil::bind(&bmqp::AckEventBuilder::appendMessage, - &d_state.d_ackBuilder, - bmqp::ProtocolUtil::ackResultToCode(status), - correlationId, - messageGUID, - queueId), - bdlf::BindUtil::bind(&ClientSession::flush, this)); + BuildAckFunctor(d_state.d_ackBuilder, + bmqp::ProtocolUtil::ackResultToCode(status), + correlationId, + messageGUID, + queueId), + BuildAckOverflowFunctor(*this)); if (rc != bmqt::EventBuilderResult::e_SUCCESS) { BALL_LOG_ERROR << "Failed to append ACK [rc: " << rc << ", source: '"