use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use camel_api::BoxProcessorExt;
static ALLOC_COUNT: AtomicU64 = AtomicU64::new(0);
static COUNTING: AtomicBool = AtomicBool::new(false);
struct CountingAllocator;
unsafe impl GlobalAlloc for CountingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if COUNTING.load(Ordering::Relaxed) {
ALLOC_COUNT.fetch_add(1, Ordering::Relaxed);
}
unsafe { System.alloc(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) }
}
}
#[global_allocator]
static ALLOCATOR: CountingAllocator = CountingAllocator;
fn make_exchange() -> camel_api::Exchange {
camel_api::Exchange::new(camel_api::Message::new("alloc-test"))
}
fn passthrough() -> camel_api::BoxProcessor {
camel_api::BoxProcessor::from_fn(|ex| Box::pin(async move { Ok(ex) }))
}
fn build_three_step_pipeline() -> camel_api::BoxProcessor {
use camel_core::route::{CompiledStep, PipelineRuntimeCtx, compose_pipeline};
let processors: Vec<CompiledStep> = (0..3)
.map(|_| CompiledStep::Process {
processor: passthrough(),
body_contract: None,
lifecycle: None,
})
.collect();
compose_pipeline(processors, PipelineRuntimeCtx::compile_time())
}
#[tokio::test(flavor = "current_thread")]
async fn allocations_drop_after_a2() {
use camel_api::BoxProcessor;
use tower::Service;
use tower::ServiceExt;
let pipeline = build_three_step_pipeline();
let mut svc = BoxProcessor::new(pipeline);
for _ in 0..2 {
let ex = make_exchange();
let _ = svc.ready().await.unwrap().call(ex).await.unwrap();
}
let ex = make_exchange();
COUNTING.store(true, Ordering::Relaxed);
ALLOC_COUNT.store(0, Ordering::Relaxed);
let result = svc.ready().await.unwrap().call(ex).await.unwrap();
let allocs = ALLOC_COUNT.load(Ordering::Relaxed);
COUNTING.store(false, Ordering::Relaxed);
let body_str = format!("{:?}", result.input.body);
assert!(
body_str.contains("alloc-test"),
"exchange body mismatch: {body_str}"
);
const ALLOCS_BEFORE_A2: u64 = 22;
eprintln!("allocations per pipeline call: {allocs}");
assert!(
allocs <= ALLOCS_BEFORE_A2 - 2,
"A2 allocation reduction insufficient: {allocs} > {} (baseline {ALLOCS_BEFORE_A2})",
ALLOCS_BEFORE_A2 - 2
);
}