1#![allow(improper_ctypes_definitions)]
2#![allow(non_upper_case_globals)]
3#![allow(non_camel_case_types)]
4#![allow(non_snake_case)]
5#![allow(clippy::all)]
6#![allow(unused_unsafe)]
7#![allow(unused_variables)]
8#![allow(unsafe_op_in_unsafe_fn)]
9#![doc = include_str!("../README.md")]
10#[allow(improper_ctypes_definitions)]
20#[allow(unpredictable_function_pointer_comparisons)]
21pub mod bindings {
22 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
23}
24
25use bindings::*;
26use log::info;
27use std::path::Path;
28use std::sync::Arc;
29use std::sync::atomic::{AtomicBool, Ordering};
30use std::thread::{JoinHandle, sleep};
31use std::time::Duration;
32
33include!(concat!(env!("OUT_DIR"), "/aeron.rs"));
34include!(concat!(env!("OUT_DIR"), "/aeron_custom.rs"));
35
36unsafe impl Sync for AeronDriverContext {}
37unsafe impl Send for AeronDriverContext {}
38unsafe impl Sync for AeronDriver {}
39unsafe impl Send for AeronDriver {}
40
41pub struct EmbeddedMediaDriver {
45 stop: Option<Arc<AtomicBool>>,
46 handle: Option<JoinHandle<Result<(), AeronCError>>>,
47}
48
49impl EmbeddedMediaDriver {
50 pub fn stop(&self) {
53 if let Some(stop) = &self.stop {
54 stop.store(true, Ordering::SeqCst);
55 }
56 }
57
58 pub fn is_finished(&self) -> bool {
60 self.handle.as_ref().map_or(true, |h| h.is_finished())
61 }
62
63 pub fn join(mut self) -> Result<(), AeronCError> {
65 self.stop();
66 if let Some(h) = self.handle.take() {
67 return h.join().map_err(|_| AeronCError::from_code(-1)).and_then(|r| r);
70 }
71 Ok(())
72 }
73}
74
75impl Drop for EmbeddedMediaDriver {
76 fn drop(&mut self) {
77 if let Some(stop) = &self.stop {
78 stop.store(true, Ordering::SeqCst);
79 }
80 if let Some(h) = self.handle.take() {
81 let _ = h.join();
82 }
83 }
84}
85
86pub mod testing;
87
88impl AeronDriverContext {
89 pub fn set_conductor_idle_strategy_kind(&self, kind: AeronIdleStrategyKind) -> Result<i32, AeronCError> {
91 self.set_conductor_idle_strategy(kind.name_c())
92 }
93
94 pub fn set_sender_idle_strategy_kind(&self, kind: AeronIdleStrategyKind) -> Result<i32, AeronCError> {
96 self.set_sender_idle_strategy(kind.name_c())
97 }
98
99 pub fn set_receiver_idle_strategy_kind(&self, kind: AeronIdleStrategyKind) -> Result<i32, AeronCError> {
101 self.set_receiver_idle_strategy(kind.name_c())
102 }
103
104 pub fn set_sharednetwork_idle_strategy_kind(&self, kind: AeronIdleStrategyKind) -> Result<i32, AeronCError> {
106 self.set_sharednetwork_idle_strategy(kind.name_c())
107 }
108
109 pub fn set_shared_idle_strategy_kind(&self, kind: AeronIdleStrategyKind) -> Result<i32, AeronCError> {
111 self.set_shared_idle_strategy(kind.name_c())
112 }
113}
114
115impl AeronDriver {
116 pub fn launch_embedded(
117 aeron_context: AeronDriverContext,
118 register_sigint: bool,
119 ) -> (Arc<AtomicBool>, JoinHandle<Result<(), AeronCError>>) {
120 AeronDriver::wait_for_previous_media_driver_to_timeout(&aeron_context);
121
122 let stop = Arc::new(AtomicBool::new(false));
123 let stop_copy = stop.clone();
124 if register_sigint {
126 let stop_copy2 = stop.clone();
127 ctrlc::set_handler(move || {
128 stop_copy2.store(true, Ordering::SeqCst);
129 })
130 .expect("Error setting Ctrl-C handler");
131 }
132
133 let started = Arc::new(AtomicBool::new(false));
134 let started2 = started.clone();
135
136 let dir = aeron_context.get_dir().to_string();
137 info!("Starting media driver [dir={}]", dir);
138 let handle = std::thread::spawn(move || {
139 let aeron_context = aeron_context.clone();
140 let aeron_driver = AeronDriver::new(&aeron_context)?;
141 aeron_driver.start(true)?;
142
143 info!("Aeron driver started [dir={}]", aeron_driver.context().get_dir());
144
145 started2.store(true, Ordering::SeqCst);
146
147 while !stop.load(Ordering::Acquire) {
149 aeron_driver.main_idle_strategy(aeron_driver.main_do_work()?);
150 }
151
152 info!("stopping media driver");
153
154 Ok::<_, AeronCError>(())
155 });
156
157 while !started.load(Ordering::SeqCst) && !handle.is_finished() {
158 sleep(Duration::from_millis(100));
159 }
160
161 if handle.is_finished() {
162 panic!("failed to start media driver {:?}", handle.join())
163 }
164 info!("started media driver [dir={}]", dir);
165
166 (stop_copy, handle)
167 }
168
169 pub fn launch_embedded_guard(aeron_context: AeronDriverContext, register_sigint: bool) -> EmbeddedMediaDriver {
176 let (stop, handle) = Self::launch_embedded(aeron_context, register_sigint);
177 EmbeddedMediaDriver {
178 stop: Some(stop),
179 handle: Some(handle),
180 }
181 }
182
183 pub fn wait_for_previous_media_driver_to_timeout(aeron_context: &AeronDriverContext) {
186 if !aeron_context.get_dir_delete_on_start() {
187 let cnc_file = Path::new(aeron_context.get_dir()).join("cnc.dat");
188
189 if cnc_file.exists() {
190 let timeout = Duration::from_millis(aeron_context.get_driver_timeout_ms() * 2).as_nanos() as i64;
191
192 let mut duration = timeout;
193
194 if let Ok(md) = cnc_file.metadata() {
195 if let Ok(modified_time) = md.modified() {
196 if let Ok(took) = modified_time.elapsed() {
197 duration = took.as_nanos() as i64;
198 }
199 }
200 }
201
202 let delay = timeout - duration;
203
204 if delay > 0 {
205 let sleep_duration = Duration::from_nanos((delay + 1_000_000) as u64);
206 info!(
207 "cnc file already exists, will need to wait {sleep_duration:?} for timeout [file={cnc_file:?}]"
208 );
209 sleep(sleep_duration);
210 }
211 }
212 }
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use log::error;
220 use std::os::raw::c_int;
221 use std::sync::atomic::Ordering;
222 use std::time::Duration;
223
224 #[test]
225 fn driver_idle_strategy_kinds_round_trip() {
226 let ctx = AeronDriverContext::new().unwrap();
227 for (kind, name) in [
228 (AeronIdleStrategyKind::Sleeping, "sleeping"),
229 (AeronIdleStrategyKind::Yielding, "yield"),
230 (AeronIdleStrategyKind::BusySpin, "spin"),
231 (AeronIdleStrategyKind::NoOp, "noop"),
232 (AeronIdleStrategyKind::Backoff, "backoff"),
233 ] {
234 ctx.set_conductor_idle_strategy_kind(kind).unwrap();
235 assert_eq!(name, ctx.get_conductor_idle_strategy(), "conductor {kind:?}");
236 ctx.set_sender_idle_strategy_kind(kind).unwrap();
237 ctx.set_receiver_idle_strategy_kind(kind).unwrap();
238 ctx.set_sharednetwork_idle_strategy_kind(kind).unwrap();
239 ctx.set_shared_idle_strategy_kind(kind).unwrap();
240 }
241 }
242
243 #[test]
244 fn version_check() {
245 let major = unsafe { crate::aeron_version_major() };
246 let minor = unsafe { crate::aeron_version_minor() };
247 let patch = unsafe { crate::aeron_version_patch() };
248
249 let aeron_version = format!("{}.{}.{}", major, minor, patch);
250 let cargo_version = "1.52.2";
251 assert_eq!(aeron_version, cargo_version);
252 }
253
254 #[test]
255 fn send_message() -> Result<(), AeronCError> {
256 rusteron_code_gen::test_logger::init(log::LevelFilter::Info);
257 let topic = AERON_IPC_STREAM;
258 let stream_id = 32;
259
260 let aeron_context = AeronDriverContext::new()?;
261 aeron_context.set_dir_delete_on_shutdown(true)?;
262 aeron_context.set_dir_delete_on_start(true)?;
263
264 let (stop, _driver_handle) = AeronDriver::launch_embedded(aeron_context.clone(), false);
265
266 info!("aeron dir: {:?}", aeron_context.get_dir());
272
273 let dir = aeron_context.get_dir().to_string();
274 let ctx = AeronContext::new()?;
275 ctx.set_dir(&dir.into_c_string())?;
276
277 let client = Aeron::new(&ctx)?;
278
279 #[derive(Default, Debug)]
280 struct ErrorCount {
281 error_count: usize,
282 }
283
284 impl AeronErrorHandlerCallback for ErrorCount {
285 fn handle_aeron_error_handler(&mut self, error_code: c_int, msg: &str) {
286 error!("Aeron error {}: {}", error_code, msg);
287 self.error_count += 1;
288 }
289 }
290
291 let error_handler = Handler::new(ErrorCount::default());
292 ctx.set_error_handler(Some(error_handler.clone()))?;
293
294 struct Test {}
295 impl AeronAvailableCounterCallback for Test {
296 fn handle_aeron_on_available_counter(
297 &mut self,
298 counters_reader: AeronCountersReader,
299 registration_id: i64,
300 counter_id: i32,
301 ) -> () {
302 info!(
303 "new counter counters_reader={counters_reader:?} registration_id={registration_id} counter_id={counter_id}"
304 );
305 }
306 }
307
308 impl AeronNewPublicationCallback for Test {
309 fn handle_aeron_on_new_publication(
310 &mut self,
311 channel: &str,
312 stream_id: i32,
313 session_id: i32,
314 correlation_id: i64,
315 ) -> () {
316 info!("on new publication {channel} {stream_id} {session_id} {correlation_id}")
317 }
318 }
319 let handler = Handler::new(Test {});
320 ctx.set_on_available_counter(Some(handler.clone()))?;
321 ctx.set_on_new_publication(Some(handler.clone()))?;
322
323 client.start()?;
324 info!("aeron driver started");
325 assert!(Aeron::epoch_clock() > 0);
326 assert!(Aeron::nano_clock() > 0);
327
328 let counter_async = AeronAsyncAddCounter::new(&client, 2543543, "12312312".as_bytes(), "abcd")?;
329
330 let counter = counter_async.poll_blocking(Duration::from_secs(15))?;
331 unsafe {
332 *counter.addr() += 1;
333 }
334
335 let result = AeronAsyncAddPublication::new(&client, topic, stream_id)?;
336
337 let publication = result.poll_blocking(std::time::Duration::from_secs(15))?;
338
339 info!("publication channel: {:?}", publication.channel());
340 info!("publication stream_id: {:?}", publication.stream_id());
341 info!("publication status: {:?}", publication.channel_status());
342
343 drop(publication);
344 drop(counter);
345 drop(client);
346 stop.store(true, Ordering::SeqCst);
347
348 Ok(())
349 }
350
351 #[test]
352 pub fn test_debug() -> Result<(), Box<dyn std::error::Error>> {
353 let ctx = AeronDriverContext::new()?;
354
355 println!("{:#?}", ctx);
356
357 struct AgentStartHandler {
364 ctx_ptr: *mut aeron_driver_context_t,
365 }
366 unsafe impl Send for AgentStartHandler {}
369
370 impl AeronAgentStartFuncCallback for AgentStartHandler {
371 fn handle_aeron_agent_on_start_func(&mut self, role: &str) -> () {
372 unsafe {
373 aeron_set_thread_affinity_on_start(
374 self.ctx_ptr as *mut _,
375 std::ffi::CString::new(role).unwrap().into_raw(),
376 );
377 }
378 }
379 }
380
381 let agent_handler = Handler::new(AgentStartHandler {
382 ctx_ptr: ctx.get_inner(),
383 });
384 ctx.set_agent_on_start_function(Some(agent_handler.clone()))?;
385
386 println!("{:#?}", ctx);
387
388 Ok(())
389 }
390}