use crate::{Aeron, AeronCError, AeronDriver, AeronDriverContext, IntoCString};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::JoinHandle;
pub struct EmbeddedDriver {
dir: String,
context: AeronDriverContext,
stop: Arc<AtomicBool>,
handle: Option<JoinHandle<Result<(), AeronCError>>>,
}
impl EmbeddedDriver {
pub fn launch() -> Result<Self, AeronCError> {
Self::launch_with(|_| Ok(()))
}
pub fn launch_with(
configure: impl FnOnce(&AeronDriverContext) -> Result<(), AeronCError>,
) -> Result<Self, AeronCError> {
let context = AeronDriverContext::new()?;
context.set_dir_delete_on_shutdown(true)?;
context.set_dir_delete_on_start(true)?;
context.set_dir(&format!("{}{}", context.get_dir(), Aeron::nano_clock()).into_c_string())?;
configure(&context)?;
let dir = context.get_dir().to_string();
let (stop, handle) = AeronDriver::launch_embedded(context.clone(), false);
Ok(Self {
dir,
context,
stop,
handle: Some(handle),
})
}
#[inline]
pub fn dir(&self) -> &str {
&self.dir
}
#[inline]
pub fn context(&self) -> &AeronDriverContext {
&self.context
}
pub fn stop(mut self) {
self.stop_and_join();
}
fn stop_and_join(&mut self) {
self.stop.store(true, Ordering::SeqCst);
if let Some(handle) = self.handle.take() {
let _ = handle.join();
}
}
}
impl Drop for EmbeddedDriver {
fn drop(&mut self) {
self.stop_and_join();
}
}
pub fn find_unused_udp_port(start: u16) -> Option<u16> {
(start..65535).find(|p| std::net::UdpSocket::bind(("127.0.0.1", *p)).is_ok())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn embedded_driver_launches_and_stops_on_drop() {
let driver = EmbeddedDriver::launch().unwrap();
assert!(!driver.dir().is_empty());
let dir = driver.dir().to_string();
drop(driver); assert!(!std::path::Path::new(&dir).join("cnc.dat").exists());
}
#[test]
fn find_unused_udp_port_returns_bindable_port() {
let port = find_unused_udp_port(23000).unwrap();
assert!(std::net::UdpSocket::bind(("127.0.0.1", port)).is_ok());
}
}