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
//! Per-precision thread-local scratch pools for 2D and 3D parallel Strang.
//!
//! Rust's `thread_local!` macro forbids generic statics, so Wave 5 (ADR-0045
//! §5.3) introduces one concrete `ScratchPool<f64>` and one `ScratchPool<f32>`
//! static per dimensionality class (2D, 3D), dispatched via sealed traits.
//!
//! ## Design (ADR-0045 §4)
//!
//! ```text
//! thread_local! {
//! PARALLEL_2D_POOL_F64: RefCell<ScratchPool<f64>>
//! PARALLEL_2D_POOL_F32: RefCell<ScratchPool<f32>>
//! PARALLEL_3D_POOL_F64: RefCell<ScratchPool<f64>>
//! PARALLEL_3D_POOL_F32: RefCell<ScratchPool<f32>>
//! }
//! ```
//!
//! `ParallelPool2D`/`ParallelPool3D` are sealed `pub(crate)` traits; only `f32`
//! and `f64` may implement them. The dispatch is zero-cost (monomorphisation
//! selects the right `thread_local!` at compile time).
//!
//! The f32 pool is **idle** in the common case (unit overhead: ~24 bytes per
//! thread for the empty `Vec<Vec<f32>>` header). See ADR-0045 §4.4.
use core::cell::RefCell;
use crate::{float::SemiflowFloat, scratch::ScratchPool};
// ---------------------------------------------------------------------------
// 2D thread-local pools (one per concrete F)
// ---------------------------------------------------------------------------
thread_local! {
/// Per-thread 2D scratch pool for `F = f64` (Wave 2, ADR-0042).
///
/// Not part of the stable public API; not frozen at v1.0.0.
#[doc(hidden)]
pub static PARALLEL_2D_POOL_F64: RefCell<ScratchPool<f64>> =
RefCell::new(ScratchPool::new());
/// Per-thread 2D scratch pool for `F = f32` (Wave 5, ADR-0045).
///
/// Not part of the stable public API; not frozen at v1.0.0.
#[doc(hidden)]
pub static PARALLEL_2D_POOL_F32: RefCell<ScratchPool<f32>> =
RefCell::new(ScratchPool::new());
}
// ---------------------------------------------------------------------------
// 3D thread-local pools (one per concrete F)
// ---------------------------------------------------------------------------
thread_local! {
/// Per-thread 3D scratch pool for `F = f64` (Wave 2, ADR-0042).
///
/// Not part of the stable public API; not frozen at v1.0.0.
#[doc(hidden)]
pub static PARALLEL_3D_POOL_F64: RefCell<ScratchPool<f64>> =
RefCell::new(ScratchPool::new());
/// Per-thread 3D scratch pool for `F = f32` (Wave 5, ADR-0045).
///
/// Not part of the stable public API; not frozen at v1.0.0.
#[doc(hidden)]
pub static PARALLEL_3D_POOL_F32: RefCell<ScratchPool<f32>> =
RefCell::new(ScratchPool::new());
}
// ---------------------------------------------------------------------------
// Sealed 2D pool trait
// ---------------------------------------------------------------------------
/// Sealed trait dispatching the correct 2D per-precision thread-local pool.
///
/// `pub(crate)` prevents downstream crates from adding new implementations.
/// Only `f32` and `f64` may implement this trait (ADR-0045 §4.5).
pub(crate) trait ParallelPool2D: SemiflowFloat {
/// Borrow the calling thread's 2D scratch pool for the duration of `f`.
fn with_pool_2d<R>(f: impl FnOnce(&mut ScratchPool<Self>) -> R) -> R;
}
impl ParallelPool2D for f64 {
fn with_pool_2d<R>(f: impl FnOnce(&mut ScratchPool<f64>) -> R) -> R {
PARALLEL_2D_POOL_F64.with(|cell| f(&mut cell.borrow_mut()))
}
}
impl ParallelPool2D for f32 {
fn with_pool_2d<R>(f: impl FnOnce(&mut ScratchPool<f32>) -> R) -> R {
PARALLEL_2D_POOL_F32.with(|cell| f(&mut cell.borrow_mut()))
}
}
// ---------------------------------------------------------------------------
// Sealed 3D pool trait
// ---------------------------------------------------------------------------
/// Sealed trait dispatching the correct 3D per-precision thread-local pool.
///
/// `pub(crate)` prevents downstream crates from adding new implementations.
/// Only `f32` and `f64` may implement this trait (ADR-0045 §4.5).
pub(crate) trait ParallelPool3D: SemiflowFloat {
/// Borrow the calling thread's 3D scratch pool for the duration of `f`.
fn with_pool_3d<R>(f: impl FnOnce(&mut ScratchPool<Self>) -> R) -> R;
}
impl ParallelPool3D for f64 {
fn with_pool_3d<R>(f: impl FnOnce(&mut ScratchPool<f64>) -> R) -> R {
PARALLEL_3D_POOL_F64.with(|cell| f(&mut cell.borrow_mut()))
}
}
impl ParallelPool3D for f32 {
fn with_pool_3d<R>(f: impl FnOnce(&mut ScratchPool<f32>) -> R) -> R {
PARALLEL_3D_POOL_F32.with(|cell| f(&mut cell.borrow_mut()))
}
}
// ---------------------------------------------------------------------------
// Drain helpers (test hooks)
// ---------------------------------------------------------------------------
/// Drain the calling thread's 2D parallel scratch pool for precision `F`.
///
/// Clears all free buffers; capacity released. Used by
/// `tests/parallel_scratch_drain.rs`. Not part of the stable public API.
#[doc(hidden)]
#[allow(private_bounds)]
pub(crate) fn drain_thread_local_pools_2d_for<F: ParallelPool2D>() {
F::with_pool_2d(|pool| *pool = ScratchPool::new());
}
/// Drain the calling thread's 3D parallel scratch pool for precision `F`.
///
/// Not part of the stable public API.
#[doc(hidden)]
#[allow(private_bounds)]
pub(crate) fn drain_thread_local_pools_3d_for<F: ParallelPool3D>() {
F::with_pool_3d(|pool| *pool = ScratchPool::new());
}