diff --git a/examples/threads/threads.txs b/examples/threads/threads.txs index 7396bb40..0e359ff3 100644 --- a/examples/threads/threads.txs +++ b/examples/threads/threads.txs @@ -6,12 +6,12 @@ spawn_fn = thread["spawn"] join_fn = thread["join"] do task_a() - time.sleep(100) + time.sleep(150) say "Task A completed on worker thread." end do task_b() - time.sleep(50) + time.sleep(20) say "Task B completed on worker thread." end diff --git a/stdlib/src/sync.rs b/stdlib/src/sync.rs index 36ec88f4..368518cf 100644 --- a/stdlib/src/sync.rs +++ b/stdlib/src/sync.rs @@ -4,7 +4,7 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use std::sync::{Condvar, Mutex}; -use techscript_runtime::{error::RuntimeError, value::RuntimeValue}; +use techscript_runtime::{error::RuntimeError, value::RuntimeValue, RuntimeContext}; pub struct ScriptMutex { locked: Mutex, @@ -34,6 +34,58 @@ impl ScriptMutex { } } +fn sync_make_mutex( + ctx: &mut RuntimeContext, + _args: Vec, +) -> Result { + let mutex = ScriptMutex::new(); + let handle_id = ctx.resources.borrow_mut().insert(mutex); + let mut map = IndexMap::new(); + map.insert("_handle".to_string(), RuntimeValue::Int(handle_id as i64)); + Ok(RuntimeValue::Map { + entries: Rc::new(RefCell::new(map)), + is_const: false, + }) +} + +fn sync_mutex_lock( + ctx: &mut RuntimeContext, + args: Vec, +) -> Result { + if let RuntimeValue::Map { entries, .. } = &args[0] { + let handle_id = entries + .borrow() + .get("_handle") + .cloned() + .unwrap_or(RuntimeValue::Null) + .try_into_int()? as u32; + let resources = ctx.resources.borrow(); + if let Some(mutex) = resources.get::(handle_id) { + mutex.lock(); + } + } + Ok(RuntimeValue::Null) +} + +fn sync_mutex_unlock( + ctx: &mut RuntimeContext, + args: Vec, +) -> Result { + if let RuntimeValue::Map { entries, .. } = &args[0] { + let handle_id = entries + .borrow() + .get("_handle") + .cloned() + .unwrap_or(RuntimeValue::Null) + .try_into_int()? as u32; + let resources = ctx.resources.borrow(); + if let Some(mutex) = resources.get::(handle_id) { + mutex.unlock(); + } + } + Ok(RuntimeValue::Null) +} + impl StdlibRegistry { pub fn register_sync(&mut self) { let mut exports: HashMap> = @@ -44,16 +96,7 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "make_mutex".to_string(), arity: 0, - callback: |ctx, _args| { - let mutex = ScriptMutex::new(); - let handle_id = ctx.resources.borrow_mut().insert(mutex); - let mut map = IndexMap::new(); - map.insert("_handle".to_string(), RuntimeValue::Int(handle_id as i64)); - Ok(RuntimeValue::Map { - entries: Rc::new(RefCell::new(map)), - is_const: false, - }) - }, + callback: sync_make_mutex, }), ); @@ -62,21 +105,7 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "mutex_lock".to_string(), arity: 1, - callback: |ctx, args| { - if let RuntimeValue::Map { entries, .. } = &args[0] { - let handle_id = entries - .borrow() - .get("_handle") - .cloned() - .unwrap_or(RuntimeValue::Null) - .try_into_int()? as u32; - let resources = ctx.resources.borrow(); - if let Some(mutex) = resources.get::(handle_id) { - mutex.lock(); - } - } - Ok(RuntimeValue::Null) - }, + callback: sync_mutex_lock, }), ); @@ -85,21 +114,7 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "mutex_unlock".to_string(), arity: 1, - callback: |ctx, args| { - if let RuntimeValue::Map { entries, .. } = &args[0] { - let handle_id = entries - .borrow() - .get("_handle") - .cloned() - .unwrap_or(RuntimeValue::Null) - .try_into_int()? as u32; - let resources = ctx.resources.borrow(); - if let Some(mutex) = resources.get::(handle_id) { - mutex.unlock(); - } - } - Ok(RuntimeValue::Null) - }, + callback: sync_mutex_unlock, }), ); @@ -118,6 +133,36 @@ impl StdlibRegistry { #[cfg(test)] mod tests { use super::*; + use techscript_runtime::context::RuntimeConfig; + + #[test] + fn test_register_sync() { + let mut registry = StdlibRegistry::new(); + registry.register_sync(); + + let module = registry.get_module("std.sync").unwrap(); + + let make_mutex = module.exports.get("make_mutex").unwrap(); + assert_eq!(make_mutex.name(), "make_mutex"); + assert_eq!(make_mutex.arity(), 0); + + let mutex_lock = module.exports.get("mutex_lock").unwrap(); + assert_eq!(mutex_lock.name(), "mutex_lock"); + assert_eq!(mutex_lock.arity(), 1); + + let mutex_unlock = module.exports.get("mutex_unlock").unwrap(); + assert_eq!(mutex_unlock.name(), "mutex_unlock"); + assert_eq!(mutex_unlock.arity(), 1); + + let mut ctx = RuntimeContext::new(RuntimeConfig::default()); + let mutex_val = make_mutex.call(&mut ctx, vec![]).unwrap(); + + let lock_res = mutex_lock.call(&mut ctx, vec![mutex_val.clone()]); + assert!(lock_res.is_ok()); + + let unlock_res = mutex_unlock.call(&mut ctx, vec![mutex_val]); + assert!(unlock_res.is_ok()); + } #[test] fn test_script_mutex_new() { diff --git a/stdlib/tests/stdlib_tests.rs b/stdlib/tests/stdlib_tests.rs index 05c13a8c..0e2964f5 100644 --- a/stdlib/tests/stdlib_tests.rs +++ b/stdlib/tests/stdlib_tests.rs @@ -519,6 +519,7 @@ fn test_http_module() { let response = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello Server!"; let _ = stream.write_all(response.as_bytes()); let _ = stream.flush(); + let _ = stream.shutdown(std::net::Shutdown::Both); } }); @@ -573,12 +574,22 @@ fn test_http_module() { capabilities: caps, }); - let res = get - .call( + let mut res = Err(techscript_runtime::RuntimeError::new( + techscript_runtime::RuntimeErrorKind::InvalidOperation("Failed".to_string()), + None, + None, + )); + for _ in 0..5 { + res = get.call( &mut ctx, vec![RuntimeValue::Str(format!("http://127.0.0.1:{}", port))], - ) - .unwrap(); + ); + if res.is_ok() { + break; + } + std::thread::sleep(std::time::Duration::from_millis(50)); + } + let res = res.unwrap(); if let RuntimeValue::Map { entries, .. } = res { let entries_borrow = entries.borrow(); @@ -591,26 +602,25 @@ fn test_http_module() { panic!("get did not return a Map"); } - let res2 = match post.call( - &mut ctx, - vec![ - RuntimeValue::Str(format!("http://127.0.0.1:{}", port)), - RuntimeValue::Str("body".to_string()), - ], - ) { - Ok(v) => v, - Err(_) => { - std::thread::sleep(std::time::Duration::from_millis(50)); - post.call( - &mut ctx, - vec![ - RuntimeValue::Str(format!("http://127.0.0.1:{}", port)), - RuntimeValue::Str("body".to_string()), - ], - ) - .unwrap() + let mut res2 = Err(techscript_runtime::RuntimeError::new( + techscript_runtime::RuntimeErrorKind::InvalidOperation("Failed".to_string()), + None, + None, + )); + for _ in 0..5 { + res2 = post.call( + &mut ctx, + vec![ + RuntimeValue::Str(format!("http://127.0.0.1:{}", port)), + RuntimeValue::Str("body".to_string()), + ], + ); + if res2.is_ok() { + break; } - }; + std::thread::sleep(std::time::Duration::from_millis(50)); + } + let res2 = res2.unwrap(); if let RuntimeValue::Map { entries, .. } = res2 { let entries_borrow = entries.borrow();