Skip to main content

tenshift_core/
lib.rs

1#![warn(missing_docs)]
2#![warn(clippy::pedantic)]
3#![allow(
4    clippy::module_name_repetitions,
5    clippy::must_use_candidate,
6    clippy::missing_errors_doc,
7)]
8#![forbid(unsafe_code)]
9//! `tenshift-core` builds thread-safe, backpressure-aware data loading pipelines
10//! for iterative processing workloads such as training loops, evaluation passes,
11//! and large offline transforms.
12//!
13//! A pipeline connects a [`source::Source`] to parallel worker stages and a
14//! collector stage with bounded channels between them. Those bounded channels
15//! are the backpressure mechanism: when the consumer slows down, upstream
16//! stages stop overproducing instead of growing memory without bound.
17//!
18//! # Quick Start
19//!
20//! ```rust
21//! use tenshift_core::sample::{Sample, Tensor};
22//! use tenshift_core::sources::MemorySource;
23//! use tenshift_core::Pipeline;
24//!
25//! let samples: Vec<Sample> = (0..100)
26//!     .map(|i| {
27//!         Sample::new()
28//!             .with("x", Tensor::f32(&vec![i as f32; 10], vec![10]))
29//!             .with("y", Tensor::i64(&[(i % 10) as i64], vec![1]))
30//!     })
31//!     .collect();
32//!
33//! let mut pipeline = Pipeline::from_source(MemorySource::new("train", samples))
34//!     .workers(4)
35//!     .prefetch(8)
36//!     .batch(32)
37//!     .start()?;
38//!
39//! let mut batches = 0;
40//! for batch in &mut pipeline {
41//!     assert!(!batch.is_empty());
42//!     batches += 1;
43//! }
44//!
45//! assert_eq!(batches, 4);
46//! # Ok::<(), tenshift_core::error::Error>(())
47//! ```
48//!
49//! # Architecture
50//!
51//! ```text
52//! Source thread ──▸ N worker threads ──▸ Collector thread ──▸ Consumer
53//!   (I/O)           (parallel map/       (serial shuffle/     (caller)
54//!                    filter)              batch, flush)
55//! ```
56//!
57//! # Auto-Scaling Defaults
58//!
59//! The pipeline automatically configures itself based on available resources:
60//!
61//! ## Prefetch Buffer Size
62//! **Default:** `num_cpus() * 2`, minimum 8
63//!
64//! This heuristic balances two competing concerns:
65//! - **Worker starvation prevention:** If the prefetch buffer is too small, workers
66//!   finish their assigned chunks and stall waiting for the collector to consume
67//!   results. A larger buffer keeps all workers fed even when the consumer is slow.
68//! - **Memory pressure:** Each slot in the prefetch buffer holds a full chunk of
69//!   samples. On high-core-count machines (64+), a fixed small buffer would cause
70//!   workers to sit idle.
71//!
72//! The `* 2` multiplier provides headroom for bursty throughput, while the `min 8`
73//! floor ensures reasonable behavior on single-core or container-constrained
74//! environments.
75//!
76//! ## Channel Chunk Size
77//! **Default:** 64 samples per chunk
78//!
79//! Samples flow through channels in chunks rather than individually:
80//! - **Overhead amortization:** Sending 64 samples across a channel costs nearly
81//!   the same as sending 1, dramatically reducing synchronization overhead.
82//! - **Cache locality:** Workers process chunks sequentially, improving cache hit
83//!   rates for transform chains.
84//! - **Backpressure granularity:** 64 provides a sweet spot - large enough to batch
85//!   productively, small enough that bounded channels still provide meaningful
86//!   backpressure to prevent OOM.
87//!
88//! # Shutdown Semantics
89//!
90//! Tenshift guarantees clean shutdown in all error scenarios:
91//!
92//! ## Producer Panic (Source Thread)
93//! If the source thread panics (e.g., filesystem failure), the panic is caught
94//! and converted to an error message. The `shutdown` atomic flag is set, signaling
95//! all workers and the collector to drain and exit. No zombie threads remain.
96//!
97//! ## Consumer Panic (Iterator Drop)
98//! If the consumer thread panics or drops the [`PipelineIterator`] without
99//! exhausting it, the iterator's `Drop` implementation sets `shutdown` and clears
100//! the receiver channel. This unblocks any producer threads blocked on full
101//! channels, allowing graceful thread termination.
102//!
103//! ## Channel Disconnect
104//! All channels are bounded. If any stage disconnects (e.g., worker thread death),
105//! subsequent sends fail immediately. Workers detect send failures and exit their
106//! loops. The collector detects receive failures and flushes remaining data before
107//! terminating.
108//!
109//! ## Explicit Stop
110//! Call [`PipelineIterator::stop()`] to signal shutdown without dropping the
111//! iterator. This allows checking final [`PipelineStats`] after cancellation.
112//!
113//! # Extension Points
114//!
115//! The community extends tenshift through two traits:
116//!
117//! - **[`source::Source`]**  -  Add a new data origin (database, S3, video, etc.)
118//! - **[`transform::Transform`]**  -  Add a new operation (augmentation, normalization, etc.)
119
120#![cfg_attr(
121    not(test),
122    deny(
123        clippy::unwrap_used,
124        clippy::expect_used,
125        clippy::todo,
126        clippy::unimplemented,
127        clippy::panic
128    )
129)]
130// Builder methods all return Self  -  requiring #[must_use] on 40+ methods is noise.
131// Trait name() methods returning &str are idiomatic even when the impl returns a literal.
132// u64-to-usize casts: this crate targets 64-bit ML workloads, not 32-bit embedded.
133#![allow(
134    clippy::return_self_not_must_use,
135    clippy::needless_pass_by_value,
136    clippy::unnecessary_literal_bound
137)]
138
139/// Error types and the crate-wide [`error::Result`] alias.
140pub mod error;
141/// Pipeline builder, runtime configuration, and iterator types.
142pub mod pipeline;
143/// Sample and tensor data structures exchanged between pipeline stages.
144pub mod sample;
145/// Source traits for synchronous and asynchronous data origins.
146pub mod source;
147/// Built-in source implementations such as glob, memory, and JSONL readers.
148pub mod sources;
149/// Stateless and stateful transform traits plus built-in transforms.
150pub mod transform;
151
152/// Error handling behavior when a source item or transform fails.
153pub use pipeline::ErrorPolicy;
154/// Builder for a thread-safe, backpressure-aware data loading pipeline.
155pub use pipeline::Pipeline;
156/// Runtime configuration for pipeline execution.
157pub use pipeline::PipelineConfig;
158/// Running pipeline iterator that yields output batches.
159pub use pipeline::PipelineIterator;
160/// Observability snapshot for a running or completed pipeline.
161pub use pipeline::PipelineStats;
162#[cfg(feature = "csv")]
163/// CSV-backed [`source::Source`] implementation.
164pub use sources::CsvSource;
165/// Source wrapper that shards items across distributed ranks.
166pub use sources::DistributedSampler;
167/// Glob-backed file source that emits one sample per matched file.
168pub use sources::GlobSource;
169#[cfg(feature = "uring")]
170/// `io_uring`-accelerated file source.
171pub use sources::RingSource;
172
173/// Compile-checks the README quick-start example as a doctest.
174#[cfg(doctest)]
175#[doc = include_str!("../README.md")]
176struct ReadmeExamples;