mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
575
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7232ba6d55 | ||
|
|
a14de909eb | ||
|
|
d8789cd379 | ||
|
|
8d4be0361e | ||
|
|
3f2710397d | ||
|
|
8895a7d3ab | ||
|
|
10cb9dd468 | ||
|
|
2ca214bd2c | ||
|
|
2a01c26d58 | ||
|
|
ea172537aa | ||
|
|
baa2502ec6 | ||
|
|
d4d17392fe | ||
|
|
6807363efd | ||
|
|
1c5d131245 | ||
|
|
19500f7df8 | ||
|
|
ad189826f4 | ||
|
|
a612736f54 | ||
|
|
bda8dd5113 | ||
|
|
415a786049 | ||
|
|
2edc35a45d | ||
|
|
9cffda59c9 | ||
|
|
23d95b44e7 | ||
|
|
69e45f4be4 | ||
|
|
6bdfa159a7 | ||
|
|
5d87a9cee1 | ||
|
|
16d3540ce9 | ||
|
|
e5ebd02885 | ||
|
|
08c21e7bac | ||
|
|
8786741ba9 | ||
|
|
494f0dc176 | ||
|
|
df9025594c | ||
|
|
7b1306e6c2 | ||
|
|
b7f4e337be | ||
|
|
d1046db735 | ||
|
|
923a80e098 | ||
|
|
64435f5b35 | ||
|
|
d304791c0e | ||
|
|
5846b3fc2a | ||
|
|
8eb3e58b7d | ||
|
|
c0a2cc1f9e | ||
|
|
61b2889881 | ||
|
|
5dab821b29 | ||
|
|
2abeff01a5 | ||
|
|
96a542451d | ||
|
|
95899e007d | ||
|
|
e6e3c49e0e | ||
|
|
4d514b7eb3 | ||
|
|
2a1585157e | ||
|
|
189d6baac4 | ||
|
|
3ad27e99ec | ||
|
|
bf1305c421 | ||
|
|
cf7435ba30 | ||
|
|
e18c23afa1 | ||
|
|
bb9de276ae | ||
|
|
fed4d72eff | ||
|
|
142bd3b2a9 | ||
|
|
7da5603a42 | ||
|
|
264fef60e3 | ||
|
|
25dd54d263 | ||
|
|
5555cbc85e | ||
|
|
c769b915b7 | ||
|
|
1f91a890b4 | ||
|
|
869615f1d2 | ||
|
|
ae20270d00 | ||
|
|
f1cb12e14f | ||
|
|
56c5797872 | ||
|
|
aa4b1b4311 | ||
|
|
687871d3e5 | ||
|
|
071d8704ce | ||
|
|
9f7a98af3c | ||
|
|
7db7719419 | ||
|
|
21c0f3a9d8 | ||
|
|
e1b3085153 | ||
|
|
df6e24255b | ||
|
|
df19119c0a | ||
|
|
164ee8f106 | ||
|
|
7238cfa5e2 | ||
|
|
7de749b77b | ||
|
|
2eabc37599 | ||
|
|
1190176be7 | ||
|
|
8e1a9101f0 | ||
|
|
427b7325d0 | ||
|
|
2961a2388c | ||
|
|
df3a92532b | ||
|
|
23f451e8b1 | ||
|
|
40cbd0f296 | ||
|
|
3b64fe9363 | ||
|
|
5334de5e44 | ||
|
|
fe14e7b127 | ||
|
|
e0d95aa037 | ||
|
|
dc225faf24 | ||
|
|
8605d5d243 | ||
|
|
4704f61277 | ||
|
|
88a7030f73 | ||
|
|
35aeabd3ff | ||
|
|
0b58bded7c | ||
|
|
609786ed41 | ||
|
|
a9da59882c | ||
|
|
4ae76132c1 | ||
|
|
23bc9d20d3 | ||
|
|
c225b46b18 | ||
|
|
c30fa62dda | ||
|
|
ad8338e4da | ||
|
|
73b763f69f | ||
|
|
f0ea9d6f4c | ||
|
|
567887cc75 | ||
|
|
3840ceafee | ||
|
|
a5e9c311bf | ||
|
|
2e94b658ed | ||
|
|
b9db119b45 | ||
|
|
a87936080b | ||
|
|
65cbfced29 | ||
|
|
a616220090 | ||
|
|
a998367002 | ||
|
|
c1c06f8ac1 | ||
|
|
ae627db266 | ||
|
|
f4ec9a6360 | ||
|
|
0f4706d752 | ||
|
|
117dcba8cb | ||
|
|
fd37fb0f17 | ||
|
|
a41c0800f9 | ||
|
|
e140dabed2 | ||
|
|
fbdf04f604 | ||
|
|
a3c4aec127 | ||
|
|
730228c8cb | ||
|
|
826e27685c | ||
|
|
025f52aadc | ||
|
|
b081e9593f | ||
|
|
99fc38e0d6 | ||
|
|
ce014943ec | ||
|
|
dac13c1df4 | ||
|
|
555c97b313 | ||
|
|
b395ccb6d9 | ||
|
|
943cd860cb | ||
|
|
cde387d7e7 | ||
|
|
9303076a6b | ||
|
|
571e322755 | ||
|
|
4ef772b2db | ||
|
|
849771ecfa | ||
|
|
a577bfc033 | ||
|
|
32f2750c2d | ||
|
|
c45bbaa04e | ||
|
|
963eee3bc7 | ||
|
|
23a0e990d2 | ||
|
|
402cd3034d | ||
|
|
cf793d4053 | ||
|
|
108e1a2c1a | ||
|
|
46062794aa | ||
|
|
2e58422890 | ||
|
|
e86fc4917a | ||
|
|
8fcce957cd | ||
|
|
259996d805 | ||
|
|
329bca15a6 | ||
|
|
7c768fc046 | ||
|
|
c801584d24 | ||
|
|
0b54557796 | ||
|
|
b7ff130a0d | ||
|
|
31f8cb9467 | ||
|
|
4c268a8939 | ||
|
|
701488e6a1 | ||
|
|
2764fc3de2 | ||
|
|
9c16d47632 | ||
|
|
a402b1ca1e | ||
|
|
1fe55b2b55 | ||
|
|
6cca729f40 | ||
|
|
2ab2affe69 | ||
|
|
8c838a2709 | ||
|
|
c6f1ff13d2 | ||
|
|
697851210c | ||
|
|
b23a997cb8 | ||
|
|
36aaaa1520 | ||
|
|
25f30c91c4 | ||
|
|
0c5c5dfafe | ||
|
|
3588f4d6ad | ||
|
|
ca8104ad69 | ||
|
|
39173f8830 | ||
|
|
7624061021 | ||
|
|
bd9a07f3ad | ||
|
|
b4fa47bd09 | ||
|
|
6b8ad4ff99 | ||
|
|
01198532c1 | ||
|
|
e10de1e94e | ||
|
|
335c3e73a4 | ||
|
|
1c88b8f336 | ||
|
|
84916f66fd | ||
|
|
259c7a0884 | ||
|
|
25760609fa | ||
|
|
0868b97832 | ||
|
|
fbd0a9e5f1 | ||
|
|
24dd856cfd | ||
|
|
e33155edb1 | ||
|
|
de33e02c01 | ||
|
|
317c11552c | ||
|
|
fb05eb34af | ||
|
|
25dcb1fab5 | ||
|
|
97d80296cb | ||
|
|
b320d9ee58 | ||
|
|
63d7be0368 | ||
|
|
7950b43505 | ||
|
|
c80105fe1e | ||
|
|
8a43472b35 | ||
|
|
ecedea3404 | ||
|
|
2e5cd1640e | ||
|
|
85257e19af | ||
|
|
a611f6ec30 | ||
|
|
bda6ed9241 | ||
|
|
5e4cfdfab1 | ||
|
|
e0b751b013 | ||
|
|
5e92b10f3f | ||
|
|
a4e947fba0 | ||
|
|
7b94cf307d | ||
|
|
645ae7051d | ||
|
|
b9dc4c9008 | ||
|
|
e30b182147 | ||
|
|
fce59de79e | ||
|
|
04eba12d43 | ||
|
|
f12b761b77 | ||
|
|
e8617ea1fc | ||
|
|
77d9a1aa4f | ||
|
|
6090e22135 | ||
|
|
e5fdccfff0 | ||
|
|
fbd257f2bd | ||
|
|
05b1ba4342 | ||
|
|
77b0ee0a47 | ||
|
|
2a71ef8b13 | ||
|
|
d03c736b4c | ||
|
|
8c297a6208 | ||
|
|
8fba4858ac | ||
|
|
5fe7b4225f | ||
|
|
c13e7f3533 | ||
|
|
2653a2195c | ||
|
|
6fb62d3232 | ||
|
|
8004900ce9 | ||
|
|
8379b87c53 | ||
|
|
bf93b79120 | ||
|
|
d85f54dffb | ||
|
|
ddb244429e | ||
|
|
c65c331767 | ||
|
|
363e15f36c | ||
|
|
562aa65c99 | ||
|
|
74670f287e | ||
|
|
9cd80f1cbd | ||
|
|
d23c1a2b98 | ||
|
|
16d15520ad | ||
|
|
0be8eab260 | ||
|
|
7acffe4785 | ||
|
|
42f73cb0ec | ||
|
|
81beb44565 | ||
|
|
01635dfd56 | ||
|
|
4dd3d30f2a | ||
|
|
b92fd2d22a | ||
|
|
9e80c82400 | ||
|
|
4d8d293913 | ||
|
|
d723faf2d8 | ||
|
|
5567ec904e | ||
|
|
98e99c71c4 | ||
|
|
011a7b02f7 | ||
|
|
05191f14d7 | ||
|
|
0cfa98566e | ||
|
|
4545110f45 | ||
|
|
3111443113 | ||
|
|
29144e6c9b | ||
|
|
8d58b70ce6 | ||
|
|
f5db8136d7 | ||
|
|
45e69bfb61 | ||
|
|
37e2870f04 | ||
|
|
8383e8bf7a | ||
|
|
c80953f078 | ||
|
|
8c1838e093 | ||
|
|
e79f665440 | ||
|
|
a8e09c5608 | ||
|
|
89fcc96dd4 | ||
|
|
8fecf98aef | ||
|
|
b0d04e04f2 | ||
|
|
a67e0d3efb | ||
|
|
eb66ff3ad1 | ||
|
|
5c411c548b | ||
|
|
3b8f1695ff | ||
|
|
bd75a522c3 | ||
|
|
1722d8ed45 | ||
|
|
6a4df9a3b6 | ||
|
|
e8bd2117d0 | ||
|
|
b773d55444 | ||
|
|
005994cdea | ||
|
|
9e3f3caad0 | ||
|
|
6c774756e6 | ||
|
|
a3c1796a45 | ||
|
|
c9a8208c8a | ||
|
|
54dcd3a015 | ||
|
|
1bc2ef6aff | ||
|
|
fe9a798811 | ||
|
|
a597b962a3 | ||
|
|
65c5c5241b | ||
|
|
e11dd06ead | ||
|
|
5adde38a65 | ||
|
|
574b6f0c54 | ||
|
|
b4cd223476 | ||
|
|
f86addf113 | ||
|
|
a4017ee846 | ||
|
|
8a360f87f5 | ||
|
|
cecaad715d | ||
|
|
e31b76c047 | ||
|
|
05f01d6c9b | ||
|
|
829563ccfc | ||
|
|
cf74a6b2c9 | ||
|
|
baeb35b6d4 | ||
|
|
a4b4d57cb7 | ||
|
|
4363663dea | ||
|
|
c1f1320203 | ||
|
|
5b425dfed9 | ||
|
|
fcdeb0e5d6 | ||
|
|
75fe37bc8c | ||
|
|
c9d0fe59dd | ||
|
|
b22b797278 | ||
|
|
e69b67fcfe | ||
|
|
b9e182a2ce | ||
|
|
e8d4524586 | ||
|
|
8d02a1c2b6 | ||
|
|
9fe0bc1907 | ||
|
|
0c553f958a | ||
|
|
e502603532 | ||
|
|
d0833074d6 | ||
|
|
c47c445be6 | ||
|
|
28ace4d33c | ||
|
|
1985eae7f5 | ||
|
|
f77fe7d3b5 | ||
|
|
9294b1345c | ||
|
|
c61d3bc71e | ||
|
|
372d55ce0b | ||
|
|
2810471035 | ||
|
|
76c3e162b7 | ||
|
|
64c360c30e | ||
|
|
2499dd35a2 | ||
|
|
4eb036d3f6 | ||
|
|
e5bbf004d3 | ||
|
|
ff7fcd1074 | ||
|
|
ae314245dc | ||
|
|
58913c80a3 | ||
|
|
5929a59aca | ||
|
|
717e99ca80 | ||
|
|
99078c5cc1 | ||
|
|
3dd4178bb5 | ||
|
|
000e9b5ab9 | ||
|
|
50f007a49b | ||
|
|
7b7b3a817b | ||
|
|
24d941d676 | ||
|
|
b9f7808f6e | ||
|
|
0588204048 | ||
|
|
46d68f739a | ||
|
|
9bb1366f33 | ||
|
|
ef89def808 | ||
|
|
edf6a57450 | ||
|
|
68a8f26b40 | ||
|
|
e0990e8a5a | ||
|
|
1864ecc46f | ||
|
|
7abe6db0ae | ||
|
|
936727fa52 | ||
|
|
38df0d7f0f | ||
|
|
f036691681 | ||
|
|
5bab8e85b9 | ||
|
|
dc27c0a524 | ||
|
|
3b38e518ca | ||
|
|
7f96b7e9e4 | ||
|
|
b89150c464 | ||
|
|
e970e9a79c | ||
|
|
1a48b79474 | ||
|
|
9b62ade962 | ||
|
|
0d10b0e05a | ||
|
|
56c2c31bcf | ||
|
|
1a6753df1f | ||
|
|
2cb600bd19 | ||
|
|
b2ac1d8f93 | ||
|
|
b12d32ce1c | ||
|
|
e27abd3841 | ||
|
|
dcf994d517 | ||
|
|
ab3915d47d | ||
|
|
161811de8b | ||
|
|
71d8672aab | ||
|
|
4994f762a9 | ||
|
|
1305f105cb | ||
|
|
592a99bca4 | ||
|
|
0f49a69a06 | ||
|
|
bd86bab42d | ||
|
|
bc16ad039d | ||
|
|
1bc3899016 | ||
|
|
46dd38b7d7 | ||
|
|
89a9ab6d34 | ||
|
|
c353de13fc | ||
|
|
21851dd25c | ||
|
|
06a4c5e2d5 | ||
|
|
f6241b6330 | ||
|
|
092574b7de | ||
|
|
6e7410567c | ||
|
|
3d69a8b7d0 | ||
|
|
bbea632e04 | ||
|
|
6b888ea20f | ||
|
|
7ae124077e | ||
|
|
17faf329e0 | ||
|
|
91347f44a5 | ||
|
|
da3544f468 | ||
|
|
be7992e639 | ||
|
|
71baa4967d | ||
|
|
307ba7a867 | ||
|
|
d1a2a9d324 | ||
|
|
45fc13071c | ||
|
|
c459b9835c | ||
|
|
36e3dbf418 | ||
|
|
3eac142e5f | ||
|
|
9dce803e4a | ||
|
|
60796f40fd | ||
|
|
fce913c04d | ||
|
|
4744a2e48b | ||
|
|
6dc8333fb2 | ||
|
|
f96d37ccf3 | ||
|
|
79136ca211 | ||
|
|
556143b7c6 | ||
|
|
614887b8c1 | ||
|
|
8e7ed8ae21 | ||
|
|
0a3dc0bb75 | ||
|
|
4615c3ea78 | ||
|
|
237bcead7a | ||
|
|
503f4a0405 | ||
|
|
623ce443d8 | ||
|
|
88fb0b32af | ||
|
|
bb22ef5f82 | ||
|
|
c9554fa174 | ||
|
|
a1dfa14034 | ||
|
|
37a2bed4cf | ||
|
|
4a07828095 | ||
|
|
cefdb0c321 | ||
|
|
4719fbdb28 | ||
|
|
6d6c2aa390 | ||
|
|
2c5e4ebba8 | ||
|
|
b84ef90a98 | ||
|
|
3ced812993 | ||
|
|
6789527952 | ||
|
|
eecf3d129c | ||
|
|
7cf7833631 | ||
|
|
bc2f857236 | ||
|
|
688b67c8d8 | ||
|
|
227f454c52 | ||
|
|
315f601822 | ||
|
|
62514fc40b | ||
|
|
b1d02eb598 | ||
|
|
da37ad0948 | ||
|
|
b227738bd7 | ||
|
|
411caa786d | ||
|
|
6961efa8dd | ||
|
|
a99b2529e0 | ||
|
|
a94be1bca9 | ||
|
|
0205b855d0 | ||
|
|
e32115b1b4 | ||
|
|
37879bd911 | ||
|
|
b0033eb463 | ||
|
|
2545237309 | ||
|
|
f019f5f5bb | ||
|
|
d2440a4059 | ||
|
|
ce8a9d460e | ||
|
|
866dad2e85 | ||
|
|
f69f748470 | ||
|
|
42bbe86cb6 | ||
|
|
4b3472ceae | ||
|
|
e92d4bf52b | ||
|
|
f62d1ddc8d | ||
|
|
1d40bf14f7 | ||
|
|
c2e80ebb30 | ||
|
|
181dcdd9ea | ||
|
|
326537b861 | ||
|
|
f41c55f9f7 | ||
|
|
418a973520 | ||
|
|
fb497aba44 | ||
|
|
933b34cd25 | ||
|
|
32ccf255b5 | ||
|
|
00fb7ea4a3 | ||
|
|
550dc76bbd | ||
|
|
80d23caa5e | ||
|
|
b2ae5ac7c9 | ||
|
|
ab5025a8b7 | ||
|
|
8f92dc9d56 | ||
|
|
0000210e0c | ||
|
|
83784fd983 | ||
|
|
cd331dde34 | ||
|
|
eb643c4d9e | ||
|
|
1f0d2198ad | ||
|
|
66cff8e84b | ||
|
|
e60002b653 | ||
|
|
6c045d31ac | ||
|
|
93c61bb384 | ||
|
|
0eeeca4ac1 | ||
|
|
9fe1d8aeca | ||
|
|
a9d24810be | ||
|
|
93e1d4778d | ||
|
|
d7f76ca549 | ||
|
|
815dc803db | ||
|
|
abf4521300 | ||
|
|
03ac1ea460 | ||
|
|
242f8fa1b2 | ||
|
|
3794cf7f1d | ||
|
|
3282b3ec0d | ||
|
|
2081bcc430 | ||
|
|
6947933b6b | ||
|
|
02538d035f | ||
|
|
6c6fb917ee | ||
|
|
376974c349 | ||
|
|
330ab823b0 | ||
|
|
440a813c5a | ||
|
|
7a852a7f49 | ||
|
|
909f3e923c | ||
|
|
c820d67d66 | ||
|
|
b9395808be | ||
|
|
32557cd832 | ||
|
|
3715e62471 | ||
|
|
235812a4da | ||
|
|
37a53a1085 | ||
|
|
347669ed2b | ||
|
|
d6e8998582 | ||
|
|
f107c8d860 | ||
|
|
e71d509fee | ||
|
|
d1cd455225 | ||
|
|
cff73b7a9d | ||
|
|
9c309af597 | ||
|
|
df9730fcbe | ||
|
|
2bd616df51 | ||
|
|
b9dae23e3f | ||
|
|
5ab323e5c2 | ||
|
|
a9b53d61d2 | ||
|
|
0a707ffccb | ||
|
|
12a05b9568 | ||
|
|
32eb457dad | ||
|
|
9672f90091 | ||
|
|
8025d85019 | ||
|
|
311bfa07a3 | ||
|
|
d0b911189c | ||
|
|
293d104177 | ||
|
|
62f306629d | ||
|
|
217af868e1 | ||
|
|
8327d327c1 | ||
|
|
3158a2c73b | ||
|
|
b508964db6 | ||
|
|
9911f421eb | ||
|
|
ef3efd4175 | ||
|
|
0aa7ab94ff | ||
|
|
f08e8ec672 | ||
|
|
18c371b158 | ||
|
|
517d5c7434 | ||
|
|
7c4bac5166 | ||
|
|
681316dfa1 | ||
|
|
00daa2ca23 | ||
|
|
68103727f1 | ||
|
|
bfe91f9f36 | ||
|
|
8d73133907 | ||
|
|
4380e42587 | ||
|
|
164193fe82 | ||
|
|
8daac0347d | ||
|
|
d2bb8e01fd | ||
|
|
5b18514970 | ||
|
|
42a0a10294 | ||
|
|
304914b707 | ||
|
|
8eb1f681af | ||
|
|
368a9c70e8 | ||
|
|
5d0fdf26a9 | ||
|
|
e7f4313cf4 | ||
|
|
04bd33e390 | ||
|
|
5f9185ef4c | ||
|
|
5c9daad88b | ||
|
|
1d7098eece | ||
|
|
c458e23940 | ||
|
|
260255e674 | ||
|
|
1b07e91834 | ||
|
|
79760da66c | ||
|
|
5970203b21 | ||
|
|
7ffe72dc73 | ||
|
|
b47b9f5398 | ||
|
|
192579d62a | ||
|
|
bc64194be1 |
@@ -0,0 +1,19 @@
|
||||
environment:
|
||||
matrix:
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
platform: x64
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
platform: x86
|
||||
|
||||
install:
|
||||
- appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||
- rustup-init.exe -y --default-host %TARGET%
|
||||
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
|
||||
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
|
||||
build: false
|
||||
|
||||
test_script:
|
||||
- cargo test --all --target %TARGET%
|
||||
+66
-16
@@ -1,25 +1,75 @@
|
||||
---
|
||||
language: rust
|
||||
|
||||
rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
sudo: false
|
||||
before_script:
|
||||
- pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# This represents the minimum Rust version supported by Tokio. Updating this
|
||||
# should be done in a dedicated PR and cannot be greater than two 0.x
|
||||
# releases prior to the current stable.
|
||||
- rust: 1.21.0
|
||||
- rust: stable
|
||||
- os: osx
|
||||
- rust: beta
|
||||
- rust: nightly
|
||||
- env: TARGET=x86_64-unknown-freebsd
|
||||
|
||||
script:
|
||||
- cargo build
|
||||
- cargo test
|
||||
- cargo doc --no-deps
|
||||
after_success:
|
||||
- travis-cargo --only nightly doc-upload
|
||||
- |
|
||||
set -e
|
||||
if [[ "$TRAVIS_RUST_VERSION" == nightly ]]
|
||||
then
|
||||
# Pin the nightly version until rust-lang/rust#49436 is resolved.
|
||||
rustup override set nightly-2018-03-26
|
||||
|
||||
# Make sure the benchmarks compile
|
||||
cargo build --benches --all
|
||||
|
||||
# Run address sanitizer
|
||||
ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0" \
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test -p tokio-timer --test hammer --target x86_64-unknown-linux-gnu
|
||||
|
||||
# Run thread sanitizer
|
||||
TSAN_OPTIONS="suppressions=`pwd`/ci/tsan" \
|
||||
RUSTFLAGS="-Z sanitizer=thread" \
|
||||
cargo test -p tokio-timer --test hammer --target x86_64-unknown-linux-gnu
|
||||
fi
|
||||
- |
|
||||
set -e
|
||||
if [[ "$TARGET" ]]
|
||||
then
|
||||
rustup target add $TARGET
|
||||
cargo check --all --target $TARGET
|
||||
cargo check --tests --all --target $TARGET
|
||||
else
|
||||
cargo test --all
|
||||
# Disable these tests for now as they are buggy
|
||||
#
|
||||
# cargo test --features unstable-futures
|
||||
# cargo test --manifest-path tokio-threadpool/Cargo.toml --features unstable-futures
|
||||
# cargo test --manifest-path tokio-reactor/Cargo.toml --features unstable-futures
|
||||
fi
|
||||
|
||||
before_deploy:
|
||||
- cargo doc --all --no-deps
|
||||
|
||||
deploy:
|
||||
provider: pages
|
||||
skip_cleanup: true
|
||||
github_token: $GH_TOKEN
|
||||
target_branch: gh-pages
|
||||
local_dir: target/doc
|
||||
on:
|
||||
branch: master
|
||||
repo: tokio-rs/tokio
|
||||
rust: stable
|
||||
condition: $TRAVIS_OS_NAME = linux
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: "SXXK7Znvm1s5WWQ94l9IP25mXA0uGIQ7ghBuumZz3nSAfxhhJQnYi5hCAbl2/cOfSbpgEtE137dgk6Nd9UDx7rIkLCSe8TWyYjzraX/vvX3xNtLh/fjsayYYRK9a6qU2HIJegZdxPgyF5h2DeBgeLks0Ue8drrFQ1s9bYZVUO0yeuZ3aLkL1FkIG6RXGItUFpb6srEYL1NLizYLxXFEG3cL+kKoFIWc2qPx3EwOqv/eii134nQsuObhWZvPqfTo7zfNP8W/6TnoiggpRH1nrZc3DI3CynTICIOJ2Ogn9gFX9LftYKuJysSwUNVN3WF5aOuLP/XjRSBLYc+PW3v0iqiGzMX3n1VpcyhcbsSNA7ZckGn1HZsWYwspAxkN3idSuVie9Mezm7IV4005juiYKEWEr6hlkv1lzd49QZkWOvLCFCMRiwOOGp4NyzilG1Q1Zs3G1wrcvstmasNpK+QUFNdOFvT2sm34rI4x2rQUvjC/OyqbAK+PjYmTHL47YKON5ymfUL3mAcwgUfBUSd4Wpx8G3VKg3gMcmQm27ah1knOGJWH6XulYTnfGfx6bLo5t2NGx+vZk0naqajD3auWnseobMDsFjhUIRrt6GlnfPqeFoJSm0unu3riAX+RDF/iqZdDfjhX4evETIw3SaTl8EQtVLwz7kJTnxSbTU4XTi+0M="
|
||||
- secure: iwlN1zfUCp/5BAAheqIRSFIqiM9zSwfIGcVDw/V7jHveqXyNzmCs7H58/cd90WLqonqpPX0t5GF66oTjms4v0DFjgXr/k4358qeSZaV082V3baNrVpCDHeCQV0SvKsfiYxDDJGSUL1WIUP+tqqDm4+ksZQP3LnwZojkABjWz5CBNt4kX+Wz5ZbYqtQoxyuZba5UyPY2CXJtubvCVPGMJULuUpklYxXZ4dWM2olzGgVJ8rE8udhSZ4ER4JgxB0KUx3/5TwHHzgyPEsWR4bKN6JzBjIczQofXUcUXXdoZBs23H/VhCpzKcn3/oJ8btVYPzwtdj5FmVB1aVR/gjPo2bSGi/sofq+LwL/1HJXkM+kjl8m2dLLcDBKqNYNERtVA1++LhkMWAFRgGYe8v8Ryxjiue1NF5LgAIA/fjK0uI1DELTzTf/TKrM+AtPDNTvhOft4/YD+hoImjwk6nv6PBb2TiTYnc79Qf4AZ65tv1qtsAUPuw4plLaccHQAO4ldYVXn4u9c+iisJwvovs6jo06bF3U3qtdI5gXsrI9+T25TrXvYb+IREo0MHzYEM0KlPFnscEArzC3eajuSd36ARFP3lDc+gp2RPs89iJjowms0eRyepp7Cu6XO3Cd2pfAX8AqvnmttZf4Nm51ONeiBPXPXItUkJm49MCpMJywU1IZcWZg=
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# 0.1.5 (March 30, 2018)
|
||||
|
||||
* Provide timer API (#266)
|
||||
|
||||
# 0.1.4 (March 22, 2018)
|
||||
|
||||
* Fix build on FreeBSD (#218)
|
||||
* Shutdown the Runtime when the handle is dropped (#214)
|
||||
* Set Runtime thread name prefix for worker threads (#232)
|
||||
* Add builder for Runtime (#234)
|
||||
* Extract TCP and UDP types into separate crates (#224)
|
||||
* Optionally support futures 0.2.
|
||||
|
||||
# 0.1.3 (March 09, 2018)
|
||||
|
||||
* Fix `CurrentThread::turn` to block on idle (#212).
|
||||
|
||||
# 0.1.2 (March 09, 2018)
|
||||
|
||||
* Introduce Tokio Runtime (#141)
|
||||
* Provide `CurrentThread` for more flexible usage of current thread executor (#141).
|
||||
* Add Lio for platforms that support it (#142).
|
||||
* I/O resources now lazily bind to the reactor (#160).
|
||||
* Extract Reactor to dedicated crate (#169)
|
||||
* Add facade to sub crates and add prelude (#166).
|
||||
* Switch TCP/UDP fns to poll_ -> Poll<...> style (#175)
|
||||
|
||||
# 0.1.1 (February 09, 2018)
|
||||
|
||||
* Doc fixes
|
||||
|
||||
# 0.1.0 (February 07, 2018)
|
||||
|
||||
* Initial crate released based on [RFC](https://github.com/tokio-rs/tokio-rfcs/pull/3).
|
||||
+75
-17
@@ -1,25 +1,83 @@
|
||||
[package]
|
||||
name = "tokio-signal"
|
||||
version = "0.1.2"
|
||||
authors = ["Alex Crichton <[email protected]>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/alexcrichton/tokio-signal"
|
||||
homepage = "https://github.com/alexcrichton/tokio-signal"
|
||||
documentation = "https://docs.rs/tokio-signal/0.1"
|
||||
name = "tokio"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.5"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio/0.1"
|
||||
description = """
|
||||
An implementation of an asynchronous Unix signal handling backed futures.
|
||||
An event-driven, non-blocking I/O platform for writing asynchronous I/O
|
||||
backed applications.
|
||||
"""
|
||||
categories = ["asynchronous", "network-programming"]
|
||||
keywords = ["io", "async", "non-blocking", "futures"]
|
||||
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"./",
|
||||
"tokio-executor",
|
||||
"tokio-io",
|
||||
"tokio-reactor",
|
||||
"tokio-threadpool",
|
||||
"tokio-timer",
|
||||
"tokio-tcp",
|
||||
"tokio-udp",
|
||||
"futures2",
|
||||
]
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "tokio-rs/tokio" }
|
||||
appveyor = { repository = "carllerche/tokio", id = "s83yxhy9qeb58va7" }
|
||||
|
||||
[dependencies]
|
||||
tokio-core = "0.1.4"
|
||||
futures = "0.1.7"
|
||||
tokio-io = { version = "0.1.6", path = "tokio-io" }
|
||||
tokio-executor = { version = "0.1.2", path = "tokio-executor" }
|
||||
tokio-reactor = { version = "0.1.1", path = "tokio-reactor" }
|
||||
tokio-threadpool = { version = "0.1.2", path = "tokio-threadpool" }
|
||||
tokio-tcp = { version = "0.1.0", path = "tokio-tcp" }
|
||||
tokio-udp = { version = "0.1.0", path = "tokio-udp" }
|
||||
tokio-timer = { version = "0.2.0", path = "tokio-timer" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
futures = "0.1.20"
|
||||
|
||||
# Needed until `reactor` is removed from `tokio`.
|
||||
mio = "0.6.14"
|
||||
|
||||
# Futures 0.2 integration
|
||||
futures2 = { version = "0.1.0", path = "futures2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
bytes = "0.4"
|
||||
env_logger = { version = "0.4", default-features = false }
|
||||
flate2 = { version = "1", features = ["tokio"] }
|
||||
futures-cpupool = "0.1"
|
||||
http = "0.1"
|
||||
httparse = "1.0"
|
||||
libc = "0.2"
|
||||
mio = "0.6"
|
||||
mio-uds = "0.6"
|
||||
num_cpus = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
time = "0.1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = "0.2"
|
||||
kernel32-sys = "0.2"
|
||||
mio = "0.6"
|
||||
[patch.crates-io]
|
||||
tokio-io = { path = "tokio-io" }
|
||||
|
||||
[features]
|
||||
unstable-futures = [
|
||||
"futures2",
|
||||
"tokio-reactor/unstable-futures",
|
||||
"tokio-threadpool/unstable-futures",
|
||||
"tokio-executor/unstable-futures",
|
||||
"tokio-tcp/unstable-futures",
|
||||
"tokio-udp/unstable-futures"
|
||||
]
|
||||
default = []
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016 Alex Crichton
|
||||
Copyright (c) 2018 Tokio Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
-201
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,32 +1,139 @@
|
||||
# tokio-signal
|
||||
# Tokio
|
||||
|
||||
An implementation of Unix signal handling for Tokio
|
||||
A runtime for writing reliable, asynchronous, and slim applications with
|
||||
the Rust programming language. It is:
|
||||
|
||||
[](https://travis-ci.org/alexcrichton/tokio-signal)
|
||||
* **Fast**: Tokio's zero-cost abstractions give you bare-metal
|
||||
performance.
|
||||
|
||||
[Documentation](https://docs.rs/tokio-signal)
|
||||
* **Reliable**: Tokio leverages Rust's ownership, type system, and
|
||||
concurrency model to reduce bugs and ensure thread safety.
|
||||
|
||||
## Usage
|
||||
* **Scalable**: Tokio has a minimal footprint, and handles backpressure
|
||||
and cancellation naturally.
|
||||
|
||||
First, add this to your `Cargo.toml`:
|
||||
[![Crates.io][crates-badge]][crates-url]
|
||||
[![MIT licensed][mit-badge]][mit-url]
|
||||
[![Travis Build Status][travis-badge]][travis-url]
|
||||
[![Appveyor Build Status][appveyor-badge]][appveyor-url]
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio-signal = "0.1"
|
||||
```
|
||||
[crates-badge]: https://img.shields.io/crates/v/tokio.svg
|
||||
[crates-url]: https://crates.io/crates/tokio
|
||||
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[mit-url]: LICENSE-MIT
|
||||
[travis-badge]: https://travis-ci.org/tokio-rs/tokio.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/tokio-rs/tokio
|
||||
[appveyor-badge]: https://ci.appveyor.com/api/projects/status/s83yxhy9qeb58va7/branch/master?svg=true
|
||||
[appveyor-url]: https://ci.appveyor.com/project/carllerche/tokio/branch/master
|
||||
|
||||
Next, add this to your crate:
|
||||
[Website](https://tokio.rs) |
|
||||
[Guides](https://tokio.rs/docs/getting-started/hello-world/) |
|
||||
[API Docs](https://docs.rs/tokio)
|
||||
|
||||
The API docs for the master branch are published [here][master-dox].
|
||||
|
||||
[master-dox]: https://tokio-rs.github.io/tokio/tokio/
|
||||
|
||||
## Overview
|
||||
|
||||
Tokio is an event-driven, non-blocking I/O platform for writing
|
||||
asynchronous applications with the Rust programming language. At a high
|
||||
level, it provides a few major components:
|
||||
|
||||
* A multithreaded, work-stealing based task [scheduler].
|
||||
* A [reactor] backed by the operating system's event queue (epoll, kqueue,
|
||||
IOCP, etc...).
|
||||
* Asynchronous [TCP and UDP][net] sockets.
|
||||
|
||||
These components provide the runtime components necessary for building
|
||||
an asynchronous application.
|
||||
|
||||
[net]: https://docs.rs/tokio/0.1/tokio/net/index.html
|
||||
[reactor]: https://docs.rs/tokio/0.1.1/tokio/reactor/index.html
|
||||
[scheduler]: https://tokio-rs.github.io/tokio/tokio/runtime/index.html
|
||||
|
||||
## Example
|
||||
|
||||
A basic TCP echo server with Tokio:
|
||||
|
||||
```rust
|
||||
extern crate tokio_signal;
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use tokio::io::copy;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
fn main() {
|
||||
// Bind the server's socket.
|
||||
let addr = "127.0.0.1:12345".parse().unwrap();
|
||||
let listener = TcpListener::bind(&addr)
|
||||
.expect("unable to bind TCP listener");
|
||||
|
||||
// Pull out a stream of sockets for incoming connections
|
||||
let server = listener.incoming()
|
||||
.map_err(|e| eprintln!("accept failed = {:?}", e))
|
||||
.for_each(|sock| {
|
||||
// Split up the reading and writing parts of the
|
||||
// socket.
|
||||
let (reader, writer) = sock.split();
|
||||
|
||||
// A future that echos the data and returns how
|
||||
// many bytes were copied...
|
||||
let bytes_copied = copy(reader, writer);
|
||||
|
||||
// ... after which we'll print what happened.
|
||||
let handle_conn = bytes_copied.map(|amt| {
|
||||
println!("wrote {:?} bytes", amt)
|
||||
}).map_err(|err| {
|
||||
eprintln!("IO error {:?}", err)
|
||||
});
|
||||
|
||||
// Spawn the future as a concurrent task.
|
||||
tokio::spawn(handle_conn)
|
||||
});
|
||||
|
||||
// Start the Tokio runtime
|
||||
tokio::run(server);
|
||||
}
|
||||
```
|
||||
|
||||
# License
|
||||
More examples can be found [here](examples).
|
||||
|
||||
`tokio-signal` is primarily distributed under the terms of both the MIT
|
||||
license and the Apache License (Version 2.0), with portions covered by various
|
||||
BSD-like licenses.
|
||||
## Project layout
|
||||
|
||||
See LICENSE-APACHE, and LICENSE-MIT for details.
|
||||
The `tokio` crate, found at the root, is primarily intended for use by
|
||||
application developers. Library authors should depend on the sub crates, which
|
||||
have greater guarantees of stability.
|
||||
|
||||
The crates included as part of Tokio are:
|
||||
|
||||
* [`tokio-executor`]: Task execution related traits and utilities.
|
||||
|
||||
* [`tokio-io`]: Asynchronous I/O related traits and utilities.
|
||||
|
||||
* [`tokio-reactor`]: Event loop that drives I/O resources (like TCP and UDP
|
||||
sockets).
|
||||
|
||||
* [`tokio-threadpool`]: Schedules the execution of futures across a pool of
|
||||
threads.
|
||||
|
||||
* [`tokio-tcp`]: TCP bindings for use with `tokio-io` and `tokio-reactor`.
|
||||
|
||||
* [`tokio-udp`]: UDP bindings for use with `tokio-io` and `tokio-reactor`.
|
||||
|
||||
[`tokio-executor`]: tokio-executor
|
||||
[`tokio-io`]: tokio-io
|
||||
[`tokio-reactor`]: tokio-reactor
|
||||
[`tokio-threadpool`]: tokio-threadpool
|
||||
[`tokio-tcp`]: tokio-tcp
|
||||
[`tokio-udp`]: tokio-udp
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE).
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
|
||||
terms or conditions.
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate test;
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::thread;
|
||||
|
||||
use futures::sync::oneshot;
|
||||
use futures::sync::mpsc;
|
||||
use futures::{Future, Poll, Sink, Stream};
|
||||
use test::Bencher;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
/// UDP echo server
|
||||
struct EchoServer {
|
||||
socket: UdpSocket,
|
||||
buf: Vec<u8>,
|
||||
to_send: Option<(usize, SocketAddr)>,
|
||||
}
|
||||
|
||||
impl EchoServer {
|
||||
fn new(s: UdpSocket) -> Self {
|
||||
EchoServer {
|
||||
socket: s,
|
||||
to_send: None,
|
||||
buf: vec![0u8; 1600],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for EchoServer {
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(), io::Error> {
|
||||
loop {
|
||||
if let Some(&(size, peer)) = self.to_send.as_ref() {
|
||||
try_ready!(self.socket.poll_send_to(&self.buf[..size], &peer));
|
||||
self.to_send = None;
|
||||
}
|
||||
self.to_send = Some(try_ready!(self.socket.poll_recv_from(&mut self.buf)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn udp_echo_latency(b: &mut Bencher) {
|
||||
let any_addr = "127.0.0.1:0".to_string();
|
||||
let any_addr = any_addr.parse::<SocketAddr>().unwrap();
|
||||
|
||||
let (stop_c, stop_p) = oneshot::channel::<()>();
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
let child = thread::spawn(move || {
|
||||
|
||||
let socket = tokio::net::UdpSocket::bind(&any_addr).unwrap();
|
||||
tx.send(socket.local_addr().unwrap()).unwrap();
|
||||
|
||||
let server = EchoServer::new(socket);
|
||||
let server = server.select(stop_p.map_err(|_| panic!()));
|
||||
let server = server.map_err(|_| ());
|
||||
server.wait().unwrap();
|
||||
});
|
||||
|
||||
|
||||
let client = std::net::UdpSocket::bind(&any_addr).unwrap();
|
||||
|
||||
let server_addr = rx.wait().unwrap();
|
||||
let mut buf = [0u8; 1000];
|
||||
|
||||
// warmup phase; for some reason initial couple of
|
||||
// runs are much slower
|
||||
//
|
||||
// TODO: Describe the exact reasons; caching? branch predictor? lazy closures?
|
||||
for _ in 0..8 {
|
||||
client.send_to(&buf, &server_addr).unwrap();
|
||||
let _ = client.recv_from(&mut buf).unwrap();
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
client.send_to(&buf, &server_addr).unwrap();
|
||||
let _ = client.recv_from(&mut buf).unwrap();
|
||||
});
|
||||
|
||||
stop_c.send(()).unwrap();
|
||||
child.join().unwrap();
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn futures_channel_latency(b: &mut Bencher) {
|
||||
let (mut in_tx, in_rx) = mpsc::channel(32);
|
||||
let (out_tx, out_rx) = mpsc::channel::<_>(32);
|
||||
|
||||
let child = thread::spawn(|| out_tx.send_all(in_rx.then(|r| r.unwrap())).wait());
|
||||
let mut rx_iter = out_rx.wait();
|
||||
|
||||
// warmup phase; for some reason initial couple of runs are much slower
|
||||
//
|
||||
// TODO: Describe the exact reasons; caching? branch predictor? lazy closures?
|
||||
for _ in 0..8 {
|
||||
in_tx.start_send(Ok(1usize)).unwrap();
|
||||
let _ = rx_iter.next();
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
in_tx.start_send(Ok(1usize)).unwrap();
|
||||
let _ = rx_iter.next();
|
||||
});
|
||||
|
||||
drop(in_tx);
|
||||
child.join().unwrap().unwrap();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Measure cost of different operations
|
||||
// to get a sense of performance tradeoffs
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate test;
|
||||
extern crate mio;
|
||||
|
||||
use test::Bencher;
|
||||
|
||||
use mio::tcp::TcpListener;
|
||||
use mio::{Token, Ready, PollOpt};
|
||||
|
||||
|
||||
#[bench]
|
||||
fn mio_register_deregister(b: &mut Bencher) {
|
||||
let addr = "127.0.0.1:0".parse().unwrap();
|
||||
// Setup the server socket
|
||||
let sock = TcpListener::bind(&addr).unwrap();
|
||||
let poll = mio::Poll::new().unwrap();
|
||||
|
||||
const CLIENT: Token = Token(1);
|
||||
|
||||
b.iter(|| {
|
||||
poll.register(&sock, CLIENT, Ready::readable(),
|
||||
PollOpt::edge()).unwrap();
|
||||
poll.deregister(&sock).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn mio_reregister(b: &mut Bencher) {
|
||||
let addr = "127.0.0.1:0".parse().unwrap();
|
||||
// Setup the server socket
|
||||
let sock = TcpListener::bind(&addr).unwrap();
|
||||
let poll = mio::Poll::new().unwrap();
|
||||
|
||||
const CLIENT: Token = Token(1);
|
||||
poll.register(&sock, CLIENT, Ready::readable(),
|
||||
PollOpt::edge()).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
poll.reregister(&sock, CLIENT, Ready::readable(),
|
||||
PollOpt::edge()).unwrap();
|
||||
});
|
||||
poll.deregister(&sock).unwrap();
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn mio_poll(b: &mut Bencher) {
|
||||
let poll = mio::Poll::new().unwrap();
|
||||
let timeout = std::time::Duration::new(0, 0);
|
||||
let mut events = mio::Events::with_capacity(1024);
|
||||
|
||||
b.iter(|| {
|
||||
poll.poll(&mut events, Some(timeout)).unwrap();
|
||||
});
|
||||
}
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
#[macro_use]
|
||||
extern crate tokio_io;
|
||||
|
||||
pub extern crate test;
|
||||
|
||||
mod prelude {
|
||||
pub use futures::*;
|
||||
pub use tokio::reactor::Reactor;
|
||||
pub use tokio::net::{TcpListener, TcpStream};
|
||||
pub use tokio::executor::current_thread;
|
||||
pub use tokio_io::io::read_to_end;
|
||||
|
||||
pub use test::{self, Bencher};
|
||||
pub use std::thread;
|
||||
pub use std::time::Duration;
|
||||
pub use std::io::{self, Read, Write};
|
||||
}
|
||||
|
||||
mod connect_churn {
|
||||
use ::prelude::*;
|
||||
|
||||
const NUM: usize = 300;
|
||||
const CONCURRENT: usize = 8;
|
||||
|
||||
#[bench]
|
||||
fn one_thread(b: &mut Bencher) {
|
||||
let addr = "127.0.0.1:0".parse().unwrap();
|
||||
|
||||
b.iter(move || {
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
// Spawn a single future that accepts & drops connections
|
||||
let serve_incomings = listener.incoming()
|
||||
.map_err(|e| panic!("server err: {:?}", e))
|
||||
.for_each(|_| Ok(()));
|
||||
|
||||
let connects = stream::iter_result((0..NUM).map(|_| {
|
||||
Ok(TcpStream::connect(&addr)
|
||||
.and_then(|sock| {
|
||||
sock.set_linger(Some(Duration::from_secs(0))).unwrap();
|
||||
read_to_end(sock, vec![])
|
||||
}))
|
||||
}));
|
||||
|
||||
let connects_concurrent = connects.buffer_unordered(CONCURRENT)
|
||||
.map_err(|e| panic!("client err: {:?}", e))
|
||||
.for_each(|_| Ok(()));
|
||||
|
||||
serve_incomings.select(connects_concurrent)
|
||||
.map(|_| ()).map_err(|_| ())
|
||||
.wait().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
fn n_workers(n: usize, b: &mut Bencher) {
|
||||
let (shutdown_tx, shutdown_rx) = sync::oneshot::channel();
|
||||
let (addr_tx, addr_rx) = sync::oneshot::channel();
|
||||
|
||||
// Spawn reactor thread
|
||||
let server_thread = thread::spawn(move || {
|
||||
// Bind the TCP listener
|
||||
let listener = TcpListener::bind(
|
||||
&"127.0.0.1:0".parse().unwrap()).unwrap();
|
||||
|
||||
// Get the address being listened on.
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
// Send the remote & address back to the main thread
|
||||
addr_tx.send(addr).unwrap();
|
||||
|
||||
// Spawn a single future that accepts & drops connections
|
||||
let serve_incomings = listener.incoming()
|
||||
.map_err(|e| panic!("server err: {:?}", e))
|
||||
.for_each(|_| Ok(()));
|
||||
|
||||
// Run server
|
||||
serve_incomings.select(shutdown_rx)
|
||||
.map(|_| ()).map_err(|_| ())
|
||||
.wait().unwrap();
|
||||
});
|
||||
|
||||
// Get the bind addr of the server
|
||||
let addr = addr_rx.wait().unwrap();
|
||||
|
||||
b.iter(move || {
|
||||
use std::sync::{Barrier, Arc};
|
||||
|
||||
// Create a barrier to coordinate threads
|
||||
let barrier = Arc::new(Barrier::new(n + 1));
|
||||
|
||||
// Spawn worker threads
|
||||
let threads: Vec<_> = (0..n).map(|_| {
|
||||
let barrier = barrier.clone();
|
||||
let addr = addr.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
let connects = stream::iter_result((0..(NUM / n)).map(|_| {
|
||||
Ok(TcpStream::connect(&addr)
|
||||
.map_err(|e| panic!("connect err: {:?}", e))
|
||||
.and_then(|sock| {
|
||||
sock.set_linger(Some(Duration::from_secs(0))).unwrap();
|
||||
read_to_end(sock, vec![])
|
||||
}))
|
||||
}));
|
||||
|
||||
barrier.wait();
|
||||
|
||||
connects.buffer_unordered(CONCURRENT)
|
||||
.map_err(|e| panic!("client err: {:?}", e))
|
||||
.for_each(|_| Ok(())).wait().unwrap();
|
||||
})
|
||||
}).collect();
|
||||
|
||||
barrier.wait();
|
||||
|
||||
for th in threads {
|
||||
th.join().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
// Shutdown the server
|
||||
shutdown_tx.send(()).unwrap();
|
||||
server_thread.join().unwrap();
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn two_threads(b: &mut Bencher) {
|
||||
n_workers(1, b);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn multi_threads(b: &mut Bencher) {
|
||||
n_workers(4, b);
|
||||
}
|
||||
}
|
||||
|
||||
mod transfer {
|
||||
use ::prelude::*;
|
||||
use std::{cmp, mem};
|
||||
|
||||
const MB: usize = 3 * 1024 * 1024;
|
||||
|
||||
struct Drain {
|
||||
sock: TcpStream,
|
||||
chunk: usize,
|
||||
}
|
||||
|
||||
impl Future for Drain {
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(), io::Error> {
|
||||
let mut buf: [u8; 1024] = unsafe { mem::uninitialized() };
|
||||
|
||||
loop {
|
||||
match try_nb!(self.sock.read(&mut buf[..self.chunk])) {
|
||||
0 => return Ok(Async::Ready(())),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Transfer {
|
||||
sock: TcpStream,
|
||||
rem: usize,
|
||||
chunk: usize,
|
||||
}
|
||||
|
||||
impl Future for Transfer {
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(), io::Error> {
|
||||
while self.rem > 0 {
|
||||
let len = cmp::min(self.rem, self.chunk);
|
||||
let buf = &DATA[..len];
|
||||
|
||||
let n = try_nb!(self.sock.write(&buf));
|
||||
self.rem -= n;
|
||||
}
|
||||
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
static DATA: [u8; 1024] = [0; 1024];
|
||||
|
||||
fn one_thread(b: &mut Bencher, read_size: usize, write_size: usize) {
|
||||
let addr = "127.0.0.1:0".parse().unwrap();
|
||||
|
||||
b.iter(move || {
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
// Spawn a single future that accepts 1 connection, Drain it and drops
|
||||
let server = listener.incoming()
|
||||
.into_future() // take the first connection
|
||||
.map_err(|(e, _other_incomings)| e)
|
||||
.map(|(connection, _other_incomings)| connection.unwrap())
|
||||
.and_then(|sock| {
|
||||
sock.set_linger(Some(Duration::from_secs(0))).unwrap();
|
||||
let drain = Drain {
|
||||
sock: sock,
|
||||
chunk: read_size,
|
||||
};
|
||||
drain.map(|_| ()).map_err(|e| panic!("server error: {:?}", e))
|
||||
})
|
||||
.map_err(|e| panic!("server err: {:?}", e));
|
||||
|
||||
let client = TcpStream::connect(&addr)
|
||||
.and_then(move |sock| {
|
||||
Transfer {
|
||||
sock: sock,
|
||||
rem: MB,
|
||||
chunk: write_size,
|
||||
}
|
||||
})
|
||||
.map_err(|e| panic!("client err: {:?}", e));
|
||||
|
||||
server.join(client).wait().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
mod small_chunks {
|
||||
use ::prelude::*;
|
||||
|
||||
#[bench]
|
||||
fn one_thread(b: &mut Bencher) {
|
||||
super::one_thread(b, 32, 32);
|
||||
}
|
||||
}
|
||||
|
||||
mod big_chunks {
|
||||
use ::prelude::*;
|
||||
|
||||
#[bench]
|
||||
fn one_thread(b: &mut Bencher) {
|
||||
super::one_thread(b, 1_024, 1_024);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# TSAN suppressions file for Tokio
|
||||
|
||||
# TSAN does not understand fences and `Arc::drop` is implemented using a fence.
|
||||
# This causes many false positives.
|
||||
race:Arc*drop
|
||||
@@ -0,0 +1,54 @@
|
||||
## Examples of how to use Tokio
|
||||
|
||||
This directory contains a number of examples showcasing various capabilities of
|
||||
the `tokio` crate.
|
||||
|
||||
All examples can be executed with:
|
||||
|
||||
```
|
||||
cargo run --example $name
|
||||
```
|
||||
|
||||
A high level description of each example is:
|
||||
|
||||
* [`hello_world`](hello_world.rs) - a tiny server that writes "hello world" to
|
||||
all connected clients and then terminates the connection, should help see how
|
||||
to create and initialize `tokio`.
|
||||
|
||||
* [`echo`](echo.rs) - this is your standard TCP "echo server" which accepts
|
||||
connections and then echos back any contents that are read from each connected
|
||||
client.
|
||||
|
||||
* [`echo-udp`](echo-udp.rs) - again your standard "echo server", except for UDP
|
||||
instead of TCP. This will echo back any packets received to the original
|
||||
sender.
|
||||
|
||||
* [`connect`](connect.rs) - this is a `nc`-like clone which can be used to
|
||||
interact with most other examples. The program creates a TCP connection or UDP
|
||||
socket to sends all information read on stdin to the remote peer, displaying
|
||||
any data received on stdout. Often quite useful when interacting with the
|
||||
various other servers here!
|
||||
|
||||
* [`chat`](chat.rs) - this spins up a local TCP server which will broadcast from
|
||||
any connected client to all other connected clients. You can connect to this
|
||||
in multiple terminals and use it to chat between the terminals.
|
||||
|
||||
* [`chat-combinator`](chat-combinator.rs) - Similar to `chat`, but this uses a
|
||||
much more functional programming approch using combinators.
|
||||
|
||||
* [`proxy`](proxy.rs) - an example proxy server that will forward all connected
|
||||
TCP clients to the remote address specified when starting the program.
|
||||
|
||||
* [`tinyhttp`](tinyhttp.rs) - a tiny HTTP/1.1 server which doesn't support HTTP
|
||||
request bodies showcasing running on multiple cores, working with futures and
|
||||
spawning tasks, and finally framing a TCP connection to discrete
|
||||
request/response objects.
|
||||
|
||||
* [`tinydb`](tinydb.rs) - an in-memory database which shows sharing state
|
||||
between all connected clients, notably the key/value store of this database.
|
||||
|
||||
* [`udp-client`](udp-client.rs) - a simple `send_dgram`/`recv_dgram` example.
|
||||
|
||||
If you've got an example you'd like to see here, please feel free to open an
|
||||
issue. Otherwise if you've got an example you'd like to add, please feel free
|
||||
to make a PR!
|
||||
@@ -0,0 +1,150 @@
|
||||
//! A chat server that broadcasts a message to all connections.
|
||||
//!
|
||||
//! This is a line-based server which accepts connections, reads lines from
|
||||
//! those connections, and broadcasts the lines to all other connected clients.
|
||||
//!
|
||||
//! This example is similar to chat.rs, but uses combinators and a much more
|
||||
//! functional style.
|
||||
//!
|
||||
//! You can test this out by running:
|
||||
//!
|
||||
//! cargo run --example chat
|
||||
//!
|
||||
//! And then in another window run:
|
||||
//!
|
||||
//! cargo run --example connect 127.0.0.1:8080
|
||||
//!
|
||||
//! You can run the second command in multiple windows and then chat between the
|
||||
//! two, seeing the messages from the other client as they're received. For all
|
||||
//! connected clients they'll all join the same room and see everyone else's
|
||||
//! messages.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
extern crate futures;
|
||||
|
||||
use tokio::io;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::iter;
|
||||
use std::env;
|
||||
use std::io::{BufReader};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
fn main() {
|
||||
// Create the TCP listener we'll accept connections on.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse().unwrap();
|
||||
|
||||
let socket = TcpListener::bind(&addr).unwrap();
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
// This is running on the Tokio runtime, so it will be multi-threaded. The
|
||||
// `Arc<Mutex<...>>` allows state to be shared across the threads.
|
||||
let connections = Arc::new(Mutex::new(HashMap::new()));
|
||||
|
||||
// The server task asynchronously iterates over and processes each incoming
|
||||
// connection.
|
||||
let srv = socket.incoming()
|
||||
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
|
||||
.for_each(move |stream| {
|
||||
// The client's socket address
|
||||
let addr = stream.peer_addr().unwrap();
|
||||
|
||||
println!("New Connection: {}", addr);
|
||||
|
||||
// Split the TcpStream into two separate handles. One handle for reading
|
||||
// and one handle for writing. This lets us use separate tasks for
|
||||
// reading and writing.
|
||||
let (reader, writer) = stream.split();
|
||||
|
||||
// Create a channel for our stream, which other sockets will use to
|
||||
// send us messages. Then register our address with the stream to send
|
||||
// data to us.
|
||||
let (tx, rx) = futures::sync::mpsc::unbounded();
|
||||
connections.lock().unwrap().insert(addr, tx);
|
||||
|
||||
// Define here what we do for the actual I/O. That is, read a bunch of
|
||||
// lines from the socket and dispatch them while we also write any lines
|
||||
// from other sockets.
|
||||
let connections_inner = connections.clone();
|
||||
let reader = BufReader::new(reader);
|
||||
|
||||
// Model the read portion of this socket by mapping an infinite
|
||||
// iterator to each line off the socket. This "loop" is then
|
||||
// terminated with an error once we hit EOF on the socket.
|
||||
let iter = stream::iter_ok::<_, io::Error>(iter::repeat(()));
|
||||
|
||||
let socket_reader = iter.fold(reader, move |reader, _| {
|
||||
// Read a line off the socket, failing if we're at EOF
|
||||
let line = io::read_until(reader, b'\n', Vec::new());
|
||||
let line = line.and_then(|(reader, vec)| {
|
||||
if vec.len() == 0 {
|
||||
Err(io::Error::new(io::ErrorKind::BrokenPipe, "broken pipe"))
|
||||
} else {
|
||||
Ok((reader, vec))
|
||||
}
|
||||
});
|
||||
|
||||
// Convert the bytes we read into a string, and then send that
|
||||
// string to all other connected clients.
|
||||
let line = line.map(|(reader, vec)| {
|
||||
(reader, String::from_utf8(vec))
|
||||
});
|
||||
|
||||
// Move the connection state into the closure below.
|
||||
let connections = connections_inner.clone();
|
||||
|
||||
line.map(move |(reader, message)| {
|
||||
println!("{}: {:?}", addr, message);
|
||||
let mut conns = connections.lock().unwrap();
|
||||
|
||||
if let Ok(msg) = message {
|
||||
// For each open connection except the sender, send the
|
||||
// string via the channel.
|
||||
let iter = conns.iter_mut()
|
||||
.filter(|&(&k, _)| k != addr)
|
||||
.map(|(_, v)| v);
|
||||
for tx in iter {
|
||||
tx.unbounded_send(format!("{}: {}", addr, msg)).unwrap();
|
||||
}
|
||||
} else {
|
||||
let tx = conns.get_mut(&addr).unwrap();
|
||||
tx.unbounded_send("You didn't send valid UTF-8.".to_string()).unwrap();
|
||||
}
|
||||
|
||||
reader
|
||||
})
|
||||
});
|
||||
|
||||
// Whenever we receive a string on the Receiver, we write it to
|
||||
// `WriteHalf<TcpStream>`.
|
||||
let socket_writer = rx.fold(writer, |writer, msg| {
|
||||
let amt = io::write_all(writer, msg.into_bytes());
|
||||
let amt = amt.map(|(writer, _)| writer);
|
||||
amt.map_err(|_| ())
|
||||
});
|
||||
|
||||
// Now that we've got futures representing each half of the socket, we
|
||||
// use the `select` combinator to wait for either half to be done to
|
||||
// tear down the other. Then we spawn off the result.
|
||||
let connections = connections.clone();
|
||||
let socket_reader = socket_reader.map_err(|_| ());
|
||||
let connection = socket_reader.map(|_| ()).select(socket_writer.map(|_| ()));
|
||||
|
||||
// Spawn a task to process the connection
|
||||
tokio::spawn(connection.then(move |_| {
|
||||
connections.lock().unwrap().remove(&addr);
|
||||
println!("Connection {} closed.", addr);
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// execute server
|
||||
tokio::run(srv);
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
//! A chat server that broadcasts a message to all connections.
|
||||
//!
|
||||
//! This example is explicitly more verbose than it has to be. This is to
|
||||
//! illustrate more concepts.
|
||||
//!
|
||||
//! A chat server for telnet clients. After a telnet client connects, the first
|
||||
//! line should contain the client's name. After that, all lines send by a
|
||||
//! client are broadcasted to all other connected clients.
|
||||
//!
|
||||
//! Because the client is telnet, lines are delimited by "\r\n".
|
||||
//!
|
||||
//! You can test this out by running:
|
||||
//!
|
||||
//! cargo run --example chat
|
||||
//!
|
||||
//! And then in another terminal run:
|
||||
//!
|
||||
//! telnet localhost 6142
|
||||
//!
|
||||
//! You can run the `telnet` command in any number of additional windows.
|
||||
//!
|
||||
//! You can run the second command in multiple windows and then chat between the
|
||||
//! two, seeing the messages from the other client as they're received. For all
|
||||
//! connected clients they'll all join the same room and see everyone else's
|
||||
//! messages.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate bytes;
|
||||
|
||||
use tokio::io;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
use futures::sync::mpsc;
|
||||
use futures::future::{self, Either};
|
||||
use bytes::{BytesMut, Bytes, BufMut};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Shorthand for the transmit half of the message channel.
|
||||
type Tx = mpsc::UnboundedSender<Bytes>;
|
||||
|
||||
/// Shorthand for the receive half of the message channel.
|
||||
type Rx = mpsc::UnboundedReceiver<Bytes>;
|
||||
|
||||
/// Data that is shared between all peers in the chat server.
|
||||
///
|
||||
/// This is the set of `Tx` handles for all connected clients. Whenever a
|
||||
/// message is received from a client, it is broadcasted to all peers by
|
||||
/// iterating over the `peers` entries and sending a copy of the message on each
|
||||
/// `Tx`.
|
||||
struct Shared {
|
||||
peers: HashMap<SocketAddr, Tx>,
|
||||
}
|
||||
|
||||
/// The state for each connected client.
|
||||
struct Peer {
|
||||
/// Name of the peer.
|
||||
///
|
||||
/// When a client connects, the first line sent is treated as the client's
|
||||
/// name (like alice or bob). The name is used to preface all messages that
|
||||
/// arrive from the client so that we can simulate a real chat server:
|
||||
///
|
||||
/// ```text
|
||||
/// alice: Hello everyone.
|
||||
/// bob: Welcome to telnet chat!
|
||||
/// ```
|
||||
name: BytesMut,
|
||||
|
||||
/// The TCP socket wrapped with the `Lines` codec, defined below.
|
||||
///
|
||||
/// This handles sending and receiving data on the socket. When using
|
||||
/// `Lines`, we can work at the line level instead of having to manage the
|
||||
/// raw byte operations.
|
||||
lines: Lines,
|
||||
|
||||
/// Handle to the shared chat state.
|
||||
///
|
||||
/// This is used to broadcast messages read off the socket to all connected
|
||||
/// peers.
|
||||
state: Arc<Mutex<Shared>>,
|
||||
|
||||
/// Receive half of the message channel.
|
||||
///
|
||||
/// This is used to receive messages from peers. When a message is received
|
||||
/// off of this `Rx`, it will be written to the socket.
|
||||
rx: Rx,
|
||||
|
||||
/// Client socket address.
|
||||
///
|
||||
/// The socket address is used as the key in the `peers` HashMap. The
|
||||
/// address is saved so that the `Peer` drop implementation can clean up its
|
||||
/// entry.
|
||||
addr: SocketAddr,
|
||||
}
|
||||
|
||||
/// Line based codec
|
||||
///
|
||||
/// This decorates a socket and presents a line based read / write interface.
|
||||
///
|
||||
/// As a user of `Lines`, we can focus on working at the line level. So, we send
|
||||
/// and receive values that represent entire lines. The `Lines` codec will
|
||||
/// handle the encoding and decoding as well as reading from and writing to the
|
||||
/// socket.
|
||||
#[derive(Debug)]
|
||||
struct Lines {
|
||||
/// The TCP socket.
|
||||
socket: TcpStream,
|
||||
|
||||
/// Buffer used when reading from the socket. Data is not returned from this
|
||||
/// buffer until an entire line has been read.
|
||||
rd: BytesMut,
|
||||
|
||||
/// Buffer used to stage data before writing it to the socket.
|
||||
wr: BytesMut,
|
||||
}
|
||||
|
||||
impl Shared {
|
||||
/// Create a new, empty, instance of `Shared`.
|
||||
fn new() -> Self {
|
||||
Shared {
|
||||
peers: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Peer {
|
||||
/// Create a new instance of `Peer`.
|
||||
fn new(name: BytesMut,
|
||||
state: Arc<Mutex<Shared>>,
|
||||
lines: Lines) -> Peer
|
||||
{
|
||||
// Get the client socket address
|
||||
let addr = lines.socket.peer_addr().unwrap();
|
||||
|
||||
// Create a channel for this peer
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
|
||||
// Add an entry for this `Peer` in the shared state map.
|
||||
state.lock().unwrap()
|
||||
.peers.insert(addr, tx);
|
||||
|
||||
Peer {
|
||||
name,
|
||||
lines,
|
||||
state,
|
||||
rx,
|
||||
addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is where a connected client is managed.
|
||||
///
|
||||
/// A `Peer` is also a future representing completly processing the client.
|
||||
///
|
||||
/// When a `Peer` is created, the first line (representing the client's name)
|
||||
/// has already been read. When the socket closes, the `Peer` future completes.
|
||||
///
|
||||
/// While processing, the peer future implementation will:
|
||||
///
|
||||
/// 1) Receive messages on its message channel and write them to the socket.
|
||||
/// 2) Receive messages from the socket and broadcast them to all peers.
|
||||
///
|
||||
impl Future for Peer {
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(), io::Error> {
|
||||
// Tokio (and futures) use cooperative scheduling without any
|
||||
// preemption. If a task never yields execution back to the executor,
|
||||
// then other tasks may be starved.
|
||||
//
|
||||
// To deal with this, robust applications should not have any unbounded
|
||||
// loops. In this example, we will read at most `LINES_PER_TICK` lines
|
||||
// from the client on each tick.
|
||||
//
|
||||
// If the limit is hit, the current task is notified, informing the
|
||||
// executor to schedule the task again asap.
|
||||
const LINES_PER_TICK: usize = 10;
|
||||
|
||||
// Receive all messages from peers.
|
||||
for i in 0..LINES_PER_TICK {
|
||||
// Polling an `UnboundedReceiver` cannot fail, so `unwrap` here is
|
||||
// safe.
|
||||
match self.rx.poll().unwrap() {
|
||||
Async::Ready(Some(v)) => {
|
||||
// Buffer the line. Once all lines are buffered, they will
|
||||
// be flushed to the socket (right below).
|
||||
self.lines.buffer(&v);
|
||||
|
||||
// If this is the last iteration, the loop will break even
|
||||
// though there could still be lines to read. Because we did
|
||||
// not reach `Async::NotReady`, we have to notify ourselves
|
||||
// in order to tell the executor to schedule the task again.
|
||||
if i+1 == LINES_PER_TICK {
|
||||
task::current().notify();
|
||||
}
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the write buffer to the socket
|
||||
let _ = self.lines.poll_flush()?;
|
||||
|
||||
// Read new lines from the socket
|
||||
while let Async::Ready(line) = self.lines.poll()? {
|
||||
println!("Received line ({:?}) : {:?}", self.name, line);
|
||||
|
||||
if let Some(message) = line {
|
||||
// Append the peer's name to the front of the line:
|
||||
let mut line = self.name.clone();
|
||||
line.extend_from_slice(b": ");
|
||||
line.extend_from_slice(&message);
|
||||
line.extend_from_slice(b"\r\n");
|
||||
|
||||
// We're using `Bytes`, which allows zero-copy clones (by
|
||||
// storing the data in an Arc internally).
|
||||
//
|
||||
// However, before cloning, we must freeze the data. This
|
||||
// converts it from mutable -> immutable, allowing zero copy
|
||||
// cloning.
|
||||
let line = line.freeze();
|
||||
|
||||
// Now, send the line to all other peers
|
||||
for (addr, tx) in &self.state.lock().unwrap().peers {
|
||||
// Don't send the message to ourselves
|
||||
if *addr != self.addr {
|
||||
// The send only fails if the rx half has been dropped,
|
||||
// however this is impossible as the `tx` half will be
|
||||
// removed from the map before the `rx` is dropped.
|
||||
tx.unbounded_send(line.clone()).unwrap();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// EOF was reached. The remote client has disconnected. There is
|
||||
// nothing more to do.
|
||||
return Ok(Async::Ready(()));
|
||||
}
|
||||
}
|
||||
|
||||
// As always, it is important to not just return `NotReady` without
|
||||
// ensuring an inner future also returned `NotReady`.
|
||||
//
|
||||
// We know we got a `NotReady` from either `self.rx` or `self.lines`, so
|
||||
// the contract is respected.
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Peer {
|
||||
fn drop(&mut self) {
|
||||
self.state.lock().unwrap().peers
|
||||
.remove(&self.addr);
|
||||
}
|
||||
}
|
||||
|
||||
impl Lines {
|
||||
/// Create a new `Lines` codec backed by the socket
|
||||
fn new(socket: TcpStream) -> Self {
|
||||
Lines {
|
||||
socket,
|
||||
rd: BytesMut::new(),
|
||||
wr: BytesMut::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffer a line.
|
||||
///
|
||||
/// This writes the line to an internal buffer. Calls to `poll_flush` will
|
||||
/// attempt to flush this buffer to the socket.
|
||||
fn buffer(&mut self, line: &[u8]) {
|
||||
// Ensure the buffer has capacity. Ideally this would not be unbounded,
|
||||
// but to keep the example simple, we will not limit this.
|
||||
self.wr.reserve(line.len());
|
||||
|
||||
// Push the line onto the end of the write buffer.
|
||||
//
|
||||
// The `put` function is from the `BufMut` trait.
|
||||
self.wr.put(line);
|
||||
}
|
||||
|
||||
/// Flush the write buffer to the socket
|
||||
fn poll_flush(&mut self) -> Poll<(), io::Error> {
|
||||
// As long as there is buffered data to write, try to write it.
|
||||
while !self.wr.is_empty() {
|
||||
// Try to read some bytes from the socket
|
||||
let n = try_ready!(self.socket.poll_write(&self.wr));
|
||||
|
||||
// As long as the wr is not empty, a successful write should
|
||||
// never write 0 bytes.
|
||||
assert!(n > 0);
|
||||
|
||||
// This discards the first `n` bytes of the buffer.
|
||||
let _ = self.wr.split_to(n);
|
||||
}
|
||||
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
|
||||
/// Read data from the socket.
|
||||
///
|
||||
/// This only returns `Ready` when the socket has closed.
|
||||
fn fill_read_buf(&mut self) -> Poll<(), io::Error> {
|
||||
loop {
|
||||
// Ensure the read buffer has capacity.
|
||||
//
|
||||
// This might result in an internal allocation.
|
||||
self.rd.reserve(1024);
|
||||
|
||||
// Read data into the buffer.
|
||||
let n = try_ready!(self.socket.read_buf(&mut self.rd));
|
||||
|
||||
if n == 0 {
|
||||
return Ok(Async::Ready(()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for Lines {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
// First, read any new data that might have been received off the socket
|
||||
let sock_closed = self.fill_read_buf()?.is_ready();
|
||||
|
||||
// Now, try finding lines
|
||||
let pos = self.rd.windows(2).enumerate()
|
||||
.find(|&(_, bytes)| bytes == b"\r\n")
|
||||
.map(|(i, _)| i);
|
||||
|
||||
if let Some(pos) = pos {
|
||||
// Remove the line from the read buffer and set it to `line`.
|
||||
let mut line = self.rd.split_to(pos + 2);
|
||||
|
||||
// Drop the trailing \r\n
|
||||
line.split_off(pos);
|
||||
|
||||
// Return the line
|
||||
return Ok(Async::Ready(Some(line)));
|
||||
}
|
||||
|
||||
if sock_closed {
|
||||
Ok(Async::Ready(None))
|
||||
} else {
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a task to manage the socket.
|
||||
///
|
||||
/// This will read the first line from the socket to identify the client, then
|
||||
/// add the client to the set of connected peers in the chat service.
|
||||
fn process(socket: TcpStream, state: Arc<Mutex<Shared>>) {
|
||||
// Wrap the socket with the `Lines` codec that we wrote above.
|
||||
//
|
||||
// By doing this, we can operate at the line level instead of doing raw byte
|
||||
// manipulation.
|
||||
let lines = Lines::new(socket);
|
||||
|
||||
// The first line is treated as the client's name. The client is not added
|
||||
// to the set of connected peers until this line is received.
|
||||
//
|
||||
// We use the `into_future` combinator to extract the first item from the
|
||||
// lines stream. `into_future` takes a `Stream` and converts it to a future
|
||||
// of `(first, rest)` where `rest` is the original stream instance.
|
||||
let connection = lines.into_future()
|
||||
// `into_future` doesn't have the right error type, so map the error to
|
||||
// make it work.
|
||||
.map_err(|(e, _)| e)
|
||||
// Process the first received line as the client's name.
|
||||
.and_then(|(name, lines)| {
|
||||
// If `name` is `None`, then the client disconnected without
|
||||
// actually sending a line of data.
|
||||
//
|
||||
// Since the connection is closed, there is no further work that we
|
||||
// need to do. So, we just terminate processing by returning
|
||||
// `future::ok()`.
|
||||
//
|
||||
// The problem is that only a single future type can be returned
|
||||
// from a combinator closure, but we want to return both
|
||||
// `future::ok()` and `Peer` (below).
|
||||
//
|
||||
// This is a common problem, so the `futures` crate solves this by
|
||||
// providing the `Either` helper enum that allows creating a single
|
||||
// return type that covers two concrete future types.
|
||||
let name = match name {
|
||||
Some(name) => name,
|
||||
None => {
|
||||
// The remote client closed the connection without sending
|
||||
// any data.
|
||||
return Either::A(future::ok(()));
|
||||
}
|
||||
};
|
||||
|
||||
println!("`{:?}` is joining the chat", name);
|
||||
|
||||
// Create the peer.
|
||||
//
|
||||
// This is also a future that processes the connection, only
|
||||
// completing when the socket closes.
|
||||
let peer = Peer::new(
|
||||
name,
|
||||
state,
|
||||
lines);
|
||||
|
||||
// Wrap `peer` with `Either::B` to make the return type fit.
|
||||
Either::B(peer)
|
||||
})
|
||||
// Task futures have an error of type `()`, this ensures we handle the
|
||||
// error. We do this by printing the error to STDOUT.
|
||||
.map_err(|e| {
|
||||
println!("connection error = {:?}", e);
|
||||
});
|
||||
|
||||
// Spawn the task. Internally, this submits the task to a thread pool.
|
||||
tokio::spawn(connection);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
// Create the shared state. This is how all the peers communicate.
|
||||
//
|
||||
// The server task will hold a handle to this. For every new client, the
|
||||
// `state` handle is cloned and passed into the task that processes the
|
||||
// client connection.
|
||||
let state = Arc::new(Mutex::new(Shared::new()));
|
||||
|
||||
let addr = "127.0.0.1:6142".parse().unwrap();
|
||||
|
||||
// Bind a TCP listener to the socket address.
|
||||
//
|
||||
// Note that this is the Tokio TcpListener, which is fully async.
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
|
||||
// The server task asynchronously iterates over and processes each
|
||||
// incoming connection.
|
||||
let server = listener.incoming().for_each(move |socket| {
|
||||
// Spawn a task to process the connection
|
||||
process(socket, state.clone());
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| {
|
||||
// All tasks must have an `Error` type of `()`. This forces error
|
||||
// handling and helps avoid silencing failures.
|
||||
//
|
||||
// In our example, we are only going to log the error to STDOUT.
|
||||
println!("accept error = {:?}", err);
|
||||
});
|
||||
|
||||
println!("server running on localhost:6142");
|
||||
|
||||
// Start the Tokio runtime.
|
||||
//
|
||||
// The Tokio is a pre-configured "out of the box" runtime for building
|
||||
// asynchronous applications. It includes both a reactor and a task
|
||||
// scheduler. This means applications are multithreaded by default.
|
||||
//
|
||||
// This function blocks until the runtime reaches an idle state. Idle is
|
||||
// defined as all spawned tasks have completed and all I/O resources (TCP
|
||||
// sockets in our case) have been dropped.
|
||||
//
|
||||
// In our example, we have not defined a shutdown strategy, so this will
|
||||
// block until `ctrl-c` is pressed at the terminal.
|
||||
tokio::run(server);
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
//! An example of hooking up stdin/stdout to either a TCP or UDP stream.
|
||||
//!
|
||||
//! This example will connect to a socket address specified in the argument list
|
||||
//! and then forward all data read on stdin to the server, printing out all data
|
||||
//! received on stdout. An optional `--udp` argument can be passed to specify
|
||||
//! that the connection should be made over UDP instead of TCP, translating each
|
||||
//! line entered on stdin to a UDP packet to be sent to the remote address.
|
||||
//!
|
||||
//! Note that this is not currently optimized for performance, especially
|
||||
//! around buffer management. Rather it's intended to show an example of
|
||||
//! working with a client.
|
||||
//!
|
||||
//! This example can be quite useful when interacting with the other examples in
|
||||
//! this repository! Many of them recommend running this as a simple "hook up
|
||||
//! stdin/stdout to a server" to get up and running.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
extern crate futures;
|
||||
extern crate bytes;
|
||||
|
||||
use std::env;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::SocketAddr;
|
||||
use std::thread;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use futures::sync::mpsc;
|
||||
|
||||
fn main() {
|
||||
// Determine if we're going to run in TCP or UDP mode
|
||||
let mut args = env::args().skip(1).collect::<Vec<_>>();
|
||||
let tcp = match args.iter().position(|a| a == "--udp") {
|
||||
Some(i) => {
|
||||
args.remove(i);
|
||||
false
|
||||
}
|
||||
None => true,
|
||||
};
|
||||
|
||||
// Parse what address we're going to connect to
|
||||
let addr = args.first().unwrap_or_else(|| {
|
||||
panic!("this program requires at least one argument")
|
||||
});
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
|
||||
// Right now Tokio doesn't support a handle to stdin running on the event
|
||||
// loop, so we farm out that work to a separate thread. This thread will
|
||||
// read data (with blocking I/O) from stdin and then send it to the event
|
||||
// loop over a standard futures channel.
|
||||
let (stdin_tx, stdin_rx) = mpsc::channel(0);
|
||||
thread::spawn(|| read_stdin(stdin_tx));
|
||||
let stdin_rx = stdin_rx.map_err(|_| panic!()); // errors not possible on rx
|
||||
|
||||
// Now that we've got our stdin read we either set up our TCP connection or
|
||||
// our UDP connection to get a stream of bytes we're going to emit to
|
||||
// stdout.
|
||||
let stdout = if tcp {
|
||||
tcp::connect(&addr, Box::new(stdin_rx))
|
||||
} else {
|
||||
udp::connect(&addr, Box::new(stdin_rx))
|
||||
};
|
||||
|
||||
// And now with our stream of bytes to write to stdout, we execute that in
|
||||
// the event loop! Note that this is doing blocking I/O to emit data to
|
||||
// stdout, and in general it's a no-no to do that sort of work on the event
|
||||
// loop. In this case, though, we know it's ok as the event loop isn't
|
||||
// otherwise running anything useful.
|
||||
let mut out = io::stdout();
|
||||
|
||||
tokio::run({
|
||||
stdout
|
||||
.for_each(move |chunk| {
|
||||
out.write_all(&chunk)
|
||||
})
|
||||
.map_err(|e| println!("error reading stdout; error = {:?}", e))
|
||||
});
|
||||
}
|
||||
|
||||
mod codec {
|
||||
use std::io;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use tokio_io::codec::{Encoder, Decoder};
|
||||
|
||||
/// A simple `Codec` implementation that just ships bytes around.
|
||||
///
|
||||
/// This type is used for "framing" a TCP/UDP stream of bytes but it's really
|
||||
/// just a convenient method for us to work with streams/sinks for now.
|
||||
/// This'll just take any data read and interpret it as a "frame" and
|
||||
/// conversely just shove data into the output location without looking at
|
||||
/// it.
|
||||
pub struct Bytes;
|
||||
|
||||
impl Decoder for Bytes {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<BytesMut>> {
|
||||
if buf.len() > 0 {
|
||||
let len = buf.len();
|
||||
Ok(Some(buf.split_to(len)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder for Bytes {
|
||||
type Item = Vec<u8>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, data: Vec<u8>, buf: &mut BytesMut) -> io::Result<()> {
|
||||
buf.put(&data[..]);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod tcp {
|
||||
use tokio;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
|
||||
use bytes::BytesMut;
|
||||
use codec::Bytes;
|
||||
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
pub fn connect(addr: &SocketAddr,
|
||||
stdin: Box<Stream<Item = Vec<u8>, Error = io::Error> + Send>)
|
||||
-> Box<Stream<Item = BytesMut, Error = io::Error> + Send>
|
||||
{
|
||||
let tcp = TcpStream::connect(addr);
|
||||
|
||||
// After the TCP connection has been established, we set up our client
|
||||
// to start forwarding data.
|
||||
//
|
||||
// First we use the `Io::framed` method with a simple implementation of
|
||||
// a `Codec` (listed below) that just ships bytes around. We then split
|
||||
// that in two to work with the stream and sink separately.
|
||||
//
|
||||
// Half of the work we're going to do is to take all data we receive on
|
||||
// `stdin` and send that along the TCP stream (`sink`). The second half
|
||||
// is to take all the data we receive (`stream`) and then write that to
|
||||
// stdout. We'll be passing this handle back out from this method.
|
||||
//
|
||||
// You'll also note that we *spawn* the work to read stdin and write it
|
||||
// to the TCP stream. This is done to ensure that happens concurrently
|
||||
// with us reading data from the stream.
|
||||
Box::new(tcp.map(move |stream| {
|
||||
let (sink, stream) = stream.framed(Bytes).split();
|
||||
|
||||
tokio::spawn(stdin.forward(sink).then(|result| {
|
||||
if let Err(e) = result {
|
||||
panic!("failed to write to socket: {}", e)
|
||||
}
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
stream
|
||||
}).flatten_stream())
|
||||
}
|
||||
}
|
||||
|
||||
mod udp {
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use tokio;
|
||||
use tokio::net::{UdpSocket, UdpFramed};
|
||||
use tokio::prelude::*;
|
||||
use bytes::BytesMut;
|
||||
|
||||
use codec::Bytes;
|
||||
|
||||
pub fn connect(&addr: &SocketAddr,
|
||||
stdin: Box<Stream<Item = Vec<u8>, Error = io::Error> + Send>)
|
||||
-> Box<Stream<Item = BytesMut, Error = io::Error> + Send>
|
||||
{
|
||||
// We'll bind our UDP socket to a local IP/port, but for now we
|
||||
// basically let the OS pick both of those.
|
||||
let addr_to_bind = if addr.ip().is_ipv4() {
|
||||
"0.0.0.0:0".parse().unwrap()
|
||||
} else {
|
||||
"[::]:0".parse().unwrap()
|
||||
};
|
||||
let udp = UdpSocket::bind(&addr_to_bind)
|
||||
.expect("failed to bind socket");
|
||||
|
||||
// Like above with TCP we use an instance of `Bytes` codec to transform
|
||||
// this UDP socket into a framed sink/stream which operates over
|
||||
// discrete values. In this case we're working with *pairs* of socket
|
||||
// addresses and byte buffers.
|
||||
let (sink, stream) = UdpFramed::new(udp, Bytes).split();
|
||||
|
||||
// All bytes from `stdin` will go to the `addr` specified in our
|
||||
// argument list. Like with TCP this is spawned concurrently
|
||||
let forward_stdin = stdin.map(move |chunk| {
|
||||
(chunk, addr)
|
||||
}).forward(sink).then(|result| {
|
||||
if let Err(e) = result {
|
||||
panic!("failed to write to socket: {}", e)
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// With UDP we could receive data from any source, so filter out
|
||||
// anything coming from a different address
|
||||
let receive = stream.filter_map(move |(chunk, src)| {
|
||||
if src == addr {
|
||||
Some(chunk.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
Box::new(future::lazy(|| {
|
||||
tokio::spawn(forward_stdin);
|
||||
future::ok(receive)
|
||||
}).flatten_stream())
|
||||
}
|
||||
}
|
||||
|
||||
// Our helper method which will read data from stdin and send it along the
|
||||
// sender provided.
|
||||
fn read_stdin(mut tx: mpsc::Sender<Vec<u8>>) {
|
||||
let mut stdin = io::stdin();
|
||||
loop {
|
||||
let mut buf = vec![0; 1024];
|
||||
let n = match stdin.read(&mut buf) {
|
||||
Err(_) |
|
||||
Ok(0) => break,
|
||||
Ok(n) => n,
|
||||
};
|
||||
buf.truncate(n);
|
||||
tx = match tx.send(buf).wait() {
|
||||
Ok(tx) => tx,
|
||||
Err(_) => break,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
//! An UDP echo server that just sends back everything that it receives.
|
||||
//!
|
||||
//! If you're on unix you can test this out by in one terminal executing:
|
||||
//!
|
||||
//! cargo run --example echo-udp
|
||||
//!
|
||||
//! and in another terminal you can run:
|
||||
//!
|
||||
//! cargo run --example connect -- --udp 127.0.0.1:8080
|
||||
//!
|
||||
//! Each line you type in to the `nc` terminal should be echo'd back to you!
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
use std::{env, io};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
struct Server {
|
||||
socket: UdpSocket,
|
||||
buf: Vec<u8>,
|
||||
to_send: Option<(usize, SocketAddr)>,
|
||||
}
|
||||
|
||||
impl Future for Server {
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(), io::Error> {
|
||||
loop {
|
||||
// First we check to see if there's a message we need to echo back.
|
||||
// If so then we try to send it back to the original source, waiting
|
||||
// until it's writable and we're able to do so.
|
||||
if let Some((size, peer)) = self.to_send {
|
||||
let amt = try_ready!(self.socket.poll_send_to(&self.buf[..size], &peer));
|
||||
println!("Echoed {}/{} bytes to {}", amt, size, peer);
|
||||
self.to_send = None;
|
||||
}
|
||||
|
||||
// If we're here then `to_send` is `None`, so we take a look for the
|
||||
// next message we're going to echo back.
|
||||
self.to_send = Some(try_ready!(self.socket.poll_recv_from(&mut self.buf)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
|
||||
let socket = UdpSocket::bind(&addr).unwrap();
|
||||
println!("Listening on: {}", socket.local_addr().unwrap());
|
||||
|
||||
let server = Server {
|
||||
socket: socket,
|
||||
buf: vec![0; 1024],
|
||||
to_send: None,
|
||||
};
|
||||
|
||||
// This starts the server task.
|
||||
//
|
||||
// `map_err` handles the error by logging it and maps the future to a type
|
||||
// that can be spawned.
|
||||
//
|
||||
// `tokio::run` spanws the task on the Tokio runtime and starts running.
|
||||
tokio::run(server.map_err(|e| println!("server error = {:?}", e)));
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
//! A "hello world" echo server with Tokio
|
||||
//!
|
||||
//! This server will create a TCP listener, accept connections in a loop, and
|
||||
//! write back everything that's read off of each TCP connection.
|
||||
//!
|
||||
//! Because the Tokio runtime uses a thread poool, each TCP connection is
|
||||
//! processed concurrently with all other TCP connections across multiple
|
||||
//! threads.
|
||||
//!
|
||||
//! To see this server in action, you can run this in one terminal:
|
||||
//!
|
||||
//! cargo run --example echo
|
||||
//!
|
||||
//! and in another terminal you can run:
|
||||
//!
|
||||
//! cargo run --example connect 127.0.0.1:8080
|
||||
//!
|
||||
//! Each line you type in to the `connect` terminal should be echo'd back to
|
||||
//! you! If you open up multiple terminals running the `connect` example you
|
||||
//! should be able to see them all make progress simultaneously.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::io;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
fn main() {
|
||||
// Allow passing an address to listen on as the first argument of this
|
||||
// program, but otherwise we'll just set up our TCP listener on
|
||||
// 127.0.0.1:8080 for connections.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
|
||||
// Next up we create a TCP listener which will listen for incoming
|
||||
// connections. This TCP listener is bound to the address we determined
|
||||
// above and must be associated with an event loop, so we pass in a handle
|
||||
// to our event loop. After the socket's created we inform that we're ready
|
||||
// to go and start accepting connections.
|
||||
let socket = TcpListener::bind(&addr).unwrap();
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
// Here we convert the `TcpListener` to a stream of incoming connections
|
||||
// with the `incoming` method. We then define how to process each element in
|
||||
// the stream with the `for_each` method.
|
||||
//
|
||||
// This combinator, defined on the `Stream` trait, will allow us to define a
|
||||
// computation to happen for all items on the stream (in this case TCP
|
||||
// connections made to the server). The return value of the `for_each`
|
||||
// method is itself a future representing processing the entire stream of
|
||||
// connections, and ends up being our server.
|
||||
let done = socket.incoming()
|
||||
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
|
||||
.for_each(move |socket| {
|
||||
// Once we're inside this closure this represents an accepted client
|
||||
// from our server. The `socket` is the client connection (similar to
|
||||
// how the standard library operates).
|
||||
//
|
||||
// We just want to copy all data read from the socket back onto the
|
||||
// socket itself (e.g. "echo"). We can use the standard `io::copy`
|
||||
// combinator in the `tokio-core` crate to do precisely this!
|
||||
//
|
||||
// The `copy` function takes two arguments, where to read from and where
|
||||
// to write to. We only have one argument, though, with `socket`.
|
||||
// Luckily there's a method, `Io::split`, which will split an Read/Write
|
||||
// stream into its two halves. This operation allows us to work with
|
||||
// each stream independently, such as pass them as two arguments to the
|
||||
// `copy` function.
|
||||
//
|
||||
// The `copy` function then returns a future, and this future will be
|
||||
// resolved when the copying operation is complete, resolving to the
|
||||
// amount of data that was copied.
|
||||
let (reader, writer) = socket.split();
|
||||
let amt = io::copy(reader, writer);
|
||||
|
||||
// After our copy operation is complete we just print out some helpful
|
||||
// information.
|
||||
let msg = amt.then(move |result| {
|
||||
match result {
|
||||
Ok((amt, _, _)) => println!("wrote {} bytes", amt),
|
||||
Err(e) => println!("error: {}", e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
|
||||
// And this is where much of the magic of this server happens. We
|
||||
// crucially want all clients to make progress concurrently, rather than
|
||||
// blocking one on completion of another. To achieve this we use the
|
||||
// `tokio::spawn` function to execute the work in the background.
|
||||
//
|
||||
// This function will transfer ownership of the future (`msg` in this
|
||||
// case) to the Tokio runtime thread pool that. The thread pool will
|
||||
// drive the future to completion.
|
||||
//
|
||||
// Essentially here we're executing a new task to run concurrently,
|
||||
// which will allow all of our clients to be processed concurrently.
|
||||
tokio::spawn(msg)
|
||||
});
|
||||
|
||||
// And finally now that we've define what our server is, we run it!
|
||||
//
|
||||
// This starts the Tokio runtime, spawns the server task, and blocks the
|
||||
// current thread until all tasks complete execution. Since the `done` task
|
||||
// never completes (it just keeps accepting sockets), `tokio::run` blocks
|
||||
// forever (until ctrl-c is pressed).
|
||||
tokio::run(done);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
//! Hello world server.
|
||||
//!
|
||||
//! A simple server that accepts connections, writes "hello world\n", and closes
|
||||
//! the connection.
|
||||
//!
|
||||
//! You can test this out by running:
|
||||
//!
|
||||
//! cargo run --example hello_world
|
||||
//!
|
||||
//! And then in another terminal run:
|
||||
//!
|
||||
//! telnet localhost 6142
|
||||
//!
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::io;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
|
||||
pub fn main() {
|
||||
let addr = "127.0.0.1:6142".parse().unwrap();
|
||||
|
||||
// Bind a TCP listener to the socket address.
|
||||
//
|
||||
// Note that this is the Tokio TcpListener, which is fully async.
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
|
||||
// The server task asynchronously iterates over and processes each
|
||||
// incoming connection.
|
||||
let server = listener.incoming().for_each(|socket| {
|
||||
println!("accepted socket; addr={:?}", socket.peer_addr().unwrap());
|
||||
|
||||
let connection = io::write_all(socket, "hello world\n")
|
||||
.then(|res| {
|
||||
println!("wrote message; success={:?}", res.is_ok());
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// Spawn a new task that processes the socket:
|
||||
tokio::spawn(connection);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| {
|
||||
// All tasks must have an `Error` type of `()`. This forces error
|
||||
// handling and helps avoid silencing failures.
|
||||
//
|
||||
// In our example, we are only going to log the error to STDOUT.
|
||||
println!("accept error = {:?}", err);
|
||||
});
|
||||
|
||||
println!("server running on localhost:6142");
|
||||
|
||||
// Start the Tokio runtime.
|
||||
//
|
||||
// The Tokio is a pre-configured "out of the box" runtime for building
|
||||
// asynchronous applications. It includes both a reactor and a task
|
||||
// scheduler. This means applications are multithreaded by default.
|
||||
//
|
||||
// This function blocks until the runtime reaches an idle state. Idle is
|
||||
// defined as all spawned tasks have completed and all I/O resources (TCP
|
||||
// sockets in our case) have been dropped.
|
||||
//
|
||||
// In our example, we have not defined a shutdown strategy, so this will
|
||||
// block until `ctrl-c` is pressed at the terminal.
|
||||
tokio::run(server);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
extern crate futures;
|
||||
extern crate tokio_core;
|
||||
extern crate tokio_signal;
|
||||
|
||||
use futures::stream::Stream;
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
fn main() {
|
||||
let mut core = Core::new().unwrap();
|
||||
let ctrlc = tokio_signal::ctrl_c(&core.handle());
|
||||
let stream = core.run(ctrlc).unwrap();
|
||||
|
||||
core.run(stream.for_each(|()| {
|
||||
println!("Ctrl-C received!");
|
||||
Ok(())
|
||||
})).unwrap();
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
//! A proxy that forwards data to another server and forwards that server's
|
||||
//! responses back to clients.
|
||||
//!
|
||||
//! Because the Tokio runtime uses a thread poool, each TCP connection is
|
||||
//! processed concurrently with all other TCP connections across multiple
|
||||
//! threads.
|
||||
//!
|
||||
//! You can showcase this by running this in one terminal:
|
||||
//!
|
||||
//! cargo run --example proxy
|
||||
//!
|
||||
//! This in another terminal
|
||||
//!
|
||||
//! cargo run --example echo
|
||||
//!
|
||||
//! And finally this in another terminal
|
||||
//!
|
||||
//! cargo run --example connect 127.0.0.1:8081
|
||||
//!
|
||||
//! This final terminal will connect to our proxy, which will in turn connect to
|
||||
//! the echo server, and you'll be able to see data flowing between them.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::env;
|
||||
use std::net::{Shutdown, SocketAddr};
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use tokio::io::{copy, shutdown};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let listen_addr = env::args().nth(1).unwrap_or("127.0.0.1:8081".to_string());
|
||||
let listen_addr = listen_addr.parse::<SocketAddr>().unwrap();
|
||||
|
||||
let server_addr = env::args().nth(2).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let server_addr = server_addr.parse::<SocketAddr>().unwrap();
|
||||
|
||||
// Create a TCP listener which will listen for incoming connections.
|
||||
let socket = TcpListener::bind(&listen_addr).unwrap();
|
||||
println!("Listening on: {}", listen_addr);
|
||||
println!("Proxying to: {}", server_addr);
|
||||
|
||||
let done = socket.incoming()
|
||||
.map_err(|e| println!("error accepting socket; error = {:?}", e))
|
||||
.for_each(move |client| {
|
||||
let server = TcpStream::connect(&server_addr);
|
||||
let amounts = server.and_then(move |server| {
|
||||
// Create separate read/write handles for the TCP clients that we're
|
||||
// proxying data between. Note that typically you'd use
|
||||
// `AsyncRead::split` for this operation, but we want our writer
|
||||
// handles to have a custom implementation of `shutdown` which
|
||||
// actually calls `TcpStream::shutdown` to ensure that EOF is
|
||||
// transmitted properly across the proxied connection.
|
||||
//
|
||||
// As a result, we wrap up our client/server manually in arcs and
|
||||
// use the impls below on our custom `MyTcpStream` type.
|
||||
let client_reader = MyTcpStream(Arc::new(Mutex::new(client)));
|
||||
let client_writer = client_reader.clone();
|
||||
let server_reader = MyTcpStream(Arc::new(Mutex::new(server)));
|
||||
let server_writer = server_reader.clone();
|
||||
|
||||
// Copy the data (in parallel) between the client and the server.
|
||||
// After the copy is done we indicate to the remote side that we've
|
||||
// finished by shutting down the connection.
|
||||
let client_to_server = copy(client_reader, server_writer)
|
||||
.and_then(|(n, _, server_writer)| {
|
||||
shutdown(server_writer).map(move |_| n)
|
||||
});
|
||||
|
||||
let server_to_client = copy(server_reader, client_writer)
|
||||
.and_then(|(n, _, client_writer)| {
|
||||
shutdown(client_writer).map(move |_| n)
|
||||
});
|
||||
|
||||
client_to_server.join(server_to_client)
|
||||
});
|
||||
|
||||
let msg = amounts.map(move |(from_client, from_server)| {
|
||||
println!("client wrote {} bytes and received {} bytes",
|
||||
from_client, from_server);
|
||||
}).map_err(|e| {
|
||||
// Don't panic. Maybe the client just disconnected too soon.
|
||||
println!("error: {}", e);
|
||||
});
|
||||
|
||||
tokio::spawn(msg);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
tokio::run(done);
|
||||
}
|
||||
|
||||
// This is a custom type used to have a custom implementation of the
|
||||
// `AsyncWrite::shutdown` method which actually calls `TcpStream::shutdown` to
|
||||
// notify the remote end that we're done writing.
|
||||
#[derive(Clone)]
|
||||
struct MyTcpStream(Arc<Mutex<TcpStream>>);
|
||||
|
||||
impl Read for MyTcpStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.lock().unwrap().read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for MyTcpStream {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.lock().unwrap().write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for MyTcpStream {}
|
||||
|
||||
impl AsyncWrite for MyTcpStream {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
try!(self.0.lock().unwrap().shutdown(Shutdown::Write));
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
//! A "tiny database" and accompanying protocol
|
||||
//!
|
||||
//! This example shows the usage of shared state amongst all connected clients,
|
||||
//! namely a database of key/value pairs. Each connected client can send a
|
||||
//! series of GET/SET commands to query the current value of a key or set the
|
||||
//! value of a key.
|
||||
//!
|
||||
//! This example has a simple protocol you can use to interact with the server.
|
||||
//! To run, first run this in one terminal window:
|
||||
//!
|
||||
//! cargo run --example tinydb
|
||||
//!
|
||||
//! and next in another windows run:
|
||||
//!
|
||||
//! cargo run --example connect 127.0.0.1:8080
|
||||
//!
|
||||
//! In the `connect` window you can type in commands where when you hit enter
|
||||
//! you'll get a response from the server for that command. An example session
|
||||
//! is:
|
||||
//!
|
||||
//!
|
||||
//! $ cargo run --example connect 127.0.0.1:8080
|
||||
//! GET foo
|
||||
//! foo = bar
|
||||
//! GET FOOBAR
|
||||
//! error: no key FOOBAR
|
||||
//! SET FOOBAR my awesome string
|
||||
//! set FOOBAR = `my awesome string`, previous: None
|
||||
//! SET foo tokio
|
||||
//! set foo = `tokio`, previous: Some("bar")
|
||||
//! GET foo
|
||||
//! foo = tokio
|
||||
//!
|
||||
//! Namely you can issue two forms of commands:
|
||||
//!
|
||||
//! * `GET $key` - this will fetch the value of `$key` from the database and
|
||||
//! return it. The server's database is initially populated with the key `foo`
|
||||
//! set to the value `bar`
|
||||
//! * `SET $key $value` - this will set the value of `$key` to `$value`,
|
||||
//! returning the previous value, if any.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::BufReader;
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use tokio::io::{lines, write_all};
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
|
||||
/// The in-memory database shared amongst all clients.
|
||||
///
|
||||
/// This database will be shared via `Arc`, so to mutate the internal map we're
|
||||
/// also going to use a `RefCell` for interior mutability.
|
||||
struct Database {
|
||||
map: Mutex<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
/// Possible requests our clients can send us
|
||||
enum Request {
|
||||
Get { key: String },
|
||||
Set { key: String, value: String },
|
||||
}
|
||||
|
||||
/// Responses to the `Request` commands above
|
||||
enum Response {
|
||||
Value { key: String, value: String },
|
||||
Set { key: String, value: String, previous: Option<String> },
|
||||
Error { msg: String },
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Parse the address we're going to run this server on
|
||||
// and set up our TCP listener to accept connections.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
let listener = TcpListener::bind(&addr).expect("failed to bind");
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
// Create the shared state of this server that will be shared amongst all
|
||||
// clients. We populate the initial database and then create the `Database`
|
||||
// structure. Note the usage of `Arc` here which will be used to ensure that
|
||||
// each independently spawned client will have a reference to the in-memory
|
||||
// database.
|
||||
let mut initial_db = HashMap::new();
|
||||
initial_db.insert("foo".to_string(), "bar".to_string());
|
||||
let db = Arc::new(Database {
|
||||
map: Mutex::new(initial_db),
|
||||
});
|
||||
|
||||
let done = listener.incoming()
|
||||
.map_err(|e| println!("error accepting socket; error = {:?}", e))
|
||||
.for_each(move |socket| {
|
||||
// As with many other small examples, the first thing we'll do is
|
||||
// *split* this TCP stream into two separately owned halves. This'll
|
||||
// allow us to work with the read and write halves independently.
|
||||
let (reader, writer) = socket.split();
|
||||
|
||||
// Since our protocol is line-based we use `tokio_io`'s `lines` utility
|
||||
// to convert our stream of bytes, `reader`, into a `Stream` of lines.
|
||||
let lines = lines(BufReader::new(reader));
|
||||
|
||||
// Here's where the meat of the processing in this server happens. First
|
||||
// we see a clone of the database being created, which is creating a
|
||||
// new reference for this connected client to use. Also note the `move`
|
||||
// keyword on the closure here which moves ownership of the reference
|
||||
// into the closure, which we'll need for spawning the client below.
|
||||
//
|
||||
// The `map` function here means that we'll run some code for all
|
||||
// requests (lines) we receive from the client. The actual handling here
|
||||
// is pretty simple, first we parse the request and if it's valid we
|
||||
// generate a response based on the values in the database.
|
||||
let db = db.clone();
|
||||
let responses = lines.map(move |line| {
|
||||
let request = match Request::parse(&line) {
|
||||
Ok(req) => req,
|
||||
Err(e) => return Response::Error { msg: e },
|
||||
};
|
||||
|
||||
let mut db = db.map.lock().unwrap();
|
||||
match request {
|
||||
Request::Get { key } => {
|
||||
match db.get(&key) {
|
||||
Some(value) => Response::Value { key, value: value.clone() },
|
||||
None => Response::Error { msg: format!("no key {}", key) },
|
||||
}
|
||||
}
|
||||
Request::Set { key, value } => {
|
||||
let previous = db.insert(key.clone(), value.clone());
|
||||
Response::Set { key, value, previous }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// At this point `responses` is a stream of `Response` types which we
|
||||
// now want to write back out to the client. To do that we use
|
||||
// `Stream::fold` to perform a loop here, serializing each response and
|
||||
// then writing it out to the client.
|
||||
let writes = responses.fold(writer, |writer, response| {
|
||||
let mut response = response.serialize();
|
||||
response.push('\n');
|
||||
write_all(writer, response.into_bytes()).map(|(w, _)| w)
|
||||
});
|
||||
|
||||
// Like with other small servers, we'll `spawn` this client to ensure it
|
||||
// runs concurrently with all other clients, for now ignoring any errors
|
||||
// that we see.
|
||||
let msg = writes.then(move |_| Ok(()));
|
||||
|
||||
tokio::spawn(msg)
|
||||
});
|
||||
|
||||
tokio::run(done);
|
||||
}
|
||||
|
||||
impl Request {
|
||||
fn parse(input: &str) -> Result<Request, String> {
|
||||
let mut parts = input.splitn(3, " ");
|
||||
match parts.next() {
|
||||
Some("GET") => {
|
||||
let key = match parts.next() {
|
||||
Some(key) => key,
|
||||
None => return Err(format!("GET must be followed by a key")),
|
||||
};
|
||||
if parts.next().is_some() {
|
||||
return Err(format!("GET's key must not be followed by anything"))
|
||||
}
|
||||
Ok(Request::Get { key: key.to_string() })
|
||||
}
|
||||
Some("SET") => {
|
||||
let key = match parts.next() {
|
||||
Some(key) => key,
|
||||
None => return Err(format!("SET must be followed by a key")),
|
||||
};
|
||||
let value = match parts.next() {
|
||||
Some(value) => value,
|
||||
None => return Err(format!("SET needs a value")),
|
||||
};
|
||||
Ok(Request::Set { key: key.to_string(), value: value.to_string() })
|
||||
}
|
||||
Some(cmd) => Err(format!("unknown command: {}", cmd)),
|
||||
None => Err(format!("empty input")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
fn serialize(&self) -> String {
|
||||
match *self {
|
||||
Response::Value { ref key, ref value } => {
|
||||
format!("{} = {}", key, value)
|
||||
}
|
||||
Response::Set { ref key, ref value, ref previous } => {
|
||||
format!("set {} = `{}`, previous: {:?}", key, value, previous)
|
||||
}
|
||||
Response::Error { ref msg } => {
|
||||
format!("error: {}", msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
//! A "tiny" example of HTTP request/response handling using just tokio-core
|
||||
//!
|
||||
//! This example is intended for *learning purposes* to see how various pieces
|
||||
//! hook up together and how HTTP can get up and running. Note that this example
|
||||
//! is written with the restriction that it *can't* use any "big" library other
|
||||
//! than tokio-core, if you'd like a "real world" HTTP library you likely want a
|
||||
//! crate like Hyper.
|
||||
//!
|
||||
//! Code here is based on the `echo-threads` example and implements two paths,
|
||||
//! the `/plaintext` and `/json` routes to respond with some text and json,
|
||||
//! respectively. By default this will run I/O on all the cores your system has
|
||||
//! available, and it doesn't support HTTP request bodies.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate bytes;
|
||||
extern crate http;
|
||||
extern crate httparse;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
extern crate time;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
|
||||
use std::{env, fmt, io};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use tokio::net::{TcpStream, TcpListener};
|
||||
use tokio::prelude::*;
|
||||
|
||||
use tokio_io::codec::{Encoder, Decoder};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use http::header::HeaderValue;
|
||||
use http::{Request, Response, StatusCode};
|
||||
|
||||
fn main() {
|
||||
// Parse the arguments, bind the TCP socket we'll be listening to, spin up
|
||||
// our worker threads, and start shipping sockets to those worker threads.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
|
||||
let listener = TcpListener::bind(&addr).expect("failed to bind");
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
tokio::run({
|
||||
listener.incoming()
|
||||
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
|
||||
.for_each(|socket| {
|
||||
process(socket);
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn process(socket: TcpStream) {
|
||||
let (tx, rx) = socket
|
||||
// Frame the socket using the `Http` protocol. This maps the TCP socket
|
||||
// to a Stream + Sink of HTTP frames.
|
||||
.framed(Http)
|
||||
// This splits a single `Stream + Sink` value into two separate handles
|
||||
// that can be used independently (even on different tasks or threads).
|
||||
.split();
|
||||
|
||||
// Map all requests into responses and send them back to the client.
|
||||
let task = tx.send_all(rx.and_then(respond))
|
||||
.then(|res| {
|
||||
if let Err(e) = res {
|
||||
println!("failed to process connection; error = {:?}", e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// Spawn the task that handles the connection.
|
||||
tokio::spawn(task);
|
||||
}
|
||||
|
||||
/// "Server logic" is implemented in this function.
|
||||
///
|
||||
/// This function is a map from and HTTP request to a future of a response and
|
||||
/// represents the various handling a server might do. Currently the contents
|
||||
/// here are pretty uninteresting.
|
||||
fn respond(req: Request<()>)
|
||||
-> Box<Future<Item = Response<String>, Error = io::Error> + Send>
|
||||
{
|
||||
let mut ret = Response::builder();
|
||||
let body = match req.uri().path() {
|
||||
"/plaintext" => {
|
||||
ret.header("Content-Type", "text/plain");
|
||||
"Hello, World!".to_string()
|
||||
}
|
||||
"/json" => {
|
||||
ret.header("Content-Type", "application/json");
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Message {
|
||||
message: &'static str,
|
||||
}
|
||||
serde_json::to_string(&Message { message: "Hello, World!" })
|
||||
.unwrap()
|
||||
}
|
||||
_ => {
|
||||
ret.status(StatusCode::NOT_FOUND);
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
Box::new(future::ok(ret.body(body).unwrap()))
|
||||
}
|
||||
|
||||
struct Http;
|
||||
|
||||
/// Implementation of encoding an HTTP response into a `BytesMut`, basically
|
||||
/// just writing out an HTTP/1.1 response.
|
||||
impl Encoder for Http {
|
||||
type Item = Response<String>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, item: Response<String>, dst: &mut BytesMut) -> io::Result<()> {
|
||||
use std::fmt::Write;
|
||||
|
||||
write!(BytesWrite(dst), "\
|
||||
HTTP/1.1 {}\r\n\
|
||||
Server: Example\r\n\
|
||||
Content-Length: {}\r\n\
|
||||
Date: {}\r\n\
|
||||
", item.status(), item.body().len(), date::now()).unwrap();
|
||||
|
||||
for (k, v) in item.headers() {
|
||||
dst.extend_from_slice(k.as_str().as_bytes());
|
||||
dst.extend_from_slice(b": ");
|
||||
dst.extend_from_slice(v.as_bytes());
|
||||
dst.extend_from_slice(b"\r\n");
|
||||
}
|
||||
|
||||
dst.extend_from_slice(b"\r\n");
|
||||
dst.extend_from_slice(item.body().as_bytes());
|
||||
|
||||
return Ok(());
|
||||
|
||||
// Right now `write!` on `Vec<u8>` goes through io::Write and is not
|
||||
// super speedy, so inline a less-crufty implementation here which
|
||||
// doesn't go through io::Error.
|
||||
struct BytesWrite<'a>(&'a mut BytesMut);
|
||||
|
||||
impl<'a> fmt::Write for BytesWrite<'a> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.0.extend_from_slice(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
|
||||
fmt::write(self, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of decoding an HTTP request from the bytes we've read so far.
|
||||
/// This leverages the `httparse` crate to do the actual parsing and then we use
|
||||
/// that information to construct an instance of a `http::Request` object,
|
||||
/// trying to avoid allocations where possible.
|
||||
impl Decoder for Http {
|
||||
type Item = Request<()>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> io::Result<Option<Request<()>>> {
|
||||
// TODO: we should grow this headers array if parsing fails and asks
|
||||
// for more headers
|
||||
let mut headers = [None; 16];
|
||||
let (method, path, version, amt) = {
|
||||
let mut parsed_headers = [httparse::EMPTY_HEADER; 16];
|
||||
let mut r = httparse::Request::new(&mut parsed_headers);
|
||||
let status = r.parse(src).map_err(|e| {
|
||||
let msg = format!("failed to parse http request: {:?}", e);
|
||||
io::Error::new(io::ErrorKind::Other, msg)
|
||||
})?;
|
||||
|
||||
let amt = match status {
|
||||
httparse::Status::Complete(amt) => amt,
|
||||
httparse::Status::Partial => return Ok(None),
|
||||
};
|
||||
|
||||
let toslice = |a: &[u8]| {
|
||||
let start = a.as_ptr() as usize - src.as_ptr() as usize;
|
||||
assert!(start < src.len());
|
||||
(start, start + a.len())
|
||||
};
|
||||
|
||||
for (i, header) in r.headers.iter().enumerate() {
|
||||
let k = toslice(header.name.as_bytes());
|
||||
let v = toslice(header.value);
|
||||
headers[i] = Some((k, v));
|
||||
}
|
||||
|
||||
(toslice(r.method.unwrap().as_bytes()),
|
||||
toslice(r.path.unwrap().as_bytes()),
|
||||
r.version.unwrap(),
|
||||
amt)
|
||||
};
|
||||
if version != 1 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "only HTTP/1.1 accepted"))
|
||||
}
|
||||
let data = src.split_to(amt).freeze();
|
||||
let mut ret = Request::builder();
|
||||
ret.method(&data[method.0..method.1]);
|
||||
ret.uri(data.slice(path.0, path.1));
|
||||
ret.version(http::Version::HTTP_11);
|
||||
for header in headers.iter() {
|
||||
let (k, v) = match *header {
|
||||
Some((ref k, ref v)) => (k, v),
|
||||
None => break,
|
||||
};
|
||||
let value = unsafe {
|
||||
HeaderValue::from_shared_unchecked(data.slice(v.0, v.1))
|
||||
};
|
||||
ret.header(&data[k.0..k.1], value);
|
||||
}
|
||||
|
||||
let req = ret.body(()).map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, e)
|
||||
})?;
|
||||
Ok(Some(req))
|
||||
}
|
||||
}
|
||||
|
||||
mod date {
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::{self, Write};
|
||||
use std::str;
|
||||
|
||||
use time::{self, Duration};
|
||||
|
||||
pub struct Now(());
|
||||
|
||||
/// Returns a struct, which when formatted, renders an appropriate `Date`
|
||||
/// header value.
|
||||
pub fn now() -> Now {
|
||||
Now(())
|
||||
}
|
||||
|
||||
// Gee Alex, doesn't this seem like premature optimization. Well you see
|
||||
// there Billy, you're absolutely correct! If your server is *bottlenecked*
|
||||
// on rendering the `Date` header, well then boy do I have news for you, you
|
||||
// don't need this optimization.
|
||||
//
|
||||
// In all seriousness, though, a simple "hello world" benchmark which just
|
||||
// sends back literally "hello world" with standard headers actually is
|
||||
// bottlenecked on rendering a date into a byte buffer. Since it was at the
|
||||
// top of a profile, and this was done for some competitive benchmarks, this
|
||||
// module was written.
|
||||
//
|
||||
// Just to be clear, though, I was not intending on doing this because it
|
||||
// really does seem kinda absurd, but it was done by someone else [1], so I
|
||||
// blame them! :)
|
||||
//
|
||||
// [1]: https://github.com/rapidoid/rapidoid/blob/f1c55c0555007e986b5d069fe1086e6d09933f7b/rapidoid-commons/src/main/java/org/rapidoid/commons/Dates.java#L48-L66
|
||||
|
||||
struct LastRenderedNow {
|
||||
bytes: [u8; 128],
|
||||
amt: usize,
|
||||
next_update: time::Timespec,
|
||||
}
|
||||
|
||||
thread_local!(static LAST: RefCell<LastRenderedNow> = RefCell::new(LastRenderedNow {
|
||||
bytes: [0; 128],
|
||||
amt: 0,
|
||||
next_update: time::Timespec::new(0, 0),
|
||||
}));
|
||||
|
||||
impl fmt::Display for Now {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
LAST.with(|cache| {
|
||||
let mut cache = cache.borrow_mut();
|
||||
let now = time::get_time();
|
||||
if now >= cache.next_update {
|
||||
cache.update(now);
|
||||
}
|
||||
f.write_str(cache.buffer())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl LastRenderedNow {
|
||||
fn buffer(&self) -> &str {
|
||||
str::from_utf8(&self.bytes[..self.amt]).unwrap()
|
||||
}
|
||||
|
||||
fn update(&mut self, now: time::Timespec) {
|
||||
self.amt = 0;
|
||||
write!(LocalBuffer(self), "{}", time::at(now).rfc822()).unwrap();
|
||||
self.next_update = now + Duration::seconds(1);
|
||||
self.next_update.nsec = 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalBuffer<'a>(&'a mut LastRenderedNow);
|
||||
|
||||
impl<'a> fmt::Write for LocalBuffer<'a> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
let start = self.0.amt;
|
||||
let end = start + s.len();
|
||||
self.0.bytes[start..end].copy_from_slice(s.as_bytes());
|
||||
self.0.amt += s.len();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//! A UDP client that just sends everything it gets via `stdio` in a single datagram, and then
|
||||
//! waits for a reply.
|
||||
//!
|
||||
//! For the reasons of simplicity data from `stdio` is read until `EOF` in a blocking manner.
|
||||
//!
|
||||
//! You can test this out by running an echo server:
|
||||
//!
|
||||
//! ```
|
||||
//! $ cargo run --example echo-udp -- 127.0.0.1:8080
|
||||
//! ```
|
||||
//!
|
||||
//! and running the client in another terminal:
|
||||
//!
|
||||
//! ```
|
||||
//! $ cargo run --example udp-client
|
||||
//! ```
|
||||
//!
|
||||
//! You can optionally provide any custom endpoint address for the client:
|
||||
//!
|
||||
//! ```
|
||||
//! $ cargo run --example udp-client -- 127.0.0.1:8080
|
||||
//! ```
|
||||
//!
|
||||
//! Don't forget to pass `EOF` to the standard input of the client!
|
||||
//!
|
||||
//! Please mind that since the UDP protocol doesn't have any capabilities to detect a broken
|
||||
//! connection the server needs to be run first, otherwise the client will block forever.
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
use std::env;
|
||||
use std::io::stdin;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::prelude::*;
|
||||
|
||||
fn get_stdin_data() -> Vec<u8> {
|
||||
let mut buf = Vec::new();
|
||||
stdin().read_to_end(&mut buf).unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let remote_addr: SocketAddr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("127.0.0.1:8080".into())
|
||||
.parse()
|
||||
.unwrap();
|
||||
// We use port 0 to let the operating system allocate an available port for us.
|
||||
let local_addr: SocketAddr = if remote_addr.is_ipv4() {
|
||||
"0.0.0.0:0"
|
||||
} else {
|
||||
"[::]:0"
|
||||
}.parse()
|
||||
.unwrap();
|
||||
let socket = UdpSocket::bind(&local_addr).unwrap();
|
||||
const MAX_DATAGRAM_SIZE: usize = 65_507;
|
||||
let processing = socket
|
||||
.send_dgram(get_stdin_data(), &remote_addr)
|
||||
.and_then(|(socket, _)| socket.recv_dgram(vec![0u8; MAX_DATAGRAM_SIZE]))
|
||||
.map(|(_, data, len, _)| {
|
||||
println!(
|
||||
"Received {} bytes:\n{}",
|
||||
len,
|
||||
String::from_utf8_lossy(&data[..len])
|
||||
)
|
||||
})
|
||||
.wait();
|
||||
match processing {
|
||||
Ok(_) => {}
|
||||
Err(e) => eprintln!("Encountered an error: {}", e),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//! This example leverages `BytesCodec` to create a UDP client and server which
|
||||
//! speak a custom protocol.
|
||||
//!
|
||||
//! Here we're using the codec from tokio-io to convert a UDP socket to a stream of
|
||||
//! client messages. These messages are then processed and returned back as a
|
||||
//! new message with a new destination. Overall, we then use this to construct a
|
||||
//! "ping pong" pair where two sockets are sending messages back and forth.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
extern crate env_logger;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use tokio::net::{UdpSocket, UdpFramed};
|
||||
use tokio_io::codec::BytesCodec;
|
||||
|
||||
fn main() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
|
||||
|
||||
// Bind both our sockets and then figure out what ports we got.
|
||||
let a = UdpSocket::bind(&addr).unwrap();
|
||||
let b = UdpSocket::bind(&addr).unwrap();
|
||||
let b_addr = b.local_addr().unwrap();
|
||||
|
||||
// We're parsing each socket with the `BytesCodec` included in `tokio_io`, and then we
|
||||
// `split` each codec into the sink/stream halves.
|
||||
let (a_sink, a_stream) = UdpFramed::new(a, BytesCodec::new()).split();
|
||||
let (b_sink, b_stream) = UdpFramed::new(b, BytesCodec::new()).split();
|
||||
|
||||
// Start off by sending a ping from a to b, afterwards we just print out
|
||||
// what they send us and continually send pings
|
||||
// let pings = stream::iter((0..5).map(Ok));
|
||||
let a = a_sink.send(("PING".into(), b_addr)).and_then(|a_sink| {
|
||||
let mut i = 0;
|
||||
let a_stream = a_stream.take(4).map(move |(msg, addr)| {
|
||||
i += 1;
|
||||
println!("[a] recv: {}", String::from_utf8_lossy(&msg));
|
||||
(format!("PING {}", i).into(), addr)
|
||||
});
|
||||
a_sink.send_all(a_stream)
|
||||
});
|
||||
|
||||
// The second client we have will receive the pings from `a` and then send
|
||||
// back pongs.
|
||||
let b_stream = b_stream.map(|(msg, addr)| {
|
||||
println!("[b] recv: {}", String::from_utf8_lossy(&msg));
|
||||
("PONG".into(), addr)
|
||||
});
|
||||
let b = b_sink.send_all(b_stream);
|
||||
|
||||
// Spawn the sender of pongs and then wait for our pinger to finish.
|
||||
tokio::run({
|
||||
b.join(a)
|
||||
.map(|_| ())
|
||||
.map_err(|e| println!("error = {:?}", e))
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "futures2"
|
||||
|
||||
version = "0.1.0"
|
||||
authors = ["Aaron Turon <[email protected]>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
description = """
|
||||
Enables depending on futures 0.2 and futures 0.1 in the same crate.
|
||||
"""
|
||||
|
||||
[dependencies]
|
||||
futures = "=0.2.0-beta"
|
||||
@@ -0,0 +1,2 @@
|
||||
extern crate futures;
|
||||
pub use futures::*;
|
||||
@@ -0,0 +1,730 @@
|
||||
//! Execute many tasks concurrently on the current thread.
|
||||
//!
|
||||
//! [`CurrentThread`] is an executor that keeps tasks on the same thread that
|
||||
//! they were spawned from. This allows it to execute futures that are not
|
||||
//! `Send`.
|
||||
//!
|
||||
//! A single [`CurrentThread`] instance is able to efficiently manage a large
|
||||
//! number of tasks and will attempt to schedule all tasks fairly.
|
||||
//!
|
||||
//! All tasks that are being managed by a [`CurrentThread`] executor are able to
|
||||
//! spawn additional tasks by calling [`spawn`]. This function only works from
|
||||
//! within the context of a running [`CurrentThread`] instance.
|
||||
//!
|
||||
//! The easiest way to start a new [`CurrentThread`] executor is to call
|
||||
//! [`block_on_all`] with an initial task to seed the executor.
|
||||
//!
|
||||
//! For example:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! # extern crate futures;
|
||||
//! # use tokio::executor::current_thread;
|
||||
//! use futures::future::lazy;
|
||||
//!
|
||||
//! // Calling execute here results in a panic
|
||||
//! // current_thread::spawn(my_future);
|
||||
//!
|
||||
//! # pub fn main() {
|
||||
//! current_thread::block_on_all(lazy(|| {
|
||||
//! // The execution context is setup, futures may be executed.
|
||||
//! current_thread::spawn(lazy(|| {
|
||||
//! println!("called from the current thread executor");
|
||||
//! Ok(())
|
||||
//! }));
|
||||
//!
|
||||
//! Ok::<_, ()>(())
|
||||
//! }));
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The `block_on_all` function will block the current thread until **all**
|
||||
//! tasks that have been spawned onto the [`CurrentThread`] instance have
|
||||
//! completed.
|
||||
//!
|
||||
//! More fine-grain control can be achieved by using [`CurrentThread`] directly.
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! # extern crate futures;
|
||||
//! # use tokio::executor::current_thread::CurrentThread;
|
||||
//! use futures::future::{lazy, empty};
|
||||
//! use std::time::Duration;
|
||||
//!
|
||||
//! // Calling execute here results in a panic
|
||||
//! // current_thread::spawn(my_future);
|
||||
//!
|
||||
//! # pub fn main() {
|
||||
//! let mut current_thread = CurrentThread::new();
|
||||
//!
|
||||
//! // Spawn a task, the task is not executed yet.
|
||||
//! current_thread.spawn(lazy(|| {
|
||||
//! println!("Spawning a task");
|
||||
//! Ok(())
|
||||
//! }));
|
||||
//!
|
||||
//! // Spawn a task that never completes
|
||||
//! current_thread.spawn(empty());
|
||||
//!
|
||||
//! // Run the executor, but only until the provided future completes. This
|
||||
//! // provides the opportunity to start executing previously spawned tasks.
|
||||
//! let res = current_thread.block_on(lazy(|| {
|
||||
//! Ok::<_, ()>("Hello")
|
||||
//! })).unwrap();
|
||||
//!
|
||||
//! // Now, run the executor for *at most* 1 second. Since a task was spawned
|
||||
//! // that never completes, this function will return with an error.
|
||||
//! current_thread.run_timeout(Duration::from_secs(1)).unwrap_err();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Execution model
|
||||
//!
|
||||
//! Internally, [`CurrentThread`] maintains a queue. When one of its tasks is
|
||||
//! notified, the task gets added to the queue. The executor will pop tasks from
|
||||
//! the queue and call [`Future::poll`]. If the task gets notified while it is
|
||||
//! being executed, it won't get re-executed until all other tasks currently in
|
||||
//! the queue get polled.
|
||||
//!
|
||||
//! Before the task is polled, a thread-local variable referencing the current
|
||||
//! [`CurrentThread`] instance is set. This enables [`spawn`] to spawn new tasks
|
||||
//! onto the same executor without having to thread through a handle value.
|
||||
//!
|
||||
//! If the [`CurrentThread`] instance still has uncompleted tasks, but none of
|
||||
//! these tasks are ready to be polled, the current thread is put to sleep. When
|
||||
//! a task is notified, the thread is woken up and processing resumes.
|
||||
//!
|
||||
//! All tasks managed by [`CurrentThread`] remain on the current thread. When a
|
||||
//! task completes, it is dropped.
|
||||
//!
|
||||
//! [`spawn`]: fn.spawn.html
|
||||
//! [`block_on_all`]: fn.block_on_all.html
|
||||
//! [`CurrentThread`]: struct.CurrentThread.html
|
||||
//! [`Future::poll`]: https://docs.rs/futures/0.1/futures/future/trait.Future.html#tymethod.poll
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
mod scheduler;
|
||||
use self::scheduler::Scheduler;
|
||||
|
||||
use tokio_executor::{self, Enter, SpawnError};
|
||||
use tokio_executor::park::{Park, Unpark, ParkThread};
|
||||
|
||||
use futures::{executor, Async, Future};
|
||||
use futures::future::{self, Executor, ExecuteError, ExecuteErrorKind};
|
||||
|
||||
use std::fmt;
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
/// Executes tasks on the current thread
|
||||
pub struct CurrentThread<P: Park = ParkThread> {
|
||||
/// Execute futures and receive unpark notifications.
|
||||
scheduler: Scheduler<P::Unpark>,
|
||||
|
||||
/// Current number of futures being executed
|
||||
num_futures: usize,
|
||||
|
||||
/// Thread park handle
|
||||
park: P,
|
||||
}
|
||||
|
||||
/// Executes futures on the current thread.
|
||||
///
|
||||
/// All futures executed using this executor will be executed on the current
|
||||
/// thread. As such, `run` will wait for these futures to complete before
|
||||
/// returning.
|
||||
///
|
||||
/// For more details, see the [module level](index.html) documentation.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TaskExecutor {
|
||||
// Prevent the handle from moving across threads.
|
||||
_p: ::std::marker::PhantomData<Rc<()>>,
|
||||
}
|
||||
|
||||
/// Returned by the `turn` function
|
||||
#[derive(Debug)]
|
||||
pub struct Turn(());
|
||||
|
||||
/// A `CurrentThread` instance bound to a supplied execution conext.
|
||||
pub struct Entered<'a, P: Park + 'a> {
|
||||
executor: &'a mut CurrentThread<P>,
|
||||
enter: &'a mut Enter,
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.2", note = "use block_on_all instead")]
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug)]
|
||||
pub struct Context<'a> {
|
||||
cancel: Cell<bool>,
|
||||
_p: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
/// Error returned by the `run` function.
|
||||
#[derive(Debug)]
|
||||
pub struct RunError {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
/// Error returned by the `run_timeout` function.
|
||||
#[derive(Debug)]
|
||||
pub struct RunTimeoutError {
|
||||
timeout: bool,
|
||||
}
|
||||
|
||||
/// Error returned by the `turn` function.
|
||||
#[derive(Debug)]
|
||||
pub struct TurnError {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
/// Error returned by the `block_on` function.
|
||||
#[derive(Debug)]
|
||||
pub struct BlockError<T> {
|
||||
inner: Option<T>,
|
||||
}
|
||||
|
||||
/// This is mostly split out to make the borrow checker happy.
|
||||
struct Borrow<'a, U: 'a> {
|
||||
scheduler: &'a mut Scheduler<U>,
|
||||
num_futures: &'a mut usize,
|
||||
}
|
||||
|
||||
trait SpawnLocal {
|
||||
fn spawn_local(&mut self, future: Box<Future<Item = (), Error = ()>>);
|
||||
}
|
||||
|
||||
struct CurrentRunner {
|
||||
spawn: Cell<Option<*mut SpawnLocal>>,
|
||||
}
|
||||
|
||||
/// Current thread's task runner. This is set in `TaskRunner::with`
|
||||
thread_local!(static CURRENT: CurrentRunner = CurrentRunner {
|
||||
spawn: Cell::new(None),
|
||||
});
|
||||
|
||||
#[deprecated(since = "0.1.2", note = "use block_on_all instead")]
|
||||
#[doc(hidden)]
|
||||
#[allow(deprecated)]
|
||||
pub fn run<F, R>(f: F) -> R
|
||||
where F: FnOnce(&mut Context) -> R
|
||||
{
|
||||
let mut context = Context {
|
||||
cancel: Cell::new(false),
|
||||
_p: PhantomData,
|
||||
};
|
||||
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
let ret = current_thread
|
||||
.block_on(future::lazy(|| Ok::<_, ()>(f(&mut context))))
|
||||
.unwrap();
|
||||
|
||||
if context.cancel.get() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
current_thread.run().unwrap();
|
||||
ret
|
||||
}
|
||||
|
||||
/// Run the executor bootstrapping the execution with the provided future.
|
||||
///
|
||||
/// This creates a new [`CurrentThread`] executor, spawns the provided future,
|
||||
/// and blocks the current thread until the provided future and **all**
|
||||
/// subsequently spawned futures complete. In other words:
|
||||
///
|
||||
/// * If the provided boostrap future does **not** spawn any additional tasks,
|
||||
/// `block_on_all` returns once `future` completes.
|
||||
/// * If the provided bootstrap future **does** spawn additional tasks, then
|
||||
/// `block_on_all` returns once **all** spawned futures complete.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [`CurrentThread`]: struct.CurrentThread.html
|
||||
/// [mod]: index.html
|
||||
pub fn block_on_all<F>(future: F) -> Result<F::Item, F::Error>
|
||||
where F: Future,
|
||||
{
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
let ret = current_thread.block_on(future);
|
||||
current_thread.run().unwrap();
|
||||
|
||||
ret.map_err(|e| e.into_inner().expect("unexpected execution error"))
|
||||
}
|
||||
|
||||
/// Executes a future on the current thread.
|
||||
///
|
||||
/// The provided future must complete or be canceled before `run` will return.
|
||||
///
|
||||
/// Unlike [`tokio::spawn`], this function will always spawn on a
|
||||
/// `CurrentThread` executor and is able to spawn futures that are not `Send`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function can only be invoked from the context of a `run` call; any
|
||||
/// other use will result in a panic.
|
||||
///
|
||||
/// [`tokio::spawn`]: ../fn.spawn.html
|
||||
pub fn spawn<F>(future: F)
|
||||
where F: Future<Item = (), Error = ()> + 'static
|
||||
{
|
||||
TaskExecutor::current()
|
||||
.spawn_local(Box::new(future))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// ===== impl CurrentThread =====
|
||||
|
||||
impl CurrentThread<ParkThread> {
|
||||
/// Create a new instance of `CurrentThread`.
|
||||
pub fn new() -> Self {
|
||||
CurrentThread::new_with_park(ParkThread::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Park> CurrentThread<P> {
|
||||
/// Create a new instance of `CurrentThread` backed by the given park
|
||||
/// handle.
|
||||
pub fn new_with_park(park: P) -> Self {
|
||||
let unpark = park.unpark();
|
||||
|
||||
CurrentThread {
|
||||
scheduler: Scheduler::new(unpark),
|
||||
num_futures: 0,
|
||||
park,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the executor is currently idle.
|
||||
///
|
||||
/// An idle executor is defined by not currently having any spawned tasks.
|
||||
pub fn is_idle(&self) -> bool {
|
||||
self.num_futures == 0
|
||||
}
|
||||
|
||||
/// Spawn the future on the executor.
|
||||
///
|
||||
/// This internally queues the future to be executed once `run` is called.
|
||||
pub fn spawn<F>(&mut self, future: F) -> &mut Self
|
||||
where F: Future<Item = (), Error = ()> + 'static,
|
||||
{
|
||||
self.borrow().spawn_local(Box::new(future));
|
||||
self
|
||||
}
|
||||
|
||||
/// Synchronously waits for the provided `future` to complete.
|
||||
///
|
||||
/// This function can be used to synchronously block the current thread
|
||||
/// until the provided `future` has resolved either successfully or with an
|
||||
/// error. The result of the future is then returned from this function
|
||||
/// call.
|
||||
///
|
||||
/// Note that this function will **also** execute any spawned futures on the
|
||||
/// current thread, but will **not** block until these other spawned futures
|
||||
/// have completed.
|
||||
///
|
||||
/// The caller is responsible for ensuring that other spawned futures
|
||||
/// complete execution.
|
||||
pub fn block_on<F>(&mut self, future: F)
|
||||
-> Result<F::Item, BlockError<F::Error>>
|
||||
where F: Future
|
||||
{
|
||||
let mut enter = tokio_executor::enter().unwrap();
|
||||
self.enter(&mut enter).block_on(future)
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until **all**
|
||||
/// spawned futures have completed.
|
||||
pub fn run(&mut self) -> Result<(), RunError> {
|
||||
let mut enter = tokio_executor::enter().unwrap();
|
||||
self.enter(&mut enter).run()
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until all
|
||||
/// spawned futures have completed **or** `duration` time has elapsed.
|
||||
pub fn run_timeout(&mut self, duration: Duration)
|
||||
-> Result<(), RunTimeoutError>
|
||||
{
|
||||
let mut enter = tokio_executor::enter().unwrap();
|
||||
self.enter(&mut enter).run_timeout(duration)
|
||||
}
|
||||
|
||||
/// Perform a single iteration of the event loop.
|
||||
///
|
||||
/// This function blocks the current thread even if the executor is idle.
|
||||
pub fn turn(&mut self, duration: Option<Duration>)
|
||||
-> Result<Turn, TurnError>
|
||||
{
|
||||
let mut enter = tokio_executor::enter().unwrap();
|
||||
self.enter(&mut enter).turn(duration)
|
||||
}
|
||||
|
||||
/// Bind `CurrentThread` instance with an execution context.
|
||||
pub fn enter<'a>(&'a mut self, enter: &'a mut Enter) -> Entered<'a, P> {
|
||||
Entered {
|
||||
executor: self,
|
||||
enter,
|
||||
}
|
||||
}
|
||||
|
||||
fn borrow(&mut self) -> Borrow<P::Unpark> {
|
||||
Borrow {
|
||||
scheduler: &mut self.scheduler,
|
||||
num_futures: &mut self.num_futures,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl tokio_executor::Executor for CurrentThread {
|
||||
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
|
||||
-> Result<(), SpawnError>
|
||||
{
|
||||
self.borrow().spawn_local(future);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
fn spawn2(&mut self, _future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
|
||||
-> Result<(), futures2::executor::SpawnError>
|
||||
{
|
||||
panic!("Futures 0.2 integration is not available for current_thread");
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Park> fmt::Debug for CurrentThread<P> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("CurrentThread")
|
||||
.field("scheduler", &self.scheduler)
|
||||
.field("num_futures", &self.num_futures)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Entered =====
|
||||
|
||||
impl<'a, P: Park> Entered<'a, P> {
|
||||
/// Spawn the future on the executor.
|
||||
///
|
||||
/// This internally queues the future to be executed once `run` is called.
|
||||
pub fn spawn<F>(&mut self, future: F) -> &mut Self
|
||||
where F: Future<Item = (), Error = ()> + 'static,
|
||||
{
|
||||
self.executor.borrow().spawn_local(Box::new(future));
|
||||
self
|
||||
}
|
||||
|
||||
/// Synchronously waits for the provided `future` to complete.
|
||||
///
|
||||
/// This function can be used to synchronously block the current thread
|
||||
/// until the provided `future` has resolved either successfully or with an
|
||||
/// error. The result of the future is then returned from this function
|
||||
/// call.
|
||||
///
|
||||
/// Note that this function will **also** execute any spawned futures on the
|
||||
/// current thread, but will **not** block until these other spawned futures
|
||||
/// have completed.
|
||||
///
|
||||
/// The caller is responsible for ensuring that other spawned futures
|
||||
/// complete execution.
|
||||
pub fn block_on<F>(&mut self, future: F)
|
||||
-> Result<F::Item, BlockError<F::Error>>
|
||||
where F: Future
|
||||
{
|
||||
let mut future = executor::spawn(future);
|
||||
let notify = self.executor.scheduler.notify();
|
||||
|
||||
loop {
|
||||
let res = self.executor.borrow().enter(self.enter, || {
|
||||
future.poll_future_notify(¬ify, 0)
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(Async::Ready(e)) => return Ok(e),
|
||||
Err(e) => return Err(BlockError { inner: Some(e) }),
|
||||
Ok(Async::NotReady) => {}
|
||||
}
|
||||
|
||||
self.tick();
|
||||
|
||||
if let Err(_) = self.executor.park.park() {
|
||||
return Err(BlockError { inner: None });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until **all**
|
||||
/// spawned futures have completed.
|
||||
pub fn run(&mut self) -> Result<(), RunError> {
|
||||
self.run_timeout2(None)
|
||||
.map_err(|_| RunError { _p: () })
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until all
|
||||
/// spawned futures have completed **or** `duration` time has elapsed.
|
||||
pub fn run_timeout(&mut self, duration: Duration)
|
||||
-> Result<(), RunTimeoutError>
|
||||
{
|
||||
self.run_timeout2(Some(duration))
|
||||
}
|
||||
|
||||
/// Perform a single iteration of the event loop.
|
||||
///
|
||||
/// This function blocks the current thread even if the executor is idle.
|
||||
pub fn turn(&mut self, duration: Option<Duration>)
|
||||
-> Result<Turn, TurnError>
|
||||
{
|
||||
if !self.tick() {
|
||||
let res = match duration {
|
||||
Some(duration) => self.executor.park.park_timeout(duration),
|
||||
None => self.executor.park.park(),
|
||||
};
|
||||
|
||||
if res.is_err() {
|
||||
return Err(TurnError { _p: () });
|
||||
}
|
||||
|
||||
self.tick();
|
||||
}
|
||||
|
||||
Ok(Turn(()))
|
||||
}
|
||||
|
||||
fn run_timeout2(&mut self, dur: Option<Duration>)
|
||||
-> Result<(), RunTimeoutError>
|
||||
{
|
||||
if self.executor.is_idle() {
|
||||
// Nothing to do
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut time = dur.map(|dur| (Instant::now() + dur, dur));
|
||||
|
||||
loop {
|
||||
self.tick();
|
||||
|
||||
if self.executor.is_idle() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match time {
|
||||
Some((until, rem)) => {
|
||||
if let Err(_) = self.executor.park.park_timeout(rem) {
|
||||
return Err(RunTimeoutError::new(false));
|
||||
}
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
if now >= until {
|
||||
return Err(RunTimeoutError::new(true));
|
||||
}
|
||||
|
||||
time = Some((until, until - now));
|
||||
}
|
||||
None => {
|
||||
if let Err(_) = self.executor.park.park() {
|
||||
return Err(RunTimeoutError::new(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if any futures were processed
|
||||
fn tick(&mut self) -> bool {
|
||||
self.executor.scheduler.tick(
|
||||
&mut *self.enter,
|
||||
&mut self.executor.num_futures)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P: Park> fmt::Debug for Entered<'a, P> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Entered")
|
||||
.field("executor", &self.executor)
|
||||
.field("enter", &self.enter)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl TaskExecutor =====
|
||||
|
||||
#[deprecated(since = "0.1.2", note = "use TaskExecutor::current instead")]
|
||||
#[doc(hidden)]
|
||||
pub fn task_executor() -> TaskExecutor {
|
||||
TaskExecutor {
|
||||
_p: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskExecutor {
|
||||
/// Returns an executor that executes futures on the current thread.
|
||||
///
|
||||
/// The user of `TaskExecutor` must ensure that when a future is submitted,
|
||||
/// that it is done within the context of a call to `run`.
|
||||
///
|
||||
/// For more details, see the [module level](index.html) documentation.
|
||||
pub fn current() -> TaskExecutor {
|
||||
TaskExecutor {
|
||||
_p: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a future onto the current `CurrentThread` instance.
|
||||
pub fn spawn_local(&mut self, future: Box<Future<Item = (), Error = ()>>)
|
||||
-> Result<(), SpawnError>
|
||||
{
|
||||
CURRENT.with(|current| {
|
||||
match current.spawn.get() {
|
||||
Some(spawn) => {
|
||||
unsafe { (*spawn).spawn_local(future) };
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
Err(SpawnError::shutdown())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl tokio_executor::Executor for TaskExecutor {
|
||||
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
|
||||
-> Result<(), SpawnError>
|
||||
{
|
||||
self.spawn_local(future)
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
fn spawn2(&mut self, _future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
|
||||
-> Result<(), futures2::executor::SpawnError>
|
||||
{
|
||||
panic!("Futures 0.2 integration is not available for current_thread");
|
||||
}
|
||||
|
||||
fn status(&self) -> Result<(), SpawnError> {
|
||||
CURRENT.with(|current| {
|
||||
if current.spawn.get().is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SpawnError::shutdown())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Executor<F> for TaskExecutor
|
||||
where F: Future<Item = (), Error = ()> + 'static
|
||||
{
|
||||
fn execute(&self, future: F) -> Result<(), ExecuteError<F>> {
|
||||
CURRENT.with(|current| {
|
||||
match current.spawn.get() {
|
||||
Some(spawn) => {
|
||||
unsafe { (*spawn).spawn_local(Box::new(future)) };
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
Err(ExecuteError::new(ExecuteErrorKind::Shutdown, future))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Context =====
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
/// Cancels *all* executing futures.
|
||||
pub fn cancel_all_spawned(&self) {
|
||||
self.cancel.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Borrow =====
|
||||
|
||||
impl<'a, U: Unpark> Borrow<'a, U> {
|
||||
fn enter<F, R>(&mut self, _: &mut Enter, f: F) -> R
|
||||
where F: FnOnce() -> R,
|
||||
{
|
||||
CURRENT.with(|current| {
|
||||
current.set_spawn(self, || {
|
||||
f()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U: Unpark> SpawnLocal for Borrow<'a, U> {
|
||||
fn spawn_local(&mut self, future: Box<Future<Item = (), Error = ()>>) {
|
||||
*self.num_futures += 1;
|
||||
self.scheduler.schedule(future);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl CurrentRunner =====
|
||||
|
||||
impl CurrentRunner {
|
||||
fn set_spawn<F, R>(&self, spawn: &mut SpawnLocal, f: F) -> R
|
||||
where F: FnOnce() -> R
|
||||
{
|
||||
struct Reset<'a>(&'a CurrentRunner);
|
||||
|
||||
impl<'a> Drop for Reset<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.0.spawn.set(None);
|
||||
}
|
||||
}
|
||||
|
||||
let _reset = Reset(self);
|
||||
|
||||
let spawn = unsafe { hide_lt(spawn as *mut SpawnLocal) };
|
||||
self.spawn.set(Some(spawn));
|
||||
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn hide_lt<'a>(p: *mut (SpawnLocal + 'a)) -> *mut (SpawnLocal + 'static) {
|
||||
use std::mem;
|
||||
mem::transmute(p)
|
||||
}
|
||||
|
||||
// ===== impl RunTimeoutError =====
|
||||
|
||||
impl RunTimeoutError {
|
||||
fn new(timeout: bool) -> Self {
|
||||
RunTimeoutError { timeout }
|
||||
}
|
||||
|
||||
/// Returns `true` if the error was caused by the operation timeing out.
|
||||
pub fn is_timeout(&self) -> bool {
|
||||
self.timeout
|
||||
}
|
||||
}
|
||||
|
||||
impl From<tokio_executor::EnterError> for RunTimeoutError {
|
||||
fn from(_: tokio_executor::EnterError) -> Self {
|
||||
RunTimeoutError::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl BlockError =====
|
||||
|
||||
impl<T> BlockError<T> {
|
||||
/// Returns the error yielded by the future being blocked on
|
||||
pub fn into_inner(self) -> Option<T> {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<tokio_executor::EnterError> for BlockError<T> {
|
||||
fn from(_: tokio_executor::EnterError) -> Self {
|
||||
BlockError { inner: None }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,747 @@
|
||||
use super::Borrow;
|
||||
use tokio_executor::Enter;
|
||||
use tokio_executor::park::Unpark;
|
||||
|
||||
use futures::{Future, Async};
|
||||
use futures::executor::{self, Spawn, UnsafeNotify, NotifyHandle};
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::Ordering::{Relaxed, SeqCst, Acquire, Release, AcqRel};
|
||||
use std::sync::atomic::{AtomicPtr, AtomicBool, AtomicUsize};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::usize;
|
||||
use std::thread;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A generic task-aware scheduler.
|
||||
///
|
||||
/// This is used both by `FuturesUnordered` and the current-thread executor.
|
||||
pub struct Scheduler<U> {
|
||||
inner: Arc<Inner<U>>,
|
||||
nodes: List<U>,
|
||||
}
|
||||
|
||||
pub struct Notify<'a, U: 'a>(&'a Arc<Node<U>>);
|
||||
|
||||
// A linked-list of nodes
|
||||
struct List<U> {
|
||||
len: usize,
|
||||
head: *const Node<U>,
|
||||
tail: *const Node<U>,
|
||||
}
|
||||
|
||||
// Scheduler is implemented using two linked lists. The first linked list tracks
|
||||
// all items managed by a `Scheduler`. This list is stored on the `Scheduler`
|
||||
// struct and is **not** thread safe. The second linked list is an
|
||||
// implementation of the intrusive MPSC queue algorithm described by
|
||||
// 1024cores.net and is stored on `Inner`. This linked list can push items to
|
||||
// the back concurrently but only one consumer may pop from the front. To
|
||||
// enforce this requirement, all popping will be performed via fns on
|
||||
// `Scheduler` that take `&mut self`.
|
||||
//
|
||||
// When a item is submitted to the set a node is allocated and inserted in
|
||||
// both linked lists. This means that all insertion operations **must** be
|
||||
// originated from `Scheduler` with `&mut self` The next call to `tick` will
|
||||
// (eventually) see this node and call `poll` on the item.
|
||||
//
|
||||
// Nodes are wrapped in `Arc` cells which manage the lifetime of the node.
|
||||
// However, `Arc` handles are sometimes cast to `*const Node` pointers.
|
||||
// Specifically, when a node is stored in at least one of the two lists
|
||||
// described above, this represents a logical `Arc` handle. This is how
|
||||
// `Scheduler` maintains its reference to all nodes it manages. Each
|
||||
// `NotifyHande` instance is an `Arc<Node>` as well.
|
||||
//
|
||||
// When `Scheduler` drops, it clears the linked list of all nodes that it
|
||||
// manages. When doing so, it must attempt to decrement the reference count (by
|
||||
// dropping an Arc handle). However, it can **only** decrement the reference
|
||||
// count if the node is not currently stored in the mpsc channel. If the node
|
||||
// **is** "queued" in the mpsc channel, then the arc reference count cannot be
|
||||
// decremented. Once the node is popped from the mpsc channel, then the final
|
||||
// arc reference count can be decremented, thus freeing the node.
|
||||
|
||||
struct Inner<U> {
|
||||
// Thread unpark handle
|
||||
unpark: U,
|
||||
|
||||
// Tick number
|
||||
tick_num: AtomicUsize,
|
||||
|
||||
// Head/tail of the readiness queue
|
||||
head_readiness: AtomicPtr<Node<U>>,
|
||||
tail_readiness: UnsafeCell<*const Node<U>>,
|
||||
|
||||
// Used as part of the MPSC queue algorithm
|
||||
stub: Arc<Node<U>>,
|
||||
}
|
||||
|
||||
unsafe impl<U: Sync + Send> Send for Inner<U> {}
|
||||
unsafe impl<U: Sync + Send> Sync for Inner<U> {}
|
||||
|
||||
impl<U: Unpark> executor::Notify for Inner<U> {
|
||||
fn notify(&self, _: usize) {
|
||||
self.unpark.unpark();
|
||||
}
|
||||
}
|
||||
|
||||
struct Node<U> {
|
||||
// The item
|
||||
item: UnsafeCell<Option<Task>>,
|
||||
|
||||
// The tick at which this node was notified
|
||||
notified_at: AtomicUsize,
|
||||
|
||||
// Next pointer for linked list tracking all active nodes
|
||||
next_all: UnsafeCell<*const Node<U>>,
|
||||
|
||||
// Previous node in linked list tracking all active nodes
|
||||
prev_all: UnsafeCell<*const Node<U>>,
|
||||
|
||||
// Next pointer in readiness queue
|
||||
next_readiness: AtomicPtr<Node<U>>,
|
||||
|
||||
// Whether or not this node is currently in the mpsc queue.
|
||||
queued: AtomicBool,
|
||||
|
||||
// Queue that we'll be enqueued to when notified
|
||||
queue: Weak<Inner<U>>,
|
||||
}
|
||||
|
||||
/// Returned by `Inner::dequeue`, representing either a dequeue success (with
|
||||
/// the dequeued node), an empty list, or an inconsistent state.
|
||||
///
|
||||
/// The inconsistent state is described in more detail at [1024cores], but
|
||||
/// roughly indicates that a node will be ready to dequeue sometime shortly in
|
||||
/// the future and the caller should try again soon.
|
||||
///
|
||||
/// [1024cores]: http://www.1024cores.net/home/lock-free-algorithms/queues/intrusive-mpsc-node-based-queue
|
||||
enum Dequeue<U> {
|
||||
Data(*const Node<U>),
|
||||
Empty,
|
||||
Yield,
|
||||
Inconsistent,
|
||||
}
|
||||
|
||||
/// Wraps a spawned boxed future
|
||||
struct Task(Spawn<Box<Future<Item = (), Error = ()>>>);
|
||||
|
||||
/// A task that is scheduled. `turn` must be called
|
||||
pub struct Scheduled<'a, U: 'a> {
|
||||
task: &'a mut Task,
|
||||
notify: &'a Notify<'a, U>,
|
||||
done: &'a mut bool,
|
||||
}
|
||||
|
||||
impl<U> Scheduler<U>
|
||||
where U: Unpark,
|
||||
{
|
||||
/// Constructs a new, empty `Scheduler`
|
||||
///
|
||||
/// The returned `Scheduler` does not contain any items and, in this
|
||||
/// state, `Scheduler::poll` will return `Ok(Async::Ready(None))`.
|
||||
pub fn new(unpark: U) -> Self {
|
||||
let stub = Arc::new(Node {
|
||||
item: UnsafeCell::new(None),
|
||||
notified_at: AtomicUsize::new(0),
|
||||
next_all: UnsafeCell::new(ptr::null()),
|
||||
prev_all: UnsafeCell::new(ptr::null()),
|
||||
next_readiness: AtomicPtr::new(ptr::null_mut()),
|
||||
queued: AtomicBool::new(true),
|
||||
queue: Weak::new(),
|
||||
});
|
||||
let stub_ptr = &*stub as *const Node<U>;
|
||||
let inner = Arc::new(Inner {
|
||||
unpark,
|
||||
tick_num: AtomicUsize::new(0),
|
||||
head_readiness: AtomicPtr::new(stub_ptr as *mut _),
|
||||
tail_readiness: UnsafeCell::new(stub_ptr),
|
||||
stub: stub,
|
||||
});
|
||||
|
||||
Scheduler {
|
||||
inner: inner,
|
||||
nodes: List::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify(&self) -> NotifyHandle {
|
||||
self.inner.clone().into()
|
||||
}
|
||||
|
||||
pub fn schedule(&mut self, item: Box<Future<Item = (), Error = ()>>) {
|
||||
// Get the current scheduler tick
|
||||
let tick_num = self.inner.tick_num.load(SeqCst);
|
||||
|
||||
let node = Arc::new(Node {
|
||||
item: UnsafeCell::new(Some(Task::new(item))),
|
||||
notified_at: AtomicUsize::new(tick_num),
|
||||
next_all: UnsafeCell::new(ptr::null_mut()),
|
||||
prev_all: UnsafeCell::new(ptr::null_mut()),
|
||||
next_readiness: AtomicPtr::new(ptr::null_mut()),
|
||||
queued: AtomicBool::new(true),
|
||||
queue: Arc::downgrade(&self.inner),
|
||||
});
|
||||
|
||||
// Right now our node has a strong reference count of 1. We transfer
|
||||
// ownership of this reference count to our internal linked list
|
||||
// and we'll reclaim ownership through the `unlink` function below.
|
||||
let ptr = self.nodes.push_back(node);
|
||||
|
||||
// We'll need to get the item "into the system" to start tracking it,
|
||||
// e.g. getting its unpark notifications going to us tracking which
|
||||
// items are ready. To do that we unconditionally enqueue it for
|
||||
// polling here.
|
||||
self.inner.enqueue(ptr);
|
||||
}
|
||||
|
||||
/// Advance the scheduler state, returning `true` if any futures were
|
||||
/// processed.
|
||||
///
|
||||
/// This function should be called whenever the caller is notified via a
|
||||
/// wakeup.
|
||||
pub fn tick(&mut self, enter: &mut Enter, num_futures: &mut usize) -> bool
|
||||
{
|
||||
let mut ret = false;
|
||||
let tick = self.inner.tick_num.fetch_add(1, SeqCst)
|
||||
.wrapping_add(1);
|
||||
|
||||
loop {
|
||||
let node = match unsafe { self.inner.dequeue(Some(tick)) } {
|
||||
Dequeue::Empty => {
|
||||
return ret;
|
||||
}
|
||||
Dequeue::Yield => {
|
||||
self.inner.unpark.unpark();
|
||||
return ret;
|
||||
}
|
||||
Dequeue::Inconsistent => {
|
||||
thread::yield_now();
|
||||
continue;
|
||||
}
|
||||
Dequeue::Data(node) => node,
|
||||
};
|
||||
|
||||
ret = true;
|
||||
|
||||
debug_assert!(node != self.inner.stub());
|
||||
|
||||
unsafe {
|
||||
if (*(*node).item.get()).is_none() {
|
||||
// The node has already been released. However, while it was
|
||||
// being released, another thread notified it, which
|
||||
// resulted in it getting pushed into the mpsc channel.
|
||||
//
|
||||
// In this case, we just dec the ref count.
|
||||
let node = ptr2arc(node);
|
||||
assert!((*node.next_all.get()).is_null());
|
||||
assert!((*node.prev_all.get()).is_null());
|
||||
continue
|
||||
};
|
||||
|
||||
// We're going to need to be very careful if the `poll`
|
||||
// function below panics. We need to (a) not leak memory and
|
||||
// (b) ensure that we still don't have any use-after-frees. To
|
||||
// manage this we do a few things:
|
||||
//
|
||||
// * This "bomb" here will call `release_node` if dropped
|
||||
// abnormally. That way we'll be sure the memory management
|
||||
// of the `node` is managed correctly.
|
||||
//
|
||||
// * We unlink the node from our internal queue to preemptively
|
||||
// assume is is complete (will return Ready or panic), in
|
||||
// which case we'll want to discard it regardless.
|
||||
//
|
||||
struct Bomb<'a, U: Unpark + 'a> {
|
||||
borrow: &'a mut Borrow<'a, U>,
|
||||
enter: &'a mut Enter,
|
||||
node: Option<Arc<Node<U>>>,
|
||||
}
|
||||
|
||||
impl<'a, U: Unpark> Drop for Bomb<'a, U> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(node) = self.node.take() {
|
||||
self.borrow.enter(self.enter, || release_node(node))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let node = self.nodes.remove(node);
|
||||
|
||||
let mut borrow = Borrow {
|
||||
scheduler: self,
|
||||
num_futures,
|
||||
};
|
||||
|
||||
let mut bomb = Bomb {
|
||||
node: Some(node),
|
||||
enter: enter,
|
||||
borrow: &mut borrow,
|
||||
};
|
||||
|
||||
let mut done = false;
|
||||
|
||||
// Now that the bomb holds the node, create a new scope. This
|
||||
// scope ensures that the borrow will go out of scope before we
|
||||
// mutate the node pointer in `bomb` again
|
||||
{
|
||||
let node = bomb.node.as_ref().unwrap();
|
||||
|
||||
// Get a reference to the inner future. We already ensured
|
||||
// that the item `is_some`.
|
||||
let item = (*node.item.get()).as_mut().unwrap();
|
||||
|
||||
// Unset queued flag... this must be done before
|
||||
// polling. This ensures that the item gets
|
||||
// rescheduled if it is notified **during** a call
|
||||
// to `poll`.
|
||||
let prev = (*node).queued.swap(false, SeqCst);
|
||||
assert!(prev);
|
||||
|
||||
// Poll the underlying item with the appropriate `notify`
|
||||
// implementation. This is where a large bit of the unsafety
|
||||
// starts to stem from internally. The `notify` instance itself
|
||||
// is basically just our `Arc<Node>` and tracks the mpsc
|
||||
// queue of ready items.
|
||||
//
|
||||
// Critically though `Node` won't actually access `Task`, the
|
||||
// item, while it's floating around inside of `Task`
|
||||
// instances. These structs will basically just use `T` to size
|
||||
// the internal allocation, appropriately accessing fields and
|
||||
// deallocating the node if need be.
|
||||
let borrow = &mut *bomb.borrow;
|
||||
let enter = &mut *bomb.enter;
|
||||
let notify = Notify(bomb.node.as_ref().unwrap());
|
||||
|
||||
let mut scheduled = Scheduled {
|
||||
task: item,
|
||||
notify: ¬ify,
|
||||
done: &mut done,
|
||||
};
|
||||
|
||||
if borrow.enter(enter, || scheduled.tick()) {
|
||||
*borrow.num_futures -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
// The future is not done, push it back into the "all
|
||||
// node" list.
|
||||
let node = bomb.node.take().unwrap();
|
||||
bomb.borrow.scheduler.nodes.push_back(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U: Unpark> Scheduled<'a, U> {
|
||||
/// Polls the task, returns `true` if the task has completed.
|
||||
pub fn tick(&mut self) -> bool {
|
||||
// Tick the future
|
||||
let ret = match self.task.0.poll_future_notify(self.notify, 0) {
|
||||
Ok(Async::Ready(_)) | Err(_) => true,
|
||||
Ok(Async::NotReady) => false,
|
||||
};
|
||||
|
||||
*self.done = ret;
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Task {
|
||||
pub fn new(future: Box<Future<Item = (), Error = ()> + 'static>) -> Self {
|
||||
Task(executor::spawn(future))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Task {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Task")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn release_node<U>(node: Arc<Node<U>>) {
|
||||
// The item is done, try to reset the queued flag. This will prevent
|
||||
// `notify` from doing any work in the item
|
||||
let prev = node.queued.swap(true, SeqCst);
|
||||
|
||||
// Drop the item, even if it hasn't finished yet. This is safe
|
||||
// because we're dropping the item on the thread that owns
|
||||
// `Scheduler`, which correctly tracks T's lifetimes and such.
|
||||
unsafe {
|
||||
drop((*node.item.get()).take());
|
||||
}
|
||||
|
||||
// If the queued flag was previously set then it means that this node
|
||||
// is still in our internal mpsc queue. We then transfer ownership
|
||||
// of our reference count to the mpsc queue, and it'll come along and
|
||||
// free it later, noticing that the item is `None`.
|
||||
//
|
||||
// If, however, the queued flag was *not* set then we're safe to
|
||||
// release our reference count on the internal node. The queued flag
|
||||
// was set above so all item `enqueue` operations will not actually
|
||||
// enqueue the node, so our node will never see the mpsc queue again.
|
||||
// The node itself will be deallocated once all reference counts have
|
||||
// been dropped by the various owning tasks elsewhere.
|
||||
if prev {
|
||||
mem::forget(node);
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> Debug for Scheduler<U> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "Scheduler {{ ... }}")
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> Drop for Scheduler<U> {
|
||||
fn drop(&mut self) {
|
||||
// When a `Scheduler` is dropped we want to drop all items associated
|
||||
// with it. At the same time though there may be tons of `Task` handles
|
||||
// flying around which contain `Node` references inside them. We'll
|
||||
// let those naturally get deallocated when the `Task` itself goes out
|
||||
// of scope or gets notified.
|
||||
while let Some(node) = self.nodes.pop_front() {
|
||||
release_node(node);
|
||||
}
|
||||
|
||||
// Note that at this point we could still have a bunch of nodes in the
|
||||
// mpsc queue. None of those nodes, however, have items associated
|
||||
// with them so they're safe to destroy on any thread. At this point
|
||||
// the `Scheduler` struct, the owner of the one strong reference
|
||||
// to `Inner` will drop the strong reference. At that point
|
||||
// whichever thread releases the strong refcount last (be it this
|
||||
// thread or some other thread as part of an `upgrade`) will clear out
|
||||
// the mpsc queue and free all remaining nodes.
|
||||
//
|
||||
// While that freeing operation isn't guaranteed to happen here, it's
|
||||
// guaranteed to happen "promptly" as no more "blocking work" will
|
||||
// happen while there's a strong refcount held.
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> Inner<U> {
|
||||
/// The enqueue function from the 1024cores intrusive MPSC queue algorithm.
|
||||
fn enqueue(&self, node: *const Node<U>) {
|
||||
unsafe {
|
||||
debug_assert!((*node).queued.load(Relaxed));
|
||||
|
||||
// This action does not require any coordination
|
||||
(*node).next_readiness.store(ptr::null_mut(), Relaxed);
|
||||
|
||||
// Note that these atomic orderings come from 1024cores
|
||||
let node = node as *mut _;
|
||||
let prev = self.head_readiness.swap(node, AcqRel);
|
||||
(*prev).next_readiness.store(node, Release);
|
||||
}
|
||||
}
|
||||
|
||||
/// The dequeue function from the 1024cores intrusive MPSC queue algorithm
|
||||
///
|
||||
/// Note that this unsafe as it required mutual exclusion (only one thread
|
||||
/// can call this) to be guaranteed elsewhere.
|
||||
unsafe fn dequeue(&self, tick: Option<usize>) -> Dequeue<U> {
|
||||
let mut tail = *self.tail_readiness.get();
|
||||
let mut next = (*tail).next_readiness.load(Acquire);
|
||||
|
||||
if tail == self.stub() {
|
||||
if next.is_null() {
|
||||
return Dequeue::Empty;
|
||||
}
|
||||
|
||||
*self.tail_readiness.get() = next;
|
||||
tail = next;
|
||||
next = (*next).next_readiness.load(Acquire);
|
||||
}
|
||||
|
||||
if let Some(tick) = tick {
|
||||
let actual = (*tail).notified_at.load(SeqCst);
|
||||
|
||||
// Only dequeue if the node was not scheduled during the current
|
||||
// tick.
|
||||
if actual == tick {
|
||||
// Only doing the check above **should** be enough in
|
||||
// practice. However, technically there is a potential for
|
||||
// deadlocking if there are `usize::MAX` ticks while the thread
|
||||
// scheduling the task is frozen.
|
||||
//
|
||||
// If, for some reason, this is not enough, calling `unpark`
|
||||
// here will resolve the issue.
|
||||
return Dequeue::Yield;
|
||||
}
|
||||
}
|
||||
|
||||
if !next.is_null() {
|
||||
*self.tail_readiness.get() = next;
|
||||
debug_assert!(tail != self.stub());
|
||||
return Dequeue::Data(tail);
|
||||
}
|
||||
|
||||
if self.head_readiness.load(Acquire) as *const _ != tail {
|
||||
return Dequeue::Inconsistent;
|
||||
}
|
||||
|
||||
self.enqueue(self.stub());
|
||||
|
||||
next = (*tail).next_readiness.load(Acquire);
|
||||
|
||||
if !next.is_null() {
|
||||
*self.tail_readiness.get() = next;
|
||||
return Dequeue::Data(tail);
|
||||
}
|
||||
|
||||
Dequeue::Inconsistent
|
||||
}
|
||||
|
||||
fn stub(&self) -> *const Node<U> {
|
||||
&*self.stub
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> Drop for Inner<U> {
|
||||
fn drop(&mut self) {
|
||||
// Once we're in the destructor for `Inner` we need to clear out the
|
||||
// mpsc queue of nodes if there's anything left in there.
|
||||
//
|
||||
// Note that each node has a strong reference count associated with it
|
||||
// which is owned by the mpsc queue. All nodes should have had their
|
||||
// items dropped already by the `Scheduler` destructor above,
|
||||
// so we're just pulling out nodes and dropping their refcounts.
|
||||
unsafe {
|
||||
loop {
|
||||
match self.dequeue(None) {
|
||||
Dequeue::Empty => break,
|
||||
Dequeue::Yield => unreachable!(),
|
||||
Dequeue::Inconsistent => abort("inconsistent in drop"),
|
||||
Dequeue::Data(ptr) => drop(ptr2arc(ptr)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> List<U> {
|
||||
fn new() -> Self {
|
||||
List {
|
||||
len: 0,
|
||||
head: ptr::null_mut(),
|
||||
tail: ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepends an element to the back of the list
|
||||
fn push_back(&mut self, node: Arc<Node<U>>) -> *const Node<U> {
|
||||
let ptr = arc2ptr(node);
|
||||
|
||||
unsafe {
|
||||
// Point to the current last node in the list
|
||||
*(*ptr).prev_all.get() = self.tail;
|
||||
*(*ptr).next_all.get() = ptr::null_mut();
|
||||
|
||||
if !self.tail.is_null() {
|
||||
*(*self.tail).next_all.get() = ptr;
|
||||
self.tail = ptr;
|
||||
} else {
|
||||
// This is the first node
|
||||
self.tail = ptr;
|
||||
self.head = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
self.len += 1;
|
||||
|
||||
return ptr
|
||||
}
|
||||
|
||||
/// Pop an element from the front of the list
|
||||
fn pop_front(&mut self) -> Option<Arc<Node<U>>> {
|
||||
if self.head.is_null() {
|
||||
// The list is empty
|
||||
return None;
|
||||
}
|
||||
|
||||
self.len -= 1;
|
||||
|
||||
unsafe {
|
||||
// Convert the ptr to Arc<_>
|
||||
let node = ptr2arc(self.head);
|
||||
|
||||
// Update the head pointer
|
||||
self.head = *node.next_all.get();
|
||||
|
||||
// If the pointer is null, then the list is empty
|
||||
if self.head.is_null() {
|
||||
self.tail = ptr::null_mut();
|
||||
} else {
|
||||
*(*self.head).prev_all.get() = ptr::null_mut();
|
||||
}
|
||||
|
||||
Some(node)
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a specific node
|
||||
unsafe fn remove(&mut self, node: *const Node<U>) -> Arc<Node<U>> {
|
||||
let node = ptr2arc(node);
|
||||
let next = *node.next_all.get();
|
||||
let prev = *node.prev_all.get();
|
||||
*node.next_all.get() = ptr::null_mut();
|
||||
*node.prev_all.get() = ptr::null_mut();
|
||||
|
||||
if !next.is_null() {
|
||||
*(*next).prev_all.get() = prev;
|
||||
} else {
|
||||
self.tail = prev;
|
||||
}
|
||||
|
||||
if !prev.is_null() {
|
||||
*(*prev).next_all.get() = next;
|
||||
} else {
|
||||
self.head = next;
|
||||
}
|
||||
|
||||
self.len -= 1;
|
||||
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U> Clone for Notify<'a, U> {
|
||||
fn clone(&self) -> Self {
|
||||
Notify(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U> fmt::Debug for Notify<'a, U> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Notiy").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U: Unpark> From<Notify<'a, U>> for NotifyHandle {
|
||||
fn from(handle: Notify<'a, U>) -> NotifyHandle {
|
||||
unsafe {
|
||||
let ptr = handle.0.clone();
|
||||
let ptr = mem::transmute::<Arc<Node<U>>, *mut ArcNode<U>>(ptr);
|
||||
NotifyHandle::new(hide_lt(ptr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ArcNode<U>(PhantomData<U>);
|
||||
|
||||
// We should never touch `Task` on any thread other than the one owning
|
||||
// `Scheduler`, so this should be a safe operation.
|
||||
unsafe impl<U: Sync + Send> Send for ArcNode<U> {}
|
||||
unsafe impl<U: Sync + Send> Sync for ArcNode<U> {}
|
||||
|
||||
impl<U: Unpark> executor::Notify for ArcNode<U> {
|
||||
fn notify(&self, _id: usize) {
|
||||
unsafe {
|
||||
let me: *const ArcNode<U> = self;
|
||||
let me: *const *const ArcNode<U> = &me;
|
||||
let me = me as *const Arc<Node<U>>;
|
||||
Node::notify(&*me)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<U: Unpark> UnsafeNotify for ArcNode<U> {
|
||||
unsafe fn clone_raw(&self) -> NotifyHandle {
|
||||
let me: *const ArcNode<U> = self;
|
||||
let me: *const *const ArcNode<U> = &me;
|
||||
let me = &*(me as *const Arc<Node<U>>);
|
||||
Notify(me).into()
|
||||
}
|
||||
|
||||
unsafe fn drop_raw(&self) {
|
||||
let mut me: *const ArcNode<U> = self;
|
||||
let me = &mut me as *mut *const ArcNode<U> as *mut Arc<Node<U>>;
|
||||
ptr::drop_in_place(me);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn hide_lt<U: Unpark>(p: *mut ArcNode<U>) -> *mut UnsafeNotify {
|
||||
mem::transmute(p as *mut UnsafeNotify)
|
||||
}
|
||||
|
||||
impl<U: Unpark> Node<U> {
|
||||
fn notify(me: &Arc<Node<U>>) {
|
||||
let inner = match me.queue.upgrade() {
|
||||
Some(inner) => inner,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// It's our job to notify the node that it's ready to get polled,
|
||||
// meaning that we need to enqueue it into the readiness queue. To
|
||||
// do this we flag that we're ready to be queued, and if successful
|
||||
// we then do the literal queueing operation, ensuring that we're
|
||||
// only queued once.
|
||||
//
|
||||
// Once the node is inserted we be sure to notify the parent task,
|
||||
// as it'll want to come along and pick up our node now.
|
||||
//
|
||||
// Note that we don't change the reference count of the node here,
|
||||
// we're just enqueueing the raw pointer. The `Scheduler`
|
||||
// implementation guarantees that if we set the `queued` flag true that
|
||||
// there's a reference count held by the main `Scheduler` queue
|
||||
// still.
|
||||
let prev = me.queued.swap(true, SeqCst);
|
||||
if !prev {
|
||||
// Get the current scheduler tick
|
||||
let tick_num = inner.tick_num.load(SeqCst);
|
||||
me.notified_at.store(tick_num, SeqCst);
|
||||
|
||||
inner.enqueue(&**me);
|
||||
inner.unpark.unpark();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> Drop for Node<U> {
|
||||
fn drop(&mut self) {
|
||||
// Currently a `Node` is sent across all threads for any lifetime,
|
||||
// regardless of `T`. This means that for memory safety we can't
|
||||
// actually touch `T` at any time except when we have a reference to the
|
||||
// `Scheduler` itself.
|
||||
//
|
||||
// Consequently it *should* be the case that we always drop items from
|
||||
// the `Scheduler` instance, but this is a bomb in place to catch
|
||||
// any bugs in that logic.
|
||||
unsafe {
|
||||
if (*self.item.get()).is_some() {
|
||||
abort("item still here when dropping");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn arc2ptr<T>(ptr: Arc<T>) -> *const T {
|
||||
let addr = &*ptr as *const T;
|
||||
mem::forget(ptr);
|
||||
return addr
|
||||
}
|
||||
|
||||
unsafe fn ptr2arc<T>(ptr: *const T) -> Arc<T> {
|
||||
let anchor = mem::transmute::<usize, Arc<T>>(0x10);
|
||||
let addr = &*anchor as *const T;
|
||||
mem::forget(anchor);
|
||||
let offset = addr as isize - 0x10;
|
||||
mem::transmute::<isize, Arc<T>>(ptr as isize - offset)
|
||||
}
|
||||
|
||||
fn abort(s: &str) -> ! {
|
||||
struct DoublePanic;
|
||||
|
||||
impl Drop for DoublePanic {
|
||||
fn drop(&mut self) {
|
||||
panic!("panicking twice to abort the program");
|
||||
}
|
||||
}
|
||||
|
||||
let _bomb = DoublePanic;
|
||||
panic!("{}", s);
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
//! Task execution utilities.
|
||||
//!
|
||||
//! In the Tokio execution model, futures are lazy. When a future is created, no
|
||||
//! work is performed. In order for the work defined by the future to happen,
|
||||
//! the future must be submitted to an executor. A future that is submitted to
|
||||
//! an executor is called a "task".
|
||||
//!
|
||||
//! The executor executor is responsible for ensuring that [`Future::poll`] is
|
||||
//! called whenever the task is [notified]. Notification happens when the
|
||||
//! internal state of a task transitions from "not ready" to ready. For
|
||||
//! example, a socket might have received data and a call to `read` will now be
|
||||
//! able to succeed.
|
||||
//!
|
||||
//! The specific strategy used to manage the tasks is left up to the
|
||||
//! executor. There are two main flavors of executors: single-threaded and
|
||||
//! multithreaded. This module provides both.
|
||||
//!
|
||||
//! * **[`current_thread`]**: A single-threaded executor that support spawning
|
||||
//! tasks that are not `Send`. It guarantees that tasks will be executed on
|
||||
//! the same thread from which they are spawned.
|
||||
//!
|
||||
//! * **[`thread_pool`]**: A multi-threaded executor that maintains a pool of
|
||||
//! threads. Tasks are spawned to one of the threads in the pool and executed.
|
||||
//! The pool employes a [work-stealing] strategy for optimizing how tasks get
|
||||
//! spread across the available threads.
|
||||
//!
|
||||
//! # `Executor` trait.
|
||||
//!
|
||||
//! This module provides the [`Executor`] trait (re-exported from
|
||||
//! [`tokio-executor`]), which describes the API that all executors must
|
||||
//! implement.
|
||||
//!
|
||||
//! A free [`spawn`] function is provided that allows spawning futures onto the
|
||||
//! default executor (tracked via a thread-local variable) without referencing a
|
||||
//! handle. It is expected that all executors will set a value for the default
|
||||
//! executor. This value will often be set to the executor itself, but it is
|
||||
//! possible that the default executor might be set to a different executor.
|
||||
//!
|
||||
//! For example, the [`current_thread`] executor might set the default executor
|
||||
//! to a thread pool instead of itself, allowing futures to spawn new tasks onto
|
||||
//! the thread pool when those tasks are `Send`.
|
||||
//!
|
||||
//! [`Future::poll`]: https://docs.rs/futures/0.1/futures/future/trait.Future.html#tymethod.poll
|
||||
//! [notified]: https://docs.rs/futures/0.1/futures/executor/trait.Notify.html#tymethod.notify
|
||||
//! [`current_thread`]: current_thread/index.html
|
||||
//! [`thread_pool`]: thread_pool/index.html
|
||||
//! [work-stealing]: https://en.wikipedia.org/wiki/Work_stealing
|
||||
//! [`tokio-executor`]: #
|
||||
//! [`Executor`]: #
|
||||
//! [`spawn`]: #
|
||||
|
||||
pub mod current_thread;
|
||||
|
||||
pub mod thread_pool {
|
||||
//! Maintains a pool of threads across which the set of spawned tasks are
|
||||
//! executed.
|
||||
//!
|
||||
//! [`ThreadPool`] is an executor that uses a thread pool for executing
|
||||
//! tasks concurrently across multiple cores. It uses a thread pool that is
|
||||
//! optimized for use cases that involve multiplexing large number of
|
||||
//! independent tasks that perform short(ish) amounts of computation and are
|
||||
//! mainly waiting on I/O, i.e. the Tokio use case.
|
||||
//!
|
||||
//! Usually, users of [`ThreadPool`] will not create pool instances.
|
||||
//! Instead, they will create a [`Runtime`] instance, which comes with a
|
||||
//! pre-configured thread pool.
|
||||
//!
|
||||
//! At the core, [`ThreadPool`] uses a work-stealing based scheduling
|
||||
//! strategy. When spawning a task while *external* to the thread pool
|
||||
//! (i.e., from a thread that is not part of the thread pool), the task is
|
||||
//! randomly assigned to a worker thread. When spawning a task while
|
||||
//! *internal* to the thread pool, the task is assigned to the current
|
||||
//! worker.
|
||||
//!
|
||||
//! Each worker maintains its own queue and first focuses on processing all
|
||||
//! tasks in its queue. When the worker's queue is empty, the worker will
|
||||
//! attempt to *steal* tasks from other worker queues. This strategy helps
|
||||
//! ensure that work is evenly distributed across threads while minimizing
|
||||
//! synchronization between worker threads.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! Thread pool instances are created using [`ThreadPool::new`] or
|
||||
//! [`Builder::new`]. The first option returns a thread pool with default
|
||||
//! configuration values. The second option allows configuring the thread
|
||||
//! pool before instantiating it.
|
||||
//!
|
||||
//! Once an instance is obtained, futures may be spawned onto it using the
|
||||
//! [`spawn`] function.
|
||||
//!
|
||||
//! A handle to the thread pool is obtained using [`ThreadPool::sender`].
|
||||
//! This handle is **only** able to spawn futures onto the thread pool. It
|
||||
//! is unable to affect the lifecycle of the thread pool in any way. This
|
||||
//! handle can be passed into functions or stored in structs as a way to
|
||||
//! grant the capability of spawning futures.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```rust
|
||||
//! # extern crate tokio;
|
||||
//! # extern crate futures;
|
||||
//! # use tokio::executor::thread_pool::ThreadPool;
|
||||
//! use futures::future::{Future, lazy};
|
||||
//!
|
||||
//! # pub fn main() {
|
||||
//! // Create a thread pool with default configuration values
|
||||
//! let thread_pool = ThreadPool::new();
|
||||
//!
|
||||
//! thread_pool.spawn(lazy(|| {
|
||||
//! println!("called from a worker thread");
|
||||
//! Ok(())
|
||||
//! }));
|
||||
//!
|
||||
//! // Gracefully shutdown the threadpool
|
||||
//! thread_pool.shutdown().wait().unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! [`ThreadPool`]: struct.ThreadPool.html
|
||||
//! [`ThreadPool::new`]: struct.ThreadPool.html#method.new
|
||||
//! [`ThreadPool::sender`]: struct.ThreadPool.html#method.sender
|
||||
//! [`spawn`]: struct.ThreadPool.html#method.spawn
|
||||
//! [`Builder::new`]: struct.Builder.html#method.new
|
||||
//! [`Runtime`]: ../../runtime/struct.Runtime.html
|
||||
|
||||
pub use tokio_threadpool::{
|
||||
Builder,
|
||||
Sender,
|
||||
Shutdown,
|
||||
ThreadPool,
|
||||
};
|
||||
}
|
||||
|
||||
pub use tokio_executor::{Executor, DefaultExecutor, SpawnError};
|
||||
|
||||
use futures::{Future, IntoFuture};
|
||||
use futures::future::{self, FutureResult};
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
/// Return value from the `spawn` function.
|
||||
///
|
||||
/// Currently this value doesn't actually provide any functionality. However, it
|
||||
/// provides a way to add functionality later without breaking backwards
|
||||
/// compatibility.
|
||||
///
|
||||
/// This also implements `IntoFuture` so that it can be used as the return value
|
||||
/// in a `for_each` loop.
|
||||
///
|
||||
/// See [`spawn`] for more details.
|
||||
///
|
||||
/// [`spawn`]: fn.spawn.html
|
||||
#[derive(Debug)]
|
||||
pub struct Spawn(());
|
||||
|
||||
/// Spawns a future on the default executor.
|
||||
///
|
||||
/// In order for a future to do work, it must be spawned on an executor. The
|
||||
/// `spawn` function is the easiest way to do this. It spawns a future on the
|
||||
/// [default executor] for the current execution context (tracked using a
|
||||
/// thread-local variable).
|
||||
///
|
||||
/// The default executor is **usually** a thread pool.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// In this example, a server is started and `spawn` is used to start a new task
|
||||
/// that processes each received connection.
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use futures::{Future, Stream};
|
||||
/// use tokio::net::TcpListener;
|
||||
///
|
||||
/// # fn process<T>(_: T) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
/// # unimplemented!();
|
||||
/// # }
|
||||
/// # fn dox() {
|
||||
/// # let addr = "127.0.0.1:8080".parse().unwrap();
|
||||
/// let listener = TcpListener::bind(&addr).unwrap();
|
||||
///
|
||||
/// let server = listener.incoming()
|
||||
/// .map_err(|e| println!("error = {:?}", e))
|
||||
/// .for_each(|socket| {
|
||||
/// tokio::spawn(process(socket))
|
||||
/// });
|
||||
///
|
||||
/// tokio::run(server);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// [default executor]: struct.DefaultExecutor.html
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the default executor is not set or if spawning
|
||||
/// onto the default executor returns an error. To avoid the panic, use
|
||||
/// [`DefaultExecutor`].
|
||||
///
|
||||
/// [`DefaultExecutor`]: struct.DefaultExecutor.html
|
||||
pub fn spawn<F>(f: F) -> Spawn
|
||||
where F: Future<Item = (), Error = ()> + 'static + Send
|
||||
{
|
||||
::tokio_executor::spawn(f);
|
||||
Spawn(())
|
||||
}
|
||||
|
||||
/// Like `spawn`, but compatible with futures 0.2
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
pub fn spawn2<F>(f: F) -> Spawn
|
||||
where F: futures2::Future<Item = (), Error = futures2::Never> + 'static + Send
|
||||
{
|
||||
::tokio_executor::spawn2(f);
|
||||
Spawn(())
|
||||
}
|
||||
|
||||
impl IntoFuture for Spawn {
|
||||
type Future = FutureResult<(), ()>;
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn into_future(self) -> Self::Future {
|
||||
future::ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
impl futures2::IntoFuture for Spawn {
|
||||
type Future = futures2::future::FutureResult<(), ()>;
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn into_future(self) -> Self::Future {
|
||||
futures2::future::ok(())
|
||||
}
|
||||
}
|
||||
+185
-49
@@ -1,63 +1,199 @@
|
||||
//! Asynchronous signal handling for Tokio
|
||||
//! A runtime for writing reliable, asynchronous, and slim applications.
|
||||
//!
|
||||
//! This crate implements asynchronous signal handling for Tokio, an
|
||||
//! asynchronous I/O framework in Rust. The primary type exported from this
|
||||
//! crate, `unix::Signal`, allows listening for arbitrary signals on Unix
|
||||
//! platforms, receiving them in an asynchronous fashion.
|
||||
//! Tokio is an event-driven, non-blocking I/O platform for writing asynchronous
|
||||
//! applications with the Rust programming language. At a high level, it
|
||||
//! provides a few major components:
|
||||
//!
|
||||
//! Note that signal handling is in general a very tricky topic and should be
|
||||
//! used with great care. This crate attempts to implement 'best practice' for
|
||||
//! signal handling, but it should be evaluated for your own applications' needs
|
||||
//! to see if it's suitable.
|
||||
//! * A multi threaded, work-stealing based task [scheduler][runtime].
|
||||
//! * A [reactor][reactor] backed by the operating system's event queue (epoll, kqueue,
|
||||
//! IOCP, etc...).
|
||||
//! * Asynchronous [TCP and UDP][net] sockets.
|
||||
//! * [Timer][timer] API for scheduling work in the future.
|
||||
//!
|
||||
//! The are some fundamental limitations of this crate documented on the
|
||||
//! `Signal` structure as well.
|
||||
//! Tokio is built using [futures] as the abstraction for managing the
|
||||
//! complexity of asynchronous programming.
|
||||
//!
|
||||
//! > **Note**: This crate compiles on Windows, but currently contains no
|
||||
//! > bindings. Windows does not have signals like Unix does, but it
|
||||
//! > does have a way to receive ctrl-c notifications at the console.
|
||||
//! > It's planned that this will be bound and exported outside the
|
||||
//! > `unix` module in the future!
|
||||
//! Guide level documentation is found on the [website].
|
||||
//!
|
||||
//! [website]: https://tokio.rs/docs/getting-started/hello-world/
|
||||
//! [futures]: http://docs.rs/futures
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! A simple TCP echo server:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate tokio;
|
||||
//!
|
||||
//! use tokio::prelude::*;
|
||||
//! use tokio::io::copy;
|
||||
//! use tokio::net::TcpListener;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! // Bind the server's socket.
|
||||
//! let addr = "127.0.0.1:12345".parse().unwrap();
|
||||
//! let listener = TcpListener::bind(&addr)
|
||||
//! .expect("unable to bind TCP listener");
|
||||
//!
|
||||
//! // Pull out a stream of sockets for incoming connections
|
||||
//! let server = listener.incoming()
|
||||
//! .map_err(|e| eprintln!("accept failed = {:?}", e))
|
||||
//! .for_each(|sock| {
|
||||
//! // Split up the reading and writing parts of the
|
||||
//! // socket.
|
||||
//! let (reader, writer) = sock.split();
|
||||
//!
|
||||
//! // A future that echos the data and returns how
|
||||
//! // many bytes were copied...
|
||||
//! let bytes_copied = copy(reader, writer);
|
||||
//!
|
||||
//! // ... after which we'll print what happened.
|
||||
//! let handle_conn = bytes_copied.map(|amt| {
|
||||
//! println!("wrote {:?} bytes", amt)
|
||||
//! }).map_err(|err| {
|
||||
//! eprintln!("IO error {:?}", err)
|
||||
//! });
|
||||
//!
|
||||
//! // Spawn the future as a concurrent task.
|
||||
//! tokio::spawn(handle_conn)
|
||||
//! });
|
||||
//!
|
||||
//! // Start the Tokio runtime
|
||||
//! tokio::run(server);
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-signal/0.1")]
|
||||
#![deny(missing_docs)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio/0.1.5")]
|
||||
#![deny(missing_docs, warnings, missing_debug_implementations)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate tokio_core;
|
||||
extern crate mio;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_reactor;
|
||||
extern crate tokio_threadpool;
|
||||
extern crate tokio_timer;
|
||||
extern crate tokio_tcp;
|
||||
extern crate tokio_udp;
|
||||
|
||||
use futures::Future;
|
||||
use futures::stream::Stream;
|
||||
use tokio_core::reactor::Handle;
|
||||
use tokio_core::io::{IoStream, IoFuture};
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
extern crate futures2;
|
||||
|
||||
pub mod unix;
|
||||
pub mod windows;
|
||||
pub mod executor;
|
||||
pub mod net;
|
||||
pub mod reactor;
|
||||
pub mod runtime;
|
||||
pub mod timer;
|
||||
pub mod util;
|
||||
|
||||
/// Creates a stream which receives "ctrl-c" notifications sent to a process.
|
||||
///
|
||||
/// In general signals are handled very differently across Unix and Windows, but
|
||||
/// this is somewhat cross platform in terms of how it can be handled. A ctrl-c
|
||||
/// event to a console process can be represented as a stream for both Windows
|
||||
/// and Unix.
|
||||
///
|
||||
/// This function receives a `Handle` to an event loop and returns a future
|
||||
/// which when resolves yields a stream receiving all signal events. Note that
|
||||
/// there are a number of caveats listening for signals, and you may wish to
|
||||
/// read up on the documentation in the `unix` or `windows` module to take a
|
||||
/// peek.
|
||||
pub fn ctrl_c(handle: &Handle) -> IoFuture<IoStream<()>> {
|
||||
return ctrl_c_imp(handle);
|
||||
pub use executor::spawn;
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
pub use executor::spawn2;
|
||||
|
||||
#[cfg(unix)]
|
||||
fn ctrl_c_imp(handle: &Handle) -> IoFuture<IoStream<()>> {
|
||||
unix::Signal::new(unix::libc::SIGINT, handle).map(|x| {
|
||||
x.map(|_| ()).boxed()
|
||||
}).boxed()
|
||||
}
|
||||
pub use runtime::run;
|
||||
|
||||
#[cfg(windows)]
|
||||
fn ctrl_c_imp(handle: &Handle) -> IoFuture<IoStream<()>> {
|
||||
windows::Event::ctrl_c(handle).map(|x| x.boxed()).boxed()
|
||||
}
|
||||
pub mod io {
|
||||
//! Asynchronous I/O.
|
||||
//!
|
||||
//! This module is the asynchronous version of `std::io`. Primarily, it
|
||||
//! defines two traits, [`AsyncRead`] and [`AsyncWrite`], which extend the
|
||||
//! `Read` and `Write` traits of the standard library.
|
||||
//!
|
||||
//! [`AsyncRead`] and [`AsyncWrite`] must only be implemented for
|
||||
//! non-blocking I/O types that integrate with the futures type system. In
|
||||
//! other words, these types must never block the thread, and instead the
|
||||
//! current task is notified when the I/O resource is ready.
|
||||
//!
|
||||
//! Utilities functions are provided for working with [`AsyncRead`] /
|
||||
//! [`AsyncWrite`] types. For example, [`copy`] asynchronously copies all
|
||||
//! data from a source to a destination.
|
||||
//!
|
||||
//! Additionally, [`Read`], [`Write`], [`Error`], [`ErrorKind`], and
|
||||
//! [`Result`] are re-exported from `std::io` for ease of use.
|
||||
//!
|
||||
//! [`AsyncRead`]: trait.AsyncRead.html
|
||||
//! [`AsyncWrite`]: trait.AsyncWrite.html
|
||||
//! [`copy`]: fn.copy.html
|
||||
//! [`Read`]: trait.Read.html
|
||||
//! [`Write`]: trait.Write.html
|
||||
//! [`Error`]: struct.Error.html
|
||||
//! [`ErrorKind`]: enum.ErrorKind.html
|
||||
//! [`Result`]: type.Result.html
|
||||
|
||||
pub use tokio_io::{
|
||||
AsyncRead,
|
||||
AsyncWrite,
|
||||
};
|
||||
|
||||
// Utils
|
||||
pub use tokio_io::io::{
|
||||
copy,
|
||||
Copy,
|
||||
flush,
|
||||
Flush,
|
||||
lines,
|
||||
Lines,
|
||||
read_exact,
|
||||
ReadExact,
|
||||
read_to_end,
|
||||
ReadToEnd,
|
||||
read_until,
|
||||
ReadUntil,
|
||||
shutdown,
|
||||
Shutdown,
|
||||
write_all,
|
||||
WriteAll,
|
||||
};
|
||||
|
||||
// Re-export io::Error so that users don't have to deal
|
||||
// with conflicts when `use`ing `futures::io` and `std::io`.
|
||||
pub use ::std::io::{
|
||||
Error,
|
||||
ErrorKind,
|
||||
Result,
|
||||
Read,
|
||||
Write,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
//! A "prelude" for users of the `tokio` crate.
|
||||
//!
|
||||
//! This prelude is similar to the standard library's prelude in that you'll
|
||||
//! almost always want to import its entire contents, but unlike the standard
|
||||
//! library's prelude you'll have to do so manually:
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio::prelude::*;
|
||||
//! ```
|
||||
//!
|
||||
//! The prelude may grow over time as additional items see ubiquitous use.
|
||||
|
||||
pub use tokio_io::{
|
||||
AsyncRead,
|
||||
AsyncWrite,
|
||||
};
|
||||
|
||||
pub use util::{
|
||||
FutureExt,
|
||||
};
|
||||
|
||||
pub use ::std::io::{
|
||||
Read,
|
||||
Write,
|
||||
};
|
||||
|
||||
pub use futures::{
|
||||
Future,
|
||||
future,
|
||||
Stream,
|
||||
stream,
|
||||
Sink,
|
||||
IntoFuture,
|
||||
Async,
|
||||
AsyncSink,
|
||||
Poll,
|
||||
task,
|
||||
};
|
||||
}
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
//! TCP/UDP bindings for `tokio`.
|
||||
//!
|
||||
//! This module contains the TCP/UDP networking types, similar to the standard
|
||||
//! library, which can be used to implement networking protocols.
|
||||
//!
|
||||
//! # TCP
|
||||
//!
|
||||
//! Connecting to an address, via TCP, can be done using [`TcpStream`]'s
|
||||
//! [`connect`] method, which returns [`ConnectFuture`]. `ConnectFuture`
|
||||
//! implements a future which returns a `TcpStream`.
|
||||
//!
|
||||
//! To listen on an address [`TcpListener`] can be used. `TcpListener`'s
|
||||
//! [`incoming`][incoming_method] method can be used to accept new connections.
|
||||
//! It return the [`Incoming`] struct, which implements a stream which returns
|
||||
//! `TcpStream`s.
|
||||
//!
|
||||
//! [`TcpStream`]: struct.TcpStream.html
|
||||
//! [`connect`]: struct.TcpStream.html#method.connect
|
||||
//! [`ConnectFuture`]: struct.ConnectFuture.html
|
||||
//! [`TcpListener`]: struct.TcpListener.html
|
||||
//! [incoming_method]: struct.TcpListener.html#method.incoming
|
||||
//! [`Incoming`]: struct.Incoming.html
|
||||
//!
|
||||
//! # UDP
|
||||
//!
|
||||
//! The main struct for UDP is the [`UdpSocket`], which represents a UDP socket.
|
||||
//! Reading and writing to it can be done using futures, which return the
|
||||
//! [`RecvDgram`] and [`SendDgram`] structs respectively.
|
||||
//!
|
||||
//! For convience it's also possible to convert raw datagrams into higher-level
|
||||
//! frames.
|
||||
//!
|
||||
//! [`UdpSocket`]: struct.UdpSocket.html
|
||||
//! [`RecvDgram`]: struct.RecvDgram.html
|
||||
//! [`SendDgram`]: struct.SendDgram.html
|
||||
//! [`UdpFramed`]: struct.UdpFramed.html
|
||||
//! [`framed`]: struct.UdpSocket.html#method.framed
|
||||
|
||||
pub use tokio_tcp::{TcpStream, ConnectFuture};
|
||||
pub use tokio_tcp::{TcpListener, Incoming};
|
||||
pub use tokio_udp::{UdpSocket, UdpFramed, SendDgram, RecvDgram};
|
||||
@@ -0,0 +1,149 @@
|
||||
//! Event loop that drives Tokio I/O resources.
|
||||
//!
|
||||
//! This module contains [`Reactor`], which is the event loop that drives all
|
||||
//! Tokio I/O resources. It is the reactor's job to receive events from the
|
||||
//! operating system ([epoll], [kqueue], [IOCP], etc...) and forward them to
|
||||
//! waiting tasks. It is the bridge between operating system and the futures
|
||||
//! model.
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! When using Tokio, all operations are asynchronous and represented by
|
||||
//! futures. These futures, representing the application logic, are scheduled by
|
||||
//! an executor (see [runtime model] for more details). Executors wait for
|
||||
//! notifications before scheduling the future for execution time, i.e., nothing
|
||||
//! happens until an event is received indicating that the task can make
|
||||
//! progress.
|
||||
//!
|
||||
//! The reactor receives events from the operating system and notifies the
|
||||
//! executor.
|
||||
//!
|
||||
//! Let's start with a basic example, establishing a TCP connection.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # extern crate tokio;
|
||||
//! # fn dox() {
|
||||
//! use tokio::prelude::*;
|
||||
//! use tokio::net::TcpStream;
|
||||
//!
|
||||
//! let addr = "93.184.216.34:9243".parse().unwrap();
|
||||
//!
|
||||
//! let connect_future = TcpStream::connect(&addr);
|
||||
//!
|
||||
//! let task = connect_future
|
||||
//! .and_then(|socket| {
|
||||
//! println!("successfully connected");
|
||||
//! Ok(())
|
||||
//! })
|
||||
//! .map_err(|e| println!("failed to connect; err={:?}", e));
|
||||
//!
|
||||
//! tokio::run(task);
|
||||
//! # }
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! Establishing a TCP connection usually cannot be completed immediately.
|
||||
//! [`TcpStream::connect`] does not block the current thread. Instead, it
|
||||
//! returns a [future][connect-future] that resolves once the TCP connection has
|
||||
//! been established. The connect future itself has no way of knowing when the
|
||||
//! TCP connection has been established.
|
||||
//!
|
||||
//! Before returning the future, [`TcpStream::connect`] registers the socket
|
||||
//! with a reactor. This registration process, handled by [`Registration`], is
|
||||
//! what links the [`TcpStream`] with the [`Reactor`] instance. At this point,
|
||||
//! the reactor starts listening for connection events from the operating system
|
||||
//! for that socket.
|
||||
//!
|
||||
//! Once the connect future is passed to [`tokio::run`], it is spawned onto a
|
||||
//! thread pool. The thread pool waits until it is notified that the connection
|
||||
//! has completed.
|
||||
//!
|
||||
//! When the TCP connection is established, the reactor receives an event from
|
||||
//! the operating system. It then notifies the thread pool, telling it that the
|
||||
//! connect future can complete. At this point, the thread pool will schedule
|
||||
//! the task to run on one of its worker threads. This results in the `and_then`
|
||||
//! closure to get executed.
|
||||
//!
|
||||
//! ## Lazy registration
|
||||
//!
|
||||
//! Notice how the snippet above does not explicitly reference a reactor. When
|
||||
//! [`TcpStream::connect`] is called, it registers the socket with a reactor,
|
||||
//! but no reactor is specified. This works because the registration process
|
||||
//! mentioned above is actually lazy. It doesn't *actually* happen in the
|
||||
//! [`connect`] function. Instead, the registration is established the first
|
||||
//! time that the task is polled (again, see [runtime model]).
|
||||
//!
|
||||
//! A reactor instance is automatically made available when using the Tokio
|
||||
//! [runtime], which is done using [`tokio::run`]. The Tokio runtime's executor
|
||||
//! sets a thread-local variable referencing the associated [`Reactor`] instance
|
||||
//! and [`Handle::current`] (used by [`Registration`]) returns the reference.
|
||||
//!
|
||||
//! ## Implementation
|
||||
//!
|
||||
//! The reactor implementation uses [`mio`] to interface with the operating
|
||||
//! system's event queue. A call to [`Reactor::poll`] results in in a single
|
||||
//! call to [`Poll::poll`] which in turn results in a single call to the
|
||||
//! operating system's selector.
|
||||
//!
|
||||
//! The reactor maintains state for each registered I/O resource. This tracks
|
||||
//! the executor task to notify when events are provided by the operating
|
||||
//! system's selector. This state is stored in a `Sync` data structure and
|
||||
//! referenced by [`Registration`]. When the [`Registration`] instance is
|
||||
//! dropped, this state is cleaned up. Because the state is stored in a `Sync`
|
||||
//! data structure, the [`Registration`] instance is able to be moved to other
|
||||
//! threads.
|
||||
//!
|
||||
//! By default, a runtime's default reactor runs on a background thread. This
|
||||
//! ensures that application code cannot significantly impact the reactor's
|
||||
//! responsiveness.
|
||||
//!
|
||||
//! ## Integrating with the reactor
|
||||
//!
|
||||
//! Tokio comes with a number of I/O resources, like TCP and UDP sockets, that
|
||||
//! automatically integrate with the reactor. However, library authors or
|
||||
//! applications may wish to implement their own resources that are also backed
|
||||
//! by the reactor.
|
||||
//!
|
||||
//! There are a couple of ways to do this.
|
||||
//!
|
||||
//! If the custom I/O resource implements [`mio::Evented`] and implements
|
||||
//! [`std::Read`] and / or [`std::Write`], then [`PollEvented`] is the most
|
||||
//! suited.
|
||||
//!
|
||||
//! Otherwise, [`Registration`] can be used directly. This provides the lowest
|
||||
//! level primitive needed for integrating with the reactor: a stream of
|
||||
//! readiness events.
|
||||
//!
|
||||
//! [`Reactor`]: struct.Reactor.html
|
||||
//! [`Registration`]: struct.Registration.html
|
||||
//! [runtime model]: https://tokio.rs/docs/getting-started/runtime-model/
|
||||
//! [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
|
||||
//! [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
|
||||
//! [IOCP]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365198(v=vs.85).aspx
|
||||
//! [`TcpStream::connect`]: ../net/struct.TcpStream.html#method.connect
|
||||
//! [`connect`]: ../net/struct.TcpStream.html#method.connect
|
||||
//! [connect-future]: ../net/struct.ConnectFuture.html
|
||||
//! [`tokio::run`]: ../runtime/fn.run.html
|
||||
//! [`TcpStream`]: ../net/struct.TcpStream.html
|
||||
//! [runtime]: ../runtime
|
||||
//! [`Handle::current`]: struct.Handle.html#method.current
|
||||
//! [`mio`]: https://github.com/carllerche/mio
|
||||
//! [`Reactor::poll`]: struct.Reactor.html#method.poll
|
||||
//! [`Poll::poll`]: https://docs.rs/mio/0.6/mio/struct.Poll.html#method.poll
|
||||
//! [`mio::Evented`]: https://docs.rs/mio/0.6/mio/trait.Evented.html
|
||||
//! [`PollEvented`]: struct.PollEvented.html
|
||||
//! [`std::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
|
||||
//! [`std::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
|
||||
|
||||
pub use tokio_reactor::{
|
||||
Reactor,
|
||||
Handle,
|
||||
Background,
|
||||
Turn,
|
||||
Registration,
|
||||
PollEvented as PollEvented2,
|
||||
};
|
||||
|
||||
mod poll_evented;
|
||||
#[allow(deprecated)]
|
||||
pub use self::poll_evented::PollEvented;
|
||||
@@ -0,0 +1,539 @@
|
||||
//! Readiness tracking streams, backing I/O objects.
|
||||
//!
|
||||
//! This module contains the core type which is used to back all I/O on object
|
||||
//! in `tokio-core`. The `PollEvented` type is the implementation detail of
|
||||
//! all I/O. Each `PollEvented` manages registration with a reactor,
|
||||
//! acquisition of a token, and tracking of the readiness state on the
|
||||
//! underlying I/O primitive.
|
||||
|
||||
#![allow(deprecated, warnings)]
|
||||
|
||||
use std::fmt;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::sync::Mutex;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
|
||||
use futures::{task, Async, Poll};
|
||||
use mio::event::Evented;
|
||||
use mio::Ready;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use reactor::{Handle, Registration};
|
||||
|
||||
#[deprecated(since = "0.1.2", note = "PollEvented2 instead")]
|
||||
#[doc(hidden)]
|
||||
pub struct PollEvented<E> {
|
||||
io: E,
|
||||
inner: Inner,
|
||||
handle: Handle,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
registration: Mutex<Registration>,
|
||||
|
||||
/// Currently visible read readiness
|
||||
read_readiness: AtomicUsize,
|
||||
|
||||
/// Currently visible write readiness
|
||||
write_readiness: AtomicUsize,
|
||||
}
|
||||
|
||||
impl<E: fmt::Debug> fmt::Debug for PollEvented<E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("PollEvented")
|
||||
.field("io", &self.io)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> PollEvented<E> {
|
||||
/// Creates a new readiness stream associated with the provided
|
||||
/// `loop_handle` and for the given `source`.
|
||||
pub fn new(io: E, handle: &Handle) -> io::Result<PollEvented<E>>
|
||||
where E: Evented,
|
||||
{
|
||||
let registration = Registration::new();
|
||||
registration.register(&io)?;
|
||||
|
||||
Ok(PollEvented {
|
||||
io: io,
|
||||
inner: Inner {
|
||||
registration: Mutex::new(registration),
|
||||
read_readiness: AtomicUsize::new(0),
|
||||
write_readiness: AtomicUsize::new(0),
|
||||
},
|
||||
handle: handle.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Tests to see if this source is ready to be read from or not.
|
||||
///
|
||||
/// If this stream is not ready for a read then `Async::NotReady` will be
|
||||
/// returned and the current task will be scheduled to receive a
|
||||
/// notification when the stream is readable again. In other words, this
|
||||
/// method is only safe to call from within the context of a future's task,
|
||||
/// typically done in a `Future::poll` method.
|
||||
///
|
||||
/// This is mostly equivalent to `self.poll_ready(Ready::readable())`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if called outside the context of a future's
|
||||
/// task.
|
||||
pub fn poll_read(&mut self) -> Async<()> {
|
||||
if self.poll_read2().is_ready() {
|
||||
return ().into();
|
||||
}
|
||||
|
||||
Async::NotReady
|
||||
}
|
||||
|
||||
fn poll_read2(&self) -> Async<Ready> {
|
||||
let r = self.inner.registration.lock().unwrap();
|
||||
|
||||
// Load the cached readiness
|
||||
match self.inner.read_readiness.load(Relaxed) {
|
||||
0 => {}
|
||||
mut n => {
|
||||
// Check what's new with the reactor.
|
||||
if let Some(ready) = r.take_read_ready().unwrap() {
|
||||
n |= ready2usize(ready);
|
||||
self.inner.read_readiness.store(n, Relaxed);
|
||||
}
|
||||
|
||||
return usize2ready(n).into();
|
||||
}
|
||||
}
|
||||
|
||||
let ready = match r.poll_read_ready().unwrap() {
|
||||
Async::Ready(r) => r,
|
||||
_ => return Async::NotReady,
|
||||
};
|
||||
|
||||
// Cache the value
|
||||
self.inner.read_readiness.store(ready2usize(ready), Relaxed);
|
||||
|
||||
ready.into()
|
||||
}
|
||||
|
||||
/// Tests to see if this source is ready to be written to or not.
|
||||
///
|
||||
/// If this stream is not ready for a write then `Async::NotReady` will be returned
|
||||
/// and the current task will be scheduled to receive a notification when
|
||||
/// the stream is writable again. In other words, this method is only safe
|
||||
/// to call from within the context of a future's task, typically done in a
|
||||
/// `Future::poll` method.
|
||||
///
|
||||
/// This is mostly equivalent to `self.poll_ready(Ready::writable())`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if called outside the context of a future's
|
||||
/// task.
|
||||
pub fn poll_write(&mut self) -> Async<()> {
|
||||
let r = self.inner.registration.lock().unwrap();
|
||||
|
||||
match self.inner.write_readiness.load(Relaxed) {
|
||||
0 => {}
|
||||
mut n => {
|
||||
// Check what's new with the reactor.
|
||||
if let Some(ready) = r.take_write_ready().unwrap() {
|
||||
n |= ready2usize(ready);
|
||||
self.inner.write_readiness.store(n, Relaxed);
|
||||
}
|
||||
|
||||
return ().into();
|
||||
}
|
||||
}
|
||||
|
||||
let ready = match r.poll_write_ready().unwrap() {
|
||||
Async::Ready(r) => r,
|
||||
_ => return Async::NotReady,
|
||||
};
|
||||
|
||||
// Cache the value
|
||||
self.inner.write_readiness.store(ready2usize(ready), Relaxed);
|
||||
|
||||
().into()
|
||||
}
|
||||
|
||||
/// Test to see whether this source fulfills any condition listed in `mask`
|
||||
/// provided.
|
||||
///
|
||||
/// The `mask` given here is a mio `Ready` set of possible events. This can
|
||||
/// contain any events like read/write but also platform-specific events
|
||||
/// such as hup and error. The `mask` indicates events that are interested
|
||||
/// in being ready.
|
||||
///
|
||||
/// If any event in `mask` is ready then it is returned through
|
||||
/// `Async::Ready`. The `Ready` set returned is guaranteed to not be empty
|
||||
/// and contains all events that are currently ready in the `mask` provided.
|
||||
///
|
||||
/// If no events are ready in the `mask` provided then the current task is
|
||||
/// scheduled to receive a notification when any of them become ready. If
|
||||
/// the `writable` event is contained within `mask` then this
|
||||
/// `PollEvented`'s `write` task will be blocked and otherwise the `read`
|
||||
/// task will be blocked. This is generally only relevant if you're working
|
||||
/// with this `PollEvented` object on multiple tasks.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if called outside the context of a future's
|
||||
/// task.
|
||||
pub fn poll_ready(&mut self, mask: Ready) -> Async<Ready> {
|
||||
let mut ret = Ready::empty();
|
||||
|
||||
if mask.is_empty() {
|
||||
return ret.into();
|
||||
}
|
||||
|
||||
if mask.is_writable() {
|
||||
if self.poll_write().is_ready() {
|
||||
ret = Ready::writable();
|
||||
}
|
||||
}
|
||||
|
||||
let mask = mask - Ready::writable();
|
||||
|
||||
if !mask.is_empty() {
|
||||
if let Async::Ready(v) = self.poll_read2() {
|
||||
ret |= v & mask;
|
||||
}
|
||||
}
|
||||
|
||||
if ret.is_empty() {
|
||||
if mask.is_writable() {
|
||||
let _ = self.need_write();
|
||||
}
|
||||
|
||||
if mask.is_readable() {
|
||||
let _ = self.need_read();
|
||||
}
|
||||
|
||||
Async::NotReady
|
||||
} else {
|
||||
ret.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates to this source of events that the corresponding I/O object is
|
||||
/// no longer readable, but it needs to be.
|
||||
///
|
||||
/// This function, like `poll_read`, is only safe to call from the context
|
||||
/// of a future's task (typically in a `Future::poll` implementation). It
|
||||
/// informs this readiness stream that the underlying object is no longer
|
||||
/// readable, typically because a "would block" error was seen.
|
||||
///
|
||||
/// *All* readiness bits associated with this stream except the writable bit
|
||||
/// will be reset when this method is called. The current task is then
|
||||
/// scheduled to receive a notification whenever anything changes other than
|
||||
/// the writable bit. Note that this typically just means the readable bit
|
||||
/// is used here, but if you're using a custom I/O object for events like
|
||||
/// hup/error this may also be relevant.
|
||||
///
|
||||
/// Note that it is also only valid to call this method if `poll_read`
|
||||
/// previously indicated that the object is readable. That is, this function
|
||||
/// must always be paired with calls to `poll_read` previously.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the `Reactor` that this `PollEvented`
|
||||
/// is associated with has gone away (been destroyed). The error means that
|
||||
/// the ambient futures task could not be scheduled to receive a
|
||||
/// notification and typically means that the error should be propagated
|
||||
/// outwards.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if called outside the context of a future's
|
||||
/// task.
|
||||
pub fn need_read(&mut self) -> io::Result<()> {
|
||||
self.inner.read_readiness.store(0, Relaxed);
|
||||
|
||||
if self.poll_read().is_ready() {
|
||||
// Notify the current task
|
||||
task::current().notify();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Indicates to this source of events that the corresponding I/O object is
|
||||
/// no longer writable, but it needs to be.
|
||||
///
|
||||
/// This function, like `poll_write`, is only safe to call from the context
|
||||
/// of a future's task (typically in a `Future::poll` implementation). It
|
||||
/// informs this readiness stream that the underlying object is no longer
|
||||
/// writable, typically because a "would block" error was seen.
|
||||
///
|
||||
/// The flag indicating that this stream is writable is unset and the
|
||||
/// current task is scheduled to receive a notification when the stream is
|
||||
/// then again writable.
|
||||
///
|
||||
/// Note that it is also only valid to call this method if `poll_write`
|
||||
/// previously indicated that the object is writable. That is, this function
|
||||
/// must always be paired with calls to `poll_write` previously.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the `Reactor` that this `PollEvented`
|
||||
/// is associated with has gone away (been destroyed). The error means that
|
||||
/// the ambient futures task could not be scheduled to receive a
|
||||
/// notification and typically means that the error should be propagated
|
||||
/// outwards.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if called outside the context of a future's
|
||||
/// task.
|
||||
pub fn need_write(&mut self) -> io::Result<()> {
|
||||
self.inner.write_readiness.store(0, Relaxed);
|
||||
|
||||
if self.poll_write().is_ready() {
|
||||
// Notify the current task
|
||||
task::current().notify();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a reference to the event loop handle that this readiness stream
|
||||
/// is associated with.
|
||||
pub fn handle(&self) -> &Handle {
|
||||
&self.handle
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the underlying I/O object this readiness
|
||||
/// stream is wrapping.
|
||||
pub fn get_ref(&self) -> &E {
|
||||
&self.io
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O object this readiness
|
||||
/// stream is wrapping.
|
||||
pub fn get_mut(&mut self) -> &mut E {
|
||||
&mut self.io
|
||||
}
|
||||
|
||||
/// Consumes the `PollEvented` and returns the underlying I/O object
|
||||
pub fn into_inner(self) -> E {
|
||||
self.io
|
||||
}
|
||||
|
||||
/// Deregisters this source of events from the reactor core specified.
|
||||
///
|
||||
/// This method can optionally be called to unregister the underlying I/O
|
||||
/// object with the event loop that the `handle` provided points to.
|
||||
/// Typically this method is not required as this automatically happens when
|
||||
/// `E` is dropped, but for some use cases the `E` object doesn't represent
|
||||
/// an owned reference, so dropping it won't automatically unregister with
|
||||
/// the event loop.
|
||||
///
|
||||
/// This consumes `self` as it will no longer provide events after the
|
||||
/// method is called, and will likely return an error if this `PollEvented`
|
||||
/// was created on a separate event loop from the `handle` specified.
|
||||
pub fn deregister(&self) -> io::Result<()>
|
||||
where E: Evented,
|
||||
{
|
||||
self.inner.registration.lock().unwrap()
|
||||
.deregister(&self.io)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Read> Read for PollEvented<E> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
if let Async::NotReady = self.poll_read() {
|
||||
return Err(io::ErrorKind::WouldBlock.into())
|
||||
}
|
||||
|
||||
let r = self.get_mut().read(buf);
|
||||
|
||||
if is_wouldblock(&r) {
|
||||
self.need_read()?;
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Write> Write for PollEvented<E> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if let Async::NotReady = self.poll_write() {
|
||||
return Err(io::ErrorKind::WouldBlock.into())
|
||||
}
|
||||
|
||||
let r = self.get_mut().write(buf);
|
||||
|
||||
if is_wouldblock(&r) {
|
||||
self.need_write()?;
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
if let Async::NotReady = self.poll_write() {
|
||||
return Err(io::ErrorKind::WouldBlock.into())
|
||||
}
|
||||
|
||||
let r = self.get_mut().flush();
|
||||
|
||||
if is_wouldblock(&r) {
|
||||
self.need_write()?;
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Read> AsyncRead for PollEvented<E> {
|
||||
}
|
||||
|
||||
impl<E: Write> AsyncWrite for PollEvented<E> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
fn is_wouldblock<T>(r: &io::Result<T>) -> bool {
|
||||
match *r {
|
||||
Ok(_) => false,
|
||||
Err(ref e) => e.kind() == io::ErrorKind::WouldBlock,
|
||||
}
|
||||
}
|
||||
|
||||
const READ: usize = 1 << 0;
|
||||
const WRITE: usize = 1 << 1;
|
||||
|
||||
fn ready2usize(ready: Ready) -> usize {
|
||||
let mut bits = 0;
|
||||
if ready.is_readable() {
|
||||
bits |= READ;
|
||||
}
|
||||
if ready.is_writable() {
|
||||
bits |= WRITE;
|
||||
}
|
||||
bits | platform::ready2usize(ready)
|
||||
}
|
||||
|
||||
fn usize2ready(bits: usize) -> Ready {
|
||||
let mut ready = Ready::empty();
|
||||
if bits & READ != 0 {
|
||||
ready.insert(Ready::readable());
|
||||
}
|
||||
if bits & WRITE != 0 {
|
||||
ready.insert(Ready::writable());
|
||||
}
|
||||
ready | platform::usize2ready(bits)
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "fuchsia")))]
|
||||
mod platform {
|
||||
use mio::Ready;
|
||||
use mio::unix::UnixReady;
|
||||
|
||||
const HUP: usize = 1 << 2;
|
||||
const ERROR: usize = 1 << 3;
|
||||
const AIO: usize = 1 << 4;
|
||||
const LIO: usize = 1 << 5;
|
||||
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
fn is_aio(ready: &Ready) -> bool {
|
||||
UnixReady::from(*ready).is_aio()
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "dragonfly", target_os = "freebsd")))]
|
||||
fn is_aio(_ready: &Ready) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn is_lio(ready: &Ready) -> bool {
|
||||
UnixReady::from(*ready).is_lio()
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
fn is_lio(_ready: &Ready) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn ready2usize(ready: Ready) -> usize {
|
||||
let ready = UnixReady::from(ready);
|
||||
let mut bits = 0;
|
||||
if is_aio(&ready) {
|
||||
bits |= AIO;
|
||||
}
|
||||
if is_lio(&ready) {
|
||||
bits |= LIO;
|
||||
}
|
||||
if ready.is_error() {
|
||||
bits |= ERROR;
|
||||
}
|
||||
if ready.is_hup() {
|
||||
bits |= HUP;
|
||||
}
|
||||
bits
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios",
|
||||
target_os = "macos"))]
|
||||
fn usize2ready_aio(ready: &mut UnixReady) {
|
||||
ready.insert(UnixReady::aio());
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "dragonfly",
|
||||
target_os = "freebsd", target_os = "ios", target_os = "macos")))]
|
||||
fn usize2ready_aio(_ready: &mut UnixReady) {
|
||||
// aio not available here → empty
|
||||
}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn usize2ready_lio(ready: &mut UnixReady) {
|
||||
ready.insert(UnixReady::lio());
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
fn usize2ready_lio(_ready: &mut UnixReady) {
|
||||
// lio not available here → empty
|
||||
}
|
||||
|
||||
pub fn usize2ready(bits: usize) -> Ready {
|
||||
let mut ready = UnixReady::from(Ready::empty());
|
||||
if bits & AIO != 0 {
|
||||
usize2ready_aio(&mut ready);
|
||||
}
|
||||
if bits & LIO != 0 {
|
||||
usize2ready_lio(&mut ready);
|
||||
}
|
||||
if bits & HUP != 0 {
|
||||
ready.insert(UnixReady::hup());
|
||||
}
|
||||
if bits & ERROR != 0 {
|
||||
ready.insert(UnixReady::error());
|
||||
}
|
||||
ready.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(windows, target_os = "fuchsia"))]
|
||||
mod platform {
|
||||
use mio::Ready;
|
||||
|
||||
pub fn all() -> Ready {
|
||||
// No platform-specific Readinesses for Windows
|
||||
Ready::empty()
|
||||
}
|
||||
|
||||
pub fn hup() -> Ready {
|
||||
Ready::empty()
|
||||
}
|
||||
|
||||
pub fn ready2usize(_r: Ready) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
pub fn usize2ready(_r: usize) -> Ready {
|
||||
Ready::empty()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
use runtime::{Inner, Runtime};
|
||||
|
||||
use reactor::Reactor;
|
||||
|
||||
use std::io;
|
||||
|
||||
use tokio_reactor;
|
||||
use tokio_threadpool::Builder as ThreadPoolBuilder;
|
||||
use tokio_threadpool::park::DefaultPark;
|
||||
use tokio_timer::timer::{self, Timer};
|
||||
|
||||
/// Builds Tokio Runtime with custom configuration values.
|
||||
///
|
||||
/// Methods can be chanined in order to set the configuration values. The
|
||||
/// Runtime is constructed by calling [`build`].
|
||||
///
|
||||
/// New instances of `Builder` are obtained via [`Builder::new`].
|
||||
///
|
||||
/// See function level documentation for details on the various configuration
|
||||
/// settings.
|
||||
///
|
||||
/// [`build`]: #method.build
|
||||
/// [`Builder::new`]: #method.new
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate tokio_threadpool;
|
||||
/// # use tokio::runtime::Builder;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// // create and configure ThreadPool
|
||||
/// let mut threadpool_builder = tokio_threadpool::Builder::new();
|
||||
/// threadpool_builder
|
||||
/// .name_prefix("my-runtime-worker-")
|
||||
/// .pool_size(4);
|
||||
///
|
||||
/// // build Runtime
|
||||
/// let runtime = Builder::new()
|
||||
/// .threadpool_builder(threadpool_builder)
|
||||
/// .build();
|
||||
/// // ... call runtime.run(...)
|
||||
/// # let _ = runtime;
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Builder {
|
||||
/// Thread pool specific builder
|
||||
threadpool_builder: ThreadPoolBuilder,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Returns a new runtime builder initialized with default configuration
|
||||
/// values.
|
||||
///
|
||||
/// Configuration methods can be chained on the return value.
|
||||
pub fn new() -> Builder {
|
||||
let mut threadpool_builder = ThreadPoolBuilder::new();
|
||||
threadpool_builder.name_prefix("tokio-runtime-worker-");
|
||||
|
||||
Builder { threadpool_builder }
|
||||
}
|
||||
|
||||
/// Set builder to set up the thread pool instance.
|
||||
pub fn threadpool_builder(&mut self, val: ThreadPoolBuilder) -> &mut Self {
|
||||
self.threadpool_builder = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Create the configured `Runtime`.
|
||||
///
|
||||
/// The returned `ThreadPool` instance is ready to spawn tasks.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::runtime::Builder;
|
||||
/// # pub fn main() {
|
||||
/// let runtime = Builder::new().build();
|
||||
/// // ... call runtime.run(...)
|
||||
/// # let _ = runtime;
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn build(&mut self) -> io::Result<Runtime> {
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
let timers = Arc::new(Mutex::new(HashMap::<_, timer::Handle>::new()));
|
||||
let t1 = timers.clone();
|
||||
|
||||
// Spawn a reactor on a background thread.
|
||||
let reactor = Reactor::new()?.background()?;
|
||||
|
||||
// Get a handle to the reactor.
|
||||
let reactor_handle = reactor.handle().clone();
|
||||
|
||||
let pool = self.threadpool_builder
|
||||
.around_worker(move |w, enter| {
|
||||
let timer_handle = t1.lock().unwrap()
|
||||
.get(w.id()).unwrap()
|
||||
.clone();
|
||||
|
||||
tokio_reactor::with_default(&reactor_handle, enter, |enter| {
|
||||
timer::with_default(&timer_handle, enter, |_| {
|
||||
w.run();
|
||||
});
|
||||
});
|
||||
})
|
||||
.custom_park(move |worker_id| {
|
||||
// Create a new timer
|
||||
let timer = Timer::new(DefaultPark::new());
|
||||
|
||||
timers.lock().unwrap()
|
||||
.insert(worker_id.clone(), timer.handle());
|
||||
|
||||
timer
|
||||
})
|
||||
.build();
|
||||
|
||||
Ok(Runtime {
|
||||
inner: Some(Inner {
|
||||
reactor,
|
||||
pool,
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,470 @@
|
||||
//! A batteries included runtime for applications using Tokio.
|
||||
//!
|
||||
//! Applications using Tokio require some runtime support in order to work:
|
||||
//!
|
||||
//! * A [reactor] to drive I/O resources.
|
||||
//! * An [executor] to execute tasks that use these I/O resources.
|
||||
//! * A [timer] for scheduling work to run after a set period of time.
|
||||
//!
|
||||
//! While it is possible to setup each component manually, this involves a bunch
|
||||
//! of boilerplate.
|
||||
//!
|
||||
//! [`Runtime`] bundles all of these various runtime components into a single
|
||||
//! handle that can be started and shutdown together, eliminating the necessary
|
||||
//! boilerplate to run a Tokio application.
|
||||
//!
|
||||
//! Most applications wont need to use [`Runtime`] directly. Instead, they will
|
||||
//! use the [`run`] function, which uses [`Runtime`] under the hood.
|
||||
//!
|
||||
//! Creating a [`Runtime`] does the following:
|
||||
//!
|
||||
//! * Spawn a background thread running a [`Reactor`] instance.
|
||||
//! * Start a [`ThreadPool`] for executing futures.
|
||||
//! * Run an instance of [`Timer`] **per** thread pool worker thread.
|
||||
//!
|
||||
//! The thread pool uses a work-stealing strategy and is configured to start a
|
||||
//! worker thread for each CPU core available on the system. This tends to be
|
||||
//! the ideal setup for Tokio applications.
|
||||
//!
|
||||
//! A timer per thread pool worker thread is used to minimize the amount of
|
||||
//! synchronization that is required for working with the timer.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! Most applications will use the [`run`] function. This takes a future to
|
||||
//! "seed" the application, blocking the thread until the runtime becomes
|
||||
//! [idle].
|
||||
//!
|
||||
//! ```rust
|
||||
//! # extern crate tokio;
|
||||
//! # extern crate futures;
|
||||
//! # use futures::{Future, Stream};
|
||||
//! use tokio::net::TcpListener;
|
||||
//!
|
||||
//! # fn process<T>(_: T) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
//! # unimplemented!();
|
||||
//! # }
|
||||
//! # fn dox() {
|
||||
//! # let addr = "127.0.0.1:8080".parse().unwrap();
|
||||
//! let listener = TcpListener::bind(&addr).unwrap();
|
||||
//!
|
||||
//! let server = listener.incoming()
|
||||
//! .map_err(|e| println!("error = {:?}", e))
|
||||
//! .for_each(|socket| {
|
||||
//! tokio::spawn(process(socket))
|
||||
//! });
|
||||
//!
|
||||
//! tokio::run(server);
|
||||
//! # }
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! In this function, the `run` function blocks until the runtime becomes idle.
|
||||
//! See [`shutdown_on_idle`][idle] for more shutdown details.
|
||||
//!
|
||||
//! From within the context of the runtime, additional tasks are spawned using
|
||||
//! the [`tokio::spawn`] function. Futures spawned using this function will be
|
||||
//! executed on the same thread pool used by the [`Runtime`].
|
||||
//!
|
||||
//! A [`Runtime`] instance can also be used directly.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # extern crate tokio;
|
||||
//! # extern crate futures;
|
||||
//! # use futures::{Future, Stream};
|
||||
//! use tokio::runtime::Runtime;
|
||||
//! use tokio::net::TcpListener;
|
||||
//!
|
||||
//! # fn process<T>(_: T) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
//! # unimplemented!();
|
||||
//! # }
|
||||
//! # fn dox() {
|
||||
//! # let addr = "127.0.0.1:8080".parse().unwrap();
|
||||
//! let listener = TcpListener::bind(&addr).unwrap();
|
||||
//!
|
||||
//! let server = listener.incoming()
|
||||
//! .map_err(|e| println!("error = {:?}", e))
|
||||
//! .for_each(|socket| {
|
||||
//! tokio::spawn(process(socket))
|
||||
//! });
|
||||
//!
|
||||
//! // Create the runtime
|
||||
//! let mut rt = Runtime::new().unwrap();
|
||||
//!
|
||||
//! // Spawn the server task
|
||||
//! rt.spawn(server);
|
||||
//!
|
||||
//! // Wait until the runtime becomes idle and shut it down.
|
||||
//! rt.shutdown_on_idle()
|
||||
//! .wait().unwrap();
|
||||
//! # }
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! [reactor]: ../reactor/struct.Reactor.html
|
||||
//! [executor]: https://tokio.rs/docs/getting-started/runtime-model/#executors
|
||||
//! [timer]: ../timer/index.html
|
||||
//! [`Runtime`]: struct.Runtime.html
|
||||
//! [`Reactor`]: ../reactor/struct.Reactor.html
|
||||
//! [`ThreadPool`]: ../executor/thread_pool/struct.ThreadPool.html
|
||||
//! [`run`]: fn.run.html
|
||||
//! [idle]: struct.Runtime.html#method.shutdown_on_idle
|
||||
//! [`tokio::spawn`]: ../executor/fn.spawn.html
|
||||
//! [`Timer`]: https://docs.rs/tokio-timer/0.2/tokio_timer/timer/struct.Timer.html
|
||||
|
||||
mod builder;
|
||||
mod shutdown;
|
||||
mod task_executor;
|
||||
|
||||
pub use self::builder::Builder;
|
||||
pub use self::shutdown::Shutdown;
|
||||
pub use self::task_executor::TaskExecutor;
|
||||
|
||||
use reactor::{Background, Handle};
|
||||
|
||||
use std::io;
|
||||
|
||||
use tokio_threadpool as threadpool;
|
||||
|
||||
use futures::future::Future;
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
/// Handle to the Tokio runtime.
|
||||
///
|
||||
/// The Tokio runtime includes a reactor as well as an executor for running
|
||||
/// tasks.
|
||||
///
|
||||
/// Instances of `Runtime` can be created using [`new`] or [`Builder`]. However,
|
||||
/// most users will use [`tokio::run`], which uses a `Runtime` internally.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [mod]: index.html
|
||||
/// [`new`]: #method.new
|
||||
/// [`Builder`]: struct.Builder.html
|
||||
/// [`tokio::run`]: fn.run.html
|
||||
#[derive(Debug)]
|
||||
pub struct Runtime {
|
||||
inner: Option<Inner>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
/// Reactor running on a background thread.
|
||||
reactor: Background,
|
||||
|
||||
/// Task execution pool.
|
||||
pool: threadpool::ThreadPool,
|
||||
}
|
||||
|
||||
// ===== impl Runtime =====
|
||||
|
||||
/// Start the Tokio runtime using the supplied future to bootstrap execution.
|
||||
///
|
||||
/// This function is used to bootstrap the execution of a Tokio application. It
|
||||
/// does the following:
|
||||
///
|
||||
/// * Start the Tokio runtime using a default configuration.
|
||||
/// * Spawn the given future onto the thread pool.
|
||||
/// * Block the current thread until the runtime shuts down.
|
||||
///
|
||||
/// Note that the function will not return immediately once `future` has
|
||||
/// completed. Instead it waits for the entire runtime to become idle.
|
||||
///
|
||||
/// See the [module level][mod] documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use futures::{Future, Stream};
|
||||
/// use tokio::net::TcpListener;
|
||||
///
|
||||
/// # fn process<T>(_: T) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
/// # unimplemented!();
|
||||
/// # }
|
||||
/// # fn dox() {
|
||||
/// # let addr = "127.0.0.1:8080".parse().unwrap();
|
||||
/// let listener = TcpListener::bind(&addr).unwrap();
|
||||
///
|
||||
/// let server = listener.incoming()
|
||||
/// .map_err(|e| println!("error = {:?}", e))
|
||||
/// .for_each(|socket| {
|
||||
/// tokio::spawn(process(socket))
|
||||
/// });
|
||||
///
|
||||
/// tokio::run(server);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if called from the context of an executor.
|
||||
///
|
||||
/// [mod]: ../index.html
|
||||
pub fn run<F>(future: F)
|
||||
where F: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
runtime.spawn(future);
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
}
|
||||
|
||||
/// Start the Tokio runtime using the supplied future to bootstrap execution.
|
||||
///
|
||||
/// Identical to `run` but works with futures 0.2-style futures.
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
pub fn run2<F>(future: F)
|
||||
where F: futures2::Future<Item = (), Error = futures2::Never> + Send + 'static,
|
||||
{
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
runtime.spawn2(future);
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
/// Create a new runtime instance with default configuration values.
|
||||
///
|
||||
/// This results in a reactor, thread pool, and timer being initialized. The
|
||||
/// thread pool will not spawn any worker threads until it needs to, i.e.
|
||||
/// tasks are scheduled to run.
|
||||
///
|
||||
/// Most users will not need to call this function directly, instead they
|
||||
/// will use [`tokio::run`][fn.run.html].
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a new `Runtime` with default configuration values.
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
/// use tokio::prelude::*;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // Use the runtime...
|
||||
///
|
||||
/// // Shutdown the runtime
|
||||
/// rt.shutdown_now()
|
||||
/// .wait().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [mod]: index.html
|
||||
pub fn new() -> io::Result<Self> {
|
||||
Builder::new().build()
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.5", note = "use `reactor` instead")]
|
||||
#[doc(hidden)]
|
||||
pub fn handle(&self) -> &Handle {
|
||||
self.reactor()
|
||||
}
|
||||
|
||||
/// Return a reference to the reactor handle for this runtime instance.
|
||||
///
|
||||
/// The returned handle reference can be cloned in order to get an owned
|
||||
/// value of the handle. This handle can be used to initialize I/O resources
|
||||
/// (like TCP or UDP sockets) that will not be used on the runtime.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// let reactor_handle = rt.reactor().clone();
|
||||
///
|
||||
/// // use `reactor_handle`
|
||||
/// ```
|
||||
pub fn reactor(&self) -> &Handle {
|
||||
self.inner().reactor.handle()
|
||||
}
|
||||
|
||||
/// Return a handle to the runtime's executor.
|
||||
///
|
||||
/// The returned handle can be used to spawn tasks that run on this runtime.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// let executor_handle = rt.executor();
|
||||
///
|
||||
/// // use `executor_handle`
|
||||
/// ```
|
||||
pub fn executor(&self) -> TaskExecutor {
|
||||
let inner = self.inner().pool.sender().clone();
|
||||
TaskExecutor { inner }
|
||||
}
|
||||
|
||||
/// Spawn a future onto the Tokio runtime.
|
||||
///
|
||||
/// This spawns the given future onto the runtime's executor, usually a
|
||||
/// thread pool. The thread pool is then responsible for polling the future
|
||||
/// until it completes.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [mod]: index.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use futures::{future, Future, Stream};
|
||||
/// use tokio::runtime::Runtime;
|
||||
///
|
||||
/// # fn dox() {
|
||||
/// // Create the runtime
|
||||
/// let mut rt = Runtime::new().unwrap();
|
||||
///
|
||||
/// // Spawn a future onto the runtime
|
||||
/// rt.spawn(future::lazy(|| {
|
||||
/// println!("now running on a worker thread");
|
||||
/// Ok(())
|
||||
/// }));
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the spawn fails. Failure occurs if the executor
|
||||
/// is currently at capacity and is unable to spawn a new future.
|
||||
pub fn spawn<F>(&mut self, future: F) -> &mut Self
|
||||
where F: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
self.inner_mut().pool.sender().spawn(future).unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
/// Spawn a futures 0.2-style future onto the Tokio runtime.
|
||||
///
|
||||
/// Otherwise identical to `spawn`
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
pub fn spawn2<F>(&mut self, future: F) -> &mut Self
|
||||
where F: futures2::Future<Item = (), Error = futures2::Never> + Send + 'static,
|
||||
{
|
||||
futures2::executor::Executor::spawn(
|
||||
self.inner_mut().pool.sender_mut(), Box::new(future)
|
||||
).unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
/// Signals the runtime to shutdown once it becomes idle.
|
||||
///
|
||||
/// Returns a future that completes once the shutdown operation has
|
||||
/// completed.
|
||||
///
|
||||
/// This function can be used to perform a graceful shutdown of the runtime.
|
||||
///
|
||||
/// The runtime enters an idle state once **all** of the following occur.
|
||||
///
|
||||
/// * The thread pool has no tasks to execute, i.e., all tasks that were
|
||||
/// spawned have completed.
|
||||
/// * The reactor is not managing any I/O resources.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
/// use tokio::prelude::*;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // Use the runtime...
|
||||
///
|
||||
/// // Shutdown the runtime
|
||||
/// rt.shutdown_on_idle()
|
||||
/// .wait().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [mod]: index.html
|
||||
pub fn shutdown_on_idle(mut self) -> Shutdown {
|
||||
let inner = self.inner.take().unwrap();
|
||||
|
||||
let inner = Box::new({
|
||||
let pool = inner.pool;
|
||||
let reactor = inner.reactor;
|
||||
|
||||
pool.shutdown_on_idle().and_then(|_| {
|
||||
reactor.shutdown_on_idle()
|
||||
})
|
||||
});
|
||||
|
||||
Shutdown { inner }
|
||||
}
|
||||
|
||||
/// Signals the runtime to shutdown immediately.
|
||||
///
|
||||
/// Returns a future that completes once the shutdown operation has
|
||||
/// completed.
|
||||
///
|
||||
/// This function will forcibly shutdown the runtime, causing any
|
||||
/// in-progress work to become canceled. The shutdown steps are:
|
||||
///
|
||||
/// * Drain any scheduled work queues.
|
||||
/// * Drop any futures that have not yet completed.
|
||||
/// * Drop the reactor.
|
||||
///
|
||||
/// Once the reactor has dropped, any outstanding I/O resources bound to
|
||||
/// that reactor will no longer function. Calling any method on them will
|
||||
/// result in an error.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
/// use tokio::prelude::*;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // Use the runtime...
|
||||
///
|
||||
/// // Shutdown the runtime
|
||||
/// rt.shutdown_now()
|
||||
/// .wait().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [mod]: index.html
|
||||
pub fn shutdown_now(mut self) -> Shutdown {
|
||||
let inner = self.inner.take().unwrap();
|
||||
Shutdown::shutdown_now(inner)
|
||||
}
|
||||
|
||||
fn inner(&self) -> &Inner {
|
||||
self.inner.as_ref().unwrap()
|
||||
}
|
||||
|
||||
fn inner_mut(&mut self) -> &mut Inner {
|
||||
self.inner.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Runtime {
|
||||
fn drop(&mut self) {
|
||||
if let Some(inner) = self.inner.take() {
|
||||
let shutdown = Shutdown::shutdown_now(inner);
|
||||
let _ = shutdown.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use runtime::Inner;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// A future that resolves when the Tokio `Runtime` is shut down.
|
||||
pub struct Shutdown {
|
||||
pub(super) inner: Box<Future<Item = (), Error = ()> + Send>,
|
||||
}
|
||||
|
||||
impl Shutdown {
|
||||
pub(super) fn shutdown_now(inner: Inner) -> Self {
|
||||
let inner = Box::new({
|
||||
let pool = inner.pool;
|
||||
let reactor = inner.reactor;
|
||||
|
||||
pool.shutdown_now().and_then(|_| {
|
||||
reactor.shutdown_now()
|
||||
.then(|_| {
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
Shutdown { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for Shutdown {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
try_ready!(self.inner.poll());
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Shutdown {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Shutdown")
|
||||
.field("inner", &"Box<Future<Item = (), Error = ()>>")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
|
||||
use tokio_threadpool::Sender;
|
||||
|
||||
use futures::future::{self, Future};
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
/// Executes futures on the runtime
|
||||
///
|
||||
/// All futures spawned using this executor will be submitted to the associated
|
||||
/// Runtime's executor. This executor is usually a thread pool.
|
||||
///
|
||||
/// For more details, see the [module level](index.html) documentation.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TaskExecutor {
|
||||
pub(super) inner: Sender,
|
||||
}
|
||||
|
||||
impl TaskExecutor {
|
||||
/// Spawn a future onto the Tokio runtime.
|
||||
///
|
||||
/// This spawns the given future onto the runtime's executor, usually a
|
||||
/// thread pool. The thread pool is then responsible for polling the future
|
||||
/// until it completes.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [mod]: index.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use futures::{future, Future, Stream};
|
||||
/// use tokio::runtime::Runtime;
|
||||
///
|
||||
/// # fn dox() {
|
||||
/// // Create the runtime
|
||||
/// let mut rt = Runtime::new().unwrap();
|
||||
/// let executor = rt.executor();
|
||||
///
|
||||
/// // Spawn a future onto the runtime
|
||||
/// executor.spawn(future::lazy(|| {
|
||||
/// println!("now running on a worker thread");
|
||||
/// Ok(())
|
||||
/// }));
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the spawn fails. Failure occurs if the executor
|
||||
/// is currently at capacity and is unable to spawn a new future.
|
||||
pub fn spawn<F>(&self, future: F)
|
||||
where F: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
self.inner.spawn(future).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> future::Executor<T> for TaskExecutor
|
||||
where T: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
fn execute(&self, future: T) -> Result<(), future::ExecuteError<T>> {
|
||||
self.inner.execute(future)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::executor::Executor for TaskExecutor {
|
||||
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
|
||||
-> Result<(), ::executor::SpawnError>
|
||||
{
|
||||
self.inner.spawn(future)
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
fn spawn2(&mut self, future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
|
||||
-> Result<(), futures2::executor::SpawnError>
|
||||
{
|
||||
self.inner.spawn2(future)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
type Task2 = Box<futures2::Future<Item = (), Error = futures2::Never> + Send>;
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
impl futures2::executor::Executor for TaskExecutor {
|
||||
fn spawn(&mut self, f: Task2) -> Result<(), futures2::executor::SpawnError> {
|
||||
futures2::executor::Executor::spawn(&mut self.inner, f)
|
||||
}
|
||||
|
||||
fn status(&self) -> Result<(), futures2::executor::SpawnError> {
|
||||
futures2::executor::Executor::status(&self.inner)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
//! Utilities for tracking time.
|
||||
//!
|
||||
//! This module provides a number of types for executing code after a set period
|
||||
//! of time.
|
||||
//!
|
||||
//! * [`Delay`][Delay] is a future that does no work and completes at a specific `Instant`
|
||||
//! in time.
|
||||
//!
|
||||
//! * [`Interval`][Interval] is a stream yielding a value at a fixed period. It
|
||||
//! is initialized with a `Duration` and repeatedly yields each time the
|
||||
//! duration elapses.
|
||||
//!
|
||||
//! * [`Deadline`][Deadline] wraps a future, requiring that it completes before
|
||||
//! a specified `Instant` in time. If the future does not complete in time,
|
||||
//! then it is canceled and an error is returned.
|
||||
//!
|
||||
//! These types are sufficient for handling a large number of scenarios
|
||||
//! involving time.
|
||||
//!
|
||||
//! These types must be used from within the context of the
|
||||
//! [`Runtime`][runtime] or a timer context must be setup explicitly. See the
|
||||
//! [`tokio-timer`][tokio-timer] crate for more details on how to setup a timer
|
||||
//! context.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Wait 100ms and print "Hello World!"
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio::prelude::*;
|
||||
//! use tokio::timer::Delay;
|
||||
//!
|
||||
//! use std::time::{Duration, Instant};
|
||||
//!
|
||||
//! let when = Instant::now() + Duration::from_millis(100);
|
||||
//!
|
||||
//! tokio::run({
|
||||
//! Delay::new(when)
|
||||
//! .map_err(|e| panic!("timer failed; err={:?}", e))
|
||||
//! .and_then(|_| {
|
||||
//! println!("Hello world!");
|
||||
//! Ok(())
|
||||
//! })
|
||||
//! })
|
||||
//! ```
|
||||
//!
|
||||
//! Require that an operation takes no more than 300ms. Note that this uses the
|
||||
//! [`deadline`][ext] function on the [`FutureExt`][ext] trait. This trait is
|
||||
//! included in the prelude.
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate futures;
|
||||
//! # extern crate tokio;
|
||||
//! use tokio::prelude::*;
|
||||
//!
|
||||
//! use std::time::{Duration, Instant};
|
||||
//!
|
||||
//! fn long_op() -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
//! // ...
|
||||
//! # Box::new(futures::future::ok(()))
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let when = Instant::now() + Duration::from_millis(300);
|
||||
//!
|
||||
//! tokio::run({
|
||||
//! long_op()
|
||||
//! .deadline(when)
|
||||
//! .map_err(|e| {
|
||||
//! println!("operation timed out");
|
||||
//! })
|
||||
//! })
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! [runtime]: ../runtime/struct.Runtime.html
|
||||
//! [tokio-timer]: https://docs.rs/tokio-timer
|
||||
//! [ext]: ../util/trait.FutureExt.html#method.deadline
|
||||
|
||||
pub use tokio_timer::{
|
||||
Deadline,
|
||||
DeadlineError,
|
||||
Interval,
|
||||
Delay,
|
||||
};
|
||||
-393
@@ -1,393 +0,0 @@
|
||||
//! Unix-specific types for signal handling.
|
||||
//!
|
||||
//! This module is only defined on Unix platforms and contains the primary
|
||||
//! `Signal` type for receiving notifications of signals.
|
||||
|
||||
#![cfg(unix)]
|
||||
|
||||
pub extern crate libc;
|
||||
extern crate mio;
|
||||
extern crate mio_uds;
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::collections::HashSet;
|
||||
use std::io::prelude::*;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::os::unix::prelude::*;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
|
||||
use std::sync::{Mutex, Once, ONCE_INIT};
|
||||
|
||||
use futures::future;
|
||||
use futures::sync::mpsc::{Receiver, Sender, channel};
|
||||
use futures::{Async, AsyncSink, Future};
|
||||
use futures::{Sink, Stream, Poll};
|
||||
use self::libc::c_int;
|
||||
use self::mio::Poll as MioPoll;
|
||||
use self::mio::unix::EventedFd;
|
||||
use self::mio::{Evented, Token, Ready, PollOpt};
|
||||
use self::mio_uds::UnixStream;
|
||||
use tokio_core::io::IoFuture;
|
||||
use tokio_core::reactor::{Handle, CoreId, PollEvented};
|
||||
|
||||
pub use self::libc::{SIGINT, SIGTERM, SIGUSR1, SIGUSR2};
|
||||
pub use self::libc::{SIGHUP, SIGQUIT, SIGPIPE, SIGALRM, SIGTRAP};
|
||||
|
||||
// Number of different unix signals
|
||||
const SIGNUM: usize = 32;
|
||||
|
||||
struct SignalInfo {
|
||||
pending: AtomicBool,
|
||||
// The ones interested in this signal
|
||||
recipients: Mutex<Vec<(usize, Sender<c_int>)>>,
|
||||
|
||||
init: Once,
|
||||
initialized: UnsafeCell<bool>,
|
||||
prev: UnsafeCell<libc::sigaction>,
|
||||
}
|
||||
|
||||
struct Globals {
|
||||
sender: UnixStream,
|
||||
receiver: UnixStream,
|
||||
signals: [SignalInfo; SIGNUM],
|
||||
drivers: Mutex<HashSet<CoreId>>,
|
||||
}
|
||||
|
||||
impl Default for SignalInfo {
|
||||
fn default() -> SignalInfo {
|
||||
SignalInfo {
|
||||
pending: AtomicBool::new(false),
|
||||
init: ONCE_INIT,
|
||||
initialized: UnsafeCell::new(false),
|
||||
recipients: Mutex::new(Vec::new()),
|
||||
prev: UnsafeCell::new(unsafe { mem::zeroed() }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut GLOBALS: *mut Globals = 0 as *mut Globals;
|
||||
|
||||
fn globals() -> &'static Globals {
|
||||
static INIT: Once = ONCE_INIT;
|
||||
|
||||
unsafe {
|
||||
INIT.call_once(|| {
|
||||
let (receiver, sender) = UnixStream::pair().unwrap();
|
||||
let globals = Globals {
|
||||
sender: sender,
|
||||
receiver: receiver,
|
||||
signals: Default::default(),
|
||||
drivers: Mutex::new(HashSet::new()),
|
||||
};
|
||||
GLOBALS = Box::into_raw(Box::new(globals));
|
||||
});
|
||||
&*GLOBALS
|
||||
}
|
||||
}
|
||||
|
||||
/// Our global signal handler for all signals registered by this module.
|
||||
///
|
||||
/// The purpose of this signal handler is to primarily:
|
||||
///
|
||||
/// 1. Flag that our specific signal was received (e.g. store an atomic flag)
|
||||
/// 2. Wake up driver tasks by writing a byte to a pipe
|
||||
///
|
||||
/// Those two operations shoudl both be async-signal safe. After that's done we
|
||||
/// just try to call a previous signal handler, if any, to be "good denizens of
|
||||
/// the internet"
|
||||
extern fn handler(signum: c_int,
|
||||
info: *mut libc::siginfo_t,
|
||||
ptr: *mut libc::c_void) {
|
||||
type FnSigaction = extern fn(c_int, *mut libc::siginfo_t, *mut libc::c_void);
|
||||
type FnHandler = extern fn(c_int);
|
||||
unsafe {
|
||||
let slot = match (*GLOBALS).signals.get(signum as usize) {
|
||||
Some(slot) => slot,
|
||||
None => return,
|
||||
};
|
||||
slot.pending.store(true, Ordering::SeqCst);
|
||||
|
||||
// Send a wakeup, ignore any errors (anything reasonably possible is
|
||||
// full pipe and then it will wake up anyway).
|
||||
drop((*GLOBALS).sender.write(&[1]));
|
||||
|
||||
let fnptr = (*slot.prev.get()).sa_sigaction;
|
||||
if fnptr == 0 || fnptr == libc::SIG_DFL || fnptr == libc::SIG_IGN {
|
||||
return
|
||||
}
|
||||
if (*slot.prev.get()).sa_flags & libc::SA_SIGINFO == 0 {
|
||||
let action = mem::transmute::<usize, FnHandler>(fnptr);
|
||||
action(signum)
|
||||
} else {
|
||||
let action = mem::transmute::<usize, FnSigaction>(fnptr);
|
||||
action(signum, info, ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable this module to receive signal notifications for the `signal`
|
||||
/// provided.
|
||||
///
|
||||
/// This will register the signal handler if it hasn't already been registered,
|
||||
/// returning any error along the way if that fails.
|
||||
fn signal_enable(signal: c_int) -> io::Result<()> {
|
||||
let siginfo = match globals().signals.get(signal as usize) {
|
||||
Some(slot) => slot,
|
||||
None => {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "signal too large"))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
let mut err = None;
|
||||
siginfo.init.call_once(|| {
|
||||
let mut new: libc::sigaction = mem::zeroed();
|
||||
new.sa_sigaction = handler as usize;
|
||||
new.sa_flags = libc::SA_RESTART |
|
||||
libc::SA_SIGINFO |
|
||||
libc::SA_NOCLDSTOP;
|
||||
if libc::sigaction(signal, &new, &mut *siginfo.prev.get()) != 0 {
|
||||
err = Some(io::Error::last_os_error());
|
||||
} else {
|
||||
*siginfo.initialized.get() = true;
|
||||
}
|
||||
});
|
||||
if let Some(err) = err {
|
||||
return Err(err)
|
||||
}
|
||||
if *siginfo.initialized.get() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other,
|
||||
"failed to register signal handler"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper struct to register our global receiving end of the signal pipe on
|
||||
/// multiple event loops.
|
||||
///
|
||||
/// This structure represents registering the receiving end on all event loops,
|
||||
/// and uses `EventedFd` in mio to do so. It's stored in each driver task and is
|
||||
/// used to read data and register interest in new signals coming in.
|
||||
struct EventedReceiver;
|
||||
|
||||
impl Evented for EventedReceiver {
|
||||
fn register(&self, poll: &MioPoll, token: Token, events: Ready, opts: PollOpt) -> io::Result<()> {
|
||||
let fd = globals().receiver.as_raw_fd();
|
||||
EventedFd(&fd).register(poll, token, events, opts)
|
||||
}
|
||||
fn reregister(&self, poll: &MioPoll, token: Token, events: Ready, opts: PollOpt) -> io::Result<()> {
|
||||
let fd = globals().receiver.as_raw_fd();
|
||||
EventedFd(&fd).reregister(poll, token, events, opts)
|
||||
}
|
||||
fn deregister(&self, poll: &MioPoll) -> io::Result<()> {
|
||||
let fd = globals().receiver.as_raw_fd();
|
||||
EventedFd(&fd).deregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for EventedReceiver {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
(&globals().receiver).read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
struct Driver {
|
||||
id: CoreId,
|
||||
wakeup: PollEvented<EventedReceiver>,
|
||||
}
|
||||
|
||||
impl Future for Driver {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
// Drain the data from the pipe and maintain interest in getting more
|
||||
let any_wakeup = self.drain();
|
||||
if any_wakeup {
|
||||
self.broadcast();
|
||||
}
|
||||
// This task just lives until the end of the event loop
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Driver {
|
||||
fn drop(&mut self) {
|
||||
let mut drivers = globals().drivers.lock().unwrap();
|
||||
drivers.remove(&self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver {
|
||||
fn new(handle: &Handle) -> io::Result<Driver> {
|
||||
Ok(Driver {
|
||||
id: handle.id(),
|
||||
wakeup: try!(PollEvented::new(EventedReceiver, handle)),
|
||||
})
|
||||
}
|
||||
|
||||
/// Drain all data in the global receiver, returning whether data was to be
|
||||
/// had.
|
||||
///
|
||||
/// If this function returns `true` then some signal has been received since
|
||||
/// we last checked, otherwise `false` indicates that no signal has been
|
||||
/// received.
|
||||
fn drain(&mut self) -> bool {
|
||||
let mut received = false;
|
||||
loop {
|
||||
match self.wakeup.read(&mut [0; 128]) {
|
||||
Ok(0) => panic!("EOF on self-pipe"),
|
||||
Ok(_) => received = true,
|
||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => break,
|
||||
Err(e) => panic!("Bad read on self-pipe: {}", e),
|
||||
}
|
||||
}
|
||||
received
|
||||
}
|
||||
|
||||
/// Go through all the signals and broadcast everything.
|
||||
///
|
||||
/// Driver tasks wake up for *any* signal and simply process all globally
|
||||
/// registered signal streams, so each task is sort of cooperatively working
|
||||
/// for all the rest as well.
|
||||
fn broadcast(&self) {
|
||||
for (sig, slot) in globals().signals.iter().enumerate() {
|
||||
// Any signal of this kind arrived since we checked last?
|
||||
if !slot.pending.swap(false, Ordering::SeqCst) {
|
||||
continue
|
||||
}
|
||||
|
||||
let signum = sig as c_int;
|
||||
let mut recipients = slot.recipients.lock().unwrap();
|
||||
|
||||
// Notify all waiters on this signal that the signal has been
|
||||
// received. If we can't push a message into the queue then we don't
|
||||
// worry about it as everything is coalesced anyway. If the channel
|
||||
// has gone away then we can remove that slot.
|
||||
for i in (0..recipients.len()).rev() {
|
||||
// TODO: This thing probably generates unnecessary wakups of
|
||||
// this task when `NotReady` is received because we don't
|
||||
// actually want to get woken up to continue sending a
|
||||
// message. Let's optimise it later on though, as we know
|
||||
// this works.
|
||||
match recipients[i].1.start_send(signum) {
|
||||
Ok(AsyncSink::Ready) => {}
|
||||
Ok(AsyncSink::NotReady(_)) => {}
|
||||
Err(_) => { recipients.swap_remove(i); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An implementation of `Stream` for receiving a particular type of signal.
|
||||
///
|
||||
/// This structure implements the `Stream` trait and represents notifications
|
||||
/// of the current process receiving a particular signal. The signal being
|
||||
/// listened for is passed to `Signal::new`, and the same signal number is then
|
||||
/// yielded as each element for the stream.
|
||||
///
|
||||
/// In general signal handling on Unix is a pretty tricky topic, and this
|
||||
/// structure is no exception! There are some important limitations to keep in
|
||||
/// mind when using `Signal` streams:
|
||||
///
|
||||
/// * Signals handling in Unix already necessitates coalescing signals
|
||||
/// together sometimes. This `Signal` stream is also no exception here in
|
||||
/// that it will also coalesce signals. That is, even if the signal handler
|
||||
/// for this process runs multiple times, the `Signal` stream may only return
|
||||
/// one signal notification. Specifically, before `poll` is called, all
|
||||
/// signal notifications are coalesced into one item returned from `poll`.
|
||||
/// Once `poll` has been called, however, a further signal is guaranteed to
|
||||
/// be yielded as an item.
|
||||
///
|
||||
/// Put another way, any element pulled off the returned stream corresponds to
|
||||
/// *at least one* signal, but possibly more.
|
||||
///
|
||||
/// * Signal handling in general is relatively inefficient. Although some
|
||||
/// improvements are possible in this crate, it's recommended to not plan on
|
||||
/// having millions of signal channels open.
|
||||
///
|
||||
/// * Currently the "driver task" to process incoming signals never exits. This
|
||||
/// driver task runs in the background of the event loop provided, and
|
||||
/// in general you shouldn't need to worry about it.
|
||||
///
|
||||
/// If you've got any questions about this feel free to open an issue on the
|
||||
/// repo, though, as I'd love to chat about this! In other words, I'd love to
|
||||
/// alleviate some of these limitations if possible!
|
||||
pub struct Signal {
|
||||
signal: c_int,
|
||||
token: usize,
|
||||
rx: Receiver<c_int>,
|
||||
}
|
||||
|
||||
impl Signal {
|
||||
/// Creates a new stream which will receive notifications when the current
|
||||
/// process receives the signal `signal`.
|
||||
///
|
||||
/// This function will create a new stream which may be based on the
|
||||
/// event loop handle provided. This function returns a future which will
|
||||
/// then resolve to the signal stream, if successful.
|
||||
///
|
||||
/// The `Signal` stream is an infinite stream which will receive
|
||||
/// notifications whenever a signal is received. More documentation can be
|
||||
/// found on `Signal` itself, but to reiterate:
|
||||
///
|
||||
/// * Signals may be coalesced beyond what the kernel already does.
|
||||
/// * Once a signal handler is registered with the process the underlying
|
||||
/// libc signal handler is never unregistered.
|
||||
///
|
||||
/// A `Signal` stream can be created for a particular signal number
|
||||
/// multiple times. When a signal is received then all the associated
|
||||
/// channels will receive the signal notification.
|
||||
pub fn new(signal: c_int, handle: &Handle) -> IoFuture<Signal> {
|
||||
static TOKENS: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
let result = (|| {
|
||||
// Turn the signal delivery on once we are ready for it
|
||||
try!(signal_enable(signal));
|
||||
|
||||
// Ensure there's a driver for our associated event loop processing
|
||||
// signals.
|
||||
let id = handle.id();
|
||||
let mut drivers = globals().drivers.lock().unwrap();
|
||||
if !drivers.contains(&id) {
|
||||
handle.spawn(try!(Driver::new(handle)));
|
||||
drivers.insert(id);
|
||||
}
|
||||
drop(drivers);
|
||||
|
||||
// One wakeup in a queue is enough, no need for us to buffer up any
|
||||
// more.
|
||||
let (tx, rx) = channel(1);
|
||||
let token = TOKENS.fetch_add(1, Ordering::SeqCst);
|
||||
let idx = signal as usize;
|
||||
globals().signals[idx].recipients.lock().unwrap().push((token, tx));
|
||||
Ok(Signal {
|
||||
rx: rx,
|
||||
token: token,
|
||||
signal: signal,
|
||||
})
|
||||
})();
|
||||
|
||||
future::result(result).boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for Signal {
|
||||
type Item = c_int;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<c_int>, io::Error> {
|
||||
// receivers don't generate errors
|
||||
self.rx.poll().map_err(|_| panic!())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Signal {
|
||||
fn drop(&mut self) {
|
||||
let idx = self.signal as usize;
|
||||
let mut list = globals().signals[idx].recipients.lock().unwrap();
|
||||
list.retain(|pair| pair.0 != self.token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use tokio_timer::Deadline;
|
||||
|
||||
use futures::Future;
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
|
||||
/// An extension trait for `Future` that provides a variety of convenient
|
||||
/// combinator functions.
|
||||
///
|
||||
/// Currently, there only is a [`deadline`] function, but this will increase
|
||||
/// over time.
|
||||
///
|
||||
/// Users are not expected to implement this trait. All types that implement
|
||||
/// `Future` already implement `FutureExt`.
|
||||
///
|
||||
/// This trait can be imported directly or via the Tokio prelude: `use
|
||||
/// tokio::prelude::*`.
|
||||
///
|
||||
/// [`deadline`]: #method.deadline
|
||||
pub trait FutureExt: Future {
|
||||
|
||||
/// Creates a new future which allows `self` until `deadline`.
|
||||
///
|
||||
/// This combinator creates a new future which wraps the receiving future
|
||||
/// with a deadline. The returned future is allowed to execute until it
|
||||
/// completes or `deadline` is reached, whicheever happens first.
|
||||
///
|
||||
/// If the future completes before `deadline` then the future will resolve
|
||||
/// with that item. Otherwise the future will resolve to an error once
|
||||
/// `deadline` is reached.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// use tokio::prelude::*;
|
||||
/// use std::time::{Duration, Instant};
|
||||
/// # use futures::future::{self, FutureResult};
|
||||
///
|
||||
/// # fn long_future() -> FutureResult<(), ()> {
|
||||
/// # future::ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let future = long_future()
|
||||
/// .deadline(Instant::now() + Duration::from_secs(1))
|
||||
/// .map_err(|e| println!("error = {:?}", e));
|
||||
///
|
||||
/// tokio::run(future);
|
||||
/// # }
|
||||
/// ```
|
||||
fn deadline(self, deadline: Instant) -> Deadline<Self>
|
||||
where Self: Sized,
|
||||
{
|
||||
Deadline::new(self, deadline)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> FutureExt for T where T: Future {}
|
||||
@@ -0,0 +1,9 @@
|
||||
//! Utilities for working with Tokio.
|
||||
//!
|
||||
//! This module contains utilities that are useful for working with Tokio.
|
||||
//! Currently, this only includes [`FutureExt`][FutureExt]. However, this will
|
||||
//! include over time.
|
||||
|
||||
mod future;
|
||||
|
||||
pub use self::future::FutureExt;
|
||||
-294
@@ -1,294 +0,0 @@
|
||||
//! Windows-specific types for signal handling.
|
||||
//!
|
||||
//! This module is only defined on Windows and contains the primary `Event` type
|
||||
//! for receiving notifications of events. These events are listened for via the
|
||||
//! `SetConsoleCtrlHandler` function which receives events of the type
|
||||
//! `CTRL_C_EVENT` and `CTRL_BREAK_EVENT`
|
||||
|
||||
#![cfg(windows)]
|
||||
|
||||
extern crate kernel32;
|
||||
extern crate mio;
|
||||
extern crate winapi;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::io;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
|
||||
use futures::future;
|
||||
use futures::stream::Fuse;
|
||||
use futures::sync::mpsc;
|
||||
use futures::sync::oneshot;
|
||||
use futures::{Future, IntoFuture, Poll, Async, Stream};
|
||||
use tokio_core::io::IoFuture;
|
||||
use tokio_core::reactor::{PollEvented, Handle};
|
||||
|
||||
static INIT: Once = ONCE_INIT;
|
||||
static mut GLOBAL_STATE: *mut GlobalState = 0 as *mut _;
|
||||
|
||||
/// Stream of events discovered via `SetConsoleCtrlHandler`.
|
||||
///
|
||||
/// This structure can be used to listen for events of the type `CTRL_C_EVENT`
|
||||
/// and `CTRL_BREAK_EVENT`. The `Stream` trait is implemented for this struct
|
||||
/// and will resolve for each notification received by the process. Note that
|
||||
/// there are few limitations with this as well:
|
||||
///
|
||||
/// * A notification to this process notifies *all* `Event` streams for that
|
||||
/// event type.
|
||||
/// * Notifications to an `Event` stream **are coalesced** if they aren't
|
||||
/// processed quickly enough. This means that if two notifications are
|
||||
/// received back-to-back, then the stream may only receive one item about the
|
||||
/// two notifications.
|
||||
pub struct Event {
|
||||
reg: PollEvented<MyRegistration>,
|
||||
_finished: oneshot::Sender<()>,
|
||||
}
|
||||
|
||||
struct GlobalState {
|
||||
ready: mio::SetReadiness,
|
||||
tx: mpsc::UnboundedSender<Message>,
|
||||
ctrl_c: GlobalEventState,
|
||||
ctrl_break: GlobalEventState,
|
||||
}
|
||||
|
||||
struct GlobalEventState {
|
||||
ready: AtomicBool,
|
||||
}
|
||||
|
||||
enum Message {
|
||||
NewEvent(winapi::DWORD, oneshot::Sender<io::Result<Event>>),
|
||||
}
|
||||
|
||||
struct DriverTask {
|
||||
handle: Handle,
|
||||
reg: PollEvented<MyRegistration>,
|
||||
rx: Fuse<mpsc::UnboundedReceiver<Message>>,
|
||||
ctrl_c: EventState,
|
||||
ctrl_break: EventState,
|
||||
}
|
||||
|
||||
struct EventState {
|
||||
tasks: Vec<(RefCell<oneshot::Receiver<()>>, mio::SetReadiness)>,
|
||||
}
|
||||
|
||||
impl Event {
|
||||
/// Creates a new stream listening for the `CTRL_C_EVENT` events.
|
||||
///
|
||||
/// This function will register a handler via `SetConsoleCtrlHandler` and
|
||||
/// deliver notifications to the returned stream.
|
||||
pub fn ctrl_c(handle: &Handle) -> IoFuture<Event> {
|
||||
Event::new(winapi::CTRL_C_EVENT, handle)
|
||||
}
|
||||
|
||||
/// Creates a new stream listening for the `CTRL_BREAK_EVENT` events.
|
||||
///
|
||||
/// This function will register a handler via `SetConsoleCtrlHandler` and
|
||||
/// deliver notifications to the returned stream.
|
||||
pub fn ctrl_break(handle: &Handle) -> IoFuture<Event> {
|
||||
Event::new(winapi::CTRL_BREAK_EVENT, handle)
|
||||
}
|
||||
|
||||
fn new(signum: winapi::DWORD, handle: &Handle) -> IoFuture<Event> {
|
||||
let mut init = None;
|
||||
INIT.call_once(|| {
|
||||
init = Some(global_init(handle));
|
||||
});
|
||||
let new_signal = future::lazy(move || {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let msg = Message::NewEvent(signum, tx);
|
||||
let res = unsafe {
|
||||
(*GLOBAL_STATE).tx.clone().send(msg)
|
||||
};
|
||||
res.expect("failed to request a new signal stream, did the \
|
||||
first event loop go away?");
|
||||
rx.then(|r| r.unwrap())
|
||||
});
|
||||
match init {
|
||||
Some(init) => init.into_future().and_then(|()| new_signal).boxed(),
|
||||
None => new_signal.boxed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for Event {
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<()>, io::Error> {
|
||||
if !self.reg.poll_read().is_ready() {
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
self.reg.need_read();
|
||||
self.reg.get_ref()
|
||||
.inner.borrow()
|
||||
.as_ref().unwrap().1
|
||||
.set_readiness(mio::Ready::none())
|
||||
.expect("failed to set readiness");
|
||||
Ok(Async::Ready(Some(())))
|
||||
}
|
||||
}
|
||||
|
||||
fn global_init(handle: &Handle) -> io::Result<()> {
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
let reg = MyRegistration { inner: RefCell::new(None) };
|
||||
let reg = try!(PollEvented::new(reg, handle));
|
||||
let ready = reg.get_ref().inner.borrow().as_ref().unwrap().1.clone();
|
||||
unsafe {
|
||||
let state = Box::new(GlobalState {
|
||||
ready: ready,
|
||||
ctrl_c: GlobalEventState { ready: AtomicBool::new(false) },
|
||||
ctrl_break: GlobalEventState { ready: AtomicBool::new(false) },
|
||||
tx: tx,
|
||||
});
|
||||
GLOBAL_STATE = Box::into_raw(state);
|
||||
|
||||
let rc = kernel32::SetConsoleCtrlHandler(Some(handler), winapi::TRUE);
|
||||
if rc == 0 {
|
||||
Box::from_raw(GLOBAL_STATE);
|
||||
GLOBAL_STATE = 0 as *mut _;
|
||||
return Err(io::Error::last_os_error())
|
||||
}
|
||||
|
||||
handle.spawn(DriverTask {
|
||||
handle: handle.clone(),
|
||||
rx: rx.fuse(),
|
||||
reg: reg,
|
||||
ctrl_c: EventState { tasks: Vec::new() },
|
||||
ctrl_break: EventState { tasks: Vec::new() },
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for DriverTask {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
self.check_event_drops();
|
||||
self.check_messages();
|
||||
self.check_events();
|
||||
|
||||
// TODO: when to finish this task?
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
impl DriverTask {
|
||||
fn check_event_drops(&mut self) {
|
||||
self.ctrl_c.tasks.retain(|task| {
|
||||
!task.0.borrow_mut().poll().is_err()
|
||||
});
|
||||
self.ctrl_break.tasks.retain(|task| {
|
||||
!task.0.borrow_mut().poll().is_err()
|
||||
});
|
||||
}
|
||||
|
||||
fn check_messages(&mut self) {
|
||||
loop {
|
||||
// Acquire the next message
|
||||
let message = match self.rx.poll().unwrap() {
|
||||
Async::Ready(Some(e)) => e,
|
||||
Async::Ready(None) |
|
||||
Async::NotReady => break,
|
||||
};
|
||||
let (sig, complete) = match message {
|
||||
Message::NewEvent(sig, complete) => (sig, complete),
|
||||
};
|
||||
|
||||
let event = if sig == winapi::CTRL_C_EVENT {
|
||||
&mut self.ctrl_c
|
||||
} else {
|
||||
&mut self.ctrl_break
|
||||
};
|
||||
|
||||
// Acquire the (registration, set_readiness) pair by... assuming
|
||||
// we're on the event loop (true because of the spawn above).
|
||||
let reg = MyRegistration { inner: RefCell::new(None) };
|
||||
let reg = match PollEvented::new(reg, &self.handle) {
|
||||
Ok(reg) => reg,
|
||||
Err(e) => {
|
||||
complete.complete(Err(e));
|
||||
continue
|
||||
}
|
||||
};
|
||||
|
||||
// Create the `Event` to pass back and then also keep a handle to
|
||||
// the `SetReadiness` for ourselves internally.
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let ready = reg.get_ref().inner.borrow_mut().as_mut().unwrap().1.clone();
|
||||
complete.complete(Ok(Event {
|
||||
reg: reg,
|
||||
_finished: tx,
|
||||
}));
|
||||
event.tasks.push((RefCell::new(rx), ready));
|
||||
}
|
||||
}
|
||||
|
||||
fn check_events(&mut self) {
|
||||
if self.reg.poll_read().is_not_ready() {
|
||||
return
|
||||
}
|
||||
self.reg.need_read();
|
||||
self.reg.get_ref().inner.borrow().as_ref().unwrap()
|
||||
.1.set_readiness(mio::Ready::none()).unwrap();
|
||||
|
||||
if unsafe { (*GLOBAL_STATE).ctrl_c.ready.swap(false, Ordering::SeqCst) } {
|
||||
for task in self.ctrl_c.tasks.iter() {
|
||||
task.1.set_readiness(mio::Ready::readable()).unwrap();
|
||||
}
|
||||
}
|
||||
if unsafe { (*GLOBAL_STATE).ctrl_break.ready.swap(false, Ordering::SeqCst) } {
|
||||
for task in self.ctrl_break.tasks.iter() {
|
||||
task.1.set_readiness(mio::Ready::readable()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn handler(ty: winapi::DWORD) -> winapi::BOOL {
|
||||
let event = match ty {
|
||||
winapi::CTRL_C_EVENT => &(*GLOBAL_STATE).ctrl_c,
|
||||
winapi::CTRL_BREAK_EVENT => &(*GLOBAL_STATE).ctrl_break,
|
||||
_ => return winapi::FALSE
|
||||
};
|
||||
if event.ready.swap(true, Ordering::SeqCst) {
|
||||
winapi::FALSE
|
||||
} else {
|
||||
drop((*GLOBAL_STATE).ready.set_readiness(mio::Ready::readable()));
|
||||
// TODO: this will report that we handled a CTRL_BREAK_EVENT when in
|
||||
// fact we may not have any streams actually created for that
|
||||
// event.
|
||||
winapi::TRUE
|
||||
}
|
||||
}
|
||||
|
||||
struct MyRegistration {
|
||||
inner: RefCell<Option<(mio::Registration, mio::SetReadiness)>>,
|
||||
}
|
||||
|
||||
impl mio::Evented for MyRegistration {
|
||||
fn register(&self,
|
||||
poll: &mio::Poll,
|
||||
token: mio::Token,
|
||||
events: mio::Ready,
|
||||
opts: mio::PollOpt) -> io::Result<()> {
|
||||
let reg = mio::Registration::new(poll, token, events, opts);
|
||||
*self.inner.borrow_mut() = Some(reg);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reregister(&self,
|
||||
_poll: &mio::Poll,
|
||||
_token: mio::Token,
|
||||
_events: mio::Ready,
|
||||
_opts: mio::PollOpt) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deregister(&self, _poll: &mio::Poll) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
|
||||
use std::net::TcpStream;
|
||||
use std::thread;
|
||||
use std::io::{Read, Write, BufReader, BufWriter};
|
||||
|
||||
use futures::Future;
|
||||
use futures::stream::Stream;
|
||||
use tokio_io::io::copy;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn echo_server() {
|
||||
const N: usize = 1024;
|
||||
drop(env_logger::init());
|
||||
|
||||
let srv = t!(TcpListener::bind(&t!("127.0.0.1:0".parse())));
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let msg = "foo bar baz";
|
||||
let t = thread::spawn(move || {
|
||||
let mut s = t!(TcpStream::connect(&addr));
|
||||
|
||||
let t2 = thread::spawn(move || {
|
||||
let mut s = t!(TcpStream::connect(&addr));
|
||||
let mut b = vec![0; msg.len() * N];
|
||||
t!(s.read_exact(&mut b));
|
||||
b
|
||||
});
|
||||
|
||||
let mut expected = Vec::<u8>::new();
|
||||
for _i in 0..N {
|
||||
expected.extend(msg.as_bytes());
|
||||
assert_eq!(t!(s.write(msg.as_bytes())), msg.len());
|
||||
}
|
||||
(expected, t2)
|
||||
});
|
||||
|
||||
let clients = srv.incoming().take(2).collect();
|
||||
let copied = clients.and_then(|clients| {
|
||||
let mut clients = clients.into_iter();
|
||||
let a = BufReader::new(clients.next().unwrap());
|
||||
let b = BufWriter::new(clients.next().unwrap());
|
||||
copy(a, b)
|
||||
});
|
||||
|
||||
let (amt, _, _) = t!(copied.wait());
|
||||
let (expected, t2) = t.join().unwrap();
|
||||
let actual = t2.join().unwrap();
|
||||
|
||||
assert!(expected == actual);
|
||||
assert_eq!(amt, msg.len() as u64 * 1024);
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
#![cfg(not(feature = "unstable-futures"))]
|
||||
|
||||
extern crate tokio;
|
||||
extern crate tokio_executor;
|
||||
extern crate futures;
|
||||
|
||||
use tokio::executor::current_thread::{self, block_on_all, CurrentThread};
|
||||
|
||||
use std::any::Any;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::rc::Rc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::task;
|
||||
use futures::future::{self, lazy};
|
||||
use futures::prelude::*;
|
||||
use futures::sync::oneshot;
|
||||
|
||||
#[test]
|
||||
fn spawn_from_block_on_all() {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
let msg = current_thread::block_on_all(lazy(move || {
|
||||
c.set(1 + c.get());
|
||||
|
||||
// Spawn!
|
||||
current_thread::spawn(lazy(move || {
|
||||
c.set(1 + c.get());
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
|
||||
Ok::<_, ()>("hello")
|
||||
})).unwrap();
|
||||
|
||||
assert_eq!(2, cnt.get());
|
||||
assert_eq!(msg, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_waits() {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
thread::spawn(|| {
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let cnt2 = cnt.clone();
|
||||
|
||||
block_on_all(rx.then(move |_| {
|
||||
cnt.set(1 + cnt.get());
|
||||
Ok::<_, ()>(())
|
||||
})).unwrap();
|
||||
|
||||
assert_eq!(1, cnt2.get());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_many() {
|
||||
const ITER: usize = 200;
|
||||
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
for _ in 0..ITER {
|
||||
let cnt = cnt.clone();
|
||||
current_thread.spawn(lazy(move || {
|
||||
cnt.set(1 + cnt.get());
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
}
|
||||
|
||||
current_thread.run().unwrap();
|
||||
|
||||
assert_eq!(cnt.get(), ITER);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_set_global_executor_by_default() {
|
||||
use tokio_executor::Executor;
|
||||
|
||||
block_on_all(lazy(|| {
|
||||
tokio_executor::DefaultExecutor::current()
|
||||
.spawn(Box::new(lazy(|| ok())))
|
||||
.unwrap_err();
|
||||
|
||||
ok()
|
||||
})).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_from_block_on_future() {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
current_thread.block_on(lazy(|| {
|
||||
let cnt = cnt.clone();
|
||||
|
||||
current_thread::spawn(lazy(move || {
|
||||
cnt.set(1 + cnt.get());
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
Ok::<_, ()>(())
|
||||
})).unwrap();
|
||||
|
||||
current_thread.run().unwrap();
|
||||
|
||||
assert_eq!(1, cnt.get());
|
||||
}
|
||||
|
||||
struct Never(Rc<()>);
|
||||
|
||||
impl Future for Never {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn outstanding_tasks_are_dropped_when_executor_is_dropped() {
|
||||
let mut rc = Rc::new(());
|
||||
|
||||
let mut current_thread = CurrentThread::new();
|
||||
current_thread.spawn(Never(rc.clone()));
|
||||
|
||||
drop(current_thread);
|
||||
|
||||
// Ensure the daemon is dropped
|
||||
assert!(Rc::get_mut(&mut rc).is_some());
|
||||
|
||||
// Using the global spawn fn
|
||||
|
||||
let mut rc = Rc::new(());
|
||||
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
current_thread.block_on(lazy(|| {
|
||||
current_thread::spawn(Never(rc.clone()));
|
||||
Ok::<_, ()>(())
|
||||
})).unwrap();
|
||||
|
||||
drop(current_thread);
|
||||
|
||||
// Ensure the daemon is dropped
|
||||
assert!(Rc::get_mut(&mut rc).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn nesting_run() {
|
||||
block_on_all(lazy(|| {
|
||||
block_on_all(lazy(|| {
|
||||
ok()
|
||||
})).unwrap();
|
||||
|
||||
ok()
|
||||
})).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn run_in_future() {
|
||||
block_on_all(lazy(|| {
|
||||
current_thread::spawn(lazy(|| {
|
||||
block_on_all(lazy(|| {
|
||||
ok()
|
||||
})).unwrap();
|
||||
ok()
|
||||
}));
|
||||
ok()
|
||||
})).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tick_on_infini_future() {
|
||||
let num = Rc::new(Cell::new(0));
|
||||
|
||||
struct Infini {
|
||||
num: Rc<Cell<usize>>,
|
||||
}
|
||||
|
||||
impl Future for Infini {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
self.num.set(1 + self.num.get());
|
||||
task::current().notify();
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
CurrentThread::new()
|
||||
.spawn(Infini {
|
||||
num: num.clone(),
|
||||
})
|
||||
.turn(None)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, num.get());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tasks_are_scheduled_fairly() {
|
||||
let state = Rc::new(RefCell::new([0, 0]));
|
||||
|
||||
struct Spin {
|
||||
state: Rc<RefCell<[i32; 2]>>,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl Future for Spin {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
let mut state = self.state.borrow_mut();
|
||||
|
||||
if self.idx == 0 {
|
||||
let diff = state[0] - state[1];
|
||||
|
||||
assert!(diff.abs() <= 1);
|
||||
|
||||
if state[0] >= 50 {
|
||||
return Ok(().into());
|
||||
}
|
||||
}
|
||||
|
||||
state[self.idx] += 1;
|
||||
|
||||
if state[self.idx] >= 100 {
|
||||
return Ok(().into());
|
||||
}
|
||||
|
||||
task::current().notify();
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
block_on_all(lazy(|| {
|
||||
current_thread::spawn(Spin {
|
||||
state: state.clone(),
|
||||
idx: 0,
|
||||
});
|
||||
|
||||
current_thread::spawn(Spin {
|
||||
state: state,
|
||||
idx: 1,
|
||||
});
|
||||
|
||||
ok()
|
||||
})).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_and_turn() {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
// Spawn a basic task to get the executor to turn
|
||||
current_thread.spawn(lazy(move || {
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
// Turn once...
|
||||
current_thread.turn(None).unwrap();
|
||||
|
||||
current_thread.spawn(lazy(move || {
|
||||
c.set(1 + c.get());
|
||||
|
||||
// Spawn!
|
||||
current_thread::spawn(lazy(move || {
|
||||
c.set(1 + c.get());
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
// This does not run the newly spawned thread
|
||||
current_thread.turn(None).unwrap();
|
||||
assert_eq!(1, cnt.get());
|
||||
|
||||
// This runs the newly spawned thread
|
||||
current_thread.turn(None).unwrap();
|
||||
assert_eq!(2, cnt.get());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_in_drop() {
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
current_thread.spawn({
|
||||
struct OnDrop<F: FnOnce()>(Option<F>);
|
||||
|
||||
impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||
fn drop(&mut self) {
|
||||
(self.0.take().unwrap())();
|
||||
}
|
||||
}
|
||||
|
||||
struct MyFuture {
|
||||
_data: Box<Any>,
|
||||
}
|
||||
|
||||
impl Future for MyFuture {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
MyFuture {
|
||||
_data: Box::new(OnDrop(Some(move || {
|
||||
current_thread::spawn(lazy(move || {
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
}));
|
||||
}))),
|
||||
}
|
||||
});
|
||||
|
||||
current_thread.block_on(rx).unwrap();
|
||||
current_thread.run().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hammer_turn() {
|
||||
use futures::sync::mpsc;
|
||||
|
||||
const ITER: usize = 100;
|
||||
const N: usize = 100;
|
||||
const THREADS: usize = 4;
|
||||
|
||||
for _ in 0..ITER {
|
||||
let mut ths = vec![];
|
||||
|
||||
// Add some jitter
|
||||
for _ in 0..THREADS {
|
||||
let th = thread::spawn(|| {
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
|
||||
current_thread.spawn({
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
rx.for_each(move |_| {
|
||||
c.set(1 + c.get());
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|e| panic!("err={:?}", e))
|
||||
.map(move |v| {
|
||||
assert_eq!(N, cnt.get());
|
||||
v
|
||||
})
|
||||
});
|
||||
|
||||
thread::spawn(move || {
|
||||
for _ in 0..N {
|
||||
tx.unbounded_send(()).unwrap();
|
||||
thread::yield_now();
|
||||
}
|
||||
});
|
||||
|
||||
while !current_thread.is_idle() {
|
||||
current_thread.turn(None).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
ths.push(th);
|
||||
}
|
||||
|
||||
for th in ths {
|
||||
th.join().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ok() -> future::FutureResult<(), ()> {
|
||||
future::ok(())
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
extern crate tokio;
|
||||
extern crate futures;
|
||||
|
||||
use std::thread;
|
||||
use std::net;
|
||||
|
||||
use futures::future;
|
||||
use futures::prelude::*;
|
||||
use futures::sync::oneshot;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::reactor::Reactor;
|
||||
|
||||
#[test]
|
||||
fn tcp_doesnt_block() {
|
||||
let core = Reactor::new().unwrap();
|
||||
let handle = core.handle();
|
||||
let listener = net::TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let listener = TcpListener::from_std(listener, &handle).unwrap();
|
||||
drop(core);
|
||||
assert!(listener.incoming().wait().next().unwrap().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_wakes() {
|
||||
let core = Reactor::new().unwrap();
|
||||
let handle = core.handle();
|
||||
let listener = net::TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let listener = TcpListener::from_std(listener, &handle).unwrap();
|
||||
let (tx, rx) = oneshot::channel::<()>();
|
||||
let t = thread::spawn(move || {
|
||||
let incoming = listener.incoming();
|
||||
let new_socket = incoming.into_future().map_err(|_| ());
|
||||
let drop_tx = future::lazy(|| {
|
||||
drop(tx);
|
||||
future::ok(())
|
||||
});
|
||||
assert!(new_socket.join(drop_tx).wait().is_err());
|
||||
});
|
||||
drop(rx.wait());
|
||||
drop(core);
|
||||
t.join().unwrap();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#![cfg(feature = "unstable-futures")]
|
||||
|
||||
// This test is the same as `echo.rs`, but ported to futures 0.2
|
||||
|
||||
extern crate env_logger;
|
||||
extern crate futures2;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use std::thread;
|
||||
|
||||
use futures2::prelude::*;
|
||||
use futures2::executor::block_on;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn echo_server() {
|
||||
drop(env_logger::init());
|
||||
|
||||
let srv = t!(TcpListener::bind(&t!("127.0.0.1:0".parse())));
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let msg = "foo bar baz";
|
||||
let t = thread::spawn(move || {
|
||||
let mut s = TcpStream::connect(&addr).unwrap();
|
||||
|
||||
for _i in 0..1024 {
|
||||
assert_eq!(t!(s.write(msg.as_bytes())), msg.len());
|
||||
let mut buf = [0; 1024];
|
||||
assert_eq!(t!(s.read(&mut buf)), msg.len());
|
||||
assert_eq!(&buf[..msg.len()], msg.as_bytes());
|
||||
}
|
||||
});
|
||||
|
||||
let clients = srv.incoming();
|
||||
let client = clients.next().map(|e| e.0.unwrap()).map_err(|e| e.0);
|
||||
let halves = client.map(|s| s.split());
|
||||
let copied = halves.and_then(|(a, b)| a.copy_into(b));
|
||||
|
||||
let (amt, _, _) = t!(block_on(copied));
|
||||
t.join().unwrap();
|
||||
|
||||
assert_eq!(amt, msg.len() as u64 * 1024);
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
extern crate env_logger;
|
||||
|
||||
use std::{io, thread};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
|
||||
use futures::prelude::*;
|
||||
use tokio::net::{TcpStream, TcpListener};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hammer_old() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let threads = (0..10).map(|_| {
|
||||
thread::spawn(|| {
|
||||
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
let addr = t!(srv.local_addr());
|
||||
let mine = TcpStream::connect(&addr);
|
||||
let theirs = srv.incoming().into_future()
|
||||
.map(|(s, _)| s.unwrap())
|
||||
.map_err(|(s, _)| s);
|
||||
let (mine, theirs) = t!(mine.join(theirs).wait());
|
||||
|
||||
assert_eq!(t!(mine.local_addr()), t!(theirs.peer_addr()));
|
||||
assert_eq!(t!(theirs.local_addr()), t!(mine.peer_addr()));
|
||||
})
|
||||
}).collect::<Vec<_>>();
|
||||
for thread in threads {
|
||||
thread.join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
struct Rd(Arc<TcpStream>);
|
||||
struct Wr(Arc<TcpStream>);
|
||||
|
||||
impl io::Read for Rd {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
<&TcpStream>::read(&mut &*self.0, dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl tokio_io::AsyncRead for Rd {
|
||||
}
|
||||
|
||||
impl io::Write for Wr {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
<&TcpStream>::write(&mut &*self.0, src)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl tokio_io::AsyncWrite for Wr {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hammer_split() {
|
||||
use tokio_io::io;
|
||||
|
||||
const N: usize = 100;
|
||||
const ITER: usize = 10;
|
||||
|
||||
let _ = env_logger::init();
|
||||
|
||||
for _ in 0..ITER {
|
||||
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let cnt = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
let mut rt = Runtime::new().unwrap();
|
||||
|
||||
fn split(socket: TcpStream, cnt: Arc<AtomicUsize>) {
|
||||
let socket = Arc::new(socket);
|
||||
let rd = Rd(socket.clone());
|
||||
let wr = Wr(socket);
|
||||
|
||||
let cnt2 = cnt.clone();
|
||||
|
||||
let rd = io::read(rd, vec![0; 1])
|
||||
.map(move |_| {
|
||||
cnt2.fetch_add(1, Relaxed);
|
||||
})
|
||||
.map_err(|e| panic!("read error = {:?}", e));
|
||||
|
||||
let wr = io::write_all(wr, b"1")
|
||||
.map(move |_| {
|
||||
cnt.fetch_add(1, Relaxed);
|
||||
})
|
||||
.map_err(move |e| panic!("write error = {:?}", e));
|
||||
|
||||
tokio::spawn(rd);
|
||||
tokio::spawn(wr);
|
||||
}
|
||||
|
||||
rt.spawn({
|
||||
let cnt = cnt.clone();
|
||||
srv.incoming()
|
||||
.map_err(|e| panic!("accept error = {:?}", e))
|
||||
.take(N as u64)
|
||||
.for_each(move |socket| {
|
||||
split(socket, cnt.clone());
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
for _ in 0..N {
|
||||
rt.spawn({
|
||||
let cnt = cnt.clone();
|
||||
TcpStream::connect(&addr)
|
||||
.map_err(move |e| panic!("connect error = {:?}", e))
|
||||
.map(move |socket| split(socket, cnt))
|
||||
});
|
||||
}
|
||||
|
||||
rt.shutdown_on_idle().wait().unwrap();
|
||||
assert_eq!(N * 4, cnt.load(Relaxed));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
#![cfg(feature = "unstable-futures")]
|
||||
|
||||
// This test is the same as `global.rs`, but ported to futures 0.2
|
||||
|
||||
extern crate futures;
|
||||
extern crate futures2;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
extern crate env_logger;
|
||||
|
||||
use std::{io, thread};
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures2::prelude::*;
|
||||
use futures2::executor::block_on;
|
||||
use futures2::task;
|
||||
|
||||
use tokio::net::{TcpStream, TcpListener};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hammer() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let threads = (0..10).map(|_| {
|
||||
thread::spawn(|| {
|
||||
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
let addr = t!(srv.local_addr());
|
||||
let mine = TcpStream::connect(&addr);
|
||||
let theirs = srv.incoming().next()
|
||||
.map(|(s, _)| s.unwrap())
|
||||
.map_err(|(s, _)| s);
|
||||
let (mine, theirs) = t!(block_on(mine.join(theirs)));
|
||||
|
||||
assert_eq!(t!(mine.local_addr()), t!(theirs.peer_addr()));
|
||||
assert_eq!(t!(theirs.local_addr()), t!(mine.peer_addr()));
|
||||
})
|
||||
}).collect::<Vec<_>>();
|
||||
for thread in threads {
|
||||
thread.join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
struct Rd(Arc<TcpStream>);
|
||||
struct Wr(Arc<TcpStream>);
|
||||
|
||||
impl AsyncRead for Rd {
|
||||
fn poll_read(&mut self, cx: &mut task::Context, dst: &mut [u8]) -> Poll<usize, io::Error> {
|
||||
<&TcpStream>::poll_read(&mut &*self.0, cx, dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Wr {
|
||||
fn poll_write(&mut self, cx: &mut task::Context, src: &[u8]) -> Poll<usize, io::Error> {
|
||||
<&TcpStream>::poll_write(&mut &*self.0, cx, src)
|
||||
}
|
||||
|
||||
fn poll_flush(&mut self, _cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
fn poll_close(&mut self, _cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hammer_split() {
|
||||
const N: usize = 100;
|
||||
|
||||
let _ = env_logger::init();
|
||||
|
||||
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let mut rt = Runtime::new().unwrap();
|
||||
|
||||
fn split(socket: TcpStream) {
|
||||
let socket = Arc::new(socket);
|
||||
let rd = Rd(socket.clone());
|
||||
let wr = Wr(socket);
|
||||
|
||||
let rd = rd.read(vec![0; 1])
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("read error = {:?}", e));
|
||||
|
||||
let wr = wr.write_all(b"1")
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("write error = {:?}", e));
|
||||
|
||||
tokio::spawn2(rd);
|
||||
tokio::spawn2(wr);
|
||||
}
|
||||
|
||||
rt.spawn2({
|
||||
srv.incoming()
|
||||
.map_err(|e| panic!("accept error = {:?}", e))
|
||||
.take(N as u64)
|
||||
.for_each(|socket| {
|
||||
split(socket);
|
||||
Ok(())
|
||||
})
|
||||
.map(|_| ())
|
||||
});
|
||||
|
||||
for _ in 0..N {
|
||||
rt.spawn2({
|
||||
TcpStream::connect(&addr)
|
||||
.map_err(|e| panic!("connect error = {:?}", e))
|
||||
.map(|socket| split(socket))
|
||||
});
|
||||
}
|
||||
|
||||
futures::Future::wait(rt.shutdown_on_idle()).unwrap();
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_threadpool;
|
||||
extern crate bytes;
|
||||
|
||||
use std::io;
|
||||
use std::net::Shutdown;
|
||||
|
||||
use bytes::{BytesMut, BufMut};
|
||||
use futures::{Future, Stream, Sink};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio_io::codec::{Encoder, Decoder};
|
||||
use tokio_io::io::{write_all, read};
|
||||
use tokio_io::AsyncRead;
|
||||
use tokio_threadpool::Builder;
|
||||
|
||||
pub struct LineCodec;
|
||||
|
||||
impl Decoder for LineCodec {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<BytesMut>, io::Error> {
|
||||
match buf.iter().position(|&b| b == b'\n') {
|
||||
Some(i) => Ok(Some(buf.split_to(i + 1).into())),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, buf: &mut BytesMut) -> io::Result<Option<BytesMut>> {
|
||||
if buf.len() == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
let amt = buf.len();
|
||||
Ok(Some(buf.split_to(amt)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder for LineCodec {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, item: BytesMut, into: &mut BytesMut) -> io::Result<()> {
|
||||
into.put(&item[..]);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn echo() {
|
||||
drop(env_logger::init());
|
||||
|
||||
let pool = Builder::new()
|
||||
.pool_size(1)
|
||||
.build();
|
||||
|
||||
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
let sender = pool.sender().clone();
|
||||
let srv = listener.incoming().for_each(move |socket| {
|
||||
let (sink, stream) = socket.framed(LineCodec).split();
|
||||
sender.spawn(sink.send_all(stream).map(|_| ()).map_err(|_| ())).unwrap();
|
||||
Ok(())
|
||||
});
|
||||
|
||||
pool.sender().spawn(srv.map_err(|e| panic!("srv error: {}", e))).unwrap();
|
||||
|
||||
let client = TcpStream::connect(&addr);
|
||||
let client = client.wait().unwrap();
|
||||
let (client, _) = write_all(client, b"a\n").wait().unwrap();
|
||||
let (client, buf, amt) = read(client, vec![0; 1024]).wait().unwrap();
|
||||
assert_eq!(amt, 2);
|
||||
assert_eq!(&buf[..2], b"a\n");
|
||||
|
||||
let (client, _) = write_all(client, b"\n").wait().unwrap();
|
||||
let (client, buf, amt) = read(client, buf).wait().unwrap();
|
||||
assert_eq!(amt, 1);
|
||||
assert_eq!(&buf[..1], b"\n");
|
||||
|
||||
let (client, _) = write_all(client, b"b").wait().unwrap();
|
||||
client.shutdown(Shutdown::Write).unwrap();
|
||||
let (_client, buf, amt) = read(client, buf).wait().unwrap();
|
||||
assert_eq!(amt, 1);
|
||||
assert_eq!(&buf[..1], b"b");
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#![cfg(unix)]
|
||||
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate libc;
|
||||
extern crate mio;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{self, Write};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use mio::event::Evented;
|
||||
use mio::unix::{UnixReady, EventedFd};
|
||||
use mio::{PollOpt, Ready, Token};
|
||||
use tokio::reactor::{Handle, PollEvented2};
|
||||
use tokio_io::io::read_to_end;
|
||||
use futures::Future;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
}
|
||||
|
||||
struct MyFile(File);
|
||||
|
||||
impl MyFile {
|
||||
fn new(file: File) -> MyFile {
|
||||
unsafe {
|
||||
let r = libc::fcntl(file.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
|
||||
assert!(r != -1, "fcntl error: {}", io::Error::last_os_error());
|
||||
}
|
||||
MyFile(file)
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for MyFile {
|
||||
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Evented for MyFile {
|
||||
fn register(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt)
|
||||
-> io::Result<()> {
|
||||
let hup: Ready = UnixReady::hup().into();
|
||||
EventedFd(&self.0.as_raw_fd()).register(poll, token, interest | hup, opts)
|
||||
}
|
||||
fn reregister(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt)
|
||||
-> io::Result<()> {
|
||||
let hup: Ready = UnixReady::hup().into();
|
||||
EventedFd(&self.0.as_raw_fd()).reregister(poll, token, interest | hup, opts)
|
||||
}
|
||||
fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
|
||||
EventedFd(&self.0.as_raw_fd()).deregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hup() {
|
||||
drop(env_logger::init());
|
||||
|
||||
let handle = Handle::default();
|
||||
unsafe {
|
||||
let mut pipes = [0; 2];
|
||||
assert!(libc::pipe(pipes.as_mut_ptr()) != -1,
|
||||
"pipe error: {}", io::Error::last_os_error());
|
||||
let read = File::from_raw_fd(pipes[0]);
|
||||
let mut write = File::from_raw_fd(pipes[1]);
|
||||
let t = thread::spawn(move || {
|
||||
write.write_all(b"Hello!\n").unwrap();
|
||||
write.write_all(b"Good bye!\n").unwrap();
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
});
|
||||
|
||||
let source = PollEvented2::new_with_handle(MyFile::new(read), &handle).unwrap();
|
||||
|
||||
let reader = read_to_end(source, Vec::new());
|
||||
let (_, content) = t!(reader.wait());
|
||||
assert_eq!(&b"Hello!\nGood bye!\n"[..], &content[..]);
|
||||
t.join().unwrap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
extern crate tokio;
|
||||
extern crate env_logger;
|
||||
|
||||
use tokio::io;
|
||||
use tokio::net::{TcpStream, TcpListener};
|
||||
use tokio::prelude::*;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_runtime_usage() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
tokio::run({
|
||||
let server = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
let addr = t!(server.local_addr());
|
||||
let client = TcpStream::connect(&addr);
|
||||
|
||||
let server = server.incoming().take(1)
|
||||
.map_err(|e| panic!("accept err = {:?}", e))
|
||||
.for_each(|socket| {
|
||||
tokio::spawn({
|
||||
io::write_all(socket, b"hello")
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("write err = {:?}", e))
|
||||
})
|
||||
})
|
||||
.map(|_| ());
|
||||
|
||||
let client = client
|
||||
.map_err(|e| panic!("connect err = {:?}", e))
|
||||
.and_then(|client| {
|
||||
// Read all
|
||||
io::read_to_end(client, vec![])
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("read err = {:?}", e))
|
||||
});
|
||||
|
||||
server.join(client)
|
||||
.map(|_| ())
|
||||
});
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
#![cfg(unix)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate libc;
|
||||
extern crate tokio_core;
|
||||
extern crate tokio_signal;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::Future;
|
||||
use futures::stream::Stream;
|
||||
use tokio_core::reactor::{Core, Timeout};
|
||||
use tokio_signal::unix::Signal;
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
let mut lp = Core::new().unwrap();
|
||||
let handle = lp.handle();
|
||||
let signal = lp.run(Signal::new(libc::SIGUSR1, &handle)).unwrap();
|
||||
unsafe {
|
||||
assert_eq!(libc::kill(libc::getpid(), libc::SIGUSR1), 0);
|
||||
}
|
||||
lp.run(signal.into_future()).ok().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notify_both() {
|
||||
let mut lp = Core::new().unwrap();
|
||||
let handle = lp.handle();
|
||||
let signal1 = lp.run(Signal::new(libc::SIGUSR2, &handle)).unwrap();
|
||||
let signal2 = lp.run(Signal::new(libc::SIGUSR2, &handle)).unwrap();
|
||||
unsafe {
|
||||
assert_eq!(libc::kill(libc::getpid(), libc::SIGUSR2), 0);
|
||||
}
|
||||
lp.run(signal1.into_future().join(signal2.into_future())).ok().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_then_get_a_signal() {
|
||||
let mut lp = Core::new().unwrap();
|
||||
let handle = lp.handle();
|
||||
let signal = lp.run(Signal::new(libc::SIGUSR1, &handle)).unwrap();
|
||||
drop(signal);
|
||||
unsafe {
|
||||
assert_eq!(libc::kill(libc::getpid(), libc::SIGUSR1), 0);
|
||||
}
|
||||
let timeout = Timeout::new(Duration::from_millis(1), &lp.handle()).unwrap();
|
||||
lp.run(timeout).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn twice() {
|
||||
let mut lp = Core::new().unwrap();
|
||||
let handle = lp.handle();
|
||||
let signal = lp.run(Signal::new(libc::SIGUSR1, &handle)).unwrap();
|
||||
unsafe {
|
||||
assert_eq!(libc::kill(libc::getpid(), libc::SIGUSR1), 0);
|
||||
}
|
||||
let (num, signal) = lp.run(signal.into_future()).ok().unwrap();
|
||||
assert_eq!(num, Some(libc::SIGUSR1));
|
||||
unsafe {
|
||||
assert_eq!(libc::kill(libc::getpid(), libc::SIGUSR1), 0);
|
||||
}
|
||||
lp.run(signal.into_future()).ok().unwrap();
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
#![cfg(feature = "unstable-futures")]
|
||||
|
||||
// This test is the same as `tcp.rs`, but ported to futures 0.2
|
||||
|
||||
extern crate env_logger;
|
||||
extern crate tokio;
|
||||
extern crate mio;
|
||||
extern crate futures2;
|
||||
|
||||
use std::{net, thread};
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use futures2::executor::block_on;
|
||||
use futures2::prelude::*;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn connect() {
|
||||
drop(env_logger::init());
|
||||
let srv = t!(net::TcpListener::bind("127.0.0.1:0"));
|
||||
let addr = t!(srv.local_addr());
|
||||
let t = thread::spawn(move || {
|
||||
t!(srv.accept()).0
|
||||
});
|
||||
|
||||
let stream = TcpStream::connect(&addr);
|
||||
let mine = t!(block_on(stream));
|
||||
let theirs = t.join().unwrap();
|
||||
|
||||
assert_eq!(t!(mine.local_addr()), t!(theirs.peer_addr()));
|
||||
assert_eq!(t!(theirs.local_addr()), t!(mine.peer_addr()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accept() {
|
||||
drop(env_logger::init());
|
||||
let srv = t!(TcpListener::bind(&t!("127.0.0.1:0".parse())));
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let client = srv.incoming().map(move |t| {
|
||||
tx.send(()).unwrap();
|
||||
t
|
||||
}).next().map_err(|e| e.0);
|
||||
assert!(rx.try_recv().is_err());
|
||||
let t = thread::spawn(move || {
|
||||
net::TcpStream::connect(&addr).unwrap()
|
||||
});
|
||||
|
||||
let (mine, _remaining) = t!(block_on(client));
|
||||
let mine = mine.unwrap();
|
||||
let theirs = t.join().unwrap();
|
||||
|
||||
assert_eq!(t!(mine.local_addr()), t!(theirs.peer_addr()));
|
||||
assert_eq!(t!(theirs.local_addr()), t!(mine.peer_addr()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accept2() {
|
||||
drop(env_logger::init());
|
||||
let srv = t!(TcpListener::bind(&t!("127.0.0.1:0".parse())));
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let t = thread::spawn(move || {
|
||||
net::TcpStream::connect(&addr).unwrap()
|
||||
});
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let client = srv.incoming().map(move |t| {
|
||||
tx.send(()).unwrap();
|
||||
t
|
||||
}).next().map_err(|e| e.0);
|
||||
assert!(rx.try_recv().is_err());
|
||||
|
||||
let (mine, _remaining) = t!(block_on(client));
|
||||
mine.unwrap();
|
||||
t.join().unwrap();
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
mod unix {
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
|
||||
use env_logger;
|
||||
use futures2::future;
|
||||
use futures2::executor::block_on;
|
||||
use futures2::io::AsyncRead;
|
||||
use mio::unix::UnixReady;
|
||||
|
||||
use std::{net, thread};
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn poll_hup() {
|
||||
drop(env_logger::init());
|
||||
|
||||
let srv = t!(net::TcpListener::bind("127.0.0.1:0"));
|
||||
let addr = t!(srv.local_addr());
|
||||
let t = thread::spawn(move || {
|
||||
let mut client = t!(srv.accept()).0;
|
||||
client.write(b"hello world").unwrap();
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
});
|
||||
|
||||
let mut stream = t!(block_on(TcpStream::connect(&addr)));
|
||||
|
||||
// Poll for HUP before reading.
|
||||
block_on(future::poll_fn(|cx| {
|
||||
stream.poll_read_ready2(cx, UnixReady::hup().into())
|
||||
})).unwrap();
|
||||
|
||||
// Same for write half
|
||||
block_on(future::poll_fn(|cx| {
|
||||
stream.poll_write_ready2(cx)
|
||||
})).unwrap();
|
||||
|
||||
let mut buf = vec![0; 11];
|
||||
|
||||
// Read the data
|
||||
block_on(future::poll_fn(|cx| {
|
||||
stream.poll_read(cx, &mut buf)
|
||||
})).unwrap();
|
||||
|
||||
assert_eq!(b"hello world", &buf[..]);
|
||||
|
||||
t.join().unwrap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
extern crate env_logger;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use tokio::timer::*;
|
||||
|
||||
use std::sync::mpsc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[test]
|
||||
fn timer_with_runtime() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let when = Instant::now() + Duration::from_millis(100);
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
tokio::run({
|
||||
Delay::new(when)
|
||||
.map_err(|e| panic!("unexpected error; err={:?}", e))
|
||||
.and_then(move |_| {
|
||||
assert!(Instant::now() >= when);
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn starving() {
|
||||
use futures::{task, Poll, Async};
|
||||
|
||||
let _ = env_logger::init();
|
||||
|
||||
struct Starve(Delay, u64);
|
||||
|
||||
impl Future for Starve {
|
||||
type Item = u64;
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, ()> {
|
||||
if self.0.poll().unwrap().is_ready() {
|
||||
return Ok(self.1.into());
|
||||
}
|
||||
|
||||
self.1 += 1;
|
||||
|
||||
task::current().notify();
|
||||
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
let when = Instant::now() + Duration::from_millis(20);
|
||||
let starve = Starve(Delay::new(when), 0);
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
tokio::run({
|
||||
starve
|
||||
.and_then(move |_ticks| {
|
||||
assert!(Instant::now() >= when);
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deadline() {
|
||||
use futures::future;
|
||||
|
||||
let _ = env_logger::init();
|
||||
|
||||
let when = Instant::now() + Duration::from_millis(20);
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
tokio::run({
|
||||
future::empty::<(), ()>()
|
||||
.deadline(when)
|
||||
.then(move |res| {
|
||||
assert!(res.is_err());
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# 0.1.2 (March 30, 2018)
|
||||
|
||||
* Implement `Unpark` for `Box<Unpark>`.
|
||||
|
||||
# 0.1.1 (March 22, 2018)
|
||||
|
||||
* Optionally support futures 0.2.
|
||||
|
||||
# 0.1.0 (March 09, 2018)
|
||||
|
||||
* Initial release
|
||||
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "tokio-executor"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.2"
|
||||
documentation = "https://docs.rs/tokio-executor"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://github.com/tokio-rs/tokio"
|
||||
license = "MIT"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
description = """
|
||||
Future execution primitives
|
||||
"""
|
||||
keywords = ["futures", "tokio"]
|
||||
categories = ["concurrency", "asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.19"
|
||||
|
||||
# Futures 0.2 integration
|
||||
futures2 = { version = "0.1.0", path = "../futures2", optional = true }
|
||||
|
||||
[features]
|
||||
unstable-futures = ["futures2"]
|
||||
default = []
|
||||
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2018 Tokio Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,47 @@
|
||||
# tokio-executor
|
||||
|
||||
Task execution related traits and utilities.
|
||||
|
||||
[Documentation](https://tokio-rs.github.io/tokio/tokio_executor/)
|
||||
|
||||
## Overview
|
||||
|
||||
In the Tokio execution model, futures are lazy. When a future is created, no
|
||||
work is performed. In order for the work defined by the future to happen, the
|
||||
future must be submitted to an executor. A future that is submitted to an
|
||||
executor is called a "task".
|
||||
|
||||
The executor is responsible for ensuring that [`Future::poll`] is called
|
||||
whenever the task is [notified]. Notification happens when the internal state of
|
||||
a task transitions from "not ready" to ready. For example, a socket might have
|
||||
received data and a call to `read` will now be able to succeed.
|
||||
|
||||
This crate provides traits and utilities that are necessary for building an
|
||||
executor, including:
|
||||
|
||||
* The [`Executor`] trait describes the API for spawning a future onto an
|
||||
executor.
|
||||
|
||||
* [`enter`] marks that the the current thread is entering an execution
|
||||
context. This prevents a second executor from accidentally starting from
|
||||
within the context of one that is already running.
|
||||
|
||||
* [`DefaultExecutor`] spawns tasks onto the default executor for the current
|
||||
context.
|
||||
|
||||
* [`Park`] abstracts over blocking and unblocking the current thread.
|
||||
|
||||
[`Executor`]: https://tokio-rs.github.io/tokio/tokio_executor/trait.Executor.html
|
||||
[`enter`]: https://tokio-rs.github.io/tokio/tokio_executor/fn.enter.html
|
||||
[`DefaultExecutor`]: https://tokio-rs.github.io/tokio/tokio_executor/struct.DefaultExecutor.html
|
||||
[`Park`]: https://tokio-rs.github.io/tokio/tokio_executor/park/index.html
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE).
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
|
||||
terms or conditions.
|
||||
@@ -0,0 +1,106 @@
|
||||
use std::prelude::v1::*;
|
||||
use std::cell::Cell;
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
thread_local!(static ENTERED: Cell<bool> = Cell::new(false));
|
||||
|
||||
/// Represents an executor context.
|
||||
///
|
||||
/// For more details, see [`enter` documentation](fn.enter.html)
|
||||
pub struct Enter {
|
||||
on_exit: Vec<Box<Callback>>,
|
||||
permanent: bool,
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
_enter2: futures2::executor::Enter,
|
||||
}
|
||||
|
||||
/// An error returned by `enter` if an execution scope has already been
|
||||
/// entered.
|
||||
#[derive(Debug)]
|
||||
pub struct EnterError {
|
||||
_a: (),
|
||||
}
|
||||
|
||||
/// Marks the current thread as being within the dynamic extent of an
|
||||
/// executor.
|
||||
///
|
||||
/// Executor implementations should call this function before blocking the
|
||||
/// thread. If `None` is returned, the executor should fail by panicking or
|
||||
/// taking some other action without blocking the current thread. This prevents
|
||||
/// deadlocks due to multiple executors competing for the same thread.
|
||||
///
|
||||
/// # Error
|
||||
///
|
||||
/// Returns an error if the current thread is already marked
|
||||
pub fn enter() -> Result<Enter, EnterError> {
|
||||
ENTERED.with(|c| {
|
||||
if c.get() {
|
||||
Err(EnterError { _a: () })
|
||||
} else {
|
||||
c.set(true);
|
||||
|
||||
Ok(Enter {
|
||||
on_exit: Vec::new(),
|
||||
permanent: false,
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
_enter2: futures2::executor::enter().unwrap(),
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
impl Enter {
|
||||
/// Register a callback to be invoked if and when the thread
|
||||
/// ceased to act as an executor.
|
||||
pub fn on_exit<F>(&mut self, f: F) where F: FnOnce() + 'static {
|
||||
self.on_exit.push(Box::new(f));
|
||||
}
|
||||
|
||||
/// Treat the remainder of execution on this thread as part of an
|
||||
/// executor; used mostly for thread pool worker threads.
|
||||
///
|
||||
/// All registered `on_exit` callbacks are *dropped* without being
|
||||
/// invoked.
|
||||
pub fn make_permanent(mut self) {
|
||||
self.permanent = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Enter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Enter").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Enter {
|
||||
fn drop(&mut self) {
|
||||
ENTERED.with(|c| {
|
||||
assert!(c.get());
|
||||
|
||||
if self.permanent {
|
||||
return
|
||||
}
|
||||
|
||||
for callback in self.on_exit.drain(..) {
|
||||
callback.call();
|
||||
}
|
||||
|
||||
c.set(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
trait Callback: 'static {
|
||||
fn call(self: Box<Self>);
|
||||
}
|
||||
|
||||
impl<F: FnOnce() + 'static> Callback for F {
|
||||
fn call(self: Box<Self>) {
|
||||
(*self)()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
use super::{Executor, Enter, SpawnError};
|
||||
|
||||
use futures::Future;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
/// Executes futures on the default executor for the current execution context.
|
||||
///
|
||||
/// `DefaultExecutor` implements `Executor` and can be used to spawn futures
|
||||
/// without referencing a specific executor.
|
||||
///
|
||||
/// When an executor starts, it sets the `DefaultExecutor` handle to point to an
|
||||
/// executor (usually itself) that is used to spawn new tasks.
|
||||
///
|
||||
/// The current `DefaultExecutor` reference is tracked using a thread-local
|
||||
/// variable and is set using `tokio_executor::with_default`
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DefaultExecutor {
|
||||
// Prevent the handle from moving across threads.
|
||||
_p: PhantomData<Rc<()>>,
|
||||
}
|
||||
|
||||
impl DefaultExecutor {
|
||||
/// Returns a handle to the default executor for the current context.
|
||||
///
|
||||
/// Futures may be spawned onto the default executor using this handle.
|
||||
///
|
||||
/// The returned handle will reference whichever executor is configured as
|
||||
/// the default **at the time `spawn` is called**. This enables
|
||||
/// `DefaultExecutor::current()` to be called before an execution context is
|
||||
/// setup, then passed **into** an execution context before it is used.
|
||||
pub fn current() -> DefaultExecutor {
|
||||
DefaultExecutor {
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread-local tracking the current executor
|
||||
thread_local!(static EXECUTOR: Cell<Option<*mut Executor>> = Cell::new(None));
|
||||
|
||||
// ===== impl DefaultExecutor =====
|
||||
|
||||
impl super::Executor for DefaultExecutor {
|
||||
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
|
||||
-> Result<(), SpawnError>
|
||||
{
|
||||
EXECUTOR.with(|current_executor| {
|
||||
match current_executor.get() {
|
||||
Some(executor) => {
|
||||
let executor = unsafe { &mut *executor };
|
||||
executor.spawn(future)
|
||||
}
|
||||
None => {
|
||||
Err(SpawnError::shutdown())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
fn spawn2(&mut self, future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
|
||||
-> Result<(), futures2::executor::SpawnError>
|
||||
{
|
||||
EXECUTOR.with(|current_executor| {
|
||||
match current_executor.get() {
|
||||
Some(executor) => {
|
||||
let executor = unsafe { &mut *executor };
|
||||
executor.spawn2(future)
|
||||
}
|
||||
None => {
|
||||
Err(futures2::executor::SpawnError::shutdown())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ===== global spawn fns =====
|
||||
|
||||
/// Submits a future for execution on the default executor -- usually a
|
||||
/// threadpool.
|
||||
///
|
||||
/// Futures are lazy constructs. When they are defined, no work happens. In
|
||||
/// order for the logic defined by the future to be run, the future must be
|
||||
/// spawned on an executor. This function is the easiest way to do so.
|
||||
///
|
||||
/// This function must be called from an execution context, i.e. from a future
|
||||
/// that has been already spawned onto an executor.
|
||||
///
|
||||
/// Once spawned, the future will execute. The details of how that happens is
|
||||
/// left up to the executor instance. If the executor is a thread pool, the
|
||||
/// future will be pushed onto a queue that a worker thread polls from. If the
|
||||
/// executor is a "current thread" executor, the future might be polled
|
||||
/// immediately from within the call to `spawn` or it might be pushed onto an
|
||||
/// internal queue.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the default executor is not set or if spawning
|
||||
/// onto the default executor returns an error. To avoid the panic, use the
|
||||
/// `DefaultExecutor` handle directly.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio_executor;
|
||||
/// # use tokio_executor::spawn;
|
||||
/// # pub fn dox() {
|
||||
/// use futures::future::lazy;
|
||||
///
|
||||
/// spawn(lazy(|| {
|
||||
/// println!("running on the default executor");
|
||||
/// Ok(())
|
||||
/// }));
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn spawn<T>(future: T)
|
||||
where T: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
DefaultExecutor::current().spawn(Box::new(future))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Like `spawn` but compatible with futures 0.2
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
pub fn spawn2<T>(future: T)
|
||||
where T: futures2::Future<Item = (), Error = futures2::Never> + Send + 'static,
|
||||
{
|
||||
DefaultExecutor::current().spawn2(Box::new(future))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Set the default executor for the duration of the closure
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if there already is a default executor set.
|
||||
pub fn with_default<T, F, R>(executor: &mut T, enter: &mut Enter, f: F) -> R
|
||||
where T: Executor,
|
||||
F: FnOnce(&mut Enter) -> R
|
||||
{
|
||||
EXECUTOR.with(|cell| {
|
||||
assert!(cell.get().is_none(), "default executor already set for execution context");
|
||||
|
||||
// Ensure that the executor is removed from the thread-local context
|
||||
// when leaving the scope. This handles cases that involve panicking.
|
||||
struct Reset<'a>(&'a Cell<Option<*mut Executor>>);
|
||||
|
||||
impl<'a> Drop for Reset<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.0.set(None);
|
||||
}
|
||||
}
|
||||
|
||||
let _reset = Reset(cell);
|
||||
|
||||
// While scary, this is safe. The function takes a
|
||||
// `&mut Executor`, which guarantees that the reference lives for the
|
||||
// duration of `with_default`.
|
||||
//
|
||||
// Because we are always clearing the TLS value at the end of the
|
||||
// function, we can cast the reference to 'static which thread-local
|
||||
// cells require.
|
||||
let executor = unsafe { hide_lt(executor as &mut _ as *mut _) };
|
||||
|
||||
cell.set(Some(executor));
|
||||
|
||||
f(enter)
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn hide_lt<'a>(p: *mut (Executor + 'a)) -> *mut (Executor + 'static) {
|
||||
use std::mem;
|
||||
mem::transmute(p)
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
//! Task execution related traits and utilities.
|
||||
//!
|
||||
//! In the Tokio execution model, futures are lazy. When a future is created, no
|
||||
//! work is performed. In order for the work defined by the future to happen,
|
||||
//! the future must be submitted to an executor. A future that is submitted to
|
||||
//! an executor is called a "task".
|
||||
//!
|
||||
//! The executor is responsible for ensuring that [`Future::poll`] is called
|
||||
//! whenever the task is [notified]. Notification happens when the internal
|
||||
//! state of a task transitions from "not ready" to ready. For example, a socket
|
||||
//! might have received data and a call to `read` will now be able to succeed.
|
||||
//!
|
||||
//! This crate provides traits and utilities that are necessary for building an
|
||||
//! executor, including:
|
||||
//!
|
||||
//! * The [`Executor`] trait describes the API for spawning a future onto an
|
||||
//! executor.
|
||||
//!
|
||||
//! * [`enter`] marks that the the current thread is entering an execution
|
||||
//! context. This prevents a second executor from accidentally starting from
|
||||
//! within the context of one that is already running.
|
||||
//!
|
||||
//! * [`DefaultExecutor`] spawns tasks onto the default executor for the current
|
||||
//! context.
|
||||
//!
|
||||
//! * [`Park`] abstracts over blocking and unblocking the current thread.
|
||||
//!
|
||||
//! [`Executor`]: trait.Executor.html
|
||||
//! [`enter`]: fn.enter.html
|
||||
//! [`DefaultExecutor`]: struct.DefaultExecutor.html
|
||||
//! [`Park`]: park/index.html
|
||||
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-executor/0.1.2")]
|
||||
|
||||
extern crate futures;
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
extern crate futures2;
|
||||
|
||||
mod enter;
|
||||
mod global;
|
||||
pub mod park;
|
||||
|
||||
pub use enter::{enter, Enter, EnterError};
|
||||
pub use global::{spawn, with_default, DefaultExecutor};
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
pub use global::spawn2;
|
||||
|
||||
use futures::Future;
|
||||
|
||||
/// A value that executes futures.
|
||||
///
|
||||
/// The [`spawn`] function is used to submit a future to an executor. Once
|
||||
/// submitted, the executor takes ownership of the future and becomes
|
||||
/// responsible for driving the future to completion.
|
||||
///
|
||||
/// The strategy employed by the executor to handle the future is less defined
|
||||
/// and is left up to the `Executor` implementation. The `Executor` instance is
|
||||
/// expected to call [`poll`] on the future once it has been notified, however
|
||||
/// the "when" and "how" can vary greatly.
|
||||
///
|
||||
/// For example, the executor might be a thread pool, in which case a set of
|
||||
/// threads have already been spawned up and the future is inserted into a
|
||||
/// queue. A thread will acquire the future and poll it.
|
||||
///
|
||||
/// The `Executor` trait is only for futures that **are** `Send`. These are most
|
||||
/// common. There currently is no trait that describes executors that operate
|
||||
/// entirely on the current thread (i.e., are able to spawn futures that are not
|
||||
/// `Send`). Note that single threaded executors can still implement `Executor`,
|
||||
/// but only futures that are `Send` can be spawned via the trait.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// The [`spawn`] function returns `Result` with an error type of `SpawnError`.
|
||||
/// This error type represents the reason that the executor was unable to spawn
|
||||
/// the future. The two current represented scenarios are:
|
||||
///
|
||||
/// * An executor being at capacity or full. As such, the executor is not able
|
||||
/// to accept a new future. This error state is expected to be transient.
|
||||
/// * An executor has been shutdown and can no longer accept new futures. This
|
||||
/// error state is expected to be permanent.
|
||||
///
|
||||
/// If a caller encounters an at capacity error, the caller should try to shed
|
||||
/// load. This can be as simple as dropping the future that was spawned.
|
||||
///
|
||||
/// If the caller encounters a shutdown error, the caller should attempt to
|
||||
/// gracefully shutdown.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio_executor;
|
||||
/// # use tokio_executor::Executor;
|
||||
/// # fn docs(my_executor: &mut Executor) {
|
||||
/// use futures::future::lazy;
|
||||
/// my_executor.spawn(Box::new(lazy(|| {
|
||||
/// println!("running on the executor");
|
||||
/// Ok(())
|
||||
/// }))).unwrap();
|
||||
/// # }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// [`spawn`]: #tymethod.spawn
|
||||
/// [`poll`]: https://docs.rs/futures/0.1/futures/future/trait.Future.html#tymethod.poll
|
||||
pub trait Executor {
|
||||
/// Spawns a future object to run on this executor.
|
||||
///
|
||||
/// `future` is passed to the executor, which will begin running it. The
|
||||
/// future may run on the current thread or another thread at the discretion
|
||||
/// of the `Executor` implementation.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Implementors are encouraged to avoid panics. However, a panic is
|
||||
/// permitted and the caller should check the implementation specific
|
||||
/// documentation for more details on possible panics.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio_executor;
|
||||
/// # use tokio_executor::Executor;
|
||||
/// # fn docs(my_executor: &mut Executor) {
|
||||
/// use futures::future::lazy;
|
||||
/// my_executor.spawn(Box::new(lazy(|| {
|
||||
/// println!("running on the executor");
|
||||
/// Ok(())
|
||||
/// }))).unwrap();
|
||||
/// # }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
|
||||
-> Result<(), SpawnError>;
|
||||
|
||||
/// Like `spawn`, but compatible with futures 0.2
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
fn spawn2(&mut self, future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
|
||||
-> Result<(), futures2::executor::SpawnError>;
|
||||
|
||||
/// Provides a best effort **hint** to whether or not `spawn` will succeed.
|
||||
///
|
||||
/// This function may return both false positives **and** false negatives.
|
||||
/// If `status` returns `Ok`, then a call to `spawn` will *probably*
|
||||
/// succeed, but may fail. If `status` returns `Err`, a call to `spawn` will
|
||||
/// *probably* fail, but may succeed.
|
||||
///
|
||||
/// This allows a caller to avoid creating the task if the call to `spawn`
|
||||
/// has a high likelihood of failing.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function must not panic. Implementors must ensure that panics do
|
||||
/// not happen.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio_executor;
|
||||
/// # use tokio_executor::Executor;
|
||||
/// # fn docs(my_executor: &mut Executor) {
|
||||
/// use futures::future::lazy;
|
||||
///
|
||||
/// if my_executor.status().is_ok() {
|
||||
/// my_executor.spawn(Box::new(lazy(|| {
|
||||
/// println!("running on the executor");
|
||||
/// Ok(())
|
||||
/// }))).unwrap();
|
||||
/// } else {
|
||||
/// println!("the executor is not in a good state");
|
||||
/// }
|
||||
/// # }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
fn status(&self) -> Result<(), SpawnError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors returned by `Executor::spawn`.
|
||||
///
|
||||
/// Spawn errors should represent relatively rare scenarios. Currently, the two
|
||||
/// scenarios represented by `SpawnError` are:
|
||||
///
|
||||
/// * An executor being at capacity or full. As such, the executor is not able
|
||||
/// to accept a new future. This error state is expected to be transient.
|
||||
/// * An executor has been shutdown and can no longer accept new futures. This
|
||||
/// error state is expected to be permanent.
|
||||
#[derive(Debug)]
|
||||
pub struct SpawnError {
|
||||
is_shutdown: bool,
|
||||
}
|
||||
|
||||
impl SpawnError {
|
||||
/// Return a new `SpawnError` reflecting a shutdown executor failure.
|
||||
pub fn shutdown() -> Self {
|
||||
SpawnError { is_shutdown: true }
|
||||
}
|
||||
|
||||
/// Return a new `SpawnError` reflecting an executor at capacity failure.
|
||||
pub fn at_capacity() -> Self {
|
||||
SpawnError { is_shutdown: false }
|
||||
}
|
||||
|
||||
/// Returns `true` if the error reflects a shutdown executor failure.
|
||||
pub fn is_shutdown(&self) -> bool {
|
||||
self.is_shutdown
|
||||
}
|
||||
|
||||
/// Returns `true` if the error reflects an executor at capacity failure.
|
||||
pub fn is_at_capacity(&self) -> bool {
|
||||
!self.is_shutdown
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
//! Abstraction over blocking and unblocking the current thread.
|
||||
//!
|
||||
//! Provides an abstraction over blocking the current thread. This is similar to
|
||||
//! the park / unpark constructs provided by [`std`] but made generic. This
|
||||
//! allows embedding custom functionality to perform when the thread is blocked.
|
||||
//!
|
||||
//! A blocked [`Park`][p] instance is unblocked by calling [`unpark`] on its
|
||||
//! [`Unpark`][up] handle.
|
||||
//!
|
||||
//! The [`ParkThread`] struct implements [`Park`][p] using
|
||||
//! [`thread::park`][`std`] to put the thread to sleep. The Tokio reactor also
|
||||
//! implements park, but uses [`mio::Poll`][mio] to block the thread instead.
|
||||
//!
|
||||
//! The [`Park`][p] trait is composable. A timer implementation might decorate a
|
||||
//! [`Park`][p] implementation by checking if any timeouts have elapsed after
|
||||
//! the inner [`Park`][p] implementation unblocks.
|
||||
//!
|
||||
//! # Model
|
||||
//!
|
||||
//! Conceptually, each [`Park`][p] instance has an associated token, which is
|
||||
//! initially not present:
|
||||
//!
|
||||
//! * The [`park`] method blocks the current thread unless or until the token
|
||||
//! is available, at which point it atomically consumes the token.
|
||||
//! * The [`unpark`] method atomically makes the token available if it wasn't
|
||||
//! already.
|
||||
//!
|
||||
//! Some things to note:
|
||||
//!
|
||||
//! * If [`unpark`] is called before [`park`], the next call to [`park`] will
|
||||
//! **not** block the thread.
|
||||
//! * **Spurious** wakeups are permited, i.e., the [`park`] method may unblock
|
||||
//! even if [`unpark`] was not called.
|
||||
//! * [`park_timeout`] does the same as [`park`] but allows specifying a maximum
|
||||
//! time to block the thread for.
|
||||
//!
|
||||
//! [`std`]: https://doc.rust-lang.org/std/thread/fn.park.html
|
||||
//! [`thread::park`]: https://doc.rust-lang.org/std/thread/fn.park.html
|
||||
//! [`ParkThread`]: struct.ParkThread.html
|
||||
//! [p]: trait.Park.html
|
||||
//! [`park`]: trait.Park.html#tymethod.park
|
||||
//! [`park_timeout`]: trait.Park.html#tymethod.park_timeout
|
||||
//! [`unpark`]: trait.Unpark.html#tymethod.unpark
|
||||
//! [up]: trait.Unpark.html
|
||||
//! [mio]: https://docs.rs/mio/0.6.13/mio/struct.Poll.html
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex, Condvar};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
/// Block the current thread.
|
||||
///
|
||||
/// See [module documentation][mod] for more details.
|
||||
///
|
||||
/// [mod]: ../index.html
|
||||
pub trait Park {
|
||||
/// Unpark handle type for the `Park` implementation.
|
||||
type Unpark: Unpark;
|
||||
|
||||
/// Error returned by `park`
|
||||
type Error;
|
||||
|
||||
/// Get a new `Unpark` handle associated with this `Park` instance.
|
||||
fn unpark(&self) -> Self::Unpark;
|
||||
|
||||
/// Block the current thread unless or until the token is available.
|
||||
///
|
||||
/// A call to `park` does not guarantee that the thread will remain blocked
|
||||
/// forever, and callers should be prepared for this possibility. This
|
||||
/// function may wakeup spuriously for any reason.
|
||||
///
|
||||
/// See [module documentation][mod] for more details.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function **should** not panic, but ultimiately, panics are left as
|
||||
/// an implementation detail. Refer to the documentation for the specific
|
||||
/// `Park` implementation
|
||||
///
|
||||
/// [mod]: ../index.html
|
||||
fn park(&mut self) -> Result<(), Self::Error>;
|
||||
|
||||
/// Park the current thread for at most `duration`.
|
||||
///
|
||||
/// This function is the same as `park` but allows specifying a maximum time
|
||||
/// to block the thread for.
|
||||
///
|
||||
/// Same as `park`, there is no guarantee that the thread will remain
|
||||
/// blocked for any amount of time. Spurious wakeups are permitted for any
|
||||
/// reason.
|
||||
///
|
||||
/// See [module documentation][mod] for more details.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function **should** not panic, but ultimiately, panics are left as
|
||||
/// an implementation detail. Refer to the documentation for the specific
|
||||
/// `Park` implementation
|
||||
///
|
||||
/// [mod]: ../index.html
|
||||
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Unblock a thread blocked by the associated [`Park`] instance.
|
||||
///
|
||||
/// See [module documentation][mod] for more details.
|
||||
///
|
||||
/// [mod]: ../index.html
|
||||
/// [`Park`]: trait.Park.html
|
||||
pub trait Unpark: Sync + Send + 'static {
|
||||
/// Unblock a thread that is blocked by the associated `Park` handle.
|
||||
///
|
||||
/// Calling `unpark` atomically makes available the unpark token, if it is
|
||||
/// not already available.
|
||||
///
|
||||
/// See [module documentation][mod] for more details.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function **should** not panic, but ultimiately, panics are left as
|
||||
/// an implementation detail. Refer to the documentation for the specific
|
||||
/// `Unpark` implementation
|
||||
///
|
||||
/// [mod]: ../index.html
|
||||
fn unpark(&self);
|
||||
}
|
||||
|
||||
impl Unpark for Box<Unpark> {
|
||||
fn unpark(&self) {
|
||||
(**self).unpark()
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks the current thread using a condition variable.
|
||||
///
|
||||
/// Implements the [`Park`] functionality by using a condition variable. An
|
||||
/// atomic variable is also used to avoid using the condition variable if
|
||||
/// possible.
|
||||
///
|
||||
/// The condition variable is cached in a thread-local variable and is shared
|
||||
/// across all `ParkThread` instances created on the same thread. This also
|
||||
/// means that an instance of `ParkThread` might be unblocked by a handle
|
||||
/// associated with a different `ParkThread` instance.
|
||||
#[derive(Debug)]
|
||||
pub struct ParkThread {
|
||||
_anchor: PhantomData<Rc<()>>,
|
||||
}
|
||||
|
||||
/// Error returned by [`ParkThread`]
|
||||
///
|
||||
/// This currently is never returned, but might at some point in the future.
|
||||
///
|
||||
/// [`ParkThread`]: struct.ParkThread.html
|
||||
#[derive(Debug)]
|
||||
pub struct ParkError {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
/// Unblocks a thread that was blocked by `ParkThread`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UnparkThread {
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
state: AtomicUsize,
|
||||
mutex: Mutex<()>,
|
||||
condvar: Condvar,
|
||||
}
|
||||
|
||||
const IDLE: usize = 0;
|
||||
const NOTIFY: usize = 1;
|
||||
const SLEEP: usize = 2;
|
||||
|
||||
thread_local! {
|
||||
static CURRENT_PARK_THREAD: Arc<Inner> = Arc::new(Inner {
|
||||
state: AtomicUsize::new(IDLE),
|
||||
mutex: Mutex::new(()),
|
||||
condvar: Condvar::new(),
|
||||
});
|
||||
}
|
||||
|
||||
// ===== impl ParkThread =====
|
||||
|
||||
impl ParkThread {
|
||||
/// Create a new `ParkThread` handle for the current thread.
|
||||
///
|
||||
/// This type cannot be moved to other threads, so it should be created on
|
||||
/// the thread that the caller intends to park.
|
||||
pub fn new() -> ParkThread {
|
||||
ParkThread {
|
||||
_anchor: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the `ParkThread` handle for this thread.
|
||||
fn with_current<F, R>(&self, f: F) -> R
|
||||
where F: FnOnce(&Arc<Inner>) -> R,
|
||||
{
|
||||
CURRENT_PARK_THREAD.with(|inner| f(inner))
|
||||
}
|
||||
}
|
||||
|
||||
impl Park for ParkThread {
|
||||
type Unpark = UnparkThread;
|
||||
type Error = ParkError;
|
||||
|
||||
fn unpark(&self) -> Self::Unpark {
|
||||
let inner = self.with_current(|inner| inner.clone());
|
||||
UnparkThread { inner }
|
||||
}
|
||||
|
||||
fn park(&mut self) -> Result<(), Self::Error> {
|
||||
self.with_current(|inner| inner.park(None))
|
||||
}
|
||||
|
||||
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> {
|
||||
self.with_current(|inner| inner.park(Some(duration)))
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl UnparkThread =====
|
||||
|
||||
impl Unpark for UnparkThread {
|
||||
fn unpark(&self) {
|
||||
self.inner.unpark();
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Inner =====
|
||||
|
||||
impl Inner {
|
||||
/// Park the current thread for at most `dur`.
|
||||
fn park(&self, timeout: Option<Duration>) -> Result<(), ParkError> {
|
||||
// If currently notified, then we skip sleeping. This is checked outside
|
||||
// of the lock to avoid acquiring a mutex if not necessary.
|
||||
match self.state.compare_and_swap(NOTIFY, IDLE, Ordering::SeqCst) {
|
||||
NOTIFY => return Ok(()),
|
||||
IDLE => {},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
// The state is currently idle, so obtain the lock and then try to
|
||||
// transition to a sleeping state.
|
||||
let mut m = self.mutex.lock().unwrap();
|
||||
|
||||
// Transition to sleeping
|
||||
match self.state.compare_and_swap(IDLE, SLEEP, Ordering::SeqCst) {
|
||||
NOTIFY => {
|
||||
// Notified before we could sleep, consume the notification and
|
||||
// exit
|
||||
self.state.store(IDLE, Ordering::SeqCst);
|
||||
return Ok(());
|
||||
}
|
||||
IDLE => {},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
m = match timeout {
|
||||
Some(timeout) => self.condvar.wait_timeout(m, timeout).unwrap().0,
|
||||
None => self.condvar.wait(m).unwrap(),
|
||||
};
|
||||
|
||||
// Transition back to idle. If the state has transitione dto `NOTIFY`,
|
||||
// this will consume that notification
|
||||
self.state.store(IDLE, Ordering::SeqCst);
|
||||
|
||||
// Explicitly drop the mutex guard. There is no real point in doing it
|
||||
// except that I find it helpful to make it explicit where we want the
|
||||
// mutex to unlock.
|
||||
drop(m);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unpark(&self) {
|
||||
// First, try transitioning from IDLE -> NOTIFY, this does not require a
|
||||
// lock.
|
||||
match self.state.compare_and_swap(IDLE, NOTIFY, Ordering::SeqCst) {
|
||||
IDLE | NOTIFY => return,
|
||||
SLEEP => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
// The other half is sleeping, this requires a lock
|
||||
let _m = self.mutex.lock().unwrap();
|
||||
|
||||
// Transition from SLEEP -> NOTIFY
|
||||
match self.state.compare_and_swap(SLEEP, NOTIFY, Ordering::SeqCst) {
|
||||
SLEEP => {}
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// Wakeup the sleeper
|
||||
self.condvar.notify_one();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
extern crate tokio_executor;
|
||||
extern crate futures;
|
||||
|
||||
use tokio_executor::*;
|
||||
use futures::future::lazy;
|
||||
|
||||
#[test]
|
||||
fn spawn_out_of_executor_context() {
|
||||
let res = DefaultExecutor::current().spawn(Box::new(lazy(|| Ok(()))));
|
||||
assert!(res.is_err());
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# 0.1.6 (March 09, 2018)
|
||||
|
||||
* Add native endian builder fn to length_delimited (#144)
|
||||
* Add AsyncRead::poll_read, AsyncWrite::poll_write (#170)
|
||||
|
||||
# 0.1.5 (February 07, 2018)
|
||||
|
||||
* Fix bug in `BytesCodec` and `LinesCodec`.
|
||||
* Performance improvement to `split`.
|
||||
|
||||
# 0.1.4 (November 10, 2017)
|
||||
|
||||
* Use `FrameTooBig` as length delimited error type (#70).
|
||||
* Provide `Bytes` and `Lines` codecs (#78).
|
||||
* Provide `AllowStdIo` wrapper (#76).
|
||||
|
||||
# 0.1.3 (August 14, 2017)
|
||||
|
||||
* Fix bug involving zero sized writes in copy helper (#57).
|
||||
* Add get / set accessors for length delimited max frame length setting. (#65).
|
||||
* Add `Framed::into_parts_and_codec` (#59).
|
||||
|
||||
# 0.1.2 (May 23, 2017)
|
||||
|
||||
* Add `from_parts` and `into_parts` to the framing combinators.
|
||||
* Support passing an initialized buffer to the framing combinators.
|
||||
* Add `length_adjustment` support to length delimited encoding (#48).
|
||||
|
||||
# 0.1.1 (March 22, 2017)
|
||||
|
||||
* Add some omitted `Self: Sized` bounds.
|
||||
* Add missing "inner" fns.
|
||||
|
||||
# 0.1.0 (March 15, 2017)
|
||||
|
||||
* Initial release
|
||||
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "tokio-io"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.6"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio-io"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-io/0.1"
|
||||
description = """
|
||||
Core I/O primitives for asynchronous I/O in Rust.
|
||||
"""
|
||||
categories = ["asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
bytes = "0.4.1"
|
||||
futures = "0.1.18"
|
||||
log = "0.4"
|
||||
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2018 Tokio Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,37 @@
|
||||
# tokio-io
|
||||
|
||||
Core I/O abstractions for the Tokio stack.
|
||||
|
||||
[](https://travis-ci.org/tokio-rs/tokio-io)
|
||||
|
||||
[Documentation](https://docs.rs/tokio-io)
|
||||
|
||||
## Usage
|
||||
|
||||
First, add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio-io = "0.1"
|
||||
```
|
||||
|
||||
Next, add this to your crate:
|
||||
|
||||
```rust
|
||||
extern crate tokio_io;
|
||||
```
|
||||
|
||||
You can find extensive documentation and examples about how to use this crate
|
||||
online at [https://tokio.rs](https://tokio.rs). The [API
|
||||
documentation](https://docs.rs/tokio-io) is also a great place to get started
|
||||
for the nitty-gritty.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE).
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
|
||||
terms or conditions.
|
||||
@@ -0,0 +1,81 @@
|
||||
use {AsyncRead, AsyncWrite};
|
||||
use futures::{Async, Poll};
|
||||
use std::{fmt, io};
|
||||
|
||||
/// A simple wrapper type which allows types that only implement
|
||||
/// `std::io::Read` or `std::io::Write` to be used in contexts which expect
|
||||
/// an `AsyncRead` or `AsyncWrite`.
|
||||
///
|
||||
/// If these types issue an error with the kind `io::ErrorKind::WouldBlock`,
|
||||
/// it is expected that they will notify the current task on readiness.
|
||||
/// Synchronous `std` types should not issue errors of this kind and
|
||||
/// are safe to use in this context. However, using these types with
|
||||
/// `AllowStdIo` will cause the event loop to block, so they should be used
|
||||
/// with care.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct AllowStdIo<T>(T);
|
||||
|
||||
impl<T> AllowStdIo<T> {
|
||||
/// Creates a new `AllowStdIo` from an existing IO object.
|
||||
pub fn new(io: T) -> Self {
|
||||
AllowStdIo(io)
|
||||
}
|
||||
|
||||
/// Returns a reference to the contained IO object.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the contained IO object.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
|
||||
/// Consumes self and returns the contained IO object.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> io::Write for AllowStdIo<T> where T: io::Write {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
self.0.write_all(buf)
|
||||
}
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
|
||||
self.0.write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsyncWrite for AllowStdIo<T> where T: io::Write {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> io::Read for AllowStdIo<T> where T: io::Read {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
// TODO: implement the `initializer` fn when it stabilizes.
|
||||
// See rust-lang/rust #42788
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
self.0.read_to_end(buf)
|
||||
}
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
self.0.read_to_string(buf)
|
||||
}
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
self.0.read_exact(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsyncRead for AllowStdIo<T> where T: io::Read {
|
||||
// TODO: override prepare_unitialized_buffer once `Read::initializer` is stable.
|
||||
// See rust-lang/rust #42788
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
use std::io as std_io;
|
||||
use bytes::BufMut;
|
||||
use futures::{Async, Poll};
|
||||
|
||||
use {framed, split, AsyncWrite};
|
||||
use codec::{Decoder, Encoder, Framed};
|
||||
use split::{ReadHalf, WriteHalf};
|
||||
|
||||
/// Read bytes asynchronously.
|
||||
///
|
||||
/// This trait inherits from `std::io::Read` and indicates that an I/O object is
|
||||
/// **non-blocking**. All non-blocking I/O objects must return an error when
|
||||
/// bytes are unavailable instead of blocking the current thread.
|
||||
///
|
||||
/// Specifically, this means that the `read` function will return one of the
|
||||
/// following:
|
||||
///
|
||||
/// * `Ok(n)` means that `n` bytes of data was immediately read and placed into
|
||||
/// the output buffer, where `n` == 0 implies that EOF has been reached.
|
||||
///
|
||||
/// * `Err(e) if e.kind() == ErrorKind::WouldBlock` means that no data was read
|
||||
/// into the buffer provided. The I/O object is not currently readable but may
|
||||
/// become readable in the future. Most importantly, **the current future's
|
||||
/// task is scheduled to get unparked when the object is readable**. This
|
||||
/// means that like `Future::poll` you'll receive a notification when the I/O
|
||||
/// object is readable again.
|
||||
///
|
||||
/// * `Err(e)` for other errors are standard I/O errors coming from the
|
||||
/// underlying object.
|
||||
///
|
||||
/// This trait importantly means that the `read` method only works in the
|
||||
/// context of a future's task. The object may panic if used outside of a task.
|
||||
pub trait AsyncRead: std_io::Read {
|
||||
/// Prepares an uninitialized buffer to be safe to pass to `read`. Returns
|
||||
/// `true` if the supplied buffer was zeroed out.
|
||||
///
|
||||
/// While it would be highly unusual, implementations of [`io::Read`] are
|
||||
/// able to read data from the buffer passed as an argument. Because of
|
||||
/// this, the buffer passed to [`io::Read`] must be initialized memory. In
|
||||
/// situations where large numbers of buffers are used, constantly having to
|
||||
/// zero out buffers can be expensive.
|
||||
///
|
||||
/// This function does any necessary work to prepare an uninitialized buffer
|
||||
/// to be safe to pass to `read`. If `read` guarantees to never attempt read
|
||||
/// data out of the supplied buffer, then `prepare_uninitialized_buffer`
|
||||
/// doesn't need to do any work.
|
||||
///
|
||||
/// If this function returns `true`, then the memory has been zeroed out.
|
||||
/// This allows implementations of `AsyncRead` which are composed of
|
||||
/// multiple sub implementations to efficiently implement
|
||||
/// `prepare_uninitialized_buffer`.
|
||||
///
|
||||
/// This function isn't actually `unsafe` to call but `unsafe` to implement.
|
||||
/// The implementor must ensure that either the whole `buf` has been zeroed
|
||||
/// or `read_buf()` overwrites the buffer without reading it and returns
|
||||
/// correct value.
|
||||
///
|
||||
/// This function is called from [`read_buf`].
|
||||
///
|
||||
/// [`io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
|
||||
/// [`read_buf`]: #method.read_buf
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
for i in 0..buf.len() {
|
||||
buf[i] = 0;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Attempt to read from the `AsyncRead` into `buf`.
|
||||
///
|
||||
/// On success, returns `Ok(Async::Ready(num_bytes_read))`.
|
||||
///
|
||||
/// If no data is available for reading, the method returns
|
||||
/// `Ok(Async::Pending)` and arranges for the current task (via
|
||||
/// `cx.waker()`) to receive a notification when the object becomes
|
||||
/// readable or is closed.
|
||||
fn poll_read(&mut self, buf: &mut [u8]) -> Poll<usize, std_io::Error> {
|
||||
match self.read(buf) {
|
||||
Ok(t) => Ok(Async::Ready(t)),
|
||||
Err(ref e) if e.kind() == std_io::ErrorKind::WouldBlock => {
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Pull some bytes from this source into the specified `Buf`, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// The `buf` provided will have bytes read into it and the internal cursor
|
||||
/// will be advanced if any bytes were read. Note that this method typically
|
||||
/// will not reallocate the buffer provided.
|
||||
fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, std_io::Error>
|
||||
where Self: Sized,
|
||||
{
|
||||
if !buf.has_remaining_mut() {
|
||||
return Ok(Async::Ready(0));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let n = {
|
||||
let b = buf.bytes_mut();
|
||||
|
||||
self.prepare_uninitialized_buffer(b);
|
||||
|
||||
try_ready!(self.poll_read(b))
|
||||
};
|
||||
|
||||
buf.advance_mut(n);
|
||||
Ok(Async::Ready(n))
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides a `Stream` and `Sink` interface for reading and writing to this
|
||||
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
|
||||
///
|
||||
/// Raw I/O objects work with byte sequences, but higher-level code usually
|
||||
/// wants to batch these into meaningful chunks, called "frames". This
|
||||
/// method layers framing on top of an I/O object, by using the `Codec`
|
||||
/// traits to handle encoding and decoding of messages frames. Note that
|
||||
/// the incoming and outgoing frame types may be distinct.
|
||||
///
|
||||
/// This function returns a *single* object that is both `Stream` and
|
||||
/// `Sink`; grouping this into a single object is often useful for layering
|
||||
/// things like gzip or TLS, which require both read and write access to the
|
||||
/// underlying object.
|
||||
///
|
||||
/// If you want to work more directly with the streams and sink, consider
|
||||
/// calling `split` on the `Framed` returned by this method, which will
|
||||
/// break them into separate objects, allowing them to interact more easily.
|
||||
fn framed<T: Encoder + Decoder>(self, codec: T) -> Framed<Self, T>
|
||||
where Self: AsyncWrite + Sized,
|
||||
{
|
||||
framed::framed(self, codec)
|
||||
}
|
||||
|
||||
/// Helper method for splitting this read/write object into two halves.
|
||||
///
|
||||
/// The two halves returned implement the `Read` and `Write` traits,
|
||||
/// respectively.
|
||||
fn split(self) -> (ReadHalf<Self>, WriteHalf<Self>)
|
||||
where Self: AsyncWrite + Sized,
|
||||
{
|
||||
split::split(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncRead> AsyncRead for Box<T> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
(**self).prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + AsyncRead> AsyncRead for &'a mut T {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
(**self).prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsyncRead for &'a [u8] {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [u8]) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
use std::io as std_io;
|
||||
use bytes::Buf;
|
||||
use futures::{Async, Poll};
|
||||
|
||||
use AsyncRead;
|
||||
|
||||
/// Writes bytes asynchronously.
|
||||
///
|
||||
/// The trait inherits from `std::io::Write` and indicates that an I/O object is
|
||||
/// **nonblocking**. All non-blocking I/O objects must return an error when
|
||||
/// bytes cannot be written instead of blocking the current thread.
|
||||
///
|
||||
/// Specifically, this means that the `write` function will return one of the
|
||||
/// following:
|
||||
///
|
||||
/// * `Ok(n)` means that `n` bytes of data was immediately written .
|
||||
///
|
||||
/// * `Err(e) if e.kind() == ErrorKind::WouldBlock` means that no data was
|
||||
/// written from the buffer provided. The I/O object is not currently
|
||||
/// writable but may become writable in the future. Most importantly, **the
|
||||
/// current future's task is scheduled to get unparked when the object is
|
||||
/// readable**. This means that like `Future::poll` you'll receive a
|
||||
/// notification when the I/O object is writable again.
|
||||
///
|
||||
/// * `Err(e)` for other errors are standard I/O errors coming from the
|
||||
/// underlying object.
|
||||
///
|
||||
/// This trait importantly means that the `write` method only works in the
|
||||
/// context of a future's task. The object may panic if used outside of a task.
|
||||
///
|
||||
/// Note that this trait also represents that the `Write::flush` method works
|
||||
/// very similarly to the `write` method, notably that `Ok(())` means that the
|
||||
/// writer has successfully been flushed, a "would block" error means that the
|
||||
/// current task is ready to receive a notification when flushing can make more
|
||||
/// progress, and otherwise normal errors can happen as well.
|
||||
pub trait AsyncWrite: std_io::Write {
|
||||
/// Attempt to write bytes from `buf` into the object.
|
||||
///
|
||||
/// On success, returns `Ok(Async::Ready(num_bytes_written))`.
|
||||
///
|
||||
/// If the object is not ready for writing, the method returns
|
||||
/// `Ok(Async::Pending)` and arranges for the current task (via
|
||||
/// `cx.waker()`) to receive a notification when the object becomes
|
||||
/// readable or is closed.
|
||||
fn poll_write(&mut self, buf: &[u8]) -> Poll<usize, std_io::Error> {
|
||||
match self.write(buf) {
|
||||
Ok(t) => Ok(Async::Ready(t)),
|
||||
Err(ref e) if e.kind() == std_io::ErrorKind::WouldBlock => {
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to flush the object, ensuring that any buffered data reach
|
||||
/// their destination.
|
||||
///
|
||||
/// On success, returns `Ok(Async::Ready(()))`.
|
||||
///
|
||||
/// If flushing cannot immediately complete, this method returns
|
||||
/// `Ok(Async::Pending)` and arranges for the current task (via
|
||||
/// `cx.waker()`) to receive a notification when the object can make
|
||||
/// progress towards flushing.
|
||||
fn poll_flush(&mut self) -> Poll<(), std_io::Error> {
|
||||
match self.flush() {
|
||||
Ok(t) => Ok(Async::Ready(t)),
|
||||
Err(ref e) if e.kind() == std_io::ErrorKind::WouldBlock => {
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initiates or attempts to shut down this writer, returning success when
|
||||
/// the I/O connection has completely shut down.
|
||||
///
|
||||
/// This method is intended to be used for asynchronous shutdown of I/O
|
||||
/// connections. For example this is suitable for implementing shutdown of a
|
||||
/// TLS connection or calling `TcpStream::shutdown` on a proxied connection.
|
||||
/// Protocols sometimes need to flush out final pieces of data or otherwise
|
||||
/// perform a graceful shutdown handshake, reading/writing more data as
|
||||
/// appropriate. This method is the hook for such protocols to implement the
|
||||
/// graceful shutdown logic.
|
||||
///
|
||||
/// This `shutdown` method is required by implementors of the
|
||||
/// `AsyncWrite` trait. Wrappers typically just want to proxy this call
|
||||
/// through to the wrapped type, and base types will typically implement
|
||||
/// shutdown logic here or just return `Ok(().into())`. Note that if you're
|
||||
/// wrapping an underlying `AsyncWrite` a call to `shutdown` implies that
|
||||
/// transitively the entire stream has been shut down. After your wrapper's
|
||||
/// shutdown logic has been executed you should shut down the underlying
|
||||
/// stream.
|
||||
///
|
||||
/// Invocation of a `shutdown` implies an invocation of `flush`. Once this
|
||||
/// method returns `Ready` it implies that a flush successfully happened
|
||||
/// before the shutdown happened. That is, callers don't need to call
|
||||
/// `flush` before calling `shutdown`. They can rely that by calling
|
||||
/// `shutdown` any pending buffered data will be written out.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// This function returns a `Poll<(), io::Error>` classified as such:
|
||||
///
|
||||
/// * `Ok(Async::Ready(()))` - indicates that the connection was
|
||||
/// successfully shut down and is now safe to deallocate/drop/close
|
||||
/// resources associated with it. This method means that the current task
|
||||
/// will no longer receive any notifications due to this method and the
|
||||
/// I/O object itself is likely no longer usable.
|
||||
///
|
||||
/// * `Ok(Async::NotReady)` - indicates that shutdown is initiated but could
|
||||
/// not complete just yet. This may mean that more I/O needs to happen to
|
||||
/// continue this shutdown operation. The current task is scheduled to
|
||||
/// receive a notification when it's otherwise ready to continue the
|
||||
/// shutdown operation. When woken up this method should be called again.
|
||||
///
|
||||
/// * `Err(e)` - indicates a fatal error has happened with shutdown,
|
||||
/// indicating that the shutdown operation did not complete successfully.
|
||||
/// This typically means that the I/O object is no longer usable.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function can return normal I/O errors through `Err`, described
|
||||
/// above. Additionally this method may also render the underlying
|
||||
/// `Write::write` method no longer usable (e.g. will return errors in the
|
||||
/// future). It's recommended that once `shutdown` is called the
|
||||
/// `write` method is no longer called.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if not called within the context of a future's
|
||||
/// task.
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error>;
|
||||
|
||||
/// Write a `Buf` into this value, returning how many bytes were written.
|
||||
///
|
||||
/// Note that this method will advance the `buf` provided automatically by
|
||||
/// the number of bytes written.
|
||||
fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, std_io::Error>
|
||||
where Self: Sized,
|
||||
{
|
||||
if !buf.has_remaining() {
|
||||
return Ok(Async::Ready(0));
|
||||
}
|
||||
|
||||
let n = try_ready!(self.poll_write(buf.bytes()));
|
||||
buf.advance(n);
|
||||
Ok(Async::Ready(n))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncWrite> AsyncWrite for Box<T> {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
(**self).shutdown()
|
||||
}
|
||||
}
|
||||
impl<'a, T: ?Sized + AsyncWrite> AsyncWrite for &'a mut T {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
(**self).shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for std_io::Repeat {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for std_io::Sink {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement `prepare_uninitialized_buffer` for `io::Take`.
|
||||
// This is blocked on rust-lang/rust#27269
|
||||
impl<T: AsyncRead> AsyncRead for std_io::Take<T> {
|
||||
}
|
||||
|
||||
// TODO: Implement `prepare_uninitialized_buffer` when upstream exposes inner
|
||||
// parts
|
||||
impl<T, U> AsyncRead for std_io::Chain<T, U>
|
||||
where T: AsyncRead,
|
||||
U: AsyncRead,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite> AsyncWrite for std_io::BufWriter<T> {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
try_ready!(self.poll_flush());
|
||||
self.get_mut().shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> AsyncRead for std_io::BufReader<T> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.get_ref().prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> AsyncRead for std_io::Cursor<T> {
|
||||
}
|
||||
|
||||
impl<'a> AsyncWrite for std_io::Cursor<&'a mut [u8]> {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for std_io::Cursor<Vec<u8>> {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for std_io::Cursor<Box<[u8]>> {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
use bytes::{Bytes, BufMut, BytesMut};
|
||||
use codec::{Encoder, Decoder};
|
||||
use std::io;
|
||||
|
||||
/// A simple `Codec` implementation that just ships bytes around.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct BytesCodec(());
|
||||
|
||||
impl BytesCodec {
|
||||
/// Creates a new `BytesCodec` for shipping around raw bytes.
|
||||
pub fn new() -> BytesCodec { BytesCodec(()) }
|
||||
}
|
||||
|
||||
impl Decoder for BytesCodec {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<BytesMut>, io::Error> {
|
||||
if buf.len() > 0 {
|
||||
let len = buf.len();
|
||||
Ok(Some(buf.split_to(len)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder for BytesCodec {
|
||||
type Item = Bytes;
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, data: Bytes, buf: &mut BytesMut) -> Result<(), io::Error> {
|
||||
buf.reserve(data.len());
|
||||
buf.put(data);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
use std::io;
|
||||
use bytes::BytesMut;
|
||||
|
||||
/// Decoding of frames via buffers.
|
||||
///
|
||||
/// This trait is used when constructing an instance of `Framed` or
|
||||
/// `FramedRead`. An implementation of `Decoder` takes a byte stream that has
|
||||
/// already been buffered in `src` and decodes the data into a stream of
|
||||
/// `Self::Item` frames.
|
||||
///
|
||||
/// Implementations are able to track state on `self`, which enables
|
||||
/// implementing stateful streaming parsers. In many cases, though, this type
|
||||
/// will simply be a unit struct (e.g. `struct HttpDecoder`).
|
||||
pub trait Decoder {
|
||||
/// The type of decoded frames.
|
||||
type Item;
|
||||
|
||||
/// The type of unrecoverable frame decoding errors.
|
||||
///
|
||||
/// If an individual message is ill-formed but can be ignored without
|
||||
/// interfering with the processing of future messages, it may be more
|
||||
/// useful to report the failure as an `Item`.
|
||||
///
|
||||
/// `From<io::Error>` is required in the interest of making `Error` suitable
|
||||
/// for returning directly from a `FramedRead`, and to enable the default
|
||||
/// implementation of `decode_eof` to yield an `io::Error` when the decoder
|
||||
/// fails to consume all available data.
|
||||
///
|
||||
/// Note that implementors of this trait can simply indicate `type Error =
|
||||
/// io::Error` to use I/O errors as this type.
|
||||
type Error: From<io::Error>;
|
||||
|
||||
/// Attempts to decode a frame from the provided buffer of bytes.
|
||||
///
|
||||
/// This method is called by `FramedRead` whenever bytes are ready to be
|
||||
/// parsed. The provided buffer of bytes is what's been read so far, and
|
||||
/// this instance of `Decode` can determine whether an entire frame is in
|
||||
/// the buffer and is ready to be returned.
|
||||
///
|
||||
/// If an entire frame is available, then this instance will remove those
|
||||
/// bytes from the buffer provided and return them as a decoded
|
||||
/// frame. Note that removing bytes from the provided buffer doesn't always
|
||||
/// necessarily copy the bytes, so this should be an efficient operation in
|
||||
/// most circumstances.
|
||||
///
|
||||
/// If the bytes look valid, but a frame isn't fully available yet, then
|
||||
/// `Ok(None)` is returned. This indicates to the `Framed` instance that
|
||||
/// it needs to read some more bytes before calling this method again.
|
||||
///
|
||||
/// Note that the bytes provided may be empty. If a previous call to
|
||||
/// `decode` consumed all the bytes in the buffer then `decode` will be
|
||||
/// called again until it returns `None`, indicating that more bytes need to
|
||||
/// be read.
|
||||
///
|
||||
/// Finally, if the bytes in the buffer are malformed then an error is
|
||||
/// returned indicating why. This informs `Framed` that the stream is now
|
||||
/// corrupt and should be terminated.
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error>;
|
||||
|
||||
/// A default method available to be called when there are no more bytes
|
||||
/// available to be read from the underlying I/O.
|
||||
///
|
||||
/// This method defaults to calling `decode` and returns an error if
|
||||
/// `Ok(None)` is returned while there is unconsumed data in `buf`.
|
||||
/// Typically this doesn't need to be implemented unless the framing
|
||||
/// protocol differs near the end of the stream.
|
||||
///
|
||||
/// Note that the `buf` argument may be empty. If a previous call to
|
||||
/// `decode_eof` consumed all the bytes in the buffer, `decode_eof` will be
|
||||
/// called again until it returns `None`, indicating that there are no more
|
||||
/// frames to yield. This behavior enables returning finalization frames
|
||||
/// that may not be based on inbound data.
|
||||
fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
match try!(self.decode(buf)) {
|
||||
Some(frame) => Ok(Some(frame)),
|
||||
None => {
|
||||
if buf.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other,
|
||||
"bytes remaining on stream").into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
use std::io;
|
||||
use bytes::BytesMut;
|
||||
|
||||
/// Trait of helper objects to write out messages as bytes, for use with
|
||||
/// `FramedWrite`.
|
||||
pub trait Encoder {
|
||||
/// The type of items consumed by the `Encoder`
|
||||
type Item;
|
||||
|
||||
/// The type of encoding errors.
|
||||
///
|
||||
/// `FramedWrite` requires `Encoder`s errors to implement `From<io::Error>`
|
||||
/// in the interest letting it return `Error`s directly.
|
||||
type Error: From<io::Error>;
|
||||
|
||||
/// Encodes a frame into the buffer provided.
|
||||
///
|
||||
/// This method will encode `item` into the byte buffer provided by `dst`.
|
||||
/// The `dst` provided is an internal buffer of the `Framed` instance and
|
||||
/// will be written out when possible.
|
||||
fn encode(&mut self, item: Self::Item, dst: &mut BytesMut)
|
||||
-> Result<(), Self::Error>;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use codec::{Encoder, Decoder};
|
||||
use std::{io, str};
|
||||
|
||||
/// A simple `Codec` implementation that splits up data into lines.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct LinesCodec {
|
||||
// Stored index of the next index to examine for a `\n` character.
|
||||
// This is used to optimize searching.
|
||||
// For example, if `decode` was called with `abc`, it would hold `3`,
|
||||
// because that is the next index to examine.
|
||||
// The next time `decode` is called with `abcde\n`, the method will
|
||||
// only look at `de\n` before returning.
|
||||
next_index: usize,
|
||||
}
|
||||
|
||||
impl LinesCodec {
|
||||
/// Returns a `LinesCodec` for splitting up data into lines.
|
||||
pub fn new() -> LinesCodec {
|
||||
LinesCodec { next_index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
fn utf8(buf: &[u8]) -> Result<&str, io::Error> {
|
||||
str::from_utf8(buf).map_err(|_|
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"Unable to decode input as UTF8"))
|
||||
}
|
||||
|
||||
fn without_carriage_return(s: &[u8]) -> &[u8] {
|
||||
if let Some(&b'\r') = s.last() {
|
||||
&s[..s.len() - 1]
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoder for LinesCodec {
|
||||
type Item = String;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<String>, io::Error> {
|
||||
if let Some(newline_offset) =
|
||||
buf[self.next_index..].iter().position(|b| *b == b'\n')
|
||||
{
|
||||
let newline_index = newline_offset + self.next_index;
|
||||
let line = buf.split_to(newline_index + 1);
|
||||
let line = &line[..line.len()-1];
|
||||
let line = without_carriage_return(line);
|
||||
let line = utf8(line)?;
|
||||
self.next_index = 0;
|
||||
Ok(Some(line.to_string()))
|
||||
} else {
|
||||
self.next_index = buf.len();
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<String>, io::Error> {
|
||||
Ok(match self.decode(buf)? {
|
||||
Some(frame) => Some(frame),
|
||||
None => {
|
||||
// No terminating newline - return remaining data, if any
|
||||
if buf.is_empty() || buf == &b"\r"[..] {
|
||||
None
|
||||
} else {
|
||||
let line = buf.take();
|
||||
let line = without_carriage_return(&line);
|
||||
let line = utf8(line)?;
|
||||
self.next_index = 0;
|
||||
Some(line.to_string())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder for LinesCodec {
|
||||
type Item = String;
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, line: String, buf: &mut BytesMut) -> Result<(), io::Error> {
|
||||
buf.reserve(line.len() + 1);
|
||||
buf.put(line);
|
||||
buf.put_u8(b'\n');
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
//! Utilities for encoding and decoding frames.
|
||||
//!
|
||||
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and
|
||||
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
|
||||
//! Framed streams are also known as [transports].
|
||||
//!
|
||||
//! [`AsyncRead`]: #
|
||||
//! [`AsyncWrite`]: #
|
||||
//! [`Sink`]: #
|
||||
//! [`Stream`]: #
|
||||
//! [transports]: #
|
||||
|
||||
mod decoder;
|
||||
mod encoder;
|
||||
mod bytes_codec;
|
||||
mod lines_codec;
|
||||
|
||||
pub use self::decoder::Decoder;
|
||||
pub use self::encoder::Encoder;
|
||||
pub use self::bytes_codec::BytesCodec;
|
||||
pub use self::lines_codec::LinesCodec;
|
||||
|
||||
pub use framed::{Framed, FramedParts};
|
||||
pub use framed_read::FramedRead;
|
||||
pub use framed_write::FramedWrite;
|
||||
|
||||
pub mod length_delimited {
|
||||
//! Frame a stream of bytes based on a length prefix
|
||||
//!
|
||||
//! Many protocols delimit their frames by prefacing frame data with a
|
||||
//! frame head that specifies the length of the frame. The
|
||||
//! `length_delimited` module provides utilities for handling the length
|
||||
//! based framing. This allows the consumer to work with entire frames
|
||||
//! without having to worry about buffering or other framing logic.
|
||||
//!
|
||||
//! # Getting started
|
||||
//!
|
||||
//! If implementing a protocol from scratch, using length delimited framing
|
||||
//! is an easy way to get started. [`Framed::new()`] will adapt a
|
||||
//! full-duplex byte stream with a length delimited framer using default
|
||||
//! configuration values.
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio_io::{AsyncRead, AsyncWrite};
|
||||
//! use tokio_io::codec::length_delimited;
|
||||
//!
|
||||
//! fn bind_transport<T: AsyncRead + AsyncWrite>(io: T)
|
||||
//! -> length_delimited::Framed<T>
|
||||
//! {
|
||||
//! length_delimited::Framed::new(io)
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The returned transport implements `Sink + Stream` for `BytesMut`. It
|
||||
//! encodes the frame with a big-endian `u32` header denoting the frame
|
||||
//! payload length:
|
||||
//!
|
||||
//! ```text
|
||||
//! +----------+--------------------------------+
|
||||
//! | len: u32 | frame payload |
|
||||
//! +----------+--------------------------------+
|
||||
//! ```
|
||||
//!
|
||||
//! Specifically, given the following:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio_io;
|
||||
//! # extern crate bytes;
|
||||
//! # extern crate futures;
|
||||
//! #
|
||||
//! use tokio_io::{AsyncRead, AsyncWrite};
|
||||
//! use tokio_io::codec::length_delimited;
|
||||
//! use bytes::BytesMut;
|
||||
//! use futures::{Sink, Future};
|
||||
//!
|
||||
//! fn write_frame<T: AsyncRead + AsyncWrite>(io: T) {
|
||||
//! let mut transport = length_delimited::Framed::new(io);
|
||||
//! let frame = BytesMut::from("hello world");
|
||||
//!
|
||||
//! transport.send(frame).wait().unwrap();
|
||||
//! }
|
||||
//! #
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! The encoded frame will look like this:
|
||||
//!
|
||||
//! ```text
|
||||
//! +---- len: u32 ----+---- data ----+
|
||||
//! | \x00\x00\x00\x0b | hello world |
|
||||
//! +------------------+--------------+
|
||||
//! ```
|
||||
//!
|
||||
//! # Decoding
|
||||
//!
|
||||
//! [`FramedRead`] adapts an [`AsyncRead`] into a `Stream` of [`BytesMut`],
|
||||
//! such that each yielded [`BytesMut`] value contains the contents of an
|
||||
//! entire frame. There are many configuration parameters enabling
|
||||
//! [`FrameRead`] to handle a wide range of protocols. Here are some
|
||||
//! examples that will cover the various options at a high level.
|
||||
//!
|
||||
//! ## Example 1
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 0, including the
|
||||
//! frame head in the yielded `BytesMut`.
|
||||
//!
|
||||
//! ```
|
||||
//! # use tokio_io::AsyncRead;
|
||||
//! # use tokio_io::codec::length_delimited;
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(0) // default value
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(0) // default value
|
||||
//! .num_skip(0) // Do not strip frame header
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT DECODED
|
||||
//! +-- len ---+--- Payload ---+ +-- len ---+--- Payload ---+
|
||||
//! | \x00\x0B | Hello world | --> | \x00\x0B | Hello world |
|
||||
//! +----------+---------------+ +----------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! The value of the length field is 11 (`\x0B`) which represents the length
|
||||
//! of the payload, `hello world`. By default, [`FramedRead`] assumes that
|
||||
//! the length field represents the number of bytes that **follows** the
|
||||
//! length field. Thus, the entire frame has a length of 13: 2 bytes for the
|
||||
//! frame head + 11 bytes for the payload.
|
||||
//!
|
||||
//! ## Example 2
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 0, omitting the
|
||||
//! frame head in the yielded `BytesMut`.
|
||||
//!
|
||||
//! ```
|
||||
//! # use tokio_io::AsyncRead;
|
||||
//! # use tokio_io::codec::length_delimited;
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(0) // default value
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(0) // default value
|
||||
//! // `num_skip` is not needed, the default is to skip
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT DECODED
|
||||
//! +-- len ---+--- Payload ---+ +--- Payload ---+
|
||||
//! | \x00\x0B | Hello world | --> | Hello world |
|
||||
//! +----------+---------------+ +---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! This is similar to the first example, the only difference is that the
|
||||
//! frame head is **not** included in the yielded `BytesMut` value.
|
||||
//!
|
||||
//! ## Example 3
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 0, including the
|
||||
//! frame head in the yielded `BytesMut`. In this case, the length field
|
||||
//! **includes** the frame head length.
|
||||
//!
|
||||
//! ```
|
||||
//! # use tokio_io::AsyncRead;
|
||||
//! # use tokio_io::codec::length_delimited;
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(0) // default value
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(-2) // size of head
|
||||
//! .num_skip(0)
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT DECODED
|
||||
//! +-- len ---+--- Payload ---+ +-- len ---+--- Payload ---+
|
||||
//! | \x00\x0D | Hello world | --> | \x00\x0D | Hello world |
|
||||
//! +----------+---------------+ +----------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! In most cases, the length field represents the length of the payload
|
||||
//! only, as shown in the previous examples. However, in some protocols the
|
||||
//! length field represents the length of the whole frame, including the
|
||||
//! head. In such cases, we specify a negative `length_adjustment` to adjust
|
||||
//! the value provided in the frame head to represent the payload length.
|
||||
//!
|
||||
//! ## Example 4
|
||||
//!
|
||||
//! The following will parse a 3 byte length field at offset 0 in a 5 byte
|
||||
//! frame head, including the frame head in the yielded `BytesMut`.
|
||||
//!
|
||||
//! ```
|
||||
//! # use tokio_io::AsyncRead;
|
||||
//! # use tokio_io::codec::length_delimited;
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(0) // default value
|
||||
//! .length_field_length(3)
|
||||
//! .length_adjustment(2) // remaining head
|
||||
//! .num_skip(0)
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT
|
||||
//! +---- len -----+- head -+--- Payload ---+
|
||||
//! | \x00\x00\x0B | \xCAFE | Hello world |
|
||||
//! +--------------+--------+---------------+
|
||||
//!
|
||||
//! DECODED
|
||||
//! +---- len -----+- head -+--- Payload ---+
|
||||
//! | \x00\x00\x0B | \xCAFE | Hello world |
|
||||
//! +--------------+--------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! A more advanced example that shows a case where there is extra frame
|
||||
//! head data between the length field and the payload. In such cases, it is
|
||||
//! usually desirable to include the frame head as part of the yielded
|
||||
//! `BytesMut`. This lets consumers of the length delimited framer to
|
||||
//! process the frame head as needed.
|
||||
//!
|
||||
//! The positive `length_adjustment` value lets `FramedRead` factor in the
|
||||
//! additional head into the frame length calculation.
|
||||
//!
|
||||
//! ## Example 5
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 1 of a 4 byte
|
||||
//! frame head. The first byte and the length field will be omitted from the
|
||||
//! yielded `BytesMut`, but the trailing 2 bytes of the frame head will be
|
||||
//! included.
|
||||
//!
|
||||
//! ```
|
||||
//! # use tokio_io::AsyncRead;
|
||||
//! # use tokio_io::codec::length_delimited;
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(1) // length of hdr1
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(1) // length of hdr2
|
||||
//! .num_skip(3) // length of hdr1 + LEN
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT
|
||||
//! +- hdr1 -+-- len ---+- hdr2 -+--- Payload ---+
|
||||
//! | \xCA | \x00\x0B | \xFE | Hello world |
|
||||
//! +--------+----------+--------+---------------+
|
||||
//!
|
||||
//! DECODED
|
||||
//! +- hdr2 -+--- Payload ---+
|
||||
//! | \xFE | Hello world |
|
||||
//! +--------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! The length field is situated in the middle of the frame head. In this
|
||||
//! case, the first byte in the frame head could be a version or some other
|
||||
//! identifier that is not needed for processing. On the other hand, the
|
||||
//! second half of the head is needed.
|
||||
//!
|
||||
//! `length_field_offset` indicates how many bytes to skip before starting
|
||||
//! to read the length field. `length_adjustment` is the number of bytes to
|
||||
//! skip starting at the end of the length field. In this case, it is the
|
||||
//! second half of the head.
|
||||
//!
|
||||
//! ## Example 6
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 1 of a 4 byte
|
||||
//! frame head. The first byte and the length field will be omitted from the
|
||||
//! yielded `BytesMut`, but the trailing 2 bytes of the frame head will be
|
||||
//! included. In this case, the length field **includes** the frame head
|
||||
//! length.
|
||||
//!
|
||||
//! ```
|
||||
//! # use tokio_io::AsyncRead;
|
||||
//! # use tokio_io::codec::length_delimited;
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(1) // length of hdr1
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(-3) // length of hdr1 + LEN, negative
|
||||
//! .num_skip(3)
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT
|
||||
//! +- hdr1 -+-- len ---+- hdr2 -+--- Payload ---+
|
||||
//! | \xCA | \x00\x0F | \xFE | Hello world |
|
||||
//! +--------+----------+--------+---------------+
|
||||
//!
|
||||
//! DECODED
|
||||
//! +- hdr2 -+--- Payload ---+
|
||||
//! | \xFE | Hello world |
|
||||
//! +--------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! Similar to the example above, the difference is that the length field
|
||||
//! represents the length of the entire frame instead of just the payload.
|
||||
//! The length of `hdr1` and `len` must be counted in `length_adjustment`.
|
||||
//! Note that the length of `hdr2` does **not** need to be explicitly set
|
||||
//! anywhere because it already is factored into the total frame length that
|
||||
//! is read from the byte stream.
|
||||
//!
|
||||
//! # Encoding
|
||||
//!
|
||||
//! [`FramedWrite`] adapts an [`AsyncWrite`] into a `Sink` of [`BytesMut`],
|
||||
//! such that each submitted [`BytesMut`] is prefaced by a length field.
|
||||
//! There are fewer configuration options than [`FramedRead`]. Given
|
||||
//! protocols that have more complex frame heads, an encoder should probably
|
||||
//! be written by hand using [`Encoder`].
|
||||
//!
|
||||
//! Here is a simple example, given a `FramedWrite` with the following
|
||||
//! configuration:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio_io;
|
||||
//! # extern crate bytes;
|
||||
//! # use tokio_io::AsyncWrite;
|
||||
//! # use tokio_io::codec::length_delimited;
|
||||
//! # use bytes::BytesMut;
|
||||
//! # fn write_frame<T: AsyncWrite>(io: T) {
|
||||
//! # let _: length_delimited::FramedWrite<T, BytesMut> =
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_length(2)
|
||||
//! .new_write(io);
|
||||
//! # }
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! A payload of `hello world` will be encoded as:
|
||||
//!
|
||||
//! ```text
|
||||
//! +- len: u16 -+---- data ----+
|
||||
//! | \x00\x0b | hello world |
|
||||
//! +------------+--------------+
|
||||
//! ```
|
||||
//!
|
||||
//! [`FramedRead`]: struct.FramedRead.html
|
||||
//! [`FramedWrite`]: struct.FramedWrite.html
|
||||
//! [`AsyncRead`]: ../../trait.AsyncRead.html
|
||||
//! [`AsyncWrite`]: ../../trait.AsyncWrite.html
|
||||
//! [`Encoder`]: ../trait.Encoder.html
|
||||
//! [`BytesMut`]: https://docs.rs/bytes/~0.4/bytes/struct.BytesMut.html
|
||||
|
||||
pub use ::length_delimited::*;
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
use std::io::{self, Read, Write};
|
||||
use std::fmt;
|
||||
|
||||
use {AsyncRead, AsyncWrite};
|
||||
use codec::{Decoder, Encoder};
|
||||
use framed_read::{framed_read2, framed_read2_with_buffer, FramedRead2};
|
||||
use framed_write::{framed_write2, framed_write2_with_buffer, FramedWrite2};
|
||||
|
||||
use futures::{Stream, Sink, StartSend, Poll};
|
||||
use bytes::{BytesMut};
|
||||
|
||||
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using
|
||||
/// the `Encoder` and `Decoder` traits to encode and decode frames.
|
||||
///
|
||||
/// You can create a `Framed` instance by using the `AsyncRead::framed` adapter.
|
||||
pub struct Framed<T, U> {
|
||||
inner: FramedRead2<FramedWrite2<Fuse<T, U>>>,
|
||||
}
|
||||
|
||||
pub struct Fuse<T, U>(pub T, pub U);
|
||||
|
||||
pub fn framed<T, U>(inner: T, codec: U) -> Framed<T, U>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
U: Decoder + Encoder,
|
||||
{
|
||||
Framed {
|
||||
inner: framed_read2(framed_write2(Fuse(inner, codec))),
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Framed<T, U> {
|
||||
/// Provides a `Stream` and `Sink` interface for reading and writing to this
|
||||
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
|
||||
///
|
||||
/// Raw I/O objects work with byte sequences, but higher-level code usually
|
||||
/// wants to batch these into meaningful chunks, called "frames". This
|
||||
/// method layers framing on top of an I/O object, by using the `Codec`
|
||||
/// traits to handle encoding and decoding of messages frames. Note that
|
||||
/// the incoming and outgoing frame types may be distinct.
|
||||
///
|
||||
/// This function returns a *single* object that is both `Stream` and
|
||||
/// `Sink`; grouping this into a single object is often useful for layering
|
||||
/// things like gzip or TLS, which require both read and write access to the
|
||||
/// underlying object.
|
||||
///
|
||||
/// This objects takes a stream and a readbuffer and a writebuffer. These field
|
||||
/// can be obtained from an existing `Framed` with the `into_parts` method.
|
||||
///
|
||||
/// If you want to work more directly with the streams and sink, consider
|
||||
/// calling `split` on the `Framed` returned by this method, which will
|
||||
/// break them into separate objects, allowing them to interact more easily.
|
||||
pub fn from_parts(parts: FramedParts<T>, codec: U) -> Framed<T, U>
|
||||
{
|
||||
Framed {
|
||||
inner: framed_read2_with_buffer(framed_write2_with_buffer(Fuse(parts.inner, codec), parts.writebuf), parts.readbuf),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `Frame`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.get_ref().get_ref().0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `Frame`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.get_mut().get_mut().0
|
||||
}
|
||||
|
||||
/// Consumes the `Frame`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.into_inner().into_inner().0
|
||||
}
|
||||
|
||||
/// Consumes the `Frame`, returning its underlying I/O stream and the buffer
|
||||
/// with unprocessed data.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_parts(self) -> FramedParts<T> {
|
||||
let (inner, readbuf) = self.inner.into_parts();
|
||||
let (inner, writebuf) = inner.into_parts();
|
||||
FramedParts { inner: inner.0, readbuf: readbuf, writebuf: writebuf }
|
||||
}
|
||||
|
||||
/// Consumes the `Frame`, returning its underlying I/O stream and the buffer
|
||||
/// with unprocessed data, and also the current codec state.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
///
|
||||
/// Note that this function will be removed once the codec has been
|
||||
/// integrated into `FramedParts` in a new version (see
|
||||
/// [#53](https://github.com/tokio-rs/tokio-io/pull/53)).
|
||||
pub fn into_parts_and_codec(self) -> (FramedParts<T>, U) {
|
||||
let (inner, readbuf) = self.inner.into_parts();
|
||||
let (inner, writebuf) = inner.into_parts();
|
||||
(FramedParts { inner: inner.0, readbuf: readbuf, writebuf: writebuf }, inner.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Stream for Framed<T, U>
|
||||
where T: AsyncRead,
|
||||
U: Decoder,
|
||||
{
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Sink for Framed<T, U>
|
||||
where T: AsyncWrite,
|
||||
U: Encoder,
|
||||
U::Error: From<io::Error>,
|
||||
{
|
||||
type SinkItem = U::Item;
|
||||
type SinkError = U::Error;
|
||||
|
||||
fn start_send(&mut self,
|
||||
item: Self::SinkItem)
|
||||
-> StartSend<Self::SinkItem, Self::SinkError>
|
||||
{
|
||||
self.inner.get_mut().start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.get_mut().poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.get_mut().close()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> fmt::Debug for Framed<T, U>
|
||||
where T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Framed")
|
||||
.field("io", &self.inner.get_ref().get_ref().0)
|
||||
.field("codec", &self.inner.get_ref().get_ref().1)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Fuse =====
|
||||
|
||||
impl<T: Read, U> Read for Fuse<T, U> {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead, U> AsyncRead for Fuse<T, U> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.0.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write, U> Write for Fuse<T, U> {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(src)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite, U> AsyncWrite for Fuse<T, U> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
self.0.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: Decoder> Decoder for Fuse<T, U> {
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn decode(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
self.1.decode(buffer)
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
self.1.decode_eof(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: Encoder> Encoder for Fuse<T, U> {
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
self.1.encode(item, dst)
|
||||
}
|
||||
}
|
||||
|
||||
/// `FramedParts` contains an export of the data of a Framed transport.
|
||||
/// It can be used to construct a new `Framed` with a different codec.
|
||||
/// It contains all current buffers and the inner transport.
|
||||
#[derive(Debug)]
|
||||
pub struct FramedParts<T>
|
||||
{
|
||||
/// The inner transport used to read bytes to and write bytes to
|
||||
pub inner: T,
|
||||
/// The buffer with read but unprocessed data.
|
||||
pub readbuf: BytesMut,
|
||||
/// A buffer with unprocessed data which are not written yet.
|
||||
pub writebuf: BytesMut
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
use std::fmt;
|
||||
|
||||
use AsyncRead;
|
||||
use codec::Decoder;
|
||||
use framed::Fuse;
|
||||
|
||||
use futures::{Async, Poll, Stream, Sink, StartSend};
|
||||
use bytes::BytesMut;
|
||||
|
||||
/// A `Stream` of messages decoded from an `AsyncRead`.
|
||||
pub struct FramedRead<T, D> {
|
||||
inner: FramedRead2<Fuse<T, D>>,
|
||||
}
|
||||
|
||||
pub struct FramedRead2<T> {
|
||||
inner: T,
|
||||
eof: bool,
|
||||
is_readable: bool,
|
||||
buffer: BytesMut,
|
||||
}
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
|
||||
// ===== impl FramedRead =====
|
||||
|
||||
impl<T, D> FramedRead<T, D>
|
||||
where T: AsyncRead,
|
||||
D: Decoder,
|
||||
{
|
||||
/// Creates a new `FramedRead` with the given `decoder`.
|
||||
pub fn new(inner: T, decoder: D) -> FramedRead<T, D> {
|
||||
FramedRead {
|
||||
inner: framed_read2(Fuse(inner, decoder)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> FramedRead<T, D> {
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `FramedRead`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `FramedRead`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Consumes the `FramedRead`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying decoder.
|
||||
pub fn decoder(&self) -> &D {
|
||||
&self.inner.inner.1
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying decoder.
|
||||
pub fn decoder_mut(&mut self) -> &mut D {
|
||||
&mut self.inner.inner.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Stream for FramedRead<T, D>
|
||||
where T: AsyncRead,
|
||||
D: Decoder,
|
||||
{
|
||||
type Item = D::Item;
|
||||
type Error = D::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Sink for FramedRead<T, D>
|
||||
where T: Sink,
|
||||
{
|
||||
type SinkItem = T::SinkItem;
|
||||
type SinkError = T::SinkError;
|
||||
|
||||
fn start_send(&mut self,
|
||||
item: Self::SinkItem)
|
||||
-> StartSend<Self::SinkItem, Self::SinkError>
|
||||
{
|
||||
self.inner.inner.0.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.inner.0.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.inner.0.close()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> fmt::Debug for FramedRead<T, D>
|
||||
where T: fmt::Debug,
|
||||
D: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("FramedRead")
|
||||
.field("inner", &self.inner.inner.0)
|
||||
.field("decoder", &self.inner.inner.1)
|
||||
.field("eof", &self.inner.eof)
|
||||
.field("is_readable", &self.inner.is_readable)
|
||||
.field("buffer", &self.inner.buffer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedRead2 =====
|
||||
|
||||
pub fn framed_read2<T>(inner: T) -> FramedRead2<T> {
|
||||
FramedRead2 {
|
||||
inner: inner,
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn framed_read2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedRead2<T> {
|
||||
if buf.capacity() < INITIAL_CAPACITY {
|
||||
let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
|
||||
buf.reserve(bytes_to_reserve);
|
||||
}
|
||||
FramedRead2 {
|
||||
inner: inner,
|
||||
eof: false,
|
||||
is_readable: buf.len() > 0,
|
||||
buffer: buf,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FramedRead2<T> {
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (T, BytesMut) {
|
||||
(self.inner, self.buffer)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for FramedRead2<T>
|
||||
where T: AsyncRead + Decoder,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
loop {
|
||||
// Repeatedly call `decode` or `decode_eof` as long as it is
|
||||
// "readable". Readable is defined as not having returned `None`. If
|
||||
// the upstream has returned EOF, and the decoder is no longer
|
||||
// readable, it can be assumed that the decoder will never become
|
||||
// readable again, at which point the stream is terminated.
|
||||
if self.is_readable {
|
||||
if self.eof {
|
||||
let frame = try!(self.inner.decode_eof(&mut self.buffer));
|
||||
return Ok(Async::Ready(frame));
|
||||
}
|
||||
|
||||
trace!("attempting to decode a frame");
|
||||
|
||||
if let Some(frame) = try!(self.inner.decode(&mut self.buffer)) {
|
||||
trace!("frame decoded from buffer");
|
||||
return Ok(Async::Ready(Some(frame)));
|
||||
}
|
||||
|
||||
self.is_readable = false;
|
||||
}
|
||||
|
||||
assert!(!self.eof);
|
||||
|
||||
// Otherwise, try to read more data and try again. Make sure we've
|
||||
// got room for at least one byte to read to ensure that we don't
|
||||
// get a spurious 0 that looks like EOF
|
||||
self.buffer.reserve(1);
|
||||
if 0 == try_ready!(self.inner.read_buf(&mut self.buffer)) {
|
||||
self.eof = true;
|
||||
}
|
||||
|
||||
self.is_readable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
use std::io::{self, Read};
|
||||
use std::fmt;
|
||||
|
||||
use {AsyncRead, AsyncWrite};
|
||||
use codec::{Decoder, Encoder};
|
||||
use framed::Fuse;
|
||||
|
||||
use futures::{Async, AsyncSink, Poll, Stream, Sink, StartSend};
|
||||
use bytes::BytesMut;
|
||||
|
||||
/// A `Sink` of frames encoded to an `AsyncWrite`.
|
||||
pub struct FramedWrite<T, E> {
|
||||
inner: FramedWrite2<Fuse<T, E>>,
|
||||
}
|
||||
|
||||
pub struct FramedWrite2<T> {
|
||||
inner: T,
|
||||
buffer: BytesMut,
|
||||
}
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;
|
||||
|
||||
impl<T, E> FramedWrite<T, E>
|
||||
where T: AsyncWrite,
|
||||
E: Encoder,
|
||||
{
|
||||
/// Creates a new `FramedWrite` with the given `encoder`.
|
||||
pub fn new(inner: T, encoder: E) -> FramedWrite<T, E> {
|
||||
FramedWrite {
|
||||
inner: framed_write2(Fuse(inner, encoder)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> FramedWrite<T, E> {
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `FramedWrite`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `FramedWrite`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Consumes the `FramedWrite`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying decoder.
|
||||
pub fn encoder(&self) -> &E {
|
||||
&self.inner.inner.1
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying decoder.
|
||||
pub fn encoder_mut(&mut self) -> &mut E {
|
||||
&mut self.inner.inner.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Sink for FramedWrite<T, E>
|
||||
where T: AsyncWrite,
|
||||
E: Encoder,
|
||||
{
|
||||
type SinkItem = E::Item;
|
||||
type SinkError = E::Error;
|
||||
|
||||
fn start_send(&mut self, item: E::Item) -> StartSend<E::Item, E::Error> {
|
||||
self.inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
Ok(try!(self.inner.close()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Stream for FramedWrite<T, D>
|
||||
where T: Stream,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
self.inner.inner.0.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> fmt::Debug for FramedWrite<T, U>
|
||||
where T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("FramedWrite")
|
||||
.field("inner", &self.inner.get_ref().0)
|
||||
.field("encoder", &self.inner.get_ref().1)
|
||||
.field("buffer", &self.inner.buffer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedWrite2 =====
|
||||
|
||||
pub fn framed_write2<T>(inner: T) -> FramedWrite2<T> {
|
||||
FramedWrite2 {
|
||||
inner: inner,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn framed_write2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedWrite2<T> {
|
||||
if buf.capacity() < INITIAL_CAPACITY {
|
||||
let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
|
||||
buf.reserve(bytes_to_reserve);
|
||||
}
|
||||
FramedWrite2 {
|
||||
inner: inner,
|
||||
buffer: buf,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FramedWrite2<T> {
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (T, BytesMut) {
|
||||
(self.inner, self.buffer)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sink for FramedWrite2<T>
|
||||
where T: AsyncWrite + Encoder,
|
||||
{
|
||||
type SinkItem = T::Item;
|
||||
type SinkError = T::Error;
|
||||
|
||||
fn start_send(&mut self, item: T::Item) -> StartSend<T::Item, T::Error> {
|
||||
// If the buffer is already over 8KiB, then attempt to flush it. If after flushing it's
|
||||
// *still* over 8KiB, then apply backpressure (reject the send).
|
||||
if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
|
||||
try!(self.poll_complete());
|
||||
|
||||
if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
|
||||
return Ok(AsyncSink::NotReady(item));
|
||||
}
|
||||
}
|
||||
|
||||
try!(self.inner.encode(item, &mut self.buffer));
|
||||
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
trace!("flushing framed transport");
|
||||
|
||||
while !self.buffer.is_empty() {
|
||||
trace!("writing; remaining={}", self.buffer.len());
|
||||
|
||||
let n = try_ready!(self.inner.poll_write(&self.buffer));
|
||||
|
||||
if n == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::WriteZero, "failed to
|
||||
write frame to transport").into());
|
||||
}
|
||||
|
||||
// TODO: Add a way to `bytes` to do this w/o returning the drained
|
||||
// data.
|
||||
let _ = self.buffer.split_to(n);
|
||||
}
|
||||
|
||||
// Try flushing the underlying IO
|
||||
try_ready!(self.inner.poll_flush());
|
||||
|
||||
trace!("framed transport flushed");
|
||||
return Ok(Async::Ready(()));
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
try_ready!(self.poll_complete());
|
||||
Ok(try!(self.inner.shutdown()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decoder> Decoder for FramedWrite2<T> {
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
|
||||
self.inner.decode(src)
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
|
||||
self.inner.decode_eof(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Read> Read for FramedWrite2<T> {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> AsyncRead for FramedWrite2<T> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.inner.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
use std::io;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use {AsyncRead, AsyncWrite};
|
||||
|
||||
/// A future which will copy all data from a reader into a writer.
|
||||
///
|
||||
/// Created by the [`copy`] function, this future will resolve to the number of
|
||||
/// bytes copied or an error if one happens.
|
||||
///
|
||||
/// [`copy`]: fn.copy.html
|
||||
#[derive(Debug)]
|
||||
pub struct Copy<R, W> {
|
||||
reader: Option<R>,
|
||||
read_done: bool,
|
||||
writer: Option<W>,
|
||||
pos: usize,
|
||||
cap: usize,
|
||||
amt: u64,
|
||||
buf: Box<[u8]>,
|
||||
}
|
||||
|
||||
/// Creates a future which represents copying all the bytes from one object to
|
||||
/// another.
|
||||
///
|
||||
/// The returned future will copy all the bytes read from `reader` into the
|
||||
/// `writer` specified. This future will only complete once the `reader` has hit
|
||||
/// EOF and all bytes have been written to and flushed from the `writer`
|
||||
/// provided.
|
||||
///
|
||||
/// On success the number of bytes is returned and the `reader` and `writer` are
|
||||
/// consumed. On error the error is returned and the I/O objects are consumed as
|
||||
/// well.
|
||||
pub fn copy<R, W>(reader: R, writer: W) -> Copy<R, W>
|
||||
where R: AsyncRead,
|
||||
W: AsyncWrite,
|
||||
{
|
||||
Copy {
|
||||
reader: Some(reader),
|
||||
read_done: false,
|
||||
writer: Some(writer),
|
||||
amt: 0,
|
||||
pos: 0,
|
||||
cap: 0,
|
||||
buf: Box::new([0; 2048]),
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, W> Future for Copy<R, W>
|
||||
where R: AsyncRead,
|
||||
W: AsyncWrite,
|
||||
{
|
||||
type Item = (u64, R, W);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(u64, R, W), io::Error> {
|
||||
loop {
|
||||
// If our buffer is empty, then we need to read some data to
|
||||
// continue.
|
||||
if self.pos == self.cap && !self.read_done {
|
||||
let reader = self.reader.as_mut().unwrap();
|
||||
let n = try_ready!(reader.poll_read(&mut self.buf));
|
||||
if n == 0 {
|
||||
self.read_done = true;
|
||||
} else {
|
||||
self.pos = 0;
|
||||
self.cap = n;
|
||||
}
|
||||
}
|
||||
|
||||
// If our buffer has some data, let's write it out!
|
||||
while self.pos < self.cap {
|
||||
let writer = self.writer.as_mut().unwrap();
|
||||
let i = try_ready!(writer.poll_write(&self.buf[self.pos..self.cap]));
|
||||
if i == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::WriteZero,
|
||||
"write zero byte into writer"));
|
||||
} else {
|
||||
self.pos += i;
|
||||
self.amt += i as u64;
|
||||
}
|
||||
}
|
||||
|
||||
// If we've written al the data and we've seen EOF, flush out the
|
||||
// data and finish the transfer.
|
||||
// done with the entire transfer.
|
||||
if self.pos == self.cap && self.read_done {
|
||||
try_ready!(self.writer.as_mut().unwrap().poll_flush());
|
||||
let reader = self.reader.take().unwrap();
|
||||
let writer = self.writer.take().unwrap();
|
||||
return Ok((self.amt, reader, writer).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use std::io;
|
||||
|
||||
use futures::{Poll, Future, Async};
|
||||
|
||||
use AsyncWrite;
|
||||
|
||||
/// A future used to fully flush an I/O object.
|
||||
///
|
||||
/// Resolves to the underlying I/O object once the flush operation is complete.
|
||||
///
|
||||
/// Created by the [`flush`] function.
|
||||
///
|
||||
/// [`flush`]: fn.flush.html
|
||||
#[derive(Debug)]
|
||||
pub struct Flush<A> {
|
||||
a: Option<A>,
|
||||
}
|
||||
|
||||
/// Creates a future which will entirely flush an I/O object and then yield the
|
||||
/// object itself.
|
||||
///
|
||||
/// This function will consume the object provided if an error happens, and
|
||||
/// otherwise it will repeatedly call `flush` until it sees `Ok(())`, scheduling
|
||||
/// a retry if `WouldBlock` is seen along the way.
|
||||
pub fn flush<A>(a: A) -> Flush<A>
|
||||
where A: AsyncWrite,
|
||||
{
|
||||
Flush {
|
||||
a: Some(a),
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Future for Flush<A>
|
||||
where A: AsyncWrite,
|
||||
{
|
||||
type Item = A;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<A, io::Error> {
|
||||
try_ready!(self.a.as_mut().unwrap().poll_flush());
|
||||
Ok(Async::Ready(self.a.take().unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
//! I/O conveniences when working with primitives in `tokio-core`
|
||||
//!
|
||||
//! Contains various combinators to work with I/O objects and type definitions
|
||||
//! as well.
|
||||
//!
|
||||
//! A description of the high-level I/O combinators can be [found online] in
|
||||
//! addition to a description of the [low level details].
|
||||
//!
|
||||
//! [found online]: https://tokio.rs/docs/getting-started/core/
|
||||
//! [low level details]: https://tokio.rs/docs/going-deeper-tokio/core-low-level/
|
||||
|
||||
mod copy;
|
||||
mod flush;
|
||||
mod read;
|
||||
mod read_exact;
|
||||
mod read_to_end;
|
||||
mod read_until;
|
||||
mod shutdown;
|
||||
mod write_all;
|
||||
|
||||
pub use allow_std::AllowStdIo;
|
||||
pub use self::copy::{copy, Copy};
|
||||
pub use self::flush::{flush, Flush};
|
||||
pub use lines::{lines, Lines};
|
||||
pub use self::read::{read, Read};
|
||||
pub use self::read_exact::{read_exact, ReadExact};
|
||||
pub use self::read_to_end::{read_to_end, ReadToEnd};
|
||||
pub use self::read_until::{read_until, ReadUntil};
|
||||
pub use self::shutdown::{shutdown, Shutdown};
|
||||
pub use split::{ReadHalf, WriteHalf};
|
||||
pub use window::Window;
|
||||
pub use self::write_all::{write_all, WriteAll};
|
||||
@@ -0,0 +1,56 @@
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use AsyncRead;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<R, T> {
|
||||
Pending {
|
||||
rd: R,
|
||||
buf: T,
|
||||
},
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Tries to read some bytes directly into the given `buf` in asynchronous
|
||||
/// manner, returning a future type.
|
||||
///
|
||||
/// The returned future will resolve to both the I/O stream and the buffer
|
||||
/// as well as the number of bytes read once the read operation is completed.
|
||||
pub fn read<R, T>(rd: R, buf: T) -> Read<R, T>
|
||||
where R: AsyncRead,
|
||||
T: AsMut<[u8]>
|
||||
{
|
||||
Read { state: State::Pending { rd: rd, buf: buf } }
|
||||
}
|
||||
|
||||
/// A future which can be used to easily read available number of bytes to fill
|
||||
/// a buffer.
|
||||
///
|
||||
/// Created by the [`read`] function.
|
||||
#[derive(Debug)]
|
||||
pub struct Read<R, T> {
|
||||
state: State<R, T>,
|
||||
}
|
||||
|
||||
impl<R, T> Future for Read<R, T>
|
||||
where R: AsyncRead,
|
||||
T: AsMut<[u8]>
|
||||
{
|
||||
type Item = (R, T, usize);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(R, T, usize), io::Error> {
|
||||
let nread = match self.state {
|
||||
State::Pending { ref mut rd, ref mut buf } => try_ready!(rd.poll_read(&mut buf.as_mut()[..])),
|
||||
State::Empty => panic!("poll a Read after it's done"),
|
||||
};
|
||||
|
||||
match mem::replace(&mut self.state, State::Empty) {
|
||||
State::Pending { rd, buf } => Ok((rd, buf, nread).into()),
|
||||
State::Empty => panic!("invalid internal state"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
use futures::{Poll, Future};
|
||||
|
||||
use AsyncRead;
|
||||
|
||||
/// A future which can be used to easily read exactly enough bytes to fill
|
||||
/// a buffer.
|
||||
///
|
||||
/// Created by the [`read_exact`] function.
|
||||
///
|
||||
/// [`read_exact`]: fn.read_exact.html
|
||||
#[derive(Debug)]
|
||||
pub struct ReadExact<A, T> {
|
||||
state: State<A, T>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<A, T> {
|
||||
Reading {
|
||||
a: A,
|
||||
buf: T,
|
||||
pos: usize,
|
||||
},
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Creates a future which will read exactly enough bytes to fill `buf`,
|
||||
/// returning an error if EOF is hit sooner.
|
||||
///
|
||||
/// The returned future will resolve to both the I/O stream as well as the
|
||||
/// buffer once the read operation is completed.
|
||||
///
|
||||
/// In the case of an error the buffer and the object will be discarded, with
|
||||
/// the error yielded. In the case of success the object will be destroyed and
|
||||
/// the buffer will be returned, with all data read from the stream appended to
|
||||
/// the buffer.
|
||||
pub fn read_exact<A, T>(a: A, buf: T) -> ReadExact<A, T>
|
||||
where A: AsyncRead,
|
||||
T: AsMut<[u8]>,
|
||||
{
|
||||
ReadExact {
|
||||
state: State::Reading {
|
||||
a: a,
|
||||
buf: buf,
|
||||
pos: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn eof() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::UnexpectedEof, "early eof")
|
||||
}
|
||||
|
||||
impl<A, T> Future for ReadExact<A, T>
|
||||
where A: AsyncRead,
|
||||
T: AsMut<[u8]>,
|
||||
{
|
||||
type Item = (A, T);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(A, T), io::Error> {
|
||||
match self.state {
|
||||
State::Reading { ref mut a, ref mut buf, ref mut pos } => {
|
||||
let buf = buf.as_mut();
|
||||
while *pos < buf.len() {
|
||||
let n = try_ready!(a.poll_read(&mut buf[*pos..]));
|
||||
*pos += n;
|
||||
if n == 0 {
|
||||
return Err(eof())
|
||||
}
|
||||
}
|
||||
}
|
||||
State::Empty => panic!("poll a ReadExact after it's done"),
|
||||
}
|
||||
|
||||
match mem::replace(&mut self.state, State::Empty) {
|
||||
State::Reading { a, buf, .. } => Ok((a, buf).into()),
|
||||
State::Empty => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
use futures::{Poll, Future};
|
||||
|
||||
use AsyncRead;
|
||||
|
||||
/// A future which can be used to easily read the entire contents of a stream
|
||||
/// into a vector.
|
||||
///
|
||||
/// Created by the [`read_to_end`] function.
|
||||
///
|
||||
/// [`read_to_end`]: fn.read_to_end.html
|
||||
#[derive(Debug)]
|
||||
pub struct ReadToEnd<A> {
|
||||
state: State<A>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<A> {
|
||||
Reading {
|
||||
a: A,
|
||||
buf: Vec<u8>,
|
||||
},
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Creates a future which will read all the bytes associated with the I/O
|
||||
/// object `A` into the buffer provided.
|
||||
///
|
||||
/// In the case of an error the buffer and the object will be discarded, with
|
||||
/// the error yielded. In the case of success the object will be destroyed and
|
||||
/// the buffer will be returned, with all data read from the stream appended to
|
||||
/// the buffer.
|
||||
pub fn read_to_end<A>(a: A, buf: Vec<u8>) -> ReadToEnd<A>
|
||||
where A: AsyncRead,
|
||||
{
|
||||
ReadToEnd {
|
||||
state: State::Reading {
|
||||
a: a,
|
||||
buf: buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Future for ReadToEnd<A>
|
||||
where A: AsyncRead,
|
||||
{
|
||||
type Item = (A, Vec<u8>);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(A, Vec<u8>), io::Error> {
|
||||
match self.state {
|
||||
State::Reading { ref mut a, ref mut buf } => {
|
||||
// If we get `Ok`, then we know the stream hit EOF and we're done. If we
|
||||
// hit "would block" then all the read data so far is in our buffer, and
|
||||
// otherwise we propagate errors
|
||||
try_nb!(a.read_to_end(buf));
|
||||
},
|
||||
State::Empty => panic!("poll ReadToEnd after it's done"),
|
||||
}
|
||||
|
||||
match mem::replace(&mut self.state, State::Empty) {
|
||||
State::Reading { a, buf } => Ok((a, buf).into()),
|
||||
State::Empty => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
use std::io::{self, BufRead};
|
||||
use std::mem;
|
||||
|
||||
use futures::{Poll, Future};
|
||||
|
||||
use AsyncRead;
|
||||
|
||||
/// A future which can be used to easily read the contents of a stream into a
|
||||
/// vector until the delimiter is reached.
|
||||
///
|
||||
/// Created by the [`read_until`] function.
|
||||
///
|
||||
/// [`read_until`]: fn.read_until.html
|
||||
#[derive(Debug)]
|
||||
pub struct ReadUntil<A> {
|
||||
state: State<A>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<A> {
|
||||
Reading {
|
||||
a: A,
|
||||
byte: u8,
|
||||
buf: Vec<u8>,
|
||||
},
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Creates a future which will read all the bytes associated with the I/O
|
||||
/// object `A` into the buffer provided until the delimiter `byte` is reached.
|
||||
/// This method is the async equivalent to [`BufRead::read_until`].
|
||||
///
|
||||
/// In case of an error the buffer and the object will be discarded, with
|
||||
/// the error yielded. In the case of success the object will be destroyed and
|
||||
/// the buffer will be returned, with all bytes up to, and including, the delimiter
|
||||
/// (if found).
|
||||
///
|
||||
/// [`BufRead::read_until`]: https://doc.rust-lang.org/std/io/trait.BufRead.html#method.read_until
|
||||
pub fn read_until<A>(a: A, byte: u8, buf: Vec<u8>) -> ReadUntil<A>
|
||||
where A: AsyncRead + BufRead,
|
||||
{
|
||||
ReadUntil {
|
||||
state: State::Reading {
|
||||
a: a,
|
||||
byte: byte,
|
||||
buf: buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Future for ReadUntil<A>
|
||||
where A: AsyncRead + BufRead
|
||||
{
|
||||
type Item = (A, Vec<u8>);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(A, Vec<u8>), io::Error> {
|
||||
match self.state {
|
||||
State::Reading { ref mut a, byte, ref mut buf } => {
|
||||
// If we get `Ok(n)`, then we know the stream hit EOF or the delimiter.
|
||||
// and just return it, as we are finished.
|
||||
// If we hit "would block" then all the read data so far
|
||||
// is in our buffer, and otherwise we propagate errors.
|
||||
try_nb!(a.read_until(byte, buf));
|
||||
},
|
||||
State::Empty => panic!("poll ReadUntil after it's done"),
|
||||
}
|
||||
|
||||
match mem::replace(&mut self.state, State::Empty) {
|
||||
State::Reading { a, byte: _, buf } => Ok((a, buf).into()),
|
||||
State::Empty => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use std::io;
|
||||
|
||||
use futures::{Poll, Future, Async};
|
||||
|
||||
use AsyncWrite;
|
||||
|
||||
/// A future used to fully shutdown an I/O object.
|
||||
///
|
||||
/// Resolves to the underlying I/O object once the shutdown operation is
|
||||
/// complete.
|
||||
///
|
||||
/// Created by the [`shutdown`] function.
|
||||
///
|
||||
/// [`shutdown`]: fn.shutdown.html
|
||||
#[derive(Debug)]
|
||||
pub struct Shutdown<A> {
|
||||
a: Option<A>,
|
||||
}
|
||||
|
||||
/// Creates a future which will entirely shutdown an I/O object and then yield
|
||||
/// the object itself.
|
||||
///
|
||||
/// This function will consume the object provided if an error happens, and
|
||||
/// otherwise it will repeatedly call `shutdown` until it sees `Ok(())`,
|
||||
/// scheduling a retry if `WouldBlock` is seen along the way.
|
||||
pub fn shutdown<A>(a: A) -> Shutdown<A>
|
||||
where A: AsyncWrite,
|
||||
{
|
||||
Shutdown {
|
||||
a: Some(a),
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Future for Shutdown<A>
|
||||
where A: AsyncWrite,
|
||||
{
|
||||
type Item = A;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<A, io::Error> {
|
||||
try_ready!(self.a.as_mut().unwrap().shutdown());
|
||||
Ok(Async::Ready(self.a.take().unwrap()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
use futures::{Poll, Future};
|
||||
|
||||
use AsyncWrite;
|
||||
|
||||
/// A future used to write the entire contents of some data to a stream.
|
||||
///
|
||||
/// This is created by the [`write_all`] top-level method.
|
||||
///
|
||||
/// [`write_all`]: fn.write_all.html
|
||||
#[derive(Debug)]
|
||||
pub struct WriteAll<A, T> {
|
||||
state: State<A, T>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<A, T> {
|
||||
Writing {
|
||||
a: A,
|
||||
buf: T,
|
||||
pos: usize,
|
||||
},
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Creates a future that will write the entire contents of the buffer `buf` to
|
||||
/// the stream `a` provided.
|
||||
///
|
||||
/// The returned future will not return until all the data has been written, and
|
||||
/// the future will resolve to the stream as well as the buffer (for reuse if
|
||||
/// needed).
|
||||
///
|
||||
/// Any error which happens during writing will cause both the stream and the
|
||||
/// buffer to get destroyed.
|
||||
///
|
||||
/// The `buf` parameter here only requires the `AsRef<[u8]>` trait, which should
|
||||
/// be broadly applicable to accepting data which can be converted to a slice.
|
||||
/// The `Window` struct is also available in this crate to provide a different
|
||||
/// window into a slice if necessary.
|
||||
pub fn write_all<A, T>(a: A, buf: T) -> WriteAll<A, T>
|
||||
where A: AsyncWrite,
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
WriteAll {
|
||||
state: State::Writing {
|
||||
a: a,
|
||||
buf: buf,
|
||||
pos: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn zero_write() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::WriteZero, "zero-length write")
|
||||
}
|
||||
|
||||
impl<A, T> Future for WriteAll<A, T>
|
||||
where A: AsyncWrite,
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
type Item = (A, T);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(A, T), io::Error> {
|
||||
match self.state {
|
||||
State::Writing { ref mut a, ref buf, ref mut pos } => {
|
||||
let buf = buf.as_ref();
|
||||
while *pos < buf.len() {
|
||||
let n = try_ready!(a.poll_write(&buf[*pos..]));
|
||||
*pos += n;
|
||||
if n == 0 {
|
||||
return Err(zero_write())
|
||||
}
|
||||
}
|
||||
}
|
||||
State::Empty => panic!("poll a WriteAll after it's done"),
|
||||
}
|
||||
|
||||
match mem::replace(&mut self.state, State::Empty) {
|
||||
State::Writing { a, buf, .. } => Ok((a, buf).into()),
|
||||
State::Empty => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,909 @@
|
||||
use {codec, AsyncRead, AsyncWrite};
|
||||
|
||||
use bytes::{Buf, BufMut, BytesMut, IntoBuf, BigEndian, LittleEndian};
|
||||
use bytes::buf::Chain;
|
||||
|
||||
use futures::{Async, AsyncSink, Stream, Sink, StartSend, Poll};
|
||||
|
||||
use std::{cmp, fmt};
|
||||
use std::error::Error as StdError;
|
||||
use std::io::{self, Cursor};
|
||||
|
||||
/// Configure length delimited `FramedRead`, `FramedWrite`, and `Framed` values.
|
||||
///
|
||||
/// `Builder` enables constructing configured length delimited framers. Note
|
||||
/// that not all configuration settings apply to both encoding and decoding. See
|
||||
/// the documentation for specific methods for more detail.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Builder {
|
||||
// Maximum frame length
|
||||
max_frame_len: usize,
|
||||
|
||||
// Number of bytes representing the field length
|
||||
length_field_len: usize,
|
||||
|
||||
// Number of bytes in the header before the length field
|
||||
length_field_offset: usize,
|
||||
|
||||
// Adjust the length specified in the header field by this amount
|
||||
length_adjustment: isize,
|
||||
|
||||
// Total number of bytes to skip before reading the payload, if not set,
|
||||
// `length_field_len + length_field_offset`
|
||||
num_skip: Option<usize>,
|
||||
|
||||
// Length field byte order (little or big endian)
|
||||
length_field_is_big_endian: bool,
|
||||
}
|
||||
|
||||
/// Adapts a byte stream into a unified `Stream` and `Sink` that works over
|
||||
/// entire frame values.
|
||||
///
|
||||
/// See [module level] documentation for more detail.
|
||||
///
|
||||
/// [module level]: index.html
|
||||
pub struct Framed<T, B: IntoBuf = BytesMut> {
|
||||
inner: FramedRead<FramedWrite<T, B>>,
|
||||
}
|
||||
|
||||
/// Adapts a byte stream to a `Stream` yielding entire frame values.
|
||||
///
|
||||
/// See [module level] documentation for more detail.
|
||||
///
|
||||
/// [module level]: index.html
|
||||
#[derive(Debug)]
|
||||
pub struct FramedRead<T> {
|
||||
inner: codec::FramedRead<T, Decoder>,
|
||||
}
|
||||
|
||||
/// An error when the number of bytes read is more than max frame length.
|
||||
pub struct FrameTooBig {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Decoder {
|
||||
// Configuration values
|
||||
builder: Builder,
|
||||
|
||||
// Read state
|
||||
state: DecodeState,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum DecodeState {
|
||||
Head,
|
||||
Data(usize),
|
||||
}
|
||||
|
||||
/// Adapts a byte stream to a `Sink` accepting entire frame values.
|
||||
///
|
||||
/// See [module level] documentation for more detail.
|
||||
///
|
||||
/// [module level]: index.html
|
||||
pub struct FramedWrite<T, B: IntoBuf = BytesMut> {
|
||||
// I/O type
|
||||
inner: T,
|
||||
|
||||
// Configuration values
|
||||
builder: Builder,
|
||||
|
||||
// Current frame being written
|
||||
frame: Option<Chain<Cursor<BytesMut>, B::Buf>>,
|
||||
}
|
||||
|
||||
// ===== impl Framed =====
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, B: IntoBuf> Framed<T, B> {
|
||||
/// Creates a new `Framed` with default configuration values.
|
||||
pub fn new(inner: T) -> Framed<T, B> {
|
||||
Builder::new().new_framed(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B: IntoBuf> Framed<T, B> {
|
||||
/// Returns a reference to the underlying I/O stream wrapped by `Framed`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
self.inner.get_ref().get_ref()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `Framed`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise being
|
||||
/// worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.inner.get_mut().get_mut()
|
||||
}
|
||||
|
||||
/// Consumes the `Framed`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise being
|
||||
/// worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.into_inner().into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead, B: IntoBuf> Stream for Framed<T, B> {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<BytesMut>, io::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite, B: IntoBuf> Sink for Framed<T, B> {
|
||||
type SinkItem = B;
|
||||
type SinkError = io::Error;
|
||||
|
||||
fn start_send(&mut self, item: B) -> StartSend<B, io::Error> {
|
||||
self.inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), io::Error> {
|
||||
self.inner.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), io::Error> {
|
||||
self.inner.close()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B: IntoBuf> fmt::Debug for Framed<T, B>
|
||||
where T: fmt::Debug,
|
||||
B::Buf: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Framed")
|
||||
.field("inner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedRead =====
|
||||
|
||||
impl<T: AsyncRead> FramedRead<T> {
|
||||
/// Creates a new `FramedRead` with default configuration values.
|
||||
pub fn new(inner: T) -> FramedRead<T> {
|
||||
Builder::new().new_read(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FramedRead<T> {
|
||||
/// Returns the current max frame setting
|
||||
///
|
||||
/// This is the largest size this codec will accept from the wire. Larger
|
||||
/// frames will be rejected.
|
||||
pub fn max_frame_length(&self) -> usize {
|
||||
self.inner.decoder().builder.max_frame_len
|
||||
}
|
||||
|
||||
/// Updates the max frame setting.
|
||||
///
|
||||
/// The change takes effect the next time a frame is decoded. In other
|
||||
/// words, if a frame is currently in process of being decoded with a frame
|
||||
/// size greater than `val` but less than the max frame length in effect
|
||||
/// before calling this function, then the frame will be allowed.
|
||||
pub fn set_max_frame_length(&mut self, val: usize) {
|
||||
self.inner.decoder_mut().builder.max_frame_length(val);
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying I/O stream wrapped by `FramedRead`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
self.inner.get_ref()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `FramedRead`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise being
|
||||
/// worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.inner.get_mut()
|
||||
}
|
||||
|
||||
/// Consumes the `FramedRead`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise being
|
||||
/// worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> Stream for FramedRead<T> {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<BytesMut>, io::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sink> Sink for FramedRead<T> {
|
||||
type SinkItem = T::SinkItem;
|
||||
type SinkError = T::SinkError;
|
||||
|
||||
fn start_send(&mut self, item: T::SinkItem) -> StartSend<T::SinkItem, T::SinkError> {
|
||||
self.inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), T::SinkError> {
|
||||
self.inner.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), T::SinkError> {
|
||||
self.inner.close()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: io::Write> io::Write for FramedRead<T> {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
self.inner.get_mut().write(src)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner.get_mut().flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite> AsyncWrite for FramedRead<T> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
self.inner.get_mut().shutdown()
|
||||
}
|
||||
|
||||
fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
|
||||
self.inner.get_mut().write_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Decoder ======
|
||||
|
||||
impl Decoder {
|
||||
fn decode_head(&mut self, src: &mut BytesMut) -> io::Result<Option<usize>> {
|
||||
let head_len = self.builder.num_head_bytes();
|
||||
let field_len = self.builder.length_field_len;
|
||||
|
||||
if src.len() < head_len {
|
||||
// Not enough data
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let n = {
|
||||
let mut src = Cursor::new(&mut *src);
|
||||
|
||||
// Skip the required bytes
|
||||
src.advance(self.builder.length_field_offset);
|
||||
|
||||
// match endianess
|
||||
let n = if self.builder.length_field_is_big_endian {
|
||||
src.get_uint::<BigEndian>(field_len)
|
||||
} else {
|
||||
src.get_uint::<LittleEndian>(field_len)
|
||||
};
|
||||
|
||||
if n > self.builder.max_frame_len as u64 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, FrameTooBig {
|
||||
_priv: (),
|
||||
}));
|
||||
}
|
||||
|
||||
// The check above ensures there is no overflow
|
||||
let n = n as usize;
|
||||
|
||||
// Adjust `n` with bounds checking
|
||||
let n = if self.builder.length_adjustment < 0 {
|
||||
n.checked_sub(-self.builder.length_adjustment as usize)
|
||||
} else {
|
||||
n.checked_add(self.builder.length_adjustment as usize)
|
||||
};
|
||||
|
||||
// Error handling
|
||||
match n {
|
||||
Some(n) => n,
|
||||
None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "provided length would overflow after adjustment")),
|
||||
}
|
||||
};
|
||||
|
||||
let num_skip = self.builder.get_num_skip();
|
||||
|
||||
if num_skip > 0 {
|
||||
let _ = src.split_to(num_skip);
|
||||
}
|
||||
|
||||
// Ensure that the buffer has enough space to read the incoming
|
||||
// payload
|
||||
src.reserve(n);
|
||||
|
||||
return Ok(Some(n));
|
||||
}
|
||||
|
||||
fn decode_data(&self, n: usize, src: &mut BytesMut) -> io::Result<Option<BytesMut>> {
|
||||
// At this point, the buffer has already had the required capacity
|
||||
// reserved. All there is to do is read.
|
||||
if src.len() < n {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(Some(src.split_to(n)))
|
||||
}
|
||||
}
|
||||
|
||||
impl codec::Decoder for Decoder {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> io::Result<Option<BytesMut>> {
|
||||
let n = match self.state {
|
||||
DecodeState::Head => {
|
||||
match try!(self.decode_head(src)) {
|
||||
Some(n) => {
|
||||
self.state = DecodeState::Data(n);
|
||||
n
|
||||
}
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
DecodeState::Data(n) => n,
|
||||
};
|
||||
|
||||
match try!(self.decode_data(n, src)) {
|
||||
Some(data) => {
|
||||
// Update the decode state
|
||||
self.state = DecodeState::Head;
|
||||
|
||||
// Make sure the buffer has enough space to read the next head
|
||||
src.reserve(self.builder.num_head_bytes());
|
||||
|
||||
Ok(Some(data))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedWrite =====
|
||||
|
||||
impl<T: AsyncWrite, B: IntoBuf> FramedWrite<T, B> {
|
||||
/// Creates a new `FramedWrite` with default configuration values.
|
||||
pub fn new(inner: T) -> FramedWrite<T, B> {
|
||||
Builder::new().new_write(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B: IntoBuf> FramedWrite<T, B> {
|
||||
/// Returns the current max frame setting
|
||||
///
|
||||
/// This is the largest size this codec will write to the wire. Larger
|
||||
/// frames will be rejected.
|
||||
pub fn max_frame_length(&self) -> usize {
|
||||
self.builder.max_frame_len
|
||||
}
|
||||
|
||||
/// Updates the max frame setting.
|
||||
///
|
||||
/// The change takes effect the next time a frame is encoded. In other
|
||||
/// words, if a frame is currently in process of being encoded with a frame
|
||||
/// size greater than `val` but less than the max frame length in effect
|
||||
/// before calling this function, then the frame will be allowed.
|
||||
pub fn set_max_frame_length(&mut self, val: usize) {
|
||||
self.builder.max_frame_length(val);
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `FramedWrite`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `FramedWrite`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise being
|
||||
/// worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Consumes the `FramedWrite`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise being
|
||||
/// worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite, B: IntoBuf> FramedWrite<T, B> {
|
||||
// If there is a buffered frame, try to write it to `T`
|
||||
fn do_write(&mut self) -> Poll<(), io::Error> {
|
||||
if self.frame.is_none() {
|
||||
return Ok(Async::Ready(()));
|
||||
}
|
||||
|
||||
loop {
|
||||
let frame = self.frame.as_mut().unwrap();
|
||||
try_ready!(self.inner.write_buf(frame));
|
||||
|
||||
if !frame.has_remaining() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.frame = None;
|
||||
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
|
||||
fn set_frame(&mut self, buf: B::Buf) -> io::Result<()> {
|
||||
let mut head = BytesMut::with_capacity(8);
|
||||
let n = buf.remaining();
|
||||
|
||||
if n > self.builder.max_frame_len {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, FrameTooBig {
|
||||
_priv: (),
|
||||
}));
|
||||
}
|
||||
|
||||
// Adjust `n` with bounds checking
|
||||
let n = if self.builder.length_adjustment < 0 {
|
||||
n.checked_add(-self.builder.length_adjustment as usize)
|
||||
} else {
|
||||
n.checked_sub(self.builder.length_adjustment as usize)
|
||||
};
|
||||
|
||||
// Error handling
|
||||
let n = match n {
|
||||
Some(n) => n,
|
||||
None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "provided length would overflow after adjustment")),
|
||||
};
|
||||
|
||||
if self.builder.length_field_is_big_endian {
|
||||
head.put_uint::<BigEndian>(n as u64, self.builder.length_field_len);
|
||||
} else {
|
||||
head.put_uint::<LittleEndian>(n as u64, self.builder.length_field_len);
|
||||
}
|
||||
|
||||
debug_assert!(self.frame.is_none());
|
||||
|
||||
self.frame = Some(head.into_buf().chain(buf));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite, B: IntoBuf> Sink for FramedWrite<T, B> {
|
||||
type SinkItem = B;
|
||||
type SinkError = io::Error;
|
||||
|
||||
fn start_send(&mut self, item: B) -> StartSend<B, io::Error> {
|
||||
if !try!(self.do_write()).is_ready() {
|
||||
return Ok(AsyncSink::NotReady(item));
|
||||
}
|
||||
|
||||
try!(self.set_frame(item.into_buf()));
|
||||
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), io::Error> {
|
||||
// Write any buffered frame to T
|
||||
try_ready!(self.do_write());
|
||||
|
||||
// Try flushing the underlying IO
|
||||
try_ready!(self.inner.poll_flush());
|
||||
|
||||
return Ok(Async::Ready(()));
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), io::Error> {
|
||||
try_ready!(self.poll_complete());
|
||||
self.inner.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Stream, B: IntoBuf> Stream for FramedWrite<T, B> {
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: io::Read, B: IntoBuf> io::Read for FramedWrite<T, B> {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.get_mut().read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead, U: IntoBuf> AsyncRead for FramedWrite<T, U> {
|
||||
fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
|
||||
self.get_mut().read_buf(buf)
|
||||
}
|
||||
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.get_ref().prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B: IntoBuf> fmt::Debug for FramedWrite<T, B>
|
||||
where T: fmt::Debug,
|
||||
B::Buf: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("FramedWrite")
|
||||
.field("inner", &self.inner)
|
||||
.field("builder", &self.builder)
|
||||
.field("frame", &self.frame)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Builder =====
|
||||
|
||||
impl Builder {
|
||||
/// Creates a new length delimited framer builder with default configuration
|
||||
/// values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_field_offset(0)
|
||||
/// .length_field_length(2)
|
||||
/// .length_adjustment(0)
|
||||
/// .num_skip(0)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new() -> Builder {
|
||||
Builder {
|
||||
// Default max frame length of 8MB
|
||||
max_frame_len: 8 * 1_024 * 1_024,
|
||||
|
||||
// Default byte length of 4
|
||||
length_field_len: 4,
|
||||
|
||||
// Default to the header field being at the start of the header.
|
||||
length_field_offset: 0,
|
||||
|
||||
length_adjustment: 0,
|
||||
|
||||
// Total number of bytes to skip before reading the payload, if not set,
|
||||
// `length_field_len + length_field_offset`
|
||||
num_skip: None,
|
||||
|
||||
// Default to reading the length field in network (big) endian.
|
||||
length_field_is_big_endian: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the length field as a big endian integer
|
||||
///
|
||||
/// This is the default setting.
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .big_endian()
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn big_endian(&mut self) -> &mut Self {
|
||||
self.length_field_is_big_endian = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Read the length field as a little endian integer
|
||||
///
|
||||
/// The default setting is big endian.
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .little_endian()
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn little_endian(&mut self) -> &mut Self {
|
||||
self.length_field_is_big_endian = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Read the length field as a native endian integer
|
||||
///
|
||||
/// The default setting is big endian.
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .native_endian()
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn native_endian(&mut self) -> &mut Self {
|
||||
if cfg!(target_endian = "big") {
|
||||
self.big_endian()
|
||||
} else {
|
||||
self.little_endian()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the max frame length
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding. The
|
||||
/// default value is 8MB.
|
||||
///
|
||||
/// When decoding, the length field read from the byte stream is checked
|
||||
/// against this setting **before** any adjustments are applied. When
|
||||
/// encoding, the length of the submitted payload is checked against this
|
||||
/// setting.
|
||||
///
|
||||
/// When frames exceed the max length, an `io::Error` with the custom value
|
||||
/// of the `FrameTooBig` type will be returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .max_frame_length(8 * 1024)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn max_frame_length(&mut self, val: usize) -> &mut Self {
|
||||
self.max_frame_len = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the number of bytes used to represent the length field
|
||||
///
|
||||
/// The default value is `4`. The max value is `8`.
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_field_length(4)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn length_field_length(&mut self, val: usize) -> &mut Self {
|
||||
assert!(val > 0 && val <= 8, "invalid length field length");
|
||||
self.length_field_len = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the number of bytes in the header before the length field
|
||||
///
|
||||
/// This configuration option only applies to decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_field_offset(1)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn length_field_offset(&mut self, val: usize) -> &mut Self {
|
||||
self.length_field_offset = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Delta between the payload length specified in the header and the real
|
||||
/// payload length
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_adjustment(-2)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn length_adjustment(&mut self, val: isize) -> &mut Self {
|
||||
self.length_adjustment = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the number of bytes to skip before reading the payload
|
||||
///
|
||||
/// Default value is `length_field_len + length_field_offset`
|
||||
///
|
||||
/// This configuration option only applies to decoding
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .num_skip(4)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn num_skip(&mut self, val: usize) -> &mut Self {
|
||||
self.num_skip = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Create a configured length delimited `FramedRead`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_field_offset(0)
|
||||
/// .length_field_length(2)
|
||||
/// .length_adjustment(0)
|
||||
/// .num_skip(0)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new_read<T>(&self, upstream: T) -> FramedRead<T>
|
||||
where T: AsyncRead,
|
||||
{
|
||||
FramedRead {
|
||||
inner: codec::FramedRead::new(upstream, Decoder {
|
||||
builder: *self,
|
||||
state: DecodeState::Head,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a configured length delimited `FramedWrite`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio_io;
|
||||
/// # extern crate bytes;
|
||||
/// # use tokio_io::AsyncWrite;
|
||||
/// # use tokio_io::codec::length_delimited;
|
||||
/// # use bytes::BytesMut;
|
||||
/// # fn write_frame<T: AsyncWrite>(io: T) {
|
||||
/// # let _: length_delimited::FramedWrite<T, BytesMut> =
|
||||
/// length_delimited::Builder::new()
|
||||
/// .length_field_length(2)
|
||||
/// .new_write(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn new_write<T, B>(&self, inner: T) -> FramedWrite<T, B>
|
||||
where T: AsyncWrite,
|
||||
B: IntoBuf,
|
||||
{
|
||||
FramedWrite {
|
||||
inner: inner,
|
||||
builder: *self,
|
||||
frame: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a configured length delimited `Framed`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio_io;
|
||||
/// # extern crate bytes;
|
||||
/// # use tokio_io::{AsyncRead, AsyncWrite};
|
||||
/// # use tokio_io::codec::length_delimited;
|
||||
/// # use bytes::BytesMut;
|
||||
/// # fn write_frame<T: AsyncRead + AsyncWrite>(io: T) {
|
||||
/// # let _: length_delimited::Framed<T, BytesMut> =
|
||||
/// length_delimited::Builder::new()
|
||||
/// .length_field_length(2)
|
||||
/// .new_framed(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn new_framed<T, B>(&self, inner: T) -> Framed<T, B>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
B: IntoBuf
|
||||
{
|
||||
let inner = self.new_read(self.new_write(inner));
|
||||
Framed { inner: inner }
|
||||
}
|
||||
|
||||
fn num_head_bytes(&self) -> usize {
|
||||
let num = self.length_field_offset + self.length_field_len;
|
||||
cmp::max(num, self.num_skip.unwrap_or(0))
|
||||
}
|
||||
|
||||
fn get_num_skip(&self) -> usize {
|
||||
self.num_skip.unwrap_or(self.length_field_offset + self.length_field_len)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===== impl FrameTooBig =====
|
||||
|
||||
impl fmt::Debug for FrameTooBig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("FrameTooBig")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FrameTooBig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for FrameTooBig {
|
||||
fn description(&self) -> &str {
|
||||
"frame size too big"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//! Core I/O traits and combinators when working with Tokio.
|
||||
//!
|
||||
//! A description of the high-level I/O combinators can be [found online] in
|
||||
//! addition to a description of the [low level details].
|
||||
//!
|
||||
//! [found online]: https://tokio.rs/docs/getting-started/core/
|
||||
//! [low level details]: https://tokio.rs/docs/going-deeper-tokio/core-low-level/
|
||||
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-io/0.1.6")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate bytes;
|
||||
|
||||
use std::io as std_io;
|
||||
|
||||
use futures::{Future, Stream};
|
||||
|
||||
/// A convenience typedef around a `Future` whose error component is `io::Error`
|
||||
pub type IoFuture<T> = Box<Future<Item = T, Error = std_io::Error> + Send>;
|
||||
|
||||
/// A convenience typedef around a `Stream` whose error component is `io::Error`
|
||||
pub type IoStream<T> = Box<Stream<Item = T, Error = std_io::Error> + Send>;
|
||||
|
||||
/// A convenience macro for working with `io::Result<T>` from the `Read` and
|
||||
/// `Write` traits.
|
||||
///
|
||||
/// This macro takes `io::Result<T>` as input, and returns `T` as the output. If
|
||||
/// the input type is of the `Err` variant, then `Poll::NotReady` is returned if
|
||||
/// it indicates `WouldBlock` or otherwise `Err` is returned.
|
||||
#[macro_export]
|
||||
macro_rules! try_nb {
|
||||
($e:expr) => (match $e {
|
||||
Ok(t) => t,
|
||||
Err(ref e) if e.kind() == ::std::io::ErrorKind::WouldBlock => {
|
||||
return Ok(::futures::Async::NotReady)
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
})
|
||||
}
|
||||
|
||||
pub mod io;
|
||||
pub mod codec;
|
||||
|
||||
mod allow_std;
|
||||
mod async_read;
|
||||
mod async_write;
|
||||
mod framed;
|
||||
mod framed_read;
|
||||
mod framed_write;
|
||||
mod length_delimited;
|
||||
mod lines;
|
||||
mod split;
|
||||
mod window;
|
||||
|
||||
pub use self::async_read::AsyncRead;
|
||||
pub use self::async_write::AsyncWrite;
|
||||
|
||||
fn _assert_objects() {
|
||||
fn _assert<T>() {}
|
||||
_assert::<Box<AsyncRead>>();
|
||||
_assert::<Box<AsyncWrite>>();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
use std::io::{self, BufRead};
|
||||
use std::mem;
|
||||
|
||||
use futures::{Poll, Stream};
|
||||
|
||||
use AsyncRead;
|
||||
|
||||
/// Combinator created by the top-level `lines` method which is a stream over
|
||||
/// the lines of text on an I/O object.
|
||||
#[derive(Debug)]
|
||||
pub struct Lines<A> {
|
||||
io: A,
|
||||
line: String,
|
||||
}
|
||||
|
||||
/// Creates a new stream from the I/O object given representing the lines of
|
||||
/// input that are found on `A`.
|
||||
///
|
||||
/// This method takes an asynchronous I/O object, `a`, and returns a `Stream` of
|
||||
/// lines that the object contains. The returned stream will reach its end once
|
||||
/// `a` reaches EOF.
|
||||
pub fn lines<A>(a: A) -> Lines<A>
|
||||
where A: AsyncRead + BufRead,
|
||||
{
|
||||
Lines {
|
||||
io: a,
|
||||
line: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Lines<A> {
|
||||
/// Returns the underlying I/O object.
|
||||
///
|
||||
/// Note that this may lose data already read into internal buffers. It's
|
||||
/// recommended to only call this once the stream has reached its end.
|
||||
pub fn into_inner(self) -> A {
|
||||
self.io
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Stream for Lines<A>
|
||||
where A: AsyncRead + BufRead,
|
||||
{
|
||||
type Item = String;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<String>, io::Error> {
|
||||
let n = try_nb!(self.io.read_line(&mut self.line));
|
||||
if n == 0 && self.line.len() == 0 {
|
||||
return Ok(None.into())
|
||||
}
|
||||
if self.line.ends_with("\n") {
|
||||
self.line.pop();
|
||||
if self.line.ends_with("\r") {
|
||||
self.line.pop();
|
||||
}
|
||||
}
|
||||
Ok(Some(mem::replace(&mut self.line, String::new())).into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use futures::{Async, Poll};
|
||||
use futures::sync::BiLock;
|
||||
use bytes::{Buf, BufMut};
|
||||
|
||||
use {AsyncRead, AsyncWrite};
|
||||
|
||||
/// The readable half of an object returned from `AsyncRead::split`.
|
||||
#[derive(Debug)]
|
||||
pub struct ReadHalf<T> {
|
||||
handle: BiLock<T>,
|
||||
}
|
||||
|
||||
/// The writable half of an object returned from `AsyncRead::split`.
|
||||
#[derive(Debug)]
|
||||
pub struct WriteHalf<T> {
|
||||
handle: BiLock<T>,
|
||||
}
|
||||
|
||||
pub fn split<T: AsyncRead + AsyncWrite>(t: T) -> (ReadHalf<T>, WriteHalf<T>) {
|
||||
let (a, b) = BiLock::new(t);
|
||||
(ReadHalf { handle: a }, WriteHalf { handle: b })
|
||||
}
|
||||
|
||||
fn would_block() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::WouldBlock, "would block")
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> Read for ReadHalf<T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match self.handle.poll_lock() {
|
||||
Async::Ready(mut l) => l.read(buf),
|
||||
Async::NotReady => Err(would_block()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> AsyncRead for ReadHalf<T> {
|
||||
fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
|
||||
match self.handle.poll_lock() {
|
||||
Async::Ready(mut l) => l.read_buf(buf),
|
||||
Async::NotReady => Err(would_block()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite> Write for WriteHalf<T> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match self.handle.poll_lock() {
|
||||
Async::Ready(mut l) => l.write(buf),
|
||||
Async::NotReady => Err(would_block()),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self.handle.poll_lock() {
|
||||
Async::Ready(mut l) => l.flush(),
|
||||
Async::NotReady => Err(would_block()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite> AsyncWrite for WriteHalf<T> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
match self.handle.poll_lock() {
|
||||
Async::Ready(mut l) => l.shutdown(),
|
||||
Async::NotReady => Err(would_block()),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error>
|
||||
where Self: Sized,
|
||||
{
|
||||
match self.handle.poll_lock() {
|
||||
Async::Ready(mut l) => l.write_buf(buf),
|
||||
Async::NotReady => Err(would_block()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
use std::ops;
|
||||
|
||||
/// A owned window around an underlying buffer.
|
||||
///
|
||||
/// Normally slices work great for considering sub-portions of a buffer, but
|
||||
/// unfortunately a slice is a *borrowed* type in Rust which has an associated
|
||||
/// lifetime. When working with future and async I/O these lifetimes are not
|
||||
/// always appropriate, and are sometimes difficult to store in tasks. This
|
||||
/// type strives to fill this gap by providing an "owned slice" around an
|
||||
/// underlying buffer of bytes.
|
||||
///
|
||||
/// A `Window<T>` wraps an underlying buffer, `T`, and has configurable
|
||||
/// start/end indexes to alter the behavior of the `AsRef<[u8]>` implementation
|
||||
/// that this type carries.
|
||||
///
|
||||
/// This type can be particularly useful when working with the `write_all`
|
||||
/// combinator in this crate. Data can be sliced via `Window`, consumed by
|
||||
/// `write_all`, and then earned back once the write operation finishes through
|
||||
/// the `into_inner` method on this type.
|
||||
#[derive(Debug)]
|
||||
pub struct Window<T> {
|
||||
inner: T,
|
||||
range: ops::Range<usize>,
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> Window<T> {
|
||||
/// Creates a new window around the buffer `t` defaulting to the entire
|
||||
/// slice.
|
||||
///
|
||||
/// Further methods can be called on the returned `Window<T>` to alter the
|
||||
/// window into the data provided.
|
||||
pub fn new(t: T) -> Window<T> {
|
||||
Window {
|
||||
range: 0..t.as_ref().len(),
|
||||
inner: t,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a shared reference to the underlying buffer inside of this
|
||||
/// `Window`.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying buffer inside of this
|
||||
/// `Window`.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Consumes this `Window`, returning the underlying buffer.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
/// Returns the starting index of this window into the underlying buffer
|
||||
/// `T`.
|
||||
pub fn start(&self) -> usize {
|
||||
self.range.start
|
||||
}
|
||||
|
||||
/// Returns the end index of this window into the underlying buffer
|
||||
/// `T`.
|
||||
pub fn end(&self) -> usize {
|
||||
self.range.end
|
||||
}
|
||||
|
||||
/// Changes the starting index of this window to the index specified.
|
||||
///
|
||||
/// Returns the windows back to chain multiple calls to this method.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if `start` is out of bounds for the underlying
|
||||
/// slice or if it comes after the `end` configured in this window.
|
||||
pub fn set_start(&mut self, start: usize) -> &mut Window<T> {
|
||||
assert!(start <= self.inner.as_ref().len());
|
||||
assert!(start <= self.range.end);
|
||||
self.range.start = start;
|
||||
self
|
||||
}
|
||||
|
||||
/// Changes the end index of this window to the index specified.
|
||||
///
|
||||
/// Returns the windows back to chain multiple calls to this method.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if `end` is out of bounds for the underlying
|
||||
/// slice or if it comes before the `start` configured in this window.
|
||||
pub fn set_end(&mut self, end: usize) -> &mut Window<T> {
|
||||
assert!(end <= self.inner.as_ref().len());
|
||||
assert!(self.range.start <= end);
|
||||
self.range.end = end;
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: how about a generic set() method along the lines of:
|
||||
//
|
||||
// buffer.set(..3)
|
||||
// .set(0..2)
|
||||
// .set(4..)
|
||||
//
|
||||
// etc.
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> AsRef<[u8]> for Window<T> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.inner.as_ref()[self.range.start..self.range.end]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsMut<[u8]>> AsMut<[u8]> for Window<T> {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.inner.as_mut()[self.range.start..self.range.end]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
extern crate tokio_io;
|
||||
extern crate bytes;
|
||||
extern crate futures;
|
||||
|
||||
use tokio_io::AsyncRead;
|
||||
use bytes::{BytesMut, BufMut};
|
||||
use futures::Async;
|
||||
|
||||
use std::io::{self, Read};
|
||||
|
||||
#[test]
|
||||
fn read_buf_success() {
|
||||
struct R;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
buf[0..11].copy_from_slice(b"hello world");
|
||||
Ok(11)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for R {}
|
||||
|
||||
let mut buf = BytesMut::with_capacity(65);
|
||||
|
||||
let n = match R.read_buf(&mut buf).unwrap() {
|
||||
Async::Ready(n) => n,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
assert_eq!(11, n);
|
||||
assert_eq!(buf[..], b"hello world"[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_error() {
|
||||
struct R;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "other"))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for R {}
|
||||
|
||||
let mut buf = BytesMut::with_capacity(65);
|
||||
|
||||
let err = R.read_buf(&mut buf).unwrap_err();
|
||||
assert_eq!(err.kind(), io::ErrorKind::Other);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_no_capacity() {
|
||||
struct R;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for R {}
|
||||
|
||||
// Can't create BytesMut w/ zero capacity, so fill it up
|
||||
let mut buf = BytesMut::with_capacity(64);
|
||||
buf.put(&[0; 64][..]);
|
||||
|
||||
let n = match R.read_buf(&mut buf).unwrap() {
|
||||
Async::Ready(n) => n,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
assert_eq!(0, n);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_no_uninitialized() {
|
||||
struct R;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
for b in buf {
|
||||
assert_eq!(0, *b);
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for R {}
|
||||
|
||||
// Can't create BytesMut w/ zero capacity, so fill it up
|
||||
let mut buf = BytesMut::with_capacity(64);
|
||||
|
||||
let n = match R.read_buf(&mut buf).unwrap() {
|
||||
Async::Ready(n) => n,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
assert_eq!(0, n);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_uninitialized_ok() {
|
||||
struct R;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
assert_eq!(buf[0..11], b"hello world"[..]);
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for R {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Can't create BytesMut w/ zero capacity, so fill it up
|
||||
let mut buf = BytesMut::with_capacity(64);
|
||||
unsafe {
|
||||
buf.bytes_mut()[0..11].copy_from_slice(b"hello world");
|
||||
}
|
||||
|
||||
let n = match R.read_buf(&mut buf).unwrap() {
|
||||
Async::Ready(n) => n,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
assert_eq!(0, n);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_translate_wouldblock_to_not_ready() {
|
||||
struct R;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||
Err(io::Error::new(io::ErrorKind::WouldBlock, ""))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for R {}
|
||||
|
||||
let mut buf = BytesMut::with_capacity(65);
|
||||
assert!(!R.read_buf(&mut buf).unwrap().is_ready());
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
extern crate tokio_io;
|
||||
extern crate bytes;
|
||||
|
||||
use bytes::{BytesMut, Bytes, BufMut};
|
||||
use tokio_io::codec::{BytesCodec, LinesCodec, Decoder, Encoder};
|
||||
|
||||
#[test]
|
||||
fn bytes_decoder() {
|
||||
let mut codec = BytesCodec::new();
|
||||
let buf = &mut BytesMut::new();
|
||||
buf.put_slice(b"abc");
|
||||
assert_eq!("abc", codec.decode(buf).unwrap().unwrap());
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
buf.put_slice(b"a");
|
||||
assert_eq!("a", codec.decode(buf).unwrap().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_encoder() {
|
||||
let mut codec = BytesCodec::new();
|
||||
|
||||
// Default capacity of BytesMut
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const INLINE_CAP: usize = 4 * 8 - 1;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const INLINE_CAP: usize = 4 * 4 - 1;
|
||||
|
||||
let mut buf = BytesMut::new();
|
||||
codec.encode(Bytes::from_static(&[0; INLINE_CAP + 1]), &mut buf).unwrap();
|
||||
|
||||
// Default capacity of Framed Read
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
|
||||
let mut buf = BytesMut::with_capacity(INITIAL_CAPACITY);
|
||||
codec.encode(Bytes::from_static(&[0; INITIAL_CAPACITY + 1]), &mut buf).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines_decoder() {
|
||||
let mut codec = LinesCodec::new();
|
||||
let buf = &mut BytesMut::new();
|
||||
buf.reserve(200);
|
||||
buf.put("line 1\nline 2\r\nline 3\n\r\n\r");
|
||||
assert_eq!("line 1", codec.decode(buf).unwrap().unwrap());
|
||||
assert_eq!("line 2", codec.decode(buf).unwrap().unwrap());
|
||||
assert_eq!("line 3", codec.decode(buf).unwrap().unwrap());
|
||||
assert_eq!("", codec.decode(buf).unwrap().unwrap());
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
assert_eq!(None, codec.decode_eof(buf).unwrap());
|
||||
buf.put("k");
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
assert_eq!("\rk", codec.decode_eof(buf).unwrap().unwrap());
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
assert_eq!(None, codec.decode_eof(buf).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines_encoder() {
|
||||
let mut codec = BytesCodec::new();
|
||||
|
||||
// Default capacity of BytesMut
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const INLINE_CAP: usize = 4 * 8 - 1;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const INLINE_CAP: usize = 4 * 4 - 1;
|
||||
|
||||
let mut buf = BytesMut::new();
|
||||
codec.encode(Bytes::from_static(&[b'a'; INLINE_CAP + 1]), &mut buf).unwrap();
|
||||
|
||||
// Default capacity of Framed Read
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
|
||||
let mut buf = BytesMut::with_capacity(INITIAL_CAPACITY);
|
||||
codec.encode(Bytes::from_static(&[b'a'; INITIAL_CAPACITY + 1]), &mut buf).unwrap();
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
extern crate tokio_io;
|
||||
extern crate bytes;
|
||||
extern crate futures;
|
||||
|
||||
use futures::{Stream, Future};
|
||||
use std::io::{self, Read};
|
||||
use tokio_io::codec::{Framed, FramedParts, Decoder, Encoder};
|
||||
use tokio_io::AsyncRead;
|
||||
use bytes::{BytesMut, Buf, BufMut, IntoBuf, BigEndian};
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
|
||||
struct U32Codec;
|
||||
|
||||
impl Decoder for U32Codec {
|
||||
type Item = u32;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<u32>> {
|
||||
if buf.len() < 4 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let n = buf.split_to(4).into_buf().get_u32::<BigEndian>();
|
||||
Ok(Some(n))
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder for U32Codec {
|
||||
type Item = u32;
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, item: u32, dst: &mut BytesMut) -> io::Result<()> {
|
||||
// Reserve space
|
||||
dst.reserve(4);
|
||||
dst.put_u32::<BigEndian>(item);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct DontReadIntoThis;
|
||||
|
||||
impl Read for DontReadIntoThis {
|
||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||
Err(io::Error::new(io::ErrorKind::Other,
|
||||
"Read into something you weren't supposed to."))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for DontReadIntoThis {}
|
||||
|
||||
#[test]
|
||||
fn can_read_from_existing_buf() {
|
||||
let parts = FramedParts {
|
||||
inner: DontReadIntoThis,
|
||||
readbuf: vec![0, 0, 0, 42].into(),
|
||||
writebuf: BytesMut::with_capacity(0),
|
||||
};
|
||||
let framed = Framed::from_parts(parts, U32Codec);
|
||||
|
||||
let num = framed
|
||||
.into_future()
|
||||
.map(|(first_num, _)| {
|
||||
first_num.unwrap()
|
||||
})
|
||||
.wait()
|
||||
.map_err(|e| e.0)
|
||||
.unwrap();
|
||||
assert_eq!(num, 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_buf_grows_to_init() {
|
||||
let parts = FramedParts {
|
||||
inner: DontReadIntoThis,
|
||||
readbuf: vec![0, 0, 0, 42].into(),
|
||||
writebuf: BytesMut::with_capacity(0),
|
||||
};
|
||||
let framed = Framed::from_parts(parts, U32Codec);
|
||||
let FramedParts { readbuf, .. } = framed.into_parts();
|
||||
|
||||
assert_eq!(readbuf.capacity(), INITIAL_CAPACITY);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_buf_does_not_shrink() {
|
||||
let parts = FramedParts {
|
||||
inner: DontReadIntoThis,
|
||||
readbuf: vec![0; INITIAL_CAPACITY * 2].into(),
|
||||
writebuf: BytesMut::with_capacity(0),
|
||||
};
|
||||
let framed = Framed::from_parts(parts, U32Codec);
|
||||
let FramedParts { readbuf, .. } = framed.into_parts();
|
||||
|
||||
assert_eq!(readbuf.capacity(), INITIAL_CAPACITY * 2);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
extern crate tokio_io;
|
||||
extern crate bytes;
|
||||
extern crate futures;
|
||||
|
||||
use tokio_io::AsyncRead;
|
||||
use tokio_io::codec::{FramedRead, Decoder};
|
||||
|
||||
use bytes::{BytesMut, Buf, IntoBuf, BigEndian};
|
||||
use futures::Stream;
|
||||
use futures::Async::{Ready, NotReady};
|
||||
|
||||
use std::io::{self, Read};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
macro_rules! mock {
|
||||
($($x:expr,)*) => {{
|
||||
let mut v = VecDeque::new();
|
||||
v.extend(vec![$($x),*]);
|
||||
Mock { calls: v }
|
||||
}};
|
||||
}
|
||||
|
||||
struct U32Decoder;
|
||||
|
||||
impl Decoder for U32Decoder {
|
||||
type Item = u32;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<u32>> {
|
||||
if buf.len() < 4 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let n = buf.split_to(4).into_buf().get_u32::<BigEndian>();
|
||||
Ok(Some(n))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_multi_frame_in_packet() {
|
||||
let mock = mock! {
|
||||
Ok(b"\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02".to_vec()),
|
||||
};
|
||||
|
||||
let mut framed = FramedRead::new(mock, U32Decoder);
|
||||
assert_eq!(Ready(Some(0)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(Some(1)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(Some(2)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(None), framed.poll().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_multi_frame_across_packets() {
|
||||
let mock = mock! {
|
||||
Ok(b"\x00\x00\x00\x00".to_vec()),
|
||||
Ok(b"\x00\x00\x00\x01".to_vec()),
|
||||
Ok(b"\x00\x00\x00\x02".to_vec()),
|
||||
};
|
||||
|
||||
let mut framed = FramedRead::new(mock, U32Decoder);
|
||||
assert_eq!(Ready(Some(0)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(Some(1)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(Some(2)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(None), framed.poll().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_not_ready() {
|
||||
let mock = mock! {
|
||||
Err(io::Error::new(io::ErrorKind::WouldBlock, "")),
|
||||
Ok(b"\x00\x00\x00\x00".to_vec()),
|
||||
Ok(b"\x00\x00\x00\x01".to_vec()),
|
||||
};
|
||||
|
||||
let mut framed = FramedRead::new(mock, U32Decoder);
|
||||
assert_eq!(NotReady, framed.poll().unwrap());
|
||||
assert_eq!(Ready(Some(0)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(Some(1)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(None), framed.poll().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_partial_then_not_ready() {
|
||||
let mock = mock! {
|
||||
Ok(b"\x00\x00".to_vec()),
|
||||
Err(io::Error::new(io::ErrorKind::WouldBlock, "")),
|
||||
Ok(b"\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02".to_vec()),
|
||||
};
|
||||
|
||||
let mut framed = FramedRead::new(mock, U32Decoder);
|
||||
assert_eq!(NotReady, framed.poll().unwrap());
|
||||
assert_eq!(Ready(Some(0)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(Some(1)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(Some(2)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(None), framed.poll().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_err() {
|
||||
let mock = mock! {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "")),
|
||||
};
|
||||
|
||||
let mut framed = FramedRead::new(mock, U32Decoder);
|
||||
assert_eq!(io::ErrorKind::Other, framed.poll().unwrap_err().kind());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_partial_then_err() {
|
||||
let mock = mock! {
|
||||
Ok(b"\x00\x00".to_vec()),
|
||||
Err(io::Error::new(io::ErrorKind::Other, "")),
|
||||
};
|
||||
|
||||
let mut framed = FramedRead::new(mock, U32Decoder);
|
||||
assert_eq!(io::ErrorKind::Other, framed.poll().unwrap_err().kind());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_partial_would_block_then_err() {
|
||||
let mock = mock! {
|
||||
Ok(b"\x00\x00".to_vec()),
|
||||
Err(io::Error::new(io::ErrorKind::WouldBlock, "")),
|
||||
Err(io::Error::new(io::ErrorKind::Other, "")),
|
||||
};
|
||||
|
||||
let mut framed = FramedRead::new(mock, U32Decoder);
|
||||
assert_eq!(NotReady, framed.poll().unwrap());
|
||||
assert_eq!(io::ErrorKind::Other, framed.poll().unwrap_err().kind());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn huge_size() {
|
||||
let data = [0; 32 * 1024];
|
||||
|
||||
let mut framed = FramedRead::new(&data[..], BigDecoder);
|
||||
assert_eq!(Ready(Some(0)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(None), framed.poll().unwrap());
|
||||
|
||||
struct BigDecoder;
|
||||
|
||||
impl Decoder for BigDecoder {
|
||||
type Item = u32;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<u32>> {
|
||||
if buf.len() < 32 * 1024 {
|
||||
return Ok(None);
|
||||
}
|
||||
buf.split_to(32 * 1024);
|
||||
Ok(Some(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn data_remaining_is_error() {
|
||||
let data = [0; 5];
|
||||
|
||||
let mut framed = FramedRead::new(&data[..], U32Decoder);
|
||||
assert_eq!(Ready(Some(0)), framed.poll().unwrap());
|
||||
assert!(framed.poll().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_frames_on_eof() {
|
||||
struct MyDecoder(Vec<u32>);
|
||||
|
||||
impl Decoder for MyDecoder {
|
||||
type Item = u32;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, _buf: &mut BytesMut) -> io::Result<Option<u32>> {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, _buf: &mut BytesMut) -> io::Result<Option<u32>> {
|
||||
if self.0.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(Some(self.0.remove(0)))
|
||||
}
|
||||
}
|
||||
|
||||
let mut framed = FramedRead::new(mock!(), MyDecoder(vec![0, 1, 2, 3]));
|
||||
assert_eq!(Ready(Some(0)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(Some(1)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(Some(2)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(Some(3)), framed.poll().unwrap());
|
||||
assert_eq!(Ready(None), framed.poll().unwrap());
|
||||
}
|
||||
|
||||
// ===== Mock ======
|
||||
|
||||
struct Mock {
|
||||
calls: VecDeque<io::Result<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl Read for Mock {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
match self.calls.pop_front() {
|
||||
Some(Ok(data)) => {
|
||||
debug_assert!(dst.len() >= data.len());
|
||||
dst[..data.len()].copy_from_slice(&data[..]);
|
||||
Ok(data.len())
|
||||
}
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Mock {
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
extern crate tokio_io;
|
||||
extern crate bytes;
|
||||
extern crate futures;
|
||||
|
||||
use tokio_io::AsyncWrite;
|
||||
use tokio_io::codec::{Encoder, FramedWrite};
|
||||
|
||||
use futures::{Sink, Poll};
|
||||
use bytes::{BytesMut, BufMut, BigEndian};
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
macro_rules! mock {
|
||||
($($x:expr,)*) => {{
|
||||
let mut v = VecDeque::new();
|
||||
v.extend(vec![$($x),*]);
|
||||
Mock { calls: v }
|
||||
}};
|
||||
}
|
||||
|
||||
struct U32Encoder;
|
||||
|
||||
impl Encoder for U32Encoder {
|
||||
type Item = u32;
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, item: u32, dst: &mut BytesMut) -> io::Result<()> {
|
||||
// Reserve space
|
||||
dst.reserve(4);
|
||||
dst.put_u32::<BigEndian>(item);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_multi_frame_in_packet() {
|
||||
let mock = mock! {
|
||||
Ok(b"\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02".to_vec()),
|
||||
};
|
||||
|
||||
let mut framed = FramedWrite::new(mock, U32Encoder);
|
||||
assert!(framed.start_send(0).unwrap().is_ready());
|
||||
assert!(framed.start_send(1).unwrap().is_ready());
|
||||
assert!(framed.start_send(2).unwrap().is_ready());
|
||||
|
||||
// Nothing written yet
|
||||
assert_eq!(1, framed.get_ref().calls.len());
|
||||
|
||||
// Flush the writes
|
||||
assert!(framed.poll_complete().unwrap().is_ready());
|
||||
|
||||
assert_eq!(0, framed.get_ref().calls.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_hits_backpressure() {
|
||||
const ITER: usize = 2 * 1024;
|
||||
|
||||
let mut mock = mock! {
|
||||
// Block the `ITER`th write
|
||||
Err(io::Error::new(io::ErrorKind::WouldBlock, "not ready")),
|
||||
Ok(b"".to_vec()),
|
||||
};
|
||||
|
||||
for i in 0..(ITER + 1) {
|
||||
let mut b = BytesMut::with_capacity(4);
|
||||
b.put_u32::<BigEndian>(i as u32);
|
||||
|
||||
// Append to the end
|
||||
match mock.calls.back_mut().unwrap() {
|
||||
&mut Ok(ref mut data) => {
|
||||
// Write in 2kb chunks
|
||||
if data.len() < ITER {
|
||||
data.extend_from_slice(&b[..]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
// Push a new new chunk
|
||||
mock.calls.push_back(Ok(b[..].to_vec()));
|
||||
}
|
||||
|
||||
let mut framed = FramedWrite::new(mock, U32Encoder);
|
||||
|
||||
for i in 0..ITER {
|
||||
assert!(framed.start_send(i as u32).unwrap().is_ready());
|
||||
}
|
||||
|
||||
// This should reject
|
||||
assert!(!framed.start_send(ITER as u32).unwrap().is_ready());
|
||||
|
||||
// This should succeed and start flushing the buffer.
|
||||
assert!(framed.start_send(ITER as u32).unwrap().is_ready());
|
||||
|
||||
// Flush the rest of the buffer
|
||||
assert!(framed.poll_complete().unwrap().is_ready());
|
||||
|
||||
// Ensure the mock is empty
|
||||
assert_eq!(0, framed.get_ref().calls.len());
|
||||
}
|
||||
|
||||
// ===== Mock ======
|
||||
|
||||
struct Mock {
|
||||
calls: VecDeque<io::Result<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl Write for Mock {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
match self.calls.pop_front() {
|
||||
Some(Ok(data)) => {
|
||||
assert!(src.len() >= data.len());
|
||||
assert_eq!(&data[..], &src[..data.len()]);
|
||||
Ok(data.len())
|
||||
}
|
||||
Some(Err(e)) => Err(e),
|
||||
None => panic!("unexpected write; {:?}", src),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Mock {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,549 @@
|
||||
extern crate tokio_io;
|
||||
extern crate futures;
|
||||
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_io::codec::length_delimited::*;
|
||||
|
||||
use futures::{Stream, Sink, Poll};
|
||||
use futures::Async::*;
|
||||
|
||||
use std::io;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
macro_rules! mock {
|
||||
($($x:expr,)*) => {{
|
||||
let mut v = VecDeque::new();
|
||||
v.extend(vec![$($x),*]);
|
||||
Mock { calls: v }
|
||||
}};
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn read_empty_io_yields_nothing() {
|
||||
let mut io = FramedRead::new(mock!());
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_one_packet() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_one_packet_little_endian() {
|
||||
let mut io = Builder::new()
|
||||
.little_endian()
|
||||
.new_read(mock! {
|
||||
Ok(b"\x09\x00\x00\x00abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_one_packet_native_endian() {
|
||||
let data = if cfg!(target_endian = "big") {
|
||||
b"\x00\x00\x00\x09abcdefghi"
|
||||
} else {
|
||||
b"\x09\x00\x00\x00abcdefghi"
|
||||
};
|
||||
let mut io = Builder::new()
|
||||
.native_endian()
|
||||
.new_read(mock! {
|
||||
Ok(data[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_multi_frame_one_packet() {
|
||||
let mut data: Vec<u8> = vec![];
|
||||
data.extend_from_slice(b"\x00\x00\x00\x09abcdefghi");
|
||||
data.extend_from_slice(b"\x00\x00\x00\x03123");
|
||||
data.extend_from_slice(b"\x00\x00\x00\x0bhello world");
|
||||
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(data.into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"hello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_multi_packet() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Ok(b"\x00\x09abc"[..].into()),
|
||||
Ok(b"defghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_multi_frame_multi_packet() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Ok(b"\x00\x09abc"[..].into()),
|
||||
Ok(b"defghi"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x0312"[..].into()),
|
||||
Ok(b"3\x00\x00\x00\x0bhello world"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"hello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_multi_packet_wait() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x09abc"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"defghi"[..].into()),
|
||||
Err(would_block()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_multi_frame_multi_packet_wait() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x09abc"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"defghi"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x00\x00\x0312"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"3\x00\x00\x00\x0bhello world"[..].into()),
|
||||
Err(would_block()),
|
||||
});
|
||||
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"hello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_incomplete_head() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
});
|
||||
|
||||
assert!(io.poll().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_incomplete_head_multi() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Err(would_block()),
|
||||
Ok(b"\x00"[..].into()),
|
||||
Err(would_block()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert!(io.poll().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_incomplete_payload() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09ab"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"cd"[..].into()),
|
||||
Err(would_block()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert!(io.poll().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_max_frame_len() {
|
||||
let mut io = Builder::new()
|
||||
.max_frame_length(5)
|
||||
.new_read(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap_err().kind(), io::ErrorKind::InvalidData);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_update_max_frame_len_at_rest() {
|
||||
let mut io = Builder::new()
|
||||
.new_read(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
io.set_max_frame_length(5);
|
||||
assert_eq!(io.poll().unwrap_err().kind(), io::ErrorKind::InvalidData);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_update_max_frame_len_in_flight() {
|
||||
let mut io = Builder::new()
|
||||
.new_read(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcd"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"efghi"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
io.set_max_frame_length(5);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap_err().kind(), io::ErrorKind::InvalidData);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_one_byte_length_field() {
|
||||
let mut io = Builder::new()
|
||||
.length_field_length(1)
|
||||
.new_read(mock! {
|
||||
Ok(b"\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_header_offset() {
|
||||
let mut io = Builder::new()
|
||||
.length_field_length(2)
|
||||
.length_field_offset(4)
|
||||
.new_read(mock! {
|
||||
Ok(b"zzzz\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_multi_frame_one_packet_skip_none_adjusted() {
|
||||
let mut data: Vec<u8> = vec![];
|
||||
data.extend_from_slice(b"xx\x00\x09abcdefghi");
|
||||
data.extend_from_slice(b"yy\x00\x03123");
|
||||
data.extend_from_slice(b"zz\x00\x0bhello world");
|
||||
|
||||
let mut io = Builder::new()
|
||||
.length_field_length(2)
|
||||
.length_field_offset(2)
|
||||
.num_skip(0)
|
||||
.length_adjustment(4)
|
||||
.new_read(mock! {
|
||||
Ok(data.into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"xx\x00\x09abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"yy\x00\x03123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"zz\x00\x0bhello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_multi_frame_one_packet_length_includes_head() {
|
||||
let mut data: Vec<u8> = vec![];
|
||||
data.extend_from_slice(b"\x00\x0babcdefghi");
|
||||
data.extend_from_slice(b"\x00\x05123");
|
||||
data.extend_from_slice(b"\x00\x0dhello world");
|
||||
|
||||
let mut io = Builder::new()
|
||||
.length_field_length(2)
|
||||
.length_adjustment(-2)
|
||||
.new_read(mock! {
|
||||
Ok(data.into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"hello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_length_adjusted() {
|
||||
let mut io = Builder::new()
|
||||
.length_adjustment(-2)
|
||||
.new_write(mock! {
|
||||
Ok(b"\x00\x00\x00\x0b"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_nothing_yields_nothing() {
|
||||
let mut io: FramedWrite<_, &'static [u8]> = FramedWrite::new(mock!());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_one_packet() {
|
||||
let mut io = FramedWrite::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_multi_frame_one_packet() {
|
||||
let mut io = FramedWrite::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x03"[..].into()),
|
||||
Ok(b"123"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x0b"[..].into()),
|
||||
Ok(b"hello world"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(io.start_send("123").unwrap().is_ready());
|
||||
assert!(io.start_send("hello world").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_multi_frame_multi_packet() {
|
||||
let mut io = FramedWrite::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
Ok(b"\x00\x00\x00\x03"[..].into()),
|
||||
Ok(b"123"[..].into()),
|
||||
Ok(Flush),
|
||||
Ok(b"\x00\x00\x00\x0b"[..].into()),
|
||||
Ok(b"hello world"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.start_send("123").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.start_send("hello world").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_would_block() {
|
||||
let mut io = FramedWrite::new(mock! {
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(!io.poll_complete().unwrap().is_ready());
|
||||
assert!(!io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_little_endian() {
|
||||
let mut io = Builder::new()
|
||||
.little_endian()
|
||||
.new_write(mock! {
|
||||
Ok(b"\x09\x00\x00\x00"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_with_short_length_field() {
|
||||
let mut io = Builder::new()
|
||||
.length_field_length(1)
|
||||
.new_write(mock! {
|
||||
Ok(b"\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_max_frame_len() {
|
||||
let mut io = Builder::new()
|
||||
.max_frame_length(5)
|
||||
.new_write(mock! { });
|
||||
|
||||
assert_eq!(io.start_send("abcdef").unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_update_max_frame_len_at_rest() {
|
||||
let mut io = Builder::new()
|
||||
.new_write(mock! {
|
||||
Ok(b"\x00\x00\x00\x06"[..].into()),
|
||||
Ok(b"abcdef"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdef").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
io.set_max_frame_length(5);
|
||||
assert_eq!(io.start_send("abcdef").unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_update_max_frame_len_in_flight() {
|
||||
let mut io = Builder::new()
|
||||
.new_write(mock! {
|
||||
Ok(b"\x00\x00\x00\x06"[..].into()),
|
||||
Ok(b"ab"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"cdef"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdef").unwrap().is_ready());
|
||||
assert!(!io.poll_complete().unwrap().is_ready());
|
||||
io.set_max_frame_length(5);
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert_eq!(io.start_send("abcdef").unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
// ===== Test utils =====
|
||||
|
||||
fn would_block() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::WouldBlock, "would block")
|
||||
}
|
||||
|
||||
struct Mock {
|
||||
calls: VecDeque<io::Result<Op>>,
|
||||
}
|
||||
|
||||
enum Op {
|
||||
Data(Vec<u8>),
|
||||
Flush,
|
||||
}
|
||||
|
||||
use self::Op::*;
|
||||
|
||||
impl io::Read for Mock {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
match self.calls.pop_front() {
|
||||
Some(Ok(Op::Data(data))) => {
|
||||
debug_assert!(dst.len() >= data.len());
|
||||
dst[..data.len()].copy_from_slice(&data[..]);
|
||||
Ok(data.len())
|
||||
}
|
||||
Some(Ok(_)) => panic!(),
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Mock {
|
||||
}
|
||||
|
||||
impl io::Write for Mock {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
match self.calls.pop_front() {
|
||||
Some(Ok(Op::Data(data))) => {
|
||||
let len = data.len();
|
||||
assert!(src.len() >= len, "expect={:?}; actual={:?}", data, src);
|
||||
assert_eq!(&data[..], &src[..len]);
|
||||
Ok(len)
|
||||
}
|
||||
Some(Ok(_)) => panic!(),
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self.calls.pop_front() {
|
||||
Some(Ok(Op::Flush)) => {
|
||||
Ok(())
|
||||
}
|
||||
Some(Ok(_)) => panic!(),
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Mock {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for Op {
|
||||
fn from(src: &'a [u8]) -> Op {
|
||||
Op::Data(src.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Op {
|
||||
fn from(src: Vec<u8>) -> Op {
|
||||
Op::Data(src)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# 0.1.1 (March 22, 2018)
|
||||
|
||||
* Fix threading bugs (#227)
|
||||
* Fix notification bugs (#243)
|
||||
* Optionally support futures 0.2 (#172)
|
||||
|
||||
# 0.1.0 (March 09, 2018)
|
||||
|
||||
* Initial release
|
||||
@@ -0,0 +1,36 @@
|
||||
[package]
|
||||
name = "tokio-reactor"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.1"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-reactor/0.1"
|
||||
description = """
|
||||
Event loop that drives Tokio I/O resources.
|
||||
"""
|
||||
categories = ["asynchronous", "network-programming"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.19"
|
||||
log = "0.4.1"
|
||||
mio = "0.6.14"
|
||||
slab = "0.4.0"
|
||||
tokio-executor = { version = "0.1.1", path = "../tokio-executor" }
|
||||
tokio-io = { version = "0.1.6", path = "../tokio-io" }
|
||||
|
||||
# Futures 0.2 integration
|
||||
futures2 = { version = "0.1", path = "../futures2", optional = true }
|
||||
|
||||
[features]
|
||||
unstable-futures = [
|
||||
"futures2",
|
||||
"tokio-executor/unstable-futures",
|
||||
]
|
||||
default = []
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user