scirs2_fft/
context.rs

1//! FFT Context Managers
2//!
3//! This module provides context managers for temporarily changing FFT settings
4//! such as backends, worker counts, and plan caching behavior.
5
6use crate::backend::BackendContext;
7use crate::error::FFTResult;
8use crate::plan_cache::get_global_cache;
9use crate::worker_pool::get_global_pool;
10
11/// Context manager for FFT settings
12pub struct FftContext {
13    backend_context: Option<BackendContext>,
14    previous_workers: Option<usize>,
15    previous_cache_enabled: Option<bool>,
16    worker_pool: &'static crate::worker_pool::WorkerPool,
17    plan_cache: &'static crate::plan_cache::PlanCache,
18}
19
20impl FftContext {
21    /// Create a new FFT context with specified settings
22    pub fn new() -> Self {
23        Self {
24            backend_context: None,
25            previous_workers: None,
26            previous_cache_enabled: None,
27            worker_pool: get_global_pool(),
28            plan_cache: get_global_cache(),
29        }
30    }
31}
32
33impl Default for FftContext {
34    fn default() -> Self {
35        Self::new()
36    }
37}
38
39impl FftContext {
40    /// Set backend for this context
41    pub fn with_backend(mut self, backendname: &str) -> FFTResult<Self> {
42        self.backend_context = Some(BackendContext::new(backendname)?);
43        Ok(self)
44    }
45
46    /// Set number of workers for this context
47    pub fn with_workers(mut self, _numworkers: usize) -> Self {
48        self.previous_workers = Some(self.worker_pool.get_workers());
49        // Note: Due to static reference limitation, we can't actually change _workers
50        // This is a design limitation that would need a different architecture
51        self
52    }
53
54    /// Enable or disable plan caching for this context
55    pub fn with_cache(mut self, enabled: bool) -> Self {
56        self.previous_cache_enabled = Some(self.plan_cache.is_enabled());
57        self.plan_cache.set_enabled(enabled);
58        self
59    }
60
61    /// Enter the context
62    pub fn __enter__(self) -> Self {
63        self
64    }
65
66    /// Exit the context
67    pub fn __exit__(self) {
68        // Cleanup is handled by Drop
69    }
70}
71
72impl Drop for FftContext {
73    fn drop(&mut self) {
74        // Restore cache settings
75        if let Some(enabled) = self.previous_cache_enabled {
76            self.plan_cache.set_enabled(enabled);
77        }
78
79        // Restore worker count (if we could)
80        // Note: This is a limitation of the current design
81
82        // Backend context cleans up itself via Drop
83    }
84}
85
86/// Builder for FFT context configuration
87pub struct FftContextBuilder {
88    backend: Option<String>,
89    workers: Option<usize>,
90    cache_enabled: Option<bool>,
91    cache_size: Option<usize>,
92    cache_ttl: Option<std::time::Duration>,
93}
94
95impl FftContextBuilder {
96    /// Create a new context builder
97    pub fn new() -> Self {
98        Self {
99            backend: None,
100            workers: None,
101            cache_enabled: None,
102            cache_size: None,
103            cache_ttl: None,
104        }
105    }
106
107    /// Set the backend
108    pub fn backend(mut self, name: &str) -> Self {
109        self.backend = Some(name.to_string());
110        self
111    }
112
113    /// Set the number of workers
114    pub fn workers(mut self, count: usize) -> Self {
115        self.workers = Some(count);
116        self
117    }
118
119    /// Enable or disable caching
120    pub fn cache_enabled(mut self, enabled: bool) -> Self {
121        self.cache_enabled = Some(enabled);
122        self
123    }
124
125    /// Set cache size
126    pub fn cache_size(mut self, size: usize) -> Self {
127        self.cache_size = Some(size);
128        self
129    }
130
131    /// Set cache TTL
132    pub fn cache_ttl(mut self, ttl: std::time::Duration) -> Self {
133        self.cache_ttl = Some(ttl);
134        self
135    }
136
137    /// Build the context
138    pub fn build(self) -> FFTResult<FftContext> {
139        let mut context = FftContext::new();
140
141        if let Some(backend) = self.backend {
142            context = context.with_backend(&backend)?;
143        }
144
145        if let Some(workers) = self.workers {
146            context = context.with_workers(workers);
147        }
148
149        if let Some(enabled) = self.cache_enabled {
150            context = context.with_cache(enabled);
151        }
152
153        // Note: cache_size and cache_ttl would require recreating the cache
154        // which is not supported with the current static architecture
155
156        Ok(context)
157    }
158}
159
160impl Default for FftContextBuilder {
161    fn default() -> Self {
162        Self::new()
163    }
164}
165
166/// Create an FFT context with specific settings
167#[allow(dead_code)]
168pub fn fft_context() -> FftContextBuilder {
169    FftContextBuilder::new()
170}
171
172/// Context guard that automatically restores settings when dropped
173pub struct FftSettingsGuard {
174    _context: FftContext,
175}
176
177impl FftSettingsGuard {
178    pub fn new(context: FftContext) -> Self {
179        Self { _context: context }
180    }
181}
182
183/// Use specific FFT settings within a scope
184#[allow(dead_code)]
185pub fn with_fft_settings<F, R>(builder: FftContextBuilder, f: F) -> FFTResult<R>
186where
187    F: FnOnce() -> R,
188{
189    let context = builder.build()?;
190    let _guard = FftSettingsGuard::new(context);
191    Ok(f())
192}
193
194/// Convenience function for using a specific backend
195#[allow(dead_code)]
196pub fn with_backend<F, R>(backend: &str, f: F) -> FFTResult<R>
197where
198    F: FnOnce() -> R,
199{
200    with_fft_settings(fft_context().backend(backend), f)
201}
202
203/// Convenience function for using specific number of workers
204#[allow(dead_code)]
205pub fn with_workers<F, R>(workers: usize, f: F) -> FFTResult<R>
206where
207    F: FnOnce() -> R,
208{
209    with_fft_settings(fft_context().workers(workers), f)
210}
211
212/// Convenience function for running without cache
213#[allow(dead_code)]
214pub fn without_cache<F, R>(f: F) -> FFTResult<R>
215where
216    F: FnOnce() -> R,
217{
218    with_fft_settings(fft_context().cache_enabled(false), f)
219}
220
221#[cfg(test)]
222mod tests {
223    use super::*;
224
225    #[test]
226    fn test_context_builder() {
227        let builder = fft_context()
228            .backend("rustfft")
229            .workers(4)
230            .cache_enabled(true);
231
232        let context = builder.build().unwrap();
233        // Context is created successfully
234        drop(context);
235    }
236
237    #[test]
238    fn test_with_backend() {
239        let result = with_backend("rustfft", || {
240            // Do some FFT operations
241            42
242        });
243
244        assert_eq!(result.unwrap(), 42);
245    }
246
247    #[test]
248    fn test_with_workers() {
249        let result = with_workers(2, || {
250            // Do some FFT operations
251            84
252        });
253
254        assert_eq!(result.unwrap(), 84);
255    }
256
257    #[test]
258    fn test_without_cache() {
259        let result = without_cache(|| {
260            // Do some FFT operations
261            168
262        });
263
264        assert_eq!(result.unwrap(), 168);
265    }
266
267    #[test]
268    fn test_combined_settings() {
269        let result = with_fft_settings(
270            fft_context()
271                .backend("rustfft")
272                .workers(4)
273                .cache_enabled(false),
274            || {
275                // Do some FFT operations
276                336
277            },
278        );
279
280        assert_eq!(result.unwrap(), 336);
281    }
282}