diff --git a/README.md b/README.md index aead37d..9ab929f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ Project or files: ``` . ├── __concurrent -│   └── arc-swap +│   ├── arc-swap +│   └── green-threads ├── __cpu │   └── x86 ├── __crypto @@ -124,7 +125,7 @@ Project or files: ├── vec.rs └── while.rs -96 directories, 26 files +97 directories, 26 files ``` diff --git a/__concurrent/green-threads/Cargo.lock b/__concurrent/green-threads/Cargo.lock new file mode 100644 index 0000000..d061cf6 --- /dev/null +++ b/__concurrent/green-threads/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "green-threads" +version = "0.1.0" diff --git a/__concurrent/green-threads/Cargo.toml b/__concurrent/green-threads/Cargo.toml new file mode 100644 index 0000000..911d481 --- /dev/null +++ b/__concurrent/green-threads/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "green-threads" +version = "0.1.0" +authors = ["Hatter Jiang "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/__concurrent/green-threads/README.md b/__concurrent/green-threads/README.md new file mode 100644 index 0000000..8ecf8f3 --- /dev/null +++ b/__concurrent/green-threads/README.md @@ -0,0 +1,8 @@ + +https://zhuanlan.zhihu.com/p/101061389 +https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/final-200-lines-of-code + + +https://github.com/cfsamson/example-greenthreads + + diff --git a/__concurrent/green-threads/rust-toolchain b/__concurrent/green-threads/rust-toolchain new file mode 100644 index 0000000..bf867e0 --- /dev/null +++ b/__concurrent/green-threads/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/__concurrent/green-threads/src/main.rs b/__concurrent/green-threads/src/main.rs new file mode 100644 index 0000000..e139f21 --- /dev/null +++ b/__concurrent/green-threads/src/main.rs @@ -0,0 +1,200 @@ +#![feature(llvm_asm, naked_functions)] + +const DEFAULT_STACK_SIZE: usize = 1024 * 1024 * 2; +const MAX_THREADS: usize = 4; +static mut RUNTIME: usize = 0; + +pub struct Runtime { + threads: Vec, + current: usize, +} + +#[derive(PartialEq, Eq, Debug)] +enum State { + Available, + Running, + Ready, +} + +struct Thread { + id: usize, + stack: Vec, + ctx: ThreadContext, + state: State, +} + +#[derive(Debug, Default)] +#[repr(C)] +struct ThreadContext { + rsp: u64, + r15: u64, + r14: u64, + r13: u64, + r12: u64, + rbx: u64, + rbp: u64, +} + +impl Thread { + fn new(id: usize) -> Self { + Thread { + id, + stack: vec![0_u8; DEFAULT_STACK_SIZE], + ctx: ThreadContext::default(), + state: State::Available, + } + } +} + +impl Runtime { + pub fn new() -> Self { + let base_thread = Thread { + id: 0, + stack: vec![0_u8; DEFAULT_STACK_SIZE], + ctx: ThreadContext::default(), + state: State::Running, + }; + + let mut threads = vec![base_thread]; + let mut available_threads: Vec = (1..MAX_THREADS).map(|i| Thread::new(i)).collect(); + threads.append(&mut available_threads); + + Runtime { + threads, + current: 0, + } + } + + pub fn init(&self) { + unsafe { + let r_ptr: *const Runtime = self; + RUNTIME = r_ptr as usize; + } + } + + pub fn run(&mut self) -> ! { + while self.t_yield() {} + std::process::exit(0); + } + + fn t_return(&mut self) { + if self.current != 0 { + self.threads[self.current].state = State::Available; + self.t_yield(); + } + } + + fn t_yield(&mut self) -> bool { + let mut pos = self.current; + while self.threads[pos].state != State::Ready { + pos += 1; + if pos == self.threads.len() { + pos = 0; + } + if pos == self.current { + return false; + } + } + + 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.current = pos; + + unsafe { + switch(&mut self.threads[old_pos].ctx, &self.threads[pos].ctx); + } + + self.threads.len() > 0 + } + + 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 { + let s_ptr = available.stack.as_mut_ptr().offset(size as isize); + let s_ptr = (s_ptr as usize & !15) as *mut u8; + std::ptr::write(s_ptr.offset(-16) as *mut u64, guard as u64); + std::ptr::write(s_ptr.offset(-24) as *mut u64, skip as u64); + std::ptr::write(s_ptr.offset(-32) as *mut u64, f as u64); + available.ctx.rsp = s_ptr.offset(-32) as u64; + } + available.state = State::Ready; + } +} + +#[naked] +fn skip() { } + +fn guard() { + unsafe { + let rt_ptr = RUNTIME as *mut Runtime; + (*rt_ptr).t_return(); + }; +} + +pub fn yield_thread() { + unsafe { + let rt_ptr = RUNTIME as *mut Runtime; + (*rt_ptr).t_yield(); + }; +} + +#[naked] +#[inline(never)] +unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) { + llvm_asm!(" + mov %rsp, 0x00($0) + mov %r15, 0x08($0) + mov %r14, 0x10($0) + mov %r13, 0x18($0) + mov %r12, 0x20($0) + mov %rbx, 0x28($0) + mov %rbp, 0x30($0) + + mov 0x00($1), %rsp + mov 0x08($1), %r15 + mov 0x10($1), %r14 + mov 0x18($1), %r13 + mov 0x20($1), %r12 + mov 0x28($1), %rbx + mov 0x30($1), %rbp + ret + " + : + :"r"(old), "r"(new) + : + : "volatile", "alignstack" + ); +} +fn main() { + let mut runtime = Runtime::new(); + runtime.init(); + runtime.spawn(|| { + println!("THREAD 1 STARTING"); + let id = 1; + for i in 0..10 { + println!("thread: {} counter: {}", id, i); + yield_thread(); + } + println!("THREAD 1 FINISHED"); + }); + runtime.spawn(|| { + println!("THREAD 2 STARTING"); + let id = 2; + for i in 0..15 { + println!("thread: {} counter: {}", id, i); + yield_thread(); + } + println!("THREAD 2 FINISHED"); + }); + runtime.run(); +}