@@ -350,32 +350,22 @@ class Executor
350350 virtual void
351351 spin_once (std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1 ));
352352
353- // / Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
353+ // / Spin (blocking) until the condition is complete, it times out waiting, or rclcpp is
354+ // / interrupted.
354355 /* *
355- * \param[in] future The future to wait on. If this function returns SUCCESS, the future can be
356- * accessed without blocking (though it may still throw an exception).
356+ * \param[in] condition The callable condition to wait on.
357357 * \param[in] timeout Optional timeout parameter, which gets passed to Executor::spin_node_once.
358358 * `-1` is block forever, `0` is non-blocking.
359359 * If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return
360360 * code.
361361 * \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
362362 */
363- template <typename FutureT, typename TimeRepT = int64_t , typename TimeT = std::milli>
363+ template <typename TimeRepT = int64_t , typename TimeT = std::milli>
364364 FutureReturnCode
365- spin_until_future_complete (
366- const FutureT & future ,
365+ spin_until_complete (
366+ const std::function< bool ( void )> & condition ,
367367 std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1 ))
368368 {
369- // TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
370- // inside a callback executed by an executor.
371-
372- // Check the future before entering the while loop.
373- // If the future is already complete, don't try to spin.
374- std::future_status status = future.wait_for (std::chrono::seconds (0 ));
375- if (status == std::future_status::ready) {
376- return FutureReturnCode::SUCCESS ;
377- }
378-
379369 auto end_time = std::chrono::steady_clock::now ();
380370 std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
381371 timeout);
@@ -384,17 +374,20 @@ class Executor
384374 }
385375 std::chrono::nanoseconds timeout_left = timeout_ns;
386376
377+ // Preliminary check, finish if condition is done already.
378+ if (condition ()) {
379+ return FutureReturnCode::SUCCESS ;
380+ }
381+
387382 if (spinning.exchange (true )) {
388- throw std::runtime_error (" spin_until_future_complete () called while already spinning" );
383+ throw std::runtime_error (" spin_until_complete () called while already spinning" );
389384 }
390385 RCPPUTILS_SCOPE_EXIT (this ->spinning .store (false ); );
391386 while (rclcpp::ok (this ->context_ ) && spinning.load ()) {
392387 // Do one item of work.
393388 spin_once_impl (timeout_left);
394389
395- // Check if the future is set, return SUCCESS if it is.
396- status = future.wait_for (std::chrono::seconds (0 ));
397- if (status == std::future_status::ready) {
390+ if (condition ()) {
398391 return FutureReturnCode::SUCCESS ;
399392 }
400393 // If the original timeout is < 0, then this is blocking, never TIMEOUT.
@@ -410,10 +403,43 @@ class Executor
410403 timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
411404 }
412405
413- // The future did not complete before ok() returned false, return INTERRUPTED.
406+ // The condition did not pass before ok() returned false, return INTERRUPTED.
414407 return FutureReturnCode::INTERRUPTED ;
415408 }
416409
410+ // / Spin (blocking) for at least the given amount of duration.
411+ /* *
412+ * \param[in] duration How long to spin for, which gets passed to Executor::spin_node_once.
413+ */
414+ template <typename TimeRepT = int64_t , typename TimeT = std::milli>
415+ void
416+ spin_for (std::chrono::duration<TimeRepT, TimeT> timeout duration)
417+ {
418+ (void )spin_until_complete ([]() {return false ;}, duration);
419+ }
420+
421+ // / Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
422+ /* *
423+ * \param[in] future The future to wait on. If this function returns SUCCESS, the future can be
424+ * accessed without blocking (though it may still throw an exception).
425+ * \param[in] timeout Optional timeout parameter, which gets passed to Executor::spin_node_once.
426+ * `-1` is block forever, `0` is non-blocking.
427+ * If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return
428+ * code.
429+ * \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
430+ */
431+ template <typename FutureT, typename TimeRepT = int64_t , typename TimeT = std::milli>
432+ FutureReturnCode
433+ spin_until_future_complete (
434+ const FutureT & future,
435+ std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1 ))
436+ {
437+ const auto condition = [&future]() {
438+ return future.wait_for (std::chrono::seconds (0 )) == std::future_status::ready;
439+ };
440+ return spin_until_complete (condition, timeout);
441+ }
442+
417443 // / Cancel any running spin* function, causing it to return.
418444 /* *
419445 * This function can be called asynchonously from any thread.
0 commit comments