exception - 提升上下文:异常传播问题
问题描述
我正在使用boost::context::execution_context
(版本 2)编写一个 C++ 11 库,我想将异常从一个传播execution_context
到调用执行。
我想处理客户端提供给我的库函数的 lambda 内的异常;但是,我遇到了一个奇怪的问题,在某些情况下 boost::context 没有正确处理异常。
这可以按预期工作,并且与一些 boost 的测试和示例非常相似:
TEST(execution_context, works) {
// Client callable
auto &&f = [](boost::context::execution_context<void> &&ctx) {
throw std::runtime_error("help!");
return std::move(ctx);
};
// Library code
std::exception_ptr exc{};
boost::context::execution_context<void> source(
[&exc, &f](boost::context::execution_context<void> &&ctx) {
try {
ctx = f(std::move(ctx));
} catch (boost::context::detail::forced_unwind const &) {
throw;
} catch (...) {
exc = std::current_exception();
}
return std::move(ctx);
});
try {
source = source();
if (exc) {
std::rethrow_exception(exc);
}
} catch (std::runtime_error const &) {
std::cout << "Runtime Error Caught" << std::endl;
}
}
输出:
[ RUN ] execution_context.works
Runtime Error Caught
[ OK ] execution_context.works (0 ms)
但以下更改不起作用:
我们添加一个包装类execution_context
:
class Core {
boost::context::execution_context<void> ctx_;
public:
explicit Core(boost::context::execution_context<void> &&ctx)
: ctx_{std::move(ctx)} {}
auto &&done() { return std::move(ctx_); }
};
现在我们进行与之前相同的测试,但使用定义的类:
TEST(execution_context, fails) {
// Client callable
auto &&f = [](Core c) {
throw std::runtime_error("help!");
return c.done();
};
// Library code
std::exception_ptr exc{};
boost::context::execution_context<void> source(
[&exc, &f](boost::context::execution_context<void> &&ctx) {
try {
ctx = f(Core(std::move(ctx)));
} catch (boost::context::detail::forced_unwind const &) {
throw;
} catch (...) {
exc = std::current_exception();
}
return std::move(ctx);
});
try {
source = source();
if (exc) {
std::rethrow_exception(exc);
}
} catch (std::runtime_error const &) {
std::cout << "Runtime Error Caught" << std::endl;
}
}
输出:
[ RUN ] execution_context.fails
unknown file: Failure
Unknown C++ exception thrown in the test body.
generators.t.tsk: /home/plewis/dpkg/refroot/amd64/opt/include/boost/context/detail/exception.hpp:37: boost::context::detail::forced_unwind::~forced_unwind(): Assertion `caught' failed.
zsh: abort (core dumped) ./test.t.tsk
我注意到的唯一区别是它execution_context
包含在一个类中,这会导致异常处理不当。这是没有意义的。
环境
我正在使用 GTest。
编译器
> g++ --version
g++ (GCC) 5.3.1 20160406 (Red Hat 5.3.1-6)
...
系统
> uname -a
Linux myhostname 2.6.32-642.6.2.el6.x86_64 #1 SMP Mon Oct 24 10:22:33 EDT 2016 x86_64 x86_64 x86_64 GNU/Linux
促进
boost version 1.69.0 compiled for amd64
解决方案
问题在于移动ctx
实例。假设您有两个实例execution_context
:
execution_context src, dest;
// src and dest have some states
dest = std::move(src); // [1]
在 [1] 中,我们调用移动赋值运算符,它“窃取”资源src
并将它们放入dest
. 如果execution_context
有一个指针,这个指针在src
实例中移动后会为0,所以这个对象是没用的,不应该使用。对其进行的任何操作都可能引发一些不良行为。
简而言之,您的代码如下所示:
void foo ()
{
auto &&f = [](Core c) {};
boost::context::execution_context<void> source(
[&exc, &f](boost::context::execution_context<void> &&ctx) {
// many lines
return std::move(ctx);
});
source = source();
}
我们有两个上下文,foo
并将 lambda 的主体传递给源构造函数。当source()
被调用时,上下文被切换并被foo
恢复并被lambda
执行。在这个 lambda 上下文中foo
被销毁,因为它只是被移动到Core
实例中。那么你想如何恢复foo
执行呢?你不能。
核心问题:
class Core {
boost::context::execution_context<void> ctx_;
public:
explicit Core(boost::context::execution_context<void> &&ctx)
: ctx_{std::move(ctx)} {} // [2]
auto &&done() { return std::move(ctx_); }
};
ctx_
是非引用数据成员,因此在 [2] 中调用了移动构造函数,它窃取了 ctx 的资源。
done
如果它没有抛出异常,下一个问题将是方法:看这个:
auto &&f = [](Core c)
{
// throw std::runtime_error("help!"); COMMENTED
return c.done();
};
c
在 lambda 内部是本地的。返回对在 lambda 结束时被销毁done
的数据成员的引用。Core
所以你有悬空的参考。
修复:您可以只将对上下文的引用存储在Core
. 那么原始上下文foo
将是安全的。
推荐阅读
- ios - SwiftUI 有什么方法可以创建一个 TabView 来显示 Tab Item 下面的 View
- mysql - 我需要选择一个组总计以及各个行吗?
- python - 问;如何解决移动平台问题?在pygame中
- ios - SwiftUI 在哪里加载 MVVM 中的数据
- c - 动态数组 C
- javascript - 使用 sh 脚本在 URL 上发送带有指定查询参数的节点 js 服务器请求
- google-sheets - 行中的第一个非空白单元格作为列的数组
- docker - Docker 启动容器:创建覆盖挂载时出错 ... /merged:没有这样的文件或目录
- google-api - 为什么包缺少“AudioEncoding”?
- python - python:为什么当我调用函数时它说未定义的变量?