use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex, RwLock, MutexGuard};
use std::thread::{JoinHandle, self};
use std::time::{Duration, Instant};
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering};
use std::io::Write;
use std::io::Read;
#[derive(Debug, Clone)]
pub struct ThreadPoolConfig {
pub max_threads: usize,
pub min_threads: usize,
pub idle_timeout: Duration,
pub stack_size: usize,
}
impl Default for ThreadPoolConfig {
fn default() -> Self {
let cpus: _ = num_cpus::get();
Self {
max_threads: cpus,
min_threads: 1,
idle_timeout: Duration::from_secs(30),
stack_size: 2 * 1024 * 1024, }
}
}
#[derive(Debug, Clone, Default)]
pub struct ThreadStats {
pub max_threads: usize,
pub active_threads: usize,
pub idle_threads: usize,
pub total_tasks: u64,
pub completed_tasks: u64,
pub avg_execution_time: Duration,
}
pub struct WasmThreadHandle<T> {
inner: Option<JoinHandle<T>>,
cancelled: Arc<AtomicBool>,
}
impl<T> WasmThreadHandle<T> {
pub fn join(mut self) -> Result<T, String> {
match self.inner.take() {
Some(handle) => handle.join().map_err(|_| "Thread panicked".to_string()),
None => Err("Handle already consumed".to_string()),
}
}
pub fn join_timeout(self, _timeout: Duration) -> Result<T, String> {
if self.cancelled.load(Ordering::SeqCst) {
return Err("Task was cancelled".to_string());
}
Err("Timeout".to_string())
}
pub fn cancel(&self) -> bool {
self.cancelled.store(true, Ordering::SeqCst);
true
}
}
pub struct CancellableHandle<T> {
inner: WasmThreadHandle<T>,
}
impl<T> CancellableHandle<T> {
pub fn cancel(&self) -> bool {
self.inner.cancel()
}
}
pub struct SharedMemoryRegion {
data: Arc<RwLock<Vec<u8>>>,
size: usize,
alignment: usize,
}
impl SharedMemoryRegion {
pub fn new(size: usize) -> Self {
let page_size: _ = 4096;
let aligned_size: _ = ((size + page_size - 1) / page_size) * page_size;
Self {
data: Arc::new(Mutex::new(vec![0u8; aligned_size])),
size: aligned_size,
alignment: page_size,
}
}
pub fn size(&self) -> usize {
self.size
}
pub fn alignment(&self) -> usize {
self.alignment
}
pub fn is_valid(&self) -> bool {
self.size > 0
}
pub fn as_ptr(&self) -> *const u8 {
self.data.read().unwrap().as_ptr()
}
pub fn write(&self, offset: usize, data: &[u8]) -> Result<(), String> {
if offset + data.len() > self.size {
return Err("Write out of bounds".to_string());
}
let mut guard = self.data.write().map_err(|_| "Lock poisoned")?;
guard[offset..offset + data.len()].copy_from_slice(data);
Ok(())
}
pub fn read(&self, offset: usize, buffer: &mut [u8]) -> Result<(), String> {
if offset + buffer.len() > self.size {
return Err("Read out of bounds".to_string());
}
let guard: _ = self.data.read().map_err(|_| "Lock poisoned")?;
buffer.copy_from_slice(&guard[offset..offset + buffer.len()]);
Ok(())
}
}
unsafe impl Send for SharedMemoryRegion {}
unsafe impl Sync for SharedMemoryRegion {}
pub struct WasmMutex<T> {
inner: Mutex<T>,
locked: AtomicBool,
}
pub struct WasmMutexGuard<'a, T> {
guard: MutexGuard<'a, T>,
locked: &'a AtomicBool,
}
impl<T> WasmMutex<T> {
pub fn new(value: T) -> Self {
Self {
inner: Mutex::new(value),
locked: AtomicBool::new(false),
}
}
pub fn lock(&self) -> Result<WasmMutexGuard<'_, T>, String> {
let guard: _ = self.inner.lock().map_err(|_| "Lock poisoned")?;
self.locked.store(true, Ordering::SeqCst);
Ok(WasmMutexGuard {
guard,
locked: &self.locked,
})
}
pub fn try_lock(&self) -> Option<WasmMutexGuard<'_, T>> {
match self.inner.try_lock() {
Ok(guard) => {
self.locked.store(true, Ordering::SeqCst);
Some(WasmMutexGuard {
guard,
locked: &self.locked,
})
}
Err(_) => None,
}
}
pub fn is_locked(&self) -> bool {
self.locked.load(Ordering::SeqCst)
}
}
impl<'a, T> std::ops::Deref for WasmMutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.guard
}
}
impl<'a, T> std::ops::DerefMut for WasmMutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.guard
}
}
impl<'a, T> Drop for WasmMutexGuard<'a, T> {
fn drop(&mut self) {
self.locked.store(false, Ordering::SeqCst);
}
}
pub struct WasmAtomic {
value: AtomicI32,
}
impl WasmAtomic {
pub fn new(value: i32) -> Self {
Self {
value: AtomicI32::new(value),
}
}
pub fn load(&self) -> i32 {
self.value.load(Ordering::SeqCst)
}
pub fn store(&self, value: i32) {
self.value.store(value, Ordering::SeqCst);
}
pub fn fetch_add(&self, val: i32) -> i32 {
self.value.fetch_add(val, Ordering::SeqCst)
}
pub fn compare_and_swap(&self, current: i32, new: i32) -> i32 {
match self
.value
.compare_exchange(current, new, Ordering::SeqCst, Ordering::SeqCst)
{
Ok(v) => v,
Err(v) => v,
}
}
}
pub struct WasmThreadsManager {
config: ThreadPoolConfig,
initialized: bool,
shutdown: Arc<AtomicBool>,
stats: Arc<ManagerStats>,
thread_local_storage: RwLock<HashMap<String, Box<dyn std::any::Any + Send + Sync>>>,
}
struct ManagerStats {
total_tasks: AtomicU64,
completed_tasks: AtomicU64,
total_execution_time_ns: AtomicU64,
active_threads: AtomicU64,
}
impl ManagerStats {
fn new() -> Self {
Self {
total_tasks: AtomicU64::new(0),
completed_tasks: AtomicU64::new(0),
total_execution_time_ns: AtomicU64::new(0),
active_threads: AtomicU64::new(0),
}
}
}
impl WasmThreadsManager {
pub fn new(config: ThreadPoolConfig) -> Self {
Self {
config,
initialized: true,
shutdown: Arc::new(Mutex::new(AtomicBool::new(false))),
stats: Arc::new(Mutex::new(ManagerStats::new())),
thread_local_storage: RwLock::new(HashMap::new()),
}
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
pub fn get_config(&self) -> ThreadPoolConfig {
self.config.clone()
}
pub fn get_stats(&self) -> ThreadStats {
let total: _ = self.stats.total_tasks.load(Ordering::Relaxed);
let completed: _ = self.stats.completed_tasks.load(Ordering::Relaxed);
let total_time_ns: _ = self.stats.total_execution_time_ns.load(Ordering::Relaxed);
let active: _ = self.stats.active_threads.load(Ordering::Relaxed) as usize;
let avg_time: _ = if completed > 0 {
Duration::from_nanos(total_time_ns / completed)
} else {
Duration::default()
};
ThreadStats {
max_threads: self.config.max_threads,
active_threads: active,
idle_threads: self.config.max_threads.saturating_sub(active),
total_tasks: total,
completed_tasks: completed,
avg_execution_time: avg_time,
}
}
pub fn spawn<F, T>(&self, f: F) -> Result<WasmThreadHandle<T>, String>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
if self.shutdown.load(Ordering::SeqCst) {
return Err("Thread pool is shutdown".to_string());
}
self.stats.total_tasks.fetch_add(1, Ordering::Relaxed);
self.stats.active_threads.fetch_add(1, Ordering::Relaxed);
let cancelled: _ = Arc::new(Mutex::new(AtomicBool::new(false)));
let cancelled_clone: _ = cancelled.clone();
let stats: _ = self.stats.clone();
let handle: _ = thread::spawn(move || {
let start: _ = Instant::now();
let result: _ = f();
let elapsed: _ = start.elapsed();
stats.completed_tasks.fetch_add(1, Ordering::Relaxed);
stats.active_threads.fetch_sub(1, Ordering::Relaxed);
stats.total_execution_time_ns.fetch_add(elapsed.as_nanos() as u64, Ordering::Relaxed);
result
});
Ok(WasmThreadHandle {
inner: Some(handle),
cancelled: cancelled_clone,
})
}
pub fn spawn_cancellable<F, T>(&self, f: F) -> Result<CancellableHandle<T>, String>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
let handle: _ = self.spawn(f)?;
Ok(CancellableHandle { inner: handle })
}
pub fn create_shared_memory(&self, size: usize) -> Result<SharedMemoryRegion, String> {
Ok(SharedMemoryRegion::new(size))
}
pub fn shutdown(&self) {
self.shutdown.store(true, Ordering::SeqCst);
}
pub fn set_thread_local<T: Send + Sync + 'static>(&self, key: &str, value: T) {
let mut storage = self.thread_local_storage.write().unwrap();
storage.insert(key.to_string(), Box::new(value));
}
pub fn get_thread_local<T: Clone + 'static>(&self, key: &str) -> Option<T> {
let storage: _ = self.thread_local_storage.read().unwrap();
storage
.get(key)
.and_then(|v| v.downcast_ref::<T>())
.cloned()
}
}
impl Default for WasmThreadsManager {
fn default() -> Self {
Self::new(ThreadPoolConfig::default())
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_manager_creation() {
let manager: _ = WasmThreadsManager::new(ThreadPoolConfig::default());
assert!(manager.is_initialized());
}
#[test]
fn test_simple_task() {
let manager: _ = WasmThreadsManager::new(ThreadPoolConfig::default());
let handle: _ = manager.spawn(|| 42).unwrap();
let result: _ = handle.join().unwrap();
assert_eq!(result, 42);
}
#[test]
fn test_shared_memory() {
let manager: _ = WasmThreadsManager::new(ThreadPoolConfig::default());
let region: _ = manager.create_shared_memory(1024).unwrap();
assert_eq!(region.size(), 4096); region.write(0, &[1, 2, 3, 4]).unwrap();
let mut buf = [0u8; 4];
region.read(0, &mut buf).unwrap();
assert_eq!(buf, [1, 2, 3, 4]);
}
#[test]
fn test_mutex() {
let mutex: _ = WasmMutex::new(0);
{
let mut guard = mutex.lock().unwrap();
*guard = 42;
assert!(mutex.is_locked());
}
assert!(!mutex.is_locked());
assert_eq!(*mutex.lock().unwrap(), 42);
}
#[test]
fn test_atomic() {
let atomic: _ = WasmAtomic::new(0);
atomic.store(42);
assert_eq!(atomic.load(), 42);
assert_eq!(atomic.fetch_add(8), 42);
assert_eq!(atomic.load(), 50);
}
}