feat: add __concurrent/green-threads
This commit is contained in:
@@ -6,7 +6,8 @@ Project or files:
|
|||||||
```
|
```
|
||||||
.
|
.
|
||||||
├── __concurrent
|
├── __concurrent
|
||||||
│ └── arc-swap
|
│ ├── arc-swap
|
||||||
|
│ └── green-threads
|
||||||
├── __cpu
|
├── __cpu
|
||||||
│ └── x86
|
│ └── x86
|
||||||
├── __crypto
|
├── __crypto
|
||||||
@@ -124,7 +125,7 @@ Project or files:
|
|||||||
├── vec.rs
|
├── vec.rs
|
||||||
└── while.rs
|
└── while.rs
|
||||||
|
|
||||||
96 directories, 26 files
|
97 directories, 26 files
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
5
__concurrent/green-threads/Cargo.lock
generated
Normal file
5
__concurrent/green-threads/Cargo.lock
generated
Normal file
@@ -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"
|
||||||
9
__concurrent/green-threads/Cargo.toml
Normal file
9
__concurrent/green-threads/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "green-threads"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
8
__concurrent/green-threads/README.md
Normal file
8
__concurrent/green-threads/README.md
Normal file
@@ -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
|
||||||
|
|
||||||
|
|
||||||
1
__concurrent/green-threads/rust-toolchain
Normal file
1
__concurrent/green-threads/rust-toolchain
Normal file
@@ -0,0 +1 @@
|
|||||||
|
nightly
|
||||||
200
__concurrent/green-threads/src/main.rs
Normal file
200
__concurrent/green-threads/src/main.rs
Normal file
@@ -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<Thread>,
|
||||||
|
current: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
enum State {
|
||||||
|
Available,
|
||||||
|
Running,
|
||||||
|
Ready,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Thread {
|
||||||
|
id: usize,
|
||||||
|
stack: Vec<u8>,
|
||||||
|
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<Thread> = (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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user