Skip to content

Commit fd895b5

Browse files
committed
DRAFT: Add support for spin_for(timeout) and refactor spin_until_feature_complete (#1821)
Signed-off-by: Hubert Liberacki [email protected]
1 parent 49c2dd4 commit fd895b5

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed

rclcpp/include/rclcpp/executor.hpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <mutex>
2727
#include <string>
2828
#include <vector>
29+
#include <type_traits>
2930

3031
#include "rcl/guard_condition.h"
3132
#include "rcl/wait.h"
@@ -319,6 +320,53 @@ class Executor
319320
virtual void
320321
spin_once(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
321322

323+
// TODO(hliberacki): Add documentation
324+
template<typename FutureT, typename DurationT = std::chrono::milliseconds,
325+
// typename std::enable_if_t<
326+
// std::is_same_v<
327+
// std::remove_reference_t<FutureT>,
328+
// std::future<
329+
// decltype(std::declval<std::remove_reference_t<FutureT>>()
330+
// .get())>> ||
331+
// std::is_same_v<
332+
// std::remove_reference_t<FutureT>,
333+
// std::shared_future<
334+
// decltype(std::declval<std::remove_reference_t<FutureT>>()
335+
// .get())>>,
336+
// bool> = true>
337+
typename std::enable_if_t<!std::is_invocable_v<FutureT>, bool> = true>
338+
FutureReturnCode spin_until_complete(FutureT & future, DurationT timeout = DurationT(-1))
339+
{
340+
// call helper wait untill complete with lambda
341+
auto checkFuture = [&future]() {
342+
return future.wait_for(std::chrono::seconds(0)) ==
343+
std::future_status::ready;
344+
};
345+
return spin_until_complete_impl(checkFuture, timeout);
346+
}
347+
348+
template<
349+
typename Condition, typename DurationT = std::chrono::milliseconds,
350+
typename std::enable_if_t<std::is_invocable_v<Condition>, bool> = true>
351+
FutureReturnCode spin_until_complete(
352+
Condition & condition,
353+
DurationT timeout = DurationT(-1))
354+
{
355+
using RetT = decltype(std::declval<std::remove_reference_t<Condition>>()());
356+
static_assert(
357+
std::is_same_v<bool, RetT>,
358+
"Conditional callable has to return boolean type");
359+
return spin_until_complete_impl(condition, timeout);
360+
}
361+
// TODO(hliberacki): Add documentation
362+
/// spin (blocking) for given amount of time
363+
template<typename DurationT>
364+
void
365+
spin_for(DurationT timeout)
366+
{
367+
return spin_until_complete([]() {return false;}, timeout);
368+
}
369+
322370
/// Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
323371
/**
324372
* \param[in] future The future to wait on. If this function returns SUCCESS, the future can be
@@ -560,6 +608,46 @@ class Executor
560608
virtual void
561609
spin_once_impl(std::chrono::nanoseconds timeout);
562610

611+
private:
612+
template<typename Condition, typename DurationT>
613+
FutureReturnCode spin_until_complete_impl(Condition condition, DurationT timeout)
614+
{
615+
auto end_time = std::chrono::steady_clock::now();
616+
std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
617+
timeout);
618+
if (timeout_ns > std::chrono::nanoseconds::zero()) {
619+
end_time += timeout_ns;
620+
}
621+
std::chrono::nanoseconds timeout_left = timeout_ns;
622+
if (spinning.exchange(true)) {
623+
throw std::runtime_error("spin_until_complete() called while already spinning");
624+
}
625+
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
626+
while (rclcpp::ok(this->context_) && spinning.load()) {
627+
// Do one item of work.
628+
spin_once_impl(timeout_left);
629+
630+
if (condition()) {
631+
return FutureReturnCode::SUCCESS;
632+
}
633+
// If the original timeout is < 0, then this is blocking, never TIMEOUT.
634+
if (timeout_ns < std::chrono::nanoseconds::zero()) {
635+
continue;
636+
}
637+
// Otherwise check if we still have time to wait, return TIMEOUT if not.
638+
auto now = std::chrono::steady_clock::now();
639+
if (now >= end_time) {
640+
return FutureReturnCode::TIMEOUT;
641+
}
642+
// Subtract the elapsed time from the original timeout.
643+
timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
644+
}
645+
646+
// The condition did not pass before ok() returned false, return INTERRUPTED.
647+
return FutureReturnCode::INTERRUPTED;
648+
}
649+
650+
public:
563651
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
564652
const rclcpp::GuardCondition *,
565653
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>

0 commit comments

Comments
 (0)