Boost.Asio源码阅读(2): 服务及异步操作

本文基于Boost 1.69,在展示源代码时删减了部分deprecated或者不同配置的与本文主题无关的代码块。

服务类型

  • resolving service
  • socket/file operation
  • timer

service解析

这里以围绕reactor提供服务的reative_socket_service为例,介绍一下service。

// file: 
...
template 
class reactive_socket_service :
  public service_base >,
  public reactive_socket_service_base
...

(服务注册)服务ID

reative_socket_service继承了service_base>,前文已经介绍了,service_base包含一个静态成员id。由于子类是父类模版的参数(Curiously Recurring Template Pattern),所以每一个继承了service_base的类包含一个不同的静态成员id。这个id可以用于注册服务。

// file: 
...
template 
class service_base
  : public boost::asio::io_context::service
{
public:
  static boost::asio::detail::service_id 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: 
...
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),任务会直接交给scheduler

// file: 
...
// Start an asynchronous send. The data being sent must be valid for the
// lifetime of the asynchronous operation.
template 
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 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::all_empty(buffers)));
p.v = p.p = 0;
}
...

使用service

作为Boost.Asio的用户,如果要实现异步的tcp服务器的话,离不开相应的boost::asio::ip::tcp::socket(即basic_stream_socket),这里就以此来介绍service的使用。查看源代码可知,basic_stream_socket继承了basic_socketbasic_socket继承了basic_io_object。其中BOOST_ASIO_SVC_T为服务的类型,在windows以外平台被定义为detail::reactive_socket_service

// file: 
...
template )>
class basic_stream_socket
  : public basic_socket
...
// file: 
...
#if !defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
# if defined(BOOST_ASIO_WINDOWS_RUNTIME)
#  include 
#  define BOOST_ASIO_SVC_T detail::winrt_ssocket_service
# elif defined(BOOST_ASIO_HAS_IOCP)
#  include 
#  define BOOST_ASIO_SVC_T detail::win_iocp_socket_service
# else
#  include 
#  define BOOST_ASIO_SVC_T detail::reactive_socket_service
# endif
#endif // !defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
...
template 
class basic_socket
  : BOOST_ASIO_SVC_ACCESS basic_io_object,
    public socket_base
...

查看basic_io_object的源代码可知,basic_io_object包含两个成员:service_implementation_,其中implementation_包含socket native handle及相关状态和handle相关的reactor数据, service_通过boost::asio::use_service(io_context)函数构建,use_service查询io_context的服务队列来返回对应的服务,如果队列中没有相应的服务,io_context会构建一个新的服务。

// file: 
...
#if !defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
template 
#else
template ::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(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本身的处理稍后讲解。

// file: 
...
template 
  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 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源代码来介绍这一模型的具体实现。

内部组装和生命周期管理

下面首先以Asio内部的角度(scheduler操作队列,reactor操作队列等)来了解异步操作在Asio中如何实现。查看源码可知,scheduler操作队列的实际类型为op_queue,而io_context生成任务并传递给scheduler的类型是detail::executor_op,因此我们首先关注两个类: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: 
...
template 
class executor_op : public Operation
{
public:
  BOOST_ASIO_DEFINE_HANDLER_ALLOCATOR_PTR(executor_op);

  template 
  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(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: 
...
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: 
...
template 
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) post(
    const Executor& ex, BOOST_ASIO_MOVE_ARG(CompletionToken) token,
    typename enable_if::value>::type*)
{
  typedef BOOST_ASIO_HANDLER_TYPE(CompletionToken, void()) handler;
  async_completion init(token);
  typename associated_allocator::type alloc(
      (get_associated_allocator)(init.completion_handler));
  ex.post(detail::work_dispatcher(init.completion_handler), alloc);
  return init.result.get();
}
...
// file: 
...
template 
struct async_completion
{
  /// The real handler type to be used for the asynchronous operation.
  typedef typename boost::asio::async_result<
    typename decay::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::value,
        completion_handler_type&, CompletionToken&&>::type>(token)),
      result(completion_handler)
  {
  }
...

  /// A copy of, or reference to, a real handler object.
  typename conditional<
    is_same::value,
    completion_handler_type&, completion_handler_type>::type completion_handler;
  ...
  /// The result of the asynchronous operation's initiating function.
  async_result::type, Signature> result;
};
...
template 
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.
};
...

你可能感兴趣的:(boost,asio,c++)