本文基於Boost 1.69,在展現源代碼時刪減了部分deprecated或者不一樣配置的與本文主題無關的代碼塊。react
這裏以圍繞reactor提供服務的reative_socket_service
爲例,介紹一下service。windows
// file: <boost/asio/detail/reactive_socket_service.hpp> ... template <typename Protocol> class reactive_socket_service : public service_base<reactive_socket_service<Protocol> >, public reactive_socket_service_base ...
reative_socket_service
繼承了service_base<reactive_socket_service<Protocol>>
,前文已經介紹了,service_base
包含一個靜態成員id
。因爲子類是父類模版的參數(Curiously Recurring Template Pattern),因此每個繼承了service_base
的類包含一個不一樣的靜態成員id
。這個id
能夠用於註冊服務。服務器
// file: <boost/asio/io_context.hpp> ... template <typename Type> class service_base : public boost::asio::io_context::service { public: static boost::asio::detail::service_id<Type> id; // Constructor. service_base(boost::asio::io_context& io_context) : boost::asio::io_context::service(io_context){} }; ...
reative_socket_service
繼承了reactive_socket_service_base
,這個父類已經實現了至關多的功能。reactive_socket_service_base
成員爲io_context
引用、reactor
引用。其核心成員函數爲reactive_socket_service_base::start_op
,將具體的impl
(native socket handle及其狀態、native socket handle相關的reactor數據)、op
(操做)傳遞給reactor
。值得注意的是,若是noop
爲真,代表這是一個通常性操做,reactor會將任務提交給scheduler來完成。關於op、handler等概念,本文後面會進行講解。異步
// file: <boost/asio/detail/impl/reactive_socket_service_base.ipp> ... void reactive_socket_service_base::start_op( reactive_socket_service_base::base_implementation_type& impl, int op_type, reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop) { if (!noop) { if ((impl.state_ & socket_ops::non_blocking) || socket_ops::set_internal_non_blocking( impl.socket_, impl.state_, true, op->ec_)) { reactor_.start_op(op_type, impl.socket_, impl.reactor_data_, op, is_continuation, is_non_blocking); return; } } reactor_.post_immediate_completion(op, is_continuation); } ...
介紹了reactive_socket_service_base::start_op
,咱們再來看一個與socket io操做相關的成員函數reactive_socket_service_base::async_send
。async_send
成員函數自己很簡單,其參數主要是socket(impl
)、數據(buffers
)和回調(handler
),經過將任意類型的handler
包裝成reactive_socket_send_op
而後經過調用start_op
把任務傳遞給reactor
。start_op
成員函數最後一個參數爲noop
,當socket(impl
)爲stream_oriented且buffers是empty時(即noop
=true),任務會直接交給scheduler
。socket
// file: <boost/asio/detail/impl/reactive_socket_service_base.ipp> ... // Start an asynchronous send. The data being sent must be valid for the // lifetime of the asynchronous operation. template <typename ConstBufferSequence, typename Handler> void async_send(base_implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, Handler& handler) { bool is_continuation = boost_asio_handler_cont_helpers::is_continuation(handler); // Allocate and construct an operation to wrap the handler. typedef reactive_socket_send_op<ConstBufferSequence, Handler> op; typename op::ptr p = { boost::asio::detail::addressof(handler), op::ptr::allocate(handler), 0 }; p.p = new (p.v) op(impl.socket_, impl.state_, buffers, flags, handler); BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", &impl, impl.socket_, "async_send")); start_op(impl, reactor::write_op, p.p, is_continuation, true, ((impl.state_ & socket_ops::stream_oriented) && buffer_sequence_adapter<boost::asio::const_buffer, ConstBufferSequence>::all_empty(buffers))); p.v = p.p = 0; } ...
做爲Boost.Asio的用戶,若是要實現異步的tcp服務器的話,離不開相應的boost::asio::ip::tcp::socket
(即basic_stream_socket<tcp>
),這裏就以此來介紹service的使用。查看源代碼可知,basic_stream_socket
繼承了basic_socket
,basic_socket
繼承了basic_io_object
。其中BOOST_ASIO_SVC_T
爲服務的類型,在windows之外平臺被定義爲detail::reactive_socket_service<Protocol>
。async
// file: <boost/asio/basic_stream_socket.hpp> ... template <typename Protocol BOOST_ASIO_SVC_TPARAM_DEF1(= stream_socket_service<Protocol>)> class basic_stream_socket : public basic_socket<Protocol BOOST_ASIO_SVC_TARG> ... // file: <boost/asio/basic_socket.hpp> ... #if !defined(BOOST_ASIO_ENABLE_OLD_SERVICES) # if defined(BOOST_ASIO_WINDOWS_RUNTIME) # include <boost/asio/detail/winrt_ssocket_service.hpp> # define BOOST_ASIO_SVC_T detail::winrt_ssocket_service<Protocol> # elif defined(BOOST_ASIO_HAS_IOCP) # include <boost/asio/detail/win_iocp_socket_service.hpp> # define BOOST_ASIO_SVC_T detail::win_iocp_socket_service<Protocol> # else # include <boost/asio/detail/reactive_socket_service.hpp> # define BOOST_ASIO_SVC_T detail::reactive_socket_service<Protocol> # endif #endif // !defined(BOOST_ASIO_ENABLE_OLD_SERVICES) ... template <typename Protocol BOOST_ASIO_SVC_TPARAM> class basic_socket : BOOST_ASIO_SVC_ACCESS basic_io_object<BOOST_ASIO_SVC_T>, public socket_base ...
查看basic_io_object
的源代碼可知,basic_io_object
包含兩個成員:service_
和implementation_
,其中implementation_
包含socket native handle及相關狀態和handle相關的reactor數據, service_
經過boost::asio::use_service<IoObjectService>(io_context)
函數構建,use_service
查詢io_context
的服務隊列來返回對應的服務,若是隊列中沒有相應的服務,io_context
會構建一個新的服務。tcp
// file: <boost/asio/basic_io_object.hpp> ... #if !defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) template <typename IoObjectService> #else template <typename IoObjectService, bool Movable = detail::service_has_move<IoObjectService>::value> #endif class basic_io_object { public: /// The type of the service that will be used to provide I/O operations. typedef IoObjectService service_type; /// The underlying implementation type of I/O object. typedef typename service_type::implementation_type implementation_type; ... protected: ... explicit basic_io_object(boost::asio::io_context& io_context) : service_(boost::asio::use_service<IoObjectService>(io_context)) { service_.construct(implementation_); } ... /// Get the service associated with the I/O object. service_type& get_service() { return service_; } ... /// Get the underlying implementation of the I/O object. implementation_type& get_implementation() { return implementation_; } ... private: ... // The service associated with the I/O object. service_type& service_; /// The underlying implementation of the I/O object. implementation_type implementation_; }; ...
以上,咱們得知了boost::asio::ip::tcp::socket
如何獲取服務,下面再來看看如何使用服務。以boost::asio::ip::tcp::socket
的async_send
爲例,查看源代碼可知,async_send
調用service的相關async成員函數,並傳入implementation、數據及回調便可。handler自己的處理稍後講解。ide
// file: <boost/asio/basic_stream_socket.hpp> ... template <typename ConstBufferSequence, typename WriteHandler> BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, void (boost::system::error_code, std::size_t)) async_send(const ConstBufferSequence& buffers, socket_base::message_flags flags, BOOST_ASIO_MOVE_ARG(WriteHandler) handler) { // If you get an error on the following line it means that your handler does // not meet the documented type requirements for a WriteHandler. BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; ... async_completion<WriteHandler, void (boost::system::error_code, std::size_t)> init(handler); this->get_service().async_send( this->get_implementation(), buffers, flags, init.completion_handler); return init.result.get(); ... } ...
對於異步io來講,用戶沒法直接等待io完成後進行後續的操做,這類「操做」須要保存起來,在io到達某個特定狀態後由庫自動調用。對於這一過程,我將簡略介紹相關概念,Asio實現,並列出一些文獻供讀者深刻理解與學習。函數
Boost.Asio 引入了一個統一的異步模型,文章 N3747. A Universal Model for Asynchronous Operations 詳細講解了其中涉及的各種異步操做和統一模型的概念。從文章 N3747 看出,C++的異步操做包含的類型有callbacks、future、resumable functions及協程。因爲篇幅有限,本文再也不復述文章 N3747 中的概念性內容,而着重於經過分析Asio源代碼來介紹這一模型的具體實現。oop
下面首先以Asio內部的角度(scheduler操做隊列,reactor操做隊列等)來了解異步操做在Asio中如何實現。查看源碼可知,scheduler
操做隊列的實際類型爲op_queue<scheduler_operation>
,而io_context
生成任務並傳遞給scheduler
的類型是detail::executor_op<function_type, Allocator, detail::operation>
,所以咱們首先關注兩個類:scheduler_operation
和executor_op
。scheduler_operation
是一個普通的類,而executor_op
是一個類模版並默認繼承scheduler_operation
(這裏繼承關係很重要)。這兩個類的結合產生了有趣的做用,其中
scheduler_operation
:回調接口,類型擦除executor_op
:類模版,負責產生多樣性,並將多樣性傳遞給父類scheduler_operation
具體來講,executor_op
包含一個靜態成員函數do_complete
,將base
(void *
)cast爲executor_op *
在進行相關操做,而scheduler_operation
有一個類型爲函數指針的數據成員func_
。executor_op
構建時將靜態成員函數do_complete
的地址賦值給scheduler_operation
的數據成員func_
函數指針,scheduler_operation
執行completion的時候會調用這個函數將自身this
還原(cast)爲實例化的executor_op *
。經過子類模版和子類靜態方法,父類和父類數據成員函數指針,實現了類型擦除與重現的功能。
scheduler_operation
和executor_op
,生命週期管理。父類scheduler_operation
的成員函數complete
銷燬對象並完成回調(調用func_(owner,...)
),destroy
僅僅銷燬對象(調用func_(0,...)
),而父類的析構函數並非虛函數且函數體爲空。查看executor_op
的靜態成員函數do_complete
,其生命管理過程以下:
o
(類型爲executor_op*
)的allocator
o
生命週期的ptr
類實例p
,注意p
的析構函數會自動銷燬o
(若是o
在這以前未被銷燬的話)。o
的數據成員handler_
移動到一個本地變量,p.reset()
銷燬o
owner
的值決定是否調用handler_
// file: <boost/asio/detail/executor_op.hpp> ... template <typename Handler, typename Alloc, typename Operation = scheduler_operation> class executor_op : public Operation { public: BOOST_ASIO_DEFINE_HANDLER_ALLOCATOR_PTR(executor_op); template <typename H> executor_op(BOOST_ASIO_MOVE_ARG(H) h, const Alloc& allocator) : Operation(&executor_op::do_complete), handler_(BOOST_ASIO_MOVE_CAST(H)(h)), allocator_(allocator) { } static void do_complete(void* owner, Operation* base, const boost::system::error_code& /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. executor_op* o(static_cast<executor_op*>(base)); Alloc allocator(o->allocator_); ptr p = { detail::addressof(allocator), o, o }; ... Handler handler(BOOST_ASIO_MOVE_CAST(Handler)(o->handler_)); p.reset(); // Make the upcall if required. if (owner) { ... } } private: Handler handler_; Alloc allocator_; }; ... // file: <boost/asio/detail/scheduler_operation.hpp> ... class scheduler_operation BOOST_ASIO_INHERIT_TRACKED_HANDLER { public: typedef scheduler_operation operation_type; void complete(void* owner, const boost::system::error_code& ec, std::size_t bytes_transferred) { func_(owner, this, ec, bytes_transferred); } void destroy() { func_(0, this, boost::system::error_code(), 0); } protected: typedef void (*func_type)(void*, scheduler_operation*, const boost::system::error_code&, std::size_t); scheduler_operation(func_type func) : next_(0), func_(func), task_result_(0) { } // Prevents deletion through this type. ~scheduler_operation() { } private: friend class op_queue_access; scheduler_operation* next_; func_type func_; protected: friend class scheduler; unsigned int task_result_; // Passed into bytes transferred. };
前面咱們經過介紹executor_op
已經大體瞭解了Asio核心部分組裝異步操做的方法,其餘的op例如reactor_op
及其子類的實現與executor_op
基本相似,這裏再也不額外進行說明。剩下的部分是異步操做的用戶接口和統一模型。
一個典型的接口是boost::asio::post
函數,該函數將異步操做(任意類型)提交給指定的io_context異步運行。爲了實現統一模型,函數必須決定返回值和異步操做的真正類型:返回值,若是用戶提交的是通常的callback,則返回值應爲空,若是是future這種方式的異步操做,則必須返回相應的對象;異步操做的真正類型轉換,對於callback來講,類型是functor,對於協程則是相應的placeholder,Asio必須將用戶提交的參數轉換爲真正的異步操做類型。
經過查看boost::asio::post
的函數體,咱們發現:
async_result_helper
被用來決定boost::asio::post
返回值的類型async_completion
在函數體內進行異步類型轉換,生成返回值閱讀async_completion
的源代碼發現,async_completion
類包含數據成員completion_handler
和result
,而這兩個成員的類型由async_result
類模版(traits)肯定(result
的類型也是async_result
)。默認狀況下,async_result
返回值類型爲空,真正的異步操做類型直接等於用戶提交的異步操做類(boost::asio::post
函數模版的形參CompletionToken
)。async_result
在默認狀況下沒有任何數據成員,這樣async_completion
的數據成員result
做爲異步操做的返回值(類型爲async_result
)能夠被編譯器優化,對於普通的不須要返回值的callback來講避免了額外的開銷。
爲了處理future類型的異步操做(須要返回值和真正的異步操做類型),經過定義async_result
關於future的特化便可。
// file: <boost/asio/impl/post.hpp> ... template <typename Executor, typename CompletionToken> BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) post( const Executor& ex, BOOST_ASIO_MOVE_ARG(CompletionToken) token, typename enable_if<is_executor<Executor>::value>::type*) { typedef BOOST_ASIO_HANDLER_TYPE(CompletionToken, void()) handler; async_completion<CompletionToken, void()> init(token); typename associated_allocator<handler>::type alloc( (get_associated_allocator)(init.completion_handler)); ex.post(detail::work_dispatcher<handler>(init.completion_handler), alloc); return init.result.get(); } ... // file: <boost/asio/async_result.hpp> ... template <typename CompletionToken, typename Signature> struct async_completion { /// The real handler type to be used for the asynchronous operation. typedef typename boost::asio::async_result< typename decay<CompletionToken>::type, Signature>::completion_handler_type completion_handler_type; ... /// Constructor. /** * The constructor creates the concrete completion handler and makes the link * between the handler and the asynchronous result. */ explicit async_completion(CompletionToken& token) : completion_handler(static_cast<typename conditional< is_same<CompletionToken, completion_handler_type>::value, completion_handler_type&, CompletionToken&&>::type>(token)), result(completion_handler) { } ... /// A copy of, or reference to, a real handler object. typename conditional< is_same<CompletionToken, completion_handler_type>::value, completion_handler_type&, completion_handler_type>::type completion_handler; ... /// The result of the asynchronous operation's initiating function. async_result<typename decay<CompletionToken>::type, Signature> result; }; ... template <typename CompletionToken, typename Signature> class async_result { public: ... /// The concrete completion handler type for the specific signature. typedef CompletionToken completion_handler_type; ... /// The return type of the initiating function. typedef void return_type; ... /// Construct an async result from a given handler. /** * When using a specalised async_result, the constructor has an opportunity * to initialise some state associated with the completion handler, which is * then returned from the initiating function. */ explicit async_result(completion_handler_type& h) #if defined(BOOST_ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) // No data members to initialise. { (void)h; } ... /// Obtain the value to be returned from the initiating function. return_type get() { ... // Nothing to do. ... } // No data members. }; ...