@@ -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 DurationT = std::chrono::milliseconds >
364364 FutureReturnCode
365- spin_until_future_complete (
366- const FutureT & future ,
367- std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT> (-1 ))
365+ spin_until_complete (
366+ const std::function< bool ( void )> condition ,
367+ DurationT timeout = DurationT (-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,44 @@ 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 gets passed to Executor::spin_node_once,
413+ * spins the executor for given duration.
414+ */
415+ template <typename DurationT>
416+ void
417+ spin_for (DurationT duration)
418+ {
419+ (void )spin_until_complete ([]() {return false ;}, duration);
420+ }
421+
422+ // / Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
423+ /* *
424+ * \param[in] future The future to wait on. If this function returns SUCCESS, the future can be
425+ * accessed without blocking (though it may still throw an exception).
426+ * \param[in] timeout Optional timeout parameter, which gets passed to Executor::spin_node_once.
427+ * `-1` is block forever, `0` is non-blocking.
428+ * If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return
429+ * code.
430+ * \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
431+ */
432+ template <typename FutureT, typename TimeRepT = int64_t , typename TimeT = std::milli>
433+ FutureReturnCode
434+ spin_until_future_complete (
435+ const FutureT & future,
436+ std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1 ))
437+ {
438+ const auto condition = [&future]() {
439+ return future.wait_for (std::chrono::seconds (0 )) == std::future_status::ready;
440+ };
441+ return spin_until_complete (condition, timeout);
442+ }
443+
417444 // / Cancel any running spin* function, causing it to return.
418445 /* *
419446 * This function can be called asynchonously from any thread.
0 commit comments