You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Tree::~Tree() calls std::terminate when a node throws during halt (e.g. TryCatch catch_on_halt) #1281
Tree::~Tree() calls haltTree() (src/bt_factory.cpp:719). A destructor is implicitly noexcept, so if any node throws while halting, the process aborts in std::terminate.
One easy way to get there is TryCatch catch_on_halt="true": TryCatchNode::halt() ticks the catch child, and a tick is allowed to throw. The tree-exec fuzzer in #1277 found this; it currently avoids the case by never generating catch_on_halt="true".
classThrower : publicBT::SyncActionNode {
public:Thrower(const std::string& n, constBT::NodeConfig& c) : SyncActionNode(n, c) {}
staticBT::PortsList providedPorts() { return {}; }
BT::NodeStatus tick() override { throwstd::runtime_error("cleanup failed"); }
};
// <TryCatch catch_on_halt="true"> <Sleep msec="1000"/> <Thrower/> </TryCatch>auto tree = factory.createTreeFromText(xml);
tree.tickExactlyOnce(); // RUNNING// tree goes out of scope
terminate called after throwing an instance of 'BT::NodeExecutionError'
what(): Exception in node 'Thrower::3' [Thrower]: cleanup failed
Aborted (core dumped)
On master, catching doesn't help either. If the user calls tree.haltTree() inside a try/catch before the tree is destroyed, that first exception is caught, but ~Tree() still aborts. TryCatchNode::halt() throws before it reaches ControlNode::halt(), so the node stays RUNNING, and the destructor halts it a second time and throws again.
#1273 (reworked) adds a guard to TreeNode::haltNode(): when halt() throws, the node's status is reset to IDLE before the exception propagates. That fixes the second scenario (catch haltTree(), then destroy the tree). The first scenario still terminates, because there the first throw happens inside ~Tree() itself.
Possible fix
Don't let exceptions escape ~Tree(): catch around haltTree() and report them on stderr, as other destructors do.
TryCatchNode::halt() still skips resetting in_catch_/current_child_idx_ when the catch child throws. A try/catch + rethrow there would leave the node fully reset.
Activity