portalis_transpiler/wasi_threading/
mod.rs1use anyhow::Result;
57use std::time::Duration;
58
59pub mod thread;
61pub mod sync;
62pub mod collections;
63pub mod pool;
64
65#[cfg(not(target_arch = "wasm32"))]
67pub(crate) mod native;
68
69#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
70pub(crate) mod browser;
71
72#[cfg(all(target_arch = "wasm32", feature = "wasi"))]
73pub(crate) mod wasi_impl;
74
75pub use thread::{
77 WasiThread, ThreadConfig, ThreadHandle, ThreadId, ThreadPriority,
78 thread_sleep, thread_yield, thread_park, thread_unpark,
79};
80
81pub use sync::{
82 WasiMutex, WasiRwLock, WasiSemaphore, WasiCondvar, WasiBarrier, WasiEvent,
83 MutexGuard, RwLockReadGuard, RwLockWriteGuard,
84};
85
86pub use collections::{
87 WasiQueue, WasiStack, WasiPriorityQueue, WasiDeque,
88};
89
90pub use pool::{
91 ThreadPool, ThreadPoolConfig, ThreadPoolBuilder, WorkResult,
92};
93
94#[derive(Debug, thiserror::Error)]
96pub enum ThreadingError {
97 #[error("Thread spawn error: {0}")]
98 Spawn(String),
99
100 #[error("Thread join error: {0}")]
101 Join(String),
102
103 #[error("Lock poisoned: {0}")]
104 Poisoned(String),
105
106 #[error("Deadlock detected: {0}")]
107 Deadlock(String),
108
109 #[error("Timeout error: {0}")]
110 Timeout(String),
111
112 #[error("Channel closed: {0}")]
113 ChannelClosed(String),
114
115 #[error("Thread panic: {0}")]
116 Panic(String),
117
118 #[error("Invalid operation: {0}")]
119 InvalidOperation(String),
120
121 #[error("Platform not supported: {0}")]
122 PlatformNotSupported(String),
123
124 #[error("Resource exhausted: {0}")]
125 ResourceExhausted(String),
126}
127
128#[cfg(not(target_arch = "wasm32"))]
130pub struct ThreadLocal<T: Send + 'static> {
131 #[allow(dead_code)]
132 inner: std::thread::LocalKey<std::cell::RefCell<Option<T>>>,
133}
134
135pub struct ThreadBuilder {
137 name: Option<String>,
138 stack_size: Option<usize>,
139 priority: ThreadPriority,
140}
141
142impl ThreadBuilder {
143 pub fn new() -> Self {
145 Self {
146 name: None,
147 stack_size: None,
148 priority: ThreadPriority::Normal,
149 }
150 }
151
152 pub fn name(mut self, name: impl Into<String>) -> Self {
154 self.name = Some(name.into());
155 self
156 }
157
158 pub fn stack_size(mut self, size: usize) -> Self {
160 self.stack_size = Some(size);
161 self
162 }
163
164 pub fn priority(mut self, priority: ThreadPriority) -> Self {
166 self.priority = priority;
167 self
168 }
169
170 pub fn spawn<F, T>(self, f: F) -> Result<ThreadHandle<T>>
172 where
173 F: FnOnce() -> T + Send + 'static,
174 T: Send + 'static,
175 {
176 let config = ThreadConfig {
177 name: self.name,
178 stack_size: self.stack_size,
179 priority: self.priority,
180 };
181 WasiThread::spawn_with_config(f, config)
182 }
183}
184
185impl Default for ThreadBuilder {
186 fn default() -> Self {
187 Self::new()
188 }
189}
190
191#[cfg(not(target_arch = "wasm32"))]
193pub fn active_thread_count() -> usize {
194 #[cfg(target_os = "linux")]
196 {
197 std::fs::read_dir(format!("/proc/{}/task", std::process::id()))
199 .ok()
200 .and_then(|entries| Some(entries.count()))
201 .unwrap_or(1)
202 }
203 #[cfg(not(target_os = "linux"))]
204 {
205 1
207 }
208}
209
210pub fn current_thread_id() -> ThreadId {
212 thread::current_thread_id()
213}
214
215pub fn sleep(duration: Duration) {
217 thread_sleep(duration)
218}
219
220pub fn yield_now() {
222 thread_yield()
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[test]
230 fn test_thread_builder() {
231 let builder = ThreadBuilder::new()
232 .name("test-thread")
233 .stack_size(1024 * 1024)
234 .priority(ThreadPriority::Normal);
235
236 assert_eq!(builder.name.as_ref().unwrap(), "test-thread");
237 assert_eq!(builder.stack_size, Some(1024 * 1024));
238 }
239
240 #[test]
241 #[cfg(not(target_arch = "wasm32"))]
242 fn test_current_thread_id() {
243 let id1 = current_thread_id();
244 let id2 = current_thread_id();
245 assert_eq!(id1, id2);
246 }
247
248 #[test]
249 fn test_sleep() {
250 let start = std::time::Instant::now();
251 sleep(Duration::from_millis(10));
252 let elapsed = start.elapsed();
253 assert!(elapsed >= Duration::from_millis(10));
254 }
255}