Fixed naked function with arguments in green threads example. See https://github.com/cfsamson/example-greenthreads/issues/20 for more information.

This commit is contained in:
Carl Fredrik Samson
2020-12-06 00:44:31 +01:00
parent 3ba348601a
commit 760fd57be3

View File

@@ -149,32 +149,30 @@ _**Press the expand icon in the top right corner to show the example code.**_
```rust, edition2018 ```rust, edition2018
# #![feature(llvm_asm, naked_functions)] # #![feature(llvm_asm, naked_functions)]
# use std::ptr; #
#
# const DEFAULT_STACK_SIZE: usize = 1024 * 1024 * 2; # const DEFAULT_STACK_SIZE: usize = 1024 * 1024 * 2;
# const MAX_THREADS: usize = 4; # const MAX_THREADS: usize = 4;
# static mut RUNTIME: usize = 0; # static mut RUNTIME: usize = 0;
# #
# pub struct Runtime { # pub struct Runtime {
# threads: Vec<Thread>, # threads: Vec<Thread>,
# current: usize, # current: usize,
# } # }
# #
# #[derive(PartialEq, Eq, Debug)] # #[derive(PartialEq, Eq, Debug)]
# enum State { # enum State {
# Available, # Available,
# Running, # Running,
# Ready, # Ready,
# } # }
# #
# struct Thread { # struct Thread {
# id: usize, # id: usize,
# stack: Vec<u8>, # stack: Vec<u8>,
# ctx: ThreadContext, # ctx: ThreadContext,
# state: State, # state: State,
# task: Option<Box<dyn Fn()>>,
# } # }
# #
# #[derive(Debug, Default)] # #[derive(Debug, Default)]
# #[repr(C)] # #[repr(C)]
# struct ThreadContext { # struct ThreadContext {
@@ -185,9 +183,8 @@ _**Press the expand icon in the top right corner to show the example code.**_
# r12: u64, # r12: u64,
# rbx: u64, # rbx: u64,
# rbp: u64, # rbp: u64,
# thread_ptr: u64,
# } # }
# #
# impl Thread { # impl Thread {
# fn new(id: usize) -> Self { # fn new(id: usize) -> Self {
# Thread { # Thread {
@@ -195,11 +192,10 @@ _**Press the expand icon in the top right corner to show the example code.**_
# stack: vec![0_u8; DEFAULT_STACK_SIZE], # stack: vec![0_u8; DEFAULT_STACK_SIZE],
# ctx: ThreadContext::default(), # ctx: ThreadContext::default(),
# state: State::Available, # state: State::Available,
# task: None,
# } # }
# } # }
# } # }
# #
# impl Runtime { # impl Runtime {
# pub fn new() -> Self { # pub fn new() -> Self {
# let base_thread = Thread { # let base_thread = Thread {
@@ -207,39 +203,37 @@ _**Press the expand icon in the top right corner to show the example code.**_
# stack: vec![0_u8; DEFAULT_STACK_SIZE], # stack: vec![0_u8; DEFAULT_STACK_SIZE],
# ctx: ThreadContext::default(), # ctx: ThreadContext::default(),
# state: State::Running, # state: State::Running,
# task: None,
# }; # };
# #
# let mut threads = vec![base_thread]; # let mut threads = vec![base_thread];
# threads[0].ctx.thread_ptr = &threads[0] as *const Thread as u64;
# let mut available_threads: Vec<Thread> = (1..MAX_THREADS).map(|i| Thread::new(i)).collect(); # let mut available_threads: Vec<Thread> = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
# threads.append(&mut available_threads); # threads.append(&mut available_threads);
# #
# Runtime { # Runtime {
# threads, # threads,
# current: 0, # current: 0,
# } # }
# } # }
# #
# pub fn init(&self) { # pub fn init(&self) {
# unsafe { # unsafe {
# let r_ptr: *const Runtime = self; # let r_ptr: *const Runtime = self;
# RUNTIME = r_ptr as usize; # RUNTIME = r_ptr as usize;
# } # }
# } # }
# #
# pub fn run(&mut self) -> ! { # pub fn run(&mut self) -> ! {
# while self.t_yield() {} # while self.t_yield() {}
# std::process::exit(0); # std::process::exit(0);
# } # }
# #
# fn t_return(&mut self) { # fn t_return(&mut self) {
# if self.current != 0 { # if self.current != 0 {
# self.threads[self.current].state = State::Available; # self.threads[self.current].state = State::Available;
# self.t_yield(); # self.t_yield();
# } # }
# } # }
# #
# fn t_yield(&mut self) -> bool { # fn t_yield(&mut self) -> bool {
# let mut pos = self.current; # let mut pos = self.current;
# while self.threads[pos].state != State::Ready { # while self.threads[pos].state != State::Ready {
@@ -251,92 +245,84 @@ _**Press the expand icon in the top right corner to show the example code.**_
# return false; # return false;
# } # }
# } # }
# #
# if self.threads[self.current].state != State::Available { # if self.threads[self.current].state != State::Available {
# self.threads[self.current].state = State::Ready; # self.threads[self.current].state = State::Ready;
# } # }
# #
# self.threads[pos].state = State::Running; # self.threads[pos].state = State::Running;
# let old_pos = self.current; # let old_pos = self.current;
# self.current = pos; # self.current = pos;
# #
# unsafe { # unsafe {
# switch(&mut self.threads[old_pos].ctx, &self.threads[pos].ctx); # let old: *mut ThreadContext = &mut self.threads[old_pos].ctx;
# let new: *const ThreadContext = &self.threads[pos].ctx;
# llvm_asm!(
# "mov $0, %rdi
# mov $1, %rsi"::"r"(old), "r"(new)
# );
# switch();
# } # }
# true # self.threads.len() > 0
# } # }
# #
# pub fn spawn<F: Fn() + 'static>(f: F){ # pub fn spawn(&mut self, f: fn()) {
# let available = self
# .threads
# .iter_mut()
# .find(|t| t.state == State::Available)
# .expect("no available thread.");
#
# let size = available.stack.len();
# unsafe { # unsafe {
# let rt_ptr = RUNTIME as *mut Runtime; # let s_ptr = available.stack.as_mut_ptr().offset(size as isize);
# let available = (*rt_ptr) # let s_ptr = (s_ptr as usize & !15) as *mut u8;
# .threads # std::ptr::write(s_ptr.offset(-16) as *mut u64, guard as u64);
# .iter_mut() # std::ptr::write(s_ptr.offset(-24) as *mut u64, skip as u64);
# .find(|t| t.state == State::Available) # std::ptr::write(s_ptr.offset(-32) as *mut u64, f as u64);
# .expect("no available thread."); # available.ctx.rsp = s_ptr.offset(-32) as u64;
#
# let size = available.stack.len();
# let s_ptr = available.stack.as_mut_ptr();
# available.task = Some(Box::new(f));
# available.ctx.thread_ptr = available as *const Thread as u64;
# ptr::write(s_ptr.offset((size - 8) as isize) as *mut u64, guard as u64);
# ptr::write(s_ptr.offset((size - 16) as isize) as *mut u64, call as u64);
# available.ctx.rsp = s_ptr.offset((size - 16) as isize) as u64;
# available.state = State::Ready;
# } # }
# available.state = State::Ready;
# } # }
# } # }
# #
# fn call(thread: u64) {
# let thread = unsafe { &*(thread as *const Thread) };
# if let Some(f) = &thread.task {
# f();
# }
# }
#
# #[naked] # #[naked]
# fn skip() { }
#
# fn guard() { # fn guard() {
# unsafe { # unsafe {
# let rt_ptr = RUNTIME as *mut Runtime; # let rt_ptr = RUNTIME as *mut Runtime;
# let rt = &mut *rt_ptr; # (*rt_ptr).t_return();
# println!("THREAD {} FINISHED.", rt.threads[rt.current].id);
# rt.t_return();
# }; # };
# } # }
# #
# pub fn yield_thread() { # pub fn yield_thread() {
# unsafe { # unsafe {
# let rt_ptr = RUNTIME as *mut Runtime; # let rt_ptr = RUNTIME as *mut Runtime;
# (*rt_ptr).t_yield(); # (*rt_ptr).t_yield();
# }; # };
# } # }
# #
# #[naked] # #[naked]
# #[inline(never)] # #[inline(never)]
# unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) { # unsafe fn switch() {
# llvm_asm!(" # llvm_asm!("
# mov %rsp, 0x00($0) # mov %rsp, 0x00(%rdi)
# mov %r15, 0x08($0) # mov %r15, 0x08(%rdi)
# mov %r14, 0x10($0) # mov %r14, 0x10(%rdi)
# mov %r13, 0x18($0) # mov %r13, 0x18(%rdi)
# mov %r12, 0x20($0) # mov %r12, 0x20(%rdi)
# mov %rbx, 0x28($0) # mov %rbx, 0x28(%rdi)
# mov %rbp, 0x30($0) # mov %rbp, 0x30(%rdi)
# #
# mov 0x00($1), %rsp # mov 0x00(%rsi), %rsp
# mov 0x08($1), %r15 # mov 0x08(%rsi), %r15
# mov 0x10($1), %r14 # mov 0x10(%rsi), %r14
# mov 0x18($1), %r13 # mov 0x18(%rsi), %r13
# mov 0x20($1), %r12 # mov 0x20(%rsi), %r12
# mov 0x28($1), %rbx # mov 0x28(%rsi), %rbx
# mov 0x30($1), %rbp # mov 0x30(%rsi), %rbp
# mov 0x38($1), %rdi
# ret
# " # "
# :
# : "r"(old), "r"(new)
# :
# : "alignstack"
# ); # );
# } # }
# #[cfg(not(windows))] # #[cfg(not(windows))]
@@ -541,7 +527,7 @@ async function run() {
You can consider the `run` function as a _pausable_ task consisting of several You can consider the `run` function as a _pausable_ task consisting of several
sub-tasks. On each "await" point it yields control to the scheduler (in this sub-tasks. On each "await" point it yields control to the scheduler (in this
case it's the well-known JavaScript event loop). case it's the well-known JavaScript event loop).
Once one of the sub-tasks changes state to either `fulfilled` or `rejected`, the Once one of the sub-tasks changes state to either `fulfilled` or `rejected`, the
task is scheduled to continue to the next step. task is scheduled to continue to the next step.
@@ -549,8 +535,8 @@ task is scheduled to continue to the next step.
Syntactically, Rust's Futures 0.1 was a lot like the promises example above, and Syntactically, Rust's Futures 0.1 was a lot like the promises example above, and
Rust's Futures 0.3 is a lot like async/await in our last example. Rust's Futures 0.3 is a lot like async/await in our last example.
Now this is also where the similarities between JavaScript promises and Rust's Now this is also where the similarities between JavaScript promises and Rust's
Futures stop. The reason we go through all this is to get an introduction and Futures stop. The reason we go through all this is to get an introduction and
get into the right mindset for exploring Rust's Futures. get into the right mindset for exploring Rust's Futures.
> To avoid confusion later on: There's one difference you should know. JavaScript > To avoid confusion later on: There's one difference you should know. JavaScript