asio剖析(三)_添加任务-异步定时器剖析
前言
我们前面已经剖析了asio同步定时器,本文将继续对异步定时器进行剖析。
asio同步定时器剖析:https://blog.csdn.net/weijianjain/article/details/129732711?spm=1001.2014.3001.5502
这篇文章由于本人对模板知识不是很熟练,剖析模板的部分总是模糊描述。
一、异步定时器剖析
我们将在同部定时器的基础上,剖析异步定时器。
异步定时器实例
#include <iostream>
#include <asio.hpp>
int main()
{
asio::io_context io;
asio::steady_timer t(io, asio::chrono::seconds(5));
t.async_wait([](const asio::error_code& e)
{
std::cout << "Hello, world!" << std::endl;
});
io.run();
return 0;
}
asio::io_context io;
asio::steady_timer t(io, asio::chrono::seconds(5));
开始部分我们在同步定时器已经剖析了,就不再进行。
我们直接从t.async_wait异步函数开始,先看下t.async_wait源码.
///在计时器上启动异步等待。
/**
*此函数可用于针对
*计时器。它总是立即返回。
*
*对于async_wait()的每次调用,将精确地调用所提供的处理程序
*一次。以下情况下将调用处理程序:
*
*@li计时器已过期。
*
*@li计时器被取消,在这种情况下会将错误传递给处理程序
*代码asio::error::operation_aborted。
*
*@param handler计时器到期时要调用的处理程序。副本
*将根据需要由处理程序组成。的函数签名
*处理程序必须是:
*@code无效处理程序(
*const asio::error_code&error//操作结果。
* ); @结束代码
*无论异步操作是立即完成还是
*否则,处理程序将不会从该函数中调用。调用
*将以与使用
*asio::io_context::post().
*/
template <typename WaitHandler>
ASIO_INITFN_RESULT_TYPE(WaitHandler,
void (asio::error_code))
async_wait(ASIO_MOVE_ARG(WaitHandler) handler)
{
//如果您在以下行中得到错误,则意味着您的处理程序发生了错误
//不满足WaitHandler的文档化类型要求。
ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check;
#if defined(ASIO_ENABLE_OLD_SERVICES)
return this->get_service().async_wait(this->get_implementation(),
ASIO_MOVE_CAST(WaitHandler)(handler));
#else // defined(ASIO_ENABLE_OLD_SERVICES)
async_completion<WaitHandler,
void (asio::error_code)> init(handler);
this->get_service().async_wait(this->get_implementation(),
init.completion_handler);
return init.result.get();
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
}
1、anysc_wait函数第一步先进行参数校验
//如果您在以下行中得到错误,则意味着您的处理程序发生了错误
//不满足WaitHandler的文档化类型要求。
ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check;
ASIO_WAIT_HANDLER_CHECK宏的定义
#define ASIO_WAIT_HANDLER_CHECK( \
handler_type, handler) \
\
typedef ASIO_HANDLER_TYPE(handler_type, \
void(asio::error_code)) \
asio_true_handler_type; \
\
ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \
sizeof(asio::detail::one_arg_handler_test( \
asio::detail::rvref< \
asio_true_handler_type>(), \
static_cast<const asio::error_code*>(0))) == 1, \
"WaitHandler type requirements not met") \
\
typedef asio::detail::handler_type_requirements< \
sizeof( \
asio::detail::argbyv( \
asio::detail::rvref< \
asio_true_handler_type>())) + \
sizeof( \
asio::detail::lvref< \
asio_true_handler_type>()( \
asio::detail::lvref<const asio::error_code>()), \
char(0))> ASIO_UNUSED_TYPEDEF
将校验宏展开下
typedef ASIO_HANDLER_TYPE(WaitHandler,void(asio::error_code)) asio_true_handler_type;
ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(sizeof(asio::detail::one_arg_handler_test(asio::detail::rvref<asio_true_handler_type>(),static_cast<const asio::error_code*>(0))) == 1,"WaitHandler type requirements not met")
typedef asio::detail::handler_type_requirements<sizeof(asio::detail::argbyv(asio::detail::rvref<asio_true_handler_type>())) + sizeof(asio::detail::lvref<asio_true_handler_type>()(asio::detail::lvref<const asio::error_code>()),char(0))> ASIO_UNUSED_TYPEDEF
大体分段讲一下这个宏
- ASIO_HANDLER_TYPE
typedef ASIO_HANDLER_TYPE(WaitHandler,void(asio::error_code)) asio_true_handler_type;
...
#define ASIO_HANDLER_TYPE(ct, sig) \
typename ::asio::async_result< \
typename ::asio::decay<ct>::type, sig>::completion_handler_type
传进来的WaitHandler、handler这两个参数填充到ASIO_HANDLER_TYPE宏里面
typename ::asio::async_result<typename ::asio::decay<WaitHandler>::type,handler>::completion_handler_type asio_true_handler_type;
- ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT
# define ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(expr, msg) \
static_assert(expr, msg);
ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT整体代码
static_assert(sizeof(asio::detail::one_arg_handler_test(asio::detail::rvref<asio_true_handler_type>(),static_cast<const asio::error_code*>(0))) == 1,"WaitHandler type requirements not met");
这里是一个static_assert断言,对WaitHandler类型要求判断是否满足。
typedef asio::detail::handler_type_requirements<sizeof(asio::detail::argbyv(asio::detail::rvref<asio_true_handler_type>())) + sizeof(asio::detail::lvref<asio_true_handler_type>()(asio::detail::lvref<const asio::error_code>()),char(0))> ASIO_UNUSED_TYPEDEF
ASIO_WAIT_HANDLER_CHECK整体转换代码
typename ::asio::async_result<typename ::asio::decay<WaitHandler>::type,handler>::completion_handler_type asio_true_handler_type;
static_assert(sizeof(asio::detail::one_arg_handler_test(asio::detail::rvref<asio_true_handler_type>(),static_cast<const asio::error_code*>(0))) == 1,"WaitHandler type requirements not met");
typedef asio::detail::handler_type_requirements<sizeof(asio::detail::argbyv(asio::detail::rvref<asio_true_handler_type>())) + sizeof(asio::detail::lvref<asio_true_handler_type>()(asio::detail::lvref<const asio::error_code>()),char(0))> ASIO_UNUSED_TYPEDEF
2、async_completion<WaitHandler,void (asio::error_code)> init(handler);剖析
async_completion<WaitHandler,
void (asio::error_code)> init(handler);
async_completion类声明
///用于从CompletionToken推导处理程序类型的帮助程序模板,捕获
///处理程序的本地副本,然后为
///处理程序。
template <typename CompletionToken, typename Signature>
struct async_completion
{
/// 要用于异步操作的实际处理程序类型。
typedef typename asio::async_result<
typename decay<CompletionToken>::type,
Signature>::completion_handler_type completion_handler_type;
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// Constructor.
/**
* 构造函数创建具体的完成处理程序并创建链接
* 在处理程序和异步结果之间。
*/
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)
{
}
#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
explicit async_completion(typename decay<CompletionToken>::type& token)
: completion_handler(token),
result(completion_handler)
{
}
explicit async_completion(const typename decay<CompletionToken>::type& token)
: completion_handler(token),
result(completion_handler)
{
}
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// 实际处理程序对象的副本或引用。
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
typename conditional<
is_same<CompletionToken, completion_handler_type>::value,
completion_handler_type&, completion_handler_type>::type completion_handler;
#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
completion_handler_type completion_handler;
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// 异步操作的启动函数的结果。
async_result<typename decay<CompletionToken>::type, Signature> result;
};
async_completion<WaitHandler,void (asio::error_code)> init(handler);
这里定义进入了async_completion构造函数
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)
{
}
token函数参是我们异步定时器传入的lambda函数,跟进下,看看这里构造做了那些操作。
completion_handler(static_cast<typename conditional<
is_same<CompletionToken, completion_handler_type>::value,
completion_handler_type&, CompletionToken&&>::type>(token))
completion_handler成员变量的初始化
completion_handler定义
typename conditional<
is_same<CompletionToken, completion_handler_type>::value,
completion_handler_type&, completion_handler_type>::type completion_handler;
result成员变量的初始化,将completion_handler赋值到result。
3、async_wait剖析
this->get_service().async_wait(this->get_implementation(),
init.completion_handler);
这里是异步的重点,我们在同步定时器剖析里面讲过this->get_service()调用的函数,实现是调用deadline_timer_service类async_wait函数。
// 在计时器上启动异步等待。
template <typename Handler>
void async_wait(implementation_type& impl, Handler& handler)
{
// 分配并构造一个操作来包装处理程序.
typedef wait_handler<Handler> op;
typename op::ptr p = { asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(handler);
impl.might_have_pending_waits = true;
// 防止编译告警
ASIO_HANDLER_CREATION((scheduler_.context(),
*p.p, "deadline_timer", &impl, 0, "async_wait"));
scheduler_.schedule_timer(timer_queue_, impl.expiry, impl.timer_data, p.p);
p.v = p.p = 0;
}
wait_handler别名op
wait_handlder类
#define ASIO_DEFINE_HANDLER_PTR(op) \
struct ptr \
{ \
Handler* h; \
op* v; \
op* p; \
~ptr() \
{ \
reset(); \
} \
static op* allocate(Handler& handler) \
{ \
typedef typename ::asio::associated_allocator< \
Handler>::type associated_allocator_type; \
typedef typename ::asio::detail::get_hook_allocator< \
Handler, associated_allocator_type>::type hook_allocator_type; \
ASIO_REBIND_ALLOC(hook_allocator_type, op) a( \
::asio::detail::get_hook_allocator< \
Handler, associated_allocator_type>::get( \
handler, ::asio::get_associated_allocator(handler))); \
return a.allocate(1); \
} \
void reset() \
{ \
if (p) \
{ \
p->~op(); \
p = 0; \
} \
if (v) \
{ \
typedef typename ::asio::associated_allocator< \
Handler>::type associated_allocator_type; \
typedef typename ::asio::detail::get_hook_allocator< \
Handler, associated_allocator_type>::type hook_allocator_type; \
ASIO_REBIND_ALLOC(hook_allocator_type, op) a( \
::asio::detail::get_hook_allocator< \
Handler, associated_allocator_type>::get( \
*h, ::asio::get_associated_allocator(*h))); \
a.deallocate(static_cast<op*>(v), 1); \
v = 0; \
} \
} \
} \
template <typename Handler>
class wait_handler : public wait_op
{
public:
ASIO_DEFINE_HANDLER_PTR(wait_handler);
wait_handler(Handler& h)
: wait_op(&wait_handler::do_complete),
handler_(ASIO_MOVE_CAST(Handler)(h))
{
handler_work<Handler>::start(handler_);
}
static void do_complete(void* owner, operation* base,
const asio::error_code& /*ec*/,
std::size_t /*bytes_transferred*/)
{
// Take ownership of the handler object.
wait_handler* h(static_cast<wait_handler*>(base));
ptr p = { asio::detail::addressof(h->handler_), h, h };
handler_work<Handler> w(h->handler_);
ASIO_HANDLER_COMPLETION((*h));
//制作处理程序的副本,以便在
//进行上行呼叫。即使我们不打算打电话
//处理程序的子对象可能是关联内存的真正所有者
//使用处理程序。因此,需要处理程序的本地副本
//以确保任何拥有的子对象在我们
//取消分配此处的内存。
detail::binder1<Handler, asio::error_code>
handler(h->handler_, h->ec_);
p.h = asio::detail::addressof(handler.handler_);
p.reset();
// Make the upcall if required.
if (owner)
{
fenced_block b(fenced_block::half);
ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_));
w.complete(handler, handler.handler_);
ASIO_HANDLER_INVOCATION_END;
}
}
private:
Handler handler_;
};
typename op::ptr p = { asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
op::ptr结构体在wait_handler内部使用宏ASIO_DEFINE_HANDLER_PTR定义。
struct ptr
{
Handler* h;
op* v;
op* p;
...
}
p = { asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
这里给p变量初始化,asio::detail::addressof(handler)赋值到Handler* h,op::ptr::allocate(handler)分配内存op* v,ptr结构体内部p变量初始化为0。
p.p = new (p.v) op(handler);
p变量的p成员变量分配内存,async_wait的lamdba函数被分配到p
impl.might_have_pending_waits = true;
将异步等待标识置为true。
# define ASIO_HANDLER_CREATION(args) (void)0
...
// 防止编译告警
ASIO_HANDLER_CREATION((scheduler_.context(),
*p.p, "deadline_timer", &impl, 0, "async_wait"));
ASIO_HANDLER_CREATION防止编译告警
// 安排和执行计时器的对象。通常是一个反应堆。
timer_scheduler& scheduler_;
...
scheduler_.schedule_timer(timer_queue_, impl.expiry, impl.timer_data, p.p);
到schedule_timer这里就是异步添加任务到asio::io_context中
schedule_timer
template <typename Time_Traits>
void epoll_reactor::schedule_timer(timer_queue<Time_Traits>& queue,
const typename Time_Traits::time_type& time,
typename timer_queue<Time_Traits>::per_timer_data& timer, wait_op* op)
{
mutex::scoped_lock lock(mutex_);
if (shutdown_)
{
scheduler_.post_immediate_completion(op, false);
return;
}
bool earliest = queue.enqueue_timer(time, timer, op);
scheduler_.work_started();
if (earliest)
update_timeout();
}
epoll_reactor epoll处理类中。
//将新计时器添加到队列中。如果这是
//队列中最早的,在这种情况下,反应器的事件多路分解
//函数调用可能需要中断并重新启动。
bool enqueue_timer(const time_type& time, per_timer_data& timer, wait_op* op)
{
// 将计时器对象排队。
if (timer.prev_ == 0 && &timer != timers_)
{
if (this->is_positive_infinity(time))
{
// 对于永不过期的计时器,不需要任何堆条目。
timer.heap_index_ = (std::numeric_limits<std::size_t>::max)();
}
else
{
//将新计时器放在堆中的正确位置。这已经完成了
//第一个原因是pushback()由于分配失败而可能引发。
timer.heap_index_ = heap_.size();
heap_entry entry = { time, &timer };
heap_.push_back(entry);
up_heap(heap_.size() - 1);
}
// 将新计时器插入活动计时器的链接列表中。
timer.next_ = timers_;
timer.prev_ = 0;
if (timers_)
timers_->prev_ = &timer;
timers_ = &timer;
}
// 登记单个计时器操作。
timer.op_queue_.push(op);
// 仅当新添加的计时器最先到期时,才中断反应器。
return timer.heap_index_ == 0 && timer.op_queue_.front() == op;
}
我们可以看到在enqueue_timer函数中将async_wait的lambda函数体添加到任务中,待io::run运行完成后,退出程序。
4、 io.run()剖析
我们在前面文章已经对asio::io_context类做了简单剖析。
asio::io_context类剖析:https://blog.csdn.net/weijianjain/article/details/129732711?spm=1001.2014.3001.5502
在此基础上我们对op_queue_队列的操作剖析。
io.run();
...
io_context::count_type io_context::run()
{
asio::error_code ec;
count_type s = impl_.run(ec);
asio::detail::throw_error(ec);
return s;
}
...
std::size_t scheduler::run(asio::error_code& ec)
{
ec = asio::error_code();
// outstanding_work_原子操作,未完成工作的数量。
// 当outstanding_work_为0,程序没有要完成的工作,停止退出,这里的退出指asio的io操作被停止,直到调用restart()恢复使用为止。
if (outstanding_work_ == 0)
{
stop();
return 0;
}
// 绑定线程操作
thread_info this_thread;
this_thread.private_outstanding_work = 0;
thread_call_stack::context ctx(this, this_thread);
// 加锁
mutex::scoped_lock lock(mutex_);
// do_run_one返回1为真,lock解锁,运行循环,n++;
// do_run_one返回0为假,退出循环,函数结束
std::size_t n = 0;
for (; do_run_one(lock, this_thread, ec); lock.lock())
if (n != (std::numeric_limits<std::size_t>::max)())
++n;
return n;
}
op_queue_任务队列在do_run_one函数中不断消耗。
std::size_t scheduler::do_run_one(mutex::scoped_lock& lock,
scheduler::thread_info& this_thread,
const asio::error_code& ec)
{
// stopped_结束标识
while (!stopped_)
{
// 不断读取op_queue_队列,队列有数据则处理
if (!op_queue_.empty())
{
// 取出对列头节点,并移除
operation* o = op_queue_.front();
op_queue_.pop();
bool more_handlers = (!op_queue_.empty());
// 如果o对列头节点为最后一个节点
if (o == &task_operation_)
{
// task_interrupted_任务中断标识
task_interrupted_ = more_handlers;
if (more_handlers && !one_thread_)
wakeup_event_.unlock_and_signal_one(lock);
else
lock.unlock();
task_cleanup on_exit = { this, &lock, &this_thread };
(void)on_exit; // (void)on_exit防止编译告警,未使用函数的参数,编译期正常来说会抛出警告(warring),而在部分场景下,我们这么做可能是为了扩展等等。那么又不想看到这么多warring。
// Run the task. May throw an exception. Only block if the operation
// queue is empty and we're not polling, otherwise we want to return
// as soon as possible.
task_->run(more_handlers ? 0 : -1, this_thread.private_op_queue);
}
else
{
std::size_t task_result = o->task_result_;
if (more_handlers && !one_thread_)
wake_one_thread_and_unlock(lock);
else
lock.unlock();
// 确保在块退出时减少未完成工作的计数。
work_cleanup on_exit = { this, &lock, &this_thread };
(void)on_exit;
// 完成操作。可能引发异常。删除对象。
o->complete(this, ec, task_result);
return 1;
}
}
else
{
wakeup_event_.clear(lock); // 重置事件。
wakeup_event_.wait(lock); // 等待事件发出信号。
}
}
return 0;
}
我们直接在 o->complete剖析
// 完成操作。可能引发异常。删除对象。
o->complete(this, ec, task_result);
我们之前异步插入的队列元素,在这里会执行
void complete(void* owner, const asio::error_code& ec,
std::size_t bytes_transferred)
{
func_(owner, this, ec, bytes_transferred);
}
这里的func函数指针执行写在async_wait里面的lamdba函数。