1use std::{
2 ffi::{CStr, CString},
3 fmt::Debug,
4 ops::{Deref, DerefMut},
5 sync::{Arc, Mutex, OnceLock},
6};
7
8use crate::{error::*, log_guard};
9use r2r_rcl::*;
10
11#[derive(Debug, Clone)]
13pub struct Context {
14 pub(crate) context_handle: Arc<Mutex<ContextHandle>>,
15}
16
17macro_rules! check_rcl_ret {
18 ($ret:expr) => {
19 if $ret != RCL_RET_OK as i32 {
20 let err_str = rcutils_get_error_string();
21 let str_ptr = &(err_str.str_) as *const std::os::raw::c_char;
24 let error_msg = CStr::from_ptr(str_ptr);
25 panic!("{}", error_msg.to_str().expect("to_str() call failed"));
26 }
27 };
28}
29
30unsafe impl Send for Context {}
31
32unsafe impl Sync for Context {}
35
36static CONTEXT: OnceLock<Result<Context>> = OnceLock::new();
43
44impl Context {
45 pub fn create() -> Result<Context> {
47 CONTEXT
48 .get_or_init(|| {
49 let mut ctx: Box<rcl_context_t> =
50 unsafe { Box::new(rcl_get_zero_initialized_context()) };
51 let args = std::env::args()
53 .map(|arg| CString::new(arg).unwrap())
54 .collect::<Vec<CString>>();
55 let mut c_args = args
56 .iter()
57 .map(|arg| arg.as_ptr())
58 .collect::<Vec<*const ::std::os::raw::c_char>>();
59 c_args.push(std::ptr::null());
60
61 let is_valid = unsafe {
62 let allocator = rcutils_get_default_allocator();
63 let mut init_options = rcl_get_zero_initialized_init_options();
64 check_rcl_ret!(rcl_init_options_init(&mut init_options, allocator));
65 check_rcl_ret!(rcl_init(
66 (c_args.len() - 1) as ::std::os::raw::c_int,
67 c_args.as_ptr(),
68 &init_options,
69 ctx.as_mut(),
70 ));
71 check_rcl_ret!(rcl_init_options_fini(&mut init_options as *mut _));
72 rcl_context_is_valid(ctx.as_mut())
73 };
74
75 let logging_ok = unsafe {
76 let _guard = log_guard();
77 let ret = rcl_logging_configure(
78 &ctx.as_ref().global_arguments,
79 &rcutils_get_default_allocator(),
80 );
81 ret == RCL_RET_OK as i32
82 };
83
84 if is_valid && logging_ok {
85 Ok(Context {
86 context_handle: Arc::new(Mutex::new(ContextHandle(ctx))),
87 })
88 } else {
89 Err(Error::RCL_RET_ERROR) }
91 })
92 .clone()
93 }
94
95 pub fn is_valid(&self) -> bool {
99 let mut ctx = self.context_handle.lock().unwrap();
100 unsafe { rcl_context_is_valid(ctx.as_mut()) }
101 }
102}
103
104#[derive(Debug)]
105pub struct ContextHandle(Box<rcl_context_t>);
106
107impl Deref for ContextHandle {
108 type Target = Box<rcl_context_t>;
109
110 fn deref(&self) -> &Box<rcl_context_t> {
111 &self.0
112 }
113}
114
115impl DerefMut for ContextHandle {
116 fn deref_mut(&mut self) -> &mut Box<rcl_context_t> {
117 &mut self.0
118 }
119}
120
121impl Drop for ContextHandle {
122 fn drop(&mut self) {
123 unsafe {
125 rcl_shutdown(self.0.as_mut());
126 rcl_context_fini(self.0.as_mut());
127 }
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_context_drop() {
137 {
138 let ctx = Context::create().unwrap();
139 assert!(ctx.is_valid());
140 }
141 {
142 let ctx = Context::create().unwrap();
143 assert!(ctx.is_valid());
144 }
145 }
146}