Skip to content

LoopNode does not reload dynamic std::vector<T> input on second execution #1124

@soohwan-justin

Description

@soohwan-justin

Version

BehaviorTree.CPP 4.9.0

Describe the bug

When LoopNode<T> receives a dynamic input port mapped to a blackboard entry containing std::vector<T>, the first execution works correctly, but a second execution of the same tree instance may not reload the new vector value.

In my case:

  • an upstream node writes a new std::vector<T> to the blackboard output port
  • LoopNode<T> consumes it correctly on the first run
  • on the second run, the upstream node again writes the correct new vector
  • however, LoopNode<T> behaves as if the queue is empty or keeps using the previous internal queue state

This seems to happen because current_queue_ is preserved across executions, and for dynamic vector input the code only converts std::vector<T> into std::deque<T> when !current_queue_.

As a result, on the second execution, if current_queue_ is already non-null, the new vector from the blackboard is not reloaded.

Expected behavior

When the same tree instance starts a fresh execution, LoopNode<T> should reload the current dynamic input value from the blackboard.

If the input port contains a new std::vector<T>, it should be reflected in the loop execution.

Actual behavior

The first execution works, but the second execution may immediately see an empty queue or behave as if the previous internal queue state is still active.

Minimal reasoning about root cause

The problematic pattern appears to be:

if(status() == NodeStatus::IDLE)
{
  child_running_ = false;
  if(static_queue_)
  {
    current_queue_ = std::make_shared<std::deque<T>>();
    *current_queue_ = *static_queue_;
  }
}

and later:

auto queue_result = any_ref.get()->tryCast<SharedQueue<T>>();
if(queue_result)
{
  current_queue_ = queue_result.value();
}
else if(!current_queue_)
{
  auto vec_result = any_ref.get()->tryCast<std::vector<T>>();
  if(vec_result)
  {
    const auto& vec = vec_result.value();
    current_queue_ = std::make_shared<std::deque<T>>(vec.begin(), vec.end());
  }
}

If the input is dynamic std::vector<T>:

  • first run: current_queue_ is null, so the vector is converted and used
  • second run: current_queue_ is still non-null, so the new vector is not reloaded

Reproduction scenario

  1. Use a tree with an upstream node that writes a std::vector<T> output to a blackboard(Output port) entry.
  2. Feed that entry to LoopNode<T> through a remapped input/inout port.
  3. Run the tree once until completion.
  4. Without destroying the tree instance, update the upstream input again so that the upstream node writes a new vector.
  5. Run the tree again.
  6. Observe that the upstream node outputs the correct new vector, but LoopNode<T> does not reflect the new value.

Here is my test xml:
Image

In above image, {scenario_pair_list} is the std::vector<T>

What I confirmed

  • The upstream node definitely writes the expected vector on the second run.
  • The issue disappears if I modify LoopNode so that current_queue_ is reset when a fresh execution starts.
  • For example, resetting current_queue_ at the beginning of a fresh IDLE execution resolves the problem in my setup.

Example fix that works locally:

if(status() == NodeStatus::IDLE)
{
  child_running_ = false;
  current_queue_.reset();

  if(static_queue_)
  {
    current_queue_ = std::make_shared<std::deque<T>>();
    *current_queue_ = *static_queue_;
  }
}

Question

Is this intended behavior for dynamic std::vector<T> inputs, or should LoopNode<T> reload the blackboard value on each fresh execution of the node?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions