epics_libcom_rs/lib.rs
1//! EPICS `libCom` for Rust: the layer an IOC is built *on*, with no record
2//! system above it.
3//!
4//! This is `epics_base_rs::runtime` and `epics_base_rs::net` lifted out of
5//! `epics-base-rs` (issue #55) so a consumer — a protocol client, a gateway,
6//! `pvxs-rs` — can take the concurrency and socket primitives without taking
7//! the database with them. The name is C's: `libCom` is where upstream EPICS
8//! keeps `epicsThread`, `epicsTime`, `errlog`, `envDefs` *and* `osiSock`, which
9//! is exactly this crate's two modules.
10//!
11//! `epics-base-rs` re-exports both modules at their original paths, so
12//! `epics_base_rs::runtime::…` and `epics_base_rs::net::…` still resolve and
13//! nothing downstream had to change.
14//!
15//! * [`runtime`] — the task seam and its two backends, `epicsThread`-parity
16//! priority bands, `errlog`, the EPICS string/environment types, the
17//! general-time provider.
18//! * [`net`] — the EPICS protocols' shared socket layer: per-NIC async UDP,
19//! interface enumeration, loopback multicast. Host-only; the wire constants
20//! beside it compile for every target, RTEMS included.
21//! * [`walltime`] — [`WallTime`](walltime::WallTime), the wall-clock instant
22//! `runtime::time` returns. It lived in `epics_base_rs::types` and moved down
23//! with its producer; `epics-base-rs` re-exports it at `types::WallTime`.
24//!
25//! # Features
26//!
27//! * `rtems-exec-model` — select the reactor-free `exec_backend` on a hosted
28//! target. See [`EXEC_BACKEND`].
29//! * `linux-rt` — back [`runtime::sync::PriorityInheritanceMutex`] with a
30//! `PTHREAD_PRIO_INHERIT` `pthread_mutex_t` on Linux.
31
32// The three `epics-base-rs` crate-level allows this code was written under and
33// still needs — `collapsible_if` and `manual_range_contains` in `runtime`,
34// `io_other_error` in `net`. Narrowed to those three rather than inherited
35// wholesale: the extraction is a move, so the code is byte-identical and a
36// lint it does not trip has no business being silenced here.
37#![allow(
38 clippy::collapsible_if,
39 clippy::io_other_error,
40 clippy::manual_range_contains
41)]
42
43// The exec backend's blocking pumps end a parked reader with a local
44// `shutdown(Shutdown::Both)` and bound a stuck writer through loopback
45// send-backpressure (`runtime::blocking_io`). Both are POSIX blocking-socket
46// semantics; Windows provides neither (measured, PR #56 CI 2026-07-24: a
47// parked `recv` outlived shutdown by the full 120 s test bound, and an
48// 8 MiB frame to a never-reading peer was swallowed in 12 ms), so a Windows
49// build selecting this backend would hang on connection teardown instead of
50// failing visibly. Refuse it at compile time rather than ship that.
51#[cfg(all(windows, exec_backend))]
52compile_error!(
53 "the exec backend (`rtems-exec-model`) relies on POSIX blocking-socket \
54 semantics (shutdown wakes a parked read; loopback sends see \
55 backpressure) that Windows does not provide; build the default tokio \
56 backend on Windows instead"
57);
58
59// Lets `#[epics_macros_rs::epics_test]` expansions — which name the runtime
60// crate by its external path — resolve inside this crate's own unit tests,
61// where proc-macro-crate reports `FoundCrate::Itself`. Same device as
62// `epics-base-rs`'s alias for the same macro.
63extern crate self as epics_libcom_rs;
64
65pub mod net;
66pub mod runtime;
67pub mod walltime;
68
69/// Which [`runtime::task`] backend this build selected — `true` for the
70/// reactor-free std-thread [`runtime::background`] executor, `false` for tokio.
71///
72/// The predicate is computed once, in this crate's `build.rs`, from the target
73/// OS and the `rtems-exec-model` feature. A crate above that derives the same
74/// `cfg` from its own `build.rs` (`epics-base-rs` does, for `server::scan`)
75/// can pin the two together with a `const _: () = assert!(...)`, so a feature
76/// forward that stops being wired fails to compile instead of splitting the
77/// workspace across two backends.
78pub const EXEC_BACKEND: bool = cfg!(exec_backend);