Skip to main content

moirai_pal/
lib.rs

1//! Platform Abstraction Layer (PAL) for Moirai async I/O operations.
2//!
3//! This module provides platform-specific implementations of async I/O primitives
4//! that enable true non-blocking operations without external runtime dependencies.
5//!
6//! ## Architecture
7//!
8//! The PAL provides a unified interface across platforms while leveraging
9//! optimal platform-specific async I/O mechanisms:
10//!
11//! - **Linux**: epoll-based event notification
12//! - **macOS/BSD**: kqueue-based event notification
13//! - **Windows**: `WSAPoll`-based socket readiness polling
14//! - **WebAssembly**: Web APIs with JavaScript interop
15//!
16//! The Windows PAL can expose a thread-affine WebView2 host for packaged
17//! HTML/CSS/WebAssembly surfaces when the opt-in `webview2` feature is enabled.
18//! Its URI, message and wait bounds are owned by the Rust provider; the
19//! installed WebView2 runtime remains a system prerequisite.
20//!
21//! ## Design Principles
22//!
23//! - **Zero External Dependencies**: Only platform system libraries
24//! - **Zero-Copy Operations**: Direct buffer management
25//! - **Sub-microsecond Latency**: Optimized for performance-critical applications
26//! - **Memory Efficiency**: Minimal per-operation overhead
27//! - **Cross-Platform Consistency**: Uniform behavior across all targets
28
29#![cfg_attr(nightly_tls_active, feature(thread_local))]
30#![deny(missing_docs)]
31
32pub mod fs;
33#[cfg(any(unix, windows))]
34pub mod instance;
35pub mod net;
36pub mod reactor;
37pub mod thread;
38pub mod timer;
39
40mod frame;
41
42#[cfg(unix)]
43pub mod unix;
44
45#[cfg(windows)]
46pub mod windows;
47
48#[cfg(target_arch = "wasm32")]
49pub mod wasm;
50
51#[cfg(any(target_arch = "wasm32", test))]
52#[path = "wasm/drop_validation.rs"]
53mod drop_validation;
54
55#[cfg(any(target_arch = "wasm32", test))]
56#[path = "wasm/file_policy.rs"]
57mod file_policy;
58
59#[cfg(any(target_arch = "wasm32", test))]
60#[path = "wasm/text_validation.rs"]
61mod text_validation;
62
63#[cfg(any(target_arch = "wasm32", test))]
64#[path = "wasm/text_model.rs"]
65mod text_model;
66
67#[cfg(any(target_arch = "wasm32", test))]
68#[path = "wasm/canvas_validation.rs"]
69mod canvas_validation;
70
71#[cfg(any(target_arch = "wasm32", test))]
72#[path = "wasm/gpu_device_loss.rs"]
73mod gpu_device_loss;
74
75#[cfg(any(target_arch = "wasm32", test))]
76#[path = "wasm/content_box.rs"]
77mod content_box;
78
79#[cfg(any(target_arch = "wasm32", test))]
80#[path = "wasm/history_path.rs"]
81mod history_path;
82#[cfg(any(target_arch = "wasm32", test))]
83#[path = "wasm/key_validation.rs"]
84mod key_validation;
85
86#[cfg(any(target_arch = "wasm32", test))]
87mod local_task;
88
89#[cfg(any(target_arch = "wasm32", test))]
90#[path = "websocket_state.rs"]
91mod websocket_state;
92
93use std::io;
94
95macro_rules! reactor_trait_items {
96    () => {
97        /// Register a file descriptor/handle for async operations.
98        fn register_fd(&self, fd: RawFd, interest: Interest) -> io::Result<()>;
99
100        /// Unregister a file descriptor/handle.
101        fn unregister_fd(&self, fd: RawFd) -> io::Result<()>;
102
103        /// Poll for ready events with timeout.
104        fn poll_events(&self, timeout: Option<std::time::Duration>) -> io::Result<Vec<Event>>;
105
106        /// Wake up the reactor from blocking poll.
107        fn wake(&self) -> io::Result<()>;
108    };
109}
110
111/// Platform-specific reactor interface.
112///
113/// Native reactors are `Send + Sync` because their descriptors and wake
114/// handles may be driven from a dedicated thread. Browser reactors stay on
115/// the JavaScript event-loop thread, where Web API callback handles are not
116/// transferable across workers.
117#[cfg(not(target_arch = "wasm32"))]
118pub trait Reactor: Send + Sync + 'static {
119    reactor_trait_items!();
120}
121
122/// Browser-thread reactor interface.
123#[cfg(target_arch = "wasm32")]
124pub trait Reactor: 'static {
125    reactor_trait_items!();
126}
127
128/// Platform-agnostic file descriptor/handle type.
129#[cfg(unix)]
130pub type RawFd = std::os::unix::io::RawFd;
131
132/// Platform-agnostic file descriptor/handle type.
133#[cfg(windows)]
134pub type RawFd = std::os::windows::io::RawHandle;
135
136/// Platform-agnostic file descriptor/handle type.
137#[cfg(target_arch = "wasm32")]
138pub type RawFd = u32;
139
140/// Platform-agnostic file descriptor/handle type.
141#[cfg(not(any(unix, windows, target_arch = "wasm32")))]
142pub type RawFd = usize;
143
144/// Reactor implementation selected by the compile target.
145#[cfg(target_os = "linux")]
146pub type PlatformReactor = unix::epoll::EpollReactor;
147
148/// Reactor implementation selected by the compile target.
149#[cfg(any(
150    target_os = "macos",
151    target_os = "freebsd",
152    target_os = "openbsd",
153    target_os = "netbsd"
154))]
155pub type PlatformReactor = unix::kqueue::KqueueReactor;
156
157/// Reactor implementation selected by the compile target.
158#[cfg(windows)]
159pub type PlatformReactor = windows::poll::WsaPollReactor;
160
161/// Reactor implementation selected by the compile target.
162#[cfg(target_arch = "wasm32")]
163pub type PlatformReactor = wasm::WebReactor;
164
165/// Reactor implementation selected by the compile target (unsupported
166/// platforms get a stand-in whose operations return `Unsupported`).
167#[cfg(not(any(
168    target_os = "linux",
169    target_os = "macos",
170    target_os = "freebsd",
171    target_os = "openbsd",
172    target_os = "netbsd",
173    windows,
174    target_arch = "wasm32"
175)))]
176pub struct PlatformReactor;
177
178/// I/O event interest specification.
179#[derive(Debug, Clone, Copy)]
180pub struct Interest {
181    /// Wake on read readiness.
182    pub readable: bool,
183    /// Wake on write readiness.
184    pub writable: bool,
185    /// Wake on error conditions.
186    pub error: bool,
187}
188
189impl Interest {
190    /// Read readiness (plus errors).
191    pub const READABLE: Self = Self {
192        readable: true,
193        writable: false,
194        error: true,
195    };
196    /// Write readiness (plus errors).
197    pub const WRITABLE: Self = Self {
198        readable: false,
199        writable: true,
200        error: true,
201    };
202    /// Read and write readiness (plus errors).
203    pub const READ_WRITE: Self = Self {
204        readable: true,
205        writable: true,
206        error: true,
207    };
208}
209
210/// I/O event notification.
211#[derive(Debug, Clone)]
212pub struct Event {
213    /// File descriptor/handle the event applies to.
214    pub fd: RawFd,
215    /// Read readiness was reported.
216    pub readable: bool,
217    /// Write readiness was reported.
218    pub writable: bool,
219    /// An error condition was reported.
220    pub error: bool,
221    /// The peer hung up.
222    pub hangup: bool,
223}
224
225#[cfg(not(any(
226    target_os = "linux",
227    target_os = "macos",
228    target_os = "freebsd",
229    target_os = "openbsd",
230    target_os = "netbsd",
231    windows,
232    target_arch = "wasm32"
233)))]
234impl Reactor for PlatformReactor {
235    fn register_fd(&self, _fd: RawFd, _interest: Interest) -> io::Result<()> {
236        Err(unsupported_reactor_error())
237    }
238
239    fn unregister_fd(&self, _fd: RawFd) -> io::Result<()> {
240        Err(unsupported_reactor_error())
241    }
242
243    fn poll_events(&self, _timeout: Option<std::time::Duration>) -> io::Result<Vec<Event>> {
244        Err(unsupported_reactor_error())
245    }
246
247    fn wake(&self) -> io::Result<()> {
248        Err(unsupported_reactor_error())
249    }
250}
251
252#[cfg(not(any(
253    target_os = "linux",
254    target_os = "macos",
255    target_os = "freebsd",
256    target_os = "openbsd",
257    target_os = "netbsd",
258    windows,
259    target_arch = "wasm32"
260)))]
261fn unsupported_reactor_error() -> io::Error {
262    io::Error::new(
263        io::ErrorKind::Unsupported,
264        "Platform not supported for native async I/O",
265    )
266}
267
268/// Platform-specific reactor factory.
269pub fn create_reactor() -> io::Result<PlatformReactor> {
270    #[cfg(target_os = "linux")]
271    return unix::epoll::EpollReactor::new();
272
273    #[cfg(any(
274        target_os = "macos",
275        target_os = "freebsd",
276        target_os = "openbsd",
277        target_os = "netbsd"
278    ))]
279    return unix::kqueue::KqueueReactor::new();
280
281    #[cfg(windows)]
282    return windows::poll::WsaPollReactor::new();
283
284    #[cfg(target_arch = "wasm32")]
285    return wasm::WebReactor::new();
286
287    #[cfg(not(any(
288        target_os = "linux",
289        target_os = "macos",
290        target_os = "freebsd",
291        target_os = "openbsd",
292        target_os = "netbsd",
293        windows,
294        target_arch = "wasm32"
295    )))]
296    return Err(io::Error::new(
297        io::ErrorKind::Unsupported,
298        "Platform not supported for native async I/O",
299    ));
300}
301
302#[cfg(test)]
303mod tests {
304    use super::*;
305
306    #[test]
307    fn test_interest_flags() {
308        let read_only = Interest::READABLE;
309        assert!(read_only.readable);
310        assert!(!read_only.writable);
311        assert!(read_only.error);
312
313        let write_only = Interest::WRITABLE;
314        assert!(!write_only.readable);
315        assert!(write_only.writable);
316        assert!(write_only.error);
317
318        let read_write = Interest::READ_WRITE;
319        assert!(read_write.readable);
320        assert!(read_write.writable);
321        assert!(read_write.error);
322    }
323}