Skip to content

Commit

Permalink
Perf[MQB]: use in-place callbacks in Dispatcher
Browse files Browse the repository at this point in the history
Signed-off-by: Evgeny Malygin <emalygin@bloomberg.net>
  • Loading branch information
678098 committed Feb 19, 2025
1 parent 062d6b0 commit 8b62d79
Show file tree
Hide file tree
Showing 25 changed files with 955 additions and 255 deletions.
86 changes: 86 additions & 0 deletions src/groups/bmq/bmqu/bmqu_managedcallback.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2025 Bloomberg Finance L.P.
// SPDX-License-Identifier: Apache-2.0
//
// 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.

// bmqu_managedcallback.cpp -*-C++-*-
#include <bmqu_managedcallback.h>

#include <bmqscm_version.h>
namespace BloombergLP {
namespace bmqu {

namespace {

class VoidCallback : public bmqu::ManagedCallback::CallbackFunctor {
private:
// PRIVATE DATA
bmqu::ManagedCallback::VoidFunctor d_callback;

public:
// CREATORS
explicit VoidCallback(const bmqu::ManagedCallback::VoidFunctor& callback)
: d_callback(callback)
{
// NOTHING
}

explicit VoidCallback(
bslmf::MovableRef<bmqu::ManagedCallback::VoidFunctor> callback)
: d_callback(bslmf::MovableRefUtil::move(callback))
{
// NOTHING
}

~VoidCallback() BSLS_KEYWORD_OVERRIDE
{
// NOTHING
}

// ACCESSORS
void operator()() const BSLS_KEYWORD_OVERRIDE
{
if (d_callback) {
d_callback();
}
}
};

} // close unnamed namespace

// ---------------------------------------
// struct ManagedCallback::CallbackFunctor
// ---------------------------------------

ManagedCallback::CallbackFunctor::~CallbackFunctor()
{
// NOTHING
}

void ManagedCallback::set(const VoidFunctor& callback)
{
// Preconditions for placement are checked in `place`.
// Destructor is called by `reset` of the holding DispatcherEvent.
new (place<VoidCallback>()) VoidCallback(callback);
}

void ManagedCallback::set(bslmf::MovableRef<VoidFunctor> callback)
{
// Preconditions for placement are checked in `place`.
// Destructor is called by `reset` of the holding DispatcherEvent.
new (place<VoidCallback>())
VoidCallback(bslmf::MovableRefUtil::move(callback));
}

} // close package namespace
} // close enterprise namespace
201 changes: 201 additions & 0 deletions src/groups/bmq/bmqu/bmqu_managedcallback.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Copyright 2025 Bloomberg Finance L.P.
// SPDX-License-Identifier: Apache-2.0
//
// 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.

// bmqu_managedcallback.h -*-C++-*-
#ifndef INCLUDED_BMQU_JSONPRINTER
#define INCLUDED_BMQU_JSONPRINTER

//@PURPOSE: Provide a mechanism to print key-value pairs in JSON format.
//
//@CLASSES:
// bmqu::JsonPrinter: Mechanism to print key-value pairs in JSON format.
//
//@DESCRIPTION: 'bmqu::JsonPrinter' provides a mechanism to print key-value
// pairs in JSON format.
//
/// Usage
///-----
// First, specify field names for printer:
//..
// bsl::vector<const char*> fields;
// fields.push_back("Queue URI");
// fields.push_back("QueueKey");
// fields.push_back("Number of AppIds");
//..
//
// Next, create an instance of bmqu::AlignedPrinter:
//..
// bsl::stringstream output;
// bmqu::JsonPrinter<true, 0, 4> printer(output, &fields);
//..
//
// Last, print field values accordingly:
//..
// bsl::string uri = "bmq://bmq.tutorial.workqueue/sample-queue";
// bsl::string queueKey = "sample";
// const int num = 1;
// printer << uri << queueKey << num;
//..
//

// BDE
#include <bsl_functional.h>
#include <bsl_vector.h>

#include <bslmf_enableif.h>
#include <bsls_assert.h>

namespace BloombergLP {
namespace bmqu {

// ===============
// ManagedCallback
// ===============

class ManagedCallback BSLS_KEYWORD_FINAL {
/// The class useful for in-place construction and passing of functors
/// between different actors.
private:
// DATA
/// Reusable buffer holding the stored callback.
bsl::vector<char> d_callbackBuffer;

/// The flag indicating if `d_callbackBuffer` is empty now.
bool d_empty;

public:
// TRAITS
BSLMF_NESTED_TRAIT_DECLARATION(ManagedCallback, bslma::UsesBslmaAllocator)

// PUBLIC TYPES
/// Signature of a `void` functor method.
typedef bsl::function<void(void)> VoidFunctor;

// ===============
// CallbackFunctor
// ===============
/// The interface for all callback functors passed to ManagedCallback.
struct CallbackFunctor {
// CREATORS
virtual ~CallbackFunctor();

// ACCESSORS
virtual void operator()() const = 0;
};

// CREATORS
/// Create a ManagedCallback object using the optionally specified
/// `allocator`.
explicit ManagedCallback(bslma::Allocator* allocator = 0);

/// Destroy this object.
~ManagedCallback();

// MANIPULATORS
/// Reset the state of this object. If this object stores a callback,
/// call a destructor for it and set this object empty.
void reset();

/// Book and return the allocated memory to store the specified
/// `CALLBACK_TYPE` that has to be inherited from `CallbackFunctor`.
/// Note that it's the user's responsibility to construct a functor object
/// at the provided address.
/// The object must be `empty()` before calling `place()`.
template <class CALLBACK_TYPE>
char* place();

/// Store the specified `callback` in this object.
/// Note: these setters are slow because they performs function copy, going
/// against the basic idea of ManagedCallback. They exist solely for
/// the compatibility with other code where this copy is acceptable.
/// In performance-critical paths, `place` should always be used
/// together with reusable ManagedCallback object(s).
void set(const VoidFunctor& callback);
void set(bslmf::MovableRef<VoidFunctor> callback);

// ACCESSORS
/// Is this object empty or not.
bool empty() const;

/// Call the stored callback.
/// The object must be `!empty()` before calling `operator()`.
void operator()() const;
};

// ============================================================================
// INLINE DEFINITIONS
// ============================================================================

// ---------------
// ManagedCallback
// ---------------

inline ManagedCallback::ManagedCallback(bslma::Allocator* allocator)
: d_callbackBuffer(allocator)
, d_empty(true)
{
// NOTHING
}

inline ManagedCallback::~ManagedCallback()
{
reset();
}

inline void ManagedCallback::reset()
{
if (!d_empty) {
// Not necessary to resize the vector or memset its elements to 0,
// we just call the virtual destructor, and `d_empty` flag
// prevents us from calling outdated callback.
reinterpret_cast<CallbackFunctor*>(d_callbackBuffer.data())
->~CallbackFunctor();
d_empty = true;
}
}

template <class CALLBACK_TYPE>
inline char* ManagedCallback::place()
{
// PRECONDITIONS
BSLS_ASSERT_SAFE(d_empty);
/// The compilation will fail here on the outer `static_cast` if we
/// don't provide a type that is inherited from the base
/// `CallbackFunctor` type.
/// TODO: replace by static_assert on C++ standard update
BSLS_ASSERT_SAFE(0 == static_cast<CALLBACK_TYPE*>(
reinterpret_cast<CallbackFunctor*>(0)));
d_callbackBuffer.resize(sizeof(CALLBACK_TYPE));
d_empty = false;
return d_callbackBuffer.data();
}

inline bool ManagedCallback::empty() const
{
return d_empty;
}

inline void ManagedCallback::operator()() const
{
// PRECONDITIONS
BSLS_ASSERT_SAFE(!d_empty);

(*reinterpret_cast<const CallbackFunctor*>(d_callbackBuffer.data()))();
}

} // close package namespace
} // close enterprise namespace

#endif
Loading

0 comments on commit 8b62d79

Please sign in to comment.