Skip to main content

runmat_runtime/context/
runtime.rs

1use super::{ContextFuture, RuntimeContextGuard, RuntimeContextState, RuntimeServicePorts};
2use crate::execution::RuntimeExecutionServices;
3use std::future::Future;
4use std::rc::Rc;
5use std::sync::{atomic::AtomicBool, Arc};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum RuntimeLanguageMode {
9    Matlab,
10    RunMat,
11    Strict,
12}
13
14pub const DEFAULT_CALLSTACK_LIMIT: usize = 200;
15pub const DEFAULT_ERROR_NAMESPACE: &str = "RunMat";
16
17/// Complete explicit runtime authority for one session/invocation tree.
18#[derive(Clone)]
19pub struct RuntimeContext {
20    execution: Rc<dyn RuntimeExecutionServices>,
21    program_revision: Option<runmat_execution::ProgramRevision>,
22    search_path: Option<Arc<crate::builtins::common::path_state::SearchPath>>,
23    services: RuntimeServicePorts,
24    state: Rc<RuntimeContextState>,
25}
26
27impl std::fmt::Debug for RuntimeContext {
28    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        formatter
30            .debug_struct("RuntimeContext")
31            .field("scope_id", &self.execution.scope_id())
32            .field("program_revision", &self.program_revision)
33            .field("services", &self.services)
34            .finish_non_exhaustive()
35    }
36}
37
38impl RuntimeContext {
39    pub fn new(execution: Rc<dyn RuntimeExecutionServices>) -> Self {
40        Self::with_cancellation(execution, Arc::new(AtomicBool::new(false)))
41    }
42
43    pub fn with_cancellation(
44        execution: Rc<dyn RuntimeExecutionServices>,
45        cancellation: Arc<AtomicBool>,
46    ) -> Self {
47        let state = Rc::new(RuntimeContextState::new(cancellation));
48        crate::class_registry::register_context_state(&state);
49        Self {
50            execution,
51            program_revision: None,
52            search_path: None,
53            services: RuntimeServicePorts::default(),
54            state,
55        }
56    }
57
58    pub fn execution(&self) -> &Rc<dyn RuntimeExecutionServices> {
59        &self.execution
60    }
61
62    pub fn service_ports(&self) -> &RuntimeServicePorts {
63        &self.services
64    }
65
66    pub(crate) fn state(&self) -> &Rc<RuntimeContextState> {
67        &self.state
68    }
69
70    pub(super) fn state_identity(&self) -> *const RuntimeContextState {
71        Rc::as_ptr(&self.state)
72    }
73
74    pub fn cancellation(&self) -> Arc<AtomicBool> {
75        Arc::clone(&self.state.cancellation.borrow())
76    }
77
78    pub fn program_revision(&self) -> Option<&runmat_execution::ProgramRevision> {
79        self.program_revision.as_ref()
80    }
81
82    pub fn with_program_revision(
83        mut self,
84        revision: Option<runmat_execution::ProgramRevision>,
85    ) -> Self {
86        if self.program_revision != revision {
87            if let (Some(service), Some(previous)) =
88                (self.services.placement(), self.program_revision.clone())
89            {
90                service.invalidate(runmat_execution::PlacementInvalidation::Program {
91                    revision: previous,
92                });
93            }
94        }
95        self.program_revision = revision;
96        self
97    }
98
99    pub fn with_execution(mut self, execution: Rc<dyn RuntimeExecutionServices>) -> Self {
100        self.execution = execution;
101        self
102    }
103
104    pub fn with_search_path(
105        mut self,
106        search_path: Arc<crate::builtins::common::path_state::SearchPath>,
107    ) -> Self {
108        self.search_path = Some(search_path);
109        self
110    }
111
112    pub fn search_path(&self) -> Option<&Arc<crate::builtins::common::path_state::SearchPath>> {
113        self.search_path.as_ref()
114    }
115
116    pub fn set_dynamic_function_loader(
117        &self,
118        loader: Option<Arc<crate::user_functions::DynamicFunctionLoader>>,
119    ) {
120        self.state.call.borrow_mut().dynamic_loader = loader;
121    }
122
123    pub fn runmat_extensions_enabled(&self) -> bool {
124        self.state.runmat_extensions_enabled.get()
125    }
126
127    pub fn set_runmat_extensions_enabled(&self, enabled: bool) {
128        self.state.runmat_extensions_enabled.set(enabled);
129    }
130
131    pub fn language_mode(&self) -> RuntimeLanguageMode {
132        self.state.language_mode.get()
133    }
134
135    pub fn set_language_mode(&self, mode: RuntimeLanguageMode) {
136        self.state.language_mode.set(mode);
137    }
138
139    pub fn top_level_await_enabled(&self) -> bool {
140        self.state.top_level_await_enabled.get()
141    }
142
143    pub fn set_top_level_await_enabled(&self, enabled: bool) {
144        self.state.top_level_await_enabled.set(enabled);
145    }
146
147    pub fn dynamic_eval_enabled(&self) -> bool {
148        self.state.dynamic_eval_enabled.get()
149    }
150
151    pub fn set_dynamic_eval_enabled(&self, enabled: bool) {
152        self.state.dynamic_eval_enabled.set(enabled);
153    }
154
155    pub fn callstack_limit(&self) -> usize {
156        self.state.callstack_limit.get()
157    }
158
159    pub fn set_callstack_limit(&self, limit: usize) {
160        self.state.callstack_limit.set(limit);
161    }
162
163    pub fn error_namespace(&self) -> String {
164        self.state.error_namespace.borrow().clone()
165    }
166
167    pub fn set_error_namespace(&self, namespace: impl Into<String>) {
168        let namespace = namespace.into();
169        let namespace = if namespace.trim().is_empty() {
170            DEFAULT_ERROR_NAMESPACE.to_string()
171        } else {
172            namespace
173        };
174        *self.state.error_namespace.borrow_mut() = namespace;
175    }
176
177    pub fn with_service_ports(mut self, services: RuntimeServicePorts) -> Self {
178        self.services = services;
179        self
180    }
181
182    /// Scope every poll of `future` to this context. This is the supported
183    /// bridge for async code that still reaches ambient compatibility APIs.
184    pub fn scope<F: Future>(&self, future: F) -> ContextFuture<F> {
185        ContextFuture::new(self.clone(), future)
186    }
187
188    /// Activate this context for one synchronous executor or foreign-host
189    /// extent. Async code should use [`Self::scope`] so the context is removed
190    /// across yields.
191    pub fn enter(&self) -> RuntimeContextGuard {
192        RuntimeContextGuard::enter(self.clone())
193    }
194}