816a047920
Async: Use new [Non]PostedAsyncCont and callOriginalCb This new hierarchy of classes gives us a central mechanism for managing both reply-posting and lockSpec unlocking. * callOriginalCb: Now uses a modern C++ variadic template design enabling it to handle both direct calling and std::bind() re-binding of an arbitrary number of arguments from the caller. This enables us to mostly eliminate the repeated, bespoke definitions of callOriginalCb littered throughout the codebase. We've also propagated these changes throughout the codebase in this patch.
136 lines
3.9 KiB
C++
136 lines
3.9 KiB
C++
#ifndef ASYNCHRONOUS_CONTINUATION_H
|
|
#define ASYNCHRONOUS_CONTINUATION_H
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <componentThread.h>
|
|
#include <lockSpec.h>
|
|
|
|
namespace smo {
|
|
|
|
/**
|
|
* AsynchronousContinuation - Template base class for async sequence management
|
|
*
|
|
* This template provides a common pattern for managing asynchronous operations
|
|
* that need to maintain object lifetime through a sequence of callbacks.
|
|
*
|
|
* The template parameter OriginalCbFnT represents the signature of the original
|
|
* callback that will be invoked when the async sequence completes.
|
|
*/
|
|
template <class OriginalCbFnT>
|
|
class AsynchronousContinuation
|
|
{
|
|
public:
|
|
explicit AsynchronousContinuation(OriginalCbFnT originalCbFn)
|
|
: originalCbFn(std::move(originalCbFn))
|
|
{}
|
|
|
|
/** EXPLANATION:
|
|
* Each numbered segmented sequence persists the lifetime of the
|
|
* continuation object by taking a copy of its shared_ptr.
|
|
*/
|
|
typedef void (SegmentFn)(
|
|
std::shared_ptr<AsynchronousContinuation<OriginalCbFnT>>
|
|
lifetimePreservingConveyance);
|
|
|
|
/**
|
|
* @brief Call the original callback with perfect forwarding
|
|
*
|
|
* IMPORTANT: This method cannot be virtual because templates cannot be
|
|
* virtual in C++. Therefore, this method MUST be called from the
|
|
* most-derived class reference/pointer context. Never call this method
|
|
* through a polymorphic base class reference/pointer, as it will not
|
|
* dispatch to the correct derived class implementation.
|
|
*
|
|
* @param args Arguments to forward to the original callback
|
|
*/
|
|
template<typename... Args>
|
|
void callOriginalCb(Args&&... args)
|
|
{
|
|
if (originalCbFn) { originalCbFn(std::forward<Args>(args)...); }
|
|
}
|
|
|
|
public:
|
|
OriginalCbFnT originalCbFn;
|
|
};
|
|
|
|
/**
|
|
* NonPostedAsynchronousContinuation - For continuations that don't post
|
|
* callbacks
|
|
*
|
|
* Note: We intentionally do not create a
|
|
* LockedNonPostedAsynchronousContinuation because the only way to implement
|
|
* non-posted locking would be via busy-spinning or sleeplocks. This would
|
|
* eliminate the throughput advantage from our Qspinning mechanism, which
|
|
* relies on re-posting to the io_service queue when locks are unavailable.
|
|
*/
|
|
template <class OriginalCbFnT>
|
|
class NonPostedAsynchronousContinuation
|
|
: public AsynchronousContinuation<OriginalCbFnT>
|
|
{
|
|
public:
|
|
explicit NonPostedAsynchronousContinuation(OriginalCbFnT originalCbFn)
|
|
: AsynchronousContinuation<OriginalCbFnT>(originalCbFn)
|
|
{}
|
|
|
|
public:
|
|
using AsynchronousContinuation<OriginalCbFnT>::callOriginalCb;
|
|
};
|
|
|
|
template <class OriginalCbFnT>
|
|
class PostedAsynchronousContinuation
|
|
: public AsynchronousContinuation<OriginalCbFnT>
|
|
{
|
|
public:
|
|
PostedAsynchronousContinuation(
|
|
const std::shared_ptr<ComponentThread> &caller,
|
|
OriginalCbFnT originalCbFn)
|
|
: AsynchronousContinuation<OriginalCbFnT>(originalCbFn),
|
|
caller(caller)
|
|
{}
|
|
|
|
template<typename... Args>
|
|
void callOriginalCb(Args&&... args)
|
|
{
|
|
if (AsynchronousContinuation<OriginalCbFnT>::originalCbFn) {
|
|
caller->getIoService().post(
|
|
std::bind(
|
|
AsynchronousContinuation<OriginalCbFnT>::originalCbFn,
|
|
std::forward<Args>(args)...));
|
|
}
|
|
}
|
|
|
|
public:
|
|
std::shared_ptr<ComponentThread> caller;
|
|
using AsynchronousContinuation<OriginalCbFnT>::callOriginalCb;
|
|
};
|
|
|
|
template <class OriginalCbFnT>
|
|
class SerializedAsynchronousContinuation
|
|
: public PostedAsynchronousContinuation<OriginalCbFnT>, public LockSpec
|
|
{
|
|
public:
|
|
SerializedAsynchronousContinuation(
|
|
const std::shared_ptr<ComponentThread> &caller,
|
|
OriginalCbFnT originalCbFn,
|
|
std::vector<std::reference_wrapper<SpinLock>> requiredLocks = {})
|
|
: PostedAsynchronousContinuation<OriginalCbFnT>(caller, originalCbFn),
|
|
LockSpec(std::move(requiredLocks))
|
|
{}
|
|
|
|
template<typename... Args>
|
|
void callOriginalCb(Args&&... args)
|
|
{
|
|
LockSpec::release();
|
|
PostedAsynchronousContinuation<OriginalCbFnT>::callOriginalCb(
|
|
std::forward<Args>(args)...);
|
|
}
|
|
|
|
public:
|
|
using AsynchronousContinuation<OriginalCbFnT>::callOriginalCb;
|
|
};
|
|
|
|
} // namespace smo
|
|
|
|
#endif // ASYNCHRONOUS_CONTINUATION_H
|