use rusteron_client::*;
use rusteron_media_driver::testing::EmbeddedDriver;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
const STREAM_ID: i32 = 1002;
const MESSAGE_LENGTH: usize = 256;
const MESSAGES: u64 = 5_000_000;
const FRAGMENT_LIMIT: usize = 256;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let running = Arc::new(AtomicBool::new(true));
let running_ctrl_c = Arc::clone(&running);
ctrlc::set_handler(move || running_ctrl_c.store(false, Ordering::SeqCst))?;
let driver = EmbeddedDriver::launch()?;
let ctx = AeronContext::new()?;
ctx.set_dir(&cformat!("{}", driver.dir()))?;
ctx.set_error_handler(Some(|code: i32, msg: &str| eprintln!("aeron error {code}: {msg}")))?;
let aeron = Aeron::new(&ctx)?;
aeron.start()?;
let publication = aeron
.async_add_publication(AERON_IPC_STREAM, STREAM_ID)?
.poll_blocking(Duration::from_secs(5))?;
let subscription = aeron
.async_add_subscription(AERON_IPC_STREAM, STREAM_ID, Handlers::NONE, Handlers::NONE)?
.poll_blocking(Duration::from_secs(5))?;
let msgs = Arc::new(AtomicU64::new(0));
let bytes = Arc::new(AtomicU64::new(0));
let msgs_poll = msgs.clone();
let bytes_poll = bytes.clone();
let running_sub = running.clone();
let subscriber = thread::spawn(move || -> Result<(), AeronCError> {
let mut assembler = AeronFragmentClosureAssembler::new()?;
let mut counters = (msgs_poll, bytes_poll);
let mut last_report = Instant::now();
let (mut last_msgs, mut last_bytes) = (0u64, 0u64);
while running_sub.load(Ordering::Acquire) {
let fragments = assembler.poll(
&subscription,
&mut counters,
|c, buf, _hdr| {
c.0.fetch_add(1, Ordering::Relaxed);
c.1.fetch_add(buf.len() as u64, Ordering::Relaxed);
},
FRAGMENT_LIMIT,
)?;
if fragments == 0 {
std::hint::spin_loop();
}
if last_report.elapsed() >= Duration::from_secs(1) {
let (m, b) = (counters.0.load(Ordering::Relaxed), counters.1.load(Ordering::Relaxed));
let secs = last_report.elapsed().as_secs_f64();
println!(
"{:.03} msgs/sec, {:.03} MB/sec, totals {} messages {} MB",
(m - last_msgs) as f64 / secs,
(b - last_bytes) as f64 / secs / (1024.0 * 1024.0),
m,
b / (1024 * 1024),
);
(last_msgs, last_bytes) = (m, b);
last_report = Instant::now();
}
}
Ok(())
});
let message = vec![42u8; MESSAGE_LENGTH];
let mut back_pressure = 0u64;
let mut not_connected_spins = 0u64;
let start = Instant::now();
let mut sent = 0u64;
'publish: while sent < MESSAGES && running.load(Ordering::Acquire) {
match publication.offer(&message) {
Ok(_) => sent += 1,
Err(e) if e.is_retryable() => {
match e {
AeronOfferError::BackPressured => back_pressure += 1,
AeronOfferError::NotConnected => not_connected_spins += 1,
_ => {}
}
std::hint::spin_loop();
}
Err(e) => {
eprintln!("fatal offer error after {sent} messages: {e}");
break 'publish;
}
}
}
let elapsed = start.elapsed();
println!(
"published {sent} messages in {elapsed:?} ({:.0} msgs/sec), {back_pressure} back-pressure events, {not_connected_spins} not-connected spins",
sent as f64 / elapsed.as_secs_f64(),
);
thread::sleep(Duration::from_millis(200));
running.store(false, Ordering::Release);
subscriber.join().expect("subscriber thread panicked")?;
let received = msgs.load(Ordering::Relaxed);
println!("received {received} messages");
assert_eq!(sent, received, "subscriber must observe every published message");
drop(publication);
drop(aeron);
Ok(())
}