@@ -350,32 +350,22 @@ class Executor
350
350
virtual void
351
351
spin_once (std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1 ));
352
352
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.
354
355
/* *
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.
357
357
* \param[in] timeout Optional timeout parameter, which gets passed to Executor::spin_node_once.
358
358
* `-1` is block forever, `0` is non-blocking.
359
359
* If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return
360
360
* code.
361
361
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
362
362
*/
363
- template <typename FutureT, typename TimeRepT = int64_t , typename TimeT = std::milli >
363
+ template <typename DurationT = std::chrono::milliseconds >
364
364
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 ))
368
368
{
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
-
379
369
auto end_time = std::chrono::steady_clock::now ();
380
370
std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
381
371
timeout);
@@ -384,17 +374,20 @@ class Executor
384
374
}
385
375
std::chrono::nanoseconds timeout_left = timeout_ns;
386
376
377
+ // Preliminary check, finish if condition is done already.
378
+ if (condition ()) {
379
+ return FutureReturnCode::SUCCESS;
380
+ }
381
+
387
382
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" );
389
384
}
390
385
RCPPUTILS_SCOPE_EXIT (this ->spinning .store (false ); );
391
386
while (rclcpp::ok (this ->context_ ) && spinning.load ()) {
392
387
// Do one item of work.
393
388
spin_once_impl (timeout_left);
394
389
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 ()) {
398
391
return FutureReturnCode::SUCCESS;
399
392
}
400
393
// If the original timeout is < 0, then this is blocking, never TIMEOUT.
@@ -410,10 +403,44 @@ class Executor
410
403
timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
411
404
}
412
405
413
- // The future did not complete before ok() returned false, return INTERRUPTED.
406
+ // The condition did not pass before ok() returned false, return INTERRUPTED.
414
407
return FutureReturnCode::INTERRUPTED;
415
408
}
416
409
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
+
417
444
// / Cancel any running spin* function, causing it to return.
418
445
/* *
419
446
* This function can be called asynchonously from any thread.
0 commit comments