Async: new hierachy; manages reply posting and unlocking
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.
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <componentThread.h>
|
||||
#include <lockSpec.h>
|
||||
|
||||
namespace smo {
|
||||
|
||||
@@ -15,20 +16,6 @@ namespace smo {
|
||||
*
|
||||
* The template parameter OriginalCbFnT represents the signature of the original
|
||||
* callback that will be invoked when the async sequence completes.
|
||||
*
|
||||
* Usage:
|
||||
* class MyAsyncReq
|
||||
* : public AsynchronousContinuation<std::function<void(bool)>>
|
||||
* {
|
||||
* public:
|
||||
* MyAsyncReq(std::function<void(bool)> originalCbFn)
|
||||
* : AsynchronousContinuation(originalCbFn)
|
||||
* {}
|
||||
*
|
||||
* // Segment methods take only the shared_ptr for lifetime management
|
||||
* void myAsyncReq1(std::shared_ptr<MyAsyncReq> context);
|
||||
* void myAsyncReq2(std::shared_ptr<MyAsyncReq> context);
|
||||
* };
|
||||
*/
|
||||
template <class OriginalCbFnT>
|
||||
class AsynchronousContinuation
|
||||
@@ -46,33 +33,101 @@ public:
|
||||
std::shared_ptr<AsynchronousContinuation<OriginalCbFnT>>
|
||||
lifetimePreservingConveyance);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @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;
|
||||
};
|
||||
|
||||
class ContinuationTarget
|
||||
/**
|
||||
* 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:
|
||||
ContinuationTarget(
|
||||
const std::shared_ptr<ComponentThread> &caller)
|
||||
: caller(caller)
|
||||
explicit NonPostedAsynchronousContinuation(OriginalCbFnT originalCbFn)
|
||||
: AsynchronousContinuation<OriginalCbFnT>(originalCbFn)
|
||||
{}
|
||||
|
||||
public:
|
||||
const std::shared_ptr<ComponentThread> caller;
|
||||
using AsynchronousContinuation<OriginalCbFnT>::callOriginalCb;
|
||||
};
|
||||
|
||||
template <class OriginalCbFnT>
|
||||
class TargetedAsynchronousContinuation
|
||||
: public AsynchronousContinuation<OriginalCbFnT>, public ContinuationTarget
|
||||
class PostedAsynchronousContinuation
|
||||
: public AsynchronousContinuation<OriginalCbFnT>
|
||||
{
|
||||
public:
|
||||
TargetedAsynchronousContinuation(
|
||||
PostedAsynchronousContinuation(
|
||||
const std::shared_ptr<ComponentThread> &caller,
|
||||
OriginalCbFnT originalCbFn)
|
||||
: AsynchronousContinuation<OriginalCbFnT>(originalCbFn),
|
||||
ContinuationTarget(caller)
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user