没有 co_return 未定义行为(UB)的 boost::asio::awaitable co 例程的结尾是否下降?我在 boost 文档中找不到任何相关内容。 Cppreference 说,当没有
Promise::return_void()
时它的 UB,与它相反的是 promise.return_void()
,它说对于所有情况,包括 co_return
和协同例程结束时的下降,也会被调用。
但是我发现了不同的行为,其中一个建议是 UB: 编译器资源管理器上的代码
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
boost::asio::awaitable<void> coroutineA() {
std::cout << "coroutineA" << std::endl;
}
boost::asio::awaitable<void> coroutineB() {
std::cout << "coroutineB" << std::endl;
co_return;
}
int main() {
boost::asio::io_context context;
auto waitA= boost::asio::co_spawn(context,
coroutineA(), boost::asio::use_future); //Seems to cause UB
auto waitB = boost::asio::co_spawn(context,
coroutineB(), boost::asio::use_future);
std::thread t([&context] () {
context.run();
});
waitA.get();
waitB.get();
t.join();
}
执行
coroutineA
会导致这个简单代码崩溃,在其他演示案例中,它会导致非0程序代码但非阻塞执行。
我能找到的唯一线索是在 boost 代码中:Boost 1.82
asio/impl/awaitable.hpp
为 void return_void()
和 template <typename Executor> class awaitable_frame<void, Executor>
定义了 template <typename Executor> class awaitable_frame<awaitable_thread_entry_point, Executor>
。根据我的理解,前一个应该是我们为您的两个协程提供的承诺类型。
Cppreference 指出,
co_return
和末端掉落都会调用 promise.return_void()
,即 awaitable_frame::return_void()
,我认为这是定义的。
那么,如果没有 co_return 未定义行为(UB),boost::asio::awaitable co 例程的结尾是否会下降?如果是,为什么?有人可以给我指出文档,或者更好地向我展示我的推理中的缺陷吗?
在阅读了 C++20 草案和进一步的实验之后,我有足够的信心说:
没有 co_return 未定义行为 (UB) 的 boost::asio::awaitable co 例程的结尾是否下降?
这要看情况。如果函数体中有任何其他协程关键字,例如
co_yield
、co_return
(如 if 分支中)或 co_await
,那么它不是 UB。如果函数中没有任何co例程关键字,则为UB。对于所有 C++20 协程都是如此,其中使用的 Promise 定义了 return_void()
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
boost::asio::awaitable<void> coroutineA(boost::asio::io_context& context) {
boost::asio::steady_timer t(context);
t.expires_after(std::chrono::seconds(1));
co_await t.async_wait(boost::asio::use_awaitable);//now this is a coroutine, falling of ok
std::cout << "coroutineA" << std::endl;
}//only ok thanks to 2 lines above
boost::asio::awaitable<void> coroutineB() {
if (false) {
co_return; //Not this is a coroutine, falling of ok
}
std::cout << "coroutineB" << std::endl;
}//only ok thanks to 3 lines above
int main() {
boost::asio::io_context context;
auto waitA= boost::asio::co_spawn(context,
coroutineA(context), boost::asio::use_future);
auto waitB = boost::asio::co_spawn(context,
coroutineB(), boost::asio::use_future);
std::thread t([&context] () {
context.run();
});
waitA.get();
waitB.get();
t.join();
}
C++20 标准及更高版本(我使用草案 N4861 和最新版本)在 9.5.4 协程定义 [dcl.fct.def.coroutine] 中 1:
中进行了说明如果一个函数的函数体包含一个协程返回语句(8.7.4)、一个等待表达式,则该函数是一个协程 (7.6.2.3),或yield表达式(7.6.17)。
在 Promise 类型的范围内查找非限定 ID return_void 和 return_value。如果两者都 发现该程序格式错误。 [注意:如果发现unqualified-id return_void,则从末尾流出 协程的 相当于没有操作数的 co_return。否则,从协程的末尾流出 导致未定义的行为(8.7.4)。 — 尾注]
因此,假设所使用的 Promise 正确定义了
return_void()
,例如boost::asio::awaitable<void>
确实如此,即在这种情况下,协程 的末尾掉落是可以的。
但是如果它的function主体包含即“包含”当前3个协程关键字之一:co_return
、
co_yield
、co_await
,那么它只是一个协程。他们是否真的被处决并不重要。 否则我们只有一个函数,而不是协程,并且非空函数末尾的下降是未定义的行为(UB),即真的非常糟糕(崩溃,有时工作等等)。