pros_simulator/
host.rs

1pub mod controllers;
2pub mod lcd;
3pub mod memory;
4pub mod multitasking;
5pub mod task;
6pub mod thread_local;
7
8use std::{alloc::Layout, sync::Arc, time::Instant};
9
10use async_trait::async_trait;
11use lcd::Lcd;
12use pros_simulator_interface::CompetitionPhase;
13use tokio::sync::{Mutex, MutexGuard};
14use wasmtime::{
15    AsContext, AsContextMut, Caller, Engine, Instance, Module, SharedMemory, TypedFunc,
16};
17
18use self::{
19    controllers::Controllers,
20    multitasking::MutexPool,
21    task::{TaskHandle, TaskPool},
22};
23use crate::interface::SimulatorInterface;
24
25/// This struct contains the functions necessary to send buffers to the sandbox.
26/// By letting the sandboxed allocator know that we want to write a buffer
27/// it can tell us where to put it without overriding anything important
28/// in the sandbox's heap.
29///
30/// `wasm_memalign` is used to request a place to write a buffer, and `wasm_free` is
31/// used to tell the sandbox that we're done with the buffer.
32#[derive(Clone)]
33pub struct WasmAllocator {
34    wasm_memalign: TypedFunc<(u32, u32), u32>,
35    wasm_free: TypedFunc<u32, ()>,
36}
37
38impl WasmAllocator {
39    pub fn new(mut store: impl AsContextMut, instance: &Instance) -> Self {
40        Self {
41            wasm_memalign: instance
42                .get_typed_func::<(u32, u32), u32>(&mut store, "wasm_memalign")
43                .unwrap(),
44            wasm_free: instance
45                .get_typed_func::<u32, ()>(&mut store, "wasm_free")
46                .unwrap(),
47        }
48    }
49
50    pub async fn memalign(
51        &self,
52        mut store: impl AsContextMut<Data = impl Send>,
53        layout: Layout,
54    ) -> u32 {
55        let size = layout.size().try_into().unwrap();
56        let alignment = layout.align().try_into().unwrap();
57        let ptr = self
58            .wasm_memalign
59            .call_async(&mut store, (alignment, size))
60            .await
61            .unwrap();
62        if ptr == 0 {
63            panic!("wasm_memalign failed");
64        }
65        ptr
66    }
67
68    pub async fn free(&self, mut store: impl AsContextMut<Data = impl Send>, ptr: u32) {
69        self.wasm_free.call_async(&mut store, ptr).await.unwrap()
70    }
71}
72
73#[derive(Clone)]
74pub struct Host {
75    memory: SharedMemory,
76    module: Module,
77    /// Interface for simulator output (e.g. log messages)
78    interface: SimulatorInterface,
79    lcd: Arc<Mutex<Lcd>>,
80    /// Pointers to mutexes created with mutex_create
81    mutexes: Arc<Mutex<MutexPool>>,
82    tasks: Arc<Mutex<TaskPool>>,
83    controllers: Arc<Mutex<Controllers>>,
84    competition_phase: Arc<Mutex<CompetitionPhase>>,
85    start_time: Instant,
86}
87
88impl Host {
89    pub fn new(
90        engine: Engine,
91        memory: SharedMemory,
92        interface: SimulatorInterface,
93        module: Module,
94    ) -> anyhow::Result<Self> {
95        let lcd = Lcd::new(interface.clone());
96        let mutexes = MutexPool::default();
97        let tasks = TaskPool::new(engine, memory.clone(), interface.clone())?;
98        let controllers = Controllers::new(None, None);
99
100        Ok(Self {
101            memory,
102            module,
103            interface,
104            lcd: Arc::new(Mutex::new(lcd)),
105            mutexes: Arc::new(Mutex::new(mutexes)),
106            tasks: Arc::new(Mutex::new(tasks)),
107            controllers: Arc::new(Mutex::new(controllers)),
108            competition_phase: Default::default(),
109            start_time: Instant::now(),
110        })
111    }
112}
113
114#[async_trait]
115pub trait HostCtx {
116    fn memory(&self) -> SharedMemory;
117    fn module(&self) -> Module;
118    fn interface(&self) -> SimulatorInterface;
119    fn lcd(&self) -> Arc<Mutex<Lcd>>;
120    async fn lcd_lock(&self) -> MutexGuard<'_, Lcd>;
121    fn mutexes(&self) -> Arc<Mutex<MutexPool>>;
122    async fn mutexes_lock(&self) -> MutexGuard<'_, MutexPool>;
123    fn tasks(&self) -> Arc<Mutex<TaskPool>>;
124    async fn tasks_lock(&self) -> MutexGuard<'_, TaskPool>;
125    fn start_time(&self) -> Instant;
126    async fn current_task(&self) -> TaskHandle;
127    fn controllers(&self) -> Arc<Mutex<Controllers>>;
128    async fn controllers_lock(&self) -> MutexGuard<'_, Controllers>;
129    fn competition_phase(&self) -> Arc<Mutex<CompetitionPhase>>;
130    async fn competition_phase_lock(&self) -> MutexGuard<'_, CompetitionPhase>;
131}
132
133#[async_trait]
134impl HostCtx for Host {
135    fn memory(&self) -> SharedMemory {
136        self.memory.clone()
137    }
138
139    fn module(&self) -> Module {
140        self.module.clone()
141    }
142
143    fn interface(&self) -> SimulatorInterface {
144        self.interface.clone()
145    }
146
147    fn lcd(&self) -> Arc<Mutex<Lcd>> {
148        self.lcd.clone()
149    }
150
151    async fn lcd_lock(&self) -> MutexGuard<'_, Lcd> {
152        self.lcd.lock().await
153    }
154
155    fn mutexes(&self) -> Arc<Mutex<MutexPool>> {
156        self.mutexes.clone()
157    }
158
159    async fn mutexes_lock(&self) -> MutexGuard<'_, MutexPool> {
160        self.mutexes.lock().await
161    }
162
163    fn tasks(&self) -> Arc<Mutex<TaskPool>> {
164        self.tasks.clone()
165    }
166
167    async fn tasks_lock(&self) -> MutexGuard<'_, TaskPool> {
168        self.tasks.lock().await
169    }
170
171    fn start_time(&self) -> Instant {
172        self.start_time
173    }
174
175    async fn current_task(&self) -> TaskHandle {
176        self.tasks.lock().await.current()
177    }
178
179    fn controllers(&self) -> Arc<Mutex<Controllers>> {
180        self.controllers.clone()
181    }
182
183    async fn controllers_lock(&self) -> MutexGuard<'_, Controllers> {
184        self.controllers.lock().await
185    }
186
187    fn competition_phase(&self) -> Arc<Mutex<CompetitionPhase>> {
188        self.competition_phase.clone()
189    }
190
191    async fn competition_phase_lock(&self) -> MutexGuard<'_, CompetitionPhase> {
192        self.competition_phase.lock().await
193    }
194}
195
196#[async_trait]
197impl<T> HostCtx for T
198where
199    T: AsContext<Data = Host> + Sync,
200{
201    fn memory(&self) -> SharedMemory {
202        self.as_context().data().memory()
203    }
204
205    fn module(&self) -> Module {
206        self.as_context().data().module()
207    }
208
209    fn interface(&self) -> SimulatorInterface {
210        self.as_context().data().interface()
211    }
212
213    fn lcd(&self) -> Arc<Mutex<Lcd>> {
214        self.as_context().data().lcd()
215    }
216
217    async fn lcd_lock(&self) -> MutexGuard<'_, Lcd> {
218        self.as_context().data().lcd_lock().await
219    }
220
221    fn mutexes(&self) -> Arc<Mutex<MutexPool>> {
222        self.as_context().data().mutexes()
223    }
224
225    async fn mutexes_lock(&self) -> MutexGuard<'_, MutexPool> {
226        self.as_context().data().mutexes_lock().await
227    }
228
229    fn tasks(&self) -> Arc<Mutex<TaskPool>> {
230        self.as_context().data().tasks()
231    }
232
233    async fn tasks_lock(&self) -> MutexGuard<'_, TaskPool> {
234        self.as_context().data().tasks_lock().await
235    }
236
237    fn start_time(&self) -> Instant {
238        self.as_context().data().start_time()
239    }
240
241    async fn current_task(&self) -> TaskHandle {
242        self.as_context().data().tasks_lock().await.current()
243    }
244
245    fn controllers(&self) -> Arc<Mutex<Controllers>> {
246        self.as_context().data().controllers()
247    }
248
249    async fn controllers_lock(&self) -> MutexGuard<'_, Controllers> {
250        self.as_context().data().controllers_lock().await
251    }
252
253    fn competition_phase(&self) -> Arc<Mutex<CompetitionPhase>> {
254        self.as_context().data().competition_phase()
255    }
256
257    async fn competition_phase_lock(&self) -> MutexGuard<'_, CompetitionPhase> {
258        self.as_context().data().competition_phase_lock().await
259    }
260}
261
262#[async_trait]
263pub trait ResultExt<T> {
264    /// If this result is an error, sets the simulator's [`errno`](Host::errno_address) to the Err value.
265    /// Returns `true` if the result was Ok and `false` if it was Err.
266    ///
267    /// # Example
268    ///
269    /// ```ignore
270    /// let res = lcd.set_line(line, "");
271    /// Ok(res.unwrap_or_errno(&mut caller).await.into())
272    /// ```
273    async fn unwrap_or_errno(self, caller: &mut Caller<'_, Host>) -> bool;
274
275    /// If this result is an error, sets the simulator's [`errno`](Host::errno_address) to the Err value.
276    /// Returns the `T` value if the result was Ok and the `error_value` parameter if it was Err.
277    ///
278    /// # Example
279    ///
280    /// ```ignore
281    /// let res = controllers.get_analog(pros_sys::E_CONTROLLER_MASTER);
282    /// Ok(res.unwrap_or_errno_as(&mut caller, 0).await)
283    /// ```
284    async fn unwrap_or_errno_as(self, caller: &mut Caller<'_, Host>, error_value: T) -> T;
285}
286
287#[async_trait]
288impl<T: Send> ResultExt<T> for Result<T, i32> {
289    async fn unwrap_or_errno(self, caller: &mut Caller<'_, Host>) -> bool {
290        if let Err(code) = self {
291            let current_task = caller.data().tasks_lock().await.current();
292            let memory = caller.data().memory();
293            let errno = current_task.lock().await.errno(&mut *caller).await;
294            errno.set(&memory, code);
295        }
296        self.is_ok()
297    }
298
299    async fn unwrap_or_errno_as(self, caller: &mut Caller<'_, Host>, error_value: T) -> T {
300        match self {
301            Err(code) => {
302                let current_task = caller.data().tasks_lock().await.current();
303                let memory = caller.data().memory();
304                let errno = current_task.lock().await.errno(&mut *caller).await;
305                errno.set(&memory, code);
306                error_value
307            }
308            Ok(value) => value,
309        }
310    }
311}