Files
rippled/Subtrees/beast/modules/beast_asio/basics/beast_HandlerCall.h
2013-08-18 03:07:12 -07:00

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