├── .gitignore ├── Cargo.toml ├── README.md ├── examples └── echo.rs └── src ├── executor.rs ├── lib.rs ├── reactor.rs └── tcp.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["ihciah "] 3 | edition = "2021" 4 | name = "mini-rust-runtime" 5 | version = "0.1.0" 6 | license = "MIT" 7 | 8 | [dependencies] 9 | polling = "2" 10 | socket2 = "0.4" 11 | libc = "0.2" 12 | nix = "0.23" 13 | scoped-tls = "1" 14 | futures = "0.3" 15 | waker-fn = "1" 16 | pin-utils = "0.1" 17 | rustc-hash = "1" 18 | 19 | tokio = {version = "1", features = ["io-util"]} 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mini Rust Runtime 2 | 3 | To show how a runtime works. 4 | 5 | The related post: [Rust Runtime 设计与实现-科普篇](https://www.ihcblog.com/rust-runtime-design-1/) 6 | 7 | Ref: https://github.com/fujita/greeter -------------------------------------------------------------------------------- /examples/echo.rs: -------------------------------------------------------------------------------- 1 | //! Echo example. 2 | //! Use `nc 127.0.0.1 30000` to connect. 3 | 4 | use futures::StreamExt; 5 | use mini_rust_runtime::executor::Executor; 6 | use mini_rust_runtime::tcp::TcpListener; 7 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; 8 | 9 | fn main() { 10 | let ex = Executor::new(); 11 | ex.block_on(serve); 12 | } 13 | 14 | async fn serve() { 15 | let mut listener = TcpListener::bind("127.0.0.1:30000").unwrap(); 16 | while let Some(ret) = listener.next().await { 17 | if let Ok((mut stream, addr)) = ret { 18 | println!("accept a new connection from {} successfully", addr); 19 | let f = async move { 20 | let mut buf = [0; 4096]; 21 | loop { 22 | match stream.read(&mut buf).await { 23 | Ok(n) => { 24 | if n == 0 || stream.write_all(&buf[..n]).await.is_err() { 25 | return; 26 | } 27 | } 28 | Err(_) => { 29 | return; 30 | } 31 | } 32 | } 33 | }; 34 | Executor::spawn(f); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/executor.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::RefCell, 3 | collections::VecDeque, 4 | marker::PhantomData, 5 | mem, 6 | rc::Rc, 7 | task::{RawWaker, RawWakerVTable, Waker, Context}, pin::Pin, 8 | }; 9 | 10 | use futures::{future::LocalBoxFuture, Future, FutureExt}; 11 | 12 | use crate::reactor::Reactor; 13 | 14 | scoped_tls::scoped_thread_local!(pub(crate) static EX: Executor); 15 | 16 | pub struct Executor { 17 | local_queue: TaskQueue, 18 | pub(crate) reactor: Rc>, 19 | 20 | /// Make sure the type is `!Send` and `!Sync`. 21 | _marker: PhantomData>, 22 | } 23 | 24 | impl Default for Executor { 25 | fn default() -> Self { 26 | Self::new() 27 | } 28 | } 29 | 30 | 31 | impl Executor { 32 | pub fn new() -> Self { 33 | Self { 34 | local_queue: TaskQueue::default(), 35 | reactor: Rc::new(RefCell::new(Reactor::default())), 36 | 37 | _marker: PhantomData, 38 | } 39 | } 40 | 41 | pub fn spawn(fut: impl Future + 'static) { 42 | let t = Rc::new(Task { 43 | future: RefCell::new(fut.boxed_local()), 44 | }); 45 | EX.with(|ex| ex.local_queue.push(t)); 46 | } 47 | 48 | pub fn block_on(&self, f: F) -> O 49 | where 50 | F: Fn() -> T, 51 | T: Future + 'static, 52 | { 53 | let _waker = waker_fn::waker_fn(|| {}); 54 | let cx = &mut Context::from_waker(&_waker); 55 | 56 | EX.set(self, || { 57 | let fut = f(); 58 | pin_utils::pin_mut!(fut); 59 | loop { 60 | // return if the outer future is ready 61 | if let std::task::Poll::Ready(t) = fut.as_mut().poll(cx) { 62 | break t; 63 | } 64 | 65 | // consume all tasks 66 | while let Some(t) = self.local_queue.pop() { 67 | let future = t.future.borrow_mut(); 68 | let w = waker(t.clone()); 69 | let mut context = Context::from_waker(&w); 70 | let _ = Pin::new(future).as_mut().poll(&mut context); 71 | } 72 | 73 | // no task to execute now, it may ready 74 | if let std::task::Poll::Ready(t) = fut.as_mut().poll(cx) { 75 | break t; 76 | } 77 | 78 | // block for io 79 | self.reactor.borrow_mut().wait(); 80 | } 81 | }) 82 | } 83 | } 84 | 85 | pub struct TaskQueue { 86 | queue: RefCell>>, 87 | } 88 | 89 | impl Default for TaskQueue { 90 | fn default() -> Self { 91 | Self::new() 92 | } 93 | } 94 | 95 | impl TaskQueue { 96 | pub fn new() -> Self { 97 | const DEFAULT_TASK_QUEUE_SIZE: usize = 4096; 98 | Self::new_with_capacity(DEFAULT_TASK_QUEUE_SIZE) 99 | } 100 | pub fn new_with_capacity(capacity: usize) -> Self { 101 | Self { 102 | queue: RefCell::new(VecDeque::with_capacity(capacity)), 103 | } 104 | } 105 | 106 | pub(crate) fn push(&self, runnable: Rc) { 107 | println!("add task"); 108 | self.queue.borrow_mut().push_back(runnable); 109 | } 110 | 111 | pub(crate) fn pop(&self) -> Option> { 112 | println!("remove task"); 113 | self.queue.borrow_mut().pop_front() 114 | } 115 | } 116 | 117 | pub struct Task { 118 | future: RefCell>, 119 | } 120 | 121 | fn waker(wake: Rc) -> Waker { 122 | let ptr = Rc::into_raw(wake) as *const (); 123 | let vtable = &Helper::VTABLE; 124 | unsafe { Waker::from_raw(RawWaker::new(ptr, vtable)) } 125 | } 126 | 127 | impl Task { 128 | fn wake_(self: Rc) { 129 | Self::wake_by_ref_(&self) 130 | } 131 | 132 | fn wake_by_ref_(self: &Rc) { 133 | EX.with(|ex| ex.local_queue.push(self.clone())); 134 | } 135 | } 136 | 137 | struct Helper; 138 | 139 | impl Helper { 140 | const VTABLE: RawWakerVTable = RawWakerVTable::new( 141 | Self::clone_waker, 142 | Self::wake, 143 | Self::wake_by_ref, 144 | Self::drop_waker, 145 | ); 146 | 147 | unsafe fn clone_waker(data: *const ()) -> RawWaker { 148 | increase_refcount(data); 149 | let vtable = &Self::VTABLE; 150 | RawWaker::new(data, vtable) 151 | } 152 | 153 | unsafe fn wake(ptr: *const ()) { 154 | let rc = Rc::from_raw(ptr as *const Task); 155 | rc.wake_(); 156 | } 157 | 158 | unsafe fn wake_by_ref(ptr: *const ()) { 159 | let rc = mem::ManuallyDrop::new(Rc::from_raw(ptr as *const Task)); 160 | rc.wake_by_ref_(); 161 | } 162 | 163 | unsafe fn drop_waker(ptr: *const ()) { 164 | drop(Rc::from_raw(ptr as *const Task)); 165 | } 166 | } 167 | 168 | #[allow(clippy::redundant_clone)] // The clone here isn't actually redundant. 169 | unsafe fn increase_refcount(data: *const ()) { 170 | // Retain Rc, but don't touch refcount by wrapping in ManuallyDrop 171 | let rc = mem::ManuallyDrop::new(Rc::::from_raw(data as *const Task)); 172 | // Now increase refcount, but don't drop new refcount either 173 | let _rc_clone: mem::ManuallyDrop<_> = rc.clone(); 174 | } 175 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | pub mod executor; 4 | pub mod tcp; 5 | 6 | mod reactor; 7 | -------------------------------------------------------------------------------- /src/reactor.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::RefCell, 3 | os::unix::prelude::{AsRawFd, RawFd}, 4 | rc::Rc, 5 | task::{Context, Waker}, 6 | }; 7 | 8 | use polling::{Event, Poller}; 9 | 10 | #[inline] 11 | pub(crate) fn get_reactor() -> Rc> { 12 | crate::executor::EX.with(|ex| ex.reactor.clone()) 13 | } 14 | 15 | #[derive(Debug)] 16 | pub struct Reactor { 17 | poller: Poller, 18 | waker_mapping: rustc_hash::FxHashMap, 19 | 20 | buffer: Vec, 21 | } 22 | 23 | impl Reactor { 24 | pub fn new() -> Self { 25 | Self { 26 | poller: Poller::new().unwrap(), 27 | waker_mapping: Default::default(), 28 | 29 | buffer: Vec::with_capacity(2048), 30 | } 31 | } 32 | 33 | // Epoll related 34 | pub fn add(&mut self, fd: RawFd) { 35 | println!("[reactor] add fd {}", fd); 36 | 37 | let flags = 38 | nix::fcntl::OFlag::from_bits(nix::fcntl::fcntl(fd, nix::fcntl::F_GETFL).unwrap()) 39 | .unwrap(); 40 | let flags_nonblocking = flags | nix::fcntl::OFlag::O_NONBLOCK; 41 | nix::fcntl::fcntl(fd, nix::fcntl::F_SETFL(flags_nonblocking)).unwrap(); 42 | self.poller 43 | .add(fd, polling::Event::none(fd as usize)) 44 | .unwrap(); 45 | } 46 | 47 | pub fn modify_readable(&mut self, fd: RawFd, cx: &mut Context) { 48 | println!("[reactor] modify_readable fd {} token {}", fd, fd * 2); 49 | 50 | self.push_completion(fd as u64 * 2, cx); 51 | let event = polling::Event::readable(fd as usize); 52 | self.poller.modify(fd, event); 53 | } 54 | 55 | pub fn modify_writable(&mut self, fd: RawFd, cx: &mut Context) { 56 | println!("[reactor] modify_writable fd {}, token {}", fd, fd * 2 + 1); 57 | 58 | self.push_completion(fd as u64 * 2 + 1, cx); 59 | let event = polling::Event::writable(fd as usize); 60 | self.poller.modify(fd, event); 61 | } 62 | 63 | pub fn wait(&mut self) { 64 | println!("[reactor] waiting"); 65 | self.poller.wait(&mut self.buffer, None); 66 | println!("[reactor] wait done"); 67 | 68 | for i in 0..self.buffer.len() { 69 | let event = self.buffer.swap_remove(0); 70 | if event.readable { 71 | if let Some(waker) = self.waker_mapping.remove(&(event.key as u64 * 2)) { 72 | println!( 73 | "[reactor token] fd {} read waker token {} removed and woken", 74 | event.key, 75 | event.key * 2 76 | ); 77 | waker.wake(); 78 | } 79 | } 80 | if event.writable { 81 | if let Some(waker) = self.waker_mapping.remove(&(event.key as u64 * 2 + 1)) { 82 | println!( 83 | "[reactor token] fd {} write waker token {} removed and woken", 84 | event.key, 85 | event.key * 2 + 1 86 | ); 87 | waker.wake(); 88 | } 89 | } 90 | } 91 | } 92 | 93 | pub fn delete(&mut self, fd: RawFd) { 94 | println!("[reactor] delete fd {}", fd); 95 | 96 | self.waker_mapping.remove(&(fd as u64 * 2)); 97 | self.waker_mapping.remove(&(fd as u64 * 2 + 1)); 98 | println!( 99 | "[reactor token] fd {} wakers token {}, {} removed", 100 | fd, 101 | fd * 2, 102 | fd * 2 + 1 103 | ); 104 | } 105 | 106 | fn push_completion(&mut self, token: u64, cx: &mut Context) { 107 | println!("[reactor token] token {} waker saved", token); 108 | 109 | self.waker_mapping.insert(token, cx.waker().clone()); 110 | } 111 | } 112 | 113 | impl Default for Reactor { 114 | fn default() -> Self { 115 | Self::new() 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/tcp.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::RefCell, 3 | io::{self, Read, Write}, 4 | net::{SocketAddr, TcpListener as StdTcpListener, TcpStream as StdTcpStream, ToSocketAddrs}, 5 | os::unix::prelude::AsRawFd, 6 | rc::{Rc, Weak}, 7 | task::Poll, 8 | }; 9 | 10 | use futures::Stream; 11 | use socket2::{Domain, Protocol, Socket, Type}; 12 | 13 | use crate::{reactor::get_reactor, reactor::Reactor}; 14 | 15 | #[derive(Debug)] 16 | pub struct TcpListener { 17 | reactor: Weak>, 18 | listener: StdTcpListener, 19 | } 20 | 21 | impl TcpListener { 22 | pub fn bind(addr: A) -> Result { 23 | let addr = addr 24 | .to_socket_addrs()? 25 | .next() 26 | .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "empty address"))?; 27 | 28 | let domain = if addr.is_ipv6() { 29 | Domain::IPV6 30 | } else { 31 | Domain::IPV4 32 | }; 33 | let sk = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?; 34 | let addr = socket2::SockAddr::from(addr); 35 | sk.set_reuse_address(true)?; 36 | sk.bind(&addr)?; 37 | sk.listen(1024)?; 38 | 39 | // add fd to reactor 40 | let reactor = get_reactor(); 41 | reactor.borrow_mut().add(sk.as_raw_fd()); 42 | 43 | println!("tcp bind with fd {}", sk.as_raw_fd()); 44 | Ok(Self { 45 | reactor: Rc::downgrade(&reactor), 46 | listener: sk.into(), 47 | }) 48 | } 49 | } 50 | 51 | impl Stream for TcpListener { 52 | type Item = std::io::Result<(TcpStream, SocketAddr)>; 53 | 54 | fn poll_next( 55 | self: std::pin::Pin<&mut Self>, 56 | cx: &mut std::task::Context<'_>, 57 | ) -> std::task::Poll> { 58 | match self.listener.accept() { 59 | Ok((stream, addr)) => Poll::Ready(Some(Ok((stream.into(), addr)))), 60 | Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { 61 | // modify reactor to register interest 62 | let reactor = self.reactor.upgrade().unwrap(); 63 | reactor 64 | .borrow_mut() 65 | .modify_readable(self.listener.as_raw_fd(), cx); 66 | Poll::Pending 67 | } 68 | Err(e) => std::task::Poll::Ready(Some(Err(e))), 69 | } 70 | } 71 | } 72 | 73 | #[derive(Debug)] 74 | pub struct TcpStream { 75 | stream: StdTcpStream, 76 | } 77 | 78 | impl From for TcpStream { 79 | fn from(stream: StdTcpStream) -> Self { 80 | let reactor = get_reactor(); 81 | reactor.borrow_mut().add(stream.as_raw_fd()); 82 | Self { stream } 83 | } 84 | } 85 | 86 | impl Drop for TcpStream { 87 | fn drop(&mut self) { 88 | println!("drop"); 89 | let reactor = get_reactor(); 90 | reactor.borrow_mut().delete(self.stream.as_raw_fd()); 91 | } 92 | } 93 | 94 | impl tokio::io::AsyncRead for TcpStream { 95 | fn poll_read( 96 | mut self: std::pin::Pin<&mut Self>, 97 | cx: &mut std::task::Context<'_>, 98 | buf: &mut tokio::io::ReadBuf<'_>, 99 | ) -> Poll> { 100 | let fd = self.stream.as_raw_fd(); 101 | unsafe { 102 | let b = &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit] as *mut [u8]); 103 | println!("read for fd {}", fd); 104 | match self.stream.read(b) { 105 | Ok(n) => { 106 | println!("read for fd {} done, {}", fd, n); 107 | buf.assume_init(n); 108 | buf.advance(n); 109 | Poll::Ready(Ok(())) 110 | } 111 | Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { 112 | println!("read for fd {} done WouldBlock", fd); 113 | // modify reactor to register interest 114 | let reactor = get_reactor(); 115 | reactor 116 | .borrow_mut() 117 | .modify_readable(self.stream.as_raw_fd(), cx); 118 | Poll::Pending 119 | } 120 | Err(e) => { 121 | println!("read for fd {} done err", fd); 122 | Poll::Ready(Err(e)) 123 | } 124 | } 125 | } 126 | } 127 | } 128 | 129 | impl tokio::io::AsyncWrite for TcpStream { 130 | fn poll_write( 131 | mut self: std::pin::Pin<&mut Self>, 132 | cx: &mut std::task::Context<'_>, 133 | buf: &[u8], 134 | ) -> Poll> { 135 | match self.stream.write(buf) { 136 | Ok(n) => Poll::Ready(Ok(n)), 137 | Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { 138 | let reactor = get_reactor(); 139 | reactor 140 | .borrow_mut() 141 | .modify_writable(self.stream.as_raw_fd(), cx); 142 | Poll::Pending 143 | } 144 | Err(e) => Poll::Ready(Err(e)), 145 | } 146 | } 147 | 148 | fn poll_flush( 149 | self: std::pin::Pin<&mut Self>, 150 | cx: &mut std::task::Context<'_>, 151 | ) -> Poll> { 152 | Poll::Ready(Ok(())) 153 | } 154 | 155 | fn poll_shutdown( 156 | self: std::pin::Pin<&mut Self>, 157 | cx: &mut std::task::Context<'_>, 158 | ) -> Poll> { 159 | self.stream.shutdown(std::net::Shutdown::Write)?; 160 | Poll::Ready(Ok(())) 161 | } 162 | } 163 | --------------------------------------------------------------------------------