Skip to main content

shape_runtime/
sync_bridge.rs

1//! Async/Sync bridge for Shape runtime
2//!
3//! Provides a shared Tokio runtime for async operations in Shape.
4//! This simplified version focuses on managing the runtime for market-data
5//! provider access so legacy synchronous callers can still execute queries.
6
7use shape_ast::error::{Result, ShapeError};
8use std::future::Future;
9use std::sync::{Arc, OnceLock};
10use tokio::runtime::{Handle, Runtime};
11
12/// Global shared Tokio runtime for Shape
13static SHARED_RUNTIME: OnceLock<Arc<Runtime>> = OnceLock::new();
14
15/// Initialize the shared runtime (called once at startup)
16pub fn initialize_shared_runtime() -> Result<()> {
17    if SHARED_RUNTIME.get().is_some() {
18        return Ok(()); // Already initialized
19    }
20
21    let runtime = Runtime::new().map_err(|e| ShapeError::RuntimeError {
22        message: format!("Failed to create shared Tokio runtime: {}", e),
23        location: None,
24    })?;
25
26    SHARED_RUNTIME
27        .set(Arc::new(runtime))
28        .map_err(|_| ShapeError::RuntimeError {
29            message: "Failed to set shared runtime (race condition)".to_string(),
30            location: None,
31        })?;
32
33    Ok(())
34}
35
36/// Get the shared runtime handle
37pub fn get_runtime_handle() -> Result<Handle> {
38    let runtime = SHARED_RUNTIME
39        .get()
40        .ok_or_else(|| ShapeError::RuntimeError {
41            message: "Shared runtime not initialized. Call initialize_shared_runtime() first."
42                .to_string(),
43            location: None,
44        })?;
45
46    Ok(runtime.handle().clone())
47}
48
49/// Block on a future using the shared runtime
50///
51/// This is safe to call from synchronous contexts as it properly handles
52/// nested runtime calls by using the handle instead of entering the runtime.
53pub fn block_on_shared<F, T>(future: F) -> Result<T>
54where
55    F: Future<Output = T> + Send + 'static,
56    T: Send + 'static,
57{
58    let handle = get_runtime_handle()?;
59
60    // Check if we're already in a Tokio runtime
61    let result = if let Ok(_handle_in_runtime) = Handle::try_current() {
62        // We're in a runtime, use spawn_blocking to avoid blocking the runtime
63        let (tx, rx) = std::sync::mpsc::channel();
64        handle.spawn(async move {
65            let result = future.await;
66            let _ = tx.send(result);
67        });
68        rx.recv().map_err(|e| ShapeError::RuntimeError {
69            message: format!("Failed to receive async result: {}", e),
70            location: None,
71        })?
72    } else {
73        // Not in a runtime, safe to block directly
74        handle.block_on(future)
75    };
76
77    Ok(result)
78}
79
80/// Deprecated: SyncDataProvider removed
81///
82/// Use the async data architecture (Phase 6) instead.
83/// Legacy code should migrate to ExecutionContext::prefetch_data().
84#[derive(Clone)]
85pub struct SyncDataProvider {
86    _placeholder: std::marker::PhantomData<()>,
87}