├── .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 | 
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 | 
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 |
--------------------------------------------------------------------------------