Skip to content

Commit cd28bdc

Browse files
Fix early planning preemption (moveit#597)
Calling preempt() before plan() is able to reset the preempt_requested_ flag causes the preemption request to get lost. To avoid this issue, we allow a) manual resetting of the request and b) reset the request before leaving plan().
1 parent 60ccd74 commit cd28bdc

File tree

6 files changed

+44
-3
lines changed

6 files changed

+44
-3
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33
url = https://github.com/pybind/pybind11
44
branch = smart_holder
55
shallow = true
6+
[submodule "core/src/scope_guard"]
7+
path = core/src/scope_guard
8+
url = https://github.com/ricab/scope_guard

core/include/moveit/task_constructor/task.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,9 @@ class Task : protected WrapperBase
127127

128128
/// reset, init scene (if not yet done), and init all stages, then start planning
129129
moveit::core::MoveItErrorCode plan(size_t max_solutions = 0);
130-
/// interrupt current planning (or execution)
130+
/// interrupt current planning
131131
void preempt();
132+
void resetPreemptRequest();
132133
/// execute solution, return the result
133134
moveit::core::MoveItErrorCode execute(const SolutionBase& s);
134135

core/include/moveit/task_constructor/task_p.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class TaskPrivate : public WrapperBasePrivate
6363
std::string ns_;
6464
robot_model_loader::RobotModelLoaderPtr robot_model_loader_;
6565
moveit::core::RobotModelConstPtr robot_model_;
66-
bool preempt_requested_;
66+
std::atomic<bool> preempt_requested_;
6767

6868
// introspection and monitoring
6969
std::unique_ptr<Introspection> introspection_;

core/src/scope_guard

Submodule scope_guard added at 71a0452

core/src/task.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
#include <moveit/robot_model_loader/robot_model_loader.h>
4747
#include <moveit/planning_pipeline/planning_pipeline.h>
4848

49+
#include <scope_guard/scope_guard.hpp>
50+
4951
#include <functional>
5052

5153
namespace {
@@ -234,6 +236,9 @@ void Task::compute() {
234236
}
235237

236238
moveit::core::MoveItErrorCode Task::plan(size_t max_solutions) {
239+
// ensure the preempt request is resetted once this method exits
240+
auto guard = sg::make_scope_guard([this]() noexcept { this->resetPreemptRequest(); });
241+
237242
auto impl = pimpl();
238243
init();
239244

@@ -245,7 +250,6 @@ moveit::core::MoveItErrorCode Task::plan(size_t max_solutions) {
245250
explainFailure();
246251
return error_code;
247252
};
248-
impl->preempt_requested_ = false;
249253
const double available_time = timeout();
250254
const auto start_time = std::chrono::steady_clock::now();
251255
while (canCompute() && (max_solutions == 0 || numSolutions() < max_solutions)) {
@@ -266,6 +270,10 @@ void Task::preempt() {
266270
pimpl()->preempt_requested_ = true;
267271
}
268272

273+
void Task::resetPreemptRequest() {
274+
pimpl()->preempt_requested_ = false;
275+
}
276+
269277
moveit::core::MoveItErrorCode Task::execute(const SolutionBase& s) {
270278
actionlib::SimpleActionClient<moveit_task_constructor_msgs::ExecuteTaskSolutionAction> ac("execute_task_solution");
271279
if (!ac.waitForServer(ros::Duration(0.5))) {

core/test/test_container.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,3 +672,31 @@ TEST(Task, timeout) {
672672
EXPECT_TRUE(t.plan());
673673
EXPECT_EQ(t.solutions().size(), 2u);
674674
}
675+
676+
// https://github.com/moveit/moveit_task_constructor/pull/597
677+
// start planning in another thread, then preempt it in this thread
678+
TEST(Task, preempt) {
679+
moveit::core::MoveItErrorCode ec;
680+
resetMockupIds();
681+
682+
Task t;
683+
t.setRobotModel(getModel());
684+
685+
auto timeout = std::chrono::milliseconds(10);
686+
t.add(std::make_unique<GeneratorMockup>(PredefinedCosts::constant(0.0)));
687+
t.add(std::make_unique<TimedForwardMockup>(timeout));
688+
689+
// preempt before preempt_request_ is reset in plan()
690+
{
691+
std::thread thread{ [&ec, &t, timeout] {
692+
std::this_thread::sleep_for(timeout);
693+
ec = t.plan(1);
694+
} };
695+
t.preempt();
696+
thread.join();
697+
}
698+
699+
EXPECT_EQ(ec, moveit::core::MoveItErrorCode::PREEMPTED);
700+
EXPECT_EQ(t.solutions().size(), 0u);
701+
EXPECT_TRUE(t.plan(1)); // make sure the preempt request has been resetted on the previous call to plan()
702+
}

0 commit comments

Comments
 (0)