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