pub mod axis;
pub mod config;
pub mod event;
pub mod port;
pub mod supervisor;
pub mod sync {
pub use std::sync::Arc;
pub use tokio::sync::{Mutex, Notify, RwLock, broadcast, mpsc, oneshot};
}
pub mod task {
use std::future::Future;
pub use epics_libcom_rs::runtime::task::{
Interval, Reactor, TaskHandle, interval, sleep, sleep_until, spawn_blocking, yield_now,
};
pub fn spawn<F>(future: F) -> TaskHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
Reactor::current()
.expect("asyn_rs::runtime::task::spawn is called from a task on an executor")
.spawn(future)
}
}
pub use tokio::select;
pub use axis::{
AxisActions, AxisDelayRequest, AxisMotorCommand, AxisPollDirective, AxisRuntime,
AxisRuntimeHandle, create_axis_runtime,
};
pub use config::{BackoffConfig, RuntimeConfig, SupervisionPolicy};
pub use event::RuntimeEvent;
pub use port::{PortRuntimeHandle, create_port_runtime, port_runtime_unavailable};
pub use supervisor::{SupervisionOutcome, supervise};
#[cfg(test)]
mod tokio_is_not_the_executor {
use source_guard::{Comments, production};
const NEEDLES: [&str; 7] = [
concat!("tokio", "::spawn("),
concat!("tokio::task", "::spawn("),
concat!("tokio::time", "::sleep("),
concat!("tokio::time", "::sleep_until("),
concat!("tokio::time", "::interval("),
concat!("tokio::time", "::timeout("),
concat!("tokio::runtime::Handle", "::try_current"),
];
#[test]
fn library_tasks_go_through_the_runtime_task_seam() {
let files: [(&str, &str); 6] = [
("adapter.rs", include_str!("../adapter.rs")),
("asyn_record/mod.rs", include_str!("../asyn_record/mod.rs")),
(
"transport/in_process.rs",
include_str!("../transport/in_process.rs"),
),
("runtime/axis.rs", include_str!("axis.rs")),
("runtime/supervisor.rs", include_str!("supervisor.rs")),
("runtime/mod.rs", include_str!("mod.rs")),
];
let anchors = [
("adapter.rs", "fn property_post_receiver"),
("asyn_record/mod.rs", "fn register_exception_callback"),
("transport/in_process.rs", "fn subscribe("),
("runtime/axis.rs", "async fn handle_command("),
("runtime/supervisor.rs", "pub async fn supervise<"),
("runtime/mod.rs", "pub fn spawn<F>("),
];
for (name, src) in files {
let prod = production(src, Comments::Strip);
let anchor = anchors.iter().find(|(n, _)| *n == name).unwrap().1;
assert!(
prod.contains(anchor),
"{name}: production slice no longer contains `{anchor}` — the \
slicer stopped covering the guarded code"
);
for needle in NEEDLES {
assert_eq!(
prod.matches(needle).count(),
0,
"{name}: library code starts tasks and waits on time through \
`crate::runtime::task`; found bare `{needle}`"
);
}
}
}
#[test]
fn epics_test_files_wait_through_the_seam_too() {
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests");
let mut checked = 0usize;
for entry in std::fs::read_dir(&dir).expect("asyn-rs/tests") {
let path = entry.expect("directory entry").path();
if path.extension().and_then(|e| e.to_str()) != Some("rs") {
continue;
}
let src = std::fs::read_to_string(&path).expect("test source");
if !src.contains("epics_test") {
continue;
}
checked += 1;
let name = path.file_name().unwrap().to_string_lossy().into_owned();
for needle in NEEDLES {
assert_eq!(
src.matches(needle).count(),
0,
"tests/{name}: an `#[epics_test]` body runs on the seam's \
executor, which is not tokio on every backend; found bare \
`{needle}`"
);
}
}
assert!(checked > 0, "no #[epics_test] file found under {dir:?}");
}
}