final audit pass
This commit is contained in:
@@ -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 "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.</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<Thread>,
|
# pub struct Runtime {
|
||||||
current: usize,
|
# threads: Vec<Thread>,
|
||||||
}
|
# 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<u8>,
|
# id: usize,
|
||||||
ctx: ThreadContext,
|
# stack: Vec<u8>,
|
||||||
state: State,
|
# ctx: ThreadContext,
|
||||||
task: Option<Box<dyn Fn()>>,
|
# state: State,
|
||||||
}
|
# task: Option<Box<dyn Fn()>>,
|
||||||
|
# }
|
||||||
#[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) -> Self {
|
# impl Thread {
|
||||||
Thread {
|
# fn new(id: usize) -> 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() -> Self {
|
# impl Runtime {
|
||||||
let base_thread = Thread {
|
# pub fn new() -> 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 = &threads[0] as *const Thread as u64;
|
# let mut threads = vec![base_thread];
|
||||||
let mut available_threads: Vec<Thread> = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
|
# threads[0].ctx.thread_ptr = &threads[0] as *const Thread as u64;
|
||||||
threads.append(&mut available_threads);
|
# let mut available_threads: Vec<Thread> = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
|
||||||
|
# threads.append(&mut available_threads);
|
||||||
Runtime {
|
#
|
||||||
threads,
|
# Runtime {
|
||||||
current: 0,
|
# threads,
|
||||||
}
|
# current: 0,
|
||||||
}
|
# }
|
||||||
|
# }
|
||||||
pub fn init(&self) {
|
#
|
||||||
unsafe {
|
# pub fn init(&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(&mut self) -> ! {
|
#
|
||||||
while self.t_yield() {}
|
# pub fn run(&mut self) -> ! {
|
||||||
std::process::exit(0);
|
# while self.t_yield() {}
|
||||||
}
|
# std::process::exit(0);
|
||||||
|
# }
|
||||||
fn t_return(&mut self) {
|
#
|
||||||
if self.current != 0 {
|
# fn t_return(&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(&mut self) -> bool {
|
#
|
||||||
let mut pos = self.current;
|
# fn t_yield(&mut self) -> 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(&mut self.threads[old_pos].ctx, &self.threads[pos].ctx);
|
# unsafe {
|
||||||
}
|
# switch(&mut self.threads[old_pos].ctx, &self.threads[pos].ctx);
|
||||||
true
|
# }
|
||||||
}
|
# true
|
||||||
|
# }
|
||||||
pub fn spawn<F: Fn() + 'static>(f: F){
|
#
|
||||||
unsafe {
|
# pub fn spawn<F: Fn() + 'static>(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("no available thread.");
|
# .find(|t| t.state == State::Available)
|
||||||
|
# .expect("no available thread.");
|
||||||
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 { &*(thread as *const Thread) };
|
# fn call(thread: u64) {
|
||||||
if let Some(f) = &thread.task {
|
# let thread = unsafe { &*(thread as *const Thread) };
|
||||||
f();
|
# if let Some(f) = &thread.task {
|
||||||
}
|
# f();
|
||||||
}
|
# }
|
||||||
|
# }
|
||||||
#[naked]
|
#
|
||||||
fn guard() {
|
# #[naked]
|
||||||
unsafe {
|
# fn guard() {
|
||||||
let rt_ptr = RUNTIME as *mut Runtime;
|
# unsafe {
|
||||||
let rt = &mut *rt_ptr;
|
# let rt_ptr = RUNTIME as *mut Runtime;
|
||||||
println!("THREAD {} FINISHED.", rt.threads[rt.current].id);
|
# let rt = &mut *rt_ptr;
|
||||||
rt.t_return();
|
# println!("THREAD {} FINISHED.", 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!("
|
# unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
|
||||||
mov %rsp, 0x00($0)
|
# asm!("
|
||||||
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
|
||||||
"
|
# 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();
|
||||||
|
|||||||
386
book/print.html
386
book/print.html
@@ -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 "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.</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<Thread>,
|
# pub struct Runtime {
|
||||||
current: usize,
|
# threads: Vec<Thread>,
|
||||||
}
|
# 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<u8>,
|
# id: usize,
|
||||||
ctx: ThreadContext,
|
# stack: Vec<u8>,
|
||||||
state: State,
|
# ctx: ThreadContext,
|
||||||
task: Option<Box<dyn Fn()>>,
|
# state: State,
|
||||||
}
|
# task: Option<Box<dyn Fn()>>,
|
||||||
|
# }
|
||||||
#[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) -> Self {
|
# impl Thread {
|
||||||
Thread {
|
# fn new(id: usize) -> 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() -> Self {
|
# impl Runtime {
|
||||||
let base_thread = Thread {
|
# pub fn new() -> 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 = &threads[0] as *const Thread as u64;
|
# let mut threads = vec![base_thread];
|
||||||
let mut available_threads: Vec<Thread> = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
|
# threads[0].ctx.thread_ptr = &threads[0] as *const Thread as u64;
|
||||||
threads.append(&mut available_threads);
|
# let mut available_threads: Vec<Thread> = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
|
||||||
|
# threads.append(&mut available_threads);
|
||||||
Runtime {
|
#
|
||||||
threads,
|
# Runtime {
|
||||||
current: 0,
|
# threads,
|
||||||
}
|
# current: 0,
|
||||||
}
|
# }
|
||||||
|
# }
|
||||||
pub fn init(&self) {
|
#
|
||||||
unsafe {
|
# pub fn init(&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(&mut self) -> ! {
|
#
|
||||||
while self.t_yield() {}
|
# pub fn run(&mut self) -> ! {
|
||||||
std::process::exit(0);
|
# while self.t_yield() {}
|
||||||
}
|
# std::process::exit(0);
|
||||||
|
# }
|
||||||
fn t_return(&mut self) {
|
#
|
||||||
if self.current != 0 {
|
# fn t_return(&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(&mut self) -> bool {
|
#
|
||||||
let mut pos = self.current;
|
# fn t_yield(&mut self) -> 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(&mut self.threads[old_pos].ctx, &self.threads[pos].ctx);
|
# unsafe {
|
||||||
}
|
# switch(&mut self.threads[old_pos].ctx, &self.threads[pos].ctx);
|
||||||
true
|
# }
|
||||||
}
|
# true
|
||||||
|
# }
|
||||||
pub fn spawn<F: Fn() + 'static>(f: F){
|
#
|
||||||
unsafe {
|
# pub fn spawn<F: Fn() + 'static>(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("no available thread.");
|
# .find(|t| t.state == State::Available)
|
||||||
|
# .expect("no available thread.");
|
||||||
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 { &*(thread as *const Thread) };
|
# fn call(thread: u64) {
|
||||||
if let Some(f) = &thread.task {
|
# let thread = unsafe { &*(thread as *const Thread) };
|
||||||
f();
|
# if let Some(f) = &thread.task {
|
||||||
}
|
# f();
|
||||||
}
|
# }
|
||||||
|
# }
|
||||||
#[naked]
|
#
|
||||||
fn guard() {
|
# #[naked]
|
||||||
unsafe {
|
# fn guard() {
|
||||||
let rt_ptr = RUNTIME as *mut Runtime;
|
# unsafe {
|
||||||
let rt = &mut *rt_ptr;
|
# let rt_ptr = RUNTIME as *mut Runtime;
|
||||||
println!("THREAD {} FINISHED.", rt.threads[rt.current].id);
|
# let rt = &mut *rt_ptr;
|
||||||
rt.t_return();
|
# println!("THREAD {} FINISHED.", 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!("
|
# unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
|
||||||
mov %rsp, 0x00($0)
|
# asm!("
|
||||||
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
|
||||||
"
|
# 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();
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user