├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── greeter-async-std ├── Cargo.toml └── src │ └── main.rs ├── greeter-glommio ├── Cargo.toml └── src │ └── main.rs ├── greeter-minimum ├── Cargo.toml └── src │ ├── main.rs │ └── runtime.rs ├── greeter-noasync ├── Cargo.toml └── src │ └── main.rs ├── greeter-smol ├── Cargo.toml └── src │ └── main.rs ├── greeter-tokio ├── Cargo.toml └── src │ └── main.rs └── proto ├── Cargo.toml └── src ├── client.rs ├── helloworld.proto ├── helloworld.rs └── lib.rs /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | setup: 7 | name: setup 8 | runs-on: ubuntu-18.04 9 | steps: 10 | - name: checking out 11 | uses: actions/checkout@v2 12 | - name: install 13 | run: | 14 | rustup component add rustfmt --toolchain stable-x86_64-unknown-linux-gnu 15 | rustup component add clippy 16 | 17 | - name: async-std 18 | run: | 19 | cd /home/runner/work/greeter/greeter/greeter-async-std 20 | cargo clippy -- -D warnings 21 | cargo test --verbose --all 22 | cargo fmt -- --check 23 | 24 | - name: glommio 25 | run: | 26 | cd /home/runner/work/greeter/greeter/greeter-glommio 27 | cargo clippy -- -D warnings 28 | cargo test --verbose --all 29 | cargo fmt -- --check 30 | 31 | - name: minimum 32 | run: | 33 | cd /home/runner/work/greeter/greeter/greeter-minimum 34 | cargo clippy -- -D warnings 35 | cargo test --verbose --all 36 | cargo fmt -- --check 37 | 38 | - name: smol 39 | run: | 40 | cd /home/runner/work/greeter/greeter/greeter-smol 41 | cargo clippy -- -D warnings 42 | cargo test --verbose --all 43 | cargo fmt -- --check 44 | 45 | - name: tokio 46 | run: | 47 | cd /home/runner/work/greeter/greeter/greeter-tokio 48 | cargo clippy -- -D warnings 49 | cargo test --verbose --all 50 | cargo fmt -- --check 51 | 52 | - name: noasync 53 | run: | 54 | cd /home/runner/work/greeter/greeter/greeter-noasync 55 | cargo clippy -- -D warnings 56 | cargo test --verbose --all 57 | cargo fmt -- --check 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The performance of Rust async runtime with io-uring 2 | 3 | This repository is for playing Rust async runtimes to investigate how io-uring could improve the performance. 4 | 5 | ## async runtime supporting multiple I/O interfaces 6 | 7 | How should server software handle lots of clients simultaneously? Implemented [simple async runtime](https://github.com/fujita/greeter/tree/master/greeter-minimum) supporting the following four I/O strategies. 8 | 9 |
10 |
epoll
11 |
epoll + non-blocking socket API
12 |
uring-poll
13 |
simply replace epoll with io-uring; io-uring (IORING_OP_POLL_ADD) + non-blocking socket API
14 |
async
15 |
purely io-uring (IORING_OP_{ACCEPT|RECV|SEND})
16 |
hybrid
17 |
try non-blocking socket API first, it fails (not ready), do io-uring (IORING_OP_{ACCEPT|RECV|SEND})
18 |
19 | 20 | ### Hardware 21 | 22 | - Server: c6gn.8xlarge (32 vCPU/64 GiB) / Kernel 5.8.0 23 | - Client: c5n.9xlarge (36 vCPU/96 GiB) 24 | 25 | ### Benchmark 26 | 27 | ``` 28 | ghz --insecure --proto helloworld.proto --call helloworld.Greeter.SayHello -d '{\"name\":\"Joe\"}' --connections=3000 172.31.22.145:50051 -c 12000 -n 3000000 -t 0 29 | ``` 30 | 31 | - One client machine runs 3,000 gRPC clients (i.e. 3,000 HTTP/2 clients). 32 | - One client machine issues 3,000,000 requests in total. 33 | - Tested four client machines. 34 | 35 | ![Throughput (requests per second)](https://raw.githubusercontent.com/fujita/greeter/images/20210307-01.png) 36 | 37 | 38 | ## gRPC greeter server supporting multiple async runtimes 39 | 40 | Tested with the following runtimes. 41 | 42 | - glommio 43 | - Tokio 44 | - smol 45 | - async-std 46 | - minimum (simple runtime that shares nothing between CPUs and uses io_uring API with IORING_OP_POLL_ADD) 47 | 48 | ### Hardware 49 | 50 | - Server: c6gn.8xlarge (32 vCPU/64 GiB) / Kernel 5.8.0 51 | - Client: c5n.9xlarge (36 vCPU/96 GiB) 52 | 53 | ### Benchmark 54 | 55 | ``` 56 | ghz --insecure --proto helloworld.proto --call helloworld.Greeter.SayHello -d '{\"name\":\"Joe\"}' --connections=3000 172.31.22.145:50051 -c 12000 -n 3000000 -t 0 57 | ``` 58 | 59 | - One client machine runs 3,000 gRPC clients (i.e. 3,000 HTTP/2 clients). 60 | - One client machine issues 3,000,000 requests in total. 61 | - Tested with one, two, four, and eight client machines. 62 | 63 | ![Throughput (requests per second)](https://raw.githubusercontent.com/fujita/greeter/images/20210219-01.png) 64 | -------------------------------------------------------------------------------- /greeter-async-std/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "greeter-async-std" 3 | version = "0.1.0" 4 | authors = ["FUJITA Tomonori "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | proto = { path="../proto" } 11 | async-std = "1.9" 12 | async-compat = "0.2" 13 | -------------------------------------------------------------------------------- /greeter-async-std/src/main.rs: -------------------------------------------------------------------------------- 1 | use async_std::net::TcpListener; 2 | use async_std::task; 3 | 4 | fn main() { 5 | println!("Hello, greeter-async-std!"); 6 | 7 | task::block_on(async { 8 | let listener: TcpListener = TcpListener::from(proto::create_listen_socket()); 9 | loop { 10 | let (stream, _) = listener.accept().await.unwrap(); 11 | task::spawn(async move { 12 | proto::client::Client::new(async_compat::Compat::new(stream)) 13 | .serve() 14 | .await; 15 | }); 16 | } 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /greeter-glommio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "greeter-glommio" 3 | version = "0.1.0" 4 | authors = ["FUJITA Tomonori "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | proto = { path="../proto" } 11 | glommio = { git = "https://github.com/DataDog/glommio", rev = "19ab20f" } 12 | async-compat = "0.2" 13 | num_cpus = "1.0" 14 | futures-util = "0.3" 15 | -------------------------------------------------------------------------------- /greeter-glommio/src/main.rs: -------------------------------------------------------------------------------- 1 | use futures_util::stream::StreamExt; 2 | use glommio::net::TcpListener; 3 | use glommio::{LocalExecutorBuilder, Task}; 4 | 5 | fn main() { 6 | let cpus = num_cpus::get(); 7 | println!("Hello, greeter-glommio ({} cpus)!", cpus); 8 | 9 | let mut handles = Vec::new(); 10 | for i in 0..cpus { 11 | let h = std::thread::spawn(move || { 12 | let ex = LocalExecutorBuilder::new().pin_to_cpu(i).make().unwrap(); 13 | ex.run(async move { 14 | let listener = TcpListener::bind("[::]:50051").unwrap(); 15 | let mut incoming = listener.incoming(); 16 | while let Some(stream) = incoming.next().await { 17 | Task::local(async move { 18 | proto::client::Client::new(async_compat::Compat::new(stream.unwrap())) 19 | .serve() 20 | .await; 21 | }) 22 | .detach(); 23 | } 24 | }); 25 | }); 26 | handles.push(h); 27 | } 28 | for h in handles { 29 | let _ = h.join(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /greeter-minimum/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "greeter-minimum" 3 | version = "0.1.0" 4 | authors = ["FUJITA Tomonori "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | proto = { path="../proto" } 11 | num_cpus = "1.0" 12 | core_affinity = "0.5" 13 | nix = "0.18" 14 | futures = "0.3" 15 | futures-util = "0.3" 16 | tokio = { version = "1", features = ["full"] } 17 | rustc-hash = "1" 18 | libc = "0.2" 19 | iou = "0.3" 20 | uring-sys = "0.7" 21 | clap = "2.0" 22 | polling = "2.0.2" 23 | pin-utils = "0.1" 24 | waker-fn = "1" 25 | -------------------------------------------------------------------------------- /greeter-minimum/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::clap_app; 2 | use futures_util::stream::StreamExt; 3 | use std::{env, thread}; 4 | 5 | mod runtime; 6 | 7 | async fn serve() { 8 | let mut listener = runtime::Async::::new(proto::create_listen_socket()); 9 | while let Some(ret) = listener.next().await { 10 | if let Ok(stream) = ret { 11 | runtime::spawn(async move { 12 | proto::client::Client::new(stream).serve().await; 13 | }); 14 | } 15 | } 16 | } 17 | 18 | fn main() { 19 | let matches = clap_app!(greeter => 20 | (@arg MODE: -m --mode +takes_value "specify I/O strategy, which can be: epoll, async, uringpoll, or hybrid") 21 | ) 22 | .get_matches(); 23 | 24 | let kind = if let Some(m) = matches.value_of("MODE") { 25 | match m { 26 | "epoll" => runtime::Kind::Epoll, 27 | "async" => runtime::Kind::Async, 28 | "uringpoll" => runtime::Kind::UringPoll, 29 | "hybrid" => runtime::Kind::Hybrid, 30 | _ => { 31 | println!("use 'epoll', 'async', 'uringpoll', or 'hybrid'"); 32 | std::process::exit(1); 33 | } 34 | } 35 | } else { 36 | runtime::Kind::Epoll 37 | }; 38 | 39 | let cpus = { 40 | env::var("RUSTMAXPROCS") 41 | .ok() 42 | .and_then(|s| s.parse().ok()) 43 | .unwrap_or_else(num_cpus::get) 44 | }; 45 | 46 | println!("Hello, greeter-minimum: {:?} mode, ({} cpus)!", kind, cpus); 47 | 48 | let mut handles = Vec::new(); 49 | for i in 0..cpus { 50 | let h = thread::spawn(move || { 51 | let ex = runtime::Runtime::new(kind).pin_to_cpu(i); 52 | 53 | ex.run(serve); 54 | }); 55 | handles.push(h); 56 | } 57 | for h in handles { 58 | let _ = h.join(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /greeter-minimum/src/runtime.rs: -------------------------------------------------------------------------------- 1 | use futures::future::{BoxFuture, FutureExt}; 2 | use futures::prelude::*; 3 | use iou::{IoUring, SQE}; 4 | use nix::poll::PollFlags; 5 | use nix::sys::socket::SockFlag; 6 | use polling::Poller; 7 | use std::cell::RefCell; 8 | use std::collections::VecDeque; 9 | use std::io::{Read, Write}; 10 | use std::net::{TcpListener, TcpStream}; 11 | use std::os::unix::io::{AsRawFd, FromRawFd}; 12 | use std::pin::Pin; 13 | use std::task::{Context, Poll, Waker}; 14 | use uring_sys::io_uring_prep_poll_add; 15 | 16 | struct Parking { 17 | kind: Option, 18 | ring: IoUring, 19 | poller: Poller, 20 | completion: rustc_hash::FxHashMap, 21 | } 22 | const NR_TASKS: usize = 2048; 23 | 24 | thread_local! { 25 | static PARKING : RefCell = { 26 | RefCell::new(Parking{ 27 | kind: None, 28 | ring: IoUring::new(4096).unwrap(), 29 | poller: Poller::new().unwrap(), 30 | completion: rustc_hash::FxHashMap::default(), 31 | }) 32 | }; 33 | 34 | static RUNNABLE : RefCell>> = { 35 | RefCell::new(VecDeque::with_capacity(NR_TASKS)) 36 | }; 37 | 38 | } 39 | 40 | impl Parking { 41 | fn add(&mut self, a: &Async) { 42 | let raw_fd = a.io.as_raw_fd(); 43 | match self.kind.as_ref().unwrap() { 44 | Kind::Epoll => { 45 | a.set_nonblocking(true); 46 | self.poller 47 | .add(a.io.as_ref(), polling::Event::none(raw_fd as usize)) 48 | .unwrap(); 49 | } 50 | Kind::UringPoll => a.set_nonblocking(true), 51 | Kind::Async | Kind::Hybrid => a.set_nonblocking(false), 52 | } 53 | } 54 | 55 | fn delete(&mut self, a: &Async) { 56 | if let Kind::Epoll = self.kind.as_ref().unwrap() { 57 | self.poller.delete(a.io.as_ref()).unwrap(); 58 | } 59 | } 60 | 61 | fn modify(&mut self, a: &Async, cx: &mut Context, is_readable: bool) { 62 | let raw_fd = a.io.as_raw_fd() as u64; 63 | let c = Completion::new(Some(cx.waker().clone())); 64 | self.completion.insert(raw_fd, c); 65 | 66 | match self.kind.as_ref().unwrap() { 67 | Kind::Epoll => { 68 | let e = if is_readable { 69 | polling::Event::readable(raw_fd as usize) 70 | } else { 71 | polling::Event::writable(raw_fd as usize) 72 | }; 73 | self.poller.modify(a.io.as_ref(), e).unwrap(); 74 | } 75 | Kind::UringPoll => { 76 | let mut q = self.ring.prepare_sqe().unwrap(); 77 | unsafe { 78 | let flags = if is_readable { 79 | PollFlags::POLLIN.bits() 80 | } else { 81 | PollFlags::POLLOUT.bits() 82 | }; 83 | io_uring_prep_poll_add(q.raw_mut(), raw_fd as i32, flags); 84 | q.set_user_data(raw_fd as u64); 85 | self.ring.submit_sqes().unwrap(); 86 | } 87 | } 88 | _ => unreachable!(), 89 | } 90 | } 91 | 92 | fn wait(&mut self, sleepable: bool) { 93 | match self.kind.as_ref().unwrap() { 94 | Kind::Epoll => { 95 | let mut events = Vec::with_capacity(NR_TASKS); 96 | let _ = self.poller.wait(&mut events, None); 97 | for ev in &events { 98 | if let Some(c) = self.completion.remove(&(ev.key as u64)) { 99 | c.waker.unwrap().wake(); 100 | } 101 | } 102 | } 103 | Kind::UringPoll => { 104 | let _ = self.ring.wait_for_cqes(1); 105 | while let Some(cqe) = self.ring.peek_for_cqe() { 106 | if let Some(c) = self.completion.remove(&cqe.user_data()) { 107 | c.waker.unwrap().wake(); 108 | } 109 | } 110 | } 111 | Kind::Async | Kind::Hybrid => { 112 | if sleepable { 113 | let _ = self.ring.wait_for_cqes(1); 114 | } 115 | while let Some(cqe) = self.ring.peek_for_cqe() { 116 | let result = cqe.result(); 117 | let user_data = cqe.user_data(); 118 | self.completion.entry(user_data).and_modify(|e| { 119 | e.complete(result.map(|x| x as usize)); 120 | }); 121 | } 122 | } 123 | } 124 | } 125 | } 126 | 127 | fn run_task(t: Rc) { 128 | let future = t.future.borrow_mut(); 129 | let w = waker(t.clone()); 130 | let mut context = Context::from_waker(&w); 131 | let _ = Pin::new(future).as_mut().poll(&mut context); 132 | } 133 | 134 | struct Task { 135 | future: RefCell>, 136 | } 137 | 138 | impl RcWake for Task { 139 | fn wake_by_ref(arc_self: &Rc) { 140 | RUNNABLE.with(|runnable| runnable.borrow_mut().push_back(arc_self.clone())); 141 | } 142 | } 143 | 144 | pub fn spawn(future: impl Future + Send + 'static) { 145 | let t = Rc::new(Task { 146 | future: RefCell::new(future.boxed()), 147 | }); 148 | RUNNABLE.with(|runnable| runnable.borrow_mut().push_back(t)); 149 | } 150 | 151 | pub struct Async { 152 | io: Box, 153 | flags: iou::sqe::OFlag, 154 | } 155 | 156 | impl Async { 157 | pub fn new(io: T) -> Self { 158 | let raw_fd = io.as_raw_fd(); 159 | let a = Async { 160 | io: Box::new(io), 161 | flags: nix::fcntl::OFlag::from_bits( 162 | nix::fcntl::fcntl(raw_fd, nix::fcntl::F_GETFL).unwrap(), 163 | ) 164 | .unwrap(), 165 | }; 166 | PARKING.with(|parker| { 167 | parker.borrow_mut().add(&a); 168 | }); 169 | a 170 | } 171 | 172 | fn set_nonblocking(&self, enable: bool) { 173 | let raw_fd = self.io.as_raw_fd(); 174 | let flags = if enable { 175 | self.flags | nix::fcntl::OFlag::O_NONBLOCK 176 | } else { 177 | self.flags & !nix::fcntl::OFlag::O_NONBLOCK 178 | }; 179 | nix::fcntl::fcntl(raw_fd, nix::fcntl::F_SETFL(flags)).unwrap(); 180 | } 181 | 182 | fn has_completion(&self) -> bool { 183 | let raw_fd = self.io.as_raw_fd() as u64; 184 | PARKING.with(|parking| { 185 | let parking = parking.borrow_mut(); 186 | parking.completion.contains_key(&raw_fd) 187 | }) 188 | } 189 | 190 | fn poll_submit<'a>( 191 | &'a mut self, 192 | cx: &mut Context<'_>, 193 | f: impl FnOnce(&mut SQE<'_>), 194 | ) -> Option> { 195 | let c = Completion::new(None); 196 | let raw_fd = self.io.as_raw_fd() as u64; 197 | PARKING.with(|parking| { 198 | let mut parking = parking.borrow_mut(); 199 | let mut q = parking.ring.prepare_sqe().unwrap(); 200 | f(&mut q); 201 | unsafe { 202 | q.set_user_data(raw_fd as u64); 203 | } 204 | parking.completion.insert(raw_fd, c); 205 | parking.ring.submit_sqes().unwrap(); 206 | 207 | parking.wait(false); 208 | }); 209 | self.poll_finish(cx) 210 | } 211 | 212 | fn poll_finish(&mut self, cx: &mut Context<'_>) -> Option> { 213 | let raw_fd = self.io.as_raw_fd() as u64; 214 | let is_completed = PARKING.with(|parking| { 215 | let mut parking = parking.borrow_mut(); 216 | let c = parking.completion.get_mut(&raw_fd).unwrap(); 217 | if c.result.is_none() { 218 | c.waker.replace(cx.waker().clone()); 219 | None 220 | } else { 221 | Some(c.result.take().unwrap()) 222 | } 223 | }); 224 | is_completed.map(|r| { 225 | PARKING.with(|parking| { 226 | parking.borrow_mut().completion.remove(&raw_fd); 227 | }); 228 | r 229 | }) 230 | } 231 | } 232 | 233 | impl Drop for Async { 234 | fn drop(&mut self) { 235 | PARKING.with(|parking| { 236 | parking.borrow_mut().delete(self); 237 | }); 238 | } 239 | } 240 | 241 | impl Async { 242 | fn poll_sync( 243 | self: Pin<&mut Self>, 244 | cx: &mut Context, 245 | ) -> Poll>>> { 246 | match self.as_ref().io.accept() { 247 | Ok((stream, _)) => Poll::Ready(Some(Ok(Async::::new(stream)))), 248 | Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { 249 | PARKING.with(|parking| { 250 | let mut parking = parking.borrow_mut(); 251 | parking.modify(&self, cx, true); 252 | }); 253 | Poll::Pending 254 | } 255 | Err(e) => std::task::Poll::Ready(Some(Err(e))), 256 | } 257 | } 258 | } 259 | 260 | impl Stream for Async { 261 | type Item = std::io::Result>; 262 | 263 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 264 | let kind = PARKING.with(|parking| parking.borrow().kind.unwrap()); 265 | let raw_fd = self.io.as_raw_fd(); 266 | let new_stream = |x: i32| { 267 | let stream = unsafe { TcpStream::from_raw_fd(x as i32) }; 268 | std::task::Poll::Ready(Some(Ok(Async::::new(stream)))) 269 | }; 270 | match kind { 271 | Kind::Epoll | Kind::UringPoll => self.poll_sync(cx), 272 | Kind::Async | Kind::Hybrid => { 273 | if !self.has_completion() { 274 | if kind == Kind::Hybrid || kind == Kind::Async { 275 | self.set_nonblocking(true); 276 | let res = self.as_ref().io.accept(); 277 | self.set_nonblocking(false); 278 | if let Ok((stream, _)) = res { 279 | return Poll::Ready(Some(Ok(Async::::new(stream)))); 280 | } 281 | } 282 | 283 | if let Some(x) = self.poll_submit(cx, |q| unsafe { 284 | q.prep_accept(raw_fd, None, SockFlag::empty()); 285 | }) { 286 | match x { 287 | Ok(x) => return new_stream(x as i32), 288 | Err(e) => return std::task::Poll::Ready(Some(Err(e))), 289 | } 290 | } 291 | return std::task::Poll::Pending; 292 | } 293 | 294 | match self.poll_finish(cx) { 295 | Some(x) => match x { 296 | Ok(x) => new_stream(x as i32), 297 | Err(e) => std::task::Poll::Ready(Some(Err(e))), 298 | }, 299 | None => std::task::Poll::Pending, 300 | } 301 | } 302 | } 303 | } 304 | } 305 | 306 | impl tokio::io::AsyncRead for Async { 307 | fn poll_read( 308 | mut self: Pin<&mut Self>, 309 | cx: &mut Context<'_>, 310 | buf: &mut tokio::io::ReadBuf<'_>, 311 | ) -> std::task::Poll> { 312 | let raw_fd = self.io.as_raw_fd() as u64; 313 | let kind = PARKING.with(|parking| parking.borrow().kind.unwrap()); 314 | 315 | match kind { 316 | Kind::Epoll | Kind::UringPoll => unsafe { 317 | let b = 318 | &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit] as *mut [u8]); 319 | match self.io.read(b) { 320 | Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { 321 | PARKING.with(|parking| { 322 | let mut parking = parking.borrow_mut(); 323 | parking.modify(&self, cx, true); 324 | }); 325 | Poll::Pending 326 | } 327 | Ok(n) => { 328 | buf.assume_init(n); 329 | buf.advance(n); 330 | Poll::Ready(Ok(())) 331 | } 332 | Err(e) => Poll::Ready(Err(e)), 333 | } 334 | }, 335 | Kind::Async | Kind::Hybrid => unsafe { 336 | let len = buf.remaining(); 337 | let b = 338 | &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit] as *mut [u8]); 339 | let addr = b.as_mut_ptr(); 340 | 341 | if !self.has_completion() { 342 | if kind == Kind::Hybrid { 343 | let res = libc::recv( 344 | raw_fd as i32, 345 | addr as _, 346 | len, 347 | nix::sys::socket::MsgFlags::MSG_DONTWAIT.bits(), 348 | ); 349 | if res >= 0 { 350 | let n = res as usize; 351 | buf.assume_init(n); 352 | buf.advance(n); 353 | return Poll::Ready(Ok(())); 354 | } 355 | } 356 | 357 | if let Some(x) = self.poll_submit(cx, |q| { 358 | uring_sys::io_uring_prep_recv( 359 | q.raw_mut(), 360 | raw_fd as i32, 361 | addr as _, 362 | len as _, 363 | 0, 364 | ); 365 | }) { 366 | match x { 367 | Ok(n) => { 368 | buf.assume_init(n); 369 | buf.advance(n); 370 | return Poll::Ready(Ok(())); 371 | } 372 | Err(e) => return Poll::Ready(Err(e)), 373 | } 374 | } 375 | return std::task::Poll::Pending; 376 | } 377 | match self.poll_finish(cx) { 378 | Some(x) => match x { 379 | Ok(n) => { 380 | buf.assume_init(n); 381 | buf.advance(n); 382 | Poll::Ready(Ok(())) 383 | } 384 | Err(e) => Poll::Ready(Err(e)), 385 | }, 386 | None => std::task::Poll::Pending, 387 | } 388 | }, 389 | } 390 | } 391 | } 392 | 393 | impl tokio::io::AsyncWrite for Async { 394 | fn poll_write( 395 | mut self: Pin<&mut Self>, 396 | cx: &mut Context<'_>, 397 | buf: &[u8], 398 | ) -> std::task::Poll> { 399 | let raw_fd = self.io.as_raw_fd() as u64; 400 | let kind = PARKING.with(|parking| parking.borrow().kind.unwrap()); 401 | match kind { 402 | Kind::Epoll | Kind::UringPoll => match self.io.write(buf) { 403 | Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { 404 | PARKING.with(|parking| { 405 | let mut parking = parking.borrow_mut(); 406 | parking.modify(&self, cx, false); 407 | }); 408 | Poll::Pending 409 | } 410 | x => Poll::Ready(x), 411 | }, 412 | Kind::Async | Kind::Hybrid => { 413 | if !self.has_completion() { 414 | if kind == Kind::Hybrid { 415 | unsafe { 416 | let res = libc::send( 417 | raw_fd as i32, 418 | buf.as_ptr() as _, 419 | buf.len() as _, 420 | nix::sys::socket::MsgFlags::MSG_DONTWAIT.bits(), 421 | ); 422 | if res >= 0 { 423 | return Poll::Ready(Ok(res as usize)); 424 | } 425 | } 426 | } 427 | if let Some(x) = self.poll_submit(cx, |q| unsafe { 428 | uring_sys::io_uring_prep_send( 429 | q.raw_mut(), 430 | raw_fd as i32, 431 | buf.as_ptr() as _, 432 | buf.len() as _, 433 | 0, 434 | ); 435 | }) { 436 | match x { 437 | Ok(n) => return Poll::Ready(Ok(n)), 438 | Err(e) => return Poll::Ready(Err(e)), 439 | } 440 | } 441 | return std::task::Poll::Pending; 442 | } 443 | match self.poll_finish(cx) { 444 | Some(x) => match x { 445 | Ok(n) => Poll::Ready(Ok(n)), 446 | Err(e) => Poll::Ready(Err(e)), 447 | }, 448 | None => std::task::Poll::Pending, 449 | } 450 | } 451 | } 452 | } 453 | 454 | fn poll_flush( 455 | self: Pin<&mut Self>, 456 | _: &mut Context<'_>, 457 | ) -> std::task::Poll> { 458 | Poll::Ready(Ok(())) 459 | } 460 | 461 | fn poll_shutdown( 462 | self: Pin<&mut Self>, 463 | _: &mut Context<'_>, 464 | ) -> std::task::Poll> { 465 | self.io.shutdown(std::net::Shutdown::Write)?; 466 | Poll::Ready(Ok(())) 467 | } 468 | } 469 | 470 | // strolen from the future code 471 | use std::mem::{self, ManuallyDrop}; 472 | use std::rc::Rc; 473 | use std::task::{RawWaker, RawWakerVTable}; 474 | 475 | pub trait RcWake { 476 | fn wake(self: Rc) { 477 | Self::wake_by_ref(&self) 478 | } 479 | 480 | fn wake_by_ref(arc_self: &Rc); 481 | } 482 | 483 | fn waker(wake: Rc) -> Waker 484 | where 485 | W: RcWake, 486 | { 487 | let ptr = Rc::into_raw(wake) as *const (); 488 | let vtable = &Helper::::VTABLE; 489 | unsafe { Waker::from_raw(RawWaker::new(ptr, vtable)) } 490 | } 491 | 492 | #[allow(clippy::redundant_clone)] // The clone here isn't actually redundant. 493 | unsafe fn increase_refcount(data: *const ()) { 494 | // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop 495 | let arc = mem::ManuallyDrop::new(Rc::::from_raw(data as *const T)); 496 | // Now increase refcount, but don't drop new refcount either 497 | let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); 498 | } 499 | 500 | struct Helper(F); 501 | 502 | impl Helper { 503 | const VTABLE: RawWakerVTable = RawWakerVTable::new( 504 | Self::clone_waker, 505 | Self::wake, 506 | Self::wake_by_ref, 507 | Self::drop_waker, 508 | ); 509 | 510 | unsafe fn clone_waker(data: *const ()) -> RawWaker { 511 | increase_refcount::(data); 512 | let vtable = &Helper::::VTABLE; 513 | RawWaker::new(data, vtable) 514 | } 515 | 516 | unsafe fn wake(ptr: *const ()) { 517 | let rc: Rc = Rc::from_raw(ptr as *const T); 518 | RcWake::wake(rc); 519 | } 520 | 521 | unsafe fn wake_by_ref(ptr: *const ()) { 522 | let arc = ManuallyDrop::new(Rc::::from_raw(ptr as *const T)); 523 | RcWake::wake_by_ref(&arc); 524 | } 525 | 526 | unsafe fn drop_waker(ptr: *const ()) { 527 | drop(Rc::from_raw(ptr as *const Task)); 528 | } 529 | } 530 | 531 | #[derive(Debug)] 532 | pub struct Completion { 533 | waker: Option, 534 | result: Option>, 535 | } 536 | 537 | impl Completion { 538 | fn new(w: Option) -> Completion { 539 | Completion { 540 | waker: w, 541 | result: None, 542 | } 543 | } 544 | 545 | fn complete(&mut self, result: std::io::Result) { 546 | self.result = Some(result); 547 | if let Some(w) = self.waker.take() { 548 | w.wake(); 549 | } 550 | } 551 | } 552 | 553 | #[derive(PartialEq, Debug, Clone, Copy)] 554 | pub enum Kind { 555 | Epoll, // socket API with epoll; non-blocking synchronous socket API 556 | UringPoll, // socket API with io_uring (POLL); replace epoll with io_uring (POLL) 557 | Async, // io_uring(ACCEPT/RECV/SEND); always doing asynchronous I/Os. 558 | Hybrid, // try non-blocking synchronous socket API first, it fails, io_uring(ACCEPT/RECV/SEND) 559 | } 560 | 561 | pub struct Runtime { 562 | kind: Kind, 563 | } 564 | 565 | impl Runtime { 566 | pub fn new(kind: Kind) -> Runtime { 567 | Runtime { kind } 568 | } 569 | 570 | pub fn pin_to_cpu(self, id: usize) -> Self { 571 | core_affinity::set_for_current(core_affinity::CoreId { id }); 572 | self 573 | } 574 | 575 | pub fn run(&self, f: F) 576 | where 577 | F: Fn() -> T, 578 | T: Future + Send + 'static, 579 | { 580 | PARKING.with(|parking| { 581 | parking.borrow_mut().kind = Some(self.kind); 582 | }); 583 | 584 | let waker = waker_fn::waker_fn(|| {}); 585 | let cx = &mut Context::from_waker(&waker); 586 | 587 | let fut = f(); 588 | pin_utils::pin_mut!(fut); 589 | 590 | loop { 591 | if fut.as_mut().poll(cx).is_ready() { 592 | break; 593 | } 594 | 595 | loop { 596 | let mut ready = 597 | RUNNABLE.with(|runnable| runnable.replace(VecDeque::with_capacity(NR_TASKS))); 598 | 599 | if ready.is_empty() { 600 | break; 601 | } 602 | 603 | for t in ready.drain(..) { 604 | run_task(t); 605 | } 606 | } 607 | 608 | if fut.as_mut().poll(cx).is_ready() { 609 | break; 610 | } 611 | PARKING.with(|parking| { 612 | parking.borrow_mut().wait(true); 613 | }); 614 | } 615 | } 616 | } 617 | -------------------------------------------------------------------------------- /greeter-noasync/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "greeter-noasync" 3 | version = "0.1.0" 4 | authors = ["FUJITA Tomonori "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | proto = { path="../proto" } 11 | mio = {version="0.7", features = ["os-poll","tcp"]} 12 | socket2 = {version="0.3", features = ["reuseport"]} 13 | solicit = { git = "https://github.com/mlalic/solicit", rev = "fdccb36" } 14 | slice_as_array = "1.0" 15 | hpack = "0.3" 16 | quick-protobuf = "0.7" 17 | num_cpus = "1.0" 18 | core_affinity = "0.5" 19 | lazy_static = "1.4" 20 | nix = "0.18" 21 | byteorder = "1.3.2" 22 | thiserror = "1.0" 23 | bytes = "0.5" 24 | rustc-hash = "1" 25 | -------------------------------------------------------------------------------- /greeter-noasync/src/main.rs: -------------------------------------------------------------------------------- 1 | use byteorder::{BigEndian, ByteOrder}; 2 | use mio::{net::TcpListener, net::TcpStream, Events, Interest, Token}; 3 | use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; 4 | use rustc_hash::FxHashMap; 5 | use solicit::http::connection::HttpFrame; 6 | use solicit::http::frame::{ 7 | unpack_header, DataFrame, Frame, FrameIR, HeadersFlag, HeadersFrame, HttpSetting, PingFrame, 8 | RawFrame, SettingsFrame, WindowUpdateFrame, FRAME_HEADER_LEN, 9 | }; 10 | use solicit::http::{Header, INITIAL_CONNECTION_WINDOW_SIZE}; 11 | use std::borrow::Cow; 12 | use std::cell::RefCell; 13 | use std::collections::{HashMap, VecDeque}; 14 | use std::io::{Cursor, Read, Write}; 15 | use std::os::unix::io::AsRawFd; 16 | use std::time::SystemTime; 17 | use std::{env, io, thread}; 18 | use thiserror::Error; 19 | 20 | #[macro_use] 21 | extern crate lazy_static; 22 | #[macro_use] 23 | extern crate slice_as_array; 24 | 25 | thread_local! { 26 | static POLL : RefCell = { 27 | RefCell::new(mio::Poll::new().unwrap()) 28 | }; 29 | } 30 | 31 | lazy_static! { 32 | static ref HTTP_STATUS_HEADERS: Vec = { 33 | let headers = vec![ 34 | Header::new(b":status", b"200"), 35 | Header::new(b"content-type".to_vec(), b"application/grpc".to_vec()), 36 | ]; 37 | hpack::Encoder::new().encode(headers.iter().map(|h| (h.name(), h.value()))) 38 | }; 39 | static ref GRPC_STATUS_HEADERS: Vec = { 40 | let headers = vec![ 41 | Header::new(b"grpc-status".to_vec(), b"0"), 42 | Header::new(b"grpc-message".to_vec(), b"".to_vec()), 43 | ]; 44 | hpack::Encoder::new().encode(headers.iter().map(|h| (h.name(), h.value()))) 45 | }; 46 | static ref REQUEST_HEADERS: HashMap, Vec> = vec![( 47 | String::from(":path").into_bytes(), 48 | String::from("/helloworld.Greeter/SayHello").into_bytes() 49 | ),] 50 | .into_iter() 51 | .collect(); 52 | static ref PREFACE: Vec = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".to_vec(); 53 | } 54 | 55 | #[derive(Error, Debug)] 56 | pub enum Error { 57 | #[error("wrong preface string")] 58 | WrongPreface, 59 | #[error("wrong headers")] 60 | WrongHeaders, 61 | #[error("disconnected")] 62 | Disconnected(#[from] io::Error), 63 | #[error("wrong http frame")] 64 | WrongHttpFrame(solicit::http::HttpError), 65 | } 66 | 67 | struct Client { 68 | stream: TcpStream, 69 | buffer: bytes::BytesMut, 70 | established: bool, 71 | wqueue: VecDeque>, 72 | ping: u64, 73 | window_size: i32, 74 | decoder: hpack::Decoder<'static>, 75 | } 76 | 77 | impl Drop for Client { 78 | fn drop(&mut self) { 79 | POLL.with(|poll| { 80 | poll.borrow_mut() 81 | .registry() 82 | .deregister(&mut self.stream) 83 | .unwrap(); 84 | }); 85 | } 86 | } 87 | 88 | impl Client { 89 | fn new(mut stream: TcpStream) -> Self { 90 | let raw_fd = stream.as_raw_fd(); 91 | let token = Token(raw_fd as usize); 92 | 93 | let flags = 94 | nix::fcntl::OFlag::from_bits(nix::fcntl::fcntl(raw_fd, nix::fcntl::F_GETFL).unwrap()) 95 | .unwrap() 96 | | nix::fcntl::OFlag::O_NONBLOCK; 97 | nix::fcntl::fcntl(raw_fd, nix::fcntl::F_SETFL(flags)).unwrap(); 98 | 99 | POLL.with(|poll| { 100 | poll.borrow_mut() 101 | .registry() 102 | .register(&mut stream, token, Interest::READABLE | Interest::WRITABLE) 103 | .unwrap(); 104 | }); 105 | 106 | let mut c = Client { 107 | stream, 108 | buffer: bytes::BytesMut::new(), 109 | established: false, 110 | wqueue: VecDeque::new(), 111 | ping: SystemTime::now() 112 | .duration_since(SystemTime::UNIX_EPOCH) 113 | .unwrap() 114 | .as_secs(), 115 | window_size: INITIAL_CONNECTION_WINDOW_SIZE, 116 | decoder: hpack::Decoder::new(), 117 | }; 118 | let mut s = SettingsFrame::new(); 119 | s.add_setting(HttpSetting::MaxFrameSize(16384)); 120 | c.queue(s); 121 | c 122 | } 123 | 124 | fn token(&self) -> Token { 125 | Token(self.stream.as_raw_fd() as usize) 126 | } 127 | 128 | fn queue(&mut self, frame: T) { 129 | let mut buf = Cursor::new(Vec::new()); 130 | frame.serialize_into(&mut buf).unwrap(); 131 | self.wqueue.push_back(buf.into_inner()); 132 | } 133 | 134 | fn flush(&mut self) { 135 | while let Some(buf) = self.wqueue.pop_front() { 136 | if buf.len() > self.window_size as usize { 137 | println!("window size is full!"); 138 | self.wqueue.push_front(buf); 139 | return; 140 | } 141 | match self.stream.write(&buf) { 142 | Ok(_) => {} 143 | Err(_) => { 144 | self.wqueue.push_front(buf); 145 | return; 146 | } 147 | } 148 | } 149 | } 150 | 151 | fn consume(&mut self, size: usize) -> Option> { 152 | if self.buffer.len() < size { 153 | return None; 154 | } 155 | Some(self.buffer.split_to(size).to_vec()) 156 | } 157 | 158 | fn read_all(&mut self) -> Result<(), Error> { 159 | const RESERVE: usize = 8192; 160 | loop { 161 | let len = self.buffer.len(); 162 | self.buffer.reserve(len + RESERVE); 163 | unsafe { 164 | self.buffer.set_len(len + RESERVE); 165 | } 166 | match self.stream.read(&mut self.buffer.as_mut()[len..]) { 167 | Ok(n) => { 168 | unsafe { 169 | self.buffer.set_len(len + n); 170 | } 171 | if n == 0 { 172 | return Err(Error::Disconnected(std::io::Error::new( 173 | std::io::ErrorKind::ConnectionReset, 174 | "read returned zero", 175 | ))); 176 | } 177 | } 178 | Err(e) => { 179 | unsafe { 180 | self.buffer.set_len(len); 181 | } 182 | if e.kind() == io::ErrorKind::WouldBlock { 183 | break; 184 | } 185 | return Err(Error::Disconnected(e)); 186 | } 187 | } 188 | } 189 | Ok(()) 190 | } 191 | 192 | fn handle_dataframe(&mut self, frame: DataFrame) { 193 | let mut buf = Cursor::new(Vec::new()); 194 | 195 | let req = proto::helloworld::HelloRequest::from_reader( 196 | &mut BytesReader::from_bytes(&frame.data[5..]), 197 | &frame.data[5..], 198 | ) 199 | .unwrap(); 200 | 201 | let stream_id = frame.get_stream_id(); 202 | 203 | WindowUpdateFrame::for_connection(frame.payload_len()) 204 | .serialize_into(&mut buf) 205 | .unwrap(); 206 | PingFrame::with_data(self.ping) 207 | .serialize_into(&mut buf) 208 | .unwrap(); 209 | 210 | self.ping += 1; 211 | 212 | let mut frame = HeadersFrame::new(HTTP_STATUS_HEADERS.to_vec(), stream_id); 213 | frame.set_flag(HeadersFlag::EndHeaders); 214 | frame.serialize_into(&mut buf).unwrap(); 215 | 216 | let reply = self.say_hello(req); 217 | 218 | let mut data = vec![0; 5]; 219 | BigEndian::write_u32(&mut data[1..], reply.get_size() as u32); 220 | reply.write_message(&mut Writer::new(&mut data)).unwrap(); 221 | 222 | let frame = DataFrame::with_data(stream_id, data); 223 | self.window_size -= frame.payload_len() as i32; 224 | frame.serialize_into(&mut buf).unwrap(); 225 | 226 | let mut frame = HeadersFrame::new(GRPC_STATUS_HEADERS.to_vec(), stream_id); 227 | frame.set_flag(HeadersFlag::EndHeaders); 228 | frame.set_flag(HeadersFlag::EndStream); 229 | frame.serialize_into(&mut buf).unwrap(); 230 | 231 | self.wqueue.push_back(buf.into_inner()); 232 | } 233 | 234 | #[allow(clippy::transmute_ptr_to_ref)] 235 | fn handle(&mut self, readable: bool, _writable: bool) -> Result<(), Error> { 236 | if readable { 237 | self.read_all()?; 238 | 239 | if !self.established { 240 | match self.consume(PREFACE.len()) { 241 | Some(buf) => { 242 | if &buf != &PREFACE as &'static Vec { 243 | return Err(Error::WrongPreface); 244 | } 245 | self.established = true; 246 | } 247 | None => { 248 | return Ok(()); 249 | } 250 | } 251 | } 252 | 253 | if self.established { 254 | loop { 255 | if self.buffer.len() < FRAME_HEADER_LEN { 256 | break; 257 | } 258 | let header = unpack_header( 259 | slice_as_array!( 260 | &self.buffer.as_ref()[0..FRAME_HEADER_LEN], 261 | [u8; FRAME_HEADER_LEN] 262 | ) 263 | .unwrap(), 264 | ); 265 | 266 | match self.consume(FRAME_HEADER_LEN + header.0 as usize) { 267 | Some(buf) => { 268 | let raw = RawFrame::from(buf); 269 | match HttpFrame::from_raw(&raw) { 270 | Ok(frame) => match frame { 271 | HttpFrame::DataFrame(frame) => { 272 | self.handle_dataframe(frame); 273 | } 274 | HttpFrame::HeadersFrame(frame) => { 275 | for (k, v) in 276 | self.decoder.decode(&frame.header_fragment()).unwrap() 277 | { 278 | if let Some(expected) = REQUEST_HEADERS.get(&k) { 279 | if &v != expected { 280 | // should send an error response instead 281 | return Err(Error::WrongHeaders); 282 | } 283 | } 284 | } 285 | } 286 | HttpFrame::RstStreamFrame(_) => {} 287 | HttpFrame::SettingsFrame(frame) => { 288 | if !frame.is_ack() { 289 | self.queue(SettingsFrame::new_ack()); 290 | } 291 | } 292 | HttpFrame::PingFrame(frame) => { 293 | if !frame.is_ack() { 294 | self.queue(PingFrame::new_ack(frame.opaque_data())); 295 | } 296 | } 297 | HttpFrame::GoawayFrame(_) => {} 298 | HttpFrame::WindowUpdateFrame(frame) => { 299 | self.window_size += frame.increment() as i32; 300 | } 301 | HttpFrame::UnknownFrame(_) => {} 302 | }, 303 | Err(e) => return Err(Error::WrongHttpFrame(e)), 304 | } 305 | } 306 | None => break, 307 | } 308 | } 309 | } 310 | } 311 | self.flush(); 312 | Ok(()) 313 | } 314 | 315 | fn say_hello(&self, req: proto::helloworld::HelloRequest) -> proto::helloworld::HelloReply { 316 | proto::helloworld::HelloReply { 317 | message: Cow::Owned(format!("Hello {}", req.name)), 318 | } 319 | } 320 | } 321 | 322 | struct Greeter { 323 | listener: TcpListener, 324 | } 325 | 326 | impl Greeter { 327 | fn new() -> Self { 328 | let mut listener = TcpListener::from_std(proto::create_listen_socket()); 329 | let listen_token = Token(listener.as_raw_fd() as usize); 330 | POLL.with(|poll| { 331 | poll.borrow_mut() 332 | .registry() 333 | .register(&mut listener, listen_token, Interest::READABLE) 334 | .unwrap(); 335 | }); 336 | Greeter { listener } 337 | } 338 | 339 | fn serve(&self) { 340 | let mut events = Events::with_capacity(2048); 341 | let mut clients = FxHashMap::default(); 342 | let listen_token = Token(self.listener.as_raw_fd() as usize); 343 | 344 | loop { 345 | POLL.with(|poll| { 346 | poll.borrow_mut().poll(&mut events, None).unwrap(); 347 | }); 348 | 349 | for e in events.iter() { 350 | if e.token() == listen_token { 351 | while let Ok((stream, _)) = self.listener.accept() { 352 | let client = Client::new(stream); 353 | clients.insert(client.token(), client); 354 | } 355 | } else if let Some(client) = clients.get_mut(&e.token()) { 356 | let r = client.handle(e.is_readable(), e.is_writable()); 357 | if r.is_err() || e.is_error() || e.is_read_closed() || e.is_write_closed() { 358 | clients.remove(&e.token()); 359 | } 360 | } 361 | } 362 | } 363 | } 364 | } 365 | 366 | fn main() { 367 | let cpus = { 368 | env::var("RUSTMAXPROCS") 369 | .ok() 370 | .and_then(|s| s.parse().ok()) 371 | .unwrap_or_else(num_cpus::get) 372 | }; 373 | 374 | println!("Hello, greeter-noasync ({} cpus)!", cpus); 375 | 376 | let mut handles = Vec::new(); 377 | for i in 0..cpus { 378 | let h = thread::spawn(move || { 379 | core_affinity::set_for_current(core_affinity::CoreId { id: i }); 380 | 381 | Greeter::new().serve(); 382 | }); 383 | handles.push(h); 384 | } 385 | for h in handles { 386 | let _ = h.join(); 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /greeter-smol/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "greeter-smol" 3 | version = "0.1.0" 4 | authors = ["FUJITA Tomonori "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | proto = { path="../proto" } 11 | smol = "1.2" 12 | async-compat = "0.2" 13 | num_cpus = "1.0" 14 | -------------------------------------------------------------------------------- /greeter-smol/src/main.rs: -------------------------------------------------------------------------------- 1 | use smol::Async; 2 | 3 | fn main() { 4 | // smol uses one cpu by default. explicitly needs to specify 5 | if std::env::var("SMOL_THREADS").is_err() { 6 | std::env::set_var("SMOL_THREADS", num_cpus::get().to_string()) 7 | } 8 | 9 | println!("Hello, greeter-smol!"); 10 | 11 | smol::block_on(async { 12 | let listener = Async::new(proto::create_listen_socket()).unwrap(); 13 | loop { 14 | let (stream, _) = listener.accept().await.unwrap(); 15 | smol::spawn(async move { 16 | proto::client::Client::new(async_compat::Compat::new(stream)) 17 | .serve() 18 | .await; 19 | }) 20 | .detach(); 21 | } 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /greeter-tokio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "greeter-tokio" 3 | version = "0.1.0" 4 | authors = ["FUJITA Tomonori "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | proto = { path="../proto" } 11 | tokio = { version = "1", features = ["full"] } 12 | -------------------------------------------------------------------------------- /greeter-tokio/src/main.rs: -------------------------------------------------------------------------------- 1 | use tokio::net::TcpListener; 2 | use tokio::runtime::Runtime; 3 | 4 | fn main() { 5 | println!("Hello, greeter-tokio!"); 6 | 7 | let rt = Runtime::new().unwrap(); 8 | 9 | rt.block_on(async { 10 | let listener = TcpListener::from_std(proto::create_listen_socket()).unwrap(); 11 | 12 | loop { 13 | let (stream, _) = listener.accept().await.unwrap(); 14 | rt.spawn(async move { 15 | proto::client::Client::new(stream).serve().await; 16 | }); 17 | } 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /proto/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "proto" 3 | version = "0.1.0" 4 | authors = ["FUJITA Tomonori "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | mio = {version="0.7", features = ["os-poll","tcp"]} 11 | socket2 = {version="0.3.18", features = ["reuseport"]} 12 | solicit = { git = "https://github.com/mlalic/solicit", rev = "fdccb36" } 13 | slice_as_array = "1.0" 14 | hpack = "0.3" 15 | quick-protobuf = "0.7" 16 | num_cpus = "1.0" 17 | core_affinity = "0.5" 18 | lazy_static = "1.4" 19 | nix = "0.18" 20 | byteorder = "1.3.2" 21 | thiserror = "1.0" 22 | bytes = "0.5" 23 | futures = "0.3" 24 | futures-util = "0.3" 25 | tokio = { version = "1", features = ["full"] } 26 | -------------------------------------------------------------------------------- /proto/src/client.rs: -------------------------------------------------------------------------------- 1 | use byteorder::{BigEndian, ByteOrder}; 2 | use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; 3 | use solicit::http::connection::HttpFrame; 4 | use solicit::http::frame::{ 5 | unpack_header, DataFrame, Frame, FrameIR, HeadersFlag, HeadersFrame, HttpSetting, PingFrame, 6 | RawFrame, SettingsFrame, WindowUpdateFrame, FRAME_HEADER_LEN, 7 | }; 8 | use solicit::http::{Header, INITIAL_CONNECTION_WINDOW_SIZE}; 9 | use std::borrow::Cow; 10 | use std::collections::{HashMap, VecDeque}; 11 | use std::io::Cursor; 12 | use std::time::SystemTime; 13 | use thiserror::Error; 14 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; 15 | 16 | use crate::helloworld; 17 | 18 | lazy_static! { 19 | static ref HTTP_STATUS_HEADERS: Vec = { 20 | let headers = vec![ 21 | Header::new(b":status", b"200"), 22 | Header::new(b"content-type".to_vec(), b"application/grpc".to_vec()), 23 | ]; 24 | hpack::Encoder::new().encode(headers.iter().map(|h| (h.name(), h.value()))) 25 | }; 26 | static ref GRPC_STATUS_HEADERS: Vec = { 27 | let headers = vec![ 28 | Header::new(b"grpc-status".to_vec(), b"0"), 29 | Header::new(b"grpc-message".to_vec(), b"".to_vec()), 30 | ]; 31 | hpack::Encoder::new().encode(headers.iter().map(|h| (h.name(), h.value()))) 32 | }; 33 | static ref REQUEST_HEADERS: HashMap, Vec> = vec![( 34 | String::from(":path").into_bytes(), 35 | String::from("/helloworld.Greeter/SayHello").into_bytes() 36 | ),] 37 | .into_iter() 38 | .collect(); 39 | static ref PREFACE: Vec = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".to_vec(); 40 | } 41 | 42 | #[derive(Error, Debug)] 43 | pub enum Error { 44 | #[error("wrong preface string")] 45 | WrongPreface, 46 | #[error("wrong headers")] 47 | WrongHeaders, 48 | #[error("disconnected")] 49 | Disconnected(#[from] std::io::Error), 50 | #[error("wrong http frame")] 51 | WrongHttpFrame(solicit::http::HttpError), 52 | } 53 | 54 | pub struct Client { 55 | stream: I, 56 | buffer: bytes::BytesMut, 57 | established: bool, 58 | wqueue: VecDeque>, 59 | ping: u64, 60 | window_size: i32, 61 | decoder: hpack::Decoder<'static>, 62 | } 63 | 64 | impl Client 65 | where 66 | I: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin, 67 | { 68 | pub fn new(stream: I) -> Self { 69 | let mut c = Client { 70 | stream, 71 | buffer: bytes::BytesMut::new(), 72 | established: false, 73 | wqueue: VecDeque::new(), 74 | ping: SystemTime::now() 75 | .duration_since(SystemTime::UNIX_EPOCH) 76 | .unwrap() 77 | .as_secs(), 78 | window_size: INITIAL_CONNECTION_WINDOW_SIZE, 79 | decoder: hpack::Decoder::new(), 80 | }; 81 | let mut s = SettingsFrame::new(); 82 | s.add_setting(HttpSetting::MaxFrameSize(16384)); 83 | c.queue(s); 84 | c 85 | } 86 | 87 | fn queue(&mut self, frame: T) { 88 | let mut buf = Cursor::new(Vec::new()); 89 | frame.serialize_into(&mut buf).unwrap(); 90 | self.wqueue.push_back(buf.into_inner()); 91 | } 92 | 93 | async fn flush(&mut self) -> Result<(), Error> { 94 | while let Some(buf) = self.wqueue.pop_front() { 95 | if buf.len() > self.window_size as usize { 96 | println!("window size is full!"); 97 | self.wqueue.push_front(buf); 98 | return Ok(()); 99 | } 100 | 101 | match self.stream.write(&buf).await { 102 | Ok(_) => {} 103 | Err(e) => { 104 | self.wqueue.push_front(buf); 105 | return Err(Error::Disconnected(e)); 106 | } 107 | } 108 | } 109 | Ok(()) 110 | } 111 | 112 | fn consume(&mut self, size: usize) -> Option> { 113 | if self.buffer.len() < size { 114 | return None; 115 | } 116 | Some(self.buffer.split_to(size).to_vec()) 117 | } 118 | 119 | fn handle_dataframe(&mut self, frame: DataFrame) { 120 | let mut buf = Cursor::new(Vec::new()); 121 | 122 | let req = helloworld::HelloRequest::from_reader( 123 | &mut BytesReader::from_bytes(&frame.data[5..]), 124 | &frame.data[5..], 125 | ) 126 | .unwrap(); 127 | 128 | let stream_id = frame.get_stream_id(); 129 | 130 | WindowUpdateFrame::for_connection(frame.payload_len()) 131 | .serialize_into(&mut buf) 132 | .unwrap(); 133 | PingFrame::with_data(self.ping) 134 | .serialize_into(&mut buf) 135 | .unwrap(); 136 | 137 | self.ping += 1; 138 | 139 | let mut frame = HeadersFrame::new(HTTP_STATUS_HEADERS.to_vec(), stream_id); 140 | frame.set_flag(HeadersFlag::EndHeaders); 141 | frame.serialize_into(&mut buf).unwrap(); 142 | 143 | let reply = self.say_hello(req); 144 | 145 | let mut data = vec![0; 5]; 146 | BigEndian::write_u32(&mut data[1..], reply.get_size() as u32); 147 | reply.write_message(&mut Writer::new(&mut data)).unwrap(); 148 | 149 | let frame = DataFrame::with_data(stream_id, data); 150 | self.window_size -= frame.payload_len() as i32; 151 | frame.serialize_into(&mut buf).unwrap(); 152 | 153 | let mut frame = HeadersFrame::new(GRPC_STATUS_HEADERS.to_vec(), stream_id); 154 | frame.set_flag(HeadersFlag::EndHeaders); 155 | frame.set_flag(HeadersFlag::EndStream); 156 | frame.serialize_into(&mut buf).unwrap(); 157 | 158 | self.wqueue.push_back(buf.into_inner()); 159 | } 160 | 161 | #[allow(clippy::transmute_ptr_to_ref)] 162 | async fn handle(&mut self) -> Result<(), Error> { 163 | const RESERVE: usize = 8192; 164 | 165 | let len = self.buffer.len(); 166 | self.buffer.reserve(len + RESERVE); 167 | unsafe { 168 | self.buffer.set_len(len + RESERVE); 169 | } 170 | match self.stream.read(&mut self.buffer.as_mut()[len..]).await { 171 | Ok(n) => { 172 | unsafe { 173 | self.buffer.set_len(len + n); 174 | } 175 | if n == 0 { 176 | return Err(Error::Disconnected(std::io::Error::new( 177 | std::io::ErrorKind::ConnectionReset, 178 | "read returned zero", 179 | ))); 180 | } 181 | } 182 | Err(e) => { 183 | unsafe { 184 | self.buffer.set_len(len); 185 | } 186 | return Err(Error::Disconnected(e)); 187 | } 188 | } 189 | 190 | if !self.established { 191 | match self.consume(PREFACE.len()) { 192 | Some(buf) => { 193 | if &buf != &PREFACE as &'static Vec { 194 | return Err(Error::WrongPreface); 195 | } 196 | self.established = true; 197 | } 198 | None => { 199 | return Ok(()); 200 | } 201 | } 202 | } 203 | 204 | if self.established { 205 | loop { 206 | if self.buffer.len() < FRAME_HEADER_LEN { 207 | break; 208 | } 209 | let header = unpack_header( 210 | slice_as_array!( 211 | &self.buffer.as_ref()[0..FRAME_HEADER_LEN], 212 | [u8; FRAME_HEADER_LEN] 213 | ) 214 | .unwrap(), 215 | ); 216 | 217 | match self.consume(FRAME_HEADER_LEN + header.0 as usize) { 218 | Some(buf) => { 219 | let raw = RawFrame::from(buf); 220 | match HttpFrame::from_raw(&raw) { 221 | Ok(frame) => match frame { 222 | HttpFrame::DataFrame(frame) => { 223 | self.handle_dataframe(frame); 224 | } 225 | HttpFrame::HeadersFrame(frame) => { 226 | for (k, v) in 227 | self.decoder.decode(&frame.header_fragment()).unwrap() 228 | { 229 | if let Some(expected) = REQUEST_HEADERS.get(&k) { 230 | if &v != expected { 231 | // should send an error response instead 232 | return Err(Error::WrongHeaders); 233 | } 234 | } 235 | } 236 | } 237 | HttpFrame::RstStreamFrame(_) => {} 238 | HttpFrame::SettingsFrame(frame) => { 239 | if !frame.is_ack() { 240 | self.queue(SettingsFrame::new_ack()); 241 | } 242 | } 243 | HttpFrame::PingFrame(frame) => { 244 | if !frame.is_ack() { 245 | self.queue(PingFrame::new_ack(frame.opaque_data())); 246 | } 247 | } 248 | HttpFrame::GoawayFrame(_) => {} 249 | HttpFrame::WindowUpdateFrame(frame) => { 250 | self.window_size += frame.increment() as i32; 251 | } 252 | HttpFrame::UnknownFrame(_) => {} 253 | }, 254 | Err(e) => return Err(Error::WrongHttpFrame(e)), 255 | } 256 | } 257 | None => break, 258 | } 259 | } 260 | } 261 | self.flush().await?; 262 | Ok(()) 263 | } 264 | 265 | pub async fn serve(&mut self) { 266 | while self.handle().await.is_ok() {} 267 | } 268 | 269 | fn say_hello(&self, req: helloworld::HelloRequest) -> helloworld::HelloReply { 270 | helloworld::HelloReply { 271 | message: Cow::Owned(format!("Hello {}", req.name)), 272 | } 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /proto/src/helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 gRPC authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | option java_multiple_files = true; 18 | option java_package = "io.grpc.examples.helloworld"; 19 | option java_outer_classname = "HelloWorldProto"; 20 | 21 | package helloworld; 22 | 23 | // The greeting service definition. 24 | service Greeter { 25 | // Sends a greeting 26 | rpc SayHello (HelloRequest) returns (HelloReply) {} 27 | } 28 | 29 | // The request message containing the user's name. 30 | message HelloRequest { 31 | string name = 1; 32 | } 33 | 34 | // The response message containing the greetings 35 | message HelloReply { 36 | string message = 1; 37 | } -------------------------------------------------------------------------------- /proto/src/helloworld.rs: -------------------------------------------------------------------------------- 1 | // Automatically generated rust module for 'helloworld.proto' file 2 | 3 | #![allow(non_snake_case)] 4 | #![allow(non_upper_case_globals)] 5 | #![allow(non_camel_case_types)] 6 | #![allow(unused_imports)] 7 | #![allow(unknown_lints)] 8 | #![allow(clippy::all)] 9 | #![cfg_attr(rustfmt, rustfmt_skip)] 10 | 11 | 12 | use std::borrow::Cow; 13 | use quick_protobuf::{MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; 14 | use quick_protobuf::sizeofs::*; 15 | //use super::*; 16 | 17 | #[derive(Debug, Default, PartialEq, Clone)] 18 | pub struct HelloRequest<'a> { 19 | pub name: Cow<'a, str>, 20 | } 21 | 22 | impl<'a> MessageRead<'a> for HelloRequest<'a> { 23 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 24 | let mut msg = Self::default(); 25 | while !r.is_eof() { 26 | match r.next_tag(bytes) { 27 | Ok(10) => msg.name = r.read_string(bytes).map(Cow::Borrowed)?, 28 | Ok(t) => { r.read_unknown(bytes, t)?; } 29 | Err(e) => return Err(e), 30 | } 31 | } 32 | Ok(msg) 33 | } 34 | } 35 | 36 | impl<'a> MessageWrite for HelloRequest<'a> { 37 | fn get_size(&self) -> usize { 38 | 0 39 | + if self.name == "" { 0 } else { 1 + sizeof_len((&self.name).len()) } 40 | } 41 | 42 | fn write_message(&self, w: &mut Writer) -> Result<()> { 43 | if self.name != "" { w.write_with_tag(10, |w| w.write_string(&**&self.name))?; } 44 | Ok(()) 45 | } 46 | } 47 | 48 | #[derive(Debug, Default, PartialEq, Clone)] 49 | pub struct HelloReply<'a> { 50 | pub message: Cow<'a, str>, 51 | } 52 | 53 | impl<'a> MessageRead<'a> for HelloReply<'a> { 54 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 55 | let mut msg = Self::default(); 56 | while !r.is_eof() { 57 | match r.next_tag(bytes) { 58 | Ok(10) => msg.message = r.read_string(bytes).map(Cow::Borrowed)?, 59 | Ok(t) => { r.read_unknown(bytes, t)?; } 60 | Err(e) => return Err(e), 61 | } 62 | } 63 | Ok(msg) 64 | } 65 | } 66 | 67 | impl<'a> MessageWrite for HelloReply<'a> { 68 | fn get_size(&self) -> usize { 69 | 0 70 | + if self.message == "" { 0 } else { 1 + sizeof_len((&self.message).len()) } 71 | } 72 | 73 | fn write_message(&self, w: &mut Writer) -> Result<()> { 74 | if self.message != "" { w.write_with_tag(10, |w| w.write_string(&**&self.message))?; } 75 | Ok(()) 76 | } 77 | } 78 | 79 | 80 | -------------------------------------------------------------------------------- /proto/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate lazy_static; 3 | #[macro_use] 4 | extern crate slice_as_array; 5 | 6 | pub mod client; 7 | pub mod helloworld; 8 | 9 | use socket2::{Domain, Socket, Type}; 10 | use std::net::SocketAddr; 11 | 12 | // std::net/mio listen socket has too small backlog so create by hand 13 | pub fn create_listen_socket() -> std::net::TcpListener { 14 | let addr: SocketAddr = "[::]:50051".parse().unwrap(); 15 | 16 | let sock = Socket::new( 17 | match addr { 18 | SocketAddr::V4(_) => Domain::ipv4(), 19 | SocketAddr::V6(_) => Domain::ipv6(), 20 | }, 21 | Type::stream(), 22 | None, 23 | ) 24 | .unwrap(); 25 | 26 | sock.set_reuse_address(true).unwrap(); 27 | sock.set_reuse_port(true).unwrap(); 28 | sock.set_nonblocking(true).unwrap(); 29 | sock.bind(&addr.into()).unwrap(); 30 | sock.listen(32768).unwrap(); 31 | 32 | sock.into_tcp_listener() 33 | } 34 | --------------------------------------------------------------------------------