@@ -5,6 +5,7 @@ use std::cell::RefCell;
55use std:: collections:: HashMap ;
66use std:: hash:: { Hash , Hasher } ;
77use std:: rc:: Rc ;
8+ use std:: sync:: Arc ;
89
910// Register file size - each function call uses ~(locals + 16) registers
1011// For 10000 deep recursion with ~20 registers per call = 200000 registers
@@ -414,6 +415,14 @@ impl VM {
414415 let symbol_rc = unsafe { frame. chunk . constants . get_unchecked ( name_idx as usize ) }
415416 . as_symbol_rc ( )
416417 . ok_or ( "CallGlobal: expected symbol" ) ?;
418+
419+ // Special handling for spawn - needs access to globals
420+ if & * symbol_rc == "spawn" {
421+ let result = self . handle_spawn ( base, dest, nargs) ?;
422+ unsafe { * self . registers . get_unchecked_mut ( base + dest as usize ) = result } ;
423+ continue ;
424+ }
425+
417426 let key = SymbolKey ( symbol_rc) ;
418427
419428 // INLINE CACHE: Check typed caches first (no type check needed)
@@ -1374,6 +1383,96 @@ impl VM {
13741383 }
13751384 }
13761385
1386+ /// Handle spawn with access to globals - allows spawned threads to call parent-defined functions
1387+ fn handle_spawn ( & self , base : usize , dest : u8 , nargs : u8 ) -> Result < Value , String > {
1388+ use std:: sync:: Mutex ;
1389+ use std:: thread;
1390+ use crate :: value:: HeapObject ;
1391+
1392+ if nargs != 1 {
1393+ return Err ( "spawn expects 1 argument (function)" . to_string ( ) ) ;
1394+ }
1395+
1396+ // Get the function argument
1397+ let arg_start = base + dest as usize + 1 ;
1398+ let func = & self . registers [ arg_start] ;
1399+
1400+ // Only compiled functions can be spawned (not closures with captured environments)
1401+ let chunk = if let Some ( chunk) = func. as_compiled_function ( ) {
1402+ chunk. clone ( )
1403+ } else {
1404+ return Err ( "spawn expects a compiled function (not a closure)" . to_string ( ) ) ;
1405+ } ;
1406+
1407+ // Convert Rc<Chunk> to Arc<Chunk> for thread safety
1408+ let arc_chunk: Arc < Chunk > = Arc :: new ( ( * chunk) . clone ( ) ) ;
1409+
1410+ // Collect shareable globals (compiled functions and native functions)
1411+ // We need to convert them to thread-safe format
1412+ let mut shared_globals: Vec < ( String , SharedGlobal ) > = Vec :: new ( ) ;
1413+ {
1414+ let globals = self . globals . borrow ( ) ;
1415+ for ( name, value) in globals. iter ( ) {
1416+ if let Some ( cf) = value. as_compiled_function ( ) {
1417+ // Convert compiled function to Arc-based
1418+ // cf is &Rc<Chunk>, so **cf gives us Chunk
1419+ shared_globals. push ( (
1420+ name. clone ( ) ,
1421+ SharedGlobal :: CompiledFunction ( Arc :: new ( ( * * cf) . clone ( ) ) )
1422+ ) ) ;
1423+ } else if let Some ( nf) = value. as_native_function ( ) {
1424+ // Native functions are just function pointers, safe to share
1425+ shared_globals. push ( (
1426+ name. clone ( ) ,
1427+ SharedGlobal :: NativeFunction ( nf. name . clone ( ) , nf. func )
1428+ ) ) ;
1429+ }
1430+ // Skip other types (data values) - they can't be safely shared
1431+ }
1432+ }
1433+
1434+ // Spawn the thread with access to shared globals
1435+ let handle = thread:: spawn ( move || {
1436+ // Create a new VM for this thread
1437+ let mut thread_vm = standard_vm ( ) ;
1438+
1439+ // Add the shared globals to the thread's VM
1440+ for ( name, shared) in shared_globals {
1441+ match shared {
1442+ SharedGlobal :: CompiledFunction ( arc_chunk) => {
1443+ // Convert Arc<Chunk> back to Rc<Chunk>
1444+ let rc_chunk = Rc :: new ( ( * arc_chunk) . clone ( ) ) ;
1445+ thread_vm. define_global ( & name, Value :: CompiledFunction ( rc_chunk) ) ;
1446+ }
1447+ SharedGlobal :: NativeFunction ( fn_name, func_ptr) => {
1448+ thread_vm. define_global ( & name, Value :: native_function ( & fn_name, func_ptr) ) ;
1449+ }
1450+ }
1451+ }
1452+
1453+ // Execute the function (it should be zero-argument)
1454+ match thread_vm. run ( ( * arc_chunk) . clone ( ) ) {
1455+ Ok ( result) => {
1456+ // Convert result to SharedValue
1457+ result. make_shared ( )
1458+ }
1459+ Err ( e) => Err ( format ! ( "Thread execution error: {}" , e) ) ,
1460+ }
1461+ } ) ;
1462+
1463+ // Wrap the JoinHandle in Arc<Mutex<Option<>>> so it can be joined once
1464+ let thread_handle = Arc :: new ( Mutex :: new ( Some ( handle) ) ) ;
1465+
1466+ // Create a HeapObject::ThreadHandle and wrap it in a Value
1467+ let heap = Rc :: new ( HeapObject :: ThreadHandle ( thread_handle) ) ;
1468+ Ok ( Value :: from_heap ( heap) )
1469+ }
1470+ }
1471+
1472+ /// Helper enum for sharing globals between threads
1473+ enum SharedGlobal {
1474+ CompiledFunction ( Arc < Chunk > ) ,
1475+ NativeFunction ( String , fn ( & [ Value ] ) -> Result < Value , String > ) ,
13771476}
13781477
13791478impl Default for VM {
0 commit comments