Skip to content

Commit 10b2bcb

Browse files
committed
Keep context alive for guard conditions
Signed-off-by: Michael X. Grey <[email protected]>
1 parent d3df842 commit 10b2bcb

File tree

3 files changed

+45
-29
lines changed

3 files changed

+45
-29
lines changed

rclrs/src/node.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,8 @@ impl Node {
211211
/// [1]: crate::GuardCondition
212212
/// [2]: crate::spin_once
213213
pub fn create_guard_condition(&self) -> Arc<GuardCondition> {
214-
let guard_condition = Arc::new(GuardCondition::new_with_rcl_context(
215-
&mut self.handle.context_handle.rcl_context.lock().unwrap(),
214+
let guard_condition = Arc::new(GuardCondition::new_with_context_handle(
215+
Arc::clone(&self.handle.context_handle),
216216
None,
217217
));
218218
{ self.guard_conditions_mtx.lock().unwrap() }
@@ -233,8 +233,8 @@ impl Node {
233233
where
234234
F: Fn() + Send + Sync + 'static,
235235
{
236-
let guard_condition = Arc::new(GuardCondition::new_with_rcl_context(
237-
&mut self.handle.context_handle.rcl_context.lock().unwrap(),
236+
let guard_condition = Arc::new(GuardCondition::new_with_context_handle(
237+
Arc::clone(&self.handle.context_handle),
238238
Some(Box::new(callback) as Box<dyn Fn() + Send + Sync>),
239239
));
240240
{ self.guard_conditions_mtx.lock().unwrap() }

rclrs/src/wait.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ pub use guard_condition::*;
3131
struct WaitSetHandle {
3232
rcl_wait_set: rcl_wait_set_t,
3333
// Used to ensure the context is alive while the wait set is alive.
34-
_rcl_context_mtx: Arc<ContextHandle>,
34+
#[allow(dead_code)]
35+
context_handle: Arc<ContextHandle>,
3536
}
3637

3738
/// A struct for waiting on subscriptions and other waitable entities to become ready.
@@ -118,7 +119,7 @@ impl WaitSet {
118119
services: Vec::new(),
119120
handle: WaitSetHandle {
120121
rcl_wait_set,
121-
_rcl_context_mtx: Arc::clone(&context.handle),
122+
context_handle: Arc::clone(&context.handle),
122123
},
123124
})
124125
}
@@ -235,7 +236,7 @@ impl WaitSet {
235236
// SAFETY: Safe if the wait set and guard condition are initialized
236237
rcl_wait_set_add_guard_condition(
237238
&mut self.handle.rcl_wait_set,
238-
&*guard_condition.rcl_guard_condition.lock().unwrap(),
239+
&*guard_condition.handle.rcl_guard_condition.lock().unwrap(),
239240
std::ptr::null_mut(),
240241
)
241242
.ok()?;

rclrs/src/wait/guard_condition.rs

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::sync::{atomic::AtomicBool, Arc, Mutex};
22

33
use crate::rcl_bindings::*;
4-
use crate::{Context, RclrsError, ToResult};
4+
use crate::{Context, ContextHandle, RclrsError, ToResult};
55

66
/// A waitable entity used for waking up a wait set manually.
77
///
@@ -45,18 +45,25 @@ use crate::{Context, RclrsError, ToResult};
4545
/// ```
4646
pub struct GuardCondition {
4747
/// The rcl_guard_condition_t that this struct encapsulates.
48-
pub(crate) rcl_guard_condition: Mutex<rcl_guard_condition_t>,
48+
pub(crate) handle: GuardConditionHandle,
4949
/// An optional callback to call when this guard condition is triggered.
5050
callback: Option<Box<dyn Fn() + Send + Sync>>,
5151
/// A flag to indicate if this guard condition has already been assigned to a wait set.
5252
pub(crate) in_use_by_wait_set: Arc<AtomicBool>,
5353
}
5454

55+
pub(crate) struct GuardConditionHandle {
56+
pub(crate) rcl_guard_condition: Mutex<rcl_guard_condition_t>,
57+
/// Keep the context alive for the whole lifecycle of the guard condition
58+
#[allow(dead_code)]
59+
pub(crate) context_handle: Arc<ContextHandle>,
60+
}
61+
5562
impl Drop for GuardCondition {
5663
fn drop(&mut self) {
5764
unsafe {
5865
// SAFETY: No precondition for this function (besides passing in a valid guard condition)
59-
rcl_guard_condition_fini(&mut *self.rcl_guard_condition.lock().unwrap());
66+
rcl_guard_condition_fini(&mut *self.handle.rcl_guard_condition.lock().unwrap());
6067
}
6168
}
6269
}
@@ -66,8 +73,8 @@ impl PartialEq for GuardCondition {
6673
// Because GuardCondition controls the creation of the rcl_guard_condition, each unique GuardCondition should have a unique
6774
// rcl_guard_condition. Thus comparing equality of this member should be enough.
6875
std::ptr::eq(
69-
&self.rcl_guard_condition.lock().unwrap().impl_,
70-
&other.rcl_guard_condition.lock().unwrap().impl_,
76+
&self.handle.rcl_guard_condition.lock().unwrap().impl_,
77+
&other.handle.rcl_guard_condition.lock().unwrap().impl_,
7178
)
7279
}
7380
}
@@ -80,16 +87,16 @@ unsafe impl Send for rcl_guard_condition_t {}
8087
impl GuardCondition {
8188
/// Creates a new guard condition with no callback.
8289
pub fn new(context: &Context) -> Self {
83-
Self::new_with_rcl_context(&mut context.handle.rcl_context.lock().unwrap(), None)
90+
Self::new_with_context_handle(Arc::clone(&context.handle), None)
8491
}
8592

8693
/// Creates a new guard condition with a callback.
8794
pub fn new_with_callback<F>(context: &Context, callback: F) -> Self
8895
where
8996
F: Fn() + Send + Sync + 'static,
9097
{
91-
Self::new_with_rcl_context(
92-
&mut context.handle.rcl_context.lock().unwrap(),
98+
Self::new_with_context_handle(
99+
Arc::clone(&context.handle),
93100
Some(Box::new(callback) as Box<dyn Fn() + Send + Sync>),
94101
)
95102
}
@@ -98,23 +105,31 @@ impl GuardCondition {
98105
/// Note this function enables calling `Node::create_guard_condition`[1] without providing the Context separately
99106
///
100107
/// [1]: Node::create_guard_condition
101-
pub(crate) fn new_with_rcl_context(
102-
context: &mut rcl_context_t,
108+
pub(crate) fn new_with_context_handle(
109+
context_handle: Arc<ContextHandle>,
103110
callback: Option<Box<dyn Fn() + Send + Sync>>,
104111
) -> Self {
105-
// SAFETY: Getting a zero initialized value is always safe
106-
let mut guard_condition = unsafe { rcl_get_zero_initialized_guard_condition() };
107-
unsafe {
108-
// SAFETY: The context must be valid, and the guard condition must be zero-initialized
109-
rcl_guard_condition_init(
110-
&mut guard_condition,
111-
context,
112-
rcl_guard_condition_get_default_options(),
113-
);
114-
}
112+
let rcl_guard_condition = {
113+
let mut rcl_context = context_handle.rcl_context.lock().unwrap();
114+
// SAFETY: Getting a zero initialized value is always safe
115+
let mut guard_condition = unsafe { rcl_get_zero_initialized_guard_condition() };
116+
unsafe {
117+
// SAFETY: The context must be valid, and the guard condition must be zero-initialized
118+
rcl_guard_condition_init(
119+
&mut guard_condition,
120+
&mut *rcl_context,
121+
rcl_guard_condition_get_default_options(),
122+
);
123+
}
124+
125+
Mutex::new(guard_condition)
126+
};
115127

116128
Self {
117-
rcl_guard_condition: Mutex::new(guard_condition),
129+
handle: GuardConditionHandle {
130+
rcl_guard_condition,
131+
context_handle,
132+
},
118133
callback,
119134
in_use_by_wait_set: Arc::new(AtomicBool::new(false)),
120135
}
@@ -124,7 +139,7 @@ impl GuardCondition {
124139
pub fn trigger(&self) -> Result<(), RclrsError> {
125140
unsafe {
126141
// SAFETY: The rcl_guard_condition_t is valid.
127-
rcl_trigger_guard_condition(&mut *self.rcl_guard_condition.lock().unwrap()).ok()?;
142+
rcl_trigger_guard_condition(&mut *self.handle.rcl_guard_condition.lock().unwrap()).ok()?;
128143
}
129144
if let Some(callback) = &self.callback {
130145
callback();

0 commit comments

Comments
 (0)