diff --git a/BeastConfig.h b/BeastConfig.h
index 8a3c22c3de..6807bb054d 100644
--- a/BeastConfig.h
+++ b/BeastConfig.h
@@ -162,6 +162,6 @@
#define BEAST_ASIO_HAS_BUFFEREDHANDSHAKE 0
-#define BEAST_ASIO_HAS_FUTURE_RETURNS 0
+//#define BEAST_ASIO_HAS_FUTURE_RETURNS 0
#endif
diff --git a/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj b/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj
index cc9fd8541c..2c773805a4 100644
--- a/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj
+++ b/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj
@@ -302,6 +302,12 @@
+
+ true
+ true
+ true
+ true
+
true
true
diff --git a/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj.filters b/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj.filters
index 73c1c0a735..601d420966 100644
--- a/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj.filters
+++ b/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj.filters
@@ -1315,6 +1315,9 @@
beast_asio\handshake
+
+ beast_asio\basics
+
diff --git a/Subtrees/beast/modules/beast_asio/basics/beast_HandlerCall.cpp b/Subtrees/beast/modules/beast_asio/basics/beast_HandlerCall.cpp
new file mode 100644
index 0000000000..f94f66c843
--- /dev/null
+++ b/Subtrees/beast/modules/beast_asio/basics/beast_HandlerCall.cpp
@@ -0,0 +1,353 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ 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.
+*/
+//==============================================================================
+//
+// Context
+//
+//------------------------------------------------------------------------------
+
+HandlerCall::Context::Context (Call* call) noexcept
+ : m_call (call)
+{
+ bassert (m_call != nullptr);
+}
+
+HandlerCall::Context::Context () noexcept
+ : m_call (nullptr)
+{
+}
+
+HandlerCall::Context::Context (Context const& other) noexcept
+ : m_call (other.m_call)
+{
+}
+
+HandlerCall::Context::Context (HandlerCall const& handler) noexcept
+ : m_call (handler.m_call.get ())
+{
+}
+
+HandlerCall::Context& HandlerCall::Context::operator= (Context other) noexcept
+{
+ m_call = other.m_call;
+ return *this;
+}
+
+bool HandlerCall::Context::operator== (Call const* call) const noexcept
+{
+ return m_call == call;
+}
+
+bool HandlerCall::Context::operator!= (Call const* call) const noexcept
+{
+ return m_call != call;
+}
+
+bool HandlerCall::Context::isComposed () const noexcept
+{
+ return m_call->is_continuation ();
+}
+
+bool HandlerCall::Context::isNull () const noexcept
+{
+ return m_call == nullptr;
+}
+
+bool HandlerCall::Context::isNotNull () const noexcept
+{
+ return m_call != nullptr;
+}
+
+bool HandlerCall::Context::operator== (Context other) const noexcept
+{
+ return m_call == other.m_call;
+}
+
+bool HandlerCall::Context::operator!= (Context other) const noexcept
+{
+ return m_call != other.m_call;
+}
+
+void* HandlerCall::Context::allocate (std::size_t size) const
+{
+ return m_call->allocate (size);
+}
+
+void HandlerCall::Context::deallocate (void* p, std::size_t size) const
+{
+ m_call->deallocate (p, size);
+}
+
+//------------------------------------------------------------------------------
+//
+// Call
+//
+//------------------------------------------------------------------------------
+
+HandlerCall::Call::Call (Context context) noexcept
+ : m_context (context.isNull () ? Context (this) : context)
+ , m_is_continuation (false)
+ , m_is_final_continuation (false)
+{
+}
+
+HandlerCall::Call::~Call ()
+{
+}
+
+HandlerCall::Context HandlerCall::Call::getContext () const noexcept
+{
+ return m_context;
+}
+
+bool HandlerCall::Call::is_continuation () const noexcept
+{
+ // If this goes off it means someone isn't calling getContext()!
+ bassert (m_context == this);
+ return m_is_continuation;
+}
+
+void HandlerCall::Call::set_continuation () noexcept
+{
+ // Setting it twice means some code is sloppy!
+ bassert (! m_is_continuation);
+
+ m_is_continuation = true;
+}
+
+void HandlerCall::Call::set_final_continuation () noexcept
+{
+ // Soemone called endComposed without calling beginComposed!
+ bassert (m_is_continuation);
+ // When true, we will clear
+ // m_is_continuation on our next completion
+ m_is_final_continuation = true;
+}
+
+void HandlerCall::Call::check_continuation () noexcept
+{
+ if (m_is_final_continuation)
+ {
+ bassert (m_is_continuation);
+
+ m_is_continuation = false;
+ m_is_final_continuation = false;
+ }
+}
+
+void HandlerCall::Call::operator() ()
+{
+ check_continuation ();
+ dispatch ();
+}
+
+void HandlerCall::Call::operator() (error_code const& ec)
+{
+ check_continuation ();
+ dispatch (ec);
+}
+
+void HandlerCall::Call::operator() (error_code const& ec, std::size_t bytes_transferred)
+{
+ check_continuation ();
+ dispatch (ec, bytes_transferred);
+}
+
+void HandlerCall::Call::dispatch ()
+{
+ pure_virtual_called ();
+}
+
+void HandlerCall::Call::dispatch (error_code const&)
+{
+ pure_virtual_called ();
+}
+
+void HandlerCall::Call::dispatch (error_code const&, std::size_t)
+{
+ pure_virtual_called ();
+}
+
+void* HandlerCall::Call::pure_virtual_called ()
+{
+ // These shouldn't be getting called. But since the object returned
+ // by most implementations of bind have operator() up to high arity
+ // levels, it is not generally possible to write a traits test that
+ // works in all scenarios for detecting a particular signature of a
+ // handler.
+ //
+ fatal_error ("pure virtual called");
+ return nullptr;
+}
+
+//------------------------------------------------------------------------------
+//
+// HandlerCall
+//
+//------------------------------------------------------------------------------
+
+HandlerCall::HandlerCall () noexcept
+{
+}
+
+HandlerCall::HandlerCall (HandlerCall const& other) noexcept
+ : m_call (other.m_call)
+{
+}
+
+HandlerCall& HandlerCall::operator= (HandlerCall const& other) noexcept
+{
+ m_call = other.m_call;
+ return *this;
+}
+
+#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
+HandlerCall::HandlerCall (HandlerCall&& other) noexcept
+ : m_call (other.m_call)
+{
+ other.m_call = nullptr;
+}
+
+HandlerCall& HandlerCall::operator= (HandlerCall&& other) noexcept
+{
+ m_call = other.m_call;
+ other.m_call = nullptr;
+ return *this;
+}
+#endif
+
+bool HandlerCall::isNull () const noexcept
+{
+ return m_call == nullptr;
+}
+
+bool HandlerCall::isNotNull () const noexcept
+{
+ return m_call != nullptr;
+}
+
+HandlerCall::Context HandlerCall::getContext () const noexcept
+{
+ bassert (m_call != nullptr);
+ return m_call->getContext ();
+}
+
+bool HandlerCall::isFinal () const noexcept
+{
+ return m_call->getContext () == m_call.get ();
+}
+
+HandlerCall& HandlerCall::beginComposed () noexcept
+{
+ // If this goes off it means that your handler is
+ // already sharing a context with another handler!
+ // You have to call beginComposed on the original handler.
+ //
+ bassert (isFinal ());
+ m_call->set_continuation ();
+ return *this;
+}
+
+HandlerCall& HandlerCall::endComposed () noexcept
+{
+ // If this goes off it means that your handler is
+ // already sharing a context with another handler!
+ // You have to call beginComposed on the original handler.
+ //
+ bassert (isFinal ());
+ m_call->set_final_continuation ();
+ return *this;
+}
+
+void HandlerCall::operator() ()
+{
+ (*m_call)();
+}
+
+void HandlerCall::operator() (error_code const& ec)
+{
+ (*m_call)(ec);
+}
+
+void HandlerCall::operator() (error_code const& ec, std::size_t bytes_transferred)
+{
+ (*m_call)(ec, bytes_transferred);
+}
+
+//------------------------------------------------------------------------------
+//
+// Specializations
+//
+//------------------------------------------------------------------------------
+
+void* asio_handler_allocate (std::size_t size, HandlerCall* call)
+{
+ // Always go through the call's context.
+ return call->getContext ().allocate (size);
+}
+
+void* asio_handler_allocate (std::size_t size, HandlerCall::Call* call)
+{
+ // Always go through the call's context.
+ return call->getContext ().allocate (size);
+}
+
+void* asio_handler_allocate (std::size_t size, HandlerCall::Context* context)
+{
+ return context->allocate (size);
+}
+
+//------------------------------------------------------------------------------
+
+void asio_handler_deallocate (void* p, std::size_t size, HandlerCall* call)
+{
+ // Always go through the call's context.
+ call->getContext ().deallocate (p, size);
+}
+
+void asio_handler_deallocate (void* p, std::size_t size, HandlerCall::Call* call)
+{
+ // Always go through the call's context.
+ call->getContext ().deallocate (p, size);
+}
+
+void asio_handler_deallocate (void* p, std::size_t size, HandlerCall::Context* context)
+{
+ context->deallocate (p, size);
+}
+
+//------------------------------------------------------------------------------
+
+bool asio_handler_is_continuation (HandlerCall* call)
+{
+ return call->getContext().isComposed ();
+}
+
+bool asio_handler_is_continuation (HandlerCall::Call* call)
+{
+ return call->getContext().isComposed ();
+}
+
+bool asio_handler_is_continuation (HandlerCall::Context*)
+{
+ // Something is horribly wrong if we're trying to
+ // use a Context as a completion handler?
+ //
+ fatal_error ("A function was unexpectedly called.");
+ return false;
+}
diff --git a/Subtrees/beast/modules/beast_asio/basics/beast_HandlerCall.h b/Subtrees/beast/modules/beast_asio/basics/beast_HandlerCall.h
index c5282fa0b2..b7d1b407f4 100644
--- a/Subtrees/beast/modules/beast_asio/basics/beast_HandlerCall.h
+++ b/Subtrees/beast/modules/beast_asio/basics/beast_HandlerCall.h
@@ -43,22 +43,56 @@ class HandlerCall
{
private:
typedef boost::system::error_code error_code;
+ typedef boost::function invoked_type;
+
+ // Forward declarations needed for friendship
+ template
+ struct CallType;
+ struct Call;
public:
typedef void result_type;
- // Really there are only 3 kings of functions.
- // Except for the composed connect, which we haven't done yet.
+ 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)
- // These tags tell us what kind of Handler we have.
- // It would be nice if we could deduce this, but the
- // return value of a bind() seems to satisfy the
- // requirements of ANY handler so that was scrapped.
-
// CompletionHandler
//
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/CompletionHandler.html
@@ -99,177 +133,454 @@ public:
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/BufferedHandshakeHandler.html
//
typedef Transfer BufferedHandshake;
+ /** @} */
//--------------------------------------------------------------------------
- HandlerCall () noexcept
- {
- }
+ /** 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
- HandlerCall (BOOST_ASIO_MOVE_ARG(Handler) handler, Completion)
+ HandlerCall (BOOST_ASIO_MOVE_ARG(Handler) handler, Completion,
+ Context context = Context ())
: m_call (construct (
- BOOST_ASIO_MOVE_CAST(Handler)(handler)))
+ BOOST_ASIO_MOVE_CAST(Handler)(handler),
+ context))
{
}
+ /** 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
- HandlerCall (BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1, Completion)
+ HandlerCall (BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1, Completion,
+ Context context = Context ())
: m_call (construct (
- BOOST_ASIO_MOVE_CAST(Handler)(handler), arg1))
+ BOOST_ASIO_MOVE_CAST(Handler)(handler),
+ context, 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
- HandlerCall (BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1, Arg2 arg2, Completion)
+ HandlerCall (BOOST_ASIO_MOVE_ARG(Handler) handler,
+ Arg1 arg1, Arg2 arg2, Completion, Context context = Context ())
: m_call (construct (
- BOOST_ASIO_MOVE_CAST(Handler)(handler), arg1, arg2))
+ BOOST_ASIO_MOVE_CAST(Handler)(handler),
+ context, 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
- HandlerCall (BOOST_ASIO_MOVE_ARG(Handler) handler, Error)
+ HandlerCall (BOOST_ASIO_MOVE_ARG(Handler) handler, Error,
+ Context context = Context ())
: m_call (construct (
- BOOST_ASIO_MOVE_CAST(Handler)(handler)))
-
+ BOOST_ASIO_MOVE_CAST(Handler)(handler),
+ context))
{
}
+ /** 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
- HandlerCall (BOOST_ASIO_MOVE_ARG(Handler) handler, Transfer)
+ HandlerCall (BOOST_ASIO_MOVE_ARG(Handler) handler, Transfer,
+ Context context = Context ())
: m_call (construct (
- BOOST_ASIO_MOVE_CAST(Handler)(handler)))
+ BOOST_ASIO_MOVE_CAST(Handler)(handler),
+ context))
{
}
- inline HandlerCall (HandlerCall const& other) noexcept
- : m_call (other.m_call)
- {
- }
+ /** Copy construction and assignment.
- inline HandlerCall& operator= (HandlerCall const& other) noexcept
- {
- m_call = other.m_call;
- return *this;
- }
+ 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
- inline HandlerCall (HandlerCall && other) noexcept
- : m_call (other.m_call)
- {
- other.m_call = nullptr;
- }
-
- inline HandlerCall& operator= (HandlerCall&& other) noexcept
- {
- m_call = other.m_call;
- other.m_call = nullptr;
- return *this;
- }
+ HandlerCall (HandlerCall&& other) noexcept;
+ HandlerCall& operator= (HandlerCall&& other) noexcept;
#endif
+ /** @} */
- inline bool isNull () const noexcept
- {
- return m_call == nullptr;
- }
+ //--------------------------------------------------------------------------
- inline bool isNotNull () const noexcept
- {
- return m_call != nullptr;
- }
+ /** Returns true if the HandlerCall is null.
- inline void operator() ()
- {
- (*m_call)();
- }
+ A null HandlerCall object may not be passed to
+ an asynchronous completion function.
+ */
+ bool isNull () const noexcept;
- inline void operator() (error_code const& ec)
- {
- (*m_call)(ec);
- }
+ /** Returns true if the HandlerCall is not null. */
+ bool isNotNull () const noexcept;
- inline void operator() (error_code const& ec, std::size_t bytes_transferred)
+ /** 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& beginComposed () 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& endComposed () 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
{
- (*m_call)(ec, bytes_transferred);
- }
+ /** 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
+ void callOnContext (Function f, Context& context)
+ {
+ using boost_asio_handler_invoke_helpers;
+ invoke (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
+ void invoke (BOOST_ASIO_MOVE_ARG(Function) f)
+ {
+ invoked_type invoked (BOOST_ASIO_MOVE_CAST(Function)(f),
+ Allocator (*this));
+ m_call->invoke (invoked);
+ }
+
+ bool operator== (Call const* call) const noexcept;
+ bool operator!= (Call const* call) const noexcept;
+
+ private:
+ template
+ 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
//
//--------------------------------------------------------------------------
- struct Call;
-
- // These construct the reference counted polymorphic wrapper (Call)
- // around the handler. We use MoveAssignable (rvalue-references)
+ // 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.
+ // 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 class Container, typename Handler>
- static Call* construct (BOOST_ASIO_MOVE_ARG(Handler) handler)
+ static Call* construct (BOOST_ASIO_MOVE_ARG(Handler) handler,
+ Context context)
{
typedef Container ContainerType;
- Handler local (BOOST_ASIO_MOVE_CAST(Handler)(handler)); // move to stack
+ Handler local (BOOST_ASIO_MOVE_CAST(Handler)(handler));
std::size_t const size (sizeof (ContainerType));
void* const p = boost_asio_handler_alloc_helpers::
allocate (size, local);
return ::new (p) ContainerType (
- BOOST_ASIO_MOVE_CAST(Handler)(local), size);
+ BOOST_ASIO_MOVE_CAST(Handler)(local), size, context);
}
template class Container,
typename Handler, typename Arg1>
- static Call* construct (BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1)
+ static Call* construct (BOOST_ASIO_MOVE_ARG(Handler) handler,
+ Context context, Arg1 arg1)
{
typedef Container ContainerType;
- Handler local (BOOST_ASIO_MOVE_CAST(Handler)(handler)); // move to stack
+ Handler local (BOOST_ASIO_MOVE_CAST(Handler)(handler));
std::size_t const size (sizeof (ContainerType));
void* const p = boost_asio_handler_alloc_helpers::
allocate (size, local);
return ::new (p) ContainerType (
- BOOST_ASIO_MOVE_CAST(Handler)(local), size, arg1);
+ BOOST_ASIO_MOVE_CAST(Handler)(local),
+ size, context, arg1);
}
template class Container,
typename Handler, typename Arg1, typename Arg2>
- static Call* construct (BOOST_ASIO_MOVE_ARG(Handler) handler, Arg1 arg1, Arg2 arg2)
+ static Call* construct (BOOST_ASIO_MOVE_ARG(Handler) handler,
+ Context context, Arg1 arg1, Arg2 arg2)
{
typedef Container ContainerType;
- Handler local (BOOST_ASIO_MOVE_CAST(Handler)(handler)); // move to stack
+ Handler local (BOOST_ASIO_MOVE_CAST(Handler)(handler));
std::size_t const size (sizeof (ContainerType));
void* const p = boost_asio_handler_alloc_helpers::
allocate (size, local);
return ::new (p) ContainerType (
- BOOST_ASIO_MOVE_CAST(Handler)(local), size, arg1, arg2);
- }
-
- inline void* allocate (std::size_t size)
- {
- return m_call->allocate (size);
- }
-
- inline void deallocate (void* p, std::size_t size)
- {
- m_call->deallocate (p, size);
- }
-
- inline void destroy ()
- {
- m_call->destroy ();
+ BOOST_ASIO_MOVE_CAST(Handler)(local),
+ size, context, 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.
+ // 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
struct Allocator
@@ -282,14 +593,16 @@ private:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
- explicit Allocator (SharedObjectPtr const& call)
- : m_call (call)
+ // Make sure this is the right context!
+ //
+ explicit Allocator (Context context)
+ : m_context (context)
{
}
template
Allocator (Allocator const& other)
- : m_call (other.m_call)
+ : m_context (other.m_context)
{
}
@@ -312,13 +625,13 @@ private:
pointer allocate (size_type n) const
{
size_type const bytes = n * sizeof (value_type);
- return static_cast (m_call->allocate (bytes));
+ return static_cast (m_context.allocate (bytes));
}
void deallocate (pointer p, size_type n) const
{
size_type const bytes = n * sizeof (value_type);
- m_call->deallocate (p, bytes);
+ m_context.deallocate (p, bytes);
}
size_type max_size () const noexcept
@@ -340,83 +653,61 @@ private:
template
friend struct Allocator;
- // The wrapped handler is stored in a reference counted
- // container, so copies and pass by value is very cheap.
+ // The context must remain valid for the lifetime of the allocator.
//
- SharedObjectPtr m_call;
+ Context m_context;
};
- //--------------------------------------------------------------------------
-
- // We use a boost::function to hold the Function from asio_handler_invoke.
- //
- typedef boost::function invoked_type;
-
- // Invoke the specified Function object. 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, and then ownership
- //
- // All of the safety guarantees of the original context are preserved
- // when going through this invoke function.
- //
- template
- void invoke (BOOST_ASIO_MOVE_ARG(Function) f)
- {
- invoked_type invoked (BOOST_ASIO_MOVE_CAST(Function)(f),
- Allocator (m_call));
- m_call->invoke (invoked);
- }
-
//--------------------------------------------------------------------------
//
// Abstract handler wrapper
//
struct Call : public SharedObject
{
- Call ()
- {
- }
+ public:
+ explicit Call (Context context) noexcept;
+ ~Call ();
- ~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;
- virtual void operator() ()
- {
- pure_virtual_called ();
- }
-
- virtual void operator() (error_code const&)
- {
- pure_virtual_called ();
- }
-
- virtual void operator() (error_code const&, std::size_t)
- {
- pure_virtual_called ();
- }
+ 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;
- virtual void invoke (invoked_type&) = 0;
+ //----------------------------------------------------------------------
- virtual void destroy () = 0;
+ protected:
+ void check_continuation () noexcept;
- static void* pure_virtual_called ()
- {
- // These shouldn't be getting called. But since the object returned
- // by most implementations of bind have operator() up to high arity
- // levels, it is not generally possible to write a traits test that
- // works in all scenarios for detecting a particular signature of a
- // handler.
- //
- fatal_error ("pure virtual called");
- return nullptr;
- }
+ 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
@@ -436,9 +727,18 @@ private:
// The size parameter corresponds to how much we allocated using
// the custom allocator, and is required for deallocation.
//
- CallType (BOOST_ASIO_MOVE_ARG(Handler) handler, std::size_t size)
- : m_handler (BOOST_ASIO_MOVE_CAST(Handler)(handler))
+ // 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 (BOOST_ASIO_MOVE_ARG(Handler) handler,
+ std::size_t size, Context context)
+ : Call (context)
, m_size (size)
+ , m_handler (BOOST_ASIO_MOVE_CAST(Handler)(handler))
{
}
@@ -447,10 +747,12 @@ private:
}
- // Allocate using the handler's context.
+ // 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 (bytes, m_handler);
}
@@ -460,6 +762,8 @@ private:
//
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 (p, size, m_handler);
}
@@ -469,6 +773,8 @@ private:
//
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, m_handler);
}
@@ -515,8 +821,10 @@ private:
pure_virtual_called ();
}
- protected:
+ private:
std::size_t const m_size;
+
+ protected:
Handler m_handler;
};
@@ -525,12 +833,14 @@ private:
template
struct PostCallType : CallType
{
- PostCallType (BOOST_ASIO_MOVE_ARG(Handler) handler, std::size_t size)
- : CallType (BOOST_ASIO_MOVE_CAST(Handler)(handler), size)
+ PostCallType (BOOST_ASIO_MOVE_ARG(Handler) handler,
+ std::size_t size, Context context)
+ : CallType (BOOST_ASIO_MOVE_CAST(Handler)(handler),
+ size, context)
{
}
- void operator() ()
+ void dispatch ()
{
this->m_handler ();
}
@@ -539,13 +849,15 @@ private:
template
struct PostCallType1 : CallType
{
- PostCallType1 (BOOST_ASIO_MOVE_ARG(Handler) handler, std::size_t size, Arg1 arg1)
- : CallType (BOOST_ASIO_MOVE_CAST(Handler)(handler), size)
+ PostCallType1 (BOOST_ASIO_MOVE_ARG(Handler) handler,
+ std::size_t size, Context context, Arg1 arg1)
+ : CallType (BOOST_ASIO_MOVE_CAST(Handler)(handler),
+ size, context)
, m_arg1 (arg1)
{
}
- void operator() ()
+ void dispatch ()
{
this->m_handler (m_arg1);
}
@@ -556,14 +868,16 @@ private:
template
struct PostCallType2 : CallType
{
- PostCallType2 (BOOST_ASIO_MOVE_ARG(Handler) handler, std::size_t size, Arg1 arg1, Arg2 arg2)
- : CallType (BOOST_ASIO_MOVE_CAST(Handler)(handler), size)
+ PostCallType2 (BOOST_ASIO_MOVE_ARG(Handler) handler,
+ std::size_t size, Context context, Arg1 arg1, Arg2 arg2)
+ : CallType (BOOST_ASIO_MOVE_CAST(Handler)(handler),
+ size, context)
, m_arg1 (arg1)
, m_arg2 (arg2)
{
}
- void operator() ()
+ void dispatch ()
{
this->m_handler (m_arg1, m_arg2);
}
@@ -575,12 +889,14 @@ private:
template
struct ErrorCallType : CallType
{
- ErrorCallType (BOOST_ASIO_MOVE_ARG(Handler) handler, std::size_t size)
- : CallType (BOOST_ASIO_MOVE_CAST(Handler)(handler), size)
+ ErrorCallType (BOOST_ASIO_MOVE_ARG(Handler) handler,
+ std::size_t size, Context context)
+ : CallType (BOOST_ASIO_MOVE_CAST(Handler)(handler),
+ size, context)
{
}
- void operator() (error_code const& ec)
+ void dispatch (error_code const& ec)
{
this->m_handler (ec);
}
@@ -589,12 +905,14 @@ private:
template
struct TransferCallType : CallType
{
- TransferCallType (BOOST_ASIO_MOVE_ARG(Handler) handler, std::size_t size)
- : CallType (BOOST_ASIO_MOVE_CAST(Handler)(handler), size)
+ TransferCallType (BOOST_ASIO_MOVE_ARG(Handler) handler,
+ std::size_t size, Context context)
+ : CallType (BOOST_ASIO_MOVE_CAST(Handler)(handler),
+ size, context)
{
}
- void operator() (error_code const& ec, std::size_t bytes_transferred)
+ void dispatch (error_code const& ec, std::size_t bytes_transferred)
{
this->m_handler (ec, bytes_transferred);
}
@@ -606,6 +924,15 @@ private:
friend void* asio_handler_allocate (std::size_t, HandlerCall*);
friend void asio_handler_deallocate (void*, std::size_t, HandlerCall*);
+ template
+ 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 m_call;
};
@@ -672,32 +999,56 @@ public:
//
// Specializations
//
-// asio_handler_invoke, asio_handler_allocate, asio_handler_deallocate
+// ContainerDeletePolicy
+// asio_handler_invoke
+// asio_handler_allocate
+// asio_handler_deallocate
//
-template
-void asio_handler_invoke (BOOST_ASIO_MOVE_ARG(Function) f, HandlerCall* call)
-{
- call->invoke (BOOST_ASIO_MOVE_CAST(Function)(f));
-}
-
-inline void* asio_handler_allocate (std::size_t size, HandlerCall* call)
-{
- return call->allocate (size);
-}
-
-inline void asio_handler_deallocate (void* p, std::size_t size, HandlerCall* call)
-{
- call->deallocate (p, size);
-}
-
template <>
struct ContainerDeletePolicy
{
+ // SharedObjectPtr will use this when
+ // the reference count drops to zero.
+ //
static void destroy (HandlerCall::Call* call)
{
call->destroy ();
}
};
+template
+void asio_handler_invoke (BOOST_ASIO_MOVE_ARG(Function) f, HandlerCall::Context* context)
+{
+ context->invoke (BOOST_ASIO_MOVE_CAST(Function)(f));
+}
+
+template
+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
+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
diff --git a/Subtrees/beast/modules/beast_asio/beast_asio.cpp b/Subtrees/beast/modules/beast_asio/beast_asio.cpp
index c22b3b6dce..ef34ceb84f 100644
--- a/Subtrees/beast/modules/beast_asio/beast_asio.cpp
+++ b/Subtrees/beast/modules/beast_asio/beast_asio.cpp
@@ -24,6 +24,7 @@
namespace beast
{
+#include "basics/beast_HandlerCall.cpp"
#include "basics/beast_PeerRole.cpp"
#include "sockets/beast_SocketBase.cpp"
diff --git a/Subtrees/beast/modules/beast_asio/handshake/beast_HandshakeDetectStream.h b/Subtrees/beast/modules/beast_asio/handshake/beast_HandshakeDetectStream.h
index 72df997a34..d4fd697c96 100644
--- a/Subtrees/beast/modules/beast_asio/handshake/beast_HandshakeDetectStream.h
+++ b/Subtrees/beast/modules/beast_asio/handshake/beast_HandshakeDetectStream.h
@@ -152,13 +152,11 @@ public:
m_origHandler = ErrorCall (
BOOST_ASIO_MOVE_CAST(HandshakeHandler)
(HandshakeHandler(init.handler)));
- bassert (m_origBufferedHandler.isNull ());
async_do_handshake (type, ConstBuffers ());
return init.result.get();
#else
m_origHandler = ErrorCall (
BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler));
- bassert (m_origBufferedHandler.isNull ());
async_do_handshake (type, ConstBuffers ());
#endif
}
@@ -184,13 +182,11 @@ public:
m_origBufferedHandler = TransferCall (
BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)
(BufferedHandshakeHandler(init.handler)));
- bassert (m_origHandler.isNull ());
async_do_handshake (type, ConstBuffers (buffers));
return init.result.get();
#else
m_origBufferedHandler = TransferCall (
BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler(handler)));
- bassert (m_origHandler.isNull ());
async_do_handshake (type, ConstBuffers (buffers));
#endif
}
@@ -245,6 +241,22 @@ public:
void async_do_handshake (handshake_type type, ConstBuffers const& buffers)
{
+ // Get the execution context from the original handler
+ // and signal the beginning of our composed operation.
+ //
+ if (m_origHandler.isNotNull ())
+ {
+ m_origHandler.beginComposed ();
+ m_context = m_origHandler.getContext ();
+ }
+ else
+ {
+ m_origBufferedHandler.beginComposed ();
+ m_context = m_origBufferedHandler.getContext ();
+ }
+
+ bassert (m_context.isNotNull ());
+
// Transfer caller data to our buffer.
// We commit the bytes in on_async_read_some.
//
@@ -280,7 +292,11 @@ public:
if (! m_origBufferedHandler.isNull ())
{
bassert (m_origHandler.isNull ());
- // continuation?
+
+ // The composed operation has completed and
+ // the original handler will eventually get called.
+ //
+ m_origBufferedHandler.endComposed ();
m_callback->on_async_detect (m_logic.get (), ec,
ConstBuffers (m_buffer.data ()), m_origBufferedHandler);
@@ -288,11 +304,12 @@ public:
}
#endif
- bassert (! m_origHandler.isNull ())
- // continuation?
+ // The composed operation has completed and
+ // the original handler will eventually get called.
+ //
+ m_origHandler.endComposed ();
m_callback->on_async_detect (m_logic.get (), ec,
- ConstBuffers (m_buffer.data ()), m_origHandler);
-
+ ConstBuffers (m_buffer.data ()), m_origHandler);
return;
}
@@ -302,27 +319,33 @@ public:
buffer_type::mutable_buffers_type buffers (m_buffer.prepare (
needed - available));
- // need a continuation hook here?
- m_next_layer.async_read_some (buffers, boost::bind (
- &this_type::on_async_read_some, this, boost::asio::placeholders::error,
- boost::asio::placeholders::bytes_transferred));
+ // Perform the asynchronous operation using the context
+ // of the original handler. This ensures that we meet the
+ // execution safety requirements of the handler.
+ //
+ HandlerCall handler (boost::bind (
+ &this_type::on_async_read_some, this,
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred),
+ HandlerCall::Read (), m_context);
+ m_next_layer.async_read_some (buffers, handler);
return;
}
+ // Error condition
+
#if BEAST_ASIO_HAS_BUFFEREDHANDSHAKE
if (! m_origBufferedHandler.isNull ())
{
- bassert (m_origHandler.isNull ());
- // continuation?
+ m_origBufferedHandler.endComposed ();
m_callback->on_async_detect (m_logic.get (), ec,
ConstBuffers (m_buffer.data ()), m_origBufferedHandler);
return;
}
#endif
- bassert (! m_origHandler.isNull ())
- // continuation?
+ m_origBufferedHandler.endComposed ();
m_callback->on_async_detect (m_logic.get (), ec,
ConstBuffers (m_buffer.data ()), m_origHandler);
}
@@ -335,6 +358,7 @@ private:
HandshakeDetectLogicType m_logic;
ErrorCall m_origHandler;
TransferCall m_origBufferedHandler;
+ HandlerCall::Context m_context;
};
/** @} */