1use std::collections::HashMap;
2use std::sync::Arc;
3use std::time::Duration;
4use log::{info, warn};
5use shared_memory::Shmem;
6use tokio::sync::Mutex;
7use mechutil::shm::ShmContext;
8
9#[derive(Debug, Clone, Copy)]
11pub struct ShmPtr(pub usize);
12
13impl ShmPtr {
14 pub fn new<T>(ptr: *mut T) -> Self {
15 Self(ptr as usize)
16 }
17
18 pub fn as_ptr<T>(&self) -> *mut T {
19 self.0 as *mut T
20 }
21}
22
23pub struct ThreadSafeShmem(pub Shmem);
25
26unsafe impl Send for ThreadSafeShmem {}
27unsafe impl Sync for ThreadSafeShmem {}
28
29impl std::fmt::Debug for ThreadSafeShmem {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 f.debug_tuple("ThreadSafeShmem")
32 .field(&self.0.get_os_id())
33 .finish()
34 }
35}
36
37impl std::ops::Deref for ThreadSafeShmem {
38 type Target = Shmem;
39 fn deref(&self) -> &Self::Target {
40 &self.0
41 }
42}
43
44#[derive(Clone)]
46pub struct SendShmContext(pub Arc<Mutex<Option<Arc<ShmContext>>>>);
47
48unsafe impl Send for SendShmContext {}
49unsafe impl Sync for SendShmContext {}
50
51#[derive(Debug, Clone)]
60pub struct ShmMap {
61 pointers: Arc<HashMap<String, ShmPtr>>,
62 _context: Option<Arc<ShmContext>>,
63}
64
65impl ShmMap {
66 pub fn new(pointers: HashMap<String, ShmPtr>) -> Self {
71 Self {
72 pointers: Arc::new(pointers),
73 _context: None,
74 }
75 }
76
77 pub fn with_context(pointers: HashMap<String, ShmPtr>, context: Arc<ShmContext>) -> Self {
81 Self {
82 pointers: Arc::new(pointers),
83 _context: Some(context),
84 }
85 }
86
87 pub fn get<T>(&self, name: &str) -> Option<*mut T> {
88 self.pointers.get(name).map(|p| p.as_ptr())
89 }
90
91 pub fn get_raw(&self, name: &str) -> Option<usize> {
92 self.pointers.get(name).map(|p| p.0)
93 }
94
95 pub unsafe fn read<T: Copy>(&self, name: &str) -> Option<T> {
98 self.get::<T>(name).map(|ptr| *ptr)
99 }
100
101 pub unsafe fn write<T: Copy>(&self, name: &str, value: T) -> bool {
104 if let Some(ptr) = self.get::<T>(name) {
105 *ptr = value;
106 true
107 } else {
108 false
109 }
110 }
111
112 pub fn len(&self) -> usize {
114 self.pointers.len()
115 }
116}
117
118pub async fn resolve_shm_pointers(
124 ctx_handle: SendShmContext,
125 names: Vec<String>,
126 timeout_ms: u64
127) -> Option<ShmMap> {
128 tokio::task::spawn_blocking(move || {
129 let ctx = ctx_handle;
130 let shm_ctx = &ctx.0;
131 let mut attempts = 0;
132 let max_attempts = timeout_ms / 100;
133
134 loop {
135 if let Ok(guard) = shm_ctx.try_lock() {
137 if let Some(ref ctx) = *guard {
138 info!("SHM context received, resolving {} pointers...", names.len());
139 let mut pointers = HashMap::new();
140 let mut mapped = 0;
141
142 for name in &names {
143 if let Some(ptr) = unsafe { ctx.get_pointer(name) } {
144 pointers.insert(name.clone(), ShmPtr::new(ptr));
145 mapped += 1;
146 }
147 }
148 info!("SHM pointers resolved: {}/{}", mapped, names.len());
149 return Some(ShmMap::with_context(pointers, Arc::clone(ctx)));
153 }
154 }
155
156 attempts += 1;
157 if attempts > max_attempts {
158 warn!("Timed out waiting for SHM context after {}ms", timeout_ms);
159 return None;
160 }
161
162 std::thread::sleep(Duration::from_millis(100));
163 }
164 }).await.unwrap_or(None)
165}