mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
789
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6fc1db698 | ||
|
|
d44ce338af | ||
|
|
8b7ea0ff5c | ||
|
|
7207bf355e | ||
|
|
a4c4ac254b | ||
|
|
0589acc9ff | ||
|
|
1dadc701c0 | ||
|
|
10f1507cf4 | ||
|
|
b9cc032d3b | ||
|
|
4213b79461 | ||
|
|
7b8ce356c3 | ||
|
|
0605abacfc | ||
|
|
b37e4a4380 | ||
|
|
41576e6c48 | ||
|
|
09b5f47381 | ||
|
|
fc65951731 | ||
|
|
564da5c128 | ||
|
|
466dd4a851 | ||
|
|
2eed6d00f5 | ||
|
|
c1232a6520 | ||
|
|
5e75b0446d | ||
|
|
d49e6ae1b3 | ||
|
|
874264a4ef | ||
|
|
55b5e1b6ad | ||
|
|
513671f8de | ||
|
|
79e4514283 | ||
|
|
64e75ad1b0 | ||
|
|
1a5de2c79d | ||
|
|
ab24a655ad | ||
|
|
c3d56b85c3 | ||
|
|
1eee6508fc | ||
|
|
116a18b849 | ||
|
|
9d6b99494b | ||
|
|
560d0fa548 | ||
|
|
6232c74724 | ||
|
|
be832f20cb | ||
|
|
326f724978 | ||
|
|
81c20d8454 | ||
|
|
b70f1ce3c0 | ||
|
|
64e4bd1b2f | ||
|
|
8ed209b612 | ||
|
|
4a24c7063b | ||
|
|
e2230f3392 | ||
|
|
00e3c29e48 | ||
|
|
bcba4aaa54 | ||
|
|
71c47fabf4 | ||
|
|
4996e27673 | ||
|
|
5bf06f2b5a | ||
|
|
12be90e3ff | ||
|
|
0d49e112b2 | ||
|
|
9eca96aa21 | ||
|
|
a16c9a5a01 | ||
|
|
f0bfebb7e1 | ||
|
|
968c143acd | ||
|
|
6fbaac91e0 | ||
|
|
e35038ed79 | ||
|
|
f9ddb93604 | ||
|
|
a70f7203a4 | ||
|
|
7079bcd609 | ||
|
|
f8714e9901 | ||
|
|
0545b349e1 | ||
|
|
8cf98d6946 | ||
|
|
f9ea576cca | ||
|
|
7f580071f3 | ||
|
|
5fe2df0fba | ||
|
|
176df2448a | ||
|
|
5bbf976268 | ||
|
|
90969420a2 | ||
|
|
bffbaab30d | ||
|
|
a5e774bb38 | ||
|
|
0bb17300f7 | ||
|
|
c7719a2d29 | ||
|
|
38bff0adda | ||
|
|
fbe143b142 | ||
|
|
9df805ff54 | ||
|
|
5d82ac2d1e | ||
|
|
5bf78d77ad | ||
|
|
3176d0a48a | ||
|
|
bb6c3839ef | ||
|
|
1475448bdf | ||
|
|
7eb8d447ad | ||
|
|
1222d81741 | ||
|
|
619d730d61 | ||
|
|
476bf0084a | ||
|
|
bd8971cd95 | ||
|
|
eb1a8e1792 | ||
|
|
5b091fa3f0 | ||
|
|
7c3f1cb4a3 | ||
|
|
64d2389911 | ||
|
|
8471e0a0ee | ||
|
|
0ba6e9abdb | ||
|
|
a939dc48b0 | ||
|
|
cfd9b36d89 | ||
|
|
f5c20cd228 | ||
|
|
c7c74a5a76 | ||
|
|
b34a849b79 | ||
|
|
a7a79f28a8 | ||
|
|
bd28a7a767 | ||
|
|
275769b5b9 | ||
|
|
b70615b299 | ||
|
|
6406328176 | ||
|
|
f28c9f0d17 | ||
|
|
7ee5542182 | ||
|
|
7fb54315f1 | ||
|
|
ffd4025fce | ||
|
|
8bf4696f31 | ||
|
|
10398b20c0 | ||
|
|
780d6f91a0 | ||
|
|
45da5f3510 | ||
|
|
855d39f849 | ||
|
|
798e86821f | ||
|
|
0193df3a59 | ||
|
|
5930acef73 | ||
|
|
3540c5b9ee | ||
|
|
188fc6e0d2 | ||
|
|
d45f61c183 | ||
|
|
dcfa895b51 | ||
|
|
f0006006ed | ||
|
|
84ff73e687 | ||
|
|
32e15b3a24 | ||
|
|
efcbf9613f | ||
|
|
3736467dbb | ||
|
|
e43f28f6a8 | ||
|
|
3cf91db4b6 | ||
|
|
e8fcf55881 | ||
|
|
a515f9c459 | ||
|
|
50b91c0247 | ||
|
|
67bf9c36f3 | ||
|
|
101f770af3 | ||
|
|
6ff4e349e2 | ||
|
|
adc5186ebd | ||
|
|
7b53b7b659 | ||
|
|
99fa93bf0e | ||
|
|
0133bc1883 | ||
|
|
a854094825 | ||
|
|
3d1b4b3058 | ||
|
|
8656b7b8eb | ||
|
|
f309b295bb | ||
|
|
b1266a48c4 | ||
|
|
de5ec6e1bc | ||
|
|
3dcd76a38f | ||
|
|
3b9c7b1715 | ||
|
|
3bff5a3ffe | ||
|
|
248bf2144f | ||
|
|
93ab70a9a0 | ||
|
|
58b5abdb99 | ||
|
|
2d78cfe56a | ||
|
|
4c645866ef | ||
|
|
b0836ece7a | ||
|
|
0c0f682010 | ||
|
|
b24ad9fe86 | ||
|
|
7c010ed030 | ||
|
|
42c942de14 | ||
|
|
5e8f7eb03c | ||
|
|
9211adbe01 | ||
|
|
e5b99b0f7a | ||
|
|
83cd754bc8 | ||
|
|
17e424112d | ||
|
|
41d15ea212 | ||
|
|
8add90210b | ||
|
|
efb4b67a54 | ||
|
|
74d33a1b2f | ||
|
|
69885e214c | ||
|
|
4b85565bd7 | ||
|
|
d593c5b051 | ||
|
|
91ecb4b4c2 | ||
|
|
8abaf89e5f | ||
|
|
b560df9e66 | ||
|
|
df8278acb6 | ||
|
|
5862b9a2e0 | ||
|
|
c0953d41a5 | ||
|
|
24cd6d67f7 | ||
|
|
975576952f | ||
|
|
5d5755dca4 | ||
|
|
41ffdbb7d9 | ||
|
|
2450b5bfc9 | ||
|
|
80abff0e57 | ||
|
|
c632337e6f | ||
|
|
a53f94ab61 | ||
|
|
98c9a77f18 | ||
|
|
e00c49611a | ||
|
|
9c9fabc44b | ||
|
|
c3461b3ef3 | ||
|
|
b7ecd35036 | ||
|
|
dbcd1f9a09 | ||
|
|
0e729aa341 | ||
|
|
cbe369a3ed | ||
|
|
8bcbe78dbe | ||
|
|
6efe07c3fb | ||
|
|
8a2160a913 | ||
|
|
38c361781f | ||
|
|
07451f8b94 | ||
|
|
e87df0557d | ||
|
|
a8a4a9f0fc | ||
|
|
8b60c5386a | ||
|
|
af07f5bee7 | ||
|
|
939a0dd7b0 | ||
|
|
1ea6733568 | ||
|
|
8ce408492a | ||
|
|
b559a0cd9a | ||
|
|
417460cf86 | ||
|
|
adaba1a0bc | ||
|
|
467b6ea783 | ||
|
|
a2cfc877a7 | ||
|
|
ec7f2ae306 | ||
|
|
4261ab6627 | ||
|
|
aef434c089 | ||
|
|
cd73951130 | ||
|
|
524e66314f | ||
|
|
34d751bf92 | ||
|
|
942feab040 | ||
|
|
dc356a4158 | ||
|
|
2cd1d74092 | ||
|
|
632ee507ba | ||
|
|
7f605ee27f | ||
|
|
38e602f4d8 | ||
|
|
8e83a9f2c3 | ||
|
|
c146f48f0b | ||
|
|
ebf5f37989 | ||
|
|
abfa857f09 | ||
|
|
a81e2722a4 | ||
|
|
4ddc437170 | ||
|
|
3ecaa6d91c | ||
|
|
0bc68adb34 | ||
|
|
e20dff39ce | ||
|
|
7b4c999341 | ||
|
|
e1b1e216c5 | ||
|
|
7cd63fb946 | ||
|
|
bf741fec35 | ||
|
|
9b2aa14bb1 | ||
|
|
8546ff826d | ||
|
|
6866fe426c | ||
|
|
d88846c4eb | ||
|
|
7e6a10fccd | ||
|
|
502cf5d95c | ||
|
|
c223db3589 | ||
|
|
5cd665afd7 | ||
|
|
3e643c7b81 | ||
|
|
bc150cd0b5 | ||
|
|
15dce2d11a | ||
|
|
d4fec2c5d6 | ||
|
|
69975fb960 | ||
|
|
7c8b8877d4 | ||
|
|
0d38936b35 | ||
|
|
13b6e9939e | ||
|
|
44f10fe47f | ||
|
|
c147be0437 | ||
|
|
b1d9e55487 | ||
|
|
66cbed3ce3 | ||
|
|
4d19a99937 | ||
|
|
10dc659450 | ||
|
|
320c84a433 | ||
|
|
19f1fc36bd | ||
|
|
3f0eabe779 | ||
|
|
1474794055 | ||
|
|
92eb635669 | ||
|
|
8a7e57786a | ||
|
|
930679587a | ||
|
|
27e5b41067 | ||
|
|
e3df2eafd3 | ||
|
|
c15e01a09b | ||
|
|
64f2bf0072 | ||
|
|
7e35922a1d | ||
|
|
4dbe6af0a1 | ||
|
|
9bec094150 | ||
|
|
6f8b986bdb | ||
|
|
1a7f6fb201 | ||
|
|
0da23aad77 | ||
|
|
d5c1119c88 | ||
|
|
a6253ed05a | ||
|
|
94f9b04b06 | ||
|
|
966ccd5d53 | ||
|
|
3948e16292 | ||
|
|
6b35a1e8b0 | ||
|
|
e19bd77ef0 | ||
|
|
c8fdbed27a | ||
|
|
3e7d0be51d | ||
|
|
d70c928d88 | ||
|
|
742d89b0f3 | ||
|
|
20993341bd | ||
|
|
e699d46534 | ||
|
|
72caede7be | ||
|
|
64c26ab1ee | ||
|
|
02f7264008 | ||
|
|
2902e39db0 | ||
|
|
630d3136dd | ||
|
|
2c870b588f | ||
|
|
109fd3086b | ||
|
|
e3261440e5 | ||
|
|
2b909d6805 | ||
|
|
c62ef2d232 | ||
|
|
7eb264a0d0 | ||
|
|
1195263584 | ||
|
|
bccb713d98 | ||
|
|
474befd23c | ||
|
|
987ba7373c | ||
|
|
227533d456 | ||
|
|
03a9378297 | ||
|
|
99940aeeb4 | ||
|
|
cfc15617a5 | ||
|
|
b8cee1a60a | ||
|
|
c9bcbe77b9 | ||
|
|
978013a215 | ||
|
|
6aa6ebb5bc | ||
|
|
4bee94eb06 | ||
|
|
ed5a94eb2d | ||
|
|
2a181320b7 | ||
|
|
4c97e9dc28 | ||
|
|
1cae04f8b3 | ||
|
|
29f35df7f8 | ||
|
|
741bef8fe1 | ||
|
|
804dbd6f8e | ||
|
|
69fe65e972 | ||
|
|
b8913ec7c0 | ||
|
|
8aa520e2bd | ||
|
|
ab2f71a612 | ||
|
|
42a5cb1508 | ||
|
|
2b4b0619d7 | ||
|
|
55caddb9ce | ||
|
|
aefaef3abf | ||
|
|
c78c9168d7 | ||
|
|
9e1eef829a | ||
|
|
f48980ae52 | ||
|
|
a1d1eb5eb3 | ||
|
|
5efe31f2ed | ||
|
|
5ce5a0a0e0 | ||
|
|
5fd5329497 | ||
|
|
3b8ee2d991 | ||
|
|
7c341f45e0 | ||
|
|
611b4e11a7 | ||
|
|
159abb375f | ||
|
|
032b39487c | ||
|
|
b71b7b36be | ||
|
|
c4567f741a | ||
|
|
18cef1901f | ||
|
|
c81447fdcc | ||
|
|
d50d050fae | ||
|
|
3a55aba251 | ||
|
|
ddbb0c3836 | ||
|
|
376d63867a | ||
|
|
eb2d0fbcd1 | ||
|
|
695165feac | ||
|
|
ff186a4d03 | ||
|
|
6611b32cce | ||
|
|
80ba2a4ff6 | ||
|
|
8d09f61d33 | ||
|
|
815173f8e5 | ||
|
|
34e388619f | ||
|
|
9d5af20bcf | ||
|
|
613fde2637 | ||
|
|
22a3b10171 | ||
|
|
e3415d8d61 | ||
|
|
d1f60ac4c6 | ||
|
|
e2161502ad | ||
|
|
ab785bfba7 | ||
|
|
5f2f3f076d | ||
|
|
efb27731ad | ||
|
|
578a9aec16 | ||
|
|
5b8fc19701 | ||
|
|
c0a64d67ca | ||
|
|
6369d0f4f2 | ||
|
|
f69ee652e6 | ||
|
|
9b3f8564af | ||
|
|
9766cd644f | ||
|
|
431d4857e8 | ||
|
|
26432355d5 | ||
|
|
2f91c85ad8 | ||
|
|
951827229a | ||
|
|
383bb0a143 | ||
|
|
d2bd6f5002 | ||
|
|
6a94d2cf4f | ||
|
|
4f99470d46 | ||
|
|
3d9134d13e | ||
|
|
15dc0563b7 | ||
|
|
4e26258ac3 | ||
|
|
a59e096c47 | ||
|
|
fc1640891e | ||
|
|
de9f05d4d3 | ||
|
|
9c31797a08 | ||
|
|
d1c58b7940 | ||
|
|
5f74a99ea3 | ||
|
|
08e20fcf6a | ||
|
|
08099bb2d3 | ||
|
|
807d536846 | ||
|
|
654f9d703f | ||
|
|
a285689664 | ||
|
|
13930eff2a | ||
|
|
24fb33e012 | ||
|
|
a791f4a758 | ||
|
|
7e7a5147a3 | ||
|
|
2d56312b89 | ||
|
|
357df38861 | ||
|
|
34a9dc2d76 | ||
|
|
68d5fcb8d1 | ||
|
|
08b07afbd9 | ||
|
|
7b0c60849c | ||
|
|
6d8d388dc5 | ||
|
|
bc61bd9d3d | ||
|
|
a9585f0318 | ||
|
|
88b4ec84d7 | ||
|
|
9f0daad5ac | ||
|
|
c187cd75b6 | ||
|
|
a83f5e4ba6 | ||
|
|
4935aae164 | ||
|
|
ba1829fd26 | ||
|
|
ce7e60e396 | ||
|
|
d8b23ef852 | ||
|
|
4788d3a9e3 | ||
|
|
f1f61a3b15 | ||
|
|
d0a8e5d6f2 | ||
|
|
3b27dc31d2 | ||
|
|
37131b2114 | ||
|
|
8538c25170 | ||
|
|
7b6438a172 | ||
|
|
9de7083be8 | ||
|
|
8d55f98f6f | ||
|
|
999a600494 | ||
|
|
fb9809c068 | ||
|
|
517162792f | ||
|
|
fe90d61446 | ||
|
|
338b37884a | ||
|
|
513326e01d | ||
|
|
73a91ad7b3 | ||
|
|
930cce8677 | ||
|
|
6a125082e4 | ||
|
|
d9f9c5658f | ||
|
|
fff39c03b1 | ||
|
|
756606a58b | ||
|
|
e3b4c99a33 | ||
|
|
42fa0c28d3 | ||
|
|
f7b41c9dcc | ||
|
|
73102760cf | ||
|
|
18833a8e67 | ||
|
|
eba8bf2b4b | ||
|
|
790d649dc5 | ||
|
|
50e5d401df | ||
|
|
2e69f2a7fd | ||
|
|
962521f449 | ||
|
|
831be9c08e | ||
|
|
23c380a78f | ||
|
|
0a05332648 | ||
|
|
7268b0bb3a | ||
|
|
6412389bba | ||
|
|
53a94c025d | ||
|
|
7174c63bf9 | ||
|
|
cb2336ff3d | ||
|
|
47e2ff48d9 | ||
|
|
2f43b0a023 | ||
|
|
05d00aebb7 | ||
|
|
62733a6594 | ||
|
|
6d8cc4e475 | ||
|
|
6cbe3d4f82 | ||
|
|
337646b97f | ||
|
|
0bb015588a | ||
|
|
63377e2110 | ||
|
|
878503f965 | ||
|
|
2c01b3e0e0 | ||
|
|
ee9105d166 | ||
|
|
5a4f849bba | ||
|
|
ff41108834 | ||
|
|
6b202722ea | ||
|
|
144d980e5c | ||
|
|
81d789b88f | ||
|
|
634c19582f | ||
|
|
ff922bbe6d | ||
|
|
bf38631d6a | ||
|
|
6dda866191 | ||
|
|
03e450deb1 | ||
|
|
fbf90e6356 | ||
|
|
74168ae82f | ||
|
|
d038009e7d | ||
|
|
132e9f1da5 | ||
|
|
fe021e6c00 | ||
|
|
f311ac3d4f | ||
|
|
298be80249 | ||
|
|
79b017c773 | ||
|
|
ca0e5cc670 | ||
|
|
59bc364a0e | ||
|
|
e88d10a3cb | ||
|
|
a3b8d82711 | ||
|
|
d9688bc094 | ||
|
|
320a5fdca7 | ||
|
|
9af07ce208 | ||
|
|
1b2d997863 | ||
|
|
7a52ddcd09 | ||
|
|
9d3e5aac08 | ||
|
|
a99fa6e096 | ||
|
|
b89ed00a0d | ||
|
|
12ce75f088 | ||
|
|
a88308ed9f | ||
|
|
a298472da8 | ||
|
|
d0bb16192b | ||
|
|
a18ddb3b61 | ||
|
|
003b4d8074 | ||
|
|
6d186fe40e | ||
|
|
0d99ddd4f4 | ||
|
|
448d9d2eab | ||
|
|
0de3a69eb4 | ||
|
|
61aee5fc28 | ||
|
|
7f7f74985e | ||
|
|
e6cf976662 | ||
|
|
b14e189e44 | ||
|
|
2dde2b448f | ||
|
|
6742816e78 | ||
|
|
ab040bb498 | ||
|
|
0cfa120ba8 | ||
|
|
5774a9cd64 | ||
|
|
da49ede41e | ||
|
|
d224d6415e | ||
|
|
83273b8b50 | ||
|
|
ca708d6d87 | ||
|
|
0b75c0c53d | ||
|
|
5fbb36a060 | ||
|
|
c897a5b696 | ||
|
|
48d7f7b931 | ||
|
|
2291823181 | ||
|
|
795e02f4c6 | ||
|
|
7ac8bfc821 | ||
|
|
a79483750f | ||
|
|
3855f373d3 | ||
|
|
bd3f3270db | ||
|
|
e5525628cd | ||
|
|
f1b8a318d9 | ||
|
|
64343f1b78 | ||
|
|
82795184c1 | ||
|
|
f529928d87 | ||
|
|
461eebe612 | ||
|
|
407d15cf93 | ||
|
|
80915906d8 | ||
|
|
88e775dcf0 | ||
|
|
8b49a1e05f | ||
|
|
8fa1510d67 | ||
|
|
7797a377c3 | ||
|
|
b62d224fac | ||
|
|
d4803bc868 | ||
|
|
e07a03b3c5 | ||
|
|
7b86acb71d | ||
|
|
0651f09427 | ||
|
|
516251052d | ||
|
|
cbad83f362 | ||
|
|
bd9760e124 | ||
|
|
3e898f58a5 | ||
|
|
c531865d2c | ||
|
|
722eb257be | ||
|
|
ceed29586b | ||
|
|
70eca184f0 | ||
|
|
b2c777846e | ||
|
|
8e7d8af588 | ||
|
|
7380dd2482 | ||
|
|
af46eac583 | ||
|
|
e7488d983e | ||
|
|
e4415d986a | ||
|
|
ff906acdfb | ||
|
|
32ceccb465 | ||
|
|
0af05e7408 | ||
|
|
6b9e7bdace | ||
|
|
ed4d4a5353 | ||
|
|
1f47ed3dcc | ||
|
|
e9aaacddbd | ||
|
|
11f6b2862f | ||
|
|
8404f796ac | ||
|
|
6316aa1d0b | ||
|
|
0784dc2767 | ||
|
|
dd126c2333 | ||
|
|
3cc33dca7c | ||
|
|
dc5fa80a09 | ||
|
|
455782b964 | ||
|
|
29e417c257 | ||
|
|
9df1140340 | ||
|
|
e2b4bdb647 | ||
|
|
c6defbce4b | ||
|
|
b7846a4e2f | ||
|
|
cb8607a816 | ||
|
|
27c15471c1 | ||
|
|
0ab25878bd | ||
|
|
448302c3d4 | ||
|
|
934a1467d4 | ||
|
|
4d639e246b | ||
|
|
ff5381de8d | ||
|
|
061452dc01 | ||
|
|
a6b2682309 | ||
|
|
cf84a59e5a | ||
|
|
e90e33d5df | ||
|
|
fa5da27d98 | ||
|
|
ecaa069f0f | ||
|
|
fc15d7d4a4 | ||
|
|
a70a3b599a | ||
|
|
26faefcc34 | ||
|
|
f16725ea9f | ||
|
|
caf43221b5 | ||
|
|
93680357dd | ||
|
|
784d21ae31 | ||
|
|
0938ccfefd | ||
|
|
6fa2fdab44 | ||
|
|
d0d13d0bd0 | ||
|
|
8a1777b800 | ||
|
|
42d0f53ddb | ||
|
|
db0c4147c8 | ||
|
|
10fd2afd18 | ||
|
|
83a55601ef | ||
|
|
b37120f61c | ||
|
|
91dbf24cf4 | ||
|
|
c78fd6d6c5 | ||
|
|
025474dfbb | ||
|
|
e7dfcf90fe | ||
|
|
ecdfe4c474 | ||
|
|
37b4efb9e2 | ||
|
|
c94f607f1b | ||
|
|
76438c9e70 | ||
|
|
e0e9594f71 | ||
|
|
3b43262a10 | ||
|
|
5f18bf669f | ||
|
|
1581c8b475 | ||
|
|
d3b2efc815 | ||
|
|
f7c4e3cd84 | ||
|
|
329ad3324c | ||
|
|
2b6695d25a | ||
|
|
9290602815 | ||
|
|
827e77e71e | ||
|
|
7b3e4b98ac | ||
|
|
5e9d60e834 | ||
|
|
8270965459 | ||
|
|
e6b044a820 | ||
|
|
de9b401457 | ||
|
|
ad5179b2d5 | ||
|
|
0aceba21bd | ||
|
|
09e21eceea | ||
|
|
bdc87856f2 | ||
|
|
7987b64445 | ||
|
|
32c928b607 | ||
|
|
82aeae147d | ||
|
|
f48944c1fb | ||
|
|
f0680617ee | ||
|
|
dbc185cd3a | ||
|
|
c205e2c358 | ||
|
|
acec6356ee | ||
|
|
c11eec3908 | ||
|
|
69295fac1e | ||
|
|
b9c6eb309c | ||
|
|
56d3914675 | ||
|
|
34e71fa71a | ||
|
|
914b803429 | ||
|
|
4d11784b01 | ||
|
|
50cabae181 | ||
|
|
c101e9e11d | ||
|
|
5c5f793ef0 | ||
|
|
1384b31d60 | ||
|
|
521dc94021 | ||
|
|
ed23a06fb1 | ||
|
|
1aee22505a | ||
|
|
01b5bf6761 | ||
|
|
6638cbc80e | ||
|
|
22bc5e2738 | ||
|
|
a0c162c0ff | ||
|
|
ca51ae9651 | ||
|
|
f3f99b723f | ||
|
|
f20e7a4d2b | ||
|
|
4a92c4d4b6 | ||
|
|
9680ecc109 | ||
|
|
124391e42b | ||
|
|
6150be189f | ||
|
|
ca9586a089 | ||
|
|
89b9792931 | ||
|
|
a0cc60153a | ||
|
|
7f3f868b66 | ||
|
|
97ebb2275c | ||
|
|
849a5ad0b2 | ||
|
|
b16a8613b1 | ||
|
|
5664660156 | ||
|
|
4416ea07d8 | ||
|
|
56222c588b | ||
|
|
72179d49c5 | ||
|
|
073a1a251a | ||
|
|
31c81faf96 | ||
|
|
5e68b0d51d | ||
|
|
4bd07ac6aa | ||
|
|
f4f7bb232e | ||
|
|
413e1b78a7 | ||
|
|
649fa13a15 | ||
|
|
eef655f3b1 | ||
|
|
97508096fa | ||
|
|
06c473e628 | ||
|
|
aa99950b9c | ||
|
|
aac6998c22 | ||
|
|
df2c3cd475 | ||
|
|
36ed35c52c | ||
|
|
5925ca7720 | ||
|
|
2ac132fb46 | ||
|
|
f9a0cb8792 | ||
|
|
9fa6092e5a | ||
|
|
d4adeeef2f | ||
|
|
4f6395b31c | ||
|
|
5c0b56278b | ||
|
|
41ca9a43de | ||
|
|
8d0f102de8 | ||
|
|
5dcb379f6d | ||
|
|
970f75f830 | ||
|
|
619efed28b | ||
|
|
18ed0be851 | ||
|
|
01052f930a | ||
|
|
940f2c3431 | ||
|
|
84d5a7f5a0 | ||
|
|
1b498e8aa2 | ||
|
|
b2c53987d9 | ||
|
|
38092010c4 | ||
|
|
475dabe96d | ||
|
|
cb4aea394e | ||
|
|
79d8820050 | ||
|
|
951f2fd910 | ||
|
|
4ef736b9d5 | ||
|
|
219f24cbf1 | ||
|
|
ea282efb2e | ||
|
|
042224d33c | ||
|
|
927eb80ad4 | ||
|
|
6a8934e897 | ||
|
|
0e400af78c | ||
|
|
df702130d6 | ||
|
|
949adbb887 | ||
|
|
b2b796a228 | ||
|
|
abb014efc2 | ||
|
|
e5cf0cc717 | ||
|
|
62f34e15ce | ||
|
|
3ebca76a9a | ||
|
|
fea1f780bc | ||
|
|
712ca84033 | ||
|
|
cf06621998 | ||
|
|
7e51ab05e9 | ||
|
|
4bfa4ffcdf | ||
|
|
88b942652c | ||
|
|
5029e80a89 | ||
|
|
847fb59b17 | ||
|
|
b4fe517a16 | ||
|
|
7ae010f0f3 | ||
|
|
9144b2ff53 | ||
|
|
2c4549a18a | ||
|
|
4f819b7cd1 | ||
|
|
197f88f3bc | ||
|
|
44f65afcc6 | ||
|
|
4271a9cd8d | ||
|
|
9d8096b911 | ||
|
|
597f271c08 | ||
|
|
599955f716 | ||
|
|
91bb0f73f5 | ||
|
|
6c9d8abba9 | ||
|
|
824b7b6759 | ||
|
|
cb91dd274a | ||
|
|
ea7178b8c6 | ||
|
|
a99b8e2e0b | ||
|
|
03859a7dcd | ||
|
|
d8177f81ac | ||
|
|
ceca2a3cd6 | ||
|
|
1524ee4b60 | ||
|
|
7793d63739 | ||
|
|
e0e26bc223 | ||
|
|
9c5cad037f | ||
|
|
30330da11a | ||
|
|
6e4945025c | ||
|
|
3c8f110730 | ||
|
|
678f15bd48 | ||
|
|
b1172f8074 | ||
|
|
cdde2e7a27 | ||
|
|
85487727d4 | ||
|
|
92d51202ef | ||
|
|
cb55bf4012 | ||
|
|
987ccfc8ac | ||
|
|
1bc6d75543 | ||
|
|
27148d6110 | ||
|
|
a1871b1480 | ||
|
|
acd08eb23d | ||
|
|
90b1a01010 | ||
|
|
676824988e | ||
|
|
46149f031e | ||
|
|
5510ba6dba | ||
|
|
b8f63308d7 | ||
|
|
4313d65b38 | ||
|
|
e780fccce4 | ||
|
|
b01e71b3d8 | ||
|
|
7f911b6b70 | ||
|
|
d88aba8d1c | ||
|
|
6fbef0a528 | ||
|
|
9be5f3f9ff | ||
|
|
e28856cffe | ||
|
|
85e3bd34af | ||
|
|
db4019d84a | ||
|
|
195c4b0496 | ||
|
|
619d3b163b | ||
|
|
5ff6e37c59 | ||
|
|
43d69d77e2 | ||
|
|
dbb04e310c |
@@ -1,22 +0,0 @@
|
||||
image: Visual Studio 2017
|
||||
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
|
||||
- set RUST_BACKTRACE=1
|
||||
- set LOOM_MAX_DURATION=10
|
||||
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
|
||||
build: false
|
||||
|
||||
test_script:
|
||||
- cargo test --all --no-fail-fast --target %TARGET%
|
||||
+25
-11
@@ -8,21 +8,35 @@ freebsd_instance:
|
||||
task:
|
||||
name: FreeBSD 12.0
|
||||
env:
|
||||
LOOM_MAX_DURATION: 10
|
||||
LOOM_MAX_PREEMPTIONS: 2
|
||||
RUSTFLAGS: -Dwarnings
|
||||
setup_script:
|
||||
- pkg install -y curl
|
||||
- curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
- sh rustup.sh -y
|
||||
- sh rustup.sh -y --profile minimal --default-toolchain stable
|
||||
- . $HOME/.cargo/env
|
||||
- rustup target add i686-unknown-freebsd
|
||||
cargo_cache:
|
||||
folder: $HOME/.cargo/registry
|
||||
- |
|
||||
echo "~~~~ rustc --version ~~~~"
|
||||
rustc --version
|
||||
|
||||
# Remove any existing patch statements
|
||||
mv Cargo.toml Cargo.toml.bck
|
||||
sed -n '/\[patch.crates-io\]/q;p' Cargo.toml.bck > Cargo.toml
|
||||
|
||||
# Patch all crates
|
||||
cat ci/patch.toml >> Cargo.toml
|
||||
|
||||
# Print `Cargo.toml` for debugging
|
||||
echo "~~~~ Cargo.toml ~~~~"
|
||||
cat Cargo.toml
|
||||
echo "~~~~~~~~~~~~~~~~~~~~"
|
||||
test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test --all --no-fail-fast
|
||||
- cargo doc --all
|
||||
i686_test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test --all --exclude tokio-tls --no-fail-fast --target i686-unknown-freebsd
|
||||
before_cache_script:
|
||||
- rm -rf $HOME/.cargo/registry/index
|
||||
- cargo test --all
|
||||
- cargo doc --all --no-deps
|
||||
# TODO: Re-enable
|
||||
# i686_test_script:
|
||||
# - . $HOME/.cargo/env
|
||||
# - |
|
||||
# cargo test --all --exclude tokio-tls --exclude tokio-macros --target i686-unknown-freebsd
|
||||
|
||||
-132
@@ -1,132 +0,0 @@
|
||||
---
|
||||
language: rust
|
||||
sudo: false
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
# to x-compile miniz-sys from sources
|
||||
- gcc-multilib
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- rust: stable
|
||||
- rust: beta
|
||||
- rust: nightly
|
||||
env: ALLOW_FAILURES=true
|
||||
- os: osx
|
||||
- env: TARGET=i686-unknown-linux-gnu
|
||||
|
||||
# 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.
|
||||
#
|
||||
# Tests are not run as tests may require newer versions of rust.
|
||||
- rust: 1.26.0
|
||||
script: |
|
||||
cargo check --all
|
||||
|
||||
# Test combinations of enabled features and rustfmt
|
||||
- rust: stable
|
||||
script: |
|
||||
cargo fmt --all -- --check
|
||||
shopt -s expand_aliases
|
||||
alias check="cargo check --no-default-features"
|
||||
check
|
||||
check --features codec
|
||||
check --features fs
|
||||
check --features io
|
||||
check --features reactor
|
||||
check --features rt-full
|
||||
check --features tcp
|
||||
check --features timer
|
||||
check --features udp
|
||||
check --features uds
|
||||
|
||||
# Test the async / await preview. We don't want to block PRs on this failing
|
||||
# though.
|
||||
- rust: nightly
|
||||
env: ALLOW_FAILURES=true
|
||||
script: |
|
||||
cd tokio-async-await
|
||||
cargo check --all
|
||||
cargo check --features async-await-preview
|
||||
|
||||
# This runs TSAN against nightly and allows failures to propagate up.
|
||||
- rust: nightly-2018-11-18
|
||||
env: TSAN=yes
|
||||
script: |
|
||||
set -e
|
||||
# Make sure the benchmarks compile
|
||||
cargo build --benches --all
|
||||
|
||||
export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0"
|
||||
export TSAN_OPTIONS="suppressions=`pwd`/ci/tsan"
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
# === tokio-timer ====
|
||||
|
||||
# Run address sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test -p tokio-timer --test hammer --target x86_64-unknown-linux-gnu
|
||||
|
||||
# Run thread sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=thread" \
|
||||
cargo test -p tokio-timer --test hammer --target x86_64-unknown-linux-gnu
|
||||
|
||||
# === tokio-threadpool ====
|
||||
|
||||
# Run address sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test -p tokio-threadpool --tests --target x86_64-unknown-linux-gnu
|
||||
|
||||
# Run thread sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=thread" \
|
||||
cargo test -p tokio-threadpool --tests --target x86_64-unknown-linux-gnu
|
||||
|
||||
# This runs cargo +nightly doc
|
||||
- name: nightly_docs
|
||||
rust: nightly
|
||||
env: ALLOW_FAILURES=true
|
||||
script: cargo doc
|
||||
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
env: ALLOW_FAILURES=true
|
||||
|
||||
script: |
|
||||
set -e
|
||||
if [[ "$TARGET" ]]
|
||||
then
|
||||
rustup target add $TARGET
|
||||
cargo check --all --exclude tokio-tls --target $TARGET
|
||||
cargo check --tests --all --exclude tokio-tls --target $TARGET
|
||||
else
|
||||
# Limit the execution time of loom tests.
|
||||
export LOOM_MAX_DURATION=10
|
||||
cargo test --all --no-fail-fast
|
||||
cargo test -p tokio-buf --no-default-features
|
||||
cargo doc --all
|
||||
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" && $TARGET = ""
|
||||
|
||||
env:
|
||||
global:
|
||||
- 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
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
This changelog only applies to the `tokio` crate proper. Each sub crate
|
||||
maintains its own changelog tracking changes made in each respective sub crate.
|
||||
|
||||
# 0.1.15 (January 24, 2019)
|
||||
|
||||
### Added
|
||||
- Re-export tokio-sync APIs (#839).
|
||||
- Stream enumerate combinator (#832).
|
||||
|
||||
# 0.1.14 (January 6, 2019)
|
||||
|
||||
* Use feature flags to break up the crate, allowing users to pick & choose
|
||||
components (#808).
|
||||
* Export `UnixDatagram` and `UnixDatagramFramed` (#772).
|
||||
|
||||
# 0.1.13 (November 21, 2018)
|
||||
|
||||
* Fix `Runtime::reactor()` when no tasks are spawned (#721).
|
||||
* `runtime::Builder` no longer uses deprecated methods (#749).
|
||||
* Provide `after_start` and `before_stop` configuration settings for
|
||||
`Runtime` (#756).
|
||||
* Implement throttle stream combinator (#736).
|
||||
|
||||
# 0.1.12 (October 23, 2018)
|
||||
|
||||
* runtime: expose `keep_alive` on runtime builder (#676).
|
||||
* runtime: create a reactor per worker thread (#660).
|
||||
* codec: fix panic in `LengthDelimitedCodec` (#682).
|
||||
* io: re-export `tokio_io::io::read` function (#689).
|
||||
* runtime: check for executor re-entry in more places (#708).
|
||||
|
||||
# 0.1.11 (September 28, 2018)
|
||||
|
||||
* Fix `tokio-async-await` dependency (#675).
|
||||
|
||||
# 0.1.10 (September 27, 2018)
|
||||
|
||||
* Fix minimal versions
|
||||
|
||||
# 0.1.9 (September 27, 2018)
|
||||
|
||||
* Experimental async/await improvements (#661).
|
||||
* Re-export `TaskExecutor` from `tokio-current-thread` (#652).
|
||||
* Improve `Runtime` builder API (#645).
|
||||
* `tokio::run` panics when called from the context of an executor
|
||||
(#646).
|
||||
* Introduce `StreamExt` with a `timeout` helper (#573).
|
||||
* Move `length_delimited` into `tokio` (#575).
|
||||
* Re-organize `tokio::net` module (#548).
|
||||
* Re-export `tokio-current-thread::spawn` in current_thread runtime
|
||||
(#579).
|
||||
|
||||
# 0.1.8 (August 23, 2018)
|
||||
|
||||
* Extract tokio::executor::current_thread to a sub crate (#370)
|
||||
* Add `Runtime::block_on` (#398)
|
||||
* Add `runtime::current_thread::block_on_all` (#477)
|
||||
* Misc documentation improvements (#450)
|
||||
* Implement `std::error::Error` for error types (#501)
|
||||
|
||||
# 0.1.7 (June 6, 2018)
|
||||
|
||||
* Add `Runtime::block_on` for concurrent runtime (#391).
|
||||
* Provide handle to `current_thread::Runtime` that allows spawning tasks from
|
||||
other threads (#340).
|
||||
* Provide `clock::now()`, a configurable source of time (#381).
|
||||
|
||||
# 0.1.6 (May 2, 2018)
|
||||
|
||||
* Add asynchronous filesystem APIs (#323).
|
||||
* Add "current thread" runtime variant (#308).
|
||||
* `CurrentThread`: Expose inner `Park` instance.
|
||||
* Improve fairness of `CurrentThread` executor (#313).
|
||||
|
||||
# 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).
|
||||
@@ -0,0 +1,7 @@
|
||||
# Code of Conduct
|
||||
|
||||
The Tokio project adheres to the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct). This describes the minimum behavior expected from all contributors.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of violations of the Code of Conduct can be reported by contacting the project team at [[email protected]](mailto:[email protected]).
|
||||
+63
-7
@@ -12,15 +12,15 @@ use your help.
|
||||
This guide will help you get started. **Do not let this guide intimidate you**.
|
||||
It should be considered a map to help you navigate the process.
|
||||
|
||||
You may also get help with contributing in the [dev channel][dev], please join
|
||||
The [dev channel][dev] is available for any concerns not covered in this guide, please join
|
||||
us!
|
||||
|
||||
[dev]: https://gitter.im/tokio-rs/dev
|
||||
[dev]: https://discord.gg/6yGkFeN
|
||||
|
||||
## Conduct
|
||||
|
||||
The Tokio project adheres to the [Rust Code of Conduct][coc]. This describes
|
||||
the _minimum_ behavior expected from all contributors.
|
||||
the _minimum_ behavior expected from all contributors. Instances of violations of the Code of Conduct can be reported by contacting the project team at [[email protected]](mailto:[email protected]).
|
||||
|
||||
[coc]: https://github.com/rust-lang/rust/blob/master/CODE_OF_CONDUCT.md
|
||||
|
||||
@@ -153,8 +153,6 @@ The type level example for `tokio_timer::Timeout` provides a good example of a
|
||||
documentation test:
|
||||
|
||||
```
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio;
|
||||
/// // import the `timeout` function, usually this is done
|
||||
/// // with `use tokio::prelude::*`
|
||||
/// use tokio::prelude::FutureExt;
|
||||
@@ -192,8 +190,6 @@ If this were a documentation test for the `Timeout::new` function, then the
|
||||
example would explicitly use `Timeout::new`. For example:
|
||||
|
||||
```
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio;
|
||||
/// use tokio::timer::Timeout;
|
||||
/// use futures::Future;
|
||||
/// use futures::sync::oneshot;
|
||||
@@ -385,3 +381,63 @@ _Adapted from the [Node.js contributing guide][node]_.
|
||||
[node]: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md
|
||||
[hiding-a-comment]: https://help.github.com/articles/managing-disruptive-comments/#hiding-a-comment
|
||||
[documentation test]: https://doc.rust-lang.org/rustdoc/documentation-tests.html
|
||||
|
||||
## Releasing
|
||||
|
||||
Since the Tokio project consists of a number of crates, many of which depend on
|
||||
each other, releasing new versions to crates.io can involve some complexities.
|
||||
When releasing a new version of a crate, follow these steps:
|
||||
|
||||
1. **Ensure that the release crate has no path dependencies.** When the HEAD
|
||||
version of a Tokio crate requires unreleased changes in another Tokio crate,
|
||||
the crates.io dependency on the second crate will be replaced with a path
|
||||
dependency. Crates with path dependencies cannot be published, so before
|
||||
publishing the dependent crate, any path dependencies must also be published.
|
||||
This should be done through a form of depth-first tree traversal:
|
||||
|
||||
1. Starting with the first path dependency in the crate to be released,
|
||||
inspect the `Cargo.toml` for the dependency. If the dependency has any
|
||||
path dependencies of its own, repeat this step with the first such
|
||||
dependency.
|
||||
2. Begin the release process for the path dependency.
|
||||
3. Once the path dependency has been published to crates.io, update the
|
||||
dependent crate to depend on the crates.io version.
|
||||
4. When all path dependencies have been published, the dependent crate may
|
||||
be published.
|
||||
|
||||
To verify that a crate is ready to publish, run:
|
||||
|
||||
```bash
|
||||
bin/publish --dry-run <CRATE NAME> <CRATE VERSION>
|
||||
```
|
||||
|
||||
2. **Update Cargo metadata.** After releasing any path dependencies, update the
|
||||
`version` field in `Cargo.toml` to the new version, and the `documentation`
|
||||
field to the docs.rs URL of the new version.
|
||||
3. **Update other documentation links.** Update the `#![doc(html_root_url)]`
|
||||
attribute in the crate's `lib.rs` and the "Documentation" link in the crate's
|
||||
`README.md` to point to the docs.rs URL of the new version.
|
||||
4. **Update the changelog for the crate.** Each crate in the Tokio repository
|
||||
has its own `CHANGELOG.md` in that crate's subdirectory. Any changes to that
|
||||
crate since the last release should be added to the changelog. Change
|
||||
descriptions may be taken from the Git history, but should be edited to
|
||||
ensure a consistent format, based on [Keep A Changelog][keep-a-changelog].
|
||||
Other entries in that crate's changelog may also be used for reference.
|
||||
5. **Perform a final audit for breaking changes.** Compare the HEAD version of
|
||||
crate with the Git tag for the most recent release version. If there are any
|
||||
breaking API changes, determine if those changes can be made without breaking
|
||||
existing APIs. If so, resolve those issues. Otherwise, if it is necessary to
|
||||
make a breaking release, update the version numbers to reflect this.
|
||||
6. **Open a pull request with your changes.** Once that pull request has been
|
||||
approved by a maintainer and the pull request has been merged, continue to
|
||||
the next step.
|
||||
7. **Release the crate.** Run the following command:
|
||||
|
||||
```bash
|
||||
bin/publish <NAME OF CRATE> <VERSION>
|
||||
```
|
||||
|
||||
Your editor and prompt you to edit a message for the tag. Copy the changelog
|
||||
entry for that release version into your editor and close the window.
|
||||
|
||||
[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog/blob/master/CHANGELOG.md
|
||||
|
||||
+10
-124
@@ -1,129 +1,15 @@
|
||||
[package]
|
||||
name = "tokio"
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.15"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/tokio/0.1.15/tokio/"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
description = """
|
||||
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-async-await",
|
||||
"tokio-buf",
|
||||
"tokio-codec",
|
||||
"tokio-current-thread",
|
||||
"tokio-executor",
|
||||
"tokio-fs",
|
||||
"tokio-io",
|
||||
"tokio-reactor",
|
||||
"tokio-signal",
|
||||
"tokio-sync",
|
||||
"tokio-threadpool",
|
||||
"tokio-timer",
|
||||
"tokio-tcp",
|
||||
"tokio",
|
||||
"tokio-macros",
|
||||
"tokio-test",
|
||||
"tokio-tls",
|
||||
"tokio-trace",
|
||||
"tokio-trace/tokio-trace-core",
|
||||
"tokio-udp",
|
||||
"tokio-uds",
|
||||
"tokio-util",
|
||||
|
||||
# Internal
|
||||
"benches",
|
||||
"examples",
|
||||
"tests-build",
|
||||
"tests-integration",
|
||||
]
|
||||
|
||||
[features]
|
||||
default = [
|
||||
"codec",
|
||||
"fs",
|
||||
"io",
|
||||
"reactor",
|
||||
"rt-full",
|
||||
"sync",
|
||||
"tcp",
|
||||
"timer",
|
||||
"udp",
|
||||
"uds",
|
||||
]
|
||||
|
||||
codec = ["tokio-codec"]
|
||||
fs = ["tokio-fs"]
|
||||
io = ["bytes", "tokio-io"]
|
||||
reactor = ["io", "mio", "tokio-reactor"]
|
||||
rt-full = [
|
||||
"num_cpus",
|
||||
"reactor",
|
||||
"timer",
|
||||
"tokio-current-thread",
|
||||
"tokio-executor",
|
||||
"tokio-threadpool",
|
||||
]
|
||||
sync = ["tokio-sync"]
|
||||
tcp = ["tokio-tcp"]
|
||||
timer = ["tokio-timer"]
|
||||
udp = ["tokio-udp"]
|
||||
uds = ["tokio-uds"]
|
||||
|
||||
# This feature comes with no promise of stability. Things will
|
||||
# break with each patch release. Use at your own risk.
|
||||
async-await-preview = [
|
||||
"tokio-async-await/async-await-preview",
|
||||
]
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "tokio-rs/tokio" }
|
||||
appveyor = { repository = "carllerche/tokio", id = "s83yxhy9qeb58va7" }
|
||||
|
||||
[dependencies]
|
||||
# Only non-optional dependency...
|
||||
futures = "0.1.20"
|
||||
|
||||
# Everything else is optional...
|
||||
bytes = { version = "0.4", optional = true }
|
||||
num_cpus = { version = "1.8.0", optional = true }
|
||||
tokio-codec = { version = "0.1.0", path = "tokio-codec", optional = true }
|
||||
tokio-current-thread = { version = "0.1.3", path = "tokio-current-thread", optional = true }
|
||||
tokio-fs = { version = "0.1.3", path = "tokio-fs", optional = true }
|
||||
tokio-io = { version = "0.1.6", path = "tokio-io", optional = true }
|
||||
tokio-executor = { version = "0.1.5", path = "tokio-executor", optional = true }
|
||||
tokio-reactor = { version = "0.1.1", path = "tokio-reactor", optional = true }
|
||||
tokio-sync = { version = "0.1.0", path = "tokio-sync", optional = true }
|
||||
tokio-threadpool = { version = "0.1.8", path = "tokio-threadpool", optional = true }
|
||||
tokio-tcp = { version = "0.1.0", path = "tokio-tcp", optional = true }
|
||||
tokio-udp = { version = "0.1.0", path = "tokio-udp", optional = true }
|
||||
tokio-timer = { version = "0.2.8", path = "tokio-timer", optional = true }
|
||||
|
||||
# Needed until `reactor` is removed from `tokio`.
|
||||
mio = { version = "0.6.14", optional = true }
|
||||
|
||||
# Needed for async/await preview support
|
||||
tokio-async-await = { version = "0.1.0", path = "tokio-async-await", optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
tokio-uds = { version = "0.2.1", path = "tokio-uds", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { version = "0.5", default-features = false }
|
||||
flate2 = { version = "1", features = ["tokio"] }
|
||||
futures-cpupool = "0.1"
|
||||
http = "0.1"
|
||||
httparse = "1.0"
|
||||
libc = "0.2"
|
||||
num_cpus = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
time = "0.1"
|
||||
|
||||
@@ -14,29 +14,23 @@ the Rust programming language. It is:
|
||||
|
||||
[![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]
|
||||
[![Gitter chat][gitter-badge]][gitter-url]
|
||||
[![Build Status][azure-badge]][azure-url]
|
||||
[![Discord chat][discord-badge]][discord-url]
|
||||
|
||||
[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
|
||||
[gitter-badge]: https://img.shields.io/gitter/room/tokio-rs/tokio.svg
|
||||
[gitter-url]: https://gitter.im/tokio-rs/tokio
|
||||
[mit-url]: LICENSE
|
||||
[azure-badge]: https://dev.azure.com/tokio-rs/Tokio/_apis/build/status/tokio-rs.tokio?branchName=master
|
||||
[azure-url]: https://dev.azure.com/tokio-rs/Tokio/_build/latest?definitionId=1&branchName=master
|
||||
[discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord&style=flat-square
|
||||
[discord-url]: https://discord.gg/tokio
|
||||
|
||||
[Website](https://tokio.rs) |
|
||||
[Guides](https://tokio.rs/docs/getting-started/hello-world/) |
|
||||
[API Docs](https://docs.rs/tokio/0.1.15/tokio) |
|
||||
[Chat](https://gitter.im/tokio-rs/tokio)
|
||||
|
||||
The API docs for the master branch are published [here][master-dox].
|
||||
|
||||
[master-dox]: https://tokio-rs.github.io/tokio/tokio/
|
||||
[Guides](https://tokio.rs/docs/overview/) |
|
||||
[API Docs](https://docs.rs/tokio/latest/tokio) |
|
||||
[Roadmap](https://github.com/tokio-rs/tokio/blob/master/ROADMAP.md) |
|
||||
[Chat](https://discord.gg/tokio)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -45,59 +39,54 @@ 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,
|
||||
* 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/tokio/reactor/index.html
|
||||
[scheduler]: https://tokio-rs.github.io/tokio/tokio/runtime/index.html
|
||||
[net]: https://docs.rs/tokio/latest/tokio/net/index.html
|
||||
[scheduler]: https://docs.rs/tokio/latest/tokio/runtime/index.html
|
||||
|
||||
## Example
|
||||
|
||||
A basic TCP echo server with Tokio:
|
||||
|
||||
```rust
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use tokio::io::copy;
|
||||
```rust,no_run
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
|
||||
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");
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut listener = TcpListener::bind("127.0.0.1:8080").await?;
|
||||
|
||||
// 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();
|
||||
loop {
|
||||
let (mut socket, _) = listener.accept().await?;
|
||||
|
||||
// A future that echos the data and returns how
|
||||
// many bytes were copied...
|
||||
let bytes_copied = copy(reader, writer);
|
||||
tokio::spawn(async move {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
// ... 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)
|
||||
});
|
||||
// In a loop, read data from the socket and write the data back.
|
||||
loop {
|
||||
let n = match socket.read(&mut buf).await {
|
||||
// socket closed
|
||||
Ok(n) if n == 0 => return,
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
eprintln!("failed to read from socket; err = {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Spawn the future as a concurrent task.
|
||||
tokio::spawn(handle_conn)
|
||||
// Write the data back
|
||||
if let Err(e) = socket.write_all(&buf[0..n]).await {
|
||||
eprintln!("failed to write to socket; err = {:?}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Start the Tokio runtime
|
||||
tokio::run(server);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -107,10 +96,12 @@ More examples can be found [here](examples).
|
||||
|
||||
First, see if the answer to your question can be found in the [Guides] or the
|
||||
[API documentation]. If the answer is not there, there is an active community in
|
||||
the [Tokio Gitter channel][chat]. We would be happy to try to answer your
|
||||
question. Last, if that doesn't work, try opening an [issue] with the question.
|
||||
the [Tokio Discord server][chat]. We would be happy to try to answer your
|
||||
question. Last, if that doesn't work, try opening an [issue] with the question.
|
||||
|
||||
[chat]: https://gitter.im/tokio-rs/tokio
|
||||
[Guides]: https://tokio.rs/docs/
|
||||
[API documentation]: https://docs.rs/tokio/latest/tokio
|
||||
[chat]: https://discord.gg/tokio
|
||||
[issue]: https://github.com/tokio-rs/tokio/issues/new
|
||||
|
||||
## Contributing
|
||||
@@ -119,56 +110,42 @@ question. Last, if that doesn't work, try opening an [issue] with the question.
|
||||
you! We have a [contributing guide][guide] to help you get involved in the Tokio
|
||||
project.
|
||||
|
||||
[guide]: CONTRIBUTING.md
|
||||
[guide]: https://github.com/tokio-rs/tokio/blob/master/CONTRIBUTING.md
|
||||
|
||||
## Project layout
|
||||
## Related Projects
|
||||
|
||||
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.
|
||||
In addition to the crates in this repository, the Tokio project also maintains
|
||||
several other libraries, including:
|
||||
|
||||
The crates included as part of Tokio are:
|
||||
* [`hyper`]: A fast and correct HTTP/1.1 and HTTP/2 implementation for Rust.
|
||||
|
||||
* [`tokio-async-await`]: Experimental `async` / `await` support.
|
||||
* [`tonic`]: A gRPC over HTTP/2 implementation focused on high performance, interoperability, and flexibility.
|
||||
|
||||
* [`tokio-codec`]: Utilities for encoding and decoding protocol frames.
|
||||
* [`warp`]: A super-easy, composable, web server framework for warp speeds.
|
||||
|
||||
* [`tokio-current-thread`]: Schedule the execution of futures on the current
|
||||
thread.
|
||||
* [`tower`]: A library of modular and reusable components for building robust networking clients and servers.
|
||||
|
||||
* [`tokio-executor`]: Task execution related traits and utilities.
|
||||
* [`tracing`] (formerly `tokio-trace`): A framework for application-level
|
||||
tracing and async-aware diagnostics.
|
||||
|
||||
* [`rdbc`]: A Rust database connectivity library for MySQL, Postgres and SQLite.
|
||||
|
||||
* [`tokio-fs`]: Filesystem (and standard in / out) APIs.
|
||||
* [`mio`]: A low-level, cross-platform abstraction over OS I/O APIs that powers
|
||||
`tokio`.
|
||||
|
||||
* [`tokio-io`]: Asynchronous I/O related traits and utilities.
|
||||
* [`bytes`]: Utilities for working with bytes, including efficient byte buffers.
|
||||
|
||||
* [`tokio-reactor`]: Event loop that drives I/O resources (like TCP and UDP
|
||||
sockets).
|
||||
* [`loom`]: A testing tool for concurrent Rust code
|
||||
|
||||
* [`tokio-tcp`]: TCP bindings for use with `tokio-io` and `tokio-reactor`.
|
||||
|
||||
* [`tokio-threadpool`]: Schedules the execution of futures across a pool of
|
||||
threads.
|
||||
|
||||
* [ `tokio-timer`]: Time related APIs.
|
||||
|
||||
* [`tokio-udp`]: UDP bindings for use with `tokio-io` and `tokio-reactor`.
|
||||
|
||||
* [`tokio-uds`]: Unix Domain Socket bindings for use with `tokio-io` and
|
||||
`tokio-reactor`.
|
||||
|
||||
[`tokio-async-await`]: tokio-async-await
|
||||
[`tokio-codec`]: tokio-codec
|
||||
[`tokio-current-thread`]: tokio-current-thread
|
||||
[`tokio-executor`]: tokio-executor
|
||||
[`tokio-fs`]: tokio-fs
|
||||
[`tokio-io`]: tokio-io
|
||||
[`tokio-reactor`]: tokio-reactor
|
||||
[`tokio-tcp`]: tokio-tcp
|
||||
[`tokio-threadpool`]: tokio-threadpool
|
||||
[`tokio-timer`]: tokio-timer
|
||||
[`tokio-udp`]: tokio-udp
|
||||
[`tokio-uds`]: tokio-uds
|
||||
[`warp`]: https://github.com/seanmonstar/warp
|
||||
[`hyper`]: https://github.com/hyperium/hyper
|
||||
[`tonic`]: https://github.com/hyperium/tonic
|
||||
[`tower`]: https://github.com/tower-rs/tower
|
||||
[`loom`]: https://github.com/tokio-rs/loom
|
||||
[`rdbc`]: https://github.com/tokio-rs/rdbc
|
||||
[`tracing`]: https://github.com/tokio-rs/tracing
|
||||
[`mio`]: https://github.com/tokio-rs/mio
|
||||
[`bytes`]: https://github.com/tokio-rs/bytes
|
||||
|
||||
## Supported Rust Versions
|
||||
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
# Tokio Roadmap
|
||||
|
||||
## A Roadmap to 1.0
|
||||
|
||||
The question of "why not 1.0?" has come up a few times. After all, Tokio 0.1 has
|
||||
been stable for three years. The short answer: because it isn't time. There is
|
||||
nobody who would rather ship a Tokio 1.0 than us. It also isn't something to rush.
|
||||
|
||||
After all, `async / await` only landed in the stable Rust channel weeks ago.
|
||||
There has been no significant production validation yet, except maybe fuchsia
|
||||
and that seems like a fairly specialized use case. This release of Tokio
|
||||
includes significant new code and new strategies with feature flags. Also, there
|
||||
are still big open questions, such as the [proposed changes][pr-1744] to
|
||||
`AsyncRead` and `AsyncWrite`.
|
||||
|
||||
Tokio 1.0 will be released as soon as the APIs are proven to handle real-world
|
||||
production cases.
|
||||
|
||||
### Tokio 1.0 in Q3 2020 with LTS support
|
||||
|
||||
The Tokio 1.0 release will be **no later** than Q3 2020. It will also come with
|
||||
"long-term support" guarantees:
|
||||
|
||||
* A minimum of 5 years of maintenance.
|
||||
* A minimum of 3 years before a hypothetical 2.0 release.
|
||||
|
||||
When Tokio 1.0 is released in Q3 2020, on-going support, security fixes, and
|
||||
critical bug fixes are guaranteed until **at least** Q3 2025. Tokio 2.0 will not
|
||||
be released until **at least** Q3 2023 (though, ideally there will never be a
|
||||
Tokio 2.0 release).
|
||||
|
||||
### How to get there
|
||||
|
||||
While Tokio 0.1 probably should have been a 1.0, Tokio 0.2 will be a **true**
|
||||
0.2 release. There will be breaking change releases every 2 ~ 3 months until 1.0.
|
||||
These changes will be **much** smaller than going from 0.1 -> 0.2. It is
|
||||
expected that the 1.0 release will look a lot like 0.2.
|
||||
|
||||
### What is expected to change
|
||||
|
||||
The biggest change will be the `AsyncRead` and `AsyncWrite` traits. Based on
|
||||
experience gained over the past 3 years, there are a couple of issues to
|
||||
address:
|
||||
|
||||
* Be able to **safely** use uninitialized memory as a read buffer.
|
||||
* Practical read vectored and write vectored APIs.
|
||||
|
||||
There are a few strategies to solve these problems. These strategies need to be
|
||||
investigated and the solution validated. You can see [this comment][pr-1744-comment] for a
|
||||
detailed statement of the problem.
|
||||
|
||||
The other major change, which has been in the works for a while, is updating
|
||||
Mio. Mio 0.6 was first released almost 4 years ago and has not had a breaking
|
||||
change since. Mio 0.7 has been in the works for a while. It includes a full
|
||||
rewrite of the windows support as well as a refined API. More will be written
|
||||
about this shortly.
|
||||
|
||||
Finally, now that the API is starting to stabilize, effort will be put into
|
||||
documentation. Tokio 0.2 is being released before updating the website and many
|
||||
of the old content will no longer be relevant. In the coming weeks, expect to
|
||||
see updates there.
|
||||
|
||||
So, we have our work cut out for us. We hope you enjoy this 0.2 release and are
|
||||
looking forward to your feedback and help.
|
||||
|
||||
[pr-1744]: https://github.com/tokio-rs/tokio/pull/1744
|
||||
[pr-1744-comment]: https://github.com/tokio-rs/tokio/pull/1744#issuecomment-553575438
|
||||
@@ -0,0 +1,115 @@
|
||||
trigger: ["master"]
|
||||
pr: ["master"]
|
||||
|
||||
variables:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
nightly: nightly-2020-01-25
|
||||
|
||||
jobs:
|
||||
# Test top level crate
|
||||
- template: ci/azure-test-stable.yml
|
||||
parameters:
|
||||
name: test_tokio
|
||||
rust: stable
|
||||
displayName: Test tokio
|
||||
cross: true
|
||||
crates:
|
||||
- tokio
|
||||
- tests-integration
|
||||
|
||||
# Test sub crates
|
||||
- template: ci/azure-test-stable.yml
|
||||
parameters:
|
||||
name: test_linux
|
||||
displayName: Test sub crates -
|
||||
rust: stable
|
||||
crates:
|
||||
- tokio-macros
|
||||
- tokio-test
|
||||
- tokio-tls
|
||||
- tokio-util
|
||||
- examples
|
||||
|
||||
# Run integration tests
|
||||
- template: ci/azure-test-integration.yml
|
||||
parameters:
|
||||
name: test_integration
|
||||
displayName: Integration tests
|
||||
rust: stable
|
||||
|
||||
# Run tests from `tests-build`. This requires a different process
|
||||
- template: ci/azure-test-build.yml
|
||||
parameters:
|
||||
name: test_build
|
||||
displayName: Test build permutations
|
||||
rust: stable
|
||||
|
||||
# Run loom tests
|
||||
- template: ci/azure-loom.yml
|
||||
parameters:
|
||||
name: loom
|
||||
rust: stable
|
||||
crates:
|
||||
- tokio
|
||||
|
||||
# Try cross compiling
|
||||
- template: ci/azure-cross-compile.yml
|
||||
parameters:
|
||||
name: cross
|
||||
rust: stable
|
||||
|
||||
# Check each feature works properly
|
||||
- template: ci/azure-check-features.yml
|
||||
parameters:
|
||||
rust: $(nightly)
|
||||
name: check_features
|
||||
|
||||
# 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.
|
||||
#
|
||||
# Tests are not run as tests may require newer versions of
|
||||
# rust.
|
||||
- template: ci/azure-check-minrust.yml
|
||||
parameters:
|
||||
name: minrust
|
||||
rust: 1.39.0
|
||||
|
||||
# Check formatting
|
||||
- template: ci/azure-rustfmt.yml
|
||||
parameters:
|
||||
rust: stable
|
||||
name: rustfmt
|
||||
|
||||
# Apply clippy lints to all crates
|
||||
- template: ci/azure-clippy.yml
|
||||
parameters:
|
||||
rust: stable
|
||||
name: clippy
|
||||
|
||||
# Check doc generation
|
||||
- template: ci/azure-check-docs.yml
|
||||
parameters:
|
||||
rust: $(nightly)
|
||||
name: docs
|
||||
|
||||
# - template: ci/azure-tsan.yml
|
||||
# parameters:
|
||||
# name: tsan
|
||||
# rust: stable
|
||||
|
||||
- template: ci/azure-deploy-docs.yml
|
||||
parameters:
|
||||
rust: stable
|
||||
dependsOn:
|
||||
- rustfmt
|
||||
- clippy
|
||||
- test_tokio
|
||||
- test_linux
|
||||
- test_build
|
||||
- loom
|
||||
- cross
|
||||
- minrust
|
||||
- check_features
|
||||
# - tsan
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "benches"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "0.2.0", path = "../tokio", features = ["full"] }
|
||||
bencher = "0.1.5"
|
||||
|
||||
[[bench]]
|
||||
name = "spawn"
|
||||
path = "spawn.rs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "mpsc"
|
||||
path = "mpsc.rs"
|
||||
harness = false
|
||||
@@ -1,115 +0,0 @@
|
||||
#![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::mpsc;
|
||||
use futures::sync::oneshot;
|
||||
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();
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Measure cost of different operations
|
||||
// to get a sense of performance tradeoffs
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate mio;
|
||||
extern crate test;
|
||||
|
||||
use test::Bencher;
|
||||
|
||||
use mio::tcp::TcpListener;
|
||||
use mio::{PollOpt, Ready, Token};
|
||||
|
||||
#[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();
|
||||
});
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
use bencher::{black_box, Bencher};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
type Medium = [usize; 64];
|
||||
type Large = [Medium; 64];
|
||||
|
||||
fn create_1_medium(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
black_box(&mpsc::channel::<Medium>(1));
|
||||
});
|
||||
}
|
||||
|
||||
fn create_100_medium(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
black_box(&mpsc::channel::<Medium>(100));
|
||||
});
|
||||
}
|
||||
|
||||
fn create_100_000_medium(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
black_box(&mpsc::channel::<Medium>(100_000));
|
||||
});
|
||||
}
|
||||
|
||||
fn send_medium(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let (mut tx, mut rx) = mpsc::channel::<Medium>(1000);
|
||||
|
||||
let _ = tx.try_send([0; 64]);
|
||||
|
||||
rx.try_recv().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
fn send_large(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let (mut tx, mut rx) = mpsc::channel::<Large>(1000);
|
||||
|
||||
let _ = tx.try_send([[0; 64]; 64]);
|
||||
|
||||
rx.try_recv().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
fn contention_bounded(b: &mut Bencher) {
|
||||
let mut rt = tokio::runtime::Builder::new()
|
||||
.core_threads(6)
|
||||
.threaded_scheduler()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
|
||||
|
||||
for _ in 0..5 {
|
||||
let mut tx = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
for i in 0..1000 {
|
||||
tx.send(i).await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for _ in 0..1_000 * 5 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn contention_bounded_full(b: &mut Bencher) {
|
||||
let mut rt = tokio::runtime::Builder::new()
|
||||
.core_threads(6)
|
||||
.threaded_scheduler()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<usize>(100);
|
||||
|
||||
for _ in 0..5 {
|
||||
let mut tx = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
for i in 0..1000 {
|
||||
tx.send(i).await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for _ in 0..1_000 * 5 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn contention_unbounded(b: &mut Bencher) {
|
||||
let mut rt = tokio::runtime::Builder::new()
|
||||
.core_threads(6)
|
||||
.threaded_scheduler()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
|
||||
|
||||
for _ in 0..5 {
|
||||
let tx = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
for i in 0..1000 {
|
||||
tx.send(i).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for _ in 0..1_000 * 5 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn uncontented_bounded(b: &mut Bencher) {
|
||||
let mut rt = tokio::runtime::Builder::new()
|
||||
.core_threads(6)
|
||||
.threaded_scheduler()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (mut tx, mut rx) = mpsc::channel::<usize>(1_000_000);
|
||||
|
||||
for i in 0..5000 {
|
||||
tx.send(i).await.unwrap();
|
||||
}
|
||||
|
||||
for _ in 0..5_000 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn uncontented_unbounded(b: &mut Bencher) {
|
||||
let mut rt = tokio::runtime::Builder::new()
|
||||
.core_threads(6)
|
||||
.threaded_scheduler()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
|
||||
|
||||
for i in 0..5000 {
|
||||
tx.send(i).unwrap();
|
||||
}
|
||||
|
||||
for _ in 0..5_000 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(
|
||||
create,
|
||||
create_1_medium,
|
||||
create_100_medium,
|
||||
create_100_000_medium
|
||||
);
|
||||
|
||||
bencher::benchmark_group!(send, send_medium, send_large);
|
||||
|
||||
bencher::benchmark_group!(
|
||||
contention,
|
||||
contention_bounded,
|
||||
contention_bounded_full,
|
||||
contention_unbounded,
|
||||
uncontented_bounded,
|
||||
uncontented_unbounded
|
||||
);
|
||||
|
||||
bencher::benchmark_main!(create, send, contention);
|
||||
@@ -0,0 +1,70 @@
|
||||
//! Benchmark spawning a task onto the basic and threaded Tokio executors.
|
||||
//! This essentially measure the time to enqueue a task in the local and remote
|
||||
//! case.
|
||||
|
||||
use bencher::{black_box, Bencher};
|
||||
|
||||
async fn work() -> usize {
|
||||
let val = 1 + 1;
|
||||
black_box(val)
|
||||
}
|
||||
|
||||
fn basic_scheduler_local_spawn(bench: &mut Bencher) {
|
||||
let mut runtime = tokio::runtime::Builder::new()
|
||||
.basic_scheduler()
|
||||
.build()
|
||||
.unwrap();
|
||||
runtime.block_on(async {
|
||||
bench.iter(|| {
|
||||
let h = tokio::spawn(work());
|
||||
black_box(h);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_local_spawn(bench: &mut Bencher) {
|
||||
let mut runtime = tokio::runtime::Builder::new()
|
||||
.threaded_scheduler()
|
||||
.build()
|
||||
.unwrap();
|
||||
runtime.block_on(async {
|
||||
bench.iter(|| {
|
||||
let h = tokio::spawn(work());
|
||||
black_box(h);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn basic_scheduler_remote_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new()
|
||||
.basic_scheduler()
|
||||
.build()
|
||||
.unwrap();
|
||||
let handle = runtime.handle();
|
||||
bench.iter(|| {
|
||||
let h = handle.spawn(work());
|
||||
black_box(h);
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_remote_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new()
|
||||
.threaded_scheduler()
|
||||
.build()
|
||||
.unwrap();
|
||||
let handle = runtime.handle();
|
||||
bench.iter(|| {
|
||||
let h = handle.spawn(work());
|
||||
black_box(h);
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(
|
||||
spawn,
|
||||
basic_scheduler_local_spawn,
|
||||
threaded_scheduler_local_spawn,
|
||||
basic_scheduler_remote_spawn,
|
||||
threaded_scheduler_remote_spawn
|
||||
);
|
||||
|
||||
bencher::benchmark_main!(spawn);
|
||||
-261
@@ -1,261 +0,0 @@
|
||||
#![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::net::{TcpListener, TcpStream};
|
||||
pub use tokio::reactor::Reactor;
|
||||
pub use tokio_io::io::read_to_end;
|
||||
|
||||
pub use std::io::{self, Read, Write};
|
||||
pub use std::thread;
|
||||
pub use std::time::Duration;
|
||||
pub use test::{self, Bencher};
|
||||
}
|
||||
|
||||
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::{Arc, Barrier};
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+121
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
USAGE="Publish a new release of a tokio crate
|
||||
|
||||
USAGE:
|
||||
$(basename "$0") [OPTIONS] [CRATE] [VERSION]
|
||||
|
||||
OPTIONS:
|
||||
-v, --verbose Use verbose Cargo output
|
||||
-d, --dry-run Perform a dry run (do not publish or tag the release)
|
||||
-h, --help Show this help text and exit"
|
||||
|
||||
DRY_RUN=""
|
||||
VERBOSE=""
|
||||
|
||||
err() {
|
||||
echo -e "\e[31m\e[1merror:\e[0m $@" 1>&2;
|
||||
}
|
||||
|
||||
status() {
|
||||
WIDTH=12
|
||||
printf "\e[32m\e[1m%${WIDTH}s\e[0m %s\n" "$1" "$2"
|
||||
}
|
||||
|
||||
verify() {
|
||||
status "Verifying" "if $CRATE v$VERSION can be released"
|
||||
ACTUAL=$(cargo pkgid | sed -n 's/.*#\(.*\)/\1/p')
|
||||
|
||||
if [ "$ACTUAL" != "$VERSION" ]; then
|
||||
err "expected to release version $VERSION, but Cargo.toml contained $ACTUAL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if git tag -l | grep -Fxq "$TAG" ; then
|
||||
err "git tag \`$TAG\` already exists"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PATH_DEPS=$(grep -F "path = \"" Cargo.toml | sed -e 's/^/ /')
|
||||
if [ -n "$PATH_DEPS" ]; then
|
||||
err "crate \`$CRATE\` contained path dependencies:\n$PATH_DEPS"
|
||||
echo "path dependencies must be removed prior to release"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
release() {
|
||||
status "Releasing" "$CRATE v$VERSION"
|
||||
cargo package $VERBOSE
|
||||
cargo publish $VERBOSE $DRY_RUN
|
||||
|
||||
status "Tagging" "$TAG"
|
||||
if [ -n "$DRY_RUN" ]; then
|
||||
echo "# git tag $TAG && git push --tags"
|
||||
else
|
||||
git tag "$TAG" && git push --tags
|
||||
fi
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
echo "$USAGE"
|
||||
exit 0
|
||||
;;
|
||||
-v|--verbose)
|
||||
VERBOSE="--verbose"
|
||||
set +x
|
||||
shift
|
||||
;;
|
||||
-d|--dry-run)
|
||||
DRY_RUN="--dry-run"
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
err "unknown flag \"$1\""
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
;;
|
||||
*) # crate or version
|
||||
if [ -z "$CRATE" ]; then
|
||||
CRATE="$1"
|
||||
elif [ -z "$VERSION" ]; then
|
||||
VERSION="$1"
|
||||
else
|
||||
err "unknown positional argument \"$1\""
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# set -- "${POSITIONAL[@]}"
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
err "no version specified!"
|
||||
HELP=1
|
||||
fi
|
||||
|
||||
if [ -n "$CRATE" ]; then
|
||||
TAG="$CRATE-$VERSION"
|
||||
else
|
||||
err "no crate specified!"
|
||||
HELP=1
|
||||
fi
|
||||
|
||||
if [ -n "$HELP" ]; then
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -d "$CRATE" ]; then
|
||||
(cd "$CRATE" && verify && release )
|
||||
else
|
||||
err "no such crate \"$CRATE\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Executable
+118
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
USAGE="Update links to docs.rs in a tokio crate
|
||||
|
||||
USAGE:
|
||||
$(basename "$0") [OPTIONS] [CRATE] [VERSION]
|
||||
|
||||
OPTIONS:
|
||||
-d, --dry-run Perform a dry run (do not modify any file)
|
||||
-h, --help Show this help text and exit"
|
||||
|
||||
err() {
|
||||
echo -e "\e[31m\e[1merror:\e[0m $@" 1>&2;
|
||||
}
|
||||
|
||||
status() {
|
||||
WIDTH=12
|
||||
printf "\e[32m\e[1m%${WIDTH}s\e[0m %s\n" "$1" "$2"
|
||||
}
|
||||
|
||||
c1grep() { grep "$@" || test $? = 1; }
|
||||
|
||||
update_versions_in_doc() {
|
||||
# Print what is being/would be done
|
||||
if [ -n "$DRY_RUN" ]; then
|
||||
local MSG="Would change:"
|
||||
else
|
||||
local MSG="Updating:"
|
||||
fi
|
||||
git grep -lr "docs.rs/$CRATE/" \
|
||||
| xargs sed --quiet \
|
||||
-E "s|docs.rs/$CRATE/[0-9.]+|docs.rs/$CRATE/$VERSION|gp" \
|
||||
| sed -e "s/^/$MSG /"
|
||||
|
||||
# Apply changes if not in dry run
|
||||
if [ -z "$DRY_RUN" ]; then
|
||||
git grep -lr "docs.rs/$CRATE/" \
|
||||
| xargs sed -i \
|
||||
-E "s|docs.rs/$CRATE/[0-9.]+|docs.rs/$CRATE/$VERSION|g"
|
||||
fi
|
||||
}
|
||||
|
||||
update() {
|
||||
update_versions_in_doc
|
||||
}
|
||||
|
||||
show_outdated() {
|
||||
OUTDATED=$(git grep -rn "docs.rs/$CRATE/" \
|
||||
| c1grep -v "$VERSION" \
|
||||
| sed -e 's/^/ - /')
|
||||
if [[ -n "$OUTDATED" ]]; then
|
||||
echo "Found the following links to docs.rs with an outdated version:"
|
||||
echo "$OUTDATED"
|
||||
echo
|
||||
else
|
||||
echo "Nothing to do."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
echo "$USAGE"
|
||||
exit 0
|
||||
;;
|
||||
-d|--dry-run)
|
||||
DRY_RUN="--dry-run"
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
err "unknown flag \"$1\""
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
;;
|
||||
*) # crate or version
|
||||
if [ -z "$CRATE" ]; then
|
||||
CRATE="$1"
|
||||
elif [ -z "$VERSION" ]; then
|
||||
VERSION="$1"
|
||||
else
|
||||
err "unknown positional argument \"$1\""
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# set -- "${POSITIONAL[@]}"
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
err "no version specified!"
|
||||
HELP=1
|
||||
fi
|
||||
|
||||
if [ -n "$CRATE" ]; then
|
||||
TAG="$CRATE-$VERSION"
|
||||
else
|
||||
err "no crate specified!"
|
||||
HELP=1
|
||||
fi
|
||||
|
||||
if [ -n "$HELP" ]; then
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -d "$CRATE" ]; then
|
||||
# Does not cd in order to update everywhere
|
||||
show_outdated && update
|
||||
else
|
||||
err "no such crate \"$CRATE\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
parameters:
|
||||
noDefaultFeatures: '--no-default-features'
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-is-release.yml
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
- ${{ each feature in crate.value }}:
|
||||
- script: cargo check ${{ parameters.noDefaultFeatures }} --features ${{ feature }}
|
||||
displayName: Check `${{ crate.key }}`, features = ${{ feature }}
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate.key }}
|
||||
condition: and(succeeded(), not(variables['isRelease']))
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
- ${{ each feature in crate.value }}:
|
||||
- script: cargo check ${{ parameters.noDefaultFeatures }} --features ${{ feature }}
|
||||
displayName: Check `${{ crate.key }}`, features = ${{ feature }}
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate.key }}
|
||||
@@ -0,0 +1,15 @@
|
||||
jobs:
|
||||
# Check docs
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Check docs
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- script: |
|
||||
RUSTDOCFLAGS="--cfg docsrs" cargo doc --lib --no-deps --all-features
|
||||
displayName: Check docs
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Check features
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
vmImage: ubuntu-16.04
|
||||
MacOS:
|
||||
vmImage: macOS-10.13
|
||||
Windows:
|
||||
vmImage: vs2017-win2016
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- script: cargo install cargo-hack
|
||||
displayName: Install cargo-hack
|
||||
|
||||
# Check each feature works properly
|
||||
# * --each-feature
|
||||
# run for each feature which includes --no-default-features and default features of package
|
||||
# * -Z avoid-dev-deps
|
||||
# build without dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866
|
||||
# tracking-issue: https://github.com/rust-lang/cargo/issues/5133
|
||||
- script: cargo hack check --all --each-feature -Z avoid-dev-deps
|
||||
displayName: cargo hack check --all --each-feature
|
||||
@@ -0,0 +1,14 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Min supported Rust version
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- script: cargo check --all
|
||||
displayName: cargo check --all
|
||||
@@ -0,0 +1,16 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Clippy
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
- script: |
|
||||
rustup component add clippy
|
||||
cargo clippy --version
|
||||
displayName: Install clippy
|
||||
- script: |
|
||||
cargo clippy --all --all-features
|
||||
displayName: cargo clippy --all
|
||||
@@ -0,0 +1,44 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
strategy:
|
||||
matrix:
|
||||
i686:
|
||||
vmImage: ubuntu-16.04
|
||||
target: i686-unknown-linux-gnu
|
||||
powerpc:
|
||||
vmImage: ubuntu-16.04
|
||||
target: powerpc-unknown-linux-gnu
|
||||
powerpc64:
|
||||
vmImage: ubuntu-16.04
|
||||
target: powerpc64-unknown-linux-gnu
|
||||
mips:
|
||||
vmImage: ubuntu-16.04
|
||||
target: mips-unknown-linux-gnu
|
||||
arm:
|
||||
vmImage: ubuntu-16.04
|
||||
target: arm-linux-androideabi
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- script: sudo apt-get update
|
||||
displayName: apt-get update
|
||||
|
||||
- script: sudo apt-get install gcc-multilib
|
||||
displayName: Install gcc-multilib
|
||||
|
||||
- script: cargo install cross
|
||||
displayName: Install cross
|
||||
|
||||
# Always patch
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- script: cross check --all --exclude tokio-tls --target $(target)
|
||||
displayName: Check source
|
||||
|
||||
# - script: cross check --tests --all --exclude tokio-tls --target $(target)
|
||||
# displayName: Check tests
|
||||
@@ -0,0 +1,39 @@
|
||||
parameters:
|
||||
dependsOn: []
|
||||
|
||||
jobs:
|
||||
- job: documentation
|
||||
displayName: 'Deploy API Documentation'
|
||||
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
pool:
|
||||
vmImage: 'Ubuntu 16.04'
|
||||
dependsOn:
|
||||
- ${{ parameters.dependsOn }}
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
# rust_version: stable
|
||||
rust_version: ${{ parameters.rust }}
|
||||
- script: |
|
||||
cargo doc --all --no-deps --all-features
|
||||
cp -R target/doc '$(Build.BinariesDirectory)'
|
||||
displayName: 'Generate Documentation'
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
git --version
|
||||
ls -la
|
||||
git init
|
||||
git config user.name 'Deployment Bot (from Azure Pipelines)'
|
||||
git config user.email '[email protected]'
|
||||
git config --global credential.helper 'store --file ~/.my-credentials'
|
||||
printf "protocol=https\nhost=github.com\nusername=carllerche\npassword=%s\n\n" "$GITHUB_TOKEN" | git credential-store --file ~/.my-credentials store
|
||||
git remote add origin https://github.com/tokio-rs/tokio
|
||||
git checkout -b gh-pages
|
||||
git add .
|
||||
git commit -m 'Deploy Tokio API documentation'
|
||||
git push -f origin gh-pages
|
||||
env:
|
||||
GITHUB_TOKEN: $(githubPersonalToken)
|
||||
workingDirectory: '$(Build.BinariesDirectory)'
|
||||
displayName: 'Deploy Documentation'
|
||||
@@ -0,0 +1,33 @@
|
||||
steps:
|
||||
# Linux and macOS.
|
||||
- script: |
|
||||
set -e
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal --default-toolchain none
|
||||
export PATH=$PATH:$HOME/.cargo/bin
|
||||
rustup toolchain install $RUSTUP_TOOLCHAIN
|
||||
rustup default $RUSTUP_TOOLCHAIN
|
||||
echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
|
||||
displayName: "Install rust (*nix)"
|
||||
condition: not(eq(variables['Agent.OS'], 'Windows_NT'))
|
||||
|
||||
# Windows.
|
||||
- script: |
|
||||
curl -sSf -o rustup-init.exe https://win.rustup.rs
|
||||
rustup-init.exe -y --profile minimal --default-toolchain none
|
||||
set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||
rustup toolchain install %RUSTUP_TOOLCHAIN%
|
||||
rustup default %RUSTUP_TOOLCHAIN%
|
||||
echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin"
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
|
||||
displayName: "Install rust (windows)"
|
||||
condition: eq(variables['Agent.OS'], 'Windows_NT')
|
||||
|
||||
# All platforms.
|
||||
- script: |
|
||||
rustup toolchain list
|
||||
rustc -Vv
|
||||
cargo -V
|
||||
displayName: Query rust and cargo versions
|
||||
@@ -0,0 +1,9 @@
|
||||
steps:
|
||||
- bash: |
|
||||
set -e
|
||||
|
||||
if git log --no-merges -1 --format='%B' | grep -qF '[ci-release]'; then
|
||||
echo "##vso[task.setvariable variable=isRelease]true"
|
||||
fi
|
||||
failOnStderr: true
|
||||
displayName: Check if release commit
|
||||
@@ -0,0 +1,18 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Loom tests
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
- script: RUSTFLAGS="--cfg loom" cargo test --lib --release --features "full" -- --test-threads=1 --nocapture
|
||||
env:
|
||||
LOOM_MAX_PREEMPTIONS: 1
|
||||
CI: 'True'
|
||||
displayName: test ${{ crate }}
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
@@ -0,0 +1,16 @@
|
||||
steps:
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
# Remove any existing patch statements
|
||||
mv Cargo.toml Cargo.toml.bck
|
||||
sed -n '/\[patch.crates-io\]/q;p' Cargo.toml.bck > Cargo.toml
|
||||
|
||||
# Patch all crates
|
||||
cat ci/patch.toml >> Cargo.toml
|
||||
|
||||
# Print `Cargo.toml` for debugging
|
||||
echo "~~~~ Cargo.toml ~~~~"
|
||||
cat Cargo.toml
|
||||
echo "~~~~~~~~~~~~~~~~~~~~"
|
||||
displayName: Patch Cargo.toml
|
||||
@@ -0,0 +1,18 @@
|
||||
jobs:
|
||||
# Check formatting
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Check rustfmt
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
- script: |
|
||||
rustup component add rustfmt
|
||||
cargo fmt --version
|
||||
displayName: Install rustfmt
|
||||
- script: |
|
||||
# Workaround for rust-lang/cargo#7732
|
||||
rustfmt --check --edition 2018 $(find . -name '*.rs' -print)
|
||||
displayName: Check formatting
|
||||
@@ -0,0 +1,17 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
pool:
|
||||
vmImage: 'Ubuntu 16.04'
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- script: cargo install cargo-hack
|
||||
displayName: Install cargo-hack
|
||||
|
||||
- script: cargo hack test --each-feature
|
||||
displayName: cargo hack test --each-feature
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests-build
|
||||
@@ -0,0 +1,28 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
vmImage: ubuntu-16.04
|
||||
MacOS:
|
||||
vmImage: macOS-10.13
|
||||
Windows:
|
||||
vmImage: vs2017-win2016
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- script: cargo install cargo-hack
|
||||
displayName: Install cargo-hack
|
||||
|
||||
# Run with all crate features
|
||||
- script: cargo hack test --each-feature
|
||||
env:
|
||||
CI: 'True'
|
||||
displayName: cargo hack test --each-feature
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests-integration
|
||||
@@ -0,0 +1,19 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- script: cargo check --all
|
||||
displayName: cargo check --all
|
||||
|
||||
# Check benches
|
||||
- script: cargo check --benches --all
|
||||
displayName: Check benchmarks
|
||||
@@ -0,0 +1,47 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
vmImage: ubuntu-16.04
|
||||
|
||||
${{ if parameters.cross }}:
|
||||
MacOS:
|
||||
vmImage: macOS-10.13
|
||||
Windows:
|
||||
vmImage: vs2017-win2016
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-is-release.yml
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
# Run with all crate features
|
||||
- script: cargo test --all-features
|
||||
env:
|
||||
LOOM_MAX_PREEMPTIONS: 2
|
||||
CI: 'True'
|
||||
displayName: ${{ crate }} - cargo test --all-features
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
|
||||
# Check benches
|
||||
- script: cargo check --all-features --benches
|
||||
displayName: ${{ crate }} - cargo check --benches
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
# Run with all crate features
|
||||
- script: cargo test --all-features
|
||||
env:
|
||||
LOOM_MAX_PREEMPTIONS: 2
|
||||
CI: 'True'
|
||||
displayName: ${{ crate }} - cargo test --all-features
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
@@ -0,0 +1,34 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: TSAN
|
||||
strategy:
|
||||
matrix:
|
||||
Timer:
|
||||
cmd: cargo test -p tokio-timer --test hammer
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
# Make sure the benchmarks compile
|
||||
export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0"
|
||||
export TSAN_OPTIONS="suppressions=`pwd`/ci/tsan"
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
# Run address sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
$(cmd) --target x86_64-unknown-linux-gnu
|
||||
|
||||
# Run thread sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=thread" \
|
||||
$(cmd) --target x86_64-unknown-linux-gnu
|
||||
displayName: TSAN / MSAN
|
||||
env:
|
||||
TSAN: yes
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# Patch dependencies to run all tests against versions of the crate in the
|
||||
# repository.
|
||||
[patch.crates-io]
|
||||
tokio = { path = "tokio" }
|
||||
tokio-macros = { path = "tokio-macros" }
|
||||
tokio-test = { path = "tokio-test" }
|
||||
tokio-tls = { path = "tokio-tls" }
|
||||
tokio-util = { path = "tokio-util" }
|
||||
@@ -8,6 +8,8 @@ race:Weak*drop
|
||||
# `std` mpsc is not used in any Tokio code base. This race is triggered by some
|
||||
# rust runtime logic.
|
||||
race:std*mpsc_queue
|
||||
race:std*lang_start
|
||||
race:drop*std::thread*
|
||||
|
||||
# Probably more fences in std.
|
||||
race:__call_tls_dtors
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
[package]
|
||||
name = "examples"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition = "2018"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "0.2.0", path = "../tokio", features = ["full"] }
|
||||
tokio-util = { version = "0.2.0", path = "../tokio-util", features = ["full"] }
|
||||
bytes = "0.5"
|
||||
futures = "0.3.0"
|
||||
http = "0.2"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
httparse = "1.0"
|
||||
time = "0.1"
|
||||
|
||||
[[example]]
|
||||
name = "chat"
|
||||
path = "chat.rs"
|
||||
|
||||
[[example]]
|
||||
name = "connect"
|
||||
path = "connect.rs"
|
||||
|
||||
[[example]]
|
||||
name = "echo-udp"
|
||||
path = "echo-udp.rs"
|
||||
|
||||
[[example]]
|
||||
name = "echo"
|
||||
path = "echo.rs"
|
||||
|
||||
[[example]]
|
||||
name = "hello_world"
|
||||
path = "hello_world.rs"
|
||||
|
||||
[[example]]
|
||||
name = "print_each_packet"
|
||||
path = "print_each_packet.rs"
|
||||
|
||||
[[example]]
|
||||
name = "proxy"
|
||||
path = "proxy.rs"
|
||||
|
||||
[[example]]
|
||||
name = "tinydb"
|
||||
path = "tinydb.rs"
|
||||
|
||||
[[example]]
|
||||
name = "udp-client"
|
||||
path = "udp-client.rs"
|
||||
|
||||
[[example]]
|
||||
name = "udp-codec"
|
||||
path = "udp-codec.rs"
|
||||
|
||||
[[example]]
|
||||
name = "tinyhttp"
|
||||
path = "tinyhttp.rs"
|
||||
+5
-45
@@ -9,52 +9,12 @@ 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.
|
||||
|
||||
* [`print_each_packet`](print_each_packet.rs) - this server will create a TCP
|
||||
listener, accept connections in a loop, and put down in the stdout everything
|
||||
that's read off of each TCP connection.
|
||||
|
||||
* [`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 approach 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.
|
||||
|
||||
* [`manual-runtime`](manual-runtime.rs) - manually composing a runtime.
|
||||
A good starting point for the examples would be [`hello_world`](hello_world.rs)
|
||||
and [`echo`](echo.rs). Additionally [the tokio website][tokioweb] contains
|
||||
additional guides for some of the examples.
|
||||
|
||||
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!
|
||||
|
||||
[tokioweb]: https://tokio.rs/docs/overview/
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
//! 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.
|
||||
//!
|
||||
//! Because we are here running the reactor/executor on the same thread instead
|
||||
//! of a threadpool, we can avoid full synchronization with Arc + Mutex and use
|
||||
//! Rc + RefCell instead. The max performance is however limited to a CPU HW
|
||||
//! thread.
|
||||
//!
|
||||
//! You can test this out by running:
|
||||
//!
|
||||
//! cargo run --example chat-combinator-current-thread
|
||||
//!
|
||||
//! 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 futures;
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::io;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
use tokio::runtime::current_thread::{Runtime, TaskExecutor};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::io::BufReader;
|
||||
use std::iter;
|
||||
use std::rc::Rc;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
|
||||
// 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()?;
|
||||
|
||||
let socket = TcpListener::bind(&addr)?;
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
// This is running on the Tokio current_thread runtime, so it will be single-
|
||||
// threaded. The `Rc<RefCell<...>>` allows state to be shared across the tasks.
|
||||
let connections = Rc::new(RefCell::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);
|
||||
e
|
||||
})
|
||||
.for_each(move |stream| {
|
||||
// The client's socket address
|
||||
let addr = stream.peer_addr()?;
|
||||
|
||||
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();
|
||||
let mut conns = connections.borrow_mut();
|
||||
conns.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.borrow_mut();
|
||||
|
||||
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 locally a task to process the connection
|
||||
TaskExecutor::current()
|
||||
.spawn_local(Box::new(connection.then(move |_| {
|
||||
let mut conns = connections.borrow_mut();
|
||||
conns.remove(&addr);
|
||||
println!("Connection {} closed.", addr);
|
||||
Ok(())
|
||||
})))
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| println!("error occurred: {:?}", err));
|
||||
|
||||
// Spawn srv itself
|
||||
runtime.spawn(srv);
|
||||
|
||||
// Execute server
|
||||
runtime.run().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
//! 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 futures;
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::io;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::io::BufReader;
|
||||
use std::iter;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
// 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()?;
|
||||
|
||||
let socket = TcpListener::bind(&addr)?;
|
||||
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);
|
||||
e
|
||||
})
|
||||
.for_each(move |stream| {
|
||||
// The client's socket address
|
||||
let addr = stream.peer_addr()?;
|
||||
|
||||
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(())
|
||||
})
|
||||
.map_err(|err| println!("error occurred: {:?}", err));
|
||||
|
||||
// execute server
|
||||
tokio::run(srv);
|
||||
Ok(())
|
||||
}
|
||||
+157
-372
@@ -24,29 +24,64 @@
|
||||
//! connected clients they'll all join the same room and see everyone else's
|
||||
//! messages.
|
||||
|
||||
#![deny(warnings)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
extern crate tokio;
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate bytes;
|
||||
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use futures::future::{self, Either};
|
||||
use futures::sync::mpsc;
|
||||
use tokio::io;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
use tokio::stream::{Stream, StreamExt};
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
use tokio_util::codec::{Framed, LinesCodec, LinesCodecError};
|
||||
|
||||
use futures::SinkExt;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// 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 = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:6142".to_string());
|
||||
|
||||
// Bind a TCP listener to the socket address.
|
||||
//
|
||||
// Note that this is the Tokio TcpListener, which is fully async.
|
||||
let mut listener = TcpListener::bind(&addr).await?;
|
||||
|
||||
println!("server running on {}", addr);
|
||||
|
||||
loop {
|
||||
// Asynchronously wait for an inbound TcpStream.
|
||||
let (stream, addr) = listener.accept().await?;
|
||||
|
||||
// Clone a handle to the `Shared` state for the new connection.
|
||||
let state = Arc::clone(&state);
|
||||
|
||||
// Spawn our handler to be run asynchronously.
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = process(state, stream, addr).await {
|
||||
println!("an error occurred; error = {:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand for the transmit half of the message channel.
|
||||
type Tx = mpsc::UnboundedSender<Bytes>;
|
||||
type Tx = mpsc::UnboundedSender<String>;
|
||||
|
||||
/// Shorthand for the receive half of the message channel.
|
||||
type Rx = mpsc::UnboundedReceiver<Bytes>;
|
||||
type Rx = mpsc::UnboundedReceiver<String>;
|
||||
|
||||
/// Data that is shared between all peers in the chat server.
|
||||
///
|
||||
@@ -60,64 +95,18 @@ struct Shared {
|
||||
|
||||
/// 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>>,
|
||||
lines: Framed<TcpStream, LinesCodec>,
|
||||
|
||||
/// 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 {
|
||||
@@ -127,347 +116,143 @@ impl Shared {
|
||||
peers: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a `LineCodec` encoded message to every peer, except
|
||||
/// for the sender.
|
||||
async fn broadcast(&mut self, sender: SocketAddr, message: &str) {
|
||||
for peer in self.peers.iter_mut() {
|
||||
if *peer.0 != sender {
|
||||
let _ = peer.1.send(message.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Peer {
|
||||
/// Create a new instance of `Peer`.
|
||||
fn new(name: BytesMut, state: Arc<Mutex<Shared>>, lines: Lines) -> Peer {
|
||||
async fn new(
|
||||
state: Arc<Mutex<Shared>>,
|
||||
lines: Framed<TcpStream, LinesCodec>,
|
||||
) -> io::Result<Peer> {
|
||||
// Get the client socket address
|
||||
let addr = lines.socket.peer_addr().unwrap();
|
||||
let addr = lines.get_ref().peer_addr()?;
|
||||
|
||||
// Create a channel for this peer
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
|
||||
// Add an entry for this `Peer` in the shared state map.
|
||||
state.lock().unwrap().peers.insert(addr, tx);
|
||||
state.lock().await.peers.insert(addr, tx);
|
||||
|
||||
Peer {
|
||||
name,
|
||||
lines,
|
||||
state,
|
||||
rx,
|
||||
addr,
|
||||
}
|
||||
Ok(Peer { lines, rx })
|
||||
}
|
||||
}
|
||||
|
||||
/// This is where a connected client is managed.
|
||||
///
|
||||
/// A `Peer` is also a future representing completely 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;
|
||||
#[derive(Debug)]
|
||||
enum Message {
|
||||
/// A message that should be broadcasted to others.
|
||||
Broadcast(String),
|
||||
|
||||
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)
|
||||
}
|
||||
/// A message that should be received by a client
|
||||
Received(String),
|
||||
}
|
||||
|
||||
impl Drop for Peer {
|
||||
fn drop(&mut self) {
|
||||
self.state.lock().unwrap().peers.remove(&self.addr);
|
||||
}
|
||||
}
|
||||
// Peer implements `Stream` in a way that polls both the `Rx`, and `Framed` types.
|
||||
// A message is produced whenever an event is ready until the `Framed` stream returns `None`.
|
||||
impl Stream for Peer {
|
||||
type Item = Result<Message, LinesCodecError>;
|
||||
|
||||
impl Lines {
|
||||
/// Create a new `Lines` codec backed by the socket
|
||||
fn new(socket: TcpStream) -> Self {
|
||||
Lines {
|
||||
socket,
|
||||
rd: BytesMut::new(),
|
||||
wr: BytesMut::new(),
|
||||
}
|
||||
}
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
// First poll the `UnboundedReceiver`.
|
||||
|
||||
/// 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 write some bytes to 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);
|
||||
if let Poll::Ready(Some(v)) = Pin::new(&mut self.rx).poll_next(cx) {
|
||||
return Poll::Ready(Some(Ok(Message::Received(v))));
|
||||
}
|
||||
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
// Secondly poll the `Framed` stream.
|
||||
let result: Option<_> = futures::ready!(Pin::new(&mut self.lines).poll_next(cx));
|
||||
|
||||
/// 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);
|
||||
Poll::Ready(match result {
|
||||
// We've received a message we should broadcast to others.
|
||||
Some(Ok(message)) => Some(Ok(Message::Broadcast(message))),
|
||||
|
||||
// Read data into the buffer.
|
||||
let n = try_ready!(self.socket.read_buf(&mut self.rd));
|
||||
// An error occurred.
|
||||
Some(Err(e)) => Some(Err(e)),
|
||||
|
||||
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)
|
||||
// The stream has been exhausted.
|
||||
None => None,
|
||||
})
|
||||
// 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() -> Result<(), Box<std::error::Error>> {
|
||||
// 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()));
|
||||
/// Process an individual chat client
|
||||
async fn process(
|
||||
state: Arc<Mutex<Shared>>,
|
||||
stream: TcpStream,
|
||||
addr: SocketAddr,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut lines = Framed::new(stream, LinesCodec::new());
|
||||
|
||||
let addr = "127.0.0.1:6142".parse()?;
|
||||
// Send a prompt to the client to enter their username.
|
||||
lines
|
||||
.send(String::from("Please enter your username:"))
|
||||
.await?;
|
||||
|
||||
// Bind a TCP listener to the socket address.
|
||||
//
|
||||
// Note that this is the Tokio TcpListener, which is fully async.
|
||||
let listener = TcpListener::bind(&addr)?;
|
||||
// Read the first line from the `LineCodec` stream to get the username.
|
||||
let username = match lines.next().await {
|
||||
Some(Ok(line)) => line,
|
||||
// We didn't get a line so we return early here.
|
||||
_ => {
|
||||
println!("Failed to get username from {}. Client disconnected.", addr);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
// 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);
|
||||
});
|
||||
// Register our peer with state which internally sets up some channels.
|
||||
let mut peer = Peer::new(state.clone(), lines).await?;
|
||||
|
||||
println!("server running on localhost:6142");
|
||||
// A client has connected, let's let everyone know.
|
||||
{
|
||||
let mut state = state.lock().await;
|
||||
let msg = format!("{} has joined the chat", username);
|
||||
println!("{}", msg);
|
||||
state.broadcast(addr, &msg).await;
|
||||
}
|
||||
|
||||
// Process incoming messages until our stream is exhausted by a disconnect.
|
||||
while let Some(result) = peer.next().await {
|
||||
match result {
|
||||
// A message was received from the current user, we should
|
||||
// broadcast this message to the other users.
|
||||
Ok(Message::Broadcast(msg)) => {
|
||||
let mut state = state.lock().await;
|
||||
let msg = format!("{}: {}", username, msg);
|
||||
|
||||
state.broadcast(addr, &msg).await;
|
||||
}
|
||||
// A message was received from a peer. Send it to the
|
||||
// current user.
|
||||
Ok(Message::Received(msg)) => {
|
||||
peer.lines.send(msg).await?;
|
||||
}
|
||||
Err(e) => {
|
||||
println!(
|
||||
"an error occurred while processing messages for {}; error = {:?}",
|
||||
username, e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this section is reached it means that the client was disconnected!
|
||||
// Let's let everyone still connected know about it.
|
||||
{
|
||||
let mut state = state.lock().await;
|
||||
state.peers.remove(&addr);
|
||||
|
||||
let msg = format!("{} has left the chat", username);
|
||||
println!("{}", msg);
|
||||
state.broadcast(addr, &msg).await;
|
||||
}
|
||||
|
||||
// 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);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+86
-194
@@ -14,22 +14,18 @@
|
||||
//! 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)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
extern crate bytes;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
use futures::StreamExt;
|
||||
use tokio::io;
|
||||
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
|
||||
|
||||
use std::env;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
use std::thread;
|
||||
|
||||
use futures::sync::mpsc;
|
||||
use tokio::prelude::*;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// 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") {
|
||||
@@ -41,217 +37,113 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
};
|
||||
|
||||
// Parse what address we're going to connect to
|
||||
let addr = match args.first() {
|
||||
Some(addr) => addr,
|
||||
None => Err("this program requires at least one argument")?,
|
||||
};
|
||||
let addr = args
|
||||
.first()
|
||||
.ok_or("this program requires at least one argument")?;
|
||||
let addr = addr.parse::<SocketAddr>()?;
|
||||
|
||||
// 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"));
|
||||
let stdin = FramedRead::new(io::stdin(), BytesCodec::new());
|
||||
let stdin = stdin.map(|i| i.map(|bytes| bytes.freeze()));
|
||||
let stdout = FramedWrite::new(io::stdout(), BytesCodec::new());
|
||||
|
||||
// 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))?
|
||||
if tcp {
|
||||
tcp::connect(&addr, stdin, stdout).await?;
|
||||
} else {
|
||||
udp::connect(&addr, Box::new(stdin_rx))?
|
||||
};
|
||||
udp::connect(&addr, stdin, stdout).await?;
|
||||
}
|
||||
|
||||
// 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))
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod codec {
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use std::io;
|
||||
use tokio::codec::{Decoder, Encoder};
|
||||
|
||||
/// 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::codec::Decoder;
|
||||
use bytes::Bytes;
|
||||
use futures::{future, Sink, SinkExt, Stream, StreamExt};
|
||||
use std::{error::Error, io, net::SocketAddr};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use codec::Bytes;
|
||||
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
pub fn connect(
|
||||
pub async fn connect(
|
||||
addr: &SocketAddr,
|
||||
stdin: Box<Stream<Item = Vec<u8>, Error = io::Error> + Send>,
|
||||
) -> Result<Box<Stream<Item = BytesMut, Error = io::Error> + Send>, Box<Error>> {
|
||||
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.
|
||||
let stream = Box::new(
|
||||
tcp.map(move |stream| {
|
||||
let (sink, stream) = Bytes.framed(stream).split();
|
||||
|
||||
tokio::spawn(stdin.forward(sink).then(|result| {
|
||||
if let Err(e) = result {
|
||||
println!("failed to write to socket: {}", e)
|
||||
}
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
stream
|
||||
mut stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
|
||||
mut stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut stream = TcpStream::connect(addr).await?;
|
||||
let (r, w) = stream.split();
|
||||
let mut sink = FramedWrite::new(w, BytesCodec::new());
|
||||
// filter map Result<BytesMut, Error> stream into just a Bytes stream to match stdout Sink
|
||||
// on the event of an Error, log the error and end the stream
|
||||
let mut stream = FramedRead::new(r, BytesCodec::new())
|
||||
.filter_map(|i| match i {
|
||||
//BytesMut into Bytes
|
||||
Ok(i) => future::ready(Some(i.freeze())),
|
||||
Err(e) => {
|
||||
println!("failed to read from socket; error={}", e);
|
||||
future::ready(None)
|
||||
}
|
||||
})
|
||||
.flatten_stream(),
|
||||
);
|
||||
Ok(stream)
|
||||
.map(Ok);
|
||||
|
||||
match future::join(sink.send_all(&mut stdin), stdout.send_all(&mut stream)).await {
|
||||
(Err(e), _) | (_, Err(e)) => Err(e.into()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod udp {
|
||||
use bytes::Bytes;
|
||||
use futures::{future, Sink, SinkExt, Stream, StreamExt};
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::udp::{RecvHalf, SendHalf};
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use bytes::BytesMut;
|
||||
use tokio;
|
||||
use tokio::net::{UdpFramed, UdpSocket};
|
||||
use tokio::prelude::*;
|
||||
|
||||
use codec::Bytes;
|
||||
|
||||
pub fn connect(
|
||||
&addr: &SocketAddr,
|
||||
stdin: Box<Stream<Item = Vec<u8>, Error = io::Error> + Send>,
|
||||
) -> Result<Box<Stream<Item = BytesMut, Error = io::Error> + Send>, Box<Error>> {
|
||||
pub async fn connect(
|
||||
addr: &SocketAddr,
|
||||
stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
|
||||
stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// 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()?
|
||||
let bind_addr = if addr.ip().is_ipv4() {
|
||||
"0.0.0.0:0"
|
||||
} else {
|
||||
"[::]:0".parse()?
|
||||
};
|
||||
let udp = match UdpSocket::bind(&addr_to_bind) {
|
||||
Ok(udp) => udp,
|
||||
Err(_) => Err("failed to bind socket")?,
|
||||
"[::]:0"
|
||||
};
|
||||
|
||||
// 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();
|
||||
let socket = UdpSocket::bind(&bind_addr).await?;
|
||||
socket.connect(addr).await?;
|
||||
let (mut r, mut w) = socket.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 {
|
||||
println!("failed to write to socket: {}", e)
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
future::try_join(send(stdin, &mut w), recv(stdout, &mut r)).await?;
|
||||
|
||||
// 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
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send(
|
||||
mut stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
|
||||
writer: &mut SendHalf,
|
||||
) -> Result<(), io::Error> {
|
||||
while let Some(item) = stdin.next().await {
|
||||
let buf = item?;
|
||||
writer.send(&buf[..]).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn recv(
|
||||
mut stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
|
||||
reader: &mut RecvHalf,
|
||||
) -> Result<(), io::Error> {
|
||||
loop {
|
||||
let mut buf = vec![0; 1024];
|
||||
let n = reader.recv(&mut buf[..]).await?;
|
||||
|
||||
if n > 0 {
|
||||
stdout.send(Bytes::from(buf)).await?;
|
||||
}
|
||||
});
|
||||
|
||||
let stream = Box::new(
|
||||
future::lazy(|| {
|
||||
tokio::spawn(forward_stdin);
|
||||
future::ok(receive)
|
||||
})
|
||||
.flatten_stream(),
|
||||
);
|
||||
Ok(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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-26
@@ -10,17 +10,13 @@
|
||||
//!
|
||||
//! 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;
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
use std::{env, io};
|
||||
|
||||
use tokio;
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::prelude::*;
|
||||
|
||||
struct Server {
|
||||
socket: UdpSocket,
|
||||
@@ -28,47 +24,48 @@ struct Server {
|
||||
to_send: Option<(usize, SocketAddr)>,
|
||||
}
|
||||
|
||||
impl Future for Server {
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
impl Server {
|
||||
async fn run(self) -> Result<(), io::Error> {
|
||||
let Server {
|
||||
mut socket,
|
||||
mut buf,
|
||||
mut to_send,
|
||||
} = self;
|
||||
|
||||
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));
|
||||
if let Some((size, peer)) = to_send {
|
||||
let amt = socket.send_to(&buf[..size], &peer).await?;
|
||||
|
||||
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)));
|
||||
to_send = Some(socket.recv_from(&mut buf).await?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>()?;
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
|
||||
let socket = UdpSocket::bind(&addr)?;
|
||||
let socket = UdpSocket::bind(&addr).await?;
|
||||
println!("Listening on: {}", socket.local_addr()?);
|
||||
|
||||
let server = Server {
|
||||
socket: 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` spawns the task on the Tokio runtime and starts running.
|
||||
tokio::run(server.map_err(|e| println!("server error = {:?}", e)));
|
||||
server.run().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+40
-76
@@ -19,97 +19,61 @@
|
||||
//! you! If you open up multiple terminals running the `connect` example you
|
||||
//! should be able to see them all make progress simultaneously.
|
||||
|
||||
#![deny(warnings)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::io;
|
||||
use tokio;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
use std::error::Error;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// 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>()?;
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
|
||||
// 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)?;
|
||||
// above and must be associated with an event loop.
|
||||
let mut listener = TcpListener::bind(&addr).await?;
|
||||
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);
|
||||
loop {
|
||||
// Asynchronously wait for an inbound socket.
|
||||
let (mut socket, _) = listener.accept().await?;
|
||||
|
||||
// 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),
|
||||
// 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.
|
||||
//
|
||||
// Essentially here we're executing a new task to run concurrently,
|
||||
// which will allow all of our clients to be processed concurrently.
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
// In a loop, read data from the socket and write the data back.
|
||||
loop {
|
||||
let n = socket
|
||||
.read(&mut buf)
|
||||
.await
|
||||
.expect("failed to read data from socket");
|
||||
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
socket
|
||||
.write_all(&buf[0..n])
|
||||
.await
|
||||
.expect("failed to write data to socket");
|
||||
}
|
||||
});
|
||||
|
||||
// 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);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+9
-34
@@ -11,48 +11,23 @@
|
||||
//!
|
||||
//! cargo run --example hello_world
|
||||
|
||||
#![deny(warnings)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::io;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
|
||||
pub fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let addr = "127.0.0.1:6142".parse()?;
|
||||
use std::error::Error;
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Open a TCP stream to the socket address.
|
||||
//
|
||||
// Note that this is the Tokio TcpStream, which is fully async.
|
||||
let client = TcpStream::connect(&addr)
|
||||
.and_then(|stream| {
|
||||
println!("created stream");
|
||||
io::write_all(stream, "hello world\n").then(|result| {
|
||||
println!("wrote to stream; success={:?}", result.is_ok());
|
||||
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!("connection error = {:?}", err);
|
||||
});
|
||||
let mut stream = TcpStream::connect("127.0.0.1:6142").await?;
|
||||
println!("created stream");
|
||||
|
||||
// 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.
|
||||
println!("About to create the stream and write to it...");
|
||||
tokio::run(client);
|
||||
println!("Stream has been created and written to.");
|
||||
let result = stream.write(b"hello world\n").await;
|
||||
println!("wrote to stream; success={:?}", result.is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
//! An example how to manually assemble a runtime and run some tasks on it.
|
||||
//!
|
||||
//! This is closer to the single-threaded runtime than the default tokio one, as it is simpler to
|
||||
//! grasp. There are conceptually similar, but the multi-threaded one would be more code. If you
|
||||
//! just want to *use* a single-threaded runtime, use the one provided by tokio directly
|
||||
//! (`tokio::runtime::current_thread::Runtime::new()`. This is a demonstration only.
|
||||
//!
|
||||
//! Note that the error handling is a bit left out. Also, the `run` could be modified to return the
|
||||
//! result of the provided future.
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_current_thread;
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_reactor;
|
||||
extern crate tokio_timer;
|
||||
|
||||
use std::io::Error as IoError;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use futures::{future, Future};
|
||||
use tokio_current_thread::CurrentThread;
|
||||
use tokio_reactor::Reactor;
|
||||
use tokio_timer::timer::{self, Timer};
|
||||
|
||||
/// Creates a "runtime".
|
||||
///
|
||||
/// This is similar to running `tokio::runtime::current_thread::Runtime::new()`.
|
||||
fn run<F: Future<Item = (), Error = ()>>(f: F) -> Result<(), IoError> {
|
||||
// We need a reactor to receive events about IO objects from kernel
|
||||
let reactor = Reactor::new()?;
|
||||
let reactor_handle = reactor.handle();
|
||||
// Place a timer wheel on top of the reactor. If there are no timeouts to fire, it'll let the
|
||||
// reactor pick up some new external events.
|
||||
let timer = Timer::new(reactor);
|
||||
let timer_handle = timer.handle();
|
||||
// And now put a single-threaded executor on top of the timer. When there are no futures ready
|
||||
// to do something, it'll let the timer or the reactor generate some new stimuli for the
|
||||
// futures to continue in their life.
|
||||
let mut executor = CurrentThread::new_with_park(timer);
|
||||
// Binds an executor to this thread
|
||||
let mut enter = tokio_executor::enter().expect("Multiple executors at once");
|
||||
// This will set the default handle and timer to use inside the closure and run the future.
|
||||
tokio_reactor::with_default(&reactor_handle, &mut enter, |enter| {
|
||||
timer::with_default(&timer_handle, enter, |enter| {
|
||||
// The TaskExecutor is a fake executor that looks into the current single-threaded
|
||||
// executor when used. This is a trick, because we need two mutable references to the
|
||||
// executor (one to run the provided future, another to install as the default one). We
|
||||
// use the fake one here as the default one.
|
||||
let mut default_executor = tokio_current_thread::TaskExecutor::current();
|
||||
tokio_executor::with_default(&mut default_executor, enter, |enter| {
|
||||
let mut executor = executor.enter(enter);
|
||||
// Run the provided future
|
||||
executor.block_on(f).unwrap();
|
||||
// Run all the other futures that are still left in the executor
|
||||
executor.run().unwrap();
|
||||
});
|
||||
});
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
run(future::lazy(|| {
|
||||
// Here comes the application logic. It can spawn further tasks by tokio_current_thread::spawn().
|
||||
// It also can use the default reactor and create timeouts.
|
||||
|
||||
// Connect somewhere. And then do nothing with it. Yes, useless.
|
||||
//
|
||||
// This will use the default reactor which runs in the current thread.
|
||||
let connect = tokio::net::TcpStream::connect(&"127.0.0.1:53".parse().unwrap())
|
||||
.map(|_| println!("Connected"))
|
||||
.map_err(|e| println!("Failed to connect: {}", e));
|
||||
// We can spawn it without requiring Send. This would panic if we run it outside of the
|
||||
// `run` (or outside of anything else)
|
||||
tokio_current_thread::spawn(connect);
|
||||
|
||||
// We can also create timeouts.
|
||||
let deadline = tokio::timer::Delay::new(Instant::now() + Duration::from_secs(5))
|
||||
.map(|()| println!("5 seconds are over"))
|
||||
.map_err(|e| println!("Failed to wait: {}", e));
|
||||
// We can spawn on the default executor, which is also the local one.
|
||||
tokio::executor::spawn(deadline);
|
||||
Ok(())
|
||||
}))?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -52,99 +52,55 @@
|
||||
//! ```
|
||||
//!
|
||||
|
||||
#![deny(warnings)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
extern crate tokio;
|
||||
extern crate tokio_codec;
|
||||
|
||||
use tokio::codec::Decoder;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
use tokio_codec::BytesCodec;
|
||||
use tokio::stream::StreamExt;
|
||||
use tokio_util::codec::{BytesCodec, Decoder};
|
||||
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// 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>()?;
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
|
||||
// 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)?;
|
||||
let mut listener = TcpListener::bind(&addr).await?;
|
||||
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're parsing each socket with the `BytesCodec` included in `tokio_io`,
|
||||
// and then we `split` each codec into the reader/writer halves.
|
||||
//
|
||||
// See https://docs.rs/tokio-codec/0.1/src/tokio_codec/bytes_codec.rs.html
|
||||
let framed = BytesCodec::new().framed(socket);
|
||||
let (_writer, reader) = framed.split();
|
||||
loop {
|
||||
// Asynchronously wait for an inbound socket.
|
||||
let (socket, _) = listener.accept().await?;
|
||||
|
||||
let processor = reader
|
||||
.for_each(|bytes| {
|
||||
println!("bytes: {:?}", bytes);
|
||||
Ok(())
|
||||
})
|
||||
// After our copy operation is complete we just print out some helpful
|
||||
// information.
|
||||
.and_then(|()| {
|
||||
println!("Socket received FIN packet and closed connection");
|
||||
Ok(())
|
||||
})
|
||||
.or_else(|err| {
|
||||
println!("Socket closed with error: {:?}", err);
|
||||
// We have to return the error to catch it in the next ``.then` call
|
||||
Err(err)
|
||||
})
|
||||
.then(|result| {
|
||||
println!("Socket closed with result: {:?}", result);
|
||||
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.
|
||||
//
|
||||
// Essentially here we're executing a new task to run concurrently,
|
||||
// which will allow all of our clients to be processed concurrently.
|
||||
tokio::spawn(async move {
|
||||
// We're parsing each socket with the `BytesCodec` included in `tokio::codec`.
|
||||
let mut framed = BytesCodec::new().framed(socket);
|
||||
|
||||
// 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(processor)
|
||||
// We loop while there are messages coming from the Stream `framed`.
|
||||
// The stream will return None once the client disconnects.
|
||||
while let Some(message) = framed.next().await {
|
||||
match message {
|
||||
Ok(bytes) => println!("bytes: {:?}", bytes),
|
||||
Err(err) => println!("Socket closed with error: {:?}", err),
|
||||
}
|
||||
}
|
||||
println!("Socket received FIN packet and closed connection");
|
||||
});
|
||||
|
||||
// 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);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+34
-92
@@ -20,111 +20,53 @@
|
||||
//! 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)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
extern crate tokio;
|
||||
|
||||
use std::env;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::{Shutdown, SocketAddr};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use tokio::io::{copy, shutdown};
|
||||
use tokio::io;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let listen_addr = env::args().nth(1).unwrap_or("127.0.0.1:8081".to_string());
|
||||
let listen_addr = listen_addr.parse::<SocketAddr>()?;
|
||||
use futures::future::try_join;
|
||||
use futures::FutureExt;
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
|
||||
let server_addr = env::args().nth(2).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let server_addr = server_addr.parse::<SocketAddr>()?;
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let listen_addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8081".to_string());
|
||||
let server_addr = env::args()
|
||||
.nth(2)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
|
||||
// Create a TCP listener which will listen for incoming connections.
|
||||
let socket = TcpListener::bind(&listen_addr)?;
|
||||
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();
|
||||
let mut listener = TcpListener::bind(listen_addr).await?;
|
||||
|
||||
// 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(())
|
||||
while let Ok((inbound, _)) = listener.accept().await {
|
||||
let transfer = transfer(inbound, server_addr.clone()).map(|r| {
|
||||
if let Err(e) = r {
|
||||
println!("Failed to transfer; error={}", e);
|
||||
}
|
||||
});
|
||||
|
||||
tokio::run(done);
|
||||
tokio::spawn(transfer);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 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>>);
|
||||
async fn transfer(mut inbound: TcpStream, proxy_addr: String) -> Result<(), Box<dyn Error>> {
|
||||
let mut outbound = TcpStream::connect(proxy_addr).await?;
|
||||
|
||||
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())
|
||||
}
|
||||
let (mut ri, mut wi) = inbound.split();
|
||||
let (mut ro, mut wo) = outbound.split();
|
||||
|
||||
let client_to_server = io::copy(&mut ri, &mut wo);
|
||||
let server_to_client = io::copy(&mut ro, &mut wi);
|
||||
|
||||
try_join(client_to_server, server_to_client).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+82
-85
@@ -39,20 +39,18 @@
|
||||
//! * `SET $key $value` - this will set the value of `$key` to `$value`,
|
||||
//! returning the previous value, if any.
|
||||
|
||||
#![deny(warnings)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
extern crate tokio;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::stream::StreamExt;
|
||||
use tokio_util::codec::{Framed, LinesCodec};
|
||||
|
||||
use futures::SinkExt;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::io::BufReader;
|
||||
use std::net::SocketAddr;
|
||||
use std::error::Error;
|
||||
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
|
||||
@@ -83,12 +81,15 @@ enum Response {
|
||||
},
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// 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>()?;
|
||||
let listener = TcpListener::bind(&addr).map_err(|_| "failed to bind")?;
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
|
||||
let mut listener = TcpListener::bind(&addr).await?;
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
// Create the shared state of this server that will be shared amongst all
|
||||
@@ -102,91 +103,87 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
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();
|
||||
loop {
|
||||
match listener.accept().await {
|
||||
Ok((socket, _)) => {
|
||||
// After getting a new connection first we see a clone of the database
|
||||
// being created, which is creating a new reference for this connected
|
||||
// client to use.
|
||||
let db = db.clone();
|
||||
|
||||
// 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));
|
||||
// Like with other small servers, we'll `spawn` this client to ensure it
|
||||
// runs concurrently with all other clients. The `move` keyword is used
|
||||
// here to move ownership of our db handle into the async closure.
|
||||
tokio::spawn(async move {
|
||||
// Since our protocol is line-based we use `tokio_codecs`'s `LineCodec`
|
||||
// to convert our stream of bytes, `socket`, into a `Stream` of lines
|
||||
// as well as convert our line based responses into a stream of bytes.
|
||||
let mut lines = Framed::new(socket, LinesCodec::new());
|
||||
|
||||
// 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 },
|
||||
};
|
||||
// Here for every line we get back from the `Framed` decoder,
|
||||
// we parse the request, and if it's valid we generate a response
|
||||
// based on the values in the database.
|
||||
while let Some(result) = lines.next().await {
|
||||
match result {
|
||||
Ok(line) => {
|
||||
let response = handle_request(&line, &db);
|
||||
|
||||
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,
|
||||
let response = response.serialize();
|
||||
|
||||
if let Err(e) = lines.send(response).await {
|
||||
println!("error on sending response; error = {:?}", e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("error on decoding from socket; error = {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 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)
|
||||
});
|
||||
// The connection will be closed at this point as `lines.next()` has returned `None`.
|
||||
});
|
||||
}
|
||||
Err(e) => println!("error accepting socket; error = {:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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(()));
|
||||
fn handle_request(line: &str, db: &Arc<Database>) -> Response {
|
||||
let request = match Request::parse(&line) {
|
||||
Ok(req) => req,
|
||||
Err(e) => return Response::Error { msg: e },
|
||||
};
|
||||
|
||||
tokio::spawn(msg)
|
||||
});
|
||||
|
||||
tokio::run(done);
|
||||
Ok(())
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Request {
|
||||
fn parse(input: &str) -> Result<Request, String> {
|
||||
let mut parts = input.splitn(3, " ");
|
||||
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")),
|
||||
};
|
||||
let key = parts.next().ok_or("GET must be followed by a key")?;
|
||||
if parts.next().is_some() {
|
||||
return Err(format!("GET's key must not be followed by anything"));
|
||||
return Err("GET's key must not be followed by anything".into());
|
||||
}
|
||||
Ok(Request::Get {
|
||||
key: key.to_string(),
|
||||
@@ -195,11 +192,11 @@ impl Request {
|
||||
Some("SET") => {
|
||||
let key = match parts.next() {
|
||||
Some(key) => key,
|
||||
None => return Err(format!("SET must be followed by a key")),
|
||||
None => return Err("SET must be followed by a key".into()),
|
||||
};
|
||||
let value = match parts.next() {
|
||||
Some(value) => value,
|
||||
None => return Err(format!("SET needs a value")),
|
||||
None => return Err("SET needs a value".into()),
|
||||
};
|
||||
Ok(Request::Set {
|
||||
key: key.to_string(),
|
||||
@@ -207,7 +204,7 @@ impl Request {
|
||||
})
|
||||
}
|
||||
Some(cmd) => Err(format!("unknown command: {}", cmd)),
|
||||
None => Err(format!("empty input")),
|
||||
None => Err("empty input".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+73
-93
@@ -11,108 +11,85 @@
|
||||
//! 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::net::SocketAddr;
|
||||
use std::{env, fmt, io};
|
||||
|
||||
use tokio::codec::{Decoder, Encoder};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use bytes::BytesMut;
|
||||
use http::header::HeaderValue;
|
||||
use http::{Request, Response, StatusCode};
|
||||
use futures::SinkExt;
|
||||
use http::{header::HeaderValue, Request, Response, StatusCode};
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
use serde_json;
|
||||
use std::{env, error::Error, fmt, io};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::stream::StreamExt;
|
||||
use tokio_util::codec::{Decoder, Encoder, Framed};
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// 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>()?;
|
||||
|
||||
let listener = TcpListener::bind(&addr)?;
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
let mut server = TcpListener::bind(&addr).await?;
|
||||
let mut incoming = server.incoming();
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
tokio::run({
|
||||
listener
|
||||
.incoming()
|
||||
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
|
||||
.for_each(|socket| {
|
||||
process(socket);
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
while let Some(Ok(stream)) = incoming.next().await {
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = process(stream).await {
|
||||
println!("failed to process connection; error = {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process(socket: TcpStream) {
|
||||
let (tx, rx) =
|
||||
// Frame the socket using the `Http` protocol. This maps the TCP socket
|
||||
// to a Stream + Sink of HTTP frames.
|
||||
Http.framed(socket)
|
||||
// This splits a single `Stream + Sink` value into two separate handles
|
||||
// that can be used independently (even on different tasks or threads).
|
||||
.split();
|
||||
async fn process(stream: TcpStream) -> Result<(), Box<dyn Error>> {
|
||||
let mut transport = Framed::new(stream, Http);
|
||||
|
||||
// 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);
|
||||
while let Some(request) = transport.next().await {
|
||||
match request {
|
||||
Ok(request) => {
|
||||
let response = respond(request).await?;
|
||||
transport.send(response).await?;
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// Spawn the task that handles the connection.
|
||||
tokio::spawn(task);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// "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 f = future::lazy(move || {
|
||||
let mut response = Response::builder();
|
||||
let body = match req.uri().path() {
|
||||
"/plaintext" => {
|
||||
response.header("Content-Type", "text/plain");
|
||||
"Hello, World!".to_string()
|
||||
}
|
||||
"/json" => {
|
||||
response.header("Content-Type", "application/json");
|
||||
async fn respond(req: Request<()>) -> Result<Response<String>, Box<dyn Error>> {
|
||||
let mut response = Response::builder();
|
||||
let body = match req.uri().path() {
|
||||
"/plaintext" => {
|
||||
response = response.header("Content-Type", "text/plain");
|
||||
"Hello, World!".to_string()
|
||||
}
|
||||
"/json" => {
|
||||
response = response.header("Content-Type", "application/json");
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Message {
|
||||
message: &'static str,
|
||||
}
|
||||
serde_json::to_string(&Message {
|
||||
message: "Hello, World!",
|
||||
})?
|
||||
#[derive(Serialize)]
|
||||
struct Message {
|
||||
message: &'static str,
|
||||
}
|
||||
_ => {
|
||||
response.status(StatusCode::NOT_FOUND);
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
let response = response
|
||||
.body(body)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
|
||||
Ok(response)
|
||||
});
|
||||
serde_json::to_string(&Message {
|
||||
message: "Hello, World!",
|
||||
})?
|
||||
}
|
||||
_ => {
|
||||
response = response.status(StatusCode::NOT_FOUND);
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
let response = response
|
||||
.body(body)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
|
||||
|
||||
Box::new(f)
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
struct Http;
|
||||
@@ -157,13 +134,13 @@ impl Encoder for Http {
|
||||
// doesn't go through io::Error.
|
||||
struct BytesWrite<'a>(&'a mut BytesMut);
|
||||
|
||||
impl<'a> fmt::Write for BytesWrite<'a> {
|
||||
impl fmt::Write for BytesWrite<'_> {
|
||||
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 {
|
||||
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
|
||||
fmt::write(self, args)
|
||||
}
|
||||
}
|
||||
@@ -222,16 +199,19 @@ impl Decoder for Http {
|
||||
}
|
||||
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);
|
||||
ret = ret.method(&data[method.0..method.1]);
|
||||
let s = data.slice(path.0..path.1);
|
||||
let s = unsafe { String::from_utf8_unchecked(Vec::from(s.as_ref())) };
|
||||
ret = ret.uri(s);
|
||||
ret = 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 value = HeaderValue::from_bytes(data.slice(v.0..v.1).as_ref())
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::Other, "header decode error"))?;
|
||||
ret = ret.header(&data[k.0..k.1], value);
|
||||
}
|
||||
|
||||
let req = ret
|
||||
@@ -286,7 +266,7 @@ mod date {
|
||||
}));
|
||||
|
||||
impl fmt::Display for Now {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
LAST.with(|cache| {
|
||||
let mut cache = cache.borrow_mut();
|
||||
let now = time::get_time();
|
||||
@@ -313,7 +293,7 @@ mod date {
|
||||
|
||||
struct LocalBuffer<'a>(&'a mut LastRenderedNow);
|
||||
|
||||
impl<'a> fmt::Write for LocalBuffer<'a> {
|
||||
impl fmt::Write for LocalBuffer<'_> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
let start = self.0.amt;
|
||||
let end = start + s.len();
|
||||
|
||||
+21
-19
@@ -26,26 +26,27 @@
|
||||
//! 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;
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use std::env;
|
||||
use std::io::stdin;
|
||||
use std::error::Error;
|
||||
use std::io::{stdin, Read};
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::prelude::*;
|
||||
|
||||
fn get_stdin_data() -> Result<Vec<u8>, Box<std::error::Error>> {
|
||||
fn get_stdin_data() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||
let mut buf = Vec::new();
|
||||
stdin().read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let remote_addr: SocketAddr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("127.0.0.1:8080".into())
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".into())
|
||||
.parse()?;
|
||||
|
||||
// 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"
|
||||
@@ -53,18 +54,19 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
"[::]:0"
|
||||
}
|
||||
.parse()?;
|
||||
let socket = UdpSocket::bind(&local_addr)?;
|
||||
|
||||
let mut socket = UdpSocket::bind(local_addr).await?;
|
||||
const MAX_DATAGRAM_SIZE: usize = 65_507;
|
||||
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()?;
|
||||
socket.connect(&remote_addr).await?;
|
||||
let data = get_stdin_data()?;
|
||||
socket.send(&data).await?;
|
||||
let mut data = vec![0u8; MAX_DATAGRAM_SIZE];
|
||||
let len = socket.recv(&mut data).await?;
|
||||
println!(
|
||||
"Received {} bytes:\n{}",
|
||||
len,
|
||||
String::from_utf8_lossy(&data[..len])
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+56
-41
@@ -1,65 +1,80 @@
|
||||
//! 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
|
||||
//! Here we're using the codec from `tokio-codec` 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)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
extern crate env_logger;
|
||||
extern crate tokio;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::stream::StreamExt;
|
||||
use tokio::{io, time};
|
||||
use tokio_util::codec::BytesCodec;
|
||||
use tokio_util::udp::UdpFramed;
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures::{FutureExt, SinkExt};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
use std::time::Duration;
|
||||
|
||||
use tokio::net::{UdpFramed, UdpSocket};
|
||||
use tokio::prelude::*;
|
||||
use tokio_codec::BytesCodec;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let addr: SocketAddr = "127.0.0.1:0".parse()?;
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:0".to_string());
|
||||
|
||||
// Bind both our sockets and then figure out what ports we got.
|
||||
let a = UdpSocket::bind(&addr)?;
|
||||
let b = UdpSocket::bind(&addr)?;
|
||||
let a = UdpSocket::bind(&addr).await?;
|
||||
let b = UdpSocket::bind(&addr).await?;
|
||||
|
||||
let b_addr = b.local_addr()?;
|
||||
|
||||
// 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();
|
||||
let mut a = UdpFramed::new(a, BytesCodec::new());
|
||||
let mut b = UdpFramed::new(b, BytesCodec::new());
|
||||
|
||||
// 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)
|
||||
});
|
||||
let a = ping(&mut a, b_addr);
|
||||
|
||||
// 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);
|
||||
let b = pong(&mut b);
|
||||
|
||||
// Run both futures simultaneously of `a` and `b` sending messages back and forth.
|
||||
match futures::future::try_join(a, b).await {
|
||||
Err(e) => println!("an error occurred; error = {:?}", e),
|
||||
_ => println!("done!"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn ping(socket: &mut UdpFramed<BytesCodec>, b_addr: SocketAddr) -> Result<(), io::Error> {
|
||||
socket.send((Bytes::from(&b"PING"[..]), b_addr)).await?;
|
||||
|
||||
for _ in 0..4usize {
|
||||
let (bytes, addr) = socket.next().map(|e| e.unwrap()).await?;
|
||||
|
||||
println!("[a] recv: {}", String::from_utf8_lossy(&bytes));
|
||||
|
||||
socket.send((Bytes::from(&b"PING"[..]), addr)).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn pong(socket: &mut UdpFramed<BytesCodec>) -> Result<(), io::Error> {
|
||||
let timeout = Duration::from_millis(200);
|
||||
|
||||
while let Ok(Some(Ok((bytes, addr)))) = time::timeout(timeout, socket.next()).await {
|
||||
println!("[b] recv: {}", String::from_utf8_lossy(&bytes));
|
||||
|
||||
socket.send((Bytes::from(&b"PONG"[..]), addr)).await?;
|
||||
}
|
||||
|
||||
// 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))
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
use std::future::Future as StdFuture;
|
||||
|
||||
async fn map_ok<T: StdFuture>(future: T) -> Result<(), ()> {
|
||||
let _ = await!(future);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Like `tokio::run`, but takes an `async` block
|
||||
pub fn run_async<F>(future: F)
|
||||
where
|
||||
F: StdFuture<Output = ()> + Send + 'static,
|
||||
{
|
||||
use tokio_async_await::compat::backward;
|
||||
let future = backward::Compat::new(map_ok(future));
|
||||
|
||||
::run(future);
|
||||
}
|
||||
|
||||
/// Like `tokio::spawn`, but takes an `async` block
|
||||
pub fn spawn_async<F>(future: F)
|
||||
where
|
||||
F: StdFuture<Output = ()> + Send + 'static,
|
||||
{
|
||||
use tokio_async_await::compat::backward;
|
||||
let future = backward::Compat::new(map_ok(future));
|
||||
|
||||
::spawn(future);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! A configurable source of time.
|
||||
//!
|
||||
//! This module provides the [`now`][n] function, which returns an `Instant`
|
||||
//! representing "now". The source of time used by this function is configurable
|
||||
//! (via the [`tokio-timer`] crate) and allows mocking out the source of time in
|
||||
//! tests or performing caching operations to reduce the number of syscalls.
|
||||
//!
|
||||
//! Note that, because the source of time is configurable, it is possible to
|
||||
//! observe non-monotonic behavior when calling [`now`][n] from different
|
||||
//! executors.
|
||||
//!
|
||||
//! [n]: fn.now.html
|
||||
//! [`tokio-timer`]: https://docs.rs/tokio-timer/0.2/tokio_timer/clock/index.html
|
||||
|
||||
pub use tokio_timer::clock::now;
|
||||
@@ -1,19 +0,0 @@
|
||||
//! 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`]: ../io/trait.AsyncRead.html
|
||||
//! [`AsyncWrite`]: ../io/trait.AsyncWrite.html
|
||||
//! [`Sink`]: https://docs.rs/futures/0.1/futures/sink/trait.Sink.html
|
||||
//! [`Stream`]: https://docs.rs/futures/0.1/futures/stream/trait.Stream.html
|
||||
//! [transports]: https://tokio.rs/docs/going-deeper/frames/
|
||||
|
||||
pub use tokio_codec::{
|
||||
BytesCodec, Decoder, Encoder, Framed, FramedParts, FramedRead, FramedWrite, LinesCodec,
|
||||
};
|
||||
|
||||
pub mod length_delimited;
|
||||
|
||||
pub use self::length_delimited::LengthDelimitedCodec;
|
||||
@@ -1,170 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
//! 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
|
||||
|
||||
pub use tokio_current_thread::{
|
||||
BlockError,
|
||||
CurrentThread,
|
||||
Entered,
|
||||
Handle,
|
||||
RunError,
|
||||
RunTimeoutError,
|
||||
TaskExecutor,
|
||||
Turn,
|
||||
TurnError,
|
||||
block_on_all,
|
||||
spawn,
|
||||
};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use futures::future::{self};
|
||||
|
||||
#[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 ()>,
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
/// Cancels *all* executing futures.
|
||||
pub fn cancel_all_spawned(&self) {
|
||||
self.cancel.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.2", note = "use block_on_all instead")]
|
||||
#[doc(hidden)]
|
||||
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
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.2", note = "use TaskExecutor::current instead")]
|
||||
#[doc(hidden)]
|
||||
pub fn task_executor() -> TaskExecutor {
|
||||
TaskExecutor::current()
|
||||
}
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
//! 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 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
|
||||
//! multi-threaded. Tokio provides implementation for both of these in the
|
||||
//! [`runtime`] module.
|
||||
//!
|
||||
//! # `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, a single threaded 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
|
||||
//! [`runtime`]: ../runtime/index.html
|
||||
//! [`tokio-executor`]: https://docs.rs/tokio-executor/0.1
|
||||
//! [`Executor`]: trait.Executor.html
|
||||
//! [`spawn`]: fn.spawn.html
|
||||
|
||||
#[deprecated(
|
||||
since = "0.1.8",
|
||||
note = "use tokio-current-thread crate or functions in tokio::runtime::current_thread instead",
|
||||
)]
|
||||
#[doc(hidden)]
|
||||
pub mod current_thread;
|
||||
|
||||
#[deprecated(since = "0.1.8", note = "use tokio-threadpool crate instead")]
|
||||
#[doc(hidden)]
|
||||
/// Re-exports of [`tokio-threadpool`], deprecated in favor of the crate.
|
||||
///
|
||||
/// [`tokio-threadpool`]: https://docs.rs/tokio-threadpool/0.1
|
||||
pub mod thread_pool {
|
||||
pub use tokio_threadpool::{
|
||||
Builder,
|
||||
Sender,
|
||||
Shutdown,
|
||||
ThreadPool,
|
||||
};
|
||||
}
|
||||
|
||||
pub use tokio_executor::{Executor, DefaultExecutor, SpawnError};
|
||||
|
||||
use futures::{Future, IntoFuture};
|
||||
use futures::future::{self, FutureResult};
|
||||
|
||||
/// 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(())
|
||||
}
|
||||
|
||||
impl IntoFuture for Spawn {
|
||||
type Future = FutureResult<(), ()>;
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn into_future(self) -> Self::Future {
|
||||
future::ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! Asynchronous filesystem manipulation operations.
|
||||
//!
|
||||
//! This module contains basic methods and types for manipulating the contents
|
||||
//! of the local filesystem from within the context of the Tokio runtime.
|
||||
//!
|
||||
//! Unlike *most* other Tokio APIs, the filesystem APIs **must** be used from
|
||||
//! the context of the Tokio runtime as they require Tokio specific features to
|
||||
//! function.
|
||||
|
||||
pub use tokio_fs::OpenOptions;
|
||||
pub use tokio_fs::{
|
||||
create_dir, create_dir_all, file, hard_link, metadata, os, read_dir, read_link,
|
||||
};
|
||||
pub use tokio_fs::{read, write, ReadFile, WriteFile};
|
||||
pub use tokio_fs::{remove_dir, remove_file, rename, set_permissions, symlink_metadata, File};
|
||||
@@ -1,62 +0,0 @@
|
||||
//! 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
|
||||
//!
|
||||
//! [`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.
|
||||
//!
|
||||
//! # Standard input and output
|
||||
//!
|
||||
//! Tokio provides asynchronous APIs to standard [input], [output], and [error].
|
||||
//! These APIs are very similar to the ones provided by `std`, but they also
|
||||
//! implement [`AsyncRead`] and [`AsyncWrite`].
|
||||
//!
|
||||
//! Unlike *most* other Tokio APIs, the standard input / output APIs
|
||||
//! **must** be used from the context of the Tokio runtime as they require
|
||||
//! Tokio specific features to function.
|
||||
//!
|
||||
//! [input]: fn.stdin.html
|
||||
//! [output]: fn.stdout.html
|
||||
//! [error]: fn.stderr.html
|
||||
//!
|
||||
//! # Utility functions
|
||||
//!
|
||||
//! Utilities functions are provided for working with [`AsyncRead`] /
|
||||
//! [`AsyncWrite`] types. For example, [`copy`] asynchronously copies all
|
||||
//! data from a source to a destination.
|
||||
//!
|
||||
//! # `std` re-exports
|
||||
//!
|
||||
//! 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};
|
||||
|
||||
// standard input, output, and error
|
||||
#[cfg(feature = "fs")]
|
||||
pub use tokio_fs::{stderr, stdin, stdout, Stderr, Stdin, Stdout};
|
||||
|
||||
// Utils
|
||||
pub use tokio_io::io::{
|
||||
copy, flush, lines, read, read_exact, read_to_end, read_until, shutdown, write_all, Copy,
|
||||
Flush, Lines, ReadExact, ReadHalf, ReadToEnd, ReadUntil, Shutdown, WriteAll, WriteHalf,
|
||||
};
|
||||
|
||||
// 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, Read, Result, Write};
|
||||
-154
@@ -1,154 +0,0 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio/0.1.15")]
|
||||
#![deny(missing_docs, warnings, missing_debug_implementations)]
|
||||
#![cfg_attr(
|
||||
feature = "async-await-preview",
|
||||
feature(async_await, await_macro, futures_api,)
|
||||
)]
|
||||
|
||||
//! A runtime for writing reliable, asynchronous, and slim applications.
|
||||
//!
|
||||
//! 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 multi threaded, work-stealing based task [scheduler][runtime].
|
||||
//! * A [reactor] backed by the operating system's event queue (epoll, kqueue,
|
||||
//! IOCP, etc...).
|
||||
//! * Asynchronous [TCP and UDP][net] sockets.
|
||||
//! * Asynchronous [filesystem][fs] operations.
|
||||
//! * [Timer][timer] API for scheduling work in the future.
|
||||
//!
|
||||
//! Tokio is built using [futures] as the abstraction for managing the
|
||||
//! complexity of asynchronous programming.
|
||||
//!
|
||||
//! Guide level documentation is found on the [website].
|
||||
//!
|
||||
//! [website]: https://tokio.rs/docs/getting-started/hello-world/
|
||||
//! [futures]: http://docs.rs/futures/0.1
|
||||
//!
|
||||
//! # 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);
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
macro_rules! if_runtime {
|
||||
($($i:item)*) => ($(
|
||||
#[cfg(any(feature = "rt-full"))]
|
||||
$i
|
||||
)*)
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
|
||||
#[cfg(feature = "io")]
|
||||
extern crate bytes;
|
||||
#[cfg(feature = "reactor")]
|
||||
extern crate mio;
|
||||
#[cfg(feature = "rt-full")]
|
||||
extern crate num_cpus;
|
||||
#[cfg(feature = "codec")]
|
||||
extern crate tokio_codec;
|
||||
#[cfg(feature = "rt-full")]
|
||||
extern crate tokio_current_thread;
|
||||
#[cfg(feature = "fs")]
|
||||
extern crate tokio_fs;
|
||||
#[cfg(feature = "io")]
|
||||
extern crate tokio_io;
|
||||
#[cfg(feature = "reactor")]
|
||||
extern crate tokio_reactor;
|
||||
#[cfg(feature = "sync")]
|
||||
extern crate tokio_sync;
|
||||
#[cfg(feature = "tcp")]
|
||||
extern crate tokio_tcp;
|
||||
#[cfg(feature = "rt-full")]
|
||||
extern crate tokio_threadpool;
|
||||
#[cfg(feature = "timer")]
|
||||
extern crate tokio_timer;
|
||||
#[cfg(feature = "udp")]
|
||||
extern crate tokio_udp;
|
||||
|
||||
#[cfg(feature = "async-await-preview")]
|
||||
extern crate tokio_async_await;
|
||||
|
||||
#[cfg(all(unix, feature = "uds"))]
|
||||
extern crate tokio_uds;
|
||||
|
||||
#[cfg(feature = "timer")]
|
||||
pub mod clock;
|
||||
#[cfg(feature = "codec")]
|
||||
pub mod codec;
|
||||
#[cfg(feature = "fs")]
|
||||
pub mod fs;
|
||||
#[cfg(feature = "io")]
|
||||
pub mod io;
|
||||
#[cfg(any(feature = "tcp", feature = "udp", feature = "uds"))]
|
||||
pub mod net;
|
||||
pub mod prelude;
|
||||
#[cfg(feature = "reactor")]
|
||||
pub mod reactor;
|
||||
#[cfg(feature = "sync")]
|
||||
pub mod sync;
|
||||
#[cfg(feature = "timer")]
|
||||
pub mod timer;
|
||||
pub mod util;
|
||||
|
||||
if_runtime! {
|
||||
extern crate tokio_executor;
|
||||
pub mod executor;
|
||||
pub mod runtime;
|
||||
|
||||
pub use executor::spawn;
|
||||
pub use runtime::run;
|
||||
}
|
||||
|
||||
// ===== Experimental async/await support =====
|
||||
|
||||
#[cfg(feature = "async-await-preview")]
|
||||
mod async_await;
|
||||
|
||||
#[cfg(feature = "async-await-preview")]
|
||||
pub use async_await::{run_async, spawn_async};
|
||||
|
||||
#[cfg(feature = "async-await-preview")]
|
||||
pub use tokio_async_await::await;
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
//! TCP/UDP/Unix bindings for `tokio`.
|
||||
//!
|
||||
//! This module contains the TCP/UDP/Unix networking types, similar to the standard
|
||||
//! library, which can be used to implement networking protocols.
|
||||
//!
|
||||
//! # Organization
|
||||
//!
|
||||
//! * [`TcpListener`] and [`TcpStream`] provide functionality for communication over TCP
|
||||
//! * [`UdpSocket`] and [`UdpFramed`] provide functionality for communication over UDP
|
||||
//! * [`UnixListener`] and [`UnixStream`] provide functionality for communication over a
|
||||
//! Unix Domain Stream Socket **(available on Unix only)**
|
||||
//! * [`UnixDatagram`] and [`UnixDatagramFramed`] provide functionality for communication
|
||||
//! over Unix Domain Datagram Socket **(available on Unix only)**
|
||||
|
||||
//!
|
||||
//! [`TcpListener`]: struct.TcpListener.html
|
||||
//! [`TcpStream`]: struct.TcpStream.html
|
||||
//! [`UdpSocket`]: struct.UdpSocket.html
|
||||
//! [`UdpFramed`]: struct.UdpFramed.html
|
||||
//! [`UnixListener`]: struct.UnixListener.html
|
||||
//! [`UnixStream`]: struct.UnixStream.html
|
||||
//! [`UnixDatagram`]: struct.UnixDatagram.html
|
||||
//! [`UnixDatagramFramed`]: struct.UnixDatagramFramed.html
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
pub mod tcp {
|
||||
//! TCP bindings for `tokio`.
|
||||
//!
|
||||
//! 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
|
||||
pub use tokio_tcp::{ConnectFuture, Incoming, TcpListener, TcpStream};
|
||||
}
|
||||
#[cfg(feature = "tcp")]
|
||||
pub use self::tcp::{TcpListener, TcpStream};
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
#[deprecated(note = "use `tokio::net::tcp::ConnectFuture` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type ConnectFuture = self::tcp::ConnectFuture;
|
||||
#[cfg(feature = "tcp")]
|
||||
#[deprecated(note = "use `tokio::net::tcp::Incoming` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type Incoming = self::tcp::Incoming;
|
||||
|
||||
#[cfg(feature = "udp")]
|
||||
pub mod udp {
|
||||
//! UDP bindings for `tokio`.
|
||||
//!
|
||||
//! 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 convenience 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_udp::{RecvDgram, SendDgram, UdpFramed, UdpSocket};
|
||||
}
|
||||
#[cfg(feature = "udp")]
|
||||
pub use self::udp::{UdpFramed, UdpSocket};
|
||||
|
||||
#[cfg(feature = "udp")]
|
||||
#[deprecated(note = "use `tokio::net::udp::RecvDgram` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type RecvDgram<T> = self::udp::RecvDgram<T>;
|
||||
#[cfg(feature = "udp")]
|
||||
#[deprecated(note = "use `tokio::net::udp::SendDgram` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type SendDgram<T> = self::udp::SendDgram<T>;
|
||||
|
||||
#[cfg(all(unix, feature = "uds"))]
|
||||
pub mod unix {
|
||||
//! Unix domain socket bindings for `tokio` (only available on unix systems).
|
||||
|
||||
pub use tokio_uds::{
|
||||
ConnectFuture, Incoming, RecvDgram, SendDgram, UCred, UnixDatagram, UnixDatagramFramed,
|
||||
UnixListener, UnixStream,
|
||||
};
|
||||
}
|
||||
#[cfg(all(unix, feature = "uds"))]
|
||||
pub use self::unix::{UnixDatagram, UnixDatagramFramed, UnixListener, UnixStream};
|
||||
@@ -1,28 +0,0 @@
|
||||
//! 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.
|
||||
|
||||
#[cfg(feature = "io")]
|
||||
pub use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
pub use util::{FutureExt, StreamExt};
|
||||
|
||||
pub use std::io::{Read, Write};
|
||||
|
||||
pub use futures::{future, stream, task, Async, AsyncSink, Future, IntoFuture, Poll, Sink, Stream};
|
||||
|
||||
#[cfg(feature = "async-await-preview")]
|
||||
#[doc(inline)]
|
||||
pub use tokio_async_await::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
sink::SinkExt,
|
||||
stream::StreamExt as StreamAsyncExt,
|
||||
};
|
||||
@@ -1,144 +0,0 @@
|
||||
//! 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 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::io::Read`] and / or [`std::io::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::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
|
||||
//! [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
|
||||
|
||||
pub use tokio_reactor::{
|
||||
Background, Handle, PollEvented as PollEvented2, Reactor, Registration, Turn,
|
||||
};
|
||||
|
||||
mod poll_evented;
|
||||
#[allow(deprecated)]
|
||||
pub use self::poll_evented::PollEvented;
|
||||
@@ -1,547 +0,0 @@
|
||||
//! 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::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::sync::Mutex;
|
||||
|
||||
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(unix)]
|
||||
mod platform {
|
||||
use mio::unix::UnixReady;
|
||||
use mio::Ready;
|
||||
|
||||
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(windows)]
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
use executor::current_thread::CurrentThread;
|
||||
use runtime::current_thread::Runtime;
|
||||
|
||||
use tokio_reactor::Reactor;
|
||||
use tokio_timer::clock::Clock;
|
||||
use tokio_timer::timer::Timer;
|
||||
|
||||
use std::io;
|
||||
|
||||
/// Builds a Single-threaded runtime with custom configuration values.
|
||||
///
|
||||
/// Methods can be chained 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_timer;
|
||||
///
|
||||
/// use tokio::runtime::current_thread::Builder;
|
||||
/// use tokio_timer::clock::Clock;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// // build Runtime
|
||||
/// let runtime = Builder::new()
|
||||
/// .clock(Clock::new())
|
||||
/// .build();
|
||||
/// // ... call runtime.run(...)
|
||||
/// # let _ = runtime;
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Builder {
|
||||
/// The clock to use
|
||||
clock: Clock,
|
||||
}
|
||||
|
||||
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 {
|
||||
Builder {
|
||||
clock: Clock::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the `Clock` instance that will be used by the runtime.
|
||||
pub fn clock(&mut self, clock: Clock) -> &mut Self {
|
||||
self.clock = clock;
|
||||
self
|
||||
}
|
||||
|
||||
/// Create the configured `Runtime`.
|
||||
pub fn build(&mut self) -> io::Result<Runtime> {
|
||||
// We need a reactor to receive events about IO objects from kernel
|
||||
let reactor = Reactor::new()?;
|
||||
let reactor_handle = reactor.handle();
|
||||
|
||||
// Place a timer wheel on top of the reactor. If there are no timeouts to fire, it'll let the
|
||||
// reactor pick up some new external events.
|
||||
let timer = Timer::new_with_now(reactor, self.clock.clone());
|
||||
let timer_handle = timer.handle();
|
||||
|
||||
// And now put a single-threaded executor on top of the timer. When there are no futures ready
|
||||
// to do something, it'll let the timer or the reactor to generate some new stimuli for the
|
||||
// futures to continue in their life.
|
||||
let executor = CurrentThread::new_with_park(timer);
|
||||
|
||||
let runtime = Runtime::new2(
|
||||
reactor_handle,
|
||||
timer_handle,
|
||||
self.clock.clone(),
|
||||
executor);
|
||||
|
||||
Ok(runtime)
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
//! A runtime implementation that runs everything on the current thread.
|
||||
//!
|
||||
//! [`current_thread::Runtime`][rt] is similar to the primary
|
||||
//! [`Runtime`][concurrent-rt] except that it runs all components on the current
|
||||
//! thread instead of using a thread pool. This means that it is able to spawn
|
||||
//! futures that do not implement `Send`.
|
||||
//!
|
||||
//! Same as the default [`Runtime`][concurrent-rt], the
|
||||
//! [`current_thread::Runtime`][rt] includes:
|
||||
//!
|
||||
//! * 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.
|
||||
//!
|
||||
//! Note that [`current_thread::Runtime`][rt] does not implement `Send` itself
|
||||
//! and cannot be safely moved to other threads.
|
||||
//!
|
||||
//! # Spawning from other threads
|
||||
//!
|
||||
//! While [`current_thread::Runtime`][rt] does not implement `Send` and cannot
|
||||
//! safely be moved to other threads, it provides a `Handle` that can be sent
|
||||
//! to other threads and allows to spawn new tasks from there.
|
||||
//!
|
||||
//! For example:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! # extern crate futures;
|
||||
//! use tokio::runtime::current_thread::Runtime;
|
||||
//! use tokio::prelude::*;
|
||||
//! use std::thread;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let mut runtime = Runtime::new().unwrap();
|
||||
//! let handle = runtime.handle();
|
||||
//!
|
||||
//! thread::spawn(move || {
|
||||
//! handle.spawn(future::ok(()));
|
||||
//! }).join().unwrap();
|
||||
//!
|
||||
//! # /*
|
||||
//! runtime.run().unwrap();
|
||||
//! # */
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Creating a new `Runtime` and running a future `f` until its completion and
|
||||
//! returning its result.
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio::runtime::current_thread::Runtime;
|
||||
//! use tokio::prelude::*;
|
||||
//!
|
||||
//! let mut runtime = Runtime::new().unwrap();
|
||||
//!
|
||||
//! // Use the runtime...
|
||||
//! // runtime.block_on(f); // where f is a future
|
||||
//! ```
|
||||
//!
|
||||
//! [rt]: struct.Runtime.html
|
||||
//! [concurrent-rt]: ../struct.Runtime.html
|
||||
//! [chan]: https://docs.rs/futures/0.1/futures/sync/mpsc/fn.channel.html
|
||||
//! [reactor]: ../../reactor/struct.Reactor.html
|
||||
//! [executor]: https://tokio.rs/docs/getting-started/runtime-model/#executors
|
||||
//! [timer]: ../../timer/index.html
|
||||
|
||||
mod builder;
|
||||
mod runtime;
|
||||
|
||||
pub use self::builder::Builder;
|
||||
pub use self::runtime::{Runtime, Handle};
|
||||
pub use tokio_current_thread::spawn;
|
||||
pub use tokio_current_thread::TaskExecutor;
|
||||
|
||||
use futures::Future;
|
||||
|
||||
/// Run the provided future to completion using a runtime running on the current thread.
|
||||
///
|
||||
/// This first creates a new [`Runtime`], and calls [`Runtime::block_on`] with the provided future,
|
||||
/// which blocks the current thread until the provided future completes. It then calls
|
||||
/// [`Runtime::run`] to wait for any other spawned futures to resolve.
|
||||
pub fn block_on_all<F>(future: F) -> Result<F::Item, F::Error>
|
||||
where
|
||||
F: Future,
|
||||
{
|
||||
let mut r = Runtime::new().expect("failed to start runtime on current thread");
|
||||
let v = r.block_on(future)?;
|
||||
r.run().expect("failed to resolve remaining futures");
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// Start a current-thread runtime using the supplied future to bootstrap execution.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if called from the context of an executor.
|
||||
pub fn run<F>(future: F)
|
||||
where
|
||||
F: Future<Item = (), Error = ()> + 'static,
|
||||
{
|
||||
|
||||
let mut r = Runtime::new().expect("failed to start runtime on current thread");
|
||||
r.spawn(future);
|
||||
r.run().expect("failed to resolve remaining futures");
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
use tokio_current_thread::{self as current_thread, CurrentThread};
|
||||
use tokio_current_thread::Handle as ExecutorHandle;
|
||||
use runtime::current_thread::Builder;
|
||||
|
||||
use tokio_reactor::{self, Reactor};
|
||||
use tokio_timer::clock::{self, Clock};
|
||||
use tokio_timer::timer::{self, Timer};
|
||||
use tokio_executor;
|
||||
|
||||
use futures::{future, Future};
|
||||
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
|
||||
/// Single-threaded runtime provides a way to start reactor
|
||||
/// and executor on the current thread.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [mod]: index.html
|
||||
#[derive(Debug)]
|
||||
pub struct Runtime {
|
||||
reactor_handle: tokio_reactor::Handle,
|
||||
timer_handle: timer::Handle,
|
||||
clock: Clock,
|
||||
executor: CurrentThread<Timer<Reactor>>,
|
||||
}
|
||||
|
||||
/// Handle to spawn a future on the corresponding `CurrentThread` runtime instance
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Handle(ExecutorHandle);
|
||||
|
||||
impl Handle {
|
||||
/// Spawn a future onto the `CurrentThread` runtime instance corresponding to this handle
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the spawn fails. Failure occurs if the `CurrentThread`
|
||||
/// instance of the `Handle` does not exist anymore.
|
||||
pub fn spawn<F>(&self, future: F) -> Result<(), tokio_executor::SpawnError>
|
||||
where F: Future<Item = (), Error = ()> + Send + 'static {
|
||||
self.0.spawn(future)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn status(&self) -> Result<(), tokio_executor::SpawnError> {
|
||||
self.0.status()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> future::Executor<T> for Handle
|
||||
where T: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
fn execute(&self, future: T) -> Result<(), future::ExecuteError<T>> {
|
||||
if let Err(e) = self.status() {
|
||||
let kind = if e.is_at_capacity() {
|
||||
future::ExecuteErrorKind::NoCapacity
|
||||
} else {
|
||||
future::ExecuteErrorKind::Shutdown
|
||||
};
|
||||
|
||||
return Err(future::ExecuteError::new(kind, future));
|
||||
}
|
||||
|
||||
let _ = self.spawn(future);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned by the `run` function.
|
||||
#[derive(Debug)]
|
||||
pub struct RunError {
|
||||
inner: current_thread::RunError,
|
||||
}
|
||||
|
||||
impl fmt::Display for RunError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for RunError {
|
||||
fn description(&self) -> &str {
|
||||
self.inner.description()
|
||||
}
|
||||
|
||||
// FIXME(taiki-e): When the minimum support version of tokio reaches Rust 1.30,
|
||||
// replace this with Error::source.
|
||||
#[allow(deprecated)]
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
self.inner.cause()
|
||||
}
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
/// Returns a new runtime initialized with default configuration values.
|
||||
pub fn new() -> io::Result<Runtime> {
|
||||
Builder::new().build()
|
||||
}
|
||||
|
||||
pub(super) fn new2(
|
||||
reactor_handle: tokio_reactor::Handle,
|
||||
timer_handle: timer::Handle,
|
||||
clock: Clock,
|
||||
executor: CurrentThread<Timer<Reactor>>) -> Runtime
|
||||
{
|
||||
Runtime {
|
||||
reactor_handle,
|
||||
timer_handle,
|
||||
clock,
|
||||
executor,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a new handle to spawn futures on the single-threaded Tokio runtime
|
||||
///
|
||||
/// Different to the runtime itself, the handle can be sent to different
|
||||
/// threads.
|
||||
pub fn handle(&self) -> Handle {
|
||||
Handle(self.executor.handle().clone())
|
||||
}
|
||||
|
||||
/// Spawn a future onto the single-threaded Tokio runtime.
|
||||
///
|
||||
/// 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::current_thread::Runtime;
|
||||
///
|
||||
/// # fn dox() {
|
||||
/// // Create the runtime
|
||||
/// let mut rt = Runtime::new().unwrap();
|
||||
///
|
||||
/// // Spawn a future onto the runtime
|
||||
/// rt.spawn(future::lazy(|| {
|
||||
/// println!("running on the runtime");
|
||||
/// 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 = ()> + 'static,
|
||||
{
|
||||
self.executor.spawn(future);
|
||||
self
|
||||
}
|
||||
|
||||
/// Runs the provided future, blocking the current thread until the future
|
||||
/// completes.
|
||||
///
|
||||
/// 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. Once the function returns, any uncompleted futures
|
||||
/// remain pending in the `Runtime` instance. These futures will not run
|
||||
/// until `block_on` or `run` is called again.
|
||||
///
|
||||
/// The caller is responsible for ensuring that other spawned futures
|
||||
/// complete execution by calling `block_on` or `run`.
|
||||
pub fn block_on<F>(&mut self, f: F) -> Result<F::Item, F::Error>
|
||||
where F: Future
|
||||
{
|
||||
self.enter(|executor| {
|
||||
// Run the provided future
|
||||
let ret = executor.block_on(f);
|
||||
ret.map_err(|e| e.into_inner().expect("unexpected execution error"))
|
||||
})
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until **all**
|
||||
/// spawned futures have completed.
|
||||
pub fn run(&mut self) -> Result<(), RunError> {
|
||||
self.enter(|executor| executor.run())
|
||||
.map_err(|e| RunError {
|
||||
inner: e,
|
||||
})
|
||||
}
|
||||
|
||||
fn enter<F, R>(&mut self, f: F) -> R
|
||||
where F: FnOnce(&mut current_thread::Entered<Timer<Reactor>>) -> R
|
||||
{
|
||||
let Runtime {
|
||||
ref reactor_handle,
|
||||
ref timer_handle,
|
||||
ref clock,
|
||||
ref mut executor,
|
||||
..
|
||||
} = *self;
|
||||
|
||||
// Binds an executor to this thread
|
||||
let mut enter = tokio_executor::enter().expect("Multiple executors at once");
|
||||
|
||||
// This will set the default handle and timer to use inside the closure
|
||||
// and run the future.
|
||||
tokio_reactor::with_default(&reactor_handle, &mut enter, |enter| {
|
||||
clock::with_default(clock, enter, |enter| {
|
||||
timer::with_default(&timer_handle, enter, |enter| {
|
||||
// The TaskExecutor is a fake executor that looks into the
|
||||
// current single-threaded executor when used. This is a trick,
|
||||
// because we need two mutable references to the executor (one
|
||||
// to run the provided future, another to install as the default
|
||||
// one). We use the fake one here as the default one.
|
||||
let mut default_executor = current_thread::TaskExecutor::current();
|
||||
tokio_executor::with_default(&mut default_executor, enter, |enter| {
|
||||
let mut executor = executor.enter(enter);
|
||||
f(&mut executor)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
//! 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`]: https://docs.rs/tokio-threadpool/0.1/tokio_threadpool/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
|
||||
|
||||
pub mod current_thread;
|
||||
mod threadpool;
|
||||
|
||||
pub use self::threadpool::{
|
||||
Builder,
|
||||
Runtime,
|
||||
Shutdown,
|
||||
TaskExecutor,
|
||||
run,
|
||||
};
|
||||
|
||||
@@ -1,368 +0,0 @@
|
||||
use super::{Inner, Runtime};
|
||||
|
||||
use reactor::Reactor;
|
||||
|
||||
use std::io;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
use num_cpus;
|
||||
use tokio_reactor;
|
||||
use tokio_threadpool::Builder as ThreadPoolBuilder;
|
||||
use tokio_timer::clock::{self, Clock};
|
||||
use tokio_timer::timer::{self, Timer};
|
||||
|
||||
/// Builds Tokio Runtime with custom configuration values.
|
||||
///
|
||||
/// Methods can be chained 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_timer;
|
||||
///
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// use tokio::runtime::Builder;
|
||||
/// use tokio_timer::clock::Clock;
|
||||
///
|
||||
/// fn main() {
|
||||
/// // build Runtime
|
||||
/// let mut runtime = Builder::new()
|
||||
/// .blocking_threads(4)
|
||||
/// .clock(Clock::system())
|
||||
/// .core_threads(4)
|
||||
/// .keep_alive(Some(Duration::from_secs(60)))
|
||||
/// .name_prefix("my-custom-name-")
|
||||
/// .stack_size(3 * 1024 * 1024)
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // use runtime ...
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Builder {
|
||||
/// Thread pool specific builder
|
||||
threadpool_builder: ThreadPoolBuilder,
|
||||
|
||||
/// The number of worker threads
|
||||
core_threads: usize,
|
||||
|
||||
/// The clock to use
|
||||
clock: Clock,
|
||||
}
|
||||
|
||||
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 core_threads = num_cpus::get().max(1);
|
||||
|
||||
let mut threadpool_builder = ThreadPoolBuilder::new();
|
||||
threadpool_builder.name_prefix("tokio-runtime-worker-");
|
||||
threadpool_builder.pool_size(core_threads);
|
||||
|
||||
Builder {
|
||||
threadpool_builder,
|
||||
core_threads,
|
||||
clock: Clock::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the `Clock` instance that will be used by the runtime.
|
||||
pub fn clock(&mut self, clock: Clock) -> &mut Self {
|
||||
self.clock = clock;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set builder to set up the thread pool instance.
|
||||
#[deprecated(
|
||||
since="0.1.9",
|
||||
note="use the `core_threads`, `blocking_threads`, `name_prefix`, \
|
||||
`keep_alive`, and `stack_size` functions on `runtime::Builder`, \
|
||||
instead")]
|
||||
#[doc(hidden)]
|
||||
pub fn threadpool_builder(&mut self, val: ThreadPoolBuilder) -> &mut Self {
|
||||
self.threadpool_builder = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the maximum number of worker threads for the `Runtime`'s thread pool.
|
||||
///
|
||||
/// This must be a number between 1 and 32,768 though it is advised to keep
|
||||
/// this value on the smaller side.
|
||||
///
|
||||
/// The default value is the number of cores available to the system.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let mut rt = runtime::Builder::new()
|
||||
/// .core_threads(4)
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn core_threads(&mut self, val: usize) -> &mut Self {
|
||||
self.core_threads = val;
|
||||
self.threadpool_builder.pool_size(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the maximum number of concurrent blocking sections in the `Runtime`'s
|
||||
/// thread pool.
|
||||
///
|
||||
/// When the maximum concurrent `blocking` calls is reached, any further
|
||||
/// calls to `blocking` will return `NotReady` and the task is notified once
|
||||
/// previously in-flight calls to `blocking` return.
|
||||
///
|
||||
/// This must be a number between 1 and 32,768 though it is advised to keep
|
||||
/// this value on the smaller side.
|
||||
///
|
||||
/// The default value is 100.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let mut rt = runtime::Builder::new()
|
||||
/// .blocking_threads(200)
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn blocking_threads(&mut self, val: usize) -> &mut Self {
|
||||
self.threadpool_builder.max_blocking(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the worker thread keep alive duration for threads in the `Runtime`'s
|
||||
/// thread pool.
|
||||
///
|
||||
/// If set, a worker thread will wait for up to the specified duration for
|
||||
/// work, at which point the thread will shutdown. When work becomes
|
||||
/// available, a new thread will eventually be spawned to replace the one
|
||||
/// that shut down.
|
||||
///
|
||||
/// When the value is `None`, the thread will wait for work forever.
|
||||
///
|
||||
/// The default value is `None`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let mut rt = runtime::Builder::new()
|
||||
/// .keep_alive(Some(Duration::from_secs(30)))
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn keep_alive(&mut self, val: Option<Duration>) -> &mut Self {
|
||||
self.threadpool_builder.keep_alive(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set name prefix of threads spawned by the `Runtime`'s thread pool.
|
||||
///
|
||||
/// Thread name prefix is used for generating thread names. For example, if
|
||||
/// prefix is `my-pool-`, then threads in the pool will get names like
|
||||
/// `my-pool-1` etc.
|
||||
///
|
||||
/// The default prefix is "tokio-runtime-worker-".
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let mut rt = runtime::Builder::new()
|
||||
/// .name_prefix("my-pool-")
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn name_prefix<S: Into<String>>(&mut self, val: S) -> &mut Self {
|
||||
self.threadpool_builder.name_prefix(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the stack size (in bytes) for worker threads.
|
||||
///
|
||||
/// The actual stack size may be greater than this value if the platform
|
||||
/// specifies minimal stack size.
|
||||
///
|
||||
/// The default stack size for spawned threads is 2 MiB, though this
|
||||
/// particular stack size is subject to change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let mut rt = runtime::Builder::new()
|
||||
/// .stack_size(32 * 1024)
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn stack_size(&mut self, val: usize) -> &mut Self {
|
||||
self.threadpool_builder.stack_size(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute function `f` after each thread is started but before it starts
|
||||
/// doing work.
|
||||
///
|
||||
/// This is intended for bookkeeping and monitoring use cases.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let thread_pool = runtime::Builder::new()
|
||||
/// .after_start(|| {
|
||||
/// println!("thread started");
|
||||
/// })
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn after_start<F>(&mut self, f: F) -> &mut Self
|
||||
where F: Fn() + Send + Sync + 'static
|
||||
{
|
||||
self.threadpool_builder.after_start(f);
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute function `f` before each thread stops.
|
||||
///
|
||||
/// This is intended for bookkeeping and monitoring use cases.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let thread_pool = runtime::Builder::new()
|
||||
/// .before_stop(|| {
|
||||
/// println!("thread stopping");
|
||||
/// })
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn before_stop<F>(&mut self, f: F) -> &mut Self
|
||||
where F: Fn() + Send + Sync + 'static
|
||||
{
|
||||
self.threadpool_builder.before_stop(f);
|
||||
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().unwrap();
|
||||
/// // ... call runtime.run(...)
|
||||
/// # let _ = runtime;
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn build(&mut self) -> io::Result<Runtime> {
|
||||
// TODO(stjepang): Once we remove the `threadpool_builder` method, remove this line too.
|
||||
self.threadpool_builder.pool_size(self.core_threads);
|
||||
|
||||
let mut reactor_handles = Vec::new();
|
||||
let mut timer_handles = Vec::new();
|
||||
let mut timers = Vec::new();
|
||||
|
||||
for _ in 0..self.core_threads {
|
||||
// Create a new reactor.
|
||||
let reactor = Reactor::new()?;
|
||||
reactor_handles.push(reactor.handle());
|
||||
|
||||
// Create a new timer.
|
||||
let timer = Timer::new_with_now(reactor, self.clock.clone());
|
||||
timer_handles.push(timer.handle());
|
||||
timers.push(Mutex::new(Some(timer)));
|
||||
}
|
||||
|
||||
// Get a handle to the clock for the runtime.
|
||||
let clock = self.clock.clone();
|
||||
|
||||
let pool = self.threadpool_builder
|
||||
.around_worker(move |w, enter| {
|
||||
let index = w.id().to_usize();
|
||||
|
||||
tokio_reactor::with_default(&reactor_handles[index], enter, |enter| {
|
||||
clock::with_default(&clock, enter, |enter| {
|
||||
timer::with_default(&timer_handles[index], enter, |_| {
|
||||
w.run();
|
||||
});
|
||||
})
|
||||
});
|
||||
})
|
||||
.custom_park(move |worker_id| {
|
||||
let index = worker_id.to_usize();
|
||||
|
||||
timers[index]
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.unwrap()
|
||||
})
|
||||
.build();
|
||||
|
||||
// To support deprecated `reactor()` function
|
||||
let reactor = Reactor::new()?;
|
||||
let reactor_handle = reactor.handle();
|
||||
|
||||
Ok(Runtime {
|
||||
inner: Some(Inner {
|
||||
reactor_handle,
|
||||
reactor: Mutex::new(Some(reactor)),
|
||||
pool,
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,395 +0,0 @@
|
||||
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::{Handle, Reactor};
|
||||
|
||||
use std::io;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use tokio_executor::enter;
|
||||
use tokio_threadpool as threadpool;
|
||||
|
||||
use futures;
|
||||
use futures::future::Future;
|
||||
|
||||
/// 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 {
|
||||
/// A handle to the reactor in the background thread.
|
||||
reactor_handle: Handle,
|
||||
|
||||
// TODO: This should go away in 0.2
|
||||
reactor: Mutex<Option<Reactor>>,
|
||||
|
||||
/// 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,
|
||||
{
|
||||
// Check enter before creating a new Runtime...
|
||||
let mut entered = enter().expect("nested tokio::run");
|
||||
let mut runtime = Runtime::new().expect("failed to start new Runtime");
|
||||
runtime.spawn(future);
|
||||
entered
|
||||
.block_on(runtime.shutdown_on_idle())
|
||||
.expect("shutdown cannot error")
|
||||
}
|
||||
|
||||
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 {
|
||||
#[allow(deprecated)]
|
||||
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`
|
||||
/// ```
|
||||
#[deprecated(since = "0.1.11", note = "there is now a reactor per worker thread")]
|
||||
pub fn reactor(&self) -> &Handle {
|
||||
let mut reactor = self.inner().reactor.lock().unwrap();
|
||||
if let Some(reactor) = reactor.take() {
|
||||
if let Ok(background) = reactor.background() {
|
||||
background.forget();
|
||||
}
|
||||
}
|
||||
|
||||
&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
|
||||
}
|
||||
|
||||
/// Run a future to completion on the Tokio runtime.
|
||||
///
|
||||
/// This runs the given future on the runtime, blocking until it is
|
||||
/// complete, and yielding its resolved result. Any tasks or timers which
|
||||
/// the future spawns internally will be executed on the runtime.
|
||||
///
|
||||
/// This method should not be called from an asynchronous context.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the executor is at capacity, if the provided
|
||||
/// future panics, or if called within an asynchronous execution context.
|
||||
pub fn block_on<F, R, E>(&mut self, future: F) -> Result<R, E>
|
||||
where
|
||||
F: Send + 'static + Future<Item = R, Error = E>,
|
||||
R: Send + 'static,
|
||||
E: Send + 'static,
|
||||
{
|
||||
let mut entered = enter().expect("nested block_on");
|
||||
let (tx, rx) = futures::sync::oneshot::channel();
|
||||
self.spawn(future.then(move |r| tx.send(r).map_err(|_| unreachable!())));
|
||||
entered.block_on(rx).unwrap()
|
||||
}
|
||||
|
||||
/// Run a future to completion on the Tokio runtime, then wait for all
|
||||
/// background futures to complete too.
|
||||
///
|
||||
/// This runs the given future on the runtime, blocking until it is
|
||||
/// complete, waiting for background futures to complete, and yielding
|
||||
/// its resolved result. Any tasks or timers which the future spawns
|
||||
/// internally will be executed on the runtime and waited for completion.
|
||||
///
|
||||
/// This method should not be called from an asynchronous context.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the executor is at capacity, if the provided
|
||||
/// future panics, or if called within an asynchronous execution context.
|
||||
pub fn block_on_all<F, R, E>(mut self, future: F) -> Result<R, E>
|
||||
where
|
||||
F: Send + 'static + Future<Item = R, Error = E>,
|
||||
R: Send + 'static,
|
||||
E: Send + 'static,
|
||||
{
|
||||
let mut entered = enter().expect("nested block_on_all");
|
||||
let (tx, rx) = futures::sync::oneshot::channel();
|
||||
self.spawn(future.then(move |r| tx.send(r).map_err(|_| unreachable!())));
|
||||
let block = rx
|
||||
.map_err(|_| unreachable!())
|
||||
.and_then(move |r| {
|
||||
self.shutdown_on_idle()
|
||||
.map(move |()| r)
|
||||
});
|
||||
entered.block_on(block).unwrap()
|
||||
}
|
||||
|
||||
/// 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 = inner.pool.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
use super::Inner;
|
||||
use tokio_threadpool as threadpool;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// A future that resolves when the Tokio `Runtime` is shut down.
|
||||
pub struct Shutdown {
|
||||
pub(super) inner: threadpool::Shutdown,
|
||||
}
|
||||
|
||||
impl Shutdown {
|
||||
pub(super) fn shutdown_now(inner: Inner) -> Self {
|
||||
let inner = inner.pool.shutdown_now();
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
|
||||
use tokio_threadpool::Sender;
|
||||
|
||||
use futures::future::{self, Future};
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
//! Future-aware synchronization
|
||||
//!
|
||||
//! This module is enabled with the **`sync`** feature flag.
|
||||
//!
|
||||
//! Tasks sometimes need to communicate with each other. This module contains
|
||||
//! two basic abstractions for doing so:
|
||||
//!
|
||||
//! - [oneshot](oneshot/index.html), a way of sending a single value
|
||||
//! from one task to another.
|
||||
//! - [mpsc](mpsc/index.html), a multi-producer, single-consumer channel for
|
||||
//! sending values between tasks.
|
||||
//! - [watch](watch/index.html), a single-producer, multi-consumer channel that
|
||||
//! only stores the **most recently** sent value.
|
||||
|
||||
pub use tokio_sync::{mpsc, oneshot, watch};
|
||||
@@ -1,94 +0,0 @@
|
||||
//! 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.
|
||||
//!
|
||||
//! * [`Timeout`][Timeout]: Wraps a future or stream, setting an upper bound to the
|
||||
//! amount of time it is allowed to execute. If the future or stream does not
|
||||
//! complete in time, then it is canceled and an error is returned.
|
||||
//!
|
||||
//! * [`DelayQueue`]: A queue where items are returned once the requested delay
|
||||
//! has expired.
|
||||
//!
|
||||
//! 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
|
||||
//! [`timeout`][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() {
|
||||
//! tokio::run({
|
||||
//! long_op()
|
||||
//! .timeout(Duration::from_millis(300))
|
||||
//! .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.timeout
|
||||
//! [Timeout]: struct.Timeout.html
|
||||
//! [Delay]: struct.Delay.html
|
||||
//! [Interval]: struct.Interval.html
|
||||
//! [`DelayQueue`]: struct.DelayQueue.html
|
||||
|
||||
pub use tokio_timer::{delay_queue, timeout, Delay, DelayQueue, Error, Interval, Timeout};
|
||||
|
||||
#[deprecated(since = "0.1.8", note = "use Timeout instead")]
|
||||
#[allow(deprecated)]
|
||||
#[doc(hidden)]
|
||||
pub type Deadline<T> = ::tokio_timer::Deadline<T>;
|
||||
#[deprecated(since = "0.1.8", note = "use Timeout instead")]
|
||||
#[allow(deprecated)]
|
||||
#[doc(hidden)]
|
||||
pub type DeadlineError<T> = ::tokio_timer::DeadlineError<T>;
|
||||
@@ -1,84 +0,0 @@
|
||||
use futures::{Async, Poll, Sink, StartSend, Stream};
|
||||
|
||||
/// A stream combinator which combines the yields the current item
|
||||
/// plus its count starting from 0.
|
||||
///
|
||||
/// This structure is produced by the `Stream::enumerate` method.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "Does nothing unless polled"]
|
||||
pub struct Enumerate<T> {
|
||||
inner: T,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl<T> Enumerate<T> {
|
||||
pub(crate) fn new(stream: T) -> Self {
|
||||
Self {
|
||||
inner: stream,
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Acquires a reference to the underlying stream that this combinator is
|
||||
/// pulling from.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Acquires a mutable reference to the underlying stream that this
|
||||
/// combinator is pulling from.
|
||||
///
|
||||
/// Note that care must be taken to avoid tampering with the state of the
|
||||
/// stream which may otherwise confuse this combinator.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Consumes this combinator, returning the underlying stream.
|
||||
///
|
||||
/// Note that this may discard intermediate state of this combinator, so
|
||||
/// care should be taken to avoid losing resources when this is called.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for Enumerate<T>
|
||||
where
|
||||
T: Stream,
|
||||
{
|
||||
type Item = (usize, T::Item);
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, T::Error> {
|
||||
match try_ready!(self.inner.poll()) {
|
||||
Some(item) => {
|
||||
let ret = Some((self.count, item));
|
||||
self.count += 1;
|
||||
Ok(Async::Ready(ret))
|
||||
}
|
||||
None => return Ok(Async::Ready(None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forwarding impl of Sink from the underlying stream
|
||||
impl<T> Sink for Enumerate<T>
|
||||
where
|
||||
T: Sink,
|
||||
{
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
#[cfg(feature = "timer")]
|
||||
#[allow(deprecated)]
|
||||
use tokio_timer::Deadline;
|
||||
#[cfg(feature = "timer")]
|
||||
use tokio_timer::Timeout;
|
||||
|
||||
use futures::Future;
|
||||
|
||||
#[cfg(feature = "timer")]
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// An extension trait for `Future` that provides a variety of convenient
|
||||
/// combinator functions.
|
||||
///
|
||||
/// Currently, there only is a [`timeout`] 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::*`.
|
||||
///
|
||||
/// [`timeout`]: #method.timeout
|
||||
pub trait FutureExt: Future {
|
||||
/// Creates a new future which allows `self` until `timeout`.
|
||||
///
|
||||
/// This combinator creates a new future which wraps the receiving future
|
||||
/// with a timeout. The returned future is allowed to execute until it
|
||||
/// completes or `timeout` has elapsed, whichever happens first.
|
||||
///
|
||||
/// If the future completes before `timeout` then the future will resolve
|
||||
/// with that item. Otherwise the future will resolve to an error.
|
||||
///
|
||||
/// The future is guaranteed to be polled at least once, even if `timeout`
|
||||
/// is set to zero.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// use tokio::prelude::*;
|
||||
/// use std::time::Duration;
|
||||
/// # use futures::future::{self, FutureResult};
|
||||
///
|
||||
/// # fn long_future() -> FutureResult<(), ()> {
|
||||
/// # future::ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let future = long_future()
|
||||
/// .timeout(Duration::from_secs(1))
|
||||
/// .map_err(|e| println!("error = {:?}", e));
|
||||
///
|
||||
/// tokio::run(future);
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "timer")]
|
||||
fn timeout(self, timeout: Duration) -> Timeout<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Timeout::new(self, timeout)
|
||||
}
|
||||
|
||||
#[cfg(feature = "timer")]
|
||||
#[deprecated(since = "0.1.8", note = "use `timeout` instead")]
|
||||
#[allow(deprecated)]
|
||||
#[doc(hidden)]
|
||||
fn deadline(self, deadline: Instant) -> Deadline<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Deadline::new(self, deadline)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> FutureExt for T where T: Future {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use prelude::future;
|
||||
|
||||
#[cfg(feature = "timer")]
|
||||
#[test]
|
||||
fn timeout_polls_at_least_once() {
|
||||
let base_future = future::result::<(), ()>(Ok(()));
|
||||
let timeouted_future = base_future.timeout(Duration::new(0, 0));
|
||||
assert!(timeouted_future.wait().is_ok());
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! Utilities for working with Tokio.
|
||||
//!
|
||||
//! This module contains utilities that are useful for working with Tokio.
|
||||
//! Currently, this only includes [`FutureExt`] and [`StreamExt`], but this
|
||||
//! may grow over time.
|
||||
//!
|
||||
//! [`FutureExt`]: trait.FutureExt.html
|
||||
//! [`StreamExt`]: trait.StreamExt.html
|
||||
|
||||
mod enumerate;
|
||||
mod future;
|
||||
mod stream;
|
||||
|
||||
pub use self::future::FutureExt;
|
||||
pub use self::stream::StreamExt;
|
||||
@@ -1,95 +0,0 @@
|
||||
#[cfg(feature = "timer")]
|
||||
use tokio_timer::{throttle::Throttle, Timeout};
|
||||
|
||||
use futures::Stream;
|
||||
|
||||
#[cfg(feature = "timer")]
|
||||
use std::time::Duration;
|
||||
pub use util::enumerate::Enumerate;
|
||||
|
||||
/// An extension trait for `Stream` that provides a variety of convenient
|
||||
/// combinator functions.
|
||||
///
|
||||
/// Currently, there are only [`timeout`] and [`throttle`] functions, but
|
||||
/// this will increase over time.
|
||||
///
|
||||
/// Users are not expected to implement this trait. All types that implement
|
||||
/// `Stream` already implement `StreamExt`.
|
||||
///
|
||||
/// This trait can be imported directly or via the Tokio prelude: `use
|
||||
/// tokio::prelude::*`.
|
||||
///
|
||||
/// [`timeout`]: #method.timeout
|
||||
pub trait StreamExt: Stream {
|
||||
/// Throttle down the stream by enforcing a fixed delay between items.
|
||||
///
|
||||
/// Errors are also delayed.
|
||||
#[cfg(feature = "timer")]
|
||||
fn throttle(self, duration: Duration) -> Throttle<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Throttle::new(self, duration)
|
||||
}
|
||||
|
||||
/// Creates a new stream which gives the current iteration count as well
|
||||
/// as the next value.
|
||||
///
|
||||
/// The stream returned yields pairs `(i, val)`, where `i` is the
|
||||
/// current index of iteration and `val` is the value returned by the
|
||||
/// iterator.
|
||||
///
|
||||
/// # Overflow Behavior
|
||||
///
|
||||
/// The method does no guarding against overflows, so counting elements of
|
||||
/// an iterator with more than [`std::usize::MAX`] elements either produces the
|
||||
/// wrong result or panics.
|
||||
fn enumerate(self) -> Enumerate<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Enumerate::new(self)
|
||||
}
|
||||
|
||||
/// Creates a new stream which allows `self` until `timeout`.
|
||||
///
|
||||
/// This combinator creates a new stream which wraps the receiving stream
|
||||
/// with a timeout. For each item, the returned stream is allowed to execute
|
||||
/// until it completes or `timeout` has elapsed, whichever happens first.
|
||||
///
|
||||
/// If an item completes before `timeout` then the stream will yield
|
||||
/// with that item. Otherwise the stream will yield to an error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// use tokio::prelude::*;
|
||||
/// use std::time::Duration;
|
||||
/// # use futures::future::{self, FutureResult};
|
||||
///
|
||||
/// # fn long_future() -> FutureResult<(), ()> {
|
||||
/// # future::ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let stream = long_future()
|
||||
/// .into_stream()
|
||||
/// .timeout(Duration::from_secs(1))
|
||||
/// .for_each(|i| future::ok(println!("item = {:?}", i)))
|
||||
/// .map_err(|e| println!("error = {:?}", e));
|
||||
///
|
||||
/// tokio::run(stream);
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "timer")]
|
||||
fn timeout(self, timeout: Duration) -> Timeout<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Timeout::new(self, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> StreamExt for T where T: Stream {}
|
||||
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "tests-build"
|
||||
version = "0.1.0"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
full = ["tokio/full"]
|
||||
|
||||
[dependencies]
|
||||
tokio = { path = "../tokio", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0"
|
||||
@@ -0,0 +1,2 @@
|
||||
Tests the various combination of feature flags. This is broken out to a separate
|
||||
crate to work around limitations with cargo features.
|
||||
@@ -0,0 +1,2 @@
|
||||
#[cfg(feature = "tokio")]
|
||||
pub use tokio;
|
||||
@@ -0,0 +1,25 @@
|
||||
use tests_build::tokio;
|
||||
|
||||
#[tokio::main]
|
||||
fn main_is_not_async() {}
|
||||
|
||||
#[tokio::main(foo)]
|
||||
async fn main_attr_has_unknown_args() {}
|
||||
|
||||
#[tokio::main(threadpool::bar)]
|
||||
async fn main_attr_has_path_args() {}
|
||||
|
||||
#[tokio::test]
|
||||
fn test_is_not_async() {}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_fn_has_args(_x: u8) {}
|
||||
|
||||
#[tokio::test(foo)]
|
||||
async fn test_attr_has_args() {}
|
||||
|
||||
#[tokio::test]
|
||||
#[test]
|
||||
async fn test_has_second_test_attr() {}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,41 @@
|
||||
error: the async keyword is missing from the function declaration
|
||||
--> $DIR/macros_invalid_input.rs:4:1
|
||||
|
|
||||
4 | fn main_is_not_async() {}
|
||||
| ^^
|
||||
|
||||
error: Unknown attribute foo is specified; expected `basic_scheduler` or `threaded_scheduler`
|
||||
--> $DIR/macros_invalid_input.rs:6:15
|
||||
|
|
||||
6 | #[tokio::main(foo)]
|
||||
| ^^^
|
||||
|
||||
error: Must have specified ident
|
||||
--> $DIR/macros_invalid_input.rs:9:15
|
||||
|
|
||||
9 | #[tokio::main(threadpool::bar)]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: the async keyword is missing from the function declaration
|
||||
--> $DIR/macros_invalid_input.rs:13:1
|
||||
|
|
||||
13 | fn test_is_not_async() {}
|
||||
| ^^
|
||||
|
||||
error: the test function cannot accept arguments
|
||||
--> $DIR/macros_invalid_input.rs:16:27
|
||||
|
|
||||
16 | async fn test_fn_has_args(_x: u8) {}
|
||||
| ^^^^^^
|
||||
|
||||
error: Unknown attribute foo is specified; expected `basic_scheduler` or `threaded_scheduler`
|
||||
--> $DIR/macros_invalid_input.rs:18:15
|
||||
|
|
||||
18 | #[tokio::test(foo)]
|
||||
| ^^^
|
||||
|
||||
error: second test attribute is supplied
|
||||
--> $DIR/macros_invalid_input.rs:22:1
|
||||
|
|
||||
22 | #[test]
|
||||
| ^^^^^^^
|
||||
@@ -0,0 +1,9 @@
|
||||
#[test]
|
||||
fn compile_fail() {
|
||||
let t = trybuild::TestCases::new();
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
t.compile_fail("tests/fail/macros_invalid_input.rs");
|
||||
|
||||
drop(t);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "tests-integration"
|
||||
version = "0.1.0"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
full = [
|
||||
"macros",
|
||||
"rt-core",
|
||||
"rt-threaded",
|
||||
|
||||
"tokio/full",
|
||||
"tokio-test"
|
||||
]
|
||||
macros = ["tokio/macros"]
|
||||
rt-core = ["tokio/rt-core"]
|
||||
rt-threaded = ["rt-core", "tokio/rt-threaded"]
|
||||
|
||||
[dependencies]
|
||||
tokio = { path = "../tokio" }
|
||||
tokio-test = { path = "../tokio-test", optional = true }
|
||||
doc-comment = "0.3.1"
|
||||
|
||||
[dev-dependencies]
|
||||
futures = { version = "0.3.0", features = ["async-await"] }
|
||||
@@ -0,0 +1 @@
|
||||
Tests that require additional components than just the `tokio` crate.
|
||||
@@ -0,0 +1,20 @@
|
||||
//! A cat-like utility that can be used as a subprocess to test I/O
|
||||
//! stream communication.
|
||||
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
|
||||
fn main() {
|
||||
let stdin = io::stdin();
|
||||
let mut stdout = io::stdout();
|
||||
let mut line = String::new();
|
||||
loop {
|
||||
line.clear();
|
||||
stdin.read_line(&mut line).unwrap();
|
||||
if line.is_empty() {
|
||||
break;
|
||||
}
|
||||
stdout.write_all(line.as_bytes()).unwrap();
|
||||
}
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#[cfg(feature = "full")]
|
||||
doc_comment::doc_comment!(include_str!("../../README.md"));
|
||||
@@ -0,0 +1,31 @@
|
||||
#![cfg(feature = "macros")]
|
||||
|
||||
#[tokio::main]
|
||||
async fn basic_main() -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn generic_fun<T: Default>() -> T {
|
||||
T::default()
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt-core")]
|
||||
mod spawn {
|
||||
#[tokio::main]
|
||||
async fn spawning() -> usize {
|
||||
let join = tokio::spawn(async { 1 });
|
||||
join.await.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn main_with_spawn() {
|
||||
assert_eq!(1, spawning());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shell() {
|
||||
assert_eq!(1, basic_main());
|
||||
assert_eq!(bool::default(), generic_fun::<bool>())
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
use futures::executor::block_on;
|
||||
|
||||
async fn my_async_fn() {}
|
||||
|
||||
#[test]
|
||||
fn pin() {
|
||||
block_on(async {
|
||||
let future = my_async_fn();
|
||||
tokio::pin!(future);
|
||||
(&mut future).await
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#![cfg(feature = "macros")]
|
||||
|
||||
use futures::channel::oneshot;
|
||||
use futures::executor::block_on;
|
||||
use std::thread;
|
||||
|
||||
#[test]
|
||||
fn join_with_select() {
|
||||
block_on(async {
|
||||
let (tx1, mut rx1) = oneshot::channel::<i32>();
|
||||
let (tx2, mut rx2) = oneshot::channel::<i32>();
|
||||
|
||||
thread::spawn(move || {
|
||||
tx1.send(123).unwrap();
|
||||
tx2.send(456).unwrap();
|
||||
});
|
||||
|
||||
let mut a = None;
|
||||
let mut b = None;
|
||||
|
||||
while a.is_none() || b.is_none() {
|
||||
tokio::select! {
|
||||
v1 = (&mut rx1), if a.is_none() => a = Some(v1.unwrap()),
|
||||
v2 = (&mut rx2), if b.is_none() => b = Some(v2.unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
let (a, b) = (a.unwrap(), b.unwrap());
|
||||
|
||||
assert_eq!(a, 123);
|
||||
assert_eq!(b, 456);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![cfg(feature = "full")]
|
||||
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::process::{Child, Command};
|
||||
use tokio_test::assert_ok;
|
||||
|
||||
use futures::future::{self, FutureExt};
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::process::{ExitStatus, Stdio};
|
||||
|
||||
fn cat() -> Command {
|
||||
let mut me = env::current_exe().unwrap();
|
||||
me.pop();
|
||||
|
||||
if me.ends_with("deps") {
|
||||
me.pop();
|
||||
}
|
||||
|
||||
me.push("test-cat");
|
||||
|
||||
let mut cmd = Command::new(me);
|
||||
cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
|
||||
cmd
|
||||
}
|
||||
|
||||
async fn feed_cat(mut cat: Child, n: usize) -> io::Result<ExitStatus> {
|
||||
let mut stdin = cat.stdin.take().unwrap();
|
||||
let stdout = cat.stdout.take().unwrap();
|
||||
|
||||
// Produce n lines on the child's stdout.
|
||||
let write = async {
|
||||
for i in 0..n {
|
||||
let bytes = format!("line {}\n", i).into_bytes();
|
||||
stdin.write_all(&bytes).await.unwrap();
|
||||
}
|
||||
|
||||
drop(stdin);
|
||||
};
|
||||
|
||||
let read = async {
|
||||
let mut reader = BufReader::new(stdout).lines();
|
||||
let mut num_lines = 0;
|
||||
|
||||
// Try to read `n + 1` lines, ensuring the last one is empty
|
||||
// (i.e. EOF is reached after `n` lines.
|
||||
loop {
|
||||
let data = reader
|
||||
.next_line()
|
||||
.await
|
||||
.unwrap_or_else(|_| Some(String::new()))
|
||||
.expect("failed to read line");
|
||||
|
||||
let num_read = data.len();
|
||||
let done = num_lines >= n;
|
||||
|
||||
match (done, num_read) {
|
||||
(false, 0) => panic!("broken pipe"),
|
||||
(true, n) if n != 0 => panic!("extraneous data"),
|
||||
_ => {
|
||||
let expected = format!("line {}", num_lines);
|
||||
assert_eq!(expected, data);
|
||||
}
|
||||
};
|
||||
|
||||
num_lines += 1;
|
||||
if num_lines >= n {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Compose reading and writing concurrently.
|
||||
future::join3(write, read, cat)
|
||||
.map(|(_, _, status)| status)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Check for the following properties when feeding stdin and
|
||||
/// consuming stdout of a cat-like process:
|
||||
///
|
||||
/// - A number of lines that amounts to a number of bytes exceeding a
|
||||
/// typical OS buffer size can be fed to the child without
|
||||
/// deadlock. This tests that we also consume the stdout
|
||||
/// concurrently; otherwise this would deadlock.
|
||||
///
|
||||
/// - We read the same lines from the child that we fed it.
|
||||
///
|
||||
/// - The child does produce EOF on stdout after the last line.
|
||||
#[tokio::test]
|
||||
async fn feed_a_lot() {
|
||||
let child = cat().spawn().unwrap();
|
||||
let status = feed_cat(child, 10000).await.unwrap();
|
||||
assert_eq!(status.code(), Some(0));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn wait_with_output_captures() {
|
||||
let mut child = cat().spawn().unwrap();
|
||||
let mut stdin = child.stdin.take().unwrap();
|
||||
|
||||
let write_bytes = b"1234";
|
||||
|
||||
let future = async {
|
||||
stdin.write_all(write_bytes).await?;
|
||||
drop(stdin);
|
||||
let out = child.wait_with_output();
|
||||
out.await
|
||||
};
|
||||
|
||||
let output = future.await.unwrap();
|
||||
|
||||
assert!(output.status.success());
|
||||
assert_eq!(output.stdout, write_bytes);
|
||||
assert_eq!(output.stderr.len(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn status_closes_any_pipes() {
|
||||
// Cat will open a pipe between the parent and child.
|
||||
// If `status_async` doesn't ensure the handles are closed,
|
||||
// we would end up blocking forever (and time out).
|
||||
let child = cat().status();
|
||||
|
||||
assert_ok!(child.await);
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
|
||||
use std::io::{BufReader, BufWriter, Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use std::thread;
|
||||
|
||||
use futures::stream::Stream;
|
||||
use futures::Future;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_io::io::copy;
|
||||
|
||||
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::try_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);
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_timer;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use tokio::runtime::{self, current_thread};
|
||||
use tokio::timer::*;
|
||||
use tokio_timer::clock::Clock;
|
||||
|
||||
use std::sync::mpsc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
struct MockNow(Instant);
|
||||
|
||||
impl tokio_timer::clock::Now for MockNow {
|
||||
fn now(&self) -> Instant {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clock_and_timer_concurrent() {
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let when = Instant::now() + Duration::from_millis(5_000);
|
||||
let clock = Clock::new_with_now(MockNow(when));
|
||||
|
||||
let mut rt = runtime::Builder::new().clock(clock).build().unwrap();
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
rt.spawn({
|
||||
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 clock_and_timer_single_threaded() {
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let when = Instant::now() + Duration::from_millis(5_000);
|
||||
let clock = Clock::new_with_now(MockNow(when));
|
||||
|
||||
let mut rt = current_thread::Builder::new().clock(clock).build().unwrap();
|
||||
|
||||
rt.block_on({
|
||||
Delay::new(when)
|
||||
.map_err(|e| panic!("unexpected error; err={:?}", e))
|
||||
.and_then(move |_| {
|
||||
assert!(Instant::now() < when);
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
use std::net;
|
||||
use std::thread;
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_timer;
|
||||
|
||||
use futures::sync::mpsc;
|
||||
use tokio::util::StreamExt;
|
||||
|
||||
#[test]
|
||||
fn enumerate() {
|
||||
use futures::*;
|
||||
|
||||
let (mut tx, rx) = mpsc::channel(1);
|
||||
|
||||
std::thread::spawn(|| {
|
||||
for i in 0..5 {
|
||||
tx = tx.send(i * 2).wait().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
let result = rx.enumerate().collect();
|
||||
assert_eq!(
|
||||
result.wait(),
|
||||
Ok(vec![(0, 0), (1, 2), (2, 4), (3, 6), (4, 8)])
|
||||
);
|
||||
}
|
||||
-141
@@ -1,141 +0,0 @@
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
|
||||
use futures::prelude::*;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
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::try_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::try_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));
|
||||
}
|
||||
}
|
||||
@@ -1,627 +0,0 @@
|
||||
extern crate bytes;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::codec::*;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use futures::Async::*;
|
||||
use futures::{Poll, Sink, Stream};
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::io;
|
||||
|
||||
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!(), LengthDelimitedCodec::new());
|
||||
|
||||
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()),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
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 = length_delimited::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 = length_delimited::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()),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
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()),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
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()),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
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()),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
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()),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
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()),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
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()),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
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()),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
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 = length_delimited::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 = length_delimited::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.decoder_mut().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 = length_delimited::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.decoder_mut().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 = length_delimited::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 = length_delimited::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 = length_delimited::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 = length_delimited::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 = length_delimited::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(Bytes::from("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::new(mock!(), LengthDelimitedCodec::new());
|
||||
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),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
assert!(io.start_send(Bytes::from("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),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdefghi")).unwrap().is_ready());
|
||||
assert!(io.start_send(Bytes::from("123")).unwrap().is_ready());
|
||||
assert!(io
|
||||
.start_send(Bytes::from("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),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdefghi")).unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.start_send(Bytes::from("123")).unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io
|
||||
.start_send(Bytes::from("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),
|
||||
},
|
||||
LengthDelimitedCodec::new(),
|
||||
);
|
||||
|
||||
assert!(io.start_send(Bytes::from("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 = length_delimited::Builder::new()
|
||||
.little_endian()
|
||||
.new_write(mock! {
|
||||
Ok(b"\x09\x00\x00\x00"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send(Bytes::from("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 = length_delimited::Builder::new()
|
||||
.length_field_length(1)
|
||||
.new_write(mock! {
|
||||
Ok(b"\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send(Bytes::from("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 = length_delimited::Builder::new()
|
||||
.max_frame_length(5)
|
||||
.new_write(mock! {});
|
||||
|
||||
assert_eq!(
|
||||
io.start_send(Bytes::from("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 = length_delimited::Builder::new().new_write(mock! {
|
||||
Ok(b"\x00\x00\x00\x06"[..].into()),
|
||||
Ok(b"abcdef"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdef")).unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
io.encoder_mut().set_max_frame_length(5);
|
||||
assert_eq!(
|
||||
io.start_send(Bytes::from("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 = length_delimited::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(Bytes::from("abcdef")).unwrap().is_ready());
|
||||
assert!(!io.poll_complete().unwrap().is_ready());
|
||||
io.encoder_mut().set_max_frame_length(5);
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert_eq!(
|
||||
io.start_send(Bytes::from("abcdef")).unwrap_err().kind(),
|
||||
io::ErrorKind::InvalidInput
|
||||
);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_zero() {
|
||||
let mut io = length_delimited::Builder::new().new_write(mock! {});
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdef")).unwrap().is_ready());
|
||||
assert_eq!(
|
||||
io.poll_complete().unwrap_err().kind(),
|
||||
io::ErrorKind::WriteZero
|
||||
);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_overflow() {
|
||||
// Test reproducing tokio-rs/tokio#681.
|
||||
let mut codec = length_delimited::Builder::new().new_codec();
|
||||
let mut buf = BytesMut::with_capacity(1024);
|
||||
|
||||
// Put some data into the buffer without resizing it to hold more.
|
||||
let some_as = std::iter::repeat(b'a').take(1024).collect::<Vec<_>>();
|
||||
buf.put_slice(&some_as[..]);
|
||||
|
||||
// Trying to encode the length header should resize the buffer if it won't fit.
|
||||
codec.encode(Bytes::from("hello"), &mut buf).unwrap();
|
||||
}
|
||||
|
||||
// ===== 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)
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
extern crate bytes;
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_threadpool;
|
||||
|
||||
use std::io;
|
||||
use std::net::Shutdown;
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures::{Future, Sink, Stream};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio_codec::{Decoder, Encoder};
|
||||
use tokio_io::io::{read, write_all};
|
||||
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::try_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) = LineCodec.framed(socket).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");
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user