final audit pass

This commit is contained in:
cfsamson
2020-04-07 11:55:33 +02:00
parent 01181ce2a2
commit 5947e07122
5 changed files with 581 additions and 581 deletions

View File

@@ -267,8 +267,7 @@ was one of the reasons they were removed).</li>
<li>Complicated to implement correctly if you want to support many different <li>Complicated to implement correctly if you want to support many different
platforms.</li> platforms.</li>
</ol> </ol>
<p>If you were to implement green threads in Rust, it could look something like <p>A green threads example could look something like this:</p>
this:</p>
<blockquote> <blockquote>
<p>The example presented below is an adapted example from an earlier gitbook I <p>The example presented below is an adapted example from an earlier gitbook I
wrote about green threads called <a href="https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/">Green Threads Explained in 200 lines of Rust.</a> wrote about green threads called <a href="https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/">Green Threads Explained in 200 lines of Rust.</a>
@@ -277,197 +276,198 @@ in that book. The code below is wildly unsafe and it's just to show a real examp
It's not in any way meant to showcase &quot;best practice&quot;. Just so we're on It's not in any way meant to showcase &quot;best practice&quot;. Just so we're on
the same page.</p> the same page.</p>
</blockquote> </blockquote>
<pre><pre class="playpen"><code class="language-rust edition2018">#![feature(asm, naked_functions)] <p><em><strong>Press the expand icon in the top right corner to show the example code.</strong></em> </p>
use std::ptr; <pre><pre class="playpen"><code class="language-rust edition2018"># #![feature(asm, naked_functions)]
# use std::ptr;
const DEFAULT_STACK_SIZE: usize = 1024 * 1024 * 2; #
const MAX_THREADS: usize = 4; # const DEFAULT_STACK_SIZE: usize = 1024 * 1024 * 2;
static mut RUNTIME: usize = 0; # const MAX_THREADS: usize = 4;
# static mut RUNTIME: usize = 0;
pub struct Runtime { #
threads: Vec&lt;Thread&gt;, # pub struct Runtime {
current: usize, # threads: Vec&lt;Thread&gt;,
} # current: usize,
# }
#[derive(PartialEq, Eq, Debug)] #
enum State { # #[derive(PartialEq, Eq, Debug)]
Available, # enum State {
Running, # Available,
Ready, # Running,
} # Ready,
# }
struct Thread { #
id: usize, # struct Thread {
stack: Vec&lt;u8&gt;, # id: usize,
ctx: ThreadContext, # stack: Vec&lt;u8&gt;,
state: State, # ctx: ThreadContext,
task: Option&lt;Box&lt;dyn Fn()&gt;&gt;, # state: State,
} # task: Option&lt;Box&lt;dyn Fn()&gt;&gt;,
# }
#[derive(Debug, Default)] #
#[repr(C)] # #[derive(Debug, Default)]
struct ThreadContext { # #[repr(C)]
rsp: u64, # struct ThreadContext {
r15: u64, # rsp: u64,
r14: u64, # r15: u64,
r13: u64, # r14: u64,
r12: u64, # r13: u64,
rbx: u64, # r12: u64,
rbp: u64, # rbx: u64,
thread_ptr: u64, # rbp: u64,
} # thread_ptr: u64,
# }
impl Thread { #
fn new(id: usize) -&gt; Self { # impl Thread {
Thread { # fn new(id: usize) -&gt; Self {
id, # Thread {
stack: vec![0_u8; DEFAULT_STACK_SIZE], # id,
ctx: ThreadContext::default(), # stack: vec![0_u8; DEFAULT_STACK_SIZE],
state: State::Available, # ctx: ThreadContext::default(),
task: None, # state: State::Available,
} # task: None,
} # }
} # }
# }
impl Runtime { #
pub fn new() -&gt; Self { # impl Runtime {
let base_thread = Thread { # pub fn new() -&gt; Self {
id: 0, # let base_thread = Thread {
stack: vec![0_u8; DEFAULT_STACK_SIZE], # id: 0,
ctx: ThreadContext::default(), # stack: vec![0_u8; DEFAULT_STACK_SIZE],
state: State::Running, # ctx: ThreadContext::default(),
task: None, # state: State::Running,
}; # task: None,
# };
let mut threads = vec![base_thread]; #
threads[0].ctx.thread_ptr = &amp;threads[0] as *const Thread as u64; # let mut threads = vec![base_thread];
let mut available_threads: Vec&lt;Thread&gt; = (1..MAX_THREADS).map(|i| Thread::new(i)).collect(); # threads[0].ctx.thread_ptr = &amp;threads[0] as *const Thread as u64;
threads.append(&amp;mut available_threads); # let mut available_threads: Vec&lt;Thread&gt; = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
# threads.append(&amp;mut available_threads);
Runtime { #
threads, # Runtime {
current: 0, # threads,
} # current: 0,
} # }
# }
pub fn init(&amp;self) { #
unsafe { # pub fn init(&amp;self) {
let r_ptr: *const Runtime = self; # unsafe {
RUNTIME = r_ptr as usize; # let r_ptr: *const Runtime = self;
} # RUNTIME = r_ptr as usize;
} # }
# }
pub fn run(&amp;mut self) -&gt; ! { #
while self.t_yield() {} # pub fn run(&amp;mut self) -&gt; ! {
std::process::exit(0); # while self.t_yield() {}
} # std::process::exit(0);
# }
fn t_return(&amp;mut self) { #
if self.current != 0 { # fn t_return(&amp;mut self) {
self.threads[self.current].state = State::Available; # if self.current != 0 {
self.t_yield(); # self.threads[self.current].state = State::Available;
} # self.t_yield();
} # }
# }
fn t_yield(&amp;mut self) -&gt; bool { #
let mut pos = self.current; # fn t_yield(&amp;mut self) -&gt; bool {
while self.threads[pos].state != State::Ready { # let mut pos = self.current;
pos += 1; # while self.threads[pos].state != State::Ready {
if pos == self.threads.len() { # pos += 1;
pos = 0; # if pos == self.threads.len() {
} # pos = 0;
if pos == self.current { # }
return false; # if pos == self.current {
} # return false;
} # }
# }
if self.threads[self.current].state != State::Available { #
self.threads[self.current].state = State::Ready; # if self.threads[self.current].state != State::Available {
} # self.threads[self.current].state = State::Ready;
# }
self.threads[pos].state = State::Running; #
let old_pos = self.current; # self.threads[pos].state = State::Running;
self.current = pos; # let old_pos = self.current;
# self.current = pos;
unsafe { #
switch(&amp;mut self.threads[old_pos].ctx, &amp;self.threads[pos].ctx); # unsafe {
} # switch(&amp;mut self.threads[old_pos].ctx, &amp;self.threads[pos].ctx);
true # }
} # true
# }
pub fn spawn&lt;F: Fn() + 'static&gt;(f: F){ #
unsafe { # pub fn spawn&lt;F: Fn() + 'static&gt;(f: F){
let rt_ptr = RUNTIME as *mut Runtime; # unsafe {
let available = (*rt_ptr) # let rt_ptr = RUNTIME as *mut Runtime;
.threads # let available = (*rt_ptr)
.iter_mut() # .threads
.find(|t| t.state == State::Available) # .iter_mut()
.expect(&quot;no available thread.&quot;); # .find(|t| t.state == State::Available)
# .expect(&quot;no available thread.&quot;);
let size = available.stack.len(); #
let s_ptr = available.stack.as_mut_ptr(); # let size = available.stack.len();
available.task = Some(Box::new(f)); # let s_ptr = available.stack.as_mut_ptr();
available.ctx.thread_ptr = available as *const Thread as u64; # available.task = Some(Box::new(f));
ptr::write(s_ptr.offset((size - 8) as isize) as *mut u64, guard as u64); # available.ctx.thread_ptr = available as *const Thread as u64;
ptr::write(s_ptr.offset((size - 16) as isize) as *mut u64, call as u64); # ptr::write(s_ptr.offset((size - 8) as isize) as *mut u64, guard as u64);
available.ctx.rsp = s_ptr.offset((size - 16) as isize) as u64; # ptr::write(s_ptr.offset((size - 16) as isize) as *mut u64, call as u64);
available.state = State::Ready; # available.ctx.rsp = s_ptr.offset((size - 16) as isize) as u64;
} # available.state = State::Ready;
} # }
} # }
# }
fn call(thread: u64) { #
let thread = unsafe { &amp;*(thread as *const Thread) }; # fn call(thread: u64) {
if let Some(f) = &amp;thread.task { # let thread = unsafe { &amp;*(thread as *const Thread) };
f(); # if let Some(f) = &amp;thread.task {
} # f();
} # }
# }
#[naked] #
fn guard() { # #[naked]
unsafe { # fn guard() {
let rt_ptr = RUNTIME as *mut Runtime; # unsafe {
let rt = &amp;mut *rt_ptr; # let rt_ptr = RUNTIME as *mut Runtime;
println!(&quot;THREAD {} FINISHED.&quot;, rt.threads[rt.current].id); # let rt = &amp;mut *rt_ptr;
rt.t_return(); # println!(&quot;THREAD {} FINISHED.&quot;, rt.threads[rt.current].id);
}; # rt.t_return();
} # };
# }
pub fn yield_thread() { #
unsafe { # pub fn yield_thread() {
let rt_ptr = RUNTIME as *mut Runtime; # unsafe {
(*rt_ptr).t_yield(); # let rt_ptr = RUNTIME as *mut Runtime;
}; # (*rt_ptr).t_yield();
} # };
# }
#[naked] #
#[inline(never)] # #[naked]
unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) { # #[inline(never)]
asm!(&quot; # unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
mov %rsp, 0x00($0) # asm!(&quot;
mov %r15, 0x08($0) # mov %rsp, 0x00($0)
mov %r14, 0x10($0) # mov %r15, 0x08($0)
mov %r13, 0x18($0) # mov %r14, 0x10($0)
mov %r12, 0x20($0) # mov %r13, 0x18($0)
mov %rbx, 0x28($0) # mov %r12, 0x20($0)
mov %rbp, 0x30($0) # mov %rbx, 0x28($0)
# mov %rbp, 0x30($0)
mov 0x00($1), %rsp #
mov 0x08($1), %r15 # mov 0x00($1), %rsp
mov 0x10($1), %r14 # mov 0x08($1), %r15
mov 0x18($1), %r13 # mov 0x10($1), %r14
mov 0x20($1), %r12 # mov 0x18($1), %r13
mov 0x28($1), %rbx # mov 0x20($1), %r12
mov 0x30($1), %rbp # mov 0x28($1), %rbx
mov 0x38($1), %rdi # mov 0x30($1), %rbp
ret # mov 0x38($1), %rdi
&quot; # ret
: # &quot;
: &quot;r&quot;(old), &quot;r&quot;(new) # :
: # : &quot;r&quot;(old), &quot;r&quot;(new)
: &quot;alignstack&quot; # :
); # : &quot;alignstack&quot;
} # );
# }
# #[cfg(not(windows))] # #[cfg(not(windows))]
fn main() { fn main() {
let mut runtime = Runtime::new(); let mut runtime = Runtime::new();

View File

@@ -321,8 +321,7 @@ was one of the reasons they were removed).</li>
<li>Complicated to implement correctly if you want to support many different <li>Complicated to implement correctly if you want to support many different
platforms.</li> platforms.</li>
</ol> </ol>
<p>If you were to implement green threads in Rust, it could look something like <p>A green threads example could look something like this:</p>
this:</p>
<blockquote> <blockquote>
<p>The example presented below is an adapted example from an earlier gitbook I <p>The example presented below is an adapted example from an earlier gitbook I
wrote about green threads called <a href="https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/">Green Threads Explained in 200 lines of Rust.</a> wrote about green threads called <a href="https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/">Green Threads Explained in 200 lines of Rust.</a>
@@ -331,197 +330,198 @@ in that book. The code below is wildly unsafe and it's just to show a real examp
It's not in any way meant to showcase &quot;best practice&quot;. Just so we're on It's not in any way meant to showcase &quot;best practice&quot;. Just so we're on
the same page.</p> the same page.</p>
</blockquote> </blockquote>
<pre><pre class="playpen"><code class="language-rust edition2018">#![feature(asm, naked_functions)] <p><em><strong>Press the expand icon in the top right corner to show the example code.</strong></em> </p>
use std::ptr; <pre><pre class="playpen"><code class="language-rust edition2018"># #![feature(asm, naked_functions)]
# use std::ptr;
const DEFAULT_STACK_SIZE: usize = 1024 * 1024 * 2; #
const MAX_THREADS: usize = 4; # const DEFAULT_STACK_SIZE: usize = 1024 * 1024 * 2;
static mut RUNTIME: usize = 0; # const MAX_THREADS: usize = 4;
# static mut RUNTIME: usize = 0;
pub struct Runtime { #
threads: Vec&lt;Thread&gt;, # pub struct Runtime {
current: usize, # threads: Vec&lt;Thread&gt;,
} # current: usize,
# }
#[derive(PartialEq, Eq, Debug)] #
enum State { # #[derive(PartialEq, Eq, Debug)]
Available, # enum State {
Running, # Available,
Ready, # Running,
} # Ready,
# }
struct Thread { #
id: usize, # struct Thread {
stack: Vec&lt;u8&gt;, # id: usize,
ctx: ThreadContext, # stack: Vec&lt;u8&gt;,
state: State, # ctx: ThreadContext,
task: Option&lt;Box&lt;dyn Fn()&gt;&gt;, # state: State,
} # task: Option&lt;Box&lt;dyn Fn()&gt;&gt;,
# }
#[derive(Debug, Default)] #
#[repr(C)] # #[derive(Debug, Default)]
struct ThreadContext { # #[repr(C)]
rsp: u64, # struct ThreadContext {
r15: u64, # rsp: u64,
r14: u64, # r15: u64,
r13: u64, # r14: u64,
r12: u64, # r13: u64,
rbx: u64, # r12: u64,
rbp: u64, # rbx: u64,
thread_ptr: u64, # rbp: u64,
} # thread_ptr: u64,
# }
impl Thread { #
fn new(id: usize) -&gt; Self { # impl Thread {
Thread { # fn new(id: usize) -&gt; Self {
id, # Thread {
stack: vec![0_u8; DEFAULT_STACK_SIZE], # id,
ctx: ThreadContext::default(), # stack: vec![0_u8; DEFAULT_STACK_SIZE],
state: State::Available, # ctx: ThreadContext::default(),
task: None, # state: State::Available,
} # task: None,
} # }
} # }
# }
impl Runtime { #
pub fn new() -&gt; Self { # impl Runtime {
let base_thread = Thread { # pub fn new() -&gt; Self {
id: 0, # let base_thread = Thread {
stack: vec![0_u8; DEFAULT_STACK_SIZE], # id: 0,
ctx: ThreadContext::default(), # stack: vec![0_u8; DEFAULT_STACK_SIZE],
state: State::Running, # ctx: ThreadContext::default(),
task: None, # state: State::Running,
}; # task: None,
# };
let mut threads = vec![base_thread]; #
threads[0].ctx.thread_ptr = &amp;threads[0] as *const Thread as u64; # let mut threads = vec![base_thread];
let mut available_threads: Vec&lt;Thread&gt; = (1..MAX_THREADS).map(|i| Thread::new(i)).collect(); # threads[0].ctx.thread_ptr = &amp;threads[0] as *const Thread as u64;
threads.append(&amp;mut available_threads); # let mut available_threads: Vec&lt;Thread&gt; = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
# threads.append(&amp;mut available_threads);
Runtime { #
threads, # Runtime {
current: 0, # threads,
} # current: 0,
} # }
# }
pub fn init(&amp;self) { #
unsafe { # pub fn init(&amp;self) {
let r_ptr: *const Runtime = self; # unsafe {
RUNTIME = r_ptr as usize; # let r_ptr: *const Runtime = self;
} # RUNTIME = r_ptr as usize;
} # }
# }
pub fn run(&amp;mut self) -&gt; ! { #
while self.t_yield() {} # pub fn run(&amp;mut self) -&gt; ! {
std::process::exit(0); # while self.t_yield() {}
} # std::process::exit(0);
# }
fn t_return(&amp;mut self) { #
if self.current != 0 { # fn t_return(&amp;mut self) {
self.threads[self.current].state = State::Available; # if self.current != 0 {
self.t_yield(); # self.threads[self.current].state = State::Available;
} # self.t_yield();
} # }
# }
fn t_yield(&amp;mut self) -&gt; bool { #
let mut pos = self.current; # fn t_yield(&amp;mut self) -&gt; bool {
while self.threads[pos].state != State::Ready { # let mut pos = self.current;
pos += 1; # while self.threads[pos].state != State::Ready {
if pos == self.threads.len() { # pos += 1;
pos = 0; # if pos == self.threads.len() {
} # pos = 0;
if pos == self.current { # }
return false; # if pos == self.current {
} # return false;
} # }
# }
if self.threads[self.current].state != State::Available { #
self.threads[self.current].state = State::Ready; # if self.threads[self.current].state != State::Available {
} # self.threads[self.current].state = State::Ready;
# }
self.threads[pos].state = State::Running; #
let old_pos = self.current; # self.threads[pos].state = State::Running;
self.current = pos; # let old_pos = self.current;
# self.current = pos;
unsafe { #
switch(&amp;mut self.threads[old_pos].ctx, &amp;self.threads[pos].ctx); # unsafe {
} # switch(&amp;mut self.threads[old_pos].ctx, &amp;self.threads[pos].ctx);
true # }
} # true
# }
pub fn spawn&lt;F: Fn() + 'static&gt;(f: F){ #
unsafe { # pub fn spawn&lt;F: Fn() + 'static&gt;(f: F){
let rt_ptr = RUNTIME as *mut Runtime; # unsafe {
let available = (*rt_ptr) # let rt_ptr = RUNTIME as *mut Runtime;
.threads # let available = (*rt_ptr)
.iter_mut() # .threads
.find(|t| t.state == State::Available) # .iter_mut()
.expect(&quot;no available thread.&quot;); # .find(|t| t.state == State::Available)
# .expect(&quot;no available thread.&quot;);
let size = available.stack.len(); #
let s_ptr = available.stack.as_mut_ptr(); # let size = available.stack.len();
available.task = Some(Box::new(f)); # let s_ptr = available.stack.as_mut_ptr();
available.ctx.thread_ptr = available as *const Thread as u64; # available.task = Some(Box::new(f));
ptr::write(s_ptr.offset((size - 8) as isize) as *mut u64, guard as u64); # available.ctx.thread_ptr = available as *const Thread as u64;
ptr::write(s_ptr.offset((size - 16) as isize) as *mut u64, call as u64); # ptr::write(s_ptr.offset((size - 8) as isize) as *mut u64, guard as u64);
available.ctx.rsp = s_ptr.offset((size - 16) as isize) as u64; # ptr::write(s_ptr.offset((size - 16) as isize) as *mut u64, call as u64);
available.state = State::Ready; # available.ctx.rsp = s_ptr.offset((size - 16) as isize) as u64;
} # available.state = State::Ready;
} # }
} # }
# }
fn call(thread: u64) { #
let thread = unsafe { &amp;*(thread as *const Thread) }; # fn call(thread: u64) {
if let Some(f) = &amp;thread.task { # let thread = unsafe { &amp;*(thread as *const Thread) };
f(); # if let Some(f) = &amp;thread.task {
} # f();
} # }
# }
#[naked] #
fn guard() { # #[naked]
unsafe { # fn guard() {
let rt_ptr = RUNTIME as *mut Runtime; # unsafe {
let rt = &amp;mut *rt_ptr; # let rt_ptr = RUNTIME as *mut Runtime;
println!(&quot;THREAD {} FINISHED.&quot;, rt.threads[rt.current].id); # let rt = &amp;mut *rt_ptr;
rt.t_return(); # println!(&quot;THREAD {} FINISHED.&quot;, rt.threads[rt.current].id);
}; # rt.t_return();
} # };
# }
pub fn yield_thread() { #
unsafe { # pub fn yield_thread() {
let rt_ptr = RUNTIME as *mut Runtime; # unsafe {
(*rt_ptr).t_yield(); # let rt_ptr = RUNTIME as *mut Runtime;
}; # (*rt_ptr).t_yield();
} # };
# }
#[naked] #
#[inline(never)] # #[naked]
unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) { # #[inline(never)]
asm!(&quot; # unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
mov %rsp, 0x00($0) # asm!(&quot;
mov %r15, 0x08($0) # mov %rsp, 0x00($0)
mov %r14, 0x10($0) # mov %r15, 0x08($0)
mov %r13, 0x18($0) # mov %r14, 0x10($0)
mov %r12, 0x20($0) # mov %r13, 0x18($0)
mov %rbx, 0x28($0) # mov %r12, 0x20($0)
mov %rbp, 0x30($0) # mov %rbx, 0x28($0)
# mov %rbp, 0x30($0)
mov 0x00($1), %rsp #
mov 0x08($1), %r15 # mov 0x00($1), %rsp
mov 0x10($1), %r14 # mov 0x08($1), %r15
mov 0x18($1), %r13 # mov 0x10($1), %r14
mov 0x20($1), %r12 # mov 0x18($1), %r13
mov 0x28($1), %rbx # mov 0x20($1), %r12
mov 0x30($1), %rbp # mov 0x28($1), %rbx
mov 0x38($1), %rdi # mov 0x30($1), %rbp
ret # mov 0x38($1), %rdi
&quot; # ret
: # &quot;
: &quot;r&quot;(old), &quot;r&quot;(new) # :
: # : &quot;r&quot;(old), &quot;r&quot;(new)
: &quot;alignstack&quot; # :
); # : &quot;alignstack&quot;
} # );
# }
# #[cfg(not(windows))] # #[cfg(not(windows))]
fn main() { fn main() {
let mut runtime = Runtime::new(); let mut runtime = Runtime::new();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -136,8 +136,7 @@ second as you read this.
4. Complicated to implement correctly if you want to support many different 4. Complicated to implement correctly if you want to support many different
platforms. platforms.
If you were to implement green threads in Rust, it could look something like A green threads example could look something like this:
this:
> The example presented below is an adapted example from an earlier gitbook I > The example presented below is an adapted example from an earlier gitbook I
> wrote about green threads called [Green Threads Explained in 200 lines of Rust.](https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/) > wrote about green threads called [Green Threads Explained in 200 lines of Rust.](https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/)
@@ -146,198 +145,199 @@ this:
> It's not in any way meant to showcase "best practice". Just so we're on > It's not in any way meant to showcase "best practice". Just so we're on
> the same page. > the same page.
_**Press the expand icon in the top right corner to show the example code.**_
```rust, edition2018 ```rust, edition2018
#![feature(asm, naked_functions)] # #![feature(asm, naked_functions)]
use std::ptr; # 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()>>, # task: Option<Box<dyn Fn()>>,
} # }
#
#[derive(Debug, Default)] # #[derive(Debug, Default)]
#[repr(C)] # #[repr(C)]
struct ThreadContext { # struct ThreadContext {
rsp: u64, # rsp: u64,
r15: u64, # r15: u64,
r14: u64, # r14: u64,
r13: u64, # r13: u64,
r12: u64, # r12: u64,
rbx: u64, # rbx: u64,
rbp: u64, # rbp: u64,
thread_ptr: u64, # thread_ptr: u64,
} # }
#
impl Thread { # impl Thread {
fn new(id: usize) -> Self { # fn new(id: usize) -> Self {
Thread { # Thread {
id, # id,
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, # task: None,
} # }
} # }
} # }
#
impl Runtime { # impl Runtime {
pub fn new() -> Self { # pub fn new() -> Self {
let base_thread = Thread { # let base_thread = Thread {
id: 0, # id: 0,
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, # 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; # 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 {
pos += 1; # pos += 1;
if pos == self.threads.len() { # if pos == self.threads.len() {
pos = 0; # pos = 0;
} # }
if pos == self.current { # if pos == self.current {
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); # switch(&mut self.threads[old_pos].ctx, &self.threads[pos].ctx);
} # }
true # true
} # }
#
pub fn spawn<F: Fn() + 'static>(f: F){ # pub fn spawn<F: Fn() + 'static>(f: F){
unsafe { # unsafe {
let rt_ptr = RUNTIME as *mut Runtime; # let rt_ptr = RUNTIME as *mut Runtime;
let available = (*rt_ptr) # let available = (*rt_ptr)
.threads # .threads
.iter_mut() # .iter_mut()
.find(|t| t.state == State::Available) # .find(|t| t.state == State::Available)
.expect("no available thread."); # .expect("no available thread.");
#
let size = available.stack.len(); # let size = available.stack.len();
let s_ptr = available.stack.as_mut_ptr(); # let s_ptr = available.stack.as_mut_ptr();
available.task = Some(Box::new(f)); # available.task = Some(Box::new(f));
available.ctx.thread_ptr = available as *const Thread as u64; # 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 - 8) as isize) as *mut u64, guard as u64);
ptr::write(s_ptr.offset((size - 16) as isize) as *mut u64, call 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.ctx.rsp = s_ptr.offset((size - 16) as isize) as u64;
available.state = State::Ready; # available.state = State::Ready;
} # }
} # }
} # }
#
fn call(thread: u64) { # fn call(thread: u64) {
let thread = unsafe { &*(thread as *const Thread) }; # let thread = unsafe { &*(thread as *const Thread) };
if let Some(f) = &thread.task { # if let Some(f) = &thread.task {
f(); # f();
} # }
} # }
#
#[naked] # #[naked]
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; # let rt = &mut *rt_ptr;
println!("THREAD {} FINISHED.", rt.threads[rt.current].id); # println!("THREAD {} FINISHED.", rt.threads[rt.current].id);
rt.t_return(); # 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(old: *mut ThreadContext, new: *const ThreadContext) {
asm!(" # asm!("
mov %rsp, 0x00($0) # mov %rsp, 0x00($0)
mov %r15, 0x08($0) # mov %r15, 0x08($0)
mov %r14, 0x10($0) # mov %r14, 0x10($0)
mov %r13, 0x18($0) # mov %r13, 0x18($0)
mov %r12, 0x20($0) # mov %r12, 0x20($0)
mov %rbx, 0x28($0) # mov %rbx, 0x28($0)
mov %rbp, 0x30($0) # mov %rbp, 0x30($0)
#
mov 0x00($1), %rsp # mov 0x00($1), %rsp
mov 0x08($1), %r15 # mov 0x08($1), %r15
mov 0x10($1), %r14 # mov 0x10($1), %r14
mov 0x18($1), %r13 # mov 0x18($1), %r13
mov 0x20($1), %r12 # mov 0x20($1), %r12
mov 0x28($1), %rbx # mov 0x28($1), %rbx
mov 0x30($1), %rbp # mov 0x30($1), %rbp
mov 0x38($1), %rdi # mov 0x38($1), %rdi
ret # ret
" # "
: # :
: "r"(old), "r"(new) # : "r"(old), "r"(new)
: # :
: "alignstack" # : "alignstack"
); # );
} # }
# #[cfg(not(windows))] # #[cfg(not(windows))]
fn main() { fn main() {
let mut runtime = Runtime::new(); let mut runtime = Runtime::new();