原文地址:devblogs.microsoft.com/oldnewthing…
原文作者:devblogs.microsoft.com/oldnewthing…
发布时间:2020年7月10日
前段时间,我们观察到,C++/WinRT依靠GetResults()方法的ABI结果来报告取消。这与C#和C++/CX中任务取消的预测方式不同。此外,这是一个泄漏的抽象。等待的代码需要知道底层操作是如何实现的,才能知道取消时将引发什么异常。
幸运的是,这一点在PR 643中已经得到了修正(发布的版本是2.0.200601.2),因此C++/WinRT在处理取消的方式上与其他的预测一致。现在它通过检查操作的状态来检测取消。
从概念上讲,这只是一个单行修复。
template <typename Async>
struct await_adapter
{
...
auto await_resume() const
{
if (async.Status() == AsyncStatus::Canceled) throw hresult_canceled();
return async.GetResults();
}
};
然而,这将虚拟方法调用async.Status()添加到热代码路径中。更糟糕的是,如果这是一个远程操作,虚拟方法调用必须跨越一个进程边界,这就更加昂贵了。如果你启用了Async-Async,那么这就变成了一个本地查询,但它仍然是虚拟的)。
解决的办法是缓存Completed回调报告的状态。
inline void check_status_canceled(AsyncStatus status)
{
if (status == AsyncStatus::Canceled) throw hresult_canceled();
}
template <typename Async>
struct await_adapter
{
AsyncStatus status = AsyncStatus::Started;
...
void await_suspend(std::experimental::coroutine_handle<> handle)
{
async.Completed([this, handler = disconnect_aware_handler{ handle }]
(auto&&, auto&& operation_status)
{
status = operation_status;
handler();
});
}
auto await_resume() const
{
check_status_canceled(status);
return async.GetResults();
}
};
将Canceled状态转换为异常的代码被剔除,因为需要对其他对操作结果做出反应的方法进行类似的修正。