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