#ifndef ASYNCHRONOUS_CONTINUATION_H #define ASYNCHRONOUS_CONTINUATION_H #include #include #include #include #include #include #include namespace sscl { /** * AsynchronousContinuation - Template base class for async sequence management * * This template provides a common pattern for managing asynchronous operations * that need to maintain object lifetime through a sequence of callbacks. * * The template parameter OriginalCbFnT represents the signature of the original * callback that will be invoked when the async sequence completes. */ template class AsynchronousContinuation : public AsynchronousContinuationChainLink { public: explicit AsynchronousContinuation(Callback originalCb) : originalCallback(std::move(originalCb)) {} /** EXPLANATION: * Each numbered segmented sequence persists the lifetime of the * continuation object by taking a copy of its shared_ptr. */ typedef void (SegmentFn)( std::shared_ptr> lifetimePreservingConveyance); /** EXPLANATION: * When an exception is thrown in a an async callee, which pertains to an * error in the data given by the caller, we ought not to throw the * exception within the callee. Instead, we should store the exception * in the continuation object and return it to the caller. * * The caller should then call checkException() to rethrow it on its * own stack. * * This macro should be used by the caller to bubble the exception to the * caller. */ #define CALLEE_SETEXC(continuation, type, exc_obj) \ (continuation)->exception = std::make_exception_ptr(exc_obj) #define CALLEE_SETEXC_CALLCB(continuation, type, exc_obj) \ do { \ CALLEE_SETEXC(continuation, type, exc_obj); \ (continuation)->callOriginalCb(); \ } while(0) #define CALLEE_SETEXC_CALLCB_RET(continuation, type, exc_obj) \ do { \ CALLEE_SETEXC_CALLCB(continuation, type, exc_obj); \ return; \ } while(0) // Call this in the caller to rethrow the exception. void checkException() { if (exception) { std::rethrow_exception(exception); } } // Implement the virtual method from AsynchronousContinuationChainLink virtual std::shared_ptr getCallersContinuationShPtr() const override { return originalCallback.callerContinuation; } public: Callback originalCallback; std::exception_ptr exception; }; /** * NonPostedAsynchronousContinuation - For continuations that don't post * callbacks * * Note: We intentionally do not create a * LockedNonPostedAsynchronousContinuation because the only way to implement * non-posted locking would be via busy-spinning or sleeplocks. This would * eliminate the throughput advantage from our Qspinning mechanism, which * relies on re-posting to the io_service queue when locks are unavailable. */ template class NonPostedAsynchronousContinuation : public AsynchronousContinuation { public: explicit NonPostedAsynchronousContinuation( Callback originalCb) : AsynchronousContinuation(originalCb) {} /** * @brief Call the original callback with perfect forwarding * (immediate execution) * * This implementation calls the original callback immediately without * posting to any thread or queue. Used for non-posted continuations. * * @param args Arguments to forward to the original callback */ template void callOriginalCb(Args&&... args) { if (AsynchronousContinuation::originalCallback .callbackFn) { AsynchronousContinuation::originalCallback .callbackFn(std::forward(args)...); } } }; template class PostedAsynchronousContinuation : public AsynchronousContinuation { public: PostedAsynchronousContinuation( const std::shared_ptr &caller, Callback originalCbFn) : AsynchronousContinuation(originalCbFn), caller(caller) {} template void callOriginalCb(Args&&... args) { if (AsynchronousContinuation::originalCallback .callbackFn) { caller->getIoService().post( STC(std::bind( AsynchronousContinuation::originalCallback .callbackFn, std::forward(args)...))); } } public: std::shared_ptr caller; }; } // namespace sscl #endif // ASYNCHRONOUS_CONTINUATION_H