|
| 1 | +use crate::error::ToResult; |
1 | 2 | use crate::rcl_bindings::*;
|
2 | 3 | use crate::{Context, RclrsError};
|
3 | 4 | use std::ffi::CString;
|
4 |
| -use std::os::raw::c_char; |
| 5 | +use std::sync::Arc; |
| 6 | + |
5 | 7 | use parking_lot::Mutex;
|
| 8 | +use std::os::raw::c_char; |
6 | 9 |
|
7 | 10 | pub struct ContextBuilder {
|
8 | 11 | cstring_args: Vec<CString>,
|
9 |
| - c_args: Option<Vec<*const c_char>>, |
10 |
| - context_mtx: Mutex<rcl_context_t>, |
11 |
| - allocator: rcl_allocator_t, |
12 | 12 | init_options_mtx: Mutex<rcl_init_options_t>,
|
13 | 13 | }
|
14 | 14 |
|
15 | 15 | impl ContextBuilder {
|
16 |
| - |
17 | 16 | /// Build a new ContextBuilder instance
|
18 | 17 | pub fn new(args: impl IntoIterator<Item = String>) -> Result<ContextBuilder, RclrsError> {
|
19 |
| - unsafe { |
20 |
| - Ok(ContextBuilder { |
21 |
| - cstring_args: args.into_iter().map(|arg| { |
22 |
| - CString::new(arg.as_str()).map_err(|err| RclrsError::StringContainsNul{ |
| 18 | + Ok(ContextBuilder { |
| 19 | + cstring_args: args |
| 20 | + .into_iter() |
| 21 | + .map(|arg| { |
| 22 | + CString::new(arg.as_str()).map_err(|err| RclrsError::StringContainsNul { |
23 | 23 | err,
|
24 | 24 | s: arg.clone(),
|
25 | 25 | })
|
26 |
| - }).collect::<Result<_, _>>()?, |
27 |
| - c_args: None, // to be built in the build function |
28 |
| - context_mtx: Mutex::new(rcl_get_zero_initialized_context()), |
29 |
| - allocator: rcutils_get_default_allocator(), |
30 |
| - init_options_mtx: Mutex::new(rcl_get_zero_initialized_init_options()) |
31 |
| - }) |
32 |
| - } |
| 26 | + }) |
| 27 | + .collect::<Result<_, _>>()?, |
| 28 | + // SAFETY: Getting a zero-initialized value is always safe. |
| 29 | + init_options_mtx: unsafe { Mutex::new(rcl_get_zero_initialized_init_options()) }, |
| 30 | + }) |
33 | 31 | }
|
34 | 32 |
|
| 33 | + /// Function to build the Context instance |
35 | 34 | pub fn build(&self) -> Result<Context, RclrsError> {
|
36 |
| - todo!("call build here"); |
| 35 | + let mut rcl_init_options = self.init_options_mtx.lock(); |
| 36 | + |
| 37 | + let c_args: Vec<*const c_char> = self.cstring_args.iter().map(|arg| arg.as_ptr()).collect(); |
| 38 | + unsafe { |
| 39 | + // SAFETY: Getting a zero-initialized value is always safe |
| 40 | + let mut rcl_context: rcl_context_t = rcl_get_zero_initialized_context(); |
| 41 | + // SAFETY: No preconditions for this function. |
| 42 | + let allocator: rcutils_allocator_t = rcutils_get_default_allocator(); |
| 43 | + |
| 44 | + // SAFETY: Passing in a zero-initialized value is expected. |
| 45 | + // In the case where this returns not ok, there's nothing to clean up. |
| 46 | + rcl_init_options_init(&mut *rcl_init_options, allocator).ok()?; |
| 47 | + // SAFETY: This function does not store the ephemeral init_options and c_args |
| 48 | + // pointers. Passing in a zero-initialized rcl_context is expected. |
| 49 | + |
| 50 | + let ret = rcl_init( |
| 51 | + c_args.len() as i32, |
| 52 | + if c_args.is_empty() { |
| 53 | + std::ptr::null() |
| 54 | + } else { |
| 55 | + c_args.as_ptr() |
| 56 | + }, |
| 57 | + &*rcl_init_options, |
| 58 | + &mut rcl_context, |
| 59 | + ) |
| 60 | + .ok(); |
| 61 | + // SAFETY: It's safe to pass in an initialized object. |
| 62 | + // Early return will not leak memory, because this is the last fini function. |
| 63 | + rcl_init_options_fini(&mut *rcl_init_options).ok()?; |
| 64 | + // Move the check after the last fini() |
| 65 | + ret?; |
| 66 | + Ok(Context { |
| 67 | + rcl_context_mtx: Arc::new(Mutex::new(rcl_context)), |
| 68 | + }) |
| 69 | + } |
37 | 70 | }
|
38 | 71 | }
|
0 commit comments