Skip to content

Commit ed5cc98

Browse files
authored
Make context initialization and destruction more robust (#150)
1 parent 694dc85 commit ed5cc98

File tree

1 file changed

+39
-37
lines changed

1 file changed

+39
-37
lines changed

rclrs/src/context.rs

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,15 @@ use parking_lot::Mutex;
1111

1212
impl Drop for rcl_context_t {
1313
fn drop(&mut self) {
14-
// SAFETY: These functions have no preconditions besides a valid/initialized handle
1514
unsafe {
16-
rcl_shutdown(self);
17-
rcl_context_fini(self);
15+
// The context may be invalid when rcl_init failed, e.g. because of invalid command
16+
// line arguments.
17+
// SAFETY: No preconditions for this function.
18+
if rcl_context_is_valid(self) {
19+
// SAFETY: These functions have no preconditions besides a valid handle
20+
rcl_shutdown(self);
21+
rcl_context_fini(self);
22+
}
1823
}
1924
}
2025
}
@@ -54,47 +59,44 @@ impl Context {
5459
/// # Panics
5560
/// When there is an interior null byte in any of the args.
5661
pub fn new(args: impl IntoIterator<Item = String>) -> Result<Self, RclReturnCode> {
57-
let context = Self {
58-
// SAFETY: Getting a zero-initialized value is always safe
59-
handle: Arc::new(Mutex::new(unsafe { rcl_get_zero_initialized_context() })),
60-
};
62+
// SAFETY: Getting a zero-initialized value is always safe
63+
let mut rcl_context = unsafe { rcl_get_zero_initialized_context() };
6164
let cstring_args: Vec<CString> = args
6265
.into_iter()
6366
.map(|arg| CString::new(arg).unwrap())
6467
.collect();
6568
// Vector of pointers into cstring_args
6669
let c_args: Vec<*const c_char> = cstring_args.iter().map(|arg| arg.as_ptr()).collect();
67-
// Scope for the handle
68-
{
69-
let handle = &mut *context.handle.lock();
70-
unsafe {
71-
// SAFETY: No preconditions for this function.
72-
let allocator = rcutils_get_default_allocator();
73-
// SAFETY: Getting a zero-initialized value is always safe.
74-
let mut init_options = rcl_get_zero_initialized_init_options();
75-
// SAFETY: Passing in a zero-initialized value is expected.
76-
// In the case where this returns not ok, there's nothing to clean up.
77-
rcl_init_options_init(&mut init_options, allocator).ok()?;
78-
// SAFETY: This function does not store the ephemeral init_options and c_args
79-
// pointers. Passing in a zero-initialized handle is expected.
80-
let ret = rcl_init(
81-
c_args.len() as i32,
82-
if c_args.is_empty() {
83-
std::ptr::null()
84-
} else {
85-
c_args.as_ptr()
86-
},
87-
&init_options,
88-
handle,
89-
);
90-
// SAFETY: It's safe to pass in an initialized object.
91-
// Early return will not leak memory, because this is the last fini function.
92-
rcl_init_options_fini(&mut init_options).ok()?;
93-
// Move the check after the last fini()
94-
ret.ok()?;
95-
}
70+
unsafe {
71+
// SAFETY: No preconditions for this function.
72+
let allocator = rcutils_get_default_allocator();
73+
// SAFETY: Getting a zero-initialized value is always safe.
74+
let mut init_options = rcl_get_zero_initialized_init_options();
75+
// SAFETY: Passing in a zero-initialized value is expected.
76+
// In the case where this returns not ok, there's nothing to clean up.
77+
rcl_init_options_init(&mut init_options, allocator).ok()?;
78+
// SAFETY: This function does not store the ephemeral init_options and c_args
79+
// pointers. Passing in a zero-initialized handle is expected.
80+
let ret = rcl_init(
81+
c_args.len() as i32,
82+
if c_args.is_empty() {
83+
std::ptr::null()
84+
} else {
85+
c_args.as_ptr()
86+
},
87+
&init_options,
88+
&mut rcl_context,
89+
)
90+
.ok();
91+
// SAFETY: It's safe to pass in an initialized object.
92+
// Early return will not leak memory, because this is the last fini function.
93+
rcl_init_options_fini(&mut init_options).ok()?;
94+
// Move the check after the last fini()
95+
ret?;
9696
}
97-
Ok(context)
97+
Ok(Self {
98+
handle: Arc::new(Mutex::new(rcl_context)),
99+
})
98100
}
99101

100102
/// Creates a node.

0 commit comments

Comments
 (0)