原文地址:devblogs.microsoft.com/oldnewthing…
原文作者:devblogs.microsoft.com/oldnewthing…
发布时间:2020年7月8日
上一次,我们了解到C++/WinRT服从于底层异步操作,以它认为合适的方式报告取消。今天,我们来看看异步操作是由C++/WinRT库生成的情况。
当你在一个C++/WinRT异步操作上调用Cancel()时,运行的代码是这样的。
struct promise_base : ...
{
...
void Cancel() noexcept
{
winrt::delegate<> cancel;
{
slim_lock_guard const guard(m_lock);
if (m_status == AsyncStatus::Started)
{
m_status = AsyncStatus::Canceled;
cancel = std::move(m_cancel);
}
}
if (cancel)
{
cancel();
}
}
};
promise过渡到Canceled,如果coroutine已经注册了一个取消回调,它就会被调用。
每当与承诺相关联的coroutine执行co_await时,await_transform就会启动(抱歉,我还没有解释,但请相信我),这时C++/WinRT库才有机会放弃操作。
template <typename Expression>
Expression&& await_transform(Expression&& expression)
{
if (Status() == AsyncStatus::Canceled)
{
throw winrt::hresult_canceled();
}
return std::forward<Expression>(expression);
}
抛出的hresult_canceled异常被捕获到操作中,以便以后重新抛出。
C++/WinRT库也会在coroutine运行到完成时检查是否取消。
struct promise_type final : ...
{
...
void return_void()
{
...
if (this->m_status == AsyncStatus::Started)
{
this->m_status = AsyncStatus::Completed;
}
else
{
WINRT_ASSERT(this->m_status == AsyncStatus::Canceled);
this->m_exception = make_exception_ptr(winrt::hresult_canceled());
}
...
}
};
如果操作已经被取消,那么我们制造一个假的hresult_canceled异常,并将其保存在m_exception中。
所以我们看到,无论操作的取消是由 await_transform 检测到的,还是由 return_void 检测到的(对于产生值的 coroutine,则由 return_value 检测到),我们最终都会在操作中存储一个 hresult_canceled 异常。
而当有人询问异步活动的结果时,又会出现这个异常。
struct promise_type final : ...
{
...
void GetResults()
{
...
if (this->m_status == AsyncStatus::Completed)
{
return;
}
this->rethrow_if_failed();
...
}
void rethrow_if_failed() const
{
if (m_status == AsyncStatus::Error || m_status == AsyncStatus::Canceled)
{
std::rethrow_exception(m_exception);
}
}
};
如果操作被取消了,那么我们就会达到rethrow_if_failed,它重抛捕获的异常,我们前面看到的异常将是一个hresult_canceled。
但是C++/WinRT并不是IAsyncAction和IAsyncOperation对象的唯一来源。下一次,我们将看看另一个主要来源。WRL.