Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ EXIT_CODE=0

# --- Rust Checks ---
run_rust_checks() {
echo "Rust files changed. Running Rust-specific checks..."

run_check() {
echo "Running '$@'..."
if ! "$@"; then
Expand All @@ -18,11 +16,22 @@ run_rust_checks() {
fi
}

run_check cargo audit
run_check cargo check
run_check cargo clippy --all-targets --all-features -- -D warnings
run_check cargo fmt --all -- --check
run_check cargo test --all-features
run_check scripts/msrv-check.sh

# Only run the expensive checks (tests, audit, MSRV) when source
# code actually changed. Cargo.toml/Cargo.lock-only changes and
# non-code files skip these.
RUST_SOURCE_CHANGED=$(git diff --cached --name-only --diff-filter=ACMRT | grep -E '\.rs$' || true)
if [ -n "$RUST_SOURCE_CHANGED" ]; then
echo "Rust source changed. Running tests, audit, and MSRV checks..."
run_check cargo audit
run_check cargo test --all-features
run_check scripts/msrv-check.sh
else
echo "No .rs files changed. Skipping tests, audit, and MSRV."
fi
}

# --- Python Checks ---
Expand Down
58 changes: 57 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
@@ -1 +1,57 @@

# AGENTS.md

IPC benchmark suite in Rust. Measures latency and throughput across
multiple transport mechanisms on embedded Linux platforms (ARM/aarch64).

## Constraints

- MSRV is defined by `rust-version` in Cargo.toml. That is the single
source of truth — do not hardcode the version elsewhere.
- Pin transitive dependencies that pull in editions or features above MSRV.
- Do not add `.cargo/config.toml` with `target-cpu=native` — binaries
must be portable across ARM variants. Apply CPU tuning at build time.
- No customer or partner names in code, comments, issues, or PR descriptions.

## Pre-commit hooks

Pre-commit hooks run cargo check, clippy, fmt, tests, and MSRV validation.
Never use `--no-verify` without stating the reason and getting user
confirmation. If a required tool is missing, install it rather than
skipping the check.

## Measurement accuracy

This is a benchmarking tool — results must be comparable to equivalent
C programs making the same syscalls. Overhead in the measurement path
directly affects results. Avoid allocations in send/receive hot loops.
Prefer direct libc calls over wrapper crates for timing-critical
syscalls (e.g., `clock_gettime`). Minimize abstraction layers between
the application and the kernel interface. Timestamp capture points
must be documented and placed as close to the actual IPC operation
as possible.

## Code style

- Minimize comments. Don't explain what code does — explain why when
non-obvious.
- Don't add features or abstractions beyond what the task requires.
- Prefer editing existing files over creating new ones.

## Testing

- AI-assisted code requires measurable test coverage. Library code
should be testable by tarpaulin — avoid putting testable logic in
the binary crate (`main.rs`).
- Verify changes on target hardware when performance claims are made.

## Commits

- Include an `AI-Assisted-By:` trailer in commit messages identifying
the model and tool used.

## PRs

- Reference related issues. Provide evidence (before/after data) for
performance or correctness claims.
- Keep scope focused. Don't bundle unrelated fixes.
- Rebase on main before requesting review.
32 changes: 17 additions & 15 deletions scripts/msrv-check.sh
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail

echo "[MSRV] Running Rust 1.70 build/tests in container..."

# Find container runtime
RUNTIME="$(command -v podman || command -v docker || true)"
if [[ -z "${RUNTIME}" ]]; then
echo "[MSRV] No container runtime (podman/docker) found. Skipping MSRV check."
MSRV="$(awk -F '"' '/rust-version/ {print $2}' Cargo.toml)"
if [[ -z "${MSRV}" ]]; then
echo "[MSRV] No rust-version found in Cargo.toml. Skipping."
exit 0
fi

IMAGE="docker.io/library/rust:1.70"
WORKDIR="/work"
MOUNT="$(pwd):${WORKDIR}"
if [[ "${RUNTIME}" == *podman ]]; then
MOUNT="${MOUNT}:Z"
fi
echo "[MSRV] Checking Rust ${MSRV} compatibility..."

CMD='. /usr/local/cargo/env && cargo -V && rustc -V && cargo build --locked -q && cargo test --locked -q'
if ! command -v rustup &>/dev/null; then
echo "[MSRV] rustup not found. Skipping MSRV check."
exit 0
fi

"${RUNTIME}" run --rm -v "${MOUNT}" -w "${WORKDIR}" "${IMAGE}" bash -lc "${CMD}"
if ! rustup toolchain list | grep -q "${MSRV}"; then
echo "[MSRV] Installing Rust ${MSRV} toolchain..."
rustup toolchain install "${MSRV}" --profile minimal
fi

echo "[MSRV] Rust 1.70 build/tests passed."
echo "[MSRV] Building with Rust ${MSRV}..."
rustup run "${MSRV}" cargo build --locked -q

echo "[MSRV] Testing with Rust ${MSRV}..."
rustup run "${MSRV}" cargo test --locked -q

echo "[MSRV] Rust ${MSRV} build/tests passed."
17 changes: 10 additions & 7 deletions src/standalone_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ pub fn run_standalone_client_blocking_single(
results_manager: &mut BlockingResultsManager,
config: &BenchmarkConfig,
) -> Result<()> {
let mut transport = BlockingTransportFactory::create(&mechanism, args.shm_direct)?;
let mut transport =
BlockingTransportFactory::create(&mechanism, args.shm_direct, args.send_delay)?;

info!("Connecting to server...");
connect_blocking_with_retry(&mut transport, &transport_config)?;
Expand Down Expand Up @@ -461,7 +462,8 @@ pub fn run_standalone_client_blocking_concurrent(
// exits before round-trip workers connect.
let sentinel: Option<Box<dyn crate::ipc::BlockingTransport>> =
if config.one_way && config.round_trip {
let mut transport = BlockingTransportFactory::create(&mechanism, args.shm_direct)?;
let mut transport =
BlockingTransportFactory::create(&mechanism, args.shm_direct, args.send_delay)?;
connect_blocking_with_retry(&mut transport, &transport_config)?;
debug!("Sentinel connection established to keep server alive across phases");
Some(transport)
Expand Down Expand Up @@ -491,7 +493,8 @@ pub fn run_standalone_client_blocking_concurrent(
};

std::thread::spawn(move || -> Result<PerformanceMetrics> {
let mut transport = BlockingTransportFactory::create(&mech, shm_direct)?;
let mut transport =
BlockingTransportFactory::create(&mech, shm_direct, send_delay)?;
connect_blocking_with_retry(&mut transport, &tc)?;
debug!("Worker {} connected (one-way)", worker_id);

Expand Down Expand Up @@ -587,7 +590,7 @@ pub fn run_standalone_client_blocking_concurrent(
};

std::thread::spawn(move || -> Result<(PerformanceMetrics, Vec<MessageLatencyRecord>)> {
let mut transport = BlockingTransportFactory::create(&mech, shm_direct)?;
let mut transport = BlockingTransportFactory::create(&mech, shm_direct, send_delay)?;
connect_blocking_with_retry(&mut transport, &tc)?;
debug!("Worker {} connected (round-trip)", worker_id);

Expand Down Expand Up @@ -1360,15 +1363,15 @@ mod tests {
let client_config = transport_config.clone();
let client_handle = std::thread::spawn(move || {
let mut transport =
BlockingTransportFactory::create(&IpcMechanism::TcpSocket, false).unwrap();
BlockingTransportFactory::create(&IpcMechanism::TcpSocket, false, None).unwrap();
connect_blocking_with_retry(&mut transport, &client_config).unwrap();
transport.close_blocking().unwrap();
});

// Wait, then start server
std::thread::sleep(std::time::Duration::from_millis(500));
let mut server_transport =
BlockingTransportFactory::create(&IpcMechanism::TcpSocket, false).unwrap();
BlockingTransportFactory::create(&IpcMechanism::TcpSocket, false, None).unwrap();
server_transport
.start_server_blocking(&transport_config)
.unwrap();
Expand Down Expand Up @@ -1641,7 +1644,7 @@ mod tests {
// Start server first
let server_handle = std::thread::spawn(move || {
let mut transport =
BlockingTransportFactory::create(&IpcMechanism::TcpSocket, false).unwrap();
BlockingTransportFactory::create(&IpcMechanism::TcpSocket, false, None).unwrap();
let config = TransportConfig {
host: "127.0.0.1".to_string(),
port,
Expand Down
Loading
Loading