feature: spawn for closures

This commit is contained in:
Mark Poliakov 2021-11-19 16:14:13 +02:00
parent adb95ac52e
commit d582a9b58b
8 changed files with 171 additions and 44 deletions

View File

@ -234,6 +234,38 @@ impl Process {
panic!("This code should never run"); panic!("This code should never run");
} }
pub fn exit_thread(thread: ThreadRef) {
let switch = {
let switch = thread.state() == ThreadState::Running;
let process = thread.owner().unwrap();
let mut lock = process.inner.lock();
let tid = thread.id();
if lock.threads.len() == 1 {
// TODO call Process::exit instead?
todo!();
}
lock.threads.retain(|&e| e != tid);
thread.terminate();
SCHED.dequeue(tid);
debugln!("Thread {} terminated", tid);
switch
};
if switch {
// TODO retain thread ID in process "finished" list and
// drop it when process finishes
SCHED.switch(true);
panic!("This code should not run");
} else {
// Can drop this thread: it's not running
todo!();
}
}
fn collect(&self) -> Option<ExitCode> { fn collect(&self) -> Option<ExitCode> {
let lock = self.inner.lock(); let lock = self.inner.lock();
if lock.state == ProcessState::Finished { if lock.state == ProcessState::Finished {

View File

@ -58,6 +58,10 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
Process::exit(args[0] as i32); Process::exit(args[0] as i32);
unreachable!(); unreachable!();
} }
abi::SYS_EX_THREAD_EXIT => {
Process::exit_thread(Thread::current());
unreachable!();
},
// I/O system calls // I/O system calls
abi::SYS_OPENAT => { abi::SYS_OPENAT => {

View File

@ -6,6 +6,7 @@ pub const SYS_EX_SIGRETURN: usize = 131;
pub const SYS_EX_KILL: usize = 132; pub const SYS_EX_KILL: usize = 132;
pub const SYS_EX_CLONE: usize = 133; pub const SYS_EX_CLONE: usize = 133;
pub const SYS_EX_YIELD: usize = 134; pub const SYS_EX_YIELD: usize = 134;
pub const SYS_EX_THREAD_EXIT: usize = 135;
pub const SYS_EXIT: usize = 1; pub const SYS_EXIT: usize = 1;
pub const SYS_READ: usize = 2; pub const SYS_READ: usize = 2;

View File

@ -283,6 +283,14 @@ pub fn sys_ex_clone(entry: usize, stack: usize, arg: usize) -> Result<usize, Err
}) })
} }
#[inline(always)]
pub fn sys_ex_thread_exit(status: ExitCode) -> ! {
unsafe {
syscall!(abi::SYS_EX_THREAD_EXIT, argn!(i32::from(status)));
}
unreachable!();
}
#[inline(always)] #[inline(always)]
pub fn sys_ex_yield() { pub fn sys_ex_yield() {
unsafe { unsafe {

36
libusr/src/allocator.rs Normal file
View File

@ -0,0 +1,36 @@
use core::alloc::{Layout, GlobalAlloc};
use core::sync::atomic::{AtomicUsize, Ordering};
use libsys::mem::memset;
use crate::trace;
struct Allocator;
static mut ALLOC_DATA: [u8; 65536] = [0; 65536];
static ALLOC_PTR: AtomicUsize = AtomicUsize::new(0);
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
assert!(layout.align() < 16);
let res = ALLOC_PTR.fetch_add((layout.size() + 15) & !15, Ordering::SeqCst);
if res > 65536 {
panic!("Out of memory");
}
trace!("alloc({:?}) = {:p}", layout, &ALLOC_DATA[res]);
let res = &mut ALLOC_DATA[res] as *mut _;
memset(res, 0, layout.size());
res
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
trace!("free({:p}, {:?})", ptr, layout);
}
}
#[alloc_error_handler]
fn alloc_error_handler(_layout: Layout) -> ! {
loop {}
}
#[global_allocator]
static ALLOC: Allocator = Allocator;

View File

@ -7,16 +7,20 @@ use libsys::proc::ExitCode;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
extern crate alloc;
mod allocator;
pub mod file; pub mod file;
pub mod io; pub mod io;
pub mod os; pub mod os;
pub mod sys; pub mod sys;
pub mod sync; pub mod sync;
pub mod thread;
use sys::Signal; use sys::Signal;
#[inline(never)] #[inline(never)]
extern "C" fn _signal_handler(arg: Signal) -> ! { pub(crate) extern "C" fn _signal_handler(arg: Signal) -> ! {
trace!("Entered signal handler: arg={:?}", arg); trace!("Entered signal handler: arg={:?}", arg);
match arg { match arg {
Signal::Interrupt | Signal::SegmentationFault => Signal::Interrupt | Signal::SegmentationFault =>

77
libusr/src/thread.rs Normal file
View File

@ -0,0 +1,77 @@
use alloc::boxed::Box;
use alloc::vec;
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use libsys::{
calls::{sys_ex_clone, sys_ex_thread_exit, sys_ex_signal},
error::Errno,
proc::ExitCode,
};
use crate::trace;
struct NativeData<F, T>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
closure: F,
stack: usize,
}
pub struct JoinHandle<T> {
native: u32,
_pd: PhantomData<T>,
}
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
let stack = vec![0u8; 8192].leak();
#[inline(never)]
extern "C" fn thread_entry<F, T>(data: *mut NativeData<F, T>) -> !
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
let (stack, len) = {
// Setup signal handling
let mut signal_stack = vec![0u8; 8192];
unsafe {
sys_ex_signal(
crate::_signal_handler as usize,
signal_stack.as_mut_ptr() as usize + signal_stack.len(),
)
.unwrap();
}
let data: Box<NativeData<F, T>> = unsafe { Box::from_raw(data) };
let res = (data.closure)();
(data.stack, 8192)
};
// TODO free stack
sys_ex_thread_exit(ExitCode::from(0));
}
let native = unsafe {
let stack = stack.as_mut_ptr() as usize + stack.len();
let data: *mut NativeData<F, T> = Box::into_raw(Box::new(NativeData { closure: f, stack }));
sys_ex_clone(thread_entry::<F, T> as usize, stack, data as usize).unwrap() as u32
};
JoinHandle {
native,
_pd: PhantomData,
}
}

View File

@ -6,62 +6,27 @@ extern crate libusr;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
use libusr::thread;
use libusr::io::{self, Read}; use libusr::io::{self, Read};
use libusr::sys::{Signal, SignalDestination}; use libusr::sys::{Signal, SignalDestination};
use libusr::sync::Mutex; use libusr::sync::Mutex;
static mut THREAD_STACK: [u8; 8192] = [0; 8192];
static mut THREAD_SIGNAL_STACK: [u8; 8192] = [0; 8192];
lazy_static! {
static ref MUTEX: Mutex<()> = Mutex::new(());
}
fn sleep(ns: u64) { fn sleep(ns: u64) {
let mut rem = [0; 2]; let mut rem = [0; 2];
libusr::sys::sys_ex_nanosleep(ns, &mut rem).unwrap(); libusr::sys::sys_ex_nanosleep(ns, &mut rem).unwrap();
} }
fn fn0_signal(arg: Signal) {
trace!("fn0_signal");
unsafe {
libusr::sys::sys_exit(libusr::sys::ExitCode::from(0));
}
}
fn fn0(_arg: usize) {
unsafe {
libusr::sys::sys_ex_signal(fn0_signal as usize, THREAD_SIGNAL_STACK.as_mut_ptr().add(8192) as usize);
}
unsafe {
core::ptr::read_volatile(0x1234 as *const u32);
}
loop {}
//loop {
// sleep(100_000_000);
// println!("Tick from B");
// {
// let lock = MUTEX.lock();
// sleep(1_000_000_000);
// }
//}
}
fn do_fault() {
unsafe {
core::ptr::read_volatile(0x1238 as *const u32);
}
}
#[no_mangle] #[no_mangle]
fn main() -> i32 { fn main() -> i32 {
unsafe { let value = 1234;
libusr::sys::sys_ex_clone(fn0 as usize, THREAD_STACK.as_mut_ptr().add(8192) as usize, 0); let thread = thread::spawn(move || {
} trace!("Closure is alive: {}", value);
sleep(2_000_000_000);
trace!("Closure will now exit");
});
sleep(1_000_000_000); sleep(1_000_000_000);
do_fault(); trace!("???");
loop {} loop {}