From 4f7459c35f98f5657cb864d2e9c4cc3e3cbf8080 Mon Sep 17 00:00:00 2001 From: James Sedgwick Date: Mon, 27 Apr 2015 11:44:31 -0700 Subject: [PATCH] Manage handlers with shared_ptrs, introduce StaticPipeline Summary: A few things: - Eliminate HandlerPtr by managing all handlers with shared_ptrs instead of keeping them inline in the pipeline - Kill recursively templated ChannelPipeline accordingly - Introduce StaticPipeline to retain the flexibility of zero-alloc pipelines - Introduce notion of an "owning handler" to avoid destruction order issues Test Plan: unit (will add more), thrift unit Reviewed By: davejwatson@fb.com Subscribers: fugalh, alandau, bmatheny, folly-diffs@, jsedgwick, yfeldblum, chalfant FB internal diff: D2023976 Tasks: 6836580 Signature: t1:2023976:1430159578:e50e8a149e549a40670d093fb65987a4843cdd8d --- folly/Makefile.am | 1 + folly/wangle/bootstrap/BootstrapTest.cpp | 3 +- folly/wangle/bootstrap/ServerBootstrap-inl.h | 2 +- folly/wangle/channel/Handler.h | 96 ------- folly/wangle/channel/HandlerContext.h | 42 +-- folly/wangle/channel/Pipeline.h | 256 +++++------------- folly/wangle/channel/StaticPipeline.h | 125 +++++++++ .../test/OutputBufferingHandlerTest.cpp | 6 +- folly/wangle/channel/test/PipelineTest.cpp | 26 +- folly/wangle/service/ClientDispatcher.h | 4 +- 10 files changed, 235 insertions(+), 326 deletions(-) create mode 100644 folly/wangle/channel/StaticPipeline.h diff --git a/folly/Makefile.am b/folly/Makefile.am index 1c4030cf..45296006 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -279,6 +279,7 @@ nobase_follyinclude_HEADERS = \ wangle/channel/HandlerContext.h \ wangle/channel/OutputBufferingHandler.h \ wangle/channel/Pipeline.h \ + wangle/channel/StaticPipeline.h \ wangle/concurrent/BlockingQueue.h \ wangle/concurrent/Codel.h \ wangle/concurrent/CPUThreadPoolExecutor.h \ diff --git a/folly/wangle/bootstrap/BootstrapTest.cpp b/folly/wangle/bootstrap/BootstrapTest.cpp index 9087be57..ddbd50ee 100644 --- a/folly/wangle/bootstrap/BootstrapTest.cpp +++ b/folly/wangle/bootstrap/BootstrapTest.cpp @@ -287,8 +287,7 @@ class TestHandlerPipelineFactory public: ServerBootstrap::AcceptPipeline* newPipeline(std::shared_ptr) { auto pipeline = new ServerBootstrap::AcceptPipeline; - auto handler = std::make_shared(); - pipeline->addBack(HandlerPtr(handler)); + pipeline->addBack(HandlerPipeline()); return pipeline; } }; diff --git a/folly/wangle/bootstrap/ServerBootstrap-inl.h b/folly/wangle/bootstrap/ServerBootstrap-inl.h index 1e0ebc75..ee3f9b22 100644 --- a/folly/wangle/bootstrap/ServerBootstrap-inl.h +++ b/folly/wangle/bootstrap/ServerBootstrap-inl.h @@ -70,7 +70,7 @@ class ServerAcceptor Acceptor::init(nullptr, base_); CHECK(acceptorPipeline_); - acceptorPipeline_->addBack(folly::wangle::HandlerPtr(this)); + acceptorPipeline_->addBack(this); acceptorPipeline_->finalize(); } diff --git a/folly/wangle/channel/Handler.h b/folly/wangle/channel/Handler.h index 67219f17..062d23a2 100644 --- a/folly/wangle/channel/Handler.h +++ b/folly/wangle/channel/Handler.h @@ -97,100 +97,4 @@ class HandlerAdapter : public Handler { typedef HandlerAdapter> BytesToBytesHandler; -template -class HandlerPtr : public Handler< - typename HandlerT::rin, - typename HandlerT::rout, - typename HandlerT::win, - typename HandlerT::wout> { - public: - typedef typename std::conditional< - Shared, - std::shared_ptr, - HandlerT*>::type - Ptr; - - typedef typename HandlerT::Context Context; - - explicit HandlerPtr(Ptr handler) - : handler_(std::move(handler)) {} - - Ptr getHandler() { - return handler_; - } - - void setHandler(Ptr handler) { - if (handler == handler_) { - return; - } - if (handler_ && ctx_) { - handler_->detachPipeline(ctx_); - } - handler_ = std::move(handler); - if (handler_ && ctx_) { - handler_->attachPipeline(ctx_); - if (ctx_->getTransport()) { - handler_->attachTransport(ctx_); - } - } - } - - void attachPipeline(Context* ctx) override { - ctx_ = ctx; - if (handler_) { - handler_->attachPipeline(ctx_); - } - } - - void attachTransport(Context* ctx) override { - ctx_ = ctx; - if (handler_) { - handler_->attachTransport(ctx_); - } - } - - void detachPipeline(Context* ctx) override { - ctx_ = ctx; - if (handler_) { - handler_->detachPipeline(ctx_); - } - } - - void detachTransport(Context* ctx) override { - ctx_ = ctx; - if (handler_) { - handler_->detachTransport(ctx_); - } - } - - void read(Context* ctx, typename HandlerT::rin msg) override { - DCHECK(handler_); - handler_->read(ctx, std::forward(msg)); - } - - void readEOF(Context* ctx) override { - DCHECK(handler_); - handler_->readEOF(ctx); - } - - void readException(Context* ctx, exception_wrapper e) override { - DCHECK(handler_); - handler_->readException(ctx, std::move(e)); - } - - Future write(Context* ctx, typename HandlerT::win msg) override { - DCHECK(handler_); - return handler_->write(ctx, std::forward(msg)); - } - - Future close(Context* ctx) override { - DCHECK(handler_); - return handler_->close(ctx); - } - - private: - Context* ctx_; - Ptr handler_; -}; - }} diff --git a/folly/wangle/channel/HandlerContext.h b/folly/wangle/channel/HandlerContext.h index 809f2b1a..deddb2de 100644 --- a/folly/wangle/channel/HandlerContext.h +++ b/folly/wangle/channel/HandlerContext.h @@ -59,6 +59,8 @@ class PipelineContext { public: virtual ~PipelineContext() {} + virtual void detachPipeline() = 0; + virtual void attachTransport() = 0; virtual void detachTransport() = 0; @@ -101,22 +103,30 @@ class ContextImpl : public HandlerContext - explicit ContextImpl(P* pipeline, HandlerArg&& handlerArg) - : pipeline_(pipeline), - handler_(std::forward(handlerArg)) { - handler_.attachPipeline(this); + explicit ContextImpl(P* pipeline, std::shared_ptr handler) { + initialize(pipeline, std::move(handler)); } - ~ContextImpl() { - handler_.detachPipeline(this); + void initialize(P* pipeline, std::shared_ptr handler) { + pipeline_ = pipeline; + handler_ = std::move(handler); + handler_->attachPipeline(this); } + // For StaticPipeline + ContextImpl() {} + + ~ContextImpl() {} + H* getHandler() { - return &handler_; + return handler_.get(); } // PipelineContext overrides + void detachPipeline() override { + handler_->detachPipeline(this); + } + void setNextIn(PipelineContext* ctx) override { auto nextIn = dynamic_cast*>(ctx); if (nextIn) { @@ -137,12 +147,12 @@ class ContextImpl : public HandlerContext(pipeline_)); - handler_.attachTransport(this); + handler_->attachTransport(this); } void detachTransport() override { typename P::DestructorGuard dg(static_cast(pipeline_)); - handler_.detachTransport(this); + handler_->detachTransport(this); } // HandlerContext overrides @@ -218,33 +228,33 @@ class ContextImpl : public HandlerContext(pipeline_)); - handler_.read(this, std::forward(msg)); + handler_->read(this, std::forward(msg)); } void readEOF() override { typename P::DestructorGuard dg(static_cast(pipeline_)); - handler_.readEOF(this); + handler_->readEOF(this); } void readException(exception_wrapper e) override { typename P::DestructorGuard dg(static_cast(pipeline_)); - handler_.readException(this, std::move(e)); + handler_->readException(this, std::move(e)); } // OutboundHandlerContext overrides Future write(Win msg) override { typename P::DestructorGuard dg(static_cast(pipeline_)); - return handler_.write(this, std::forward(msg)); + return handler_->write(this, std::forward(msg)); } Future close() override { typename P::DestructorGuard dg(static_cast(pipeline_)); - return handler_.close(this); + return handler_->close(this); } private: P* pipeline_; - H handler_; + std::shared_ptr handler_; InboundHandlerContext* nextIn_{nullptr}; OutboundHandlerContext* nextOut_{nullptr}; }; diff --git a/folly/wangle/channel/Pipeline.h b/folly/wangle/channel/Pipeline.h index 7d4fd2a6..0cabfb2c 100644 --- a/folly/wangle/channel/Pipeline.h +++ b/folly/wangle/channel/Pipeline.h @@ -30,14 +30,10 @@ namespace folly { namespace wangle { * R is the inbound type, i.e. inbound calls start with pipeline.read(R) * W is the outbound type, i.e. outbound calls start with pipeline.write(W) */ -template -class Pipeline; - template -class Pipeline : public DelayedDestruction { +class Pipeline : public DelayedDestruction { public: Pipeline() {} - ~Pipeline() {} std::shared_ptr getTransport() { return transport_; @@ -80,22 +76,41 @@ class Pipeline : public DelayedDestruction { } template - Pipeline& addBack(H&& handler) { - ctxs_.push_back(folly::make_unique>( - this, std::forward(handler))); + Pipeline& addBack(std::shared_ptr handler) { + ctxs_.push_back(std::make_shared>( + this, + std::move(handler))); return *this; } template - Pipeline& addFront(H&& handler) { + Pipeline& addBack(H* handler) { + return addBack(std::shared_ptr(handler, [](H*){})); + } + + template + Pipeline& addBack(H&& handler) { + return addBack(std::make_shared(std::forward(handler))); + } + + template + Pipeline& addFront(std::shared_ptr handler) { ctxs_.insert( ctxs_.begin(), - folly::make_unique>( - this, - std::forward(handler))); + std::make_shared>(this, std::move(handler))); return *this; } + template + Pipeline& addFront(H* handler) { + return addFront(std::shared_ptr(handler, [](H*){})); + } + + template + Pipeline& addFront(H&& handler) { + return addFront(std::make_shared(std::forward(handler))); + } + template H* getHandler(int i) { auto ctx = dynamic_cast*>(ctxs_[i].get()); @@ -104,21 +119,6 @@ class Pipeline : public DelayedDestruction { } void finalize() { - finalizeHelper(); - InboundHandlerContext* front; - front_ = dynamic_cast*>( - ctxs_.front().get()); - if (!front_) { - throw std::invalid_argument("wrong type for first handler"); - } - } - - protected: - explicit Pipeline(bool shouldFinalize) { - CHECK(!shouldFinalize); - } - - void finalizeHelper() { if (ctxs_.empty()) { return; } @@ -131,197 +131,69 @@ class Pipeline : public DelayedDestruction { if (!back_) { throw std::invalid_argument("wrong type for last handler"); } - } - - PipelineContext* getLocalFront() { - return ctxs_.empty() ? nullptr : ctxs_.front().get(); - } - - static const bool is_end{true}; - - std::shared_ptr transport_; - WriteFlags writeFlags_{WriteFlags::NONE}; - std::pair readBufferSettings_{2048, 2048}; - - void attachPipeline() {} - - void attachTransport( - std::shared_ptr transport) { - transport_ = std::move(transport); - } - void detachTransport() { - transport_ = nullptr; - } - - OutboundHandlerContext* back_{nullptr}; - - private: - InboundHandlerContext* front_{nullptr}; - std::vector> ctxs_; -}; - -template -class Pipeline - : public Pipeline { - protected: - template - Pipeline( - bool shouldFinalize, - HandlerArg&& handlerArg, - HandlersArgs&&... handlersArgs) - : Pipeline( - false, - std::forward(handlersArgs)...), - ctx_(this, std::forward(handlerArg)) { - if (shouldFinalize) { - finalize(); + front_ = dynamic_cast*>(ctxs_.front().get()); + if (!front_) { + throw std::invalid_argument("wrong type for first handler"); } } - public: - template - explicit Pipeline(HandlersArgs&&... handlersArgs) - : Pipeline(true, std::forward(handlersArgs)...) {} - - ~Pipeline() {} - - void read(R msg) { - typename Pipeline::DestructorGuard dg( - static_cast(this)); - front_->read(std::forward(msg)); - } - - void readEOF() { - typename Pipeline::DestructorGuard dg( - static_cast(this)); - front_->readEOF(); - } - - void readException(exception_wrapper e) { - typename Pipeline::DestructorGuard dg( - static_cast(this)); - front_->readException(std::move(e)); - } - - Future write(W msg) { - typename Pipeline::DestructorGuard dg( - static_cast(this)); - return back_->write(std::forward(msg)); - } - - Future close() { - typename Pipeline::DestructorGuard dg( - static_cast(this)); - return back_->close(); + // If one of the handlers owns the pipeline itself, use setOwner to ensure + // that the pipeline doesn't try to detach the handler during destruction, + // lest destruction ordering issues occur. + // See thrift/lib/cpp2/async/Cpp2Channel.cpp for an example + template + bool setOwner(H* handler) { + for (auto& ctx : ctxs_) { + auto ctxImpl = dynamic_cast*>(ctx.get()); + if (ctxImpl && ctxImpl->getHandler() == handler) { + owner_ = ctx; + return true; + } + } + return false; } void attachTransport( std::shared_ptr transport) { - typename Pipeline::DestructorGuard dg( - static_cast(this)); - CHECK((!Pipeline::transport_)); - Pipeline::attachTransport(std::move(transport)); - forEachCtx([&](PipelineContext* ctx){ + transport_ = std::move(transport); + for (auto& ctx : ctxs_) { ctx->attachTransport(); - }); + } } void detachTransport() { - typename Pipeline::DestructorGuard dg( - static_cast(this)); - Pipeline::detachTransport(); - forEachCtx([&](PipelineContext* ctx){ + transport_ = nullptr; + for (auto& ctx : ctxs_) { ctx->detachTransport(); - }); - } - - std::shared_ptr getTransport() { - return Pipeline::transport_; - } - - template - Pipeline& addBack(H&& handler) { - Pipeline::addBack(std::move(handler)); - return *this; + } } - template - Pipeline& addFront(H&& handler) { + protected: + template + void addContextFront(Context* context) { ctxs_.insert( ctxs_.begin(), - folly::make_unique>( - this, - std::move(handler))); - return *this; - } - - template - H* getHandler(size_t i) { - if (i > ctxs_.size()) { - return Pipeline::template getHandler( - i - (ctxs_.size() + 1)); - } else { - auto pctx = (i == ctxs_.size()) ? &ctx_ : ctxs_[i].get(); - auto ctx = dynamic_cast*>(pctx); - return ctx->getHandler(); - } - } - - void finalize() { - finalizeHelper(); - auto ctx = ctxs_.empty() ? &ctx_ : ctxs_.front().get(); - front_ = dynamic_cast*>(ctx); - if (!front_) { - throw std::invalid_argument("wrong type for first handler"); - } + std::shared_ptr(context, [](Context*){})); } - protected: - void finalizeHelper() { - Pipeline::finalizeHelper(); - back_ = Pipeline::back_; - if (!back_) { - auto is_at_end = Pipeline::is_end; - CHECK(is_at_end); - back_ = dynamic_cast*>(&ctx_); - if (!back_) { - throw std::invalid_argument("wrong type for last handler"); - } - } - - if (!ctxs_.empty()) { - for (size_t i = 0; i < ctxs_.size() - 1; i++) { - ctxs_[i]->link(ctxs_[i+1].get()); + void detachHandlers() { + for (auto& ctx : ctxs_) { + if (ctx != owner_) { + ctx->detachPipeline(); } - ctxs_.back()->link(&ctx_); - } - - auto nextFront = Pipeline::getLocalFront(); - if (nextFront) { - ctx_.link(nextFront); } } - PipelineContext* getLocalFront() { - return ctxs_.empty() ? &ctx_ : ctxs_.front().get(); - } + private: + std::shared_ptr transport_; + WriteFlags writeFlags_{WriteFlags::NONE}; + std::pair readBufferSettings_{2048, 2048}; - static const bool is_end{false}; InboundHandlerContext* front_{nullptr}; OutboundHandlerContext* back_{nullptr}; - - private: - template - void forEachCtx(const F& func) { - for (auto& ctx : ctxs_) { - func(ctx.get()); - } - func(&ctx_); - } - - ContextImpl ctx_; - std::vector> ctxs_; + std::vector> ctxs_; + std::shared_ptr owner_; }; }} diff --git a/folly/wangle/channel/StaticPipeline.h b/folly/wangle/channel/StaticPipeline.h new file mode 100644 index 00000000..3c644eb7 --- /dev/null +++ b/folly/wangle/channel/StaticPipeline.h @@ -0,0 +1,125 @@ +/* + * Copyright 2015 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace folly { namespace wangle { + +/* + * StaticPipeline allows you to create a Pipeline with minimal allocations. + * Specify your handlers after the input/output types of your Pipeline in order + * from front to back, and construct with either H&&, H*, or std::shared_ptr + * for each handler. The pipeline will be finalized for you at the end of + * construction. For example: + * + * StringToStringHandler stringHandler1; + * auto stringHandler2 = std::make_shared(); + * + * StaticPipeline( + * IntToStringHandler(), // H&& + * &stringHandler1, // H* + * stringHandler2) // std::shared_ptr + * pipeline; + * + * You can then use pipeline just like any Pipeline. See Pipeline.h. + */ +template +class StaticPipeline; + +template +class StaticPipeline : public Pipeline { + protected: + explicit StaticPipeline(bool) : Pipeline() {} +}; + +template +class StaticPipeline + : public StaticPipeline { + public: + template + explicit StaticPipeline(HandlerArgs&&... handlers) + : StaticPipeline(true, std::forward(handlers)...) { + isFirst_ = true; + } + + ~StaticPipeline() { + if (isFirst_) { + Pipeline::detachHandlers(); + } + } + + protected: + typedef ContextImpl, Handler> Context; + + template + StaticPipeline( + bool isFirst, + HandlerArg&& handler, + HandlerArgs&&... handlers) + : StaticPipeline( + false, + std::forward(handlers)...) { + isFirst_ = isFirst; + setHandler(std::forward(handler)); + CHECK(handlerPtr_); + ctx_.initialize(this, handlerPtr_); + Pipeline::addContextFront(&ctx_); + if (isFirst_) { + Pipeline::finalize(); + } + } + + private: + template + typename std::enable_if::type, + Handler + >::value>::type + setHandler(HandlerArg&& arg) { + handler_.emplace(std::forward(arg)); + handlerPtr_ = std::shared_ptr(&(*handler_), [](Handler*){}); + } + + template + typename std::enable_if::type, + std::shared_ptr + >::value>::type + setHandler(HandlerArg&& arg) { + handlerPtr_ = std::forward(arg); + } + + template + typename std::enable_if::type, + Handler* + >::value>::type + setHandler(HandlerArg&& arg) { + handlerPtr_ = std::shared_ptr(arg, [](Handler*){}); + } + + bool isFirst_; + folly::Optional handler_; + std::shared_ptr handlerPtr_; + ContextImpl, Handler> ctx_; +}; + +}} // folly::wangle diff --git a/folly/wangle/channel/test/OutputBufferingHandlerTest.cpp b/folly/wangle/channel/test/OutputBufferingHandlerTest.cpp index a08509b6..51f67275 100644 --- a/folly/wangle/channel/test/OutputBufferingHandlerTest.cpp +++ b/folly/wangle/channel/test/OutputBufferingHandlerTest.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include +#include #include #include #include @@ -35,8 +35,8 @@ MATCHER_P(IOBufContains, str, "") { return arg->moveToFbString() == str; } TEST(OutputBufferingHandlerTest, Basic) { MockBytesHandler mockHandler; EXPECT_CALL(mockHandler, attachPipeline(_)); - Pipeline, - HandlerPtr, + StaticPipeline, + MockBytesHandler, OutputBufferingHandler> pipeline(&mockHandler, OutputBufferingHandler{}); diff --git a/folly/wangle/channel/test/PipelineTest.cpp b/folly/wangle/channel/test/PipelineTest.cpp index 5fa97a64..d4d2ce7c 100644 --- a/folly/wangle/channel/test/PipelineTest.cpp +++ b/folly/wangle/channel/test/PipelineTest.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -27,7 +28,6 @@ using namespace folly::wangle; using namespace testing; typedef StrictMock> IntHandler; -typedef HandlerPtr IntHandlerPtr; ACTION(FireRead) { arg0->fireRead(arg1); @@ -55,7 +55,7 @@ TEST(PipelineTest, RealHandlersCompile) { auto socket = AsyncSocket::newSocket(&eb); // static { - Pipeline, + StaticPipeline, AsyncSocketHandler, OutputBufferingHandler> pipeline{AsyncSocketHandler(socket), OutputBufferingHandler()}; @@ -82,7 +82,7 @@ TEST(PipelineTest, FireActions) { EXPECT_CALL(handler1, attachPipeline(_)); EXPECT_CALL(handler2, attachPipeline(_)); - Pipeline + StaticPipeline pipeline(&handler1, &handler2); EXPECT_CALL(handler1, read_(_, _)).WillOnce(FireRead()); @@ -114,7 +114,7 @@ TEST(PipelineTest, FireActions) { TEST(PipelineTest, ReachEndOfPipeline) { IntHandler handler; EXPECT_CALL(handler, attachPipeline(_)); - Pipeline + StaticPipeline pipeline(&handler); EXPECT_CALL(handler, read_(_, _)).WillOnce(FireRead()); @@ -143,7 +143,7 @@ TEST(PipelineTest, TurnAround) { EXPECT_CALL(handler1, attachPipeline(_)); EXPECT_CALL(handler2, attachPipeline(_)); - Pipeline + StaticPipeline pipeline(&handler1, &handler2); EXPECT_CALL(handler1, read_(_, _)).WillOnce(FireRead()); @@ -158,20 +158,20 @@ TEST(PipelineTest, TurnAround) { TEST(PipelineTest, DynamicFireActions) { IntHandler handler1, handler2, handler3; EXPECT_CALL(handler2, attachPipeline(_)); - Pipeline + StaticPipeline pipeline(&handler2); EXPECT_CALL(handler1, attachPipeline(_)); EXPECT_CALL(handler3, attachPipeline(_)); pipeline - .addFront(IntHandlerPtr(&handler1)) - .addBack(IntHandlerPtr(&handler3)) + .addFront(&handler1) + .addBack(&handler3) .finalize(); - EXPECT_TRUE(pipeline.getHandler(0)); - EXPECT_TRUE(pipeline.getHandler(1)); - EXPECT_TRUE(pipeline.getHandler(2)); + EXPECT_TRUE(pipeline.getHandler(0)); + EXPECT_TRUE(pipeline.getHandler(1)); + EXPECT_TRUE(pipeline.getHandler(2)); EXPECT_CALL(handler1, read_(_, _)).WillOnce(FireRead()); EXPECT_CALL(handler2, read_(_, _)).WillOnce(FireRead()); @@ -217,7 +217,7 @@ TEST(Pipeline, DynamicConstruction) { std::invalid_argument); } { - Pipeline + StaticPipeline pipeline{StringHandler(), StringHandler()}; // Exercise both addFront and addBack. Final pipeline is @@ -235,7 +235,7 @@ TEST(Pipeline, DynamicConstruction) { TEST(Pipeline, AttachTransport) { IntHandler handler; EXPECT_CALL(handler, attachPipeline(_)); - Pipeline + StaticPipeline pipeline(&handler); EventBase eb; diff --git a/folly/wangle/service/ClientDispatcher.h b/folly/wangle/service/ClientDispatcher.h index ac8ccc92..b05aa9a2 100644 --- a/folly/wangle/service/ClientDispatcher.h +++ b/folly/wangle/service/ClientDispatcher.h @@ -34,9 +34,7 @@ class SerialClientDispatcher : public HandlerAdapter void setPipeline(Pipeline* pipeline) { pipeline_ = pipeline; - pipeline->addBack( - HandlerPtr, false>( - this)); + pipeline->addBack(this); pipeline->finalize(); } -- 2.34.1