Skip to content

Commit b465f6f

Browse files
committed
Return a Vec of errors from spinning
Signed-off-by: Michael X. Grey <[email protected]>
1 parent 0918476 commit b465f6f

File tree

3 files changed

+96
-14
lines changed

3 files changed

+96
-14
lines changed

rclrs/src/error.rs

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,33 @@ pub enum RclrsError {
3434
AlreadyAddedToWaitSet,
3535
}
3636

37+
impl RclrsError {
38+
/// Returns true if the error was due to a timeout, otherwise returns false.
39+
pub fn is_timeout(&self) -> bool {
40+
matches!(
41+
self,
42+
RclrsError::RclError {
43+
code: RclReturnCode::Timeout,
44+
..
45+
}
46+
)
47+
}
48+
49+
/// Returns true if the error was because a subscription, service, or client
50+
/// take failed, otherwise returns false.
51+
pub fn is_take_failed(&self) -> bool {
52+
matches!(
53+
self,
54+
RclrsError::RclError {
55+
code: RclReturnCode::SubscriptionTakeFailed
56+
| RclReturnCode::ServiceTakeFailed
57+
| RclReturnCode::ClientTakeFailed,
58+
..
59+
}
60+
)
61+
}
62+
}
63+
3764
impl Display for RclrsError {
3865
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3966
match self {
@@ -355,27 +382,77 @@ impl ToResult for rcl_ret_t {
355382

356383
/// A helper trait to disregard timeouts as not an error.
357384
pub trait RclrsErrorFilter {
385+
/// Get the first error available, or Ok(()) if there are no errors.
386+
fn first_error(self) -> Result<(), RclrsError>;
387+
358388
/// If the result was a timeout error, change it to `Ok(())`.
359-
fn timeout_ok(self) -> Result<(), RclrsError>;
389+
fn timeout_ok(self) -> Self;
390+
391+
/// If a subscription, service, or client take failed, change the result
392+
/// to be `Ok(())`.
393+
fn take_failed_ok(self) -> Self;
394+
395+
/// Some error types just indicate an early termination but do not indicate
396+
/// that anything in the system has misbehaved. This filters out anything
397+
/// that is part of the normal operation of rcl.
398+
fn ignore_non_errors(self) -> Self
399+
where
400+
Self: Sized,
401+
{
402+
self.timeout_ok().take_failed_ok()
403+
}
360404
}
361405

362406
impl RclrsErrorFilter for Result<(), RclrsError> {
407+
fn first_error(self) -> Result<(), RclrsError> {
408+
self
409+
}
410+
363411
fn timeout_ok(self) -> Result<(), RclrsError> {
364412
match self {
365413
Ok(()) => Ok(()),
366414
Err(err) => {
367-
if matches!(
368-
err,
369-
RclrsError::RclError {
370-
code: RclReturnCode::Timeout,
371-
..
372-
}
373-
) {
374-
return Ok(());
415+
if err.is_timeout() {
416+
Ok(())
417+
} else {
418+
Err(err)
375419
}
420+
}
421+
}
422+
}
376423

377-
Err(err)
424+
fn take_failed_ok(self) -> Result<(), RclrsError> {
425+
match self {
426+
Err(err) => {
427+
if err.is_take_failed() {
428+
// Spurious wakeup - this may happen even when a waitset indicated that
429+
// work was ready, so we won't report it as an error
430+
Ok(())
431+
} else {
432+
Err(err)
433+
}
378434
}
435+
other => other,
436+
}
437+
}
438+
}
439+
440+
impl RclrsErrorFilter for Vec<RclrsError> {
441+
fn first_error(mut self) -> Result<(), RclrsError> {
442+
if self.is_empty() {
443+
return Ok(());
379444
}
445+
446+
Err(self.remove(0))
447+
}
448+
449+
fn timeout_ok(mut self) -> Self {
450+
self.retain(|err| !err.is_timeout());
451+
self
452+
}
453+
454+
fn take_failed_ok(mut self) -> Self {
455+
self.retain(|err| !err.is_take_failed());
456+
self
380457
}
381458
}

rclrs/src/executor.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,20 @@ impl Executor {
3030
/// [`SpinOptions`] can be used to automatically stop the spinning when
3131
/// certain conditions are met. Use `SpinOptions::default()` to allow the
3232
/// Executor to keep spinning indefinitely.
33-
pub fn spin(&mut self, options: SpinOptions) -> Result<(), RclrsError> {
33+
pub fn spin(&mut self, options: SpinOptions) -> Vec<RclrsError> {
3434
loop {
3535
if self.nodes_mtx.lock().unwrap().is_empty() {
3636
// Nothing to spin for, so just quit here
37-
return Ok(());
37+
return Vec::new();
3838
}
3939

40-
self.spin_once(options.timeout)?;
40+
if let Err(err) = self.spin_once(options.timeout) {
41+
return vec![err];
42+
}
4143

4244
if options.only_next_available_work {
4345
// We were only suppposed to spin once, so quit here
44-
return Ok(());
46+
return Vec::new();
4547
}
4648

4749
std::thread::yield_now();

rclrs/src/parameter/service.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ mod tests {
446446
executor
447447
.spin(SpinOptions::spin_once().timeout(Duration::ZERO))
448448
.timeout_ok()
449+
.first_error()
449450
.unwrap();
450451

451452
*inner_done.read().unwrap()
@@ -589,6 +590,7 @@ mod tests {
589590
executor
590591
.spin(SpinOptions::spin_once().timeout(Duration::ZERO))
591592
.timeout_ok()
593+
.first_error()
592594
.unwrap();
593595

594596
*inner_done.read().unwrap()
@@ -825,6 +827,7 @@ mod tests {
825827
executor
826828
.spin(SpinOptions::spin_once().timeout(Duration::ZERO))
827829
.timeout_ok()
830+
.first_error()
828831
.unwrap();
829832

830833
*inner_done.read().unwrap()

0 commit comments

Comments
 (0)