Boost.Asio源碼閱讀(2): 服務及異步操做

本文基於Boost 1.69,在展現源代碼時刪減了部分deprecated或者不一樣配置的與本文主題無關的代碼塊。react

服務類型

  • resolving service
  • socket/file operation
  • timer

service解析

這裏以圍繞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
...

(服務註冊)服務ID

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_sendasync_send成員函數自己很簡單,其參數主要是socket(impl)、數據(buffers)和回調(handler),經過將任意類型的handler包裝成reactive_socket_send_op而後經過調用start_op把任務傳遞給reactorstart_op成員函數最後一個參數爲noop,當socket(impl)爲stream_oriented且buffers是empty時(即noop=true),任務會直接交給schedulersocket

// 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;
}
...

使用service

做爲Boost.Asio的用戶,若是要實現異步的tcp服務器的話,離不開相應的boost::asio::ip::tcp::socket(即basic_stream_socket<tcp>),這裏就以此來介紹service的使用。查看源代碼可知,basic_stream_socket繼承了basic_socketbasic_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::socketasync_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_operationexecutor_opscheduler_operation是一個普通的類,而executor_op是一個類模版並默認繼承scheduler_operation(這裏繼承關係很重要)。這兩個類的結合產生了有趣的做用,其中

  • scheduler_operation:回調接口,類型擦除
  • executor_op:類模版,負責產生多樣性,並將多樣性傳遞給父類scheduler_operation

具體來講,executor_op 包含一個靜態成員函數do_complete,將basevoid *)cast爲executor_op *在進行相關操做,而scheduler_operation有一個類型爲函數指針的數據成員func_executor_op構建時將靜態成員函數do_complete的地址賦值給scheduler_operation的數據成員func_函數指針,scheduler_operation執行completion的時候會調用這個函數將自身this還原(cast)爲實例化的executor_op *。經過子類模版和子類靜態方法,父類和父類數據成員函數指針,實現了類型擦除與重現的功能。

scheduler_operationexecutor_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_handlerresult,而這兩個成員的類型由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.
};
...
相關文章
相關標籤/搜索