mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +00:00
1015 lines
34 KiB
C++
1015 lines
34 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#ifndef BEAST_HANDLERCALL_H_INCLUDED
|
|
#define BEAST_HANDLERCALL_H_INCLUDED
|
|
|
|
/** A polymorphic Handler that can wrap any other handler.
|
|
|
|
This is very lightweight container that just holds a shared pointer
|
|
to the actual handler. This means it can be copied cheaply. The
|
|
allocation and deallocation of the handler is performed according
|
|
to the requirements of asio_handler_allocate and asio_handler_delete.
|
|
All calls also satisfy the safety guarantees of asio_handler_invoke.
|
|
|
|
All constructors will take ownership of the passed in handler
|
|
if your compiler has MoveConstructible and MoveAssignable support enabled.
|
|
|
|
Supports these concepts:
|
|
DefaultConstructible
|
|
MoveConstructible (C++11)
|
|
CopyConstructible
|
|
MoveAssignable (C++11)
|
|
CopyAssignable
|
|
Destructible
|
|
*/
|
|
class HandlerCall
|
|
{
|
|
private:
|
|
typedef boost::system::error_code error_code;
|
|
typedef boost::function <void(void)> invoked_type;
|
|
|
|
// Forward declarations needed for friendship
|
|
template <typename>
|
|
struct CallType;
|
|
struct Call;
|
|
|
|
public:
|
|
typedef void result_type;
|
|
|
|
struct Context;
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
/** HandlerCall construction tags.
|
|
|
|
These tags are used at the end of HandlerCall constructor parameter
|
|
lists to tell it what kind of Handler you are passing. For example:
|
|
|
|
@code
|
|
|
|
struct MyClass
|
|
{
|
|
void on_connect (error_code const& ec);
|
|
|
|
void connect (Address address)
|
|
{
|
|
HandlerCall myHandler (
|
|
bind (&MyClass::foo, this),
|
|
Connect ());
|
|
|
|
socket.async_connect (address, myHandler);
|
|
}
|
|
};
|
|
|
|
@endcode
|
|
|
|
It would be nice if we could deduce the type of handler from the template
|
|
argument alone, but the return value of most implementations of bind seem
|
|
to satisfy the traits of ANY handler so that was scrapped.
|
|
*/
|
|
/** @{ */
|
|
// These are the three basic supported function signatures
|
|
//
|
|
// ComposedConnectHandler is to-do
|
|
//
|
|
struct Post { }; // void()
|
|
struct Error { }; // void(error_code)
|
|
struct Transfer { }; // void(error_code, std::size_t)
|
|
|
|
// CompletionHandler
|
|
//
|
|
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/CompletionHandler.html
|
|
//
|
|
typedef Post Completion;
|
|
|
|
// AcceptHandler
|
|
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/AcceptHandler.html
|
|
//
|
|
typedef Error Accept;
|
|
|
|
// ConnectHandler
|
|
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/ConnectHandler.html
|
|
//
|
|
typedef Error Connect;
|
|
|
|
// ShutdownHandler
|
|
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/ShutdownHandler.html
|
|
//
|
|
typedef Error Shutdown;
|
|
|
|
// HandshakeHandler
|
|
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/HandshakeHandler.html
|
|
//
|
|
typedef Error Handshake;
|
|
|
|
// ReadHandler
|
|
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/ReadHandler.html
|
|
//
|
|
typedef Transfer Read;
|
|
|
|
// WriteHandler
|
|
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/WriteHandler.html
|
|
//
|
|
typedef Transfer Write;
|
|
|
|
// BufferedHandshakeHandler
|
|
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/BufferedHandshakeHandler.html
|
|
//
|
|
typedef Transfer BufferedHandshake;
|
|
/** @} */
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
/** Construct a null HandlerCall.
|
|
|
|
A default constructed handler has no associated call. Passing
|
|
it as a handler to an asynchronous operation will result in
|
|
undefined behavior. This constructor exists so that you can do
|
|
things like have a class member "originalHandler" which starts
|
|
out as null, and then later assign it. Routines are provided to
|
|
test the handler against null.
|
|
*/
|
|
HandlerCall () noexcept;
|
|
|
|
/** Construct a HandlerCall.
|
|
|
|
Handler must meet this requirement:
|
|
CompletionHandler
|
|
|
|
HandlerCall will meet this requirement:
|
|
CompletionHandler
|
|
*/
|
|
/** @{ */
|
|
template <typename Handler>
|
|
HandlerCall (Post, BOOST_ASIO_MOVE_ARG(Handler) handler)
|
|
: m_call (construct <PostCallType> (Context (),
|
|
BOOST_ASIO_MOVE_CAST(Handler)(handler)))
|
|
{
|
|
}
|
|
|
|
template <typename Handler>
|
|
HandlerCall (Post, Context context, BOOST_ASIO_MOVE_ARG(Handler) handler)
|
|
: m_call (construct <PostCallType> (context,
|
|
BOOST_ASIO_MOVE_CAST(Handler)(handler)))
|
|
{
|
|
}
|
|
/** @} */
|
|
|
|
/** Construct a HandlerCall with one bound parameter.
|
|
|
|
Produce a CompletionHandler that includes one bound parameter.
|
|
This can be useful if you want to call io_service::post() on
|
|
the handler and you need to give it an already existing value,
|
|
like an error_code.
|
|
|
|
Invoking operator() on the HandlerCall will be the same as:
|
|
|
|
@code
|
|
|
|
handler (arg1);
|
|
|
|
@endcode
|
|
|
|
Handler must meet one of these requirements:
|
|
AcceptHandler
|
|
ConnectHandler
|
|
ShutdownHandler
|
|
HandshakeHandler
|
|
|
|
HandlerCall will meet this requirement:
|
|
CompletionHandler
|
|
*/
|
|
/** @{ */
|
|
template <typename Handler, typename Arg1>
|
|
HandlerCall (Post, BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1)
|
|
: m_call (construct <PostCallType1> (Context (),
|
|
BOOST_ASIO_MOVE_CAST(Handler)(handler), arg1))
|
|
{
|
|
}
|
|
|
|
template <typename Handler, typename Arg1>
|
|
HandlerCall (Post, Context context, BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1)
|
|
: m_call (construct <PostCallType1> (context,
|
|
BOOST_ASIO_MOVE_CAST(Handler)(handler), arg1))
|
|
{
|
|
}
|
|
/** @} */
|
|
|
|
/** Construct a HandlerCall with two bound parameters.
|
|
|
|
Produce a CompletionHandler that includes two bound parameters.
|
|
This can be useful if you want to call io_service::post() on
|
|
the handler and you need to give it two already existing values,
|
|
like an error_code and bytes_transferred.
|
|
|
|
Invoking operator() on the HandlerCall will be the same as:
|
|
|
|
@code
|
|
|
|
handler (arg1, arg2);
|
|
|
|
@endcode
|
|
|
|
Handler must meet one of these requirements:
|
|
ReadHandler
|
|
WriteHandler
|
|
BufferedHandshakeHandler
|
|
|
|
The HandlerCall will meet these requirements:
|
|
CompletionHandler
|
|
*/
|
|
/** @{ */
|
|
template <typename Handler, typename Arg1, typename Arg2>
|
|
HandlerCall (Post, BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1, Arg2 arg2)
|
|
: m_call (construct <PostCallType2> (Context (),
|
|
BOOST_ASIO_MOVE_CAST(Handler)(handler), arg1, arg2))
|
|
{
|
|
}
|
|
|
|
template <typename Handler, typename Arg1, typename Arg2>
|
|
HandlerCall (Post, Context context, BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1, Arg2 arg2)
|
|
: m_call (construct <PostCallType2> (context,
|
|
BOOST_ASIO_MOVE_CAST(Handler)(handler), arg1, arg2))
|
|
{
|
|
}
|
|
/** @} */
|
|
|
|
/** Construct a HandlerCall from a handler that takes an error_code
|
|
|
|
Handler must meet one of these requirements:
|
|
AcceptHandler
|
|
ConnectHandler
|
|
ShutdownHandler
|
|
HandshakeHandler
|
|
|
|
The HandlerCall will meet these requirements:
|
|
AcceptHandler
|
|
ConnectHandler
|
|
ShutdownHandler
|
|
HandshakeHandler
|
|
*/
|
|
/** @{ */
|
|
template <typename Handler>
|
|
HandlerCall (Error, BOOST_ASIO_MOVE_ARG(Handler) handler)
|
|
: m_call (construct <ErrorCallType> (Context (),
|
|
BOOST_ASIO_MOVE_CAST(Handler)(handler)))
|
|
{
|
|
}
|
|
|
|
template <typename Handler>
|
|
HandlerCall (Error, Context context, BOOST_ASIO_MOVE_ARG(Handler) handler)
|
|
: m_call (construct <ErrorCallType> (context,
|
|
BOOST_ASIO_MOVE_CAST(Handler)(handler)))
|
|
{
|
|
}
|
|
/** @} */
|
|
|
|
/** Construct a HandlerCall from a handler that takes an error_code and std::size
|
|
|
|
Handler must meet one of these requirements:
|
|
ReadHandler
|
|
WriteHandler
|
|
BufferedHandshakeHandler
|
|
|
|
The HandlerCall will meet these requirements:
|
|
ReadHandler
|
|
WriteHandler
|
|
BufferedHandshakeHandler
|
|
*/
|
|
/** @{ */
|
|
template <typename Handler>
|
|
HandlerCall (Transfer, BOOST_ASIO_MOVE_ARG(Handler) handler)
|
|
: m_call (construct <TransferCallType> (Context (),
|
|
BOOST_ASIO_MOVE_CAST(Handler)(handler)))
|
|
{
|
|
}
|
|
|
|
template <typename Handler>
|
|
HandlerCall (Transfer, Context context, BOOST_ASIO_MOVE_ARG(Handler) handler)
|
|
: m_call (construct <TransferCallType> (context,
|
|
BOOST_ASIO_MOVE_CAST(Handler)(handler)))
|
|
{
|
|
}
|
|
/** @} */
|
|
|
|
/** Copy construction and assignment.
|
|
|
|
HandlerCall is a very lightweight object that holds a shared
|
|
pointer to the wrapped handler. It is cheap to copy and pass
|
|
around.
|
|
|
|
Construction and assignment from other HandlerCall objects
|
|
executes in constant time, does not allocate or free memory,
|
|
and invokes no methods calls on the wrapped handler, except
|
|
for the case where the last reference on an existing handler
|
|
is removed.
|
|
*/
|
|
/** @{ */
|
|
HandlerCall (HandlerCall const& other) noexcept;
|
|
HandlerCall& operator= (HandlerCall const& other) noexcept;
|
|
|
|
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
|
HandlerCall (HandlerCall&& other) noexcept;
|
|
HandlerCall& operator= (HandlerCall&& other) noexcept;
|
|
#endif
|
|
/** @} */
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
/** Returns true if the HandlerCall is null.
|
|
|
|
A null HandlerCall object may not be passed to
|
|
an asynchronous completion function.
|
|
*/
|
|
bool isNull () const noexcept;
|
|
|
|
/** Returns true if the HandlerCall is not null. */
|
|
bool isNotNull () const noexcept;
|
|
|
|
/** Retrieve the context associated with a handler.
|
|
|
|
The context will only be valid while the handler exists. In particular,
|
|
if the handler is dispatched all references to its context will become
|
|
invalid, and undefined behavior will result.
|
|
|
|
The typical use case is to acquire the context from a caller provided
|
|
completion handler, and use the context in a series of composed
|
|
operations. At the conclusion of the composed operations, the original
|
|
handler is invoked and the context is no longer needed.
|
|
|
|
Various methods are provided for easily creating your own completion
|
|
handlers that are associated with an existing context.
|
|
*/
|
|
Context getContext () const noexcept;
|
|
|
|
/** Determine if this handler is the final handler in a composed chain.
|
|
A Context is usually shared during a composed operation.
|
|
Normally you won't need to call this but it's useful for diagnostics.
|
|
Really what this means it that the context is its own wrapped handler.
|
|
*/
|
|
bool isFinal () const noexcept;
|
|
|
|
/** Mark this handler as part of a composed operation.
|
|
|
|
This is only valid to call if the handler is not already sharing
|
|
the context of another handler (i.e. it is already part of a
|
|
composed operation).
|
|
|
|
Any handlers that share the same context will result in true being
|
|
passed to the asio_is_continuation_hook. When you are ready to
|
|
issue the final call to the original handler (which will also
|
|
destroy the context), call endComposed on the original
|
|
handler.
|
|
|
|
To be clear, beginComposed and endComposed are called on the same
|
|
HandlerCall object, and that object was not constructed from another
|
|
context.
|
|
|
|
@see isComposed, endComposed
|
|
*/
|
|
HandlerCall const& beginComposed () const noexcept;
|
|
|
|
/** Indicate the end of a composed operation.
|
|
|
|
A composed operation starts with a handler that uses itself as its own
|
|
context. The composed operation issues new asynchronous calls with its
|
|
own callback, using the original handler's context. To optimize the
|
|
strategy for calling completion handlers, call beginComposed.
|
|
*/
|
|
HandlerCall const& endComposed () const noexcept;
|
|
|
|
/** Invoke the wrapped handler.
|
|
|
|
Normally you will not need to call this yourself. Since this is a
|
|
polymorphic wrapper, any attempt to use an operator that doesn't
|
|
correspond to the signature of the wrapped handler (taking into
|
|
account arguments bound at construction), will result in a fatal
|
|
error at run-time.
|
|
*/
|
|
/** @{ */
|
|
void operator() ();
|
|
void operator() (error_code const& ec);
|
|
void operator() (error_code const& ec, std::size_t bytes_transferred);
|
|
/** @} */
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
/** The context of execution of a particular handler.
|
|
|
|
When writing composed operations (a sequence of asynchronous function
|
|
calls), it is important that the intermediate handlers run in the same
|
|
context as the handler originally provided to signal the end of the
|
|
composed operation.
|
|
|
|
An object of this type abstracts the execution context of any handler.
|
|
You can extract the context from an existing handler and associate
|
|
new handlers you create with that context. This allows composed
|
|
operations to be written easily, lifting the burden of meeting the
|
|
composed operation requirements.
|
|
|
|
In all cases, the Context will only be valid while the original handler
|
|
exists. It is the caller's responsibility to manage the usage and
|
|
lifetimes of these objects.
|
|
|
|
Context objects are lightweight and just hold a reference to the
|
|
underlying context. They are cheap to copy and pass around.
|
|
|
|
Supports these concepts:
|
|
DefaultConstructible
|
|
CopyConstructible
|
|
CopyAssignable
|
|
Destructible
|
|
*/
|
|
struct Context
|
|
{
|
|
/** Construct a null Context.
|
|
When a null Context is specified as the Context to use when
|
|
creating a HandlerCall, it means to use the wrapped handler
|
|
as its own context. This is the default behavior.
|
|
*/
|
|
Context () noexcept;
|
|
|
|
/** Construct a Context from another Context. */
|
|
Context (Context const& other) noexcept;
|
|
|
|
/** Construct a Context from an existing handler's Context. */
|
|
Context (HandlerCall const& handler) noexcept;
|
|
|
|
/** Assign this Context from another Context. */
|
|
Context& operator= (Context other) noexcept;
|
|
|
|
/** Determine if this context is a composed asynchronous operation.
|
|
|
|
When a handler begins a composed operation it becomes its own
|
|
context (it is not constructed with a specified context). When
|
|
a composed operation starts, this will return true for all
|
|
handlers which share the context including the original handler.
|
|
|
|
You have to indicate that a composed operation is starting by
|
|
calling beginComposed on the original handler, performing
|
|
your operations using its context, and then call endComposed
|
|
before calling the original handler.
|
|
|
|
@see beginComposed, endComposed
|
|
*/
|
|
bool isComposed () const noexcept;
|
|
|
|
/** Determine whether or not this Context is a null Context.
|
|
Note that a non-null Context is no guarantee of
|
|
the validity of the context.
|
|
*/
|
|
/** @{ */
|
|
bool isNull () const noexcept;
|
|
bool isNotNull () const noexcept;
|
|
/** @} */
|
|
|
|
/** Compare two contexts.
|
|
This determines if the two contexts refer to the same underlying
|
|
completion handler and would have the same execution guarantees.
|
|
The behavior is undefined if either of the contexts have been
|
|
destroyed.
|
|
*/
|
|
/** @{ */
|
|
bool operator== (Context other) const noexcept;
|
|
bool operator!= (Context other) const noexcept;
|
|
/** @} */
|
|
|
|
/** Allocate and deallocate memory.
|
|
This takes into account any hooks installed by the context.
|
|
Normally you wont need to call this unless you are writing your
|
|
own asio_handler_invoke hook, or creating wrapped handlers.
|
|
*/
|
|
/** @{ */
|
|
void* allocate (std::size_t size) const;
|
|
void deallocate (void* p, std::size_t size) const;
|
|
/** @} */
|
|
|
|
/** Invoke the specified Function on the context.
|
|
|
|
This is equivalent to the following:
|
|
|
|
@code
|
|
|
|
template <typename Function>
|
|
void callOnContext (Function f, Context& context)
|
|
{
|
|
using boost_asio_handler_invoke_helpers;
|
|
invoke <Function, Context> (f, context);
|
|
}
|
|
|
|
@endcode
|
|
|
|
Usually you won't need to call this unless you
|
|
are writing your own asio_handler_invoke hook.
|
|
|
|
The boost::function object is created partially on the
|
|
stack, using a custom Allocator which calls into the handler's
|
|
context to perform allocation and deallocation. We take ownership
|
|
of the passed Function object.
|
|
|
|
All of the safety guarantees of the original context are preserved
|
|
when going through this invoke function.
|
|
*/
|
|
template <typename Function>
|
|
void invoke (BOOST_ASIO_MOVE_ARG(Function) f)
|
|
{
|
|
invoked_type invoked (BOOST_ASIO_MOVE_CAST(Function)(f),
|
|
Allocator <invoked_type> (*this));
|
|
m_call->invoke (invoked);
|
|
}
|
|
|
|
bool operator== (Call const* call) const noexcept;
|
|
bool operator!= (Call const* call) const noexcept;
|
|
|
|
private:
|
|
template <typename Handler>
|
|
friend struct CallType;
|
|
friend struct Call;
|
|
|
|
Context (Call* call) noexcept;
|
|
|
|
// Note that we only store a pointer here. If the original
|
|
// Call is destroyed, the context will become invalid.
|
|
//
|
|
Call* m_call;
|
|
};
|
|
|
|
private:
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// Implementation
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
// These construct the Call, a reference counted polymorphic wrapper around
|
|
// the handler and its context. We use MoveAssignable (rvalue-references)
|
|
// assignments to take ownership of the object and bring it to our stack.
|
|
// From there, we use the context's hooked allocation function to create a
|
|
// single piece of memory to hold our wrapper. Then we use placement new to
|
|
// construct the wrapper. The wrapper uses rvalue assignment to take
|
|
// ownership of the handler from the stack.
|
|
|
|
template <template <typename> class Container, typename Handler>
|
|
static Call* construct (Context context, BOOST_ASIO_MOVE_ARG(Handler) handler)
|
|
{
|
|
typedef Container <Handler> ContainerType;
|
|
Handler local (BOOST_ASIO_MOVE_CAST(Handler)(handler));
|
|
std::size_t const size (sizeof (ContainerType));
|
|
void* const p = boost_asio_handler_alloc_helpers::
|
|
allocate <Handler> (size, local);
|
|
return ::new (p) ContainerType (context, size,
|
|
BOOST_ASIO_MOVE_CAST(Handler)(local));
|
|
}
|
|
|
|
template <template <typename, typename> class Container,
|
|
typename Handler, typename Arg1>
|
|
static Call* construct (Context context, BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1)
|
|
{
|
|
typedef Container <Handler, Arg1> ContainerType;
|
|
Handler local (BOOST_ASIO_MOVE_CAST(Handler)(handler));
|
|
std::size_t const size (sizeof (ContainerType));
|
|
void* const p = boost_asio_handler_alloc_helpers::
|
|
allocate <Handler> (size, local);
|
|
return ::new (p) ContainerType (context, size,
|
|
BOOST_ASIO_MOVE_CAST(Handler)(local), arg1);
|
|
}
|
|
|
|
template <template <typename, typename, typename> class Container,
|
|
typename Handler, typename Arg1, typename Arg2>
|
|
static Call* construct (Context context, BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1, Arg2 arg2)
|
|
{
|
|
typedef Container <Handler, Arg1, Arg2> ContainerType;
|
|
Handler local (BOOST_ASIO_MOVE_CAST(Handler)(handler));
|
|
std::size_t const size (sizeof (ContainerType));
|
|
void* const p = boost_asio_handler_alloc_helpers::
|
|
allocate <Handler> (size, local);
|
|
return ::new (p) ContainerType (context, size,
|
|
BOOST_ASIO_MOVE_CAST(Handler)(local), arg1, arg2);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// Custom Allocator compatible with std::allocator and boost::function
|
|
// which uses the underlying context to allocate memory. This is vastly
|
|
// more efficient in a variety of situations especially during an upcall.
|
|
//
|
|
// The context must be valid for the duration of the invocation.
|
|
//
|
|
template <typename T>
|
|
struct Allocator
|
|
{
|
|
typedef T value_type;
|
|
typedef T* pointer;
|
|
typedef T& reference;
|
|
typedef T const* const_pointer;
|
|
typedef T const& const_reference;
|
|
typedef std::size_t size_type;
|
|
typedef std::ptrdiff_t difference_type;
|
|
|
|
// Make sure this is the right context!
|
|
//
|
|
explicit Allocator (Context context)
|
|
: m_context (context)
|
|
{
|
|
}
|
|
|
|
template <typename U>
|
|
Allocator (Allocator <U> const& other)
|
|
: m_context (other.m_context)
|
|
{
|
|
}
|
|
|
|
template <typename U>
|
|
struct rebind
|
|
{
|
|
typedef Allocator <U> other;
|
|
};
|
|
|
|
pointer address (reference x) const
|
|
{
|
|
return &x;
|
|
}
|
|
|
|
const_pointer address (const_reference x) const
|
|
{
|
|
return &x;
|
|
}
|
|
|
|
pointer allocate (size_type n) const
|
|
{
|
|
size_type const bytes = n * sizeof (value_type);
|
|
return static_cast <pointer> (m_context.allocate (bytes));
|
|
}
|
|
|
|
void deallocate (pointer p, size_type n) const
|
|
{
|
|
size_type const bytes = n * sizeof (value_type);
|
|
m_context.deallocate (p, bytes);
|
|
}
|
|
|
|
size_type max_size () const noexcept
|
|
{
|
|
return std::numeric_limits <size_type>::max () / sizeof (value_type);
|
|
}
|
|
|
|
void construct (pointer p, const_reference val) const
|
|
{
|
|
::new ((void *)p) value_type (val);
|
|
}
|
|
|
|
void destroy (pointer p) const
|
|
{
|
|
p->~value_type ();
|
|
}
|
|
|
|
private:
|
|
template <class>
|
|
friend struct Allocator;
|
|
|
|
// The context must remain valid for the lifetime of the allocator.
|
|
//
|
|
Context m_context;
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// Abstract handler wrapper
|
|
//
|
|
struct Call : public SharedObject
|
|
{
|
|
public:
|
|
explicit Call (Context context) noexcept;
|
|
~Call ();
|
|
|
|
Context getContext () const noexcept;
|
|
|
|
// Determine if the completion handler is a continuation
|
|
// of a composed operation. It's generally not possible
|
|
// to know this from here, so there's an interface to
|
|
// set the flag.
|
|
//
|
|
// Our asio_handler_is_continuation hook calls this.
|
|
//
|
|
bool is_continuation () const noexcept;
|
|
void set_continuation () noexcept;
|
|
void set_final_continuation () noexcept;
|
|
|
|
void operator() ();
|
|
void operator() (error_code const&);
|
|
void operator() (error_code const&, std::size_t);
|
|
|
|
virtual void* allocate (std::size_t) = 0;
|
|
virtual void deallocate (void*, std::size_t) = 0;
|
|
virtual void destroy () = 0;
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
protected:
|
|
void check_continuation () noexcept;
|
|
|
|
virtual void dispatch ();
|
|
virtual void dispatch (error_code const&);
|
|
virtual void dispatch (error_code const&, std::size_t);
|
|
|
|
static void* pure_virtual_called ();
|
|
|
|
Context const m_context;
|
|
bool m_is_continuation;
|
|
bool m_is_final_continuation;
|
|
|
|
private:
|
|
// called by Context
|
|
friend struct Context;
|
|
virtual void invoke (invoked_type&) = 0;
|
|
};
|
|
|
|
// Required for gaining access to our hooks
|
|
//
|
|
friend struct ContainerDeletePolicy <Call>;
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
// Holds the original handler with all of its type information.
|
|
// Can also perform the invoke, allocate, and deallocate operations
|
|
// in the same context as the original handler.
|
|
//
|
|
template <typename Handler>
|
|
struct CallType : Call
|
|
{
|
|
// We take ownership of the handler here, through move assignment.
|
|
// The size parameter corresponds to how much we allocated using
|
|
// the custom allocator, and is required for deallocation.
|
|
//
|
|
// If the passed context is null, we will use the handler itself
|
|
// as the context (this is what normally happens when you pass a
|
|
// handler into an asynchronous function operation).
|
|
//
|
|
// Context must have Call as a base or this will result in
|
|
// undefined behavior.
|
|
//
|
|
CallType (Context context, std::size_t size, BOOST_ASIO_MOVE_ARG(Handler) handler)
|
|
: Call (context)
|
|
, m_size (size)
|
|
, m_handler (BOOST_ASIO_MOVE_CAST(Handler)(handler))
|
|
{
|
|
}
|
|
|
|
~CallType ()
|
|
{
|
|
|
|
}
|
|
|
|
// Allocate using the original handler as the context.
|
|
//
|
|
void* allocate (std::size_t bytes)
|
|
{
|
|
// If this goes off someone didn't call getContext()!
|
|
bassert (m_context == this);
|
|
return boost_asio_handler_alloc_helpers::
|
|
allocate <Handler> (bytes, m_handler);
|
|
}
|
|
|
|
// Deallocate using the handlers context.
|
|
// Note that the original size is required.
|
|
//
|
|
void deallocate (void* p, std::size_t size)
|
|
{
|
|
// If this goes off someone didn't call getContext()!
|
|
bassert (m_context == this);
|
|
boost_asio_handler_alloc_helpers::
|
|
deallocate <Handler> (p, size, m_handler);
|
|
}
|
|
|
|
// Invoke the specified function on the handlers context.
|
|
// invoked has this signature void(void).
|
|
//
|
|
void invoke (invoked_type& invoked)
|
|
{
|
|
// If this goes off someone didn't call getContext()!
|
|
bassert (m_context == this);
|
|
boost_asio_handler_invoke_helpers::
|
|
invoke <invoked_type, Handler> (invoked, m_handler);
|
|
}
|
|
|
|
// Called by our ContainerDeletePolicy hook to destroy the
|
|
// object. We need this because we allocated it using a custom
|
|
// allocator. Destruction is tricky, the algorithm is as follows:
|
|
//
|
|
// First we move-assign the handler to our stack. If the build
|
|
// doesn't support move-assignment it will be a copy, still ok.
|
|
// We convert 'this' to a pointer to the polymorphic base, to
|
|
// ensure that the following direct destructor call will reach
|
|
// the most derived class. Finally, we deallocate the memory
|
|
// using the handler that is local to the stack.
|
|
//
|
|
// For this to work we need to make sure regular operator delete
|
|
// is never called for our object (it's private). We also need
|
|
// the size from the original allocation, which we saved at
|
|
// the time of construction.
|
|
//
|
|
void destroy ()
|
|
{
|
|
// Move the handler onto our stack so we have
|
|
// a context in which to perform the deallcation.
|
|
Handler local (BOOST_ASIO_MOVE_CAST(Handler)(m_handler));
|
|
std::size_t const size (m_size); // save the size member
|
|
Call* const call (dynamic_cast <Call*>(this));
|
|
call->~Call ();
|
|
boost_asio_handler_alloc_helpers::
|
|
deallocate <Handler> (call, size, local);
|
|
}
|
|
|
|
// These better not be getting called since we use our own allocators.
|
|
//
|
|
// NOTE Couldn't make them private since GCC complained.
|
|
//
|
|
void* operator new (std::size_t)
|
|
{
|
|
return pure_virtual_called ();
|
|
}
|
|
|
|
void operator delete (void*)
|
|
{
|
|
pure_virtual_called ();
|
|
}
|
|
|
|
private:
|
|
std::size_t const m_size;
|
|
|
|
protected:
|
|
Handler m_handler;
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
template <typename Handler>
|
|
struct PostCallType : CallType <Handler>
|
|
{
|
|
PostCallType (Context context, std::size_t size, BOOST_ASIO_MOVE_ARG(Handler) handler)
|
|
: CallType <Handler> (context, size, BOOST_ASIO_MOVE_CAST(Handler)(handler))
|
|
{
|
|
}
|
|
|
|
void dispatch ()
|
|
{
|
|
this->m_handler ();
|
|
}
|
|
};
|
|
|
|
template <typename Handler, typename Arg1>
|
|
struct PostCallType1 : CallType <Handler>
|
|
{
|
|
PostCallType1 (Context context, std::size_t size, BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1)
|
|
: CallType <Handler> (context, size, BOOST_ASIO_MOVE_CAST(Handler)(handler))
|
|
, m_arg1 (arg1)
|
|
{
|
|
}
|
|
|
|
void dispatch ()
|
|
{
|
|
this->m_handler (m_arg1);
|
|
}
|
|
|
|
Arg1 m_arg1;
|
|
};
|
|
|
|
template <typename Handler, typename Arg1, typename Arg2>
|
|
struct PostCallType2 : CallType <Handler>
|
|
{
|
|
PostCallType2 (Context context, std::size_t size, BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1, Arg2 arg2)
|
|
: CallType <Handler> (context, size, BOOST_ASIO_MOVE_CAST(Handler)(handler))
|
|
, m_arg1 (arg1)
|
|
, m_arg2 (arg2)
|
|
{
|
|
}
|
|
|
|
void dispatch ()
|
|
{
|
|
this->m_handler (m_arg1, m_arg2);
|
|
}
|
|
|
|
Arg1 m_arg1;
|
|
Arg2 m_arg2;
|
|
};
|
|
|
|
template <typename Handler>
|
|
struct ErrorCallType : CallType <Handler>
|
|
{
|
|
ErrorCallType (Context context, std::size_t size, BOOST_ASIO_MOVE_ARG(Handler) handler)
|
|
: CallType <Handler> (context, size, BOOST_ASIO_MOVE_CAST(Handler)(handler))
|
|
{
|
|
}
|
|
|
|
void dispatch (error_code const& ec)
|
|
{
|
|
this->m_handler (ec);
|
|
}
|
|
};
|
|
|
|
template <typename Handler>
|
|
struct TransferCallType : CallType <Handler>
|
|
{
|
|
TransferCallType (Context context, std::size_t size, BOOST_ASIO_MOVE_ARG(Handler) handler)
|
|
: CallType <Handler> (context, size, BOOST_ASIO_MOVE_CAST(Handler)(handler))
|
|
{
|
|
}
|
|
|
|
void dispatch (error_code const& ec, std::size_t bytes_transferred)
|
|
{
|
|
this->m_handler (ec, bytes_transferred);
|
|
}
|
|
};
|
|
|
|
private:
|
|
template <class Function>
|
|
friend void asio_handler_invoke (BOOST_ASIO_MOVE_ARG(Function), HandlerCall*);
|
|
friend void* asio_handler_allocate (std::size_t, HandlerCall*);
|
|
friend void asio_handler_deallocate (void*, std::size_t, HandlerCall*);
|
|
|
|
template <class Function>
|
|
friend void asio_handler_invoke (BOOST_ASIO_MOVE_ARG(Function), Call*);
|
|
friend void* asio_handler_allocate (std::size_t, Call*);
|
|
friend void asio_handler_deallocate (void*, std::size_t, Call*);
|
|
|
|
friend bool asio_handler_is_continuation (HandlerCall* call);
|
|
friend bool asio_handler_is_continuation (HandlerCall::Call* call);
|
|
friend bool asio_handler_is_continuation (HandlerCall::Context* context);
|
|
|
|
SharedObjectPtr <Call> m_call;
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
// Specializations
|
|
//
|
|
// ContainerDeletePolicy
|
|
// asio_handler_invoke
|
|
// asio_handler_allocate
|
|
// asio_handler_deallocate
|
|
//
|
|
|
|
template <>
|
|
struct ContainerDeletePolicy <HandlerCall::Call>
|
|
{
|
|
// SharedObjectPtr will use this when
|
|
// the reference count drops to zero.
|
|
//
|
|
static void destroy (HandlerCall::Call* call)
|
|
{
|
|
call->destroy ();
|
|
}
|
|
};
|
|
|
|
template <class Function>
|
|
void asio_handler_invoke (BOOST_ASIO_MOVE_ARG(Function) f, HandlerCall::Context* context)
|
|
{
|
|
context->invoke (BOOST_ASIO_MOVE_CAST(Function)(f));
|
|
}
|
|
|
|
template <class Function>
|
|
void asio_handler_invoke (BOOST_ASIO_MOVE_ARG(Function) f, HandlerCall* call)
|
|
{
|
|
// Always go through the call's context.
|
|
call->getContext().invoke (BOOST_ASIO_MOVE_CAST(Function)(f));
|
|
}
|
|
|
|
template <class Function>
|
|
void asio_handler_invoke (BOOST_ASIO_MOVE_ARG(Function) f, HandlerCall::Call* call)
|
|
{
|
|
// Always go through the call's context.
|
|
call->getContext().invoke (BOOST_ASIO_MOVE_CAST(Function)(f));
|
|
}
|
|
|
|
void* asio_handler_allocate (std::size_t size, HandlerCall* call);
|
|
void* asio_handler_allocate (std::size_t size, HandlerCall::Call* call);
|
|
void* asio_handler_allocate (std::size_t size, HandlerCall::Context* context);
|
|
|
|
void asio_handler_deallocate (void* p, std::size_t size, HandlerCall* call);
|
|
void asio_handler_deallocate (void* p, std::size_t size, HandlerCall::Call* call);
|
|
void asio_handler_deallocate (void* p, std::size_t size, HandlerCall::Context* context);
|
|
|
|
bool asio_handler_is_continuation (HandlerCall* call);
|
|
bool asio_handler_is_continuation (HandlerCall::Call* call);
|
|
bool asio_handler_is_continuation (HandlerCall::Context* context);
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
#endif
|