use crate::kmain; use std::thread::JoinHandle; use std::net::ToSocketAddrs; use std::sync::mpsc::channel; use xous_kernel::{rsyscall, SysCall}; mod shutdown; #[cfg(feature = "report-memory")] use stats_alloc::{Region, Stats, StatsAlloc, INSTRUMENTED_SYSTEM}; #[cfg(feature = "report-memory")] #[global_allocator] static GLOBAL: &StatsAlloc = &INSTRUMENTED_SYSTEM; const SERVER_SPEC: &str = "127.0.0.1:0"; fn start_kernel(server_spec: &str) -> JoinHandle<()> { assert!( std::env::var("XOUS_LISTEN_ADDR").is_err(), "XOUS_LISTEN_ADDR environment variable must be unset to run tests" ); assert!( std::env::var("XOUS_SERVER").is_err(), "XOUS_SERVER environment variable must be unset to run tests" ); use rand::{thread_rng, Rng}; let mut pid1_key = [0u8; 16]; let mut rng = thread_rng(); for b in pid1_key.iter_mut() { *b = rng.gen(); } xous_kernel::arch::set_process_key(&pid1_key); let server_addr = server_spec .to_socket_addrs() .expect("invalid server address") .next() .expect("unable to resolve server address"); // Attempt to bind. This will fail if the port is in use. // let temp_server = TcpListener::bind(server_addr).unwrap(); // let server_addr = temp_server.local_addr().unwrap(); // drop(temp_server); let (send_addr, recv_addr) = channel(); // Launch the main thread. We pass a `send_addr` channel so that the // server can notify us when it's ready to listen. let main_thread = std::thread::Builder::new() .name("kernel main".to_owned()) .spawn(move || { let server_spec_server = server_addr; crate::arch::set_pid1_key(pid1_key); crate::arch::set_send_addr(send_addr); crate::arch::set_listen_address(&server_spec_server); kmain() }) .expect("couldn't start kernel thread"); let server_addr = recv_addr.recv().unwrap(); xous_kernel::arch::set_xous_address(server_addr); // Connect to server. This first instance needs to make sure the kernel is listening. // let mut server_conn = None; // let mut connected = false; // for i in 1..11 { // let res = TcpStream::connect_timeout(&server_addr, Duration::from_millis(200)); // if res.is_ok() { // connected = true; // break; // } // println!("Retrying connection {}/10", i); // } // // Convert the Option into conn // assert!(connected, "unable to connect to server"); main_thread } fn shutdown_kernel() { // Any process ought to be able to shut down the system currently. xous_kernel::wait_process_as_thread( xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "shutdown", || { rsyscall(SysCall::Shutdown).expect("unable to shutdown server"); }, )) .expect("couldn't shut down the kernel"), ) .expect("couldn't wait for the shutdown process to end"); } // /// Spawn a new "process" with the given server spec inside the given closure // /// and return a join handle // fn as_process(f: F) -> JoinHandle // where // F: FnOnce() -> R, // F: Send + 'static, // R: Send + 'static, // { // let server_spec = xous_kernel::arch::xous_address(); // std::thread::spawn(move || { // xous_kernel::arch::set_xous_address(server_spec); // xous_kernel::arch::xous_connect(); // f() // }) // } #[test] fn shutdown() { // Start the server in another thread. let main_thread = start_kernel(SERVER_SPEC); // Send a raw `Shutdown` message to terminate the kernel. shutdown_kernel(); // Wait for the kernel to exit. main_thread.join().expect("couldn't join main thread"); } #[test] fn send_scalar_message() { // Start the server in another thread let main_thread = start_kernel(SERVER_SPEC); let (server_addr_send, server_addr_recv) = channel(); // Spawn the server "process" (which just lives in a separate thread) // and receive the message. Note that we need to communicate to the // "Client" what our server ID is. Normally this would be done via // an external nameserver. let xous_server = xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "send_scalar_message server", move || { let sid = xous_kernel::create_server(b"send_scalar_mesg") .expect("couldn't create test server"); server_addr_send.send(sid).unwrap(); let envelope = xous_kernel::receive_message(sid).expect("couldn't receive messages"); assert_eq!( envelope.body, xous_kernel::Message::Scalar(xous_kernel::ScalarMessage { id: 1, arg1: 2, arg2: 3, arg3: 4, arg4: 5 }) ); }, )) .expect("couldn't spawn server process"); // Spawn the client "process" and wait for the server address. let xous_client = xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "send_scalar_message client", move || { let sid = server_addr_recv.recv().unwrap(); let conn = xous_kernel::try_connect(sid).expect("couldn't connect to server"); xous_kernel::try_send_message( conn, xous_kernel::Message::Scalar(xous_kernel::ScalarMessage { id: 1, arg1: 2, arg2: 3, arg3: 4, arg4: 5, }), ) .expect("couldn't send message"); }, )) .expect("couldn't spawn client process"); // Wait for both processes to finish crate::wait_process_as_thread(xous_server).expect("couldn't join server process"); crate::wait_process_as_thread(xous_client).expect("couldn't join client process"); shutdown_kernel(); main_thread.join().expect("couldn't join kernel process"); } #[test] fn send_blocking_scalar_message() { // Start the server in another thread let main_thread = start_kernel(SERVER_SPEC); let (server_addr_send, server_addr_recv) = channel(); // Spawn the server "process" (which just lives in a separate thread) // and receive the message. Note that we need to communicate to the // "Client" what our server ID is. Normally this would be done via // an external nameserver. let xous_server = xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "send_scalar_message server", move || { let sid = xous_kernel::create_server(b"send_scalar_mesg") .expect("couldn't create test server"); server_addr_send.send(sid).unwrap(); let envelope = xous_kernel::receive_message(sid).expect("couldn't receive messages"); assert_eq!( envelope.body, xous_kernel::Message::BlockingScalar(xous_kernel::ScalarMessage { id: 1, arg1: 2, arg2: 3, arg3: 4, arg4: 5 }) ); xous_kernel::return_scalar(envelope.sender, 42).expect("couldn't return scalar"); let envelope = xous_kernel::receive_message(sid).expect("couldn't receive messages"); assert_eq!( envelope.body, xous_kernel::Message::BlockingScalar(xous_kernel::ScalarMessage { id: 6, arg1: 7, arg2: 8, arg3: 9, arg4: 10 }) ); xous_kernel::return_scalar2(envelope.sender, 56, 78).expect("couldn't return scalar"); }, )) .expect("couldn't spawn server process"); // Spawn the client "process" and wait for the server address. let xous_client = xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "send_scalar_message client", move || { let sid = server_addr_recv.recv().unwrap(); let conn = xous_kernel::try_connect(sid).expect("couldn't connect to server"); let result = xous_kernel::try_send_message( conn, xous_kernel::Message::BlockingScalar(xous_kernel::ScalarMessage { id: 1, arg1: 2, arg2: 3, arg3: 4, arg4: 5, }), ) .expect("couldn't send message"); assert_eq!(result, xous_kernel::Result::Scalar1(42)); let result = xous_kernel::try_send_message( conn, xous_kernel::Message::BlockingScalar(xous_kernel::ScalarMessage { id: 6, arg1: 7, arg2: 8, arg3: 9, arg4: 10, }), ) .expect("couldn't send message"); assert_eq!(result, xous_kernel::Result::Scalar2(56, 78)); }, )) .expect("couldn't spawn client process"); // Wait for both processes to finish crate::wait_process_as_thread(xous_server).expect("couldn't join server process"); crate::wait_process_as_thread(xous_client).expect("couldn't join client process"); shutdown_kernel(); main_thread.join().expect("couldn't join kernel process"); } #[test] fn send_move_message() { let test_str = "Hello, world!"; let test_bytes = test_str.as_bytes(); let main_thread = start_kernel(SERVER_SPEC); let (server_addr_send, server_addr_recv) = channel(); let xous_server = xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "send_move_message server", move || { // println!("SERVER: Creating server..."); let sid = xous_kernel::create_server(b"send_move_messag") .expect("couldn't create test server"); // println!("SERVER: Sending server address of {:?} to client", sid); server_addr_send.send(sid).unwrap(); // println!("SERVER: Starting to receive messages..."); let envelope = xous_kernel::receive_message(sid).expect("couldn't receive messages"); // println!("SERVER: Received message from {}", envelope.sender); let message = envelope.body; if let xous_kernel::Message::Move(m) = message { let buf = m.buf; let bt = unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) }; assert_eq!(*test_bytes, *bt, "message was changed by the kernel"); // let s = String::from_utf8_lossy(&bt); // println!("SERVER: Got message: {:?} -> \"{}\"", bt, s); } else { panic!("unexpected message type"); } }, )) .expect("couldn't start server"); let xous_client = xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "send_move_message client", move || { // println!("CLIENT: Waiting for server address..."); let sid = server_addr_recv.recv().unwrap(); // println!("CLIENT: Connecting to server {:?}", sid); let conn = xous_kernel::try_connect(sid).expect("couldn't connect to server"); let msg = xous_kernel::carton::Carton::from_bytes(test_bytes); xous_kernel::try_send_message(conn, xous_kernel::Message::Move(msg.into_message(0))) .expect("couldn't send a message"); // println!("CLIENT: Message sent"); }, )) .expect("couldn't start client"); // Wait for both processes to finish crate::wait_process_as_thread(xous_server).expect("couldn't join server process"); crate::wait_process_as_thread(xous_client).expect("couldn't join client process"); // Any process ought to be able to shut down the system currently. shutdown_kernel(); main_thread.join().expect("couldn't join kernel process"); } #[test] fn send_borrow_message() { let main_thread = start_kernel(SERVER_SPEC); let (server_addr_send, server_addr_recv) = channel(); let test_str = "Hello, world!"; let test_bytes = test_str.as_bytes(); let xous_server = xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "send_borrow_message server", move || { { // println!("SERVER: Creating server..."); let sid = xous_kernel::create_server(b"send_borrow_mesg") .expect("couldn't create test server"); server_addr_send.send(sid).unwrap(); // println!("SERVER: Receiving message..."); let envelope = xous_kernel::receive_message(sid).expect("couldn't receive messages"); // println!("SERVER: Received message from {}", envelope.sender); let message = envelope.body; if let xous_kernel::Message::Borrow(m) = message { let buf = m.buf; let bt = unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) }; assert_eq!(*test_bytes, *bt); // let s = String::from_utf8_lossy(&bt); // println!("SERVER: Got message: {:?} -> \"{}\"", bt, s); xous_kernel::return_memory(envelope.sender, m.buf).unwrap(); // println!("SERVER: Returned memory"); // println!("SERVER: Returned memory"); } else { panic!("unexpected message type"); } // println!("SERVER: Dropping things"); } // println!("SERVER: Exiting"); }, )) .expect("couldn't start server"); let xous_client = xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "send_borrow_message client", move || { { // Get the server address (out of band) so we know what to connect to // println!("CLIENT: Waiting for server to start..."); let sid = server_addr_recv.recv().unwrap(); // Perform a connection to the server // println!("CLIENT: Connecting to server..."); let conn = xous_kernel::connect(sid).expect("couldn't connect to server"); // Convert the message into a "Carton" that can be shipped as a message // println!("CLIENT: Creating carton..."); let carton = xous_kernel::carton::Carton::from_bytes(test_bytes); // Send the message to the server // println!("CLIENT: Lending message..."); carton .lend(conn, 0) .expect("couldn't lend message to server"); // println!("CLIENT: Done, dropping things"); } // println!("CLIENT: Exit"); }, )) .expect("couldn't start client"); // Wait for both processes to finish crate::wait_process_as_thread(xous_server).expect("couldn't join server process"); crate::wait_process_as_thread(xous_client).expect("couldn't join client process"); // Any process ought to be able to shut down the system currently. shutdown_kernel(); main_thread.join().expect("couldn't join kernel process"); } #[test] fn send_mutableborrow_message() { let main_thread = start_kernel(SERVER_SPEC); let (server_addr_send, server_addr_recv) = channel(); let test_str = "Hello, world!"; let test_bytes = test_str.as_bytes(); let xous_server = xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "send_mutableborrow_message server", move || { let sid = xous_kernel::create_server(b"send_mutborrow_m") .expect("couldn't create test server"); server_addr_send.send(sid).unwrap(); let envelope = xous_kernel::receive_message(sid).expect("couldn't receive messages"); // println!("Received message from {}", envelope.sender); let message = envelope.body; if let xous_kernel::Message::MutableBorrow(m) = message { let buf = m.buf; let bt = unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) }; for letter in bt.iter_mut() { *letter += 1; } xous_kernel::return_memory(envelope.sender, m.buf).unwrap(); } else { panic!("unexpected message type"); } }, )) .expect("couldn't start server"); let xous_client = xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "send_mutableborrow_message client", move || { // Get the server address (out of band) so we know what to connect to let sid = server_addr_recv.recv().unwrap(); // Perform a connection to the server let conn = xous_kernel::connect(sid).expect("couldn't connect to server"); // Convert the message into a "Carton" that can be shipped as a message let mut carton = xous_kernel::carton::Carton::from_bytes(&test_bytes); let mut check_bytes = test_bytes.to_vec(); for letter in check_bytes.iter_mut() { *letter += 1; } // Send the message to the server carton .lend_mut(conn, 3) .expect("couldn't mutably lend data"); let modified_bytes: &[u8] = carton.as_ref(); assert_eq!(&check_bytes, &modified_bytes); }, )) .expect("couldn't start client"); // Wait for both processes to finish crate::wait_process_as_thread(xous_server).expect("couldn't join server process"); crate::wait_process_as_thread(xous_client).expect("couldn't join client process"); // Any process ought to be able to shut down the system currently. shutdown_kernel(); main_thread.join().expect("couldn't join kernel process"); } #[test] fn send_mutableborrow_message_repeat() { let main_thread = start_kernel(SERVER_SPEC); let (server_addr_send, server_addr_recv) = channel(); let test_str = "Hello, world!"; let test_bytes = test_str.as_bytes(); let loops = 50; let xous_server = xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "send_mutableborrow_message_repeat server", move || { let sid = xous_kernel::create_server(b"send_mutborrow_r") .expect("couldn't create test server"); server_addr_send.send(sid).unwrap(); for iteration in 0..loops { let envelope = xous_kernel::receive_message(sid).expect("couldn't receive messages"); let message = envelope.body; if let xous_kernel::Message::MutableBorrow(m) = message { let buf = m.buf; let bt = unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) }; for letter in bt.iter_mut() { *letter = (*letter).wrapping_add((iteration & 0xff) as u8); } xous_kernel::return_memory(envelope.sender, m.buf).unwrap(); } else { panic!("unexpected message type"); } } }, )) .expect("couldn't start server"); let xous_client = xous_kernel::create_process_as_thread(xous_kernel::ProcessArgsAsThread::new( "send_mutableborrow_message_repeat client", move || { // Get the server address (out of band) so we know what to connect to let sid = server_addr_recv.recv().unwrap(); // Perform a connection to the server let conn = xous_kernel::connect(sid).expect("couldn't connect to server"); // Convert the message into a "Carton" that can be shipped as a message for iteration in 0..loops { let mut carton = xous_kernel::carton::Carton::from_bytes(&test_bytes); let mut check_bytes = test_bytes.to_vec(); for letter in check_bytes.iter_mut() { *letter = (*letter).wrapping_add((iteration & 0xff) as u8); } // Send the message to the server carton .lend_mut(conn, 3) .expect("couldn't mutably lend data"); let modified_bytes: &[u8] = carton.as_ref(); assert_eq!(&check_bytes, &modified_bytes); } }, )) .expect("couldn't start client"); // Wait for both processes to finish crate::wait_process_as_thread(xous_server).expect("couldn't join server process"); crate::wait_process_as_thread(xous_client).expect("couldn't join client process"); // Any process ought to be able to shut down the system currently. shutdown_kernel(); main_thread.join().expect("couldn't join kernel process"); } // #[cfg(feature = "report-memory")] // #[test] // fn measure_memory_usage() { // let mut reg = Region::new(&GLOBAL); // reg.reset(); // { // // Run the "shutdown" test in its own thread. This ensures that // // any TLS is freed when the thread exits. // let jh = as_process(shutdown); // jh.join() // .expect("couldn't run shutdown test for measuring memory"); // } // fn memory_in_use(start: &Stats) -> usize { // if start.bytes_deallocated > start.bytes_allocated { // eprintln!("Allocated a negative number of bytes!"); // 0 // } else { // start.bytes_allocated - start.bytes_deallocated // } // } // let after_join = reg.change(); // let miu = memory_in_use(&after_join); // println!("After test: {:#?} ({} bytes in use)", after_join, miu); // } /// Test that a server can be its own client #[test] fn server_client_same_process() { // Start the kernel in its own thread let main_thread = start_kernel(SERVER_SPEC); let internal_server = xous_kernel::create_process_as_thread( xous_kernel::arch::ProcessArgsAsThread::new("server_client_same_process process", || { let server = xous_kernel::create_server(b"s_c_same_process").expect("couldn't create server"); let connection = xous_kernel::try_connect(server).expect("couldn't connect to our own server"); let msg_contents = xous_kernel::ScalarMessage { id: 1, arg1: 2, arg2: 3, arg3: 4, arg4: 5, }; xous_kernel::try_send_message(connection, xous_kernel::Message::Scalar(msg_contents)) .expect("couldn't send message"); let msg = xous_kernel::receive_message(server).expect("couldn't receive message"); assert_eq!(msg.body, xous_kernel::Message::Scalar(msg_contents)); }), ) .expect("couldn't start server"); xous_kernel::wait_process_as_thread(internal_server) .expect("couldn't join internal_server process"); // Any process ought to be able to shut down the system currently. rsyscall(SysCall::Shutdown).expect("unable to shutdown server"); main_thread.join().expect("couldn't join kernel process"); } /// Test that one process can have multiple contexts #[test] fn multiple_contexts() { // ::debug_here::debug_here!(); // Start the kernel in its own thread let main_thread = start_kernel(SERVER_SPEC); let internal_server = xous_kernel::create_process_as_thread( xous_kernel::ProcessArgsAsThread::new("multiple_contexts process", move || { let server = xous_kernel::create_server(b"multiple_context").expect("couldn't create server"); let connection = xous_kernel::try_connect(server).expect("couldn't connect to our own server"); let msg_contents = xous_kernel::ScalarMessage { id: 1, arg1: 2, arg2: 3, arg3: 4, arg4: 5, }; let mut server_threads = vec![]; for _ in 1..crate::arch::process::MAX_THREAD { server_threads.push( xous_kernel::create_thread(move || { let msg = xous_kernel::receive_message(server).expect("couldn't receive message"); assert_eq!(msg.body, xous_kernel::Message::Scalar(msg_contents)); }) .expect("couldn't spawn client thread"), ); } for _ in &server_threads { xous_kernel::try_send_message( connection, xous_kernel::Message::Scalar(msg_contents), ) .expect("couldn't send message"); } for server_thread in server_threads.into_iter() { xous_kernel::wait_thread(server_thread).expect("couldn't wait for thread"); } }), ) .expect("couldn't create internal server"); xous_kernel::wait_process_as_thread(internal_server) .expect("couldn't join internal_server process"); // Any process ought to be able to shut down the system currently. shutdown_kernel(); main_thread.join().expect("couldn't join kernel process"); } #[test] fn multiple_multiple_contexts() { for _ in 0..5 { multiple_contexts(); } } /// Test that a server can be restarted and the kernel doesn't crash #[test] fn process_restart_server() { let test_str = "Hello, world!"; let test_bytes = test_str.as_bytes(); let main_thread = start_kernel(SERVER_SPEC); fn create_destroy_server(test_bytes: &'static [u8]) { let (server_addr_send, server_addr_recv) = channel(); let xous_server = xous_kernel::create_process_as_thread( xous_kernel::ProcessArgsAsThread::new("process_restart_server server", move || { let sid = xous_kernel::create_server(b"test_recreate_se") .expect("couldn't create test server"); server_addr_send.send(sid).unwrap(); let thr = xous_kernel::create_thread(move || { let envelope = xous_kernel::receive_message(sid).expect("couldn't receive messages"); // println!("Received message from {}", envelope.sender); let message = envelope.body; if let xous_kernel::Message::Move(m) = message { let buf = m.buf; let bt = unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) }; assert_eq!(*test_bytes, *bt); // let s = String::from_utf8_lossy(&bt); // println!("Got message: {:?} -> \"{}\"", bt, s); } else { panic!("unexpected message type"); } }) .unwrap(); xous_kernel::wait_thread(thr).unwrap(); }), ) .expect("couldn't spawn server process"); // Wait for the server to start up let xous_client = xous_kernel::create_process_as_thread( xous_kernel::ProcessArgsAsThread::new("process_restart_server client", move || { let sid = server_addr_recv.recv().unwrap(); let conn = xous_kernel::try_connect(sid).expect("couldn't connect to server"); let msg = xous_kernel::carton::Carton::from_bytes(test_bytes); xous_kernel::try_send_message( conn, xous_kernel::Message::Move(msg.into_message(0)), ) .expect("couldn't send a message"); }), ) .expect("couldn't start client process"); xous_kernel::wait_process_as_thread(xous_server).expect("couldn't join server process"); xous_kernel::wait_process_as_thread(xous_client).expect("couldn't join client process"); } // create_destroy_server(test_bytes); create_destroy_server(test_bytes); // Any process ought to be able to shut down the system currently. shutdown_kernel(); main_thread.join().expect("couldn't join kernel process"); }