本文基于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_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
。
// 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_socket
,basic_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
函数构建,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::socket
的async_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_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:
...
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_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:
...
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.
};
...