|
26 | 26 | #include <mutex> |
27 | 27 | #include <string> |
28 | 28 | #include <vector> |
| 29 | +#include <type_traits> |
29 | 30 |
|
30 | 31 | #include "rcl/guard_condition.h" |
31 | 32 | #include "rcl/wait.h" |
@@ -319,6 +320,53 @@ class Executor |
319 | 320 | virtual void |
320 | 321 | spin_once(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1)); |
321 | 322 |
|
| 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 | + |
322 | 370 | /// Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted. |
323 | 371 | /** |
324 | 372 | * \param[in] future The future to wait on. If this function returns SUCCESS, the future can be |
@@ -560,6 +608,46 @@ class Executor |
560 | 608 | virtual void |
561 | 609 | spin_once_impl(std::chrono::nanoseconds timeout); |
562 | 610 |
|
| 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: |
563 | 651 | typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr, |
564 | 652 | const rclcpp::GuardCondition *, |
565 | 653 | std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>> |
|
0 commit comments