forked from mirror/async-std
Compare commits
866 commits
write-by-r
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
8aa5921dfa | ||
|
cd7fb9dec2 | ||
|
c82b1efb69 | ||
|
8c4b425136 | ||
|
2ab08ebbbc | ||
|
0e7650a421 | ||
|
8f17e9275b | ||
|
18dffe8b43 | ||
|
43de93312c | ||
|
2e7e804736 | ||
|
17ab958ac2 | ||
|
caa76af745 | ||
|
e495ba46b3 | ||
|
0c2ce52ac4 | ||
|
5f418f07ac | ||
|
06a2fb8c4f | ||
|
1c1c168e1b | ||
|
5d55fa7a47 | ||
|
093d640ad7 | ||
|
42425f6c1a | ||
|
a602a91d83 | ||
|
9fa3ce3fd6 | ||
|
df22d87d09 | ||
|
924e5a3f41 | ||
|
2323ac9a8e | ||
|
5c2a3de9e7 | ||
|
e9c6ea873c | ||
|
0d98aac8f7 | ||
|
4555f193a5 | ||
|
61fc2bae72 | ||
|
5a1a681d68 | ||
|
e12cf80ab0 | ||
|
631105b650 | ||
|
0897b9184a | ||
|
6ca7b0977c | ||
|
721760a7a6 | ||
|
8389041414 | ||
|
8943ba82dd | ||
|
52c72426c1 | ||
|
0df3c02b81 | ||
|
166c469d1c | ||
|
0ec027dbff | ||
|
d60e7cc27d | ||
|
6d2a43e336 | ||
|
e1c8638173 | ||
|
06eea4225b | ||
|
252140839b | ||
|
69806403c6 | ||
|
955befd746 | ||
|
70dac51938 | ||
|
d30603affe | ||
|
c9ecb5bbbd | ||
|
9e6a76af04 | ||
|
2b6c7fedff | ||
|
b3277954c7 | ||
|
baead51a28 | ||
|
e9621af345 | ||
|
d3e59370e7 | ||
|
cd5e17fe87 | ||
|
e20b0f0d75 | ||
|
19170aead4 | ||
|
2762ec5800 | ||
|
247c94ca06 | ||
|
e404dcdd03 | ||
|
bd6a7e200b | ||
|
e4c4c93d29 | ||
|
6f6fced103 | ||
|
10f7abb3b6 | ||
|
27c605b4c9 | ||
|
faea222b9c | ||
|
1214bc2dee | ||
|
26f62aafd9 | ||
|
e0928463b1 | ||
|
92532612b7 | ||
|
1a6d4f6a2f | ||
|
7a9afbd81c | ||
|
280b1a4344 | ||
|
48dd683535 | ||
|
804a52b7fd | ||
|
e4df1405c1 | ||
|
2cd2ba3530 | ||
|
3161a4e449 | ||
|
228cc59b3b | ||
|
0a7a52aed5 | ||
|
10c8b9a6d8 | ||
|
fd6ae40817 | ||
|
ab9d6554aa | ||
|
f5fa0d7e4e | ||
|
b96afc41dc | ||
|
75ab7219df | ||
|
e082634b5e | ||
|
fc9ee0dfdd | ||
|
1308fbdf55 | ||
|
690ab16587 | ||
|
370642ef3e | ||
|
100c3423c1 | ||
|
7999e6bf4b | ||
|
e707ea96e0 | ||
|
b446cd0230 | ||
|
db438abb8f | ||
|
a4e07e345c | ||
|
aebba2bd95 | ||
|
0c9a66c1f6 | ||
|
fc4e472599 | ||
|
6674dc0edf | ||
|
088aa5662c | ||
|
68fa054517 | ||
|
b88138b5d7 | ||
|
11ee2a8985 | ||
|
322911142c | ||
|
cfaec2aa95 | ||
|
57c648cf01 | ||
|
6d3ca5a06f | ||
|
f960776846 | ||
|
5c6741724f | ||
|
24c5dbf949 | ||
|
2dbebe54ed | ||
|
d7ee29a03f | ||
|
2b44c1be2e | ||
|
b1ec1ea930 | ||
|
2ab075d027 | ||
|
c0f18600cf | ||
|
6c8237276b | ||
|
98cbf7f8eb | ||
|
84e5c5f351 | ||
|
3ff9e98f20 | ||
|
b7c7efc797 | ||
|
19fd7a4084 | ||
|
7885c245c5 | ||
|
7b7b959a6e | ||
|
32dce319d3 | ||
|
49dd02b4de | ||
|
bb11c676a1 | ||
|
e026b7579a | ||
|
51dd7ceb72 | ||
|
8931d1464e | ||
|
cc19592f80 | ||
|
f69887a50d | ||
|
0b0531057d | ||
|
61f9483cc5 | ||
|
f33d7f40ab | ||
|
e3bf89fc05 | ||
|
ec4b09ecd0 | ||
|
b95bd6c1fe | ||
|
1e18839f1f | ||
|
f31878655e | ||
|
9a62df143f | ||
|
75223905bd | ||
|
be60dd9fe7 | ||
|
23b7c174f3 | ||
|
9167d42f4b | ||
|
4034d58709 | ||
|
4742f461fe | ||
|
efab39eeaf | ||
|
bd60cd9f81 | ||
|
b9e4b6da3e | ||
|
eb03f37e43 | ||
|
d87e283215 | ||
|
283a54a155 | ||
|
3719484eba | ||
|
aae835cc14 | ||
|
68063adddf | ||
|
d7cab38b67 | ||
|
32068942a6 | ||
|
85c32ef9d2 | ||
|
b68be72763 | ||
|
c80915e216 | ||
|
303ac90b7c | ||
|
d026c44ea3 | ||
|
125fa5b0a0 | ||
|
39f2c6da78 | ||
|
3e24e0ba4e | ||
|
0d90cb07b9 | ||
|
f789f9d4f6 | ||
|
ef985bc72e | ||
|
1d875836a2 | ||
|
7efe7caf66 | ||
|
22d929d481 | ||
|
d622ec5d35 | ||
|
880b7ee987 | ||
|
1762de285b | ||
|
6aa55fde59 | ||
|
41f114d9fe | ||
|
3d32fd81f4 | ||
|
51b84a7620 | ||
|
4996f29778 | ||
|
57974ae0b7 | ||
|
57f9fb7e93 | ||
|
6c1b5eb3ed | ||
|
beb8d240c2 | ||
|
b258215952 | ||
|
1ababac97f | ||
|
f9fe5c90cf | ||
|
84fe94444b | ||
|
cad2880eb8 | ||
|
6b860c370a | ||
|
81aa6d152a | ||
|
2221441a4c | ||
|
ed7ddacb28 | ||
|
d283352a9a | ||
|
ed248017b4 | ||
|
0eb5ca14ac | ||
|
38de0bfd22 | ||
|
134089af2c | ||
|
b72dd83726 | ||
|
ee102dfc9e | ||
|
1071e82132 | ||
|
0a52864764 | ||
|
76993dd755 | ||
|
133e30e6f6 | ||
|
76ed174fd5 | ||
|
f53fcbb706 | ||
|
e9357c0307 | ||
|
879e14c6ab | ||
|
f8dd3d9816 | ||
|
a4f6806605 | ||
|
5d5064b871 | ||
|
0ed0d63094 | ||
|
0029037883 | ||
|
fb567a3a09 | ||
|
83afbab2ef | ||
|
c8c075615c | ||
|
98d45f4be1 | ||
|
9c6ab5e7c3 | ||
|
9c9ab90da3 | ||
|
5bf3d95313 | ||
|
1f78efec64 | ||
|
383057b8ea | ||
|
763862acc7 | ||
|
e2bb79c207 | ||
|
57a62797f2 | ||
|
dfb0c8124c | ||
|
d806a09599 | ||
|
af2d46d9b9 | ||
|
65d7950df1 | ||
|
6d69a3e368 | ||
|
c3d5dba1b5 | ||
|
081166f204 | ||
|
fee3b6f603 | ||
|
b3942ecfa8 | ||
|
86d3d74180 | ||
|
3fd6d8b02e | ||
|
0d4b4cd260 | ||
|
ef021dcb2b | ||
|
eedf1d3367 | ||
|
97b4901b75 | ||
|
1eeb1019e9 | ||
|
980a1f7834 | ||
|
d2c25f483a | ||
|
d8befe24e8 | ||
|
c7cf1934db | ||
|
37d8a013de | ||
|
43f4f393af | ||
|
ceba324bef | ||
|
36d24cd0e1 | ||
|
61eb52cb36 | ||
|
6f4dcad6a0 | ||
|
60de8e1082 | ||
|
cac4e081cc | ||
|
8ad1d23116 | ||
|
019aa14898 | ||
|
b7e55762d8 | ||
|
c70552ead5 | ||
|
07eb2c1280 | ||
|
732ef10f98 | ||
|
499a44ab3b | ||
|
761029cd08 | ||
|
83a488b290 | ||
|
2f0907714d | ||
|
055c64e8a7 | ||
|
96d6fc43d6 | ||
|
3d3bf914ea | ||
|
84b6d2b276 | ||
|
8de9f9b8e1 | ||
|
182fe6896f | ||
|
b0038e11be | ||
|
8e5dedec34 | ||
|
41cf0f855b | ||
|
f9a4c35fd6 | ||
|
6e8236d0e1 | ||
|
892c6008c2 | ||
|
abd360893c | ||
|
94893d2924 | ||
|
02aa2f3d2a | ||
|
ee2f52f3ce | ||
|
55194edbf7 | ||
|
c4b9a7f680 | ||
|
aabfefd015 | ||
|
cc493df433 | ||
|
78bafbb88f | ||
|
d0ef48c753 | ||
|
fa288931c6 | ||
|
c90732a805 | ||
|
63b6a2b961 | ||
|
1103c17e16 | ||
|
a0f3b3b753 | ||
|
f06ab9fbc4 | ||
|
447c17128f | ||
|
a04157850b | ||
|
9311fd7fae | ||
|
f7b21a3e8d | ||
|
f0bdcfec25 | ||
|
33e7c87dfc | ||
|
cc85533f7c | ||
|
4670388a56 | ||
|
c14c377974 | ||
|
54fa559554 | ||
|
bce8688763 | ||
|
fd86effb63 | ||
|
128a6bc6ce | ||
|
d51a135015 | ||
|
1c2055fff0 | ||
|
81e3c41826 | ||
|
fb1fb6c903 | ||
|
c85e2496b1 | ||
|
7d9a063002 | ||
|
44e38eae59 | ||
|
fe04cf26b6 | ||
|
556d7992ce | ||
|
da965e9ba4 | ||
|
9f7c1833dc | ||
|
55560ea9b4 | ||
|
bf9ee88815 | ||
|
9627826756 | ||
|
4ed15d67c9 | ||
|
0165d7f6d1 | ||
|
dba416608a | ||
|
68005661d9 | ||
|
794e331761 | ||
|
63f7ea3081 | ||
|
32765ece41 | ||
|
635c592950 | ||
|
3b055f364e | ||
|
46cafffc31 | ||
|
0f30ab8c0a | ||
|
e66e2e2b8f | ||
|
56538ebd91 | ||
|
aa7d1c27a4 | ||
|
850b8ae9d0 | ||
|
ac7a796f82 | ||
|
c1f7be5d42 | ||
|
50cefce803 | ||
|
3780ff7b44 | ||
|
c9a2e74789 | ||
|
cffacf7fa3 | ||
|
02e1d7e5ea | ||
|
ec5415358f | ||
|
ba1ee2d204 | ||
|
16edec3464 | ||
|
e01f07d72a | ||
|
b3d30de4a1 | ||
|
6f19165e0e | ||
|
d146d95a39 | ||
|
5fba3a0928 | ||
|
2ca03cabe6 | ||
|
72ed4eb4fd | ||
|
77800ab3f9 | ||
|
8ea920c9f0 | ||
|
d1189f9974 | ||
|
3f8ec5a007 | ||
|
3bc4d293dd | ||
|
f24b3a4520 | ||
|
72ca2c1a24 | ||
|
b5e66c4f93 | ||
|
080875edc9 | ||
|
ca71ad073b | ||
|
667bbc1019 | ||
|
64b2e10b93 | ||
|
314a75da28 | ||
|
c704643296 | ||
|
c6622475b2 | ||
|
65afd41a33 | ||
|
d3e7f32a30 | ||
|
f6829859fe | ||
|
2c9b558d14 | ||
|
99ddfb3f93 | ||
|
8ce3e78952 | ||
|
b2aaa8b825 | ||
|
223fcc30ee | ||
|
76975a4441 | ||
|
355e2eded8 | ||
|
6cc9e4dd2b | ||
|
ee23ba6e94 | ||
|
d68dc659b2 | ||
|
b5c3fb8bb5 | ||
|
7d616c695d | ||
|
a69b3a8a9e | ||
|
a05b6a3810 | ||
|
6cbf48f12d | ||
|
91ee4c7b9f | ||
|
603b3c5085 | ||
|
693a7257b8 | ||
|
8779c04dc7 | ||
|
3564be9c0c | ||
|
df92c63337 | ||
|
31cf932d80 | ||
|
3c6d41ccb4 | ||
|
837604b833 | ||
|
74caed2d4b | ||
|
76ec9c4563 | ||
|
d76b32e6d4 | ||
|
11268a80fb | ||
|
c4ba11ff95 | ||
|
de67bf0fd4 | ||
|
4ef55d4d7b | ||
|
77a0419a3e | ||
|
ce98834039 | ||
|
30ff7b09b6 | ||
|
c58747b5fc | ||
|
31f129ebe7 | ||
|
f49d7cbbb8 | ||
|
64216b8e6b | ||
|
786a52a09d | ||
|
f14b37ff17 | ||
|
7677e9a3df | ||
|
bfb42b432e | ||
|
4c09cdbeac | ||
|
243cdd7ff1 | ||
|
a257b7018c | ||
|
af928163e4 | ||
|
8d97e0f974 | ||
|
266754897e | ||
|
554d5cfbc1 | ||
|
8b662b659d | ||
|
95a3e53fcd | ||
|
02b261de10 | ||
|
fe3c9ef626 | ||
|
4e1d79adb1 | ||
|
1546448800 | ||
|
338273eb18 | ||
|
dda65cbff0 | ||
|
9ebe41f2d6 | ||
|
90c67c223a | ||
|
8473b738d0 | ||
|
0c2282ffdc | ||
|
d546ee3d92 | ||
|
6338341369 | ||
|
6f4bea07a1 | ||
|
5adb112a00 | ||
|
9d634cb2a7 | ||
|
879af6dc85 | ||
|
79962e20a5 | ||
|
1431ee0422 | ||
|
2dfdc1c482 | ||
|
e442eba625 | ||
|
f0875d2dca | ||
|
f611ceccc8 | ||
|
74a7d93611 | ||
|
c5b3a98e5b | ||
|
3151a70b77 | ||
|
4ab7b213de | ||
|
6990c1403f | ||
|
77a1849303 | ||
|
a722de1a10 | ||
|
88cbf2c119 | ||
|
6f6d5e9d20 | ||
|
7c7386735e | ||
|
1b7d5bea6b | ||
|
46c58b214c | ||
|
0d5c7a217f | ||
|
5017117326 | ||
|
9d7b2d6696 | ||
|
643b678ea5 | ||
|
b5b2b5a0a3 | ||
|
54371c21c1 | ||
|
1a50ffd144 | ||
|
21c5c48cb6 | ||
|
6677d52c2d | ||
|
37922408e5 | ||
|
7d2282dbd2 | ||
|
60f822bee5 | ||
|
9ad0cf9f80 | ||
|
980c30e90f | ||
|
4aa9928ece | ||
|
eea7af24db | ||
|
76c5ffe9ab | ||
|
5438258cee | ||
|
ef958f0408 | ||
|
dc5d143c16 | ||
|
c2f750d288 | ||
|
139a34b685 | ||
|
b591fc68bd | ||
|
5d558ca213 | ||
|
e48e463736 | ||
|
0c37d4af10 | ||
|
352f18bc2a | ||
|
79bbf4938d | ||
|
14d7d3bf9c | ||
|
417b548692 | ||
|
122e87364b | ||
|
925b42bc89 | ||
|
d4f38e783f | ||
|
96d3560742 | ||
|
ac1042a9ca | ||
|
9e185f1c3e | ||
|
89d611628a | ||
|
e9c0f8f6b5 | ||
|
16565ccfbc | ||
|
74882c119d | ||
|
d8e52c1002 | ||
|
037119c0c0 | ||
|
548733e5d5 | ||
|
f04b6f6fe9 | ||
|
4a78f731b7 | ||
|
d2d63348c7 | ||
|
8f3366072f | ||
|
e74e246bbb | ||
|
fb19ebde17 | ||
|
b14282457c | ||
|
ab2f64cd84 | ||
|
fd088fea38 | ||
|
335bd34470 | ||
|
f588ba6bdd | ||
|
84880c4d8b | ||
|
bc24503382 | ||
|
266e6326eb | ||
|
f8e82564d9 | ||
|
eb1ef3f4e4 | ||
|
d0f1996759 | ||
|
8a0e29473a | ||
|
2cb887e154 | ||
|
929027796e | ||
|
9a8805678e | ||
|
a064a5b13e | ||
|
c34e0f8a35 | ||
|
93b01e36ed | ||
|
d502453057 | ||
|
3144e217ae | ||
|
c3254d78d9 | ||
|
f4fb8a3534 | ||
|
1c87e97e9c | ||
|
a757cc02dc | ||
|
ae8b051892 | ||
|
1707638ebb | ||
|
84fa4ca068 | ||
|
43bb59cd02 | ||
|
5179f30d2d | ||
|
a35602f375 | ||
|
e14cc2a30d | ||
|
6d421de992 | ||
|
5874392397 | ||
|
e9edadffc7 | ||
|
282ae064fe | ||
|
e85bbe68e6 | ||
|
d7afcada76 | ||
|
e36172e808 | ||
|
a3e68704bc | ||
|
8bef812e78 | ||
|
bf0cd5987a | ||
|
20cdf73bb0 | ||
|
ed1cb49807 | ||
|
78614c6c1d | ||
|
ddbbbfc32a | ||
|
4942dc7f9f | ||
|
e0910be8fb | ||
|
57a6516e63 | ||
|
fbd5bd867d | ||
|
9ee804f9ed | ||
|
eaa56580e3 | ||
|
0186124aef | ||
|
197253aa73 | ||
|
19381fa590 | ||
|
ed5b095c73 | ||
|
5aadc5e4e9 | ||
|
b979773505 | ||
|
fd09e2f248 | ||
|
171cc82aed | ||
|
83ff11ff4c | ||
|
e1ba87e7c1 | ||
|
8126bb1882 | ||
|
486f9a964c | ||
|
a096d5ec2d | ||
|
dea1b67670 | ||
|
fa91d7f856 | ||
|
9a4f4c591c | ||
|
6f9436e575 | ||
|
735fa6954e | ||
|
3a2e6d5b92 | ||
|
5fb9d3e980 | ||
|
4c63392a85 | ||
|
3dcad984b4 | ||
|
c413e717da | ||
|
911ebada0b | ||
|
ec1a6ea3e8 | ||
|
5adc608791 | ||
|
1a51ca424a | ||
|
81873ae5f3 | ||
|
d5fd035956 | ||
|
e17a6703c8 | ||
|
8e991bcd3a | ||
|
2e66c38453 | ||
|
0661f774c2 | ||
|
2f3c867d44 | ||
|
063798ce49 | ||
|
a3b742188d | ||
|
57670dd9d7 | ||
|
dcd7c55cef | ||
|
277fd521bc | ||
|
87de4e1598 | ||
|
3dd59d7056 | ||
|
caa23381f0 | ||
|
eeb44c86e9 | ||
|
f8b8c9debe | ||
|
c1e8517959 | ||
|
cc75b65b8c | ||
|
f102588df5 | ||
|
3e0fe742f6 | ||
|
65dcaf4464 | ||
|
07d21e5eb3 | ||
|
c6c2bfa456 | ||
|
48c82a9668 | ||
|
1ab3d901e4 | ||
|
204da33391 | ||
|
17db7ffcd3 | ||
|
f5efaaa7ba | ||
|
cc949f48ea | ||
|
5fee91c050 | ||
|
ff6a44fcd5 | ||
|
40c4e1a29d | ||
|
3620b2b6ab | ||
|
2c91b30ee8 | ||
|
021862dcc8 | ||
|
b942d0a405 | ||
|
da795dec7b | ||
|
2adaaa9d3f | ||
|
b10930207c | ||
|
b3d1fa9c98 | ||
|
2b1c6f04ea | ||
|
5ff4ef8dd4 | ||
|
f311e3de9f | ||
|
3a06a1211b | ||
|
526c4da21a | ||
|
997e811c58 | ||
|
11d0577407 | ||
|
eb081b1948 | ||
|
35cb11e398 | ||
|
1175a37c47 | ||
|
206bedfd12 | ||
|
1554b04407 | ||
|
1545d24e50 | ||
|
ae7adf2c36 | ||
|
040227f38a | ||
|
688976203e | ||
|
c7dc147f73 | ||
|
b3ae6f2b03 | ||
|
4ecf49fe95 | ||
|
4346386610 | ||
|
ca4856a0e8 | ||
|
e5675151b9 | ||
|
fd940b8c6a | ||
|
1baee98ead | ||
|
613895d6be | ||
|
f262fd8a40 | ||
|
c2a084ed4a | ||
|
ae41d45da6 | ||
|
5f8e2cbd4a | ||
|
20abd5cebf | ||
|
4c4604d63e | ||
|
4cab868899 | ||
|
4475a229d6 | ||
|
a3a740c14a | ||
|
59615a655b | ||
|
fe49f2618f | ||
|
7fe2a1bbce | ||
|
7c293d37f7 | ||
|
c9d958d309 | ||
|
a42ae2f3d9 | ||
|
37f14b0195 | ||
|
13a08b0d54 | ||
|
75546ef831 | ||
|
6608d39c59 | ||
|
6549b66ad2 | ||
|
610c66e774 | ||
|
7cfec4e8ce | ||
|
fb78ed1812 | ||
|
5a4fdeb1cd | ||
|
b57849e1cb | ||
|
a8d3d1483f | ||
|
006fc7e9de | ||
|
c9e6d3a84c | ||
|
37a7eadf17 | ||
|
d0c3c9172b | ||
|
81e3cab00d | ||
|
61b7a09c70 | ||
|
8932cecec7 | ||
|
bf3508ffb2 | ||
|
6168952d6f | ||
|
b7b5df13aa | ||
|
7ce721f562 | ||
|
b66ffa670e | ||
|
0c5abee284 | ||
|
001368d3df | ||
|
00e7e58bf3 | ||
|
271b6f4a1c | ||
|
3297a0f327 | ||
|
48b255897e | ||
|
feeb3c10df | ||
|
b17af61367 | ||
|
d97b3dfdf3 | ||
|
5c9cfb4fe8 | ||
|
1fd05a157f | ||
|
1c843a8124 | ||
|
b2fe91385b | ||
|
2abf5ca891 | ||
|
f5a0a0ba86 | ||
|
d6f940110b | ||
|
020eb85093 | ||
|
944e43d4bf | ||
|
4e5828e646 | ||
|
e26eb7a719 | ||
|
faad4c8c26 | ||
|
88558eae6e | ||
|
0d4a907335 | ||
|
97094b2a1c | ||
|
ced5281b73 | ||
|
a9a7bdc290 | ||
|
410d16eaf6 | ||
|
1c1e2230f3 | ||
|
176359afae | ||
|
8138afbfad | ||
|
cd862083a5 | ||
|
2187a2a31d | ||
|
2dee289750 | ||
|
bb1416420d | ||
|
8bef2e9e95 | ||
|
ec23632f3e | ||
|
ec98b41c85 | ||
|
46f0fb1c64 | ||
|
f2bf01223c | ||
|
70e8476264 | ||
|
a5a00d7b14 | ||
|
e405544ea0 | ||
|
a2393501c5 | ||
|
b58bd8d725 | ||
|
0a4073449b | ||
|
c3f6f969c5 | ||
|
53fa132d13 | ||
|
9d55fff81d | ||
|
ca80ca981e | ||
|
802d4dfc3b | ||
|
f1ed034600 | ||
|
9ff0750d4d | ||
|
6eb3ea2b21 | ||
|
4d34a15363 | ||
|
822e4bc220 | ||
|
6e0905d3ca | ||
|
10f32ca817 | ||
|
aaa1b6ca39 | ||
|
358d2bc038 | ||
|
add6863185 | ||
|
b251fc999a | ||
|
faff1f7370 | ||
|
054f4fac74 | ||
|
7a87dea085 | ||
|
f00d32ee7d | ||
|
e986e7ba66 | ||
|
6b00e5e66c | ||
|
4b96ea1273 | ||
|
d250eee556 | ||
|
9f8fa45dc7 | ||
|
2bd82ac249 | ||
|
a7041be6f2 | ||
|
5f7238eec6 | ||
|
f0f279ec04 | ||
|
a8dc2c6f9b | ||
|
24cdb2d489 | ||
|
609a5780a2 | ||
|
4911f4599b | ||
|
00d936488b | ||
|
49faea2023 | ||
|
d46364c834 | ||
|
237cfa0315 | ||
|
33806ad44c | ||
|
12fdc1232d | ||
|
b4c1c63fd2 | ||
|
1a3429655c | ||
|
23beab4125 | ||
|
e938527f66 | ||
|
35fc85a157 | ||
|
aed9e2efdf | ||
|
94ef3dc2b2 | ||
|
9b09806593 | ||
|
f9741e7488 | ||
|
c7f6543502 | ||
|
1819408b46 | ||
|
6be8467cdc | ||
|
529a58a066 | ||
|
00a8433338 | ||
|
aa13ba758b | ||
|
5c1e0522b7 | ||
|
504f8cb137 | ||
|
f9cfee9e2c | ||
|
0adcb50f58 | ||
|
ba87048db5 | ||
|
80bee9a215 | ||
|
612a94b31e | ||
|
e1deaa58d8 | ||
|
5f52efe465 | ||
|
a9950c5c9f | ||
|
fe88da4e64 | ||
|
9b0e02dbb7 | ||
|
d6aa1fb501 | ||
|
3ac4575d94 | ||
|
05ba07daf8 | ||
|
454018ef42 | ||
|
04342c7b5d | ||
|
b601bcfcb8 | ||
|
84a148ddae | ||
|
8df55dd015 | ||
|
54c94b717c | ||
|
cc417cc001 | ||
|
07f9e48579 | ||
|
71125d5c3b | ||
|
47ef222dab | ||
|
a2baa1d8e0 | ||
|
80eaa28552 | ||
|
b9bddbb7a0 | ||
|
1bd17f11f2 | ||
|
409a10a8b5 | ||
|
3c24b1891b | ||
|
a17b017e01 | ||
|
ea43d7fd29 | ||
|
df53a07fc5 | ||
|
942403c52c | ||
|
d349333a43 | ||
|
89f73d3eda | ||
|
fef2e32a3c | ||
|
467b64b6e7 | ||
|
296d0d9d31 | ||
|
04479b13c3 | ||
|
33da049717 | ||
|
141954d205 | ||
|
cc57db02a3 | ||
|
0c03b92373 | ||
|
5d87006006 | ||
|
df9a01f534 | ||
|
20f58ea1c1 | ||
|
3a9597cd32 | ||
|
28e936f6fe | ||
|
a6e1abecfc | ||
|
a7eaae91ae | ||
|
4070833482 | ||
|
5235cd58be | ||
|
759e357bea | ||
|
a57ba7ece0 | ||
|
6c6106a292 | ||
|
6bbfd039b1 | ||
|
e690b55b18 | ||
|
930b81868d | ||
|
3bd6a9df6d | ||
|
28b0ebe83a | ||
|
e27b578c27 | ||
|
e3e9d65bae | ||
|
9d9543c46b | ||
|
13ff627b09 | ||
|
ad156b1fce | ||
|
b62e4a1e48 | ||
|
a1cd76e244 | ||
|
f3eba1fb48 | ||
|
570dedd712 | ||
|
58c3a06a14 | ||
|
483ded0e1c | ||
|
064fdf020f | ||
|
49d123c7f9 | ||
|
2384df11ed | ||
|
735d604cd1 |
282 changed files with 17077 additions and 5029 deletions
3
.github/CONTRIBUTING.md
vendored
Normal file
3
.github/CONTRIBUTING.md
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Our contribution policy can be found at [async.rs/contribute][policy].
|
||||||
|
|
||||||
|
[policy]: https://async.rs/contribute/
|
146
.github/workflows/ci.yml
vendored
146
.github/workflows/ci.yml
vendored
|
@ -4,9 +4,13 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
|
- master
|
||||||
- staging
|
- staging
|
||||||
- trying
|
- trying
|
||||||
|
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: -Dwarnings
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_test:
|
build_and_test:
|
||||||
name: Build and test
|
name: Build and test
|
||||||
|
@ -14,7 +18,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||||
rust: [nightly]
|
rust: [nightly, beta, stable]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
|
@ -25,23 +29,130 @@ jobs:
|
||||||
toolchain: ${{ matrix.rust }}
|
toolchain: ${{ matrix.rust }}
|
||||||
override: true
|
override: true
|
||||||
|
|
||||||
|
- name: Cache cargo registry
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/registry
|
||||||
|
key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-registry-${{ hashFiles('**/Cargo.toml') }}
|
||||||
|
|
||||||
|
- name: Cache cargo index
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/git
|
||||||
|
key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-index-${{ hashFiles('**/Cargo.toml') }}
|
||||||
|
|
||||||
|
- name: Cache cargo build
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: target
|
||||||
|
key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-build-target-${{ hashFiles('**/Cargo.toml') }}
|
||||||
|
|
||||||
- name: check
|
- name: check
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: check
|
command: check
|
||||||
args: --all --benches --bins --examples --tests
|
args: --all --bins --tests
|
||||||
|
|
||||||
- name: check unstable
|
- name: check unstable
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: check
|
command: check
|
||||||
args: --features unstable --all --benches --bins --examples --tests
|
args: --features unstable --all --bins --examples --tests
|
||||||
|
|
||||||
|
- name: check wasm
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
target: wasm32-unknown-unknown
|
||||||
|
override: true
|
||||||
|
args: --features unstable --all --bins --tests
|
||||||
|
|
||||||
|
- name: check bench
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
if: matrix.rust == 'nightly'
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --benches
|
||||||
|
|
||||||
|
- name: check std only
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --no-default-features --features std
|
||||||
|
|
||||||
|
- name: check attributes
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --features attributes
|
||||||
|
|
||||||
- name: tests
|
- name: tests
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: --all --doc --features unstable
|
args: --all --features "unstable attributes"
|
||||||
|
|
||||||
|
build__with_no_std:
|
||||||
|
name: Build with no-std
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
|
- name: setup
|
||||||
|
run: |
|
||||||
|
rustup default nightly
|
||||||
|
rustup target add thumbv7m-none-eabi
|
||||||
|
|
||||||
|
- name: check no_std
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --no-default-features --features alloc --target thumbv7m-none-eabi -Z avoid-dev-deps
|
||||||
|
|
||||||
|
check_tokio_02_feature:
|
||||||
|
name: Check tokio02 feature
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: check tokio02
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --all --features tokio02
|
||||||
|
|
||||||
|
cross:
|
||||||
|
name: Cross compile
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target:
|
||||||
|
- i686-unknown-linux-gnu
|
||||||
|
- powerpc-unknown-linux-gnu
|
||||||
|
- powerpc64-unknown-linux-gnu
|
||||||
|
- mips-unknown-linux-gnu
|
||||||
|
- arm-linux-androideabi
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
|
- name: Install nightly
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Install cross
|
||||||
|
run: cargo install cross
|
||||||
|
|
||||||
|
- name: check
|
||||||
|
run: cross check --all --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: check unstable
|
||||||
|
run: cross check --all --features unstable --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
run: cross test --all --features unstable --target ${{ matrix.target }}
|
||||||
|
|
||||||
check_fmt_and_docs:
|
check_fmt_and_docs:
|
||||||
name: Checking fmt and docs
|
name: Checking fmt and docs
|
||||||
|
@ -49,15 +160,12 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
- id: component
|
|
||||||
uses: actions-rs/components-nightly@v1
|
|
||||||
with:
|
|
||||||
component: rustfmt
|
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ steps.component.outputs.toolchain }}
|
profile: minimal
|
||||||
|
toolchain: nightly
|
||||||
override: true
|
override: true
|
||||||
|
components: rustfmt
|
||||||
|
|
||||||
- name: setup
|
- name: setup
|
||||||
run: |
|
run: |
|
||||||
|
@ -73,21 +181,3 @@ jobs:
|
||||||
|
|
||||||
- name: Docs
|
- name: Docs
|
||||||
run: cargo doc --features docs
|
run: cargo doc --features docs
|
||||||
|
|
||||||
clippy_check:
|
|
||||||
name: Clippy check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- id: component
|
|
||||||
uses: actions-rs/components-nightly@v1
|
|
||||||
with:
|
|
||||||
component: clippy
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: ${{ steps.component.outputs.toolchain }}
|
|
||||||
override: true
|
|
||||||
- run: rustup component add clippy
|
|
||||||
- uses: actions-rs/clippy-check@v1
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
68
.travis.yml
68
.travis.yml
|
@ -1,68 +0,0 @@
|
||||||
language: rust
|
|
||||||
|
|
||||||
env:
|
|
||||||
- RUSTFLAGS="-D warnings"
|
|
||||||
|
|
||||||
# Cache the whole `~/.cargo` directory to keep `~/cargo/.crates.toml`.
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- /home/travis/.cargo
|
|
||||||
|
|
||||||
# Don't cache the cargo registry because it's too big.
|
|
||||||
before_cache:
|
|
||||||
- rm -rf /home/travis/.cargo/registry
|
|
||||||
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
- trying
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
fast_finish: true
|
|
||||||
include:
|
|
||||||
- rust: nightly
|
|
||||||
os: linux
|
|
||||||
|
|
||||||
- rust: nightly
|
|
||||||
os: osx
|
|
||||||
osx_image: xcode9.2
|
|
||||||
|
|
||||||
- rust: nightly-x86_64-pc-windows-msvc
|
|
||||||
os: windows
|
|
||||||
|
|
||||||
- name: fmt
|
|
||||||
rust: nightly
|
|
||||||
os: linux
|
|
||||||
before_script: |
|
|
||||||
if ! rustup component add rustfmt; then
|
|
||||||
target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/rustfmt`;
|
|
||||||
echo "'rustfmt' is unavailable on the toolchain 'nightly', use the toolchain 'nightly-$target' instead";
|
|
||||||
rustup toolchain install nightly-$target;
|
|
||||||
rustup default nightly-$target;
|
|
||||||
rustup component add rustfmt;
|
|
||||||
fi
|
|
||||||
script:
|
|
||||||
- cargo fmt --all -- --check
|
|
||||||
|
|
||||||
- name: docs
|
|
||||||
rust: nightly
|
|
||||||
os: linux
|
|
||||||
script:
|
|
||||||
- cargo doc --features docs
|
|
||||||
|
|
||||||
- name: book
|
|
||||||
rust: nightly
|
|
||||||
os: linux
|
|
||||||
before_script:
|
|
||||||
- test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh
|
|
||||||
- cargo build # to find 'extern crate async_std' by `mdbook test`
|
|
||||||
script:
|
|
||||||
- mdbook build docs
|
|
||||||
- mdbook test -L ./target/debug/deps docs
|
|
||||||
|
|
||||||
script:
|
|
||||||
- cargo check --all --benches --bins --examples --tests
|
|
||||||
- cargo check --features unstable --all --benches --bins --examples --tests
|
|
||||||
- cargo test --all --doc --features unstable
|
|
580
CHANGELOG.md
580
CHANGELOG.md
|
@ -7,6 +7,569 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
# [1.6.2] - 2020-06-19
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Add `UdpSocket::peer_addr` ([#816](https://github.com/async-rs/async-std/pull/816))
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Ensure the reactor is running for sockets and timers ([#819](https://github.com/async-rs/async-std/pull/819)).
|
||||||
|
- Avoid excessive polling in `flatten` and `flat_map` ([#701](https://github.com/async-rs/async-std/pull/701))
|
||||||
|
|
||||||
|
|
||||||
|
# [1.6.1] - 2020-06-11
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `tokio02` feature flag, to allow compatability usage with tokio@0.2 ([#804](https://github.com/async-rs/async-std/pull/804)).
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Removed unstable `stdio` lock methods, due to their unsoundness ([#807](https://github.com/async-rs/async-std/pull/807)).
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Fixed wrong slice index for file reading ([#802](https://github.com/async-rs/async-std/pull/802)).
|
||||||
|
- Fixed recursive calls to `block_on` ([#799](https://github.com/async-rs/async-std/pull/799)) and ([#809](https://github.com/async-rs/async-std/pull/809)).
|
||||||
|
- Remove `default` feature requirement for the `unstable` feature ([#806](https://github.com/async-rs/async-std/pull/806)).
|
||||||
|
|
||||||
|
# [1.6.0] - 2020-05-22
|
||||||
|
|
||||||
|
See `1.6.0-beta.1` and `1.6.0-beta.2`.
|
||||||
|
|
||||||
|
# [1.6.0-beta.2] - 2020-05-19
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added an environment variable to configure the thread pool size of the runtime. ([#774](https://github.com/async-rs/async-std/pull/774))
|
||||||
|
- Implement `Clone` for `UnixStream` ([#772](https://github.com/async-rs/async-std/pull/772))
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- For `wasm`, switched underlying `Timer` implementation to [`futures-timer`](https://github.com/async-rs/futures-timer). ([#776](https://github.com/async-rs/async-std/pull/776))
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Use `smol::block_on` to handle drop of `File`, avoiding nested executor panic. ([#768](https://github.com/async-rs/async-std/pull/768))
|
||||||
|
|
||||||
|
# [1.6.0-beta.1] - 2020-05-07
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `task::spawn_local`. ([#757](https://github.com/async-rs/async-std/pull/757))
|
||||||
|
- Added out of the box support for `wasm`. ([#757](https://github.com/async-rs/async-std/pull/757))
|
||||||
|
- Added `JoinHandle::cancel` ([#757](https://github.com/async-rs/async-std/pull/757))
|
||||||
|
- Added `sync::Condvar` ([#369](https://github.com/async-rs/async-std/pull/369))
|
||||||
|
- Added `sync::Sender::try_send` and `sync::Receiver::try_recv` ([#585](https://github.com/async-rs/async-std/pull/585))
|
||||||
|
- Added `no_std` support for `task`, `future` and `stream` ([#680](https://github.com/async-rs/async-std/pull/680))
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Switched underlying runtime to [`smol`](https://github.com/stjepang/smol/). ([#757](https://github.com/async-rs/async-std/pull/757))
|
||||||
|
- Switched implementation of `sync::Barrier` to use `sync::Condvar` like `std` does. ([#581](https://github.com/async-rs/async-std/pull/581))
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Allow compilation on 32 bit targets, by using `AtomicUsize` for `TaskId`. ([#756](https://github.com/async-rs/async-std/pull/756))
|
||||||
|
|
||||||
|
# [1.5.0] - 2020-02-03
|
||||||
|
|
||||||
|
[API Documentation](https://docs.rs/async-std/1.5.0/async-std)
|
||||||
|
|
||||||
|
This patch includes various quality of life improvements to async-std.
|
||||||
|
Including improved performance, stability, and the addition of various
|
||||||
|
`Clone` impls that replace the use of `Arc` in many cases.
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added links to various ecosystem projects from the README ([#660](https://github.com/async-rs/async-std/pull/660))
|
||||||
|
- Added an example on `FromStream` for `Result<T, E>` ([#643](https://github.com/async-rs/async-std/pull/643))
|
||||||
|
- Added `stream::pending` as "unstable" ([#615](https://github.com/async-rs/async-std/pull/615))
|
||||||
|
- Added an example of `stream::timeout` to document the error flow ([#675](https://github.com/async-rs/async-std/pull/675))
|
||||||
|
- Implement `Clone` for `DirEntry` ([#682](https://github.com/async-rs/async-std/pull/682))
|
||||||
|
- Implement `Clone` for `TcpStream` ([#689](https://github.com/async-rs/async-std/pull/689))
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Removed internal comment on `stream::Interval` ([#645](https://github.com/async-rs/async-std/pull/645))
|
||||||
|
- The "unstable" feature can now be used without requiring the "default" feature ([#647](https://github.com/async-rs/async-std/pull/647))
|
||||||
|
- Removed unnecessary trait bound on `stream::FlatMap` ([#651](https://github.com/async-rs/async-std/pull/651))
|
||||||
|
- Updated the "broadcaster" dependency used by "unstable" to `1.0.0` ([#681](https://github.com/async-rs/async-std/pull/681))
|
||||||
|
- Updated `async-task` to 1.2.1 ([#676](https://github.com/async-rs/async-std/pull/676))
|
||||||
|
- `task::block_on` now parks after a single poll, improving performance in many cases ([#684](https://github.com/async-rs/async-std/pull/684))
|
||||||
|
- Improved reading flow of the "client" part of the async-std tutorial ([#550](https://github.com/async-rs/async-std/pull/550))
|
||||||
|
- Use `take_while` instead of `scan` in `impl` of `Product`, `Sum` and `FromStream` ([#667](https://github.com/async-rs/async-std/pull/667))
|
||||||
|
- `TcpStream::connect` no longer uses a thread from the threadpool, improving performance ([#687](https://github.com/async-rs/async-std/pull/687))
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Fixed crate documentation typo ([#655](https://github.com/async-rs/async-std/pull/655))
|
||||||
|
- Fixed documentation for `UdpSocket::recv` ([#648](https://github.com/async-rs/async-std/pull/648))
|
||||||
|
- Fixed documentation for `UdpSocket::send` ([#671](https://github.com/async-rs/async-std/pull/671))
|
||||||
|
- Fixed typo in stream documentation ([#650](https://github.com/async-rs/async-std/pull/650))
|
||||||
|
- Fixed typo on `sync::JoinHandle` documentation ([#659](https://github.com/async-rs/async-std/pull/659))
|
||||||
|
- Removed use of `std::error::Error::description` which failed CI ([#661](https://github.com/async-rs/async-std/pull/662))
|
||||||
|
- Removed the use of rustfmt's unstable `format_code_in_doc_comments` option which failed CI ([#685](https://github.com/async-rs/async-std/pull/685))
|
||||||
|
- Fixed a code typo in the `task::sleep` example ([#688](https://github.com/async-rs/async-std/pull/688))
|
||||||
|
|
||||||
|
# [1.4.0] - 2019-12-20
|
||||||
|
|
||||||
|
[API Documentation](https://docs.rs/async-std/1.4.0/async-std)
|
||||||
|
|
||||||
|
This patch adds `Future::timeout`, providing a method counterpart to the
|
||||||
|
`future::timeout` free function. And includes several bug fixes around missing
|
||||||
|
APIs. Notably we're not shipping our new executor yet, first announced [on our
|
||||||
|
blog](https://async.rs/blog/stop-worrying-about-blocking-the-new-async-std-runtime/).
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::future;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
let fut = future::pending::<()>(); // This future will never resolve.
|
||||||
|
let res = fut.timeout(Duration::from_millis(100)).await;
|
||||||
|
assert!(res.is_err()); // The future timed out, returning an err.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `Future::timeout` as "unstable" [(#600)](https://github.com/async-rs/async-std/pull/600)
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Fixed a doc test and enabled it on CI [(#597)](https://github.com/async-rs/async-std/pull/597)
|
||||||
|
- Fixed a rendering issue with the `stream` submodule documentation [(#621)](https://github.com/async-rs/async-std/pull/621)
|
||||||
|
- `Write::write_fmt`'s future is now correctly marked as `#[must_use]` [(#628)](https://github.com/async-rs/async-std/pull/628)
|
||||||
|
- Fixed the missing `io::Bytes` export [(#633)](https://github.com/async-rs/async-std/pull/633)
|
||||||
|
- Fixed the missing `io::Chain` export [(#633)](https://github.com/async-rs/async-std/pull/633)
|
||||||
|
- Fixed the missing `io::Take` export [(#633)](https://github.com/async-rs/async-std/pull/633)
|
||||||
|
|
||||||
|
# [1.3.0] - 2019-12-12
|
||||||
|
|
||||||
|
[API Documentation](https://docs.rs/async-std/1.3.0/async-std)
|
||||||
|
|
||||||
|
This patch introduces `Stream::delay`, more methods on `DoubleEndedStream`,
|
||||||
|
and improves compile times. `Stream::delay` is a new API that's similar to
|
||||||
|
[`task::sleep`](https://docs.rs/async-std/1.2.0/async_std/task/fn.sleep.html),
|
||||||
|
but can be passed as part of as stream, rather than as a separate block. This is
|
||||||
|
useful for examples, or when manually debugging race conditions.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let start = Instant::now();
|
||||||
|
let mut s = stream::from_iter(vec![0u8, 1]).delay(Duration::from_millis(200));
|
||||||
|
|
||||||
|
// The first time will take more than 200ms due to delay.
|
||||||
|
s.next().await;
|
||||||
|
assert!(start.elapsed().as_millis() >= 200);
|
||||||
|
|
||||||
|
// There will be no delay after the first time.
|
||||||
|
s.next().await;
|
||||||
|
assert!(start.elapsed().as_millis() <= 210);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `Stream::delay` as "unstable" [(#309)](https://github.com/async-rs/async-std/pull/309)
|
||||||
|
- Added `DoubleEndedStream::next_back` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562)
|
||||||
|
- Added `DoubleEndedStream::nth_back` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562)
|
||||||
|
- Added `DoubleEndedStream::rfind` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562)
|
||||||
|
- Added `DoubleEndedStream::rfold` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562)
|
||||||
|
- Added `DoubleEndedStream::try_rfold` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562)
|
||||||
|
- `stream::Once` now implements `DoubleEndedStream` [(#562)](https://github.com/async-rs/async-std/pull/562)
|
||||||
|
- `stream::FromIter` now implements `DoubleEndedStream` [(#562)](https://github.com/async-rs/async-std/pull/562)
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Removed our dependency on `async-macros`, speeding up compilation [(#610)](https://github.com/async-rs/async-std/pull/610)
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Fixed a link in the task docs [(#598)](https://github.com/async-rs/async-std/pull/598)
|
||||||
|
- Fixed the `UdpSocket::recv` example [(#603)](https://github.com/async-rs/async-std/pull/603)
|
||||||
|
- Fixed a link to `task::block_on` [(#608)](https://github.com/async-rs/async-std/pull/608)
|
||||||
|
- Fixed an incorrect API mention in `task::Builder` [(#612)](https://github.com/async-rs/async-std/pull/612)
|
||||||
|
- Fixed leftover mentions of `futures-preview` [(#595)](https://github.com/async-rs/async-std/pull/595)
|
||||||
|
- Fixed a typo in the tutorial [(#614)](https://github.com/async-rs/async-std/pull/614)
|
||||||
|
- `<TcpStream as Write>::poll_close` now closes the write half of the stream [(#618)](https://github.com/async-rs/async-std/pull/618)
|
||||||
|
|
||||||
|
# [1.2.0] - 2019-11-27
|
||||||
|
|
||||||
|
[API Documentation](https://docs.rs/async-std/1.2.0/async-std)
|
||||||
|
|
||||||
|
This patch includes some minor quality-of-life improvements, introduces a
|
||||||
|
new `Stream::unzip` API, and adds verbose errors to our networking types.
|
||||||
|
|
||||||
|
This means if you can't connect to a socket, you'll never have to wonder again
|
||||||
|
*which* address it was you couldn't connect to, instead of having to go through
|
||||||
|
the motions to debug what the address was.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Unzip a stream of tuples into two collections:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::stream;
|
||||||
|
|
||||||
|
let s = stream::from_iter(vec![(1,2), (3,4)]);
|
||||||
|
|
||||||
|
let (left, right): (Vec<_>, Vec<_>) = s.unzip().await;
|
||||||
|
|
||||||
|
assert_eq!(left, [1, 3]);
|
||||||
|
assert_eq!(right, [2, 4]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `Stream::unzip` as "unstable".
|
||||||
|
- Added verbose errors to the networking types.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Enabled CI on master branch.
|
||||||
|
- `Future::join` and `Future::try_join` can now join futures with different
|
||||||
|
output types.
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Fixed the docs and `Debug` output of `BufWriter`.
|
||||||
|
- Fixed a bug in `Stream::throttle` that made it consume too much CPU.
|
||||||
|
|
||||||
|
# [1.1.0] - 2019-11-21
|
||||||
|
|
||||||
|
[API Documentation](https://docs.rs/async-std/1.1.0/async-std)
|
||||||
|
|
||||||
|
This patch introduces a faster scheduler algorithm, `Stream::throttle`, and
|
||||||
|
stabilizes `task::yield_now`. Additionally we're introducing several more stream
|
||||||
|
APIs, bringing us to almost complete parity with the standard library.
|
||||||
|
|
||||||
|
Furthermore our `path` submodule now returns more context in errors. So if
|
||||||
|
opening a file fails, async-std will tell you *which* file was failed to open,
|
||||||
|
making it easier to write and debug programs.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
let mut s = stream::interval(Duration::from_millis(5))
|
||||||
|
.throttle(Duration::from_millis(10))
|
||||||
|
.take(2);
|
||||||
|
|
||||||
|
s.next().await;
|
||||||
|
assert!(start.elapsed().as_millis() >= 5);
|
||||||
|
|
||||||
|
s.next().await;
|
||||||
|
assert!(start.elapsed().as_millis() >= 15);
|
||||||
|
|
||||||
|
s.next().await;
|
||||||
|
assert!(start.elapsed().as_millis() >= 25);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `Stream::throttle` as "unstable".
|
||||||
|
- Added `Stream::count` as "unstable".
|
||||||
|
- Added `Stream::max` as "unstable".
|
||||||
|
- Added `Stream::successors` as "unstable".
|
||||||
|
- Added `Stream::by_ref` as "unstable".
|
||||||
|
- Added `Stream::partition` as "unstable".
|
||||||
|
- Added contextual errors to the `path` submodule.
|
||||||
|
- Added `os::windows::symlink_dir` as "unstable".
|
||||||
|
- Added `os::windows::symlink_file` as "unstable".
|
||||||
|
- Stabilized `task::yield_now`.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- We now ignore seek errors when rolling back failed `read` calls on `File`.
|
||||||
|
- Fixed a bug where `Stream::max_by_key` was returning the wrong result.
|
||||||
|
- Fixed a bug where `Stream::min_by_key` was returning the wrong result.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Applied various fixes to the tutorial.
|
||||||
|
- Fixed an issue with Clippy.
|
||||||
|
- Optimized an internal code generation macro, improving compilation speeds.
|
||||||
|
- Removed an `Unpin` bound from `stream::Once`.
|
||||||
|
- Removed various extra internal uses of `pin_mut!`.
|
||||||
|
- Simplified `Stream::any` and `Stream::all`'s internals.
|
||||||
|
- The `surf` example is now enabled again.
|
||||||
|
- Tweaked some streams internals.
|
||||||
|
- Updated `futures-timer` to 2.0.0, improving compilation speed.
|
||||||
|
- Upgraded `async-macros` to 2.0.0.
|
||||||
|
- `Stream::merge` now uses randomized ordering to reduce overall latency.
|
||||||
|
- The scheduler is now more efficient by keeping a slot for the next task to
|
||||||
|
run. This is similar to Go's scheduler, and Tokio's scheduler.
|
||||||
|
- Fixed the documentation of the `channel` types to link back to the `channel`
|
||||||
|
function.
|
||||||
|
|
||||||
|
# [1.0.1] - 2019-11-12
|
||||||
|
|
||||||
|
[API Documentation](https://docs.rs/async-std/1.0.1/async-std)
|
||||||
|
|
||||||
|
We were seeing a regression in our fs performance, caused by too many
|
||||||
|
long-running tasks. This patch fixes that regression by being more proactive
|
||||||
|
about closing down idle threads.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Improved thread startup/shutdown algorithm in `task::spawn_blocking`.
|
||||||
|
- Fixed a typo in the tutorial.
|
||||||
|
|
||||||
|
# [1.0.0] - 2019-11-11
|
||||||
|
|
||||||
|
[API Documentation](https://docs.rs/async-std/1.0.0/async-std)
|
||||||
|
|
||||||
|
This release marks the `1.0.0` release of async-std; a major milestone for our
|
||||||
|
development. This release itself mostly includes quality of life improvements
|
||||||
|
for all of modules, including more consistent API bounds for a lot of our
|
||||||
|
submodules.
|
||||||
|
|
||||||
|
The biggest change is that we're now using the full semver range,
|
||||||
|
`major.minor.patch`, and any breaking changes to our "stable" APIs will require
|
||||||
|
an update of the `major` number.
|
||||||
|
|
||||||
|
We're excited we've hit this milestone together with you all. Thank you!
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `Future::join` as "unstable", replacing `future::join!`.
|
||||||
|
- Added `Future::try_join` as "unstable", replacing `future::try_join!`.
|
||||||
|
- Enabled `stable` and `beta` channel testing on CI.
|
||||||
|
- Implemented `FromIterator` and `Extend` for `PathBuf`.
|
||||||
|
- Implemented `FromStream` for `PathBuf`.
|
||||||
|
- Loosened the trait bounds of `io::copy` on "unstable".
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Added a `Sync` bound to `RwLock`, resolving a memory safety issue.
|
||||||
|
- Fixed a bug in `Stream::take_while` where it could continue after it should've
|
||||||
|
ended.
|
||||||
|
- Fixed a bug where our `attributes` Cargo feature wasn't working as intended.
|
||||||
|
- Improved documentation of `Stream::merge`, documenting ordering guarantees.
|
||||||
|
- Update doc imports in examples to prefer async-std's types.
|
||||||
|
- Various quality of life improvements to the `future` submodule.
|
||||||
|
- Various quality of life improvements to the `path` submodule.
|
||||||
|
- Various quality of life improvements to the `stream` submodule.
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
|
||||||
|
- Removed `future::join!` in favor of `Future::join`.
|
||||||
|
- Removed `future::try_join!` in favor of `Future::try_join`.
|
||||||
|
|
||||||
|
# [0.99.12] - 2019-11-07
|
||||||
|
|
||||||
|
[API Documentation](https://docs.rs/async-std/0.99.12/async-std)
|
||||||
|
|
||||||
|
This patch upgrades us to `futures` 0.3, support for `async/await` on Rust
|
||||||
|
Stable, performance improvements, and brand new module-level documentation.
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `Future::flatten` as "unstable".
|
||||||
|
- Added `Future::race` as "unstable" (replaces `future::select!`).
|
||||||
|
- Added `Future::try_race` as "unstable" (replaces `future::try_select!`).
|
||||||
|
- Added `Stderr::lock` as "unstable".
|
||||||
|
- Added `Stdin::lock` as "unstable".
|
||||||
|
- Added `Stdout::lock` as "unstable".
|
||||||
|
- Added `Stream::copied` as "unstable".
|
||||||
|
- Added `Stream::eq` as "unstable".
|
||||||
|
- Added `Stream::max_by_key` as "unstable".
|
||||||
|
- Added `Stream::min` as "unstable".
|
||||||
|
- Added `Stream::ne` as "unstable".
|
||||||
|
- Added `Stream::position` as "unstable".
|
||||||
|
- Added `StreamExt` and `FutureExt` as enumerable in the `prelude`.
|
||||||
|
- Added `TcpListener` and `TcpStream` integration tests.
|
||||||
|
- Added `stream::from_iter`.
|
||||||
|
- Added `sync::WakerSet` for internal use.
|
||||||
|
- Added an example to handle both `IP v4` and `IP v6` connections.
|
||||||
|
- Added the `default` Cargo feature.
|
||||||
|
- Added the `attributes` Cargo feature.
|
||||||
|
- Added the `std` Cargo feature.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Fixed a bug in the blocking threadpool where it didn't spawn more than one thread.
|
||||||
|
- Fixed a bug with `Stream::merge` where sometimes it ended too soon.
|
||||||
|
- Fixed a bug with our GitHub actions setup.
|
||||||
|
- Fixed an issue where our channels could spuriously deadlock.
|
||||||
|
- Refactored the `task` module.
|
||||||
|
- Removed a deprecated GitHub action.
|
||||||
|
- Replaced `futures-preview` with `futures`.
|
||||||
|
- Replaced `lazy_static` with `once_cell`.
|
||||||
|
- Replaced all uses of `VecDequeue` in the examples with `stream::from_iter`.
|
||||||
|
- Simplified `sync::RwLock` using the internal `sync::WakerSet` type.
|
||||||
|
- Updated the `path` submodule documentation to match std.
|
||||||
|
- Updated the mod-level documentation to match std.
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
|
||||||
|
- Removed `future::select!` (replaced by `Future::race`).
|
||||||
|
- Removed `future::try_select!` (replaced by `Future::try_race`).
|
||||||
|
|
||||||
|
# [0.99.11] - 2019-10-29
|
||||||
|
|
||||||
|
This patch introduces `async_std::sync::channel`, a novel asynchronous port of
|
||||||
|
the ultra-fast Crossbeam channels. This has been one of the most anticipated
|
||||||
|
features for async-std, and we're excited to be providing a first version of
|
||||||
|
this!
|
||||||
|
|
||||||
|
In addition to channels, this patch has the regular list of new methods, types,
|
||||||
|
and doc fixes.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
__Send and receive items from a channel__
|
||||||
|
```rust
|
||||||
|
// Create a bounded channel with a max-size of 1
|
||||||
|
let (s, r) = channel(1);
|
||||||
|
|
||||||
|
// This call returns immediately because there is enough space in the channel.
|
||||||
|
s.send(1).await;
|
||||||
|
|
||||||
|
task::spawn(async move {
|
||||||
|
// This call blocks the current task because the channel is full.
|
||||||
|
// It will be able to complete only after the first message is received.
|
||||||
|
s.send(2).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Receive items from the channel
|
||||||
|
task::sleep(Duration::from_secs(1)).await;
|
||||||
|
assert_eq!(r.recv().await, Some(1));
|
||||||
|
assert_eq!(r.recv().await, Some(2));
|
||||||
|
```
|
||||||
|
|
||||||
|
## Added
|
||||||
|
- Added `Future::delay` as "unstable"
|
||||||
|
- Added `Stream::flat_map` as "unstable"
|
||||||
|
- Added `Stream::flatten` as "unstable"
|
||||||
|
- Added `Stream::product` as "unstable"
|
||||||
|
- Added `Stream::sum` as "unstable"
|
||||||
|
- Added `Stream::min_by_key`
|
||||||
|
- Added `Stream::max_by`
|
||||||
|
- Added `Stream::timeout` as "unstable"
|
||||||
|
- Added `sync::channel` as "unstable".
|
||||||
|
- Added doc links from instantiated structs to the methods that create them.
|
||||||
|
- Implemented `Extend` + `FromStream` for `PathBuf`.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
- Fixed an issue with `block_on` so it works even when nested.
|
||||||
|
- Fixed issues with our Clippy check on CI.
|
||||||
|
- Replaced our uses of `cfg_if` with our own macros, simplifying the codebase.
|
||||||
|
- Updated the homepage link in `Cargo.toml` to point to [async.rs](https://async.rs).
|
||||||
|
- Updated the module-level documentation for `stream` and `sync`.
|
||||||
|
- Various typos and grammar fixes.
|
||||||
|
- Removed redundant file flushes, improving the performance of `File` operations
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
Nothing was removed in this release.
|
||||||
|
|
||||||
|
# [0.99.10] - 2019-10-16
|
||||||
|
|
||||||
|
This patch stabilizes several core concurrency macros, introduces async versions
|
||||||
|
of `Path` and `PathBuf`, and adds almost 100 other commits.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
__Asynchronously read directories from the filesystem__
|
||||||
|
```rust
|
||||||
|
use async_std::fs;
|
||||||
|
use async_std::path::Path;
|
||||||
|
use async_std::prelude::*;
|
||||||
|
|
||||||
|
let path = Path::new("/laputa");
|
||||||
|
let mut dir = fs::read_dir(&path).await.unwrap();
|
||||||
|
while let Some(entry) = dir.next().await {
|
||||||
|
if let Ok(entry) = entry {
|
||||||
|
println!("{:?}", entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
__Cooperatively reschedule the current task on the executor__
|
||||||
|
```rust
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::task;
|
||||||
|
|
||||||
|
task::spawn(async {
|
||||||
|
let x = fibonnacci(1000); // Do expensive work
|
||||||
|
task::yield_now().await; // Allow other tasks to run
|
||||||
|
x + fibonnacci(100) // Do more work
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
__Create an interval stream__
|
||||||
|
```rust
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::stream;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
let mut interval = stream::interval(Duration::from_secs(4));
|
||||||
|
while let Some(_) = interval.next().await {
|
||||||
|
println!("prints every four seconds");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `FutureExt` to the `prelude`, allowing us to extend `Future`
|
||||||
|
- Added `Stream::cmp`
|
||||||
|
- Added `Stream::ge`
|
||||||
|
- Added `Stream::last`
|
||||||
|
- Added `Stream::le`
|
||||||
|
- Added `Stream::lt`
|
||||||
|
- Added `Stream::merge` as "unstable", replacing `stream::join!`
|
||||||
|
- Added `Stream::partial_cmp`
|
||||||
|
- Added `Stream::take_while`
|
||||||
|
- Added `Stream::try_fold`
|
||||||
|
- Added `future::IntoFuture` as "unstable"
|
||||||
|
- Added `io::BufRead::split`
|
||||||
|
- Added `io::Write::write_fmt`
|
||||||
|
- Added `print!`, `println!`, `eprint!`, `eprintln!` macros as "unstable"
|
||||||
|
- Added `process` as "unstable", re-exporting std types only for now
|
||||||
|
- Added `std::net` re-exports to the `net` submodule
|
||||||
|
- Added `std::path::PathBuf` with all associated methods
|
||||||
|
- Added `std::path::Path` with all associated methods
|
||||||
|
- Added `stream::ExactSizeStream` as "unstable"
|
||||||
|
- Added `stream::FusedStream` as "unstable"
|
||||||
|
- Added `stream::Product`
|
||||||
|
- Added `stream::Sum`
|
||||||
|
- Added `stream::from_fn`
|
||||||
|
- Added `stream::interval` as "unstable"
|
||||||
|
- Added `stream::repeat_with`
|
||||||
|
- Added `task::spawn_blocking` as "unstable", replacing `task::blocking`
|
||||||
|
- Added `task::yield_now`
|
||||||
|
- Added `write!` and `writeln!` macros as "unstable"
|
||||||
|
- Stabilized `future::join!` and `future::try_join!`
|
||||||
|
- Stabilized `future::timeout`
|
||||||
|
- Stabilized `path`
|
||||||
|
- Stabilized `task::ready!`
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Fixed `BufWriter::into_inner` so it calls `flush` before yielding
|
||||||
|
- Refactored `io::BufWriter` internals
|
||||||
|
- Refactored `net::ToSocketAddrs` internals
|
||||||
|
- Removed Travis CI entirely
|
||||||
|
- Rewrote the README.md
|
||||||
|
- Stabilized `io::Cursor`
|
||||||
|
- Switched bors over to use GitHub actions
|
||||||
|
- Updated the `io` documentation to match std's `io` docs
|
||||||
|
- Updated the `task` documentation to match std's `thread` docs
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
|
||||||
|
- Removed the "unstable" `stream::join!` in favor of `Stream::merge`
|
||||||
|
- Removed the "unstable" `task::blocking` in favor of `task::spawn_blocking`
|
||||||
|
|
||||||
# [0.99.9] - 2019-10-08
|
# [0.99.9] - 2019-10-08
|
||||||
|
|
||||||
This patch upgrades our `futures-rs` version, allowing us to build on the 1.39
|
This patch upgrades our `futures-rs` version, allowing us to build on the 1.39
|
||||||
|
@ -183,7 +746,22 @@ task::blocking(async {
|
||||||
|
|
||||||
- Initial beta release
|
- Initial beta release
|
||||||
|
|
||||||
[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.9...HEAD
|
[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.2...HEAD
|
||||||
|
[1.6.2]: https://github.com/async-rs/async-std/compare/v1.6.1...v1.6.2
|
||||||
|
[1.6.1]: https://github.com/async-rs/async-std/compare/v1.6.0...v1.6.1
|
||||||
|
[1.6.0]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0
|
||||||
|
[1.6.0-beta.2]: https://github.com/async-rs/async-std/compare/v1.6.0-beta.1...v1.6.0-beta.2
|
||||||
|
[1.6.0-beta.1]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0-beta.1
|
||||||
|
[1.5.0]: https://github.com/async-rs/async-std/compare/v1.4.0...v1.5.0
|
||||||
|
[1.4.0]: https://github.com/async-rs/async-std/compare/v1.3.0...v1.4.0
|
||||||
|
[1.3.0]: https://github.com/async-rs/async-std/compare/v1.2.0...v1.3.0
|
||||||
|
[1.2.0]: https://github.com/async-rs/async-std/compare/v1.1.0...v1.2.0
|
||||||
|
[1.1.0]: https://github.com/async-rs/async-std/compare/v1.0.1...v1.1.0
|
||||||
|
[1.0.1]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1
|
||||||
|
[1.0.0]: https://github.com/async-rs/async-std/compare/v0.99.12...v1.0.0
|
||||||
|
[0.99.12]: https://github.com/async-rs/async-std/compare/v0.99.11...v0.99.12
|
||||||
|
[0.99.11]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11
|
||||||
|
[0.99.10]: https://github.com/async-rs/async-std/compare/v0.99.9...v0.99.10
|
||||||
[0.99.9]: https://github.com/async-rs/async-std/compare/v0.99.8...v0.99.9
|
[0.99.9]: https://github.com/async-rs/async-std/compare/v0.99.8...v0.99.9
|
||||||
[0.99.8]: https://github.com/async-rs/async-std/compare/v0.99.7...v0.99.8
|
[0.99.8]: https://github.com/async-rs/async-std/compare/v0.99.7...v0.99.8
|
||||||
[0.99.7]: https://github.com/async-rs/async-std/compare/v0.99.6...v0.99.7
|
[0.99.7]: https://github.com/async-rs/async-std/compare/v0.99.6...v0.99.7
|
||||||
|
|
108
Cargo.toml
108
Cargo.toml
|
@ -1,15 +1,16 @@
|
||||||
[package]
|
[package]
|
||||||
name = "async-std"
|
name = "async-std"
|
||||||
version = "0.99.9"
|
version = "1.6.2"
|
||||||
authors = [
|
authors = [
|
||||||
"Stjepan Glavina <stjepang@gmail.com>",
|
"Stjepan Glavina <stjepang@gmail.com>",
|
||||||
"Yoshua Wuyts <yoshuawuyts@gmail.com>",
|
"Yoshua Wuyts <yoshuawuyts@gmail.com>",
|
||||||
|
"Friedel Ziegelmayer <me@dignifiedquire.com>",
|
||||||
"Contributors to async-std",
|
"Contributors to async-std",
|
||||||
]
|
]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "Apache-2.0/MIT"
|
license = "Apache-2.0/MIT"
|
||||||
repository = "https://github.com/async-rs/async-std"
|
repository = "https://github.com/async-rs/async-std"
|
||||||
homepage = "https://github.com/async-rs/async-std"
|
homepage = "https://async.rs"
|
||||||
documentation = "https://docs.rs/async-std"
|
documentation = "https://docs.rs/async-std"
|
||||||
description = "Async version of the Rust standard library"
|
description = "Async version of the Rust standard library"
|
||||||
keywords = ["async", "await", "future", "std", "task"]
|
keywords = ["async", "await", "future", "std", "task"]
|
||||||
|
@ -21,35 +22,86 @@ features = ["docs"]
|
||||||
rustdoc-args = ["--cfg", "feature=\"docs\""]
|
rustdoc-args = ["--cfg", "feature=\"docs\""]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
docs = ["broadcaster"]
|
default = [
|
||||||
unstable = ["broadcaster"]
|
"std",
|
||||||
|
"async-task",
|
||||||
|
"kv-log-macro",
|
||||||
|
"log",
|
||||||
|
"num_cpus",
|
||||||
|
"pin-project-lite",
|
||||||
|
"smol",
|
||||||
|
]
|
||||||
|
docs = ["attributes", "unstable", "default"]
|
||||||
|
unstable = [
|
||||||
|
"std",
|
||||||
|
"futures-timer",
|
||||||
|
]
|
||||||
|
attributes = ["async-attributes"]
|
||||||
|
std = [
|
||||||
|
"alloc",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"futures-core/std",
|
||||||
|
"futures-io",
|
||||||
|
"memchr",
|
||||||
|
"once_cell",
|
||||||
|
"pin-utils",
|
||||||
|
"slab",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"futures-channel",
|
||||||
|
"async-mutex",
|
||||||
|
]
|
||||||
|
alloc = [
|
||||||
|
"futures-core/alloc",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
tokio02 = ["smol/tokio02"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-macros = "1.0.0"
|
async-attributes = { version = "1.1.1", optional = true }
|
||||||
async-task = "1.0.0"
|
async-task = { version = "3.0.0", optional = true }
|
||||||
cfg-if = "0.1.9"
|
async-mutex = { version = "1.1.3", optional = true }
|
||||||
crossbeam-channel = "0.3.9"
|
crossbeam-utils = { version = "0.7.2", optional = true }
|
||||||
crossbeam-deque = "0.7.1"
|
futures-core = { version = "0.3.4", optional = true, default-features = false }
|
||||||
futures-core-preview = "=0.3.0-alpha.19"
|
futures-io = { version = "0.3.4", optional = true }
|
||||||
futures-io-preview = "=0.3.0-alpha.19"
|
kv-log-macro = { version = "1.0.6", optional = true }
|
||||||
futures-timer = "0.4.0"
|
log = { version = "0.4.8", features = ["kv_unstable"], optional = true }
|
||||||
lazy_static = "1.4.0"
|
memchr = { version = "2.3.3", optional = true }
|
||||||
log = { version = "0.4.8", features = ["kv_unstable"] }
|
num_cpus = { version = "1.12.0", optional = true }
|
||||||
memchr = "2.2.1"
|
once_cell = { version = "1.3.1", optional = true }
|
||||||
mio = "0.6.19"
|
pin-project-lite = { version = "0.1.4", optional = true }
|
||||||
mio-uds = "0.6.7"
|
pin-utils = { version = "0.1.0-alpha.4", optional = true }
|
||||||
num_cpus = "1.10.1"
|
slab = { version = "0.4.2", optional = true }
|
||||||
pin-utils = "0.1.0-alpha.4"
|
futures-timer = { version = "3.0.2", optional = true }
|
||||||
slab = "0.4.2"
|
|
||||||
kv-log-macro = "1.0.4"
|
# Devdepencency, but they are not allowed to be optional :/
|
||||||
broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] }
|
surf = { version = "1.0.3", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(not(target_os = "unknown"))'.dependencies]
|
||||||
|
smol = { version = "0.1.17", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
futures-timer = { version = "3.0.2", optional = true, features = ["wasm-bindgen"] }
|
||||||
|
wasm-bindgen-futures = { version = "0.4.10", optional = true }
|
||||||
|
futures-channel = { version = "0.3.4", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||||
|
wasm-bindgen-test = "0.3.10"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
femme = "1.2.0"
|
femme = "1.3.0"
|
||||||
# surf = "1.0.2"
|
rand = "0.7.3"
|
||||||
tempdir = "0.3.7"
|
tempdir = "0.3.7"
|
||||||
futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] }
|
futures = "0.3.4"
|
||||||
|
rand_xorshift = "0.2.0"
|
||||||
|
|
||||||
# These are used by the book for examples
|
[[test]]
|
||||||
futures-channel-preview = "=0.3.0-alpha.19"
|
name = "stream"
|
||||||
futures-util-preview = "=0.3.0-alpha.19"
|
required-features = ["unstable"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "tcp-ipv4-and-6-echo"
|
||||||
|
required-features = ["unstable"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "surf-web"
|
||||||
|
required-features = ["surf"]
|
||||||
|
|
245
README.md
245
README.md
|
@ -1,142 +1,159 @@
|
||||||
# Async version of the Rust standard library
|
<h1 align="center">async-std</h1>
|
||||||
|
<div align="center">
|
||||||
|
<strong>
|
||||||
|
Async version of the Rust standard library
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.com/async-rs/async-std.svg?branch=master)](https://travis-ci.com/async-rs/async-std)
|
<br />
|
||||||
[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/async-rs/async-std)
|
|
||||||
[![Cargo](https://img.shields.io/crates/v/async-std.svg)](https://crates.io/crates/async-std)
|
|
||||||
[![Documentation](https://docs.rs/async-std/badge.svg)](https://docs.rs/async-std)
|
|
||||||
[![chat](https://img.shields.io/discord/598880689856970762.svg?logo=discord)](https://discord.gg/JvZeVNe)
|
|
||||||
|
|
||||||
This crate provides an async version of [`std`]. It provides all the interfaces you
|
<div align="center">
|
||||||
are used to, but in an async version and ready for Rust's `async`/`await` syntax.
|
<!-- CI status -->
|
||||||
|
<a href="https://github.com/async-rs/async-std/actions">
|
||||||
|
<img src="https://github.com/async-rs/async-std/workflows/CI/badge.svg"
|
||||||
|
alt="CI Status" />
|
||||||
|
</a>
|
||||||
|
<!-- Crates version -->
|
||||||
|
<a href="https://crates.io/crates/async-std">
|
||||||
|
<img src="https://img.shields.io/crates/v/async-std.svg?style=flat-square"
|
||||||
|
alt="Crates.io version" />
|
||||||
|
</a>
|
||||||
|
<!-- Downloads -->
|
||||||
|
<a href="https://crates.io/crates/async-std">
|
||||||
|
<img src="https://img.shields.io/crates/d/async-std.svg?style=flat-square"
|
||||||
|
alt="Download" />
|
||||||
|
</a>
|
||||||
|
<!-- docs.rs docs -->
|
||||||
|
<a href="https://docs.rs/async-std">
|
||||||
|
<img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square"
|
||||||
|
alt="docs.rs docs" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="https://discord.gg/JvZeVNe">
|
||||||
|
<img src="https://img.shields.io/discord/598880689856970762.svg?logo=discord&style=flat-square"
|
||||||
|
alt="chat" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<h3>
|
||||||
|
<a href="https://docs.rs/async-std">
|
||||||
|
API Docs
|
||||||
|
</a>
|
||||||
|
<span> | </span>
|
||||||
|
<a href="https://book.async.rs">
|
||||||
|
Book
|
||||||
|
</a>
|
||||||
|
<span> | </span>
|
||||||
|
<a href="https://github.com/async-rs/async-std/releases">
|
||||||
|
Releases
|
||||||
|
</a>
|
||||||
|
<span> | </span>
|
||||||
|
<a href="https://async.rs/contribute">
|
||||||
|
Contributing
|
||||||
|
</a>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
This crate provides an async version of [`std`]. It provides all the interfaces
|
||||||
|
you are used to, but in an async version and ready for Rust's `async`/`await`
|
||||||
|
syntax.
|
||||||
|
|
||||||
[`std`]: https://doc.rust-lang.org/std/index.html
|
[`std`]: https://doc.rust-lang.org/std/index.html
|
||||||
|
|
||||||
## Documentation
|
## Features
|
||||||
|
|
||||||
`async-std` comes with [extensive API documentation][docs] and a [book][book].
|
- __Modern:__ Built from the ground up for `std::future` and `async/await` with
|
||||||
|
blazing fast compilation time.
|
||||||
|
- __Fast:__ Our robust allocator and threadpool designs provide ultra-high
|
||||||
|
throughput with predictably low latency.
|
||||||
|
- __Intuitive:__ Complete parity with the stdlib means you only need to learn
|
||||||
|
APIs once.
|
||||||
|
- __Clear:__ [Detailed documentation][docs] and [accessible guides][book] mean
|
||||||
|
using async Rust was never easier.
|
||||||
|
|
||||||
[docs]: https://docs.rs/async-std
|
[docs]: https://docs.rs/async-std
|
||||||
[book]: https://book.async.rs
|
[book]: https://book.async.rs
|
||||||
|
|
||||||
## Quickstart
|
## Examples
|
||||||
|
|
||||||
Add the following lines to your `Cargo.toml`:
|
```rust
|
||||||
|
use async_std::task;
|
||||||
|
|
||||||
```toml
|
async fn say_hello() {
|
||||||
[dependencies]
|
println!("Hello, world!");
|
||||||
async-std = "0.99"
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
task::block_on(say_hello())
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Or use [cargo add][cargo-add] if you have it installed:
|
More examples, including networking and file access, can be found in our
|
||||||
|
[`examples`] directory and in our [documentation].
|
||||||
|
|
||||||
|
[`examples`]: https://github.com/async-rs/async-std/tree/master/examples
|
||||||
|
[documentation]: https://docs.rs/async-std#examples
|
||||||
|
[`task::block_on`]: https://docs.rs/async-std/*/async_std/task/fn.block_on.html
|
||||||
|
[`"attributes"` feature]: https://docs.rs/async-std/#features
|
||||||
|
|
||||||
|
## Philosophy
|
||||||
|
|
||||||
|
We believe Async Rust should be as easy to pick up as Sync Rust. We also believe
|
||||||
|
that the best API is the one you already know. And finally, we believe that
|
||||||
|
providing an asynchronous counterpart to the standard library is the best way
|
||||||
|
stdlib provides a reliable basis for both performance and productivity.
|
||||||
|
|
||||||
|
Async-std is the embodiment of that vision. It combines single-allocation task
|
||||||
|
creation, with an adaptive lock-free executor, threadpool and network driver to
|
||||||
|
create a smooth system that processes work at a high pace with low latency,
|
||||||
|
using Rust's familiar stdlib API.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
With [cargo add][cargo-add] installed run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ cargo add async-std
|
$ cargo add async-std
|
||||||
```
|
```
|
||||||
|
|
||||||
|
We also provide a set of "unstable" features with async-std. See the [features
|
||||||
|
documentation] on how to enable them.
|
||||||
|
|
||||||
[cargo-add]: https://github.com/killercup/cargo-edit
|
[cargo-add]: https://github.com/killercup/cargo-edit
|
||||||
|
[features documentation]: https://docs.rs/async-std/#features
|
||||||
|
|
||||||
## Hello world
|
## Ecosystem
|
||||||
|
|
||||||
|
* [async-tls](https://crates.io/crates/async-tls) — Async TLS/SSL streams using **Rustls**.
|
||||||
|
|
||||||
|
* [async-native-tls](https://crates.io/crates/async-native-tls) — **Native TLS** for Async. Native TLS for futures and async-std.
|
||||||
|
|
||||||
|
* [async-tungstenite](https://crates.io/crates/async-tungstenite) — Asynchronous **WebSockets** for async-std, tokio, gio and any std Futures runtime.
|
||||||
|
|
||||||
|
* [Tide](https://crates.io/crates/tide) — Serve the web. A modular **web framework** built around async/await.
|
||||||
|
|
||||||
```rust
|
* [SQLx](https://crates.io/crates/sqlx) — The Rust **SQL** Toolkit. SQLx is a 100% safe Rust library for Postgres and MySQL with compile-time checked queries.
|
||||||
use async_std::task;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
task::block_on(async {
|
|
||||||
println!("Hello, world!");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Low-Friction Sockets with Built-In Timeouts
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use async_std::{
|
|
||||||
prelude::*,
|
|
||||||
task,
|
|
||||||
io,
|
|
||||||
net::TcpStream,
|
|
||||||
};
|
|
||||||
|
|
||||||
async fn get() -> io::Result<Vec<u8>> {
|
|
||||||
let mut stream = TcpStream::connect("example.com:80").await?;
|
|
||||||
stream.write_all(b"GET /index.html HTTP/1.0\r\n\r\n").await?;
|
|
||||||
|
|
||||||
let mut buf = vec![];
|
|
||||||
|
|
||||||
io::timeout(Duration::from_secs(5), async {
|
|
||||||
stream.read_to_end(&mut buf).await?;
|
|
||||||
Ok(buf)
|
|
||||||
}).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
task::block_on(async {
|
|
||||||
let raw_response = get().await.expect("request");
|
|
||||||
let response = String::from_utf8(raw_response)
|
|
||||||
.expect("utf8 conversion");
|
|
||||||
println!("received: {}", response);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
`async-std` is strongly commited to following semver. This means your code won't
|
|
||||||
break unless _you_ decide to upgrade.
|
|
||||||
|
|
||||||
However every now and then we come up with something that we think will work
|
|
||||||
_great_ for `async-std`, and we want to provide a sneak-peek so you can try it
|
|
||||||
out. This is what we call _"unstable"_ features. You can try out the unstable
|
|
||||||
features by enabling the `unstable` feature in your `Cargo.toml` file:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies.async-std]
|
|
||||||
version = "0.99"
|
|
||||||
features = ["unstable"]
|
|
||||||
```
|
|
||||||
|
|
||||||
Just be careful when using these features, as they may change between
|
|
||||||
versions.
|
|
||||||
|
|
||||||
## Take a look around
|
|
||||||
|
|
||||||
Clone the repo:
|
|
||||||
|
|
||||||
```
|
|
||||||
git clone git@github.com:async-rs/async-std.git && cd async-std
|
|
||||||
```
|
|
||||||
|
|
||||||
Generate docs:
|
|
||||||
|
|
||||||
```
|
|
||||||
cargo +nightly doc --features docs --open
|
|
||||||
```
|
|
||||||
|
|
||||||
Check out the [examples](examples). To run an example:
|
|
||||||
|
|
||||||
```
|
|
||||||
cargo +nightly run --example hello-world
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
See [our contribution document][contribution].
|
|
||||||
|
|
||||||
[contribution]: https://async.rs/contribute
|
|
||||||
|
|
||||||
|
* [Surf](https://crates.io/crates/surf) — Surf the web. Surf is a friendly **HTTP client** built for casual Rustaceans and veterans alike.
|
||||||
|
|
||||||
|
* [Xactor](https://crates.io/crates/xactor) — Xactor is a rust actors framework based on async-std.
|
||||||
|
|
||||||
|
* [async-graphql](https://crates.io/crates/async-graphql) — A GraphQL server library implemented in rust, with full support for async/await.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
<sup>
|
||||||
|
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
|
||||||
|
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
|
||||||
|
</sup>
|
||||||
|
|
||||||
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
<br/>
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
at your option.
|
|
||||||
|
|
||||||
#### Contribution
|
|
||||||
|
|
||||||
|
<sub>
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
|
||||||
dual licensed as above, without any additional terms or conditions.
|
be dual licensed as above, without any additional terms or conditions.
|
||||||
|
</sub>
|
||||||
|
|
40
benches/mutex.rs
Normal file
40
benches/mutex.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
use async_std::sync::{Arc, Mutex};
|
||||||
|
use async_std::task;
|
||||||
|
use test::Bencher;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn create(b: &mut Bencher) {
|
||||||
|
b.iter(|| Mutex::new(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn contention(b: &mut Bencher) {
|
||||||
|
b.iter(|| task::block_on(run(10, 1000)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn no_contention(b: &mut Bencher) {
|
||||||
|
b.iter(|| task::block_on(run(1, 10000)));
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(task: usize, iter: usize) {
|
||||||
|
let m = Arc::new(Mutex::new(()));
|
||||||
|
let mut tasks = Vec::new();
|
||||||
|
|
||||||
|
for _ in 0..task {
|
||||||
|
let m = m.clone();
|
||||||
|
tasks.push(task::spawn(async move {
|
||||||
|
for _ in 0..iter {
|
||||||
|
let _ = m.lock().await;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for t in tasks {
|
||||||
|
t.await;
|
||||||
|
}
|
||||||
|
}
|
11
benches/task.rs
Normal file
11
benches/task.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
use async_std::task;
|
||||||
|
use test::Bencher;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn block_on(b: &mut Bencher) {
|
||||||
|
b.iter(|| task::block_on(async {}));
|
||||||
|
}
|
|
@ -19,8 +19,9 @@
|
||||||
- [Clean Shutdown](./tutorial/clean_shutdown.md)
|
- [Clean Shutdown](./tutorial/clean_shutdown.md)
|
||||||
- [Handling Disconnection](./tutorial/handling_disconnection.md)
|
- [Handling Disconnection](./tutorial/handling_disconnection.md)
|
||||||
- [Implementing a Client](./tutorial/implementing_a_client.md)
|
- [Implementing a Client](./tutorial/implementing_a_client.md)
|
||||||
- [TODO: Async Patterns](./patterns.md)
|
- [Async Patterns](./patterns.md)
|
||||||
- [TODO: Collected Small Patterns](./patterns/small-patterns.md)
|
- [TODO: Collected Small Patterns](./patterns/small-patterns.md)
|
||||||
|
- [Production-Ready Accept Loop](./patterns/accept-loop.md)
|
||||||
- [Security practices](./security/index.md)
|
- [Security practices](./security/index.md)
|
||||||
- [Security Disclosures and Policy](./security/policy.md)
|
- [Security Disclosures and Policy](./security/policy.md)
|
||||||
- [Glossary](./glossary.md)
|
- [Glossary](./glossary.md)
|
||||||
|
|
|
@ -24,11 +24,7 @@ To sum up: Rust gives us the ability to safely abstract over important propertie
|
||||||
|
|
||||||
## An easy view of computation
|
## An easy view of computation
|
||||||
|
|
||||||
While computation is a subject to write a whole [book](https://computationbook.com/) about, a very simplified view suffices for us:
|
While computation is a subject to write a whole [book](https://computationbook.com/) about, a very simplified view suffices for us: A sequence of composable operations which can branch based on a decision, run to succession and yield a result or yield an error
|
||||||
|
|
||||||
- computation is a sequence of composable operations
|
|
||||||
- they can branch based on a decision
|
|
||||||
- they either run to succession and yield a result, or they can yield an error
|
|
||||||
|
|
||||||
## Deferring computation
|
## Deferring computation
|
||||||
|
|
||||||
|
@ -136,11 +132,11 @@ When executing 2 or more of these functions at the same time, our runtime system
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
Working from values, we searched for something that expresses *working towards a value available sometime later*. From there, we talked about the concept of polling.
|
Working from values, we searched for something that expresses *working towards a value available later*. From there, we talked about the concept of polling.
|
||||||
|
|
||||||
A `Future` is any data type that does not represent a value, but the ability to *produce a value at some point in the future*. Implementations of this are very varied and detailed depending on use-case, but the interface is simple.
|
A `Future` is any data type that does not represent a value, but the ability to *produce a value at some point in the future*. Implementations of this are very varied and detailed depending on use-case, but the interface is simple.
|
||||||
|
|
||||||
Next, we will introduce you to `tasks`, which we need to actually *run* Futures.
|
Next, we will introduce you to `tasks`, which we will use to actually *run* Futures.
|
||||||
|
|
||||||
[^1]: Two parties reading while it is guaranteed that no one is writing is always safe.
|
[^1]: Two parties reading while it is guaranteed that no one is writing is always safe.
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ Tasks in `async_std` are one of the core abstractions. Much like Rust's `thread`
|
||||||
|
|
||||||
## Blocking
|
## Blocking
|
||||||
|
|
||||||
`Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and by itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this:
|
`Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and of itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this:
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
|
|
||||||
`async-std` provides an interface to all important primitives: filesystem operations, network operations and concurrency basics like timers. It also exposes a `task` in a model similar to the `thread` module found in the Rust standard lib. But it does not only include I/O primitives, but also `async/await` compatible versions of primitives like `Mutex`.
|
`async-std` provides an interface to all important primitives: filesystem operations, network operations and concurrency basics like timers. It also exposes a `task` in a model similar to the `thread` module found in the Rust standard lib. But it does not only include I/O primitives, but also `async/await` compatible versions of primitives like `Mutex`.
|
||||||
|
|
||||||
[organization]: https://github.com/async-rs/async-std
|
[organization]: https://github.com/async-rs
|
||||||
|
|
|
@ -31,7 +31,7 @@ In general, this crate will be conservative with respect to the minimum supporte
|
||||||
|
|
||||||
## Security fixes
|
## Security fixes
|
||||||
|
|
||||||
Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give at least _3 month_ of ahead notice.
|
Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give a notice at least _3 months_ ahead.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,13 @@ Rust has two kinds of types commonly referred to as `Future`:
|
||||||
|
|
||||||
|
|
||||||
- the first is `std::future::Future` from Rust’s [standard library](https://doc.rust-lang.org/std/future/trait.Future.html).
|
- the first is `std::future::Future` from Rust’s [standard library](https://doc.rust-lang.org/std/future/trait.Future.html).
|
||||||
- the second is `futures::future::Future` from the [futures-rs crate](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/prelude/trait.Future.html), currently released as `futures-preview`.
|
- the second is `futures::future::Future` from the [futures-rs crate](https://docs.rs/futures/0.3/futures/prelude/trait.Future.html).
|
||||||
|
|
||||||
The future defined in the [futures-rs](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/prelude/trait.Future.html) crate was the original implementation of the type. To enable the `async/await` syntax, the core Future trait was moved into Rust’s standard library and became `std::future::Future`. In some sense, the `std::future::Future` can be seen as a minimal subset of `futures::future::Future`.
|
The future defined in the [futures-rs](https://docs.rs/futures/0.3/futures/prelude/trait.Future.html) crate was the original implementation of the type. To enable the `async/await` syntax, the core Future trait was moved into Rust’s standard library and became `std::future::Future`. In some sense, the `std::future::Future` can be seen as a minimal subset of `futures::future::Future`.
|
||||||
|
|
||||||
It is critical to understand the difference between `std::future::Future` and `futures::future::Future`, and the approach that `async-std` takes towards them. In itself, `std::future::Future` is not something you want to interact with as a user—except by calling `.await` on it. The inner workings of `std::future::Future` are mostly of interest to people implementing `Future`. Make no mistake—this is very useful! Most of the functionality that used to be defined on `Future` itself has been moved to an extension trait called [`FuturesExt`](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/future/trait.FutureExt.html). From this information, you might be able to infer that the `futures` library serves as an extension to the core Rust async features.
|
It is critical to understand the difference between `std::future::Future` and `futures::future::Future`, and the approach that `async-std` takes towards them. In itself, `std::future::Future` is not something you want to interact with as a user—except by calling `.await` on it. The inner workings of `std::future::Future` are mostly of interest to people implementing `Future`. Make no mistake—this is very useful! Most of the functionality that used to be defined on `Future` itself has been moved to an extension trait called [`FuturesExt`](https://docs.rs/futures/0.3/futures/future/trait.FutureExt.html). From this information, you might be able to infer that the `futures` library serves as an extension to the core Rust async features.
|
||||||
|
|
||||||
In the same tradition as `futures`, `async-std` re-exports the core `std::future::Future` type. You can actively opt into the extensions provided by the `futures-preview` crate by adding it to your `Cargo.toml` and importing `FuturesExt`.
|
In the same tradition as `futures`, `async-std` re-exports the core `std::future::Future` type. You can actively opt into the extensions provided by the `futures` crate by adding it to your `Cargo.toml` and importing `FuturesExt`.
|
||||||
|
|
||||||
## Interfaces and Stability
|
## Interfaces and Stability
|
||||||
|
|
||||||
|
|
266
docs/src/patterns/accept-loop.md
Normal file
266
docs/src/patterns/accept-loop.md
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
# Production-Ready Accept Loop
|
||||||
|
|
||||||
|
A production-ready accept loop needs the following things:
|
||||||
|
1. Handling errors
|
||||||
|
2. Limiting the number of simultanteous connections to avoid deny-of-service
|
||||||
|
(DoS) attacks
|
||||||
|
|
||||||
|
|
||||||
|
## Handling errors
|
||||||
|
|
||||||
|
There are two kinds of errors in an accept loop:
|
||||||
|
1. Per-connection errors. The system uses them to notify that there was a
|
||||||
|
connection in the queue and it's dropped by the peer. Subsequent connections
|
||||||
|
can be already queued so next connection must be accepted immediately.
|
||||||
|
2. Resource shortages. When these are encountered it doesn't make sense to
|
||||||
|
accept the next socket immediately. But the listener stays active, so you server
|
||||||
|
should try to accept socket later.
|
||||||
|
|
||||||
|
Here is the example of a per-connection error (printed in normal and debug mode):
|
||||||
|
```
|
||||||
|
Error: Connection reset by peer (os error 104)
|
||||||
|
Error: Os { code: 104, kind: ConnectionReset, message: "Connection reset by peer" }
|
||||||
|
```
|
||||||
|
|
||||||
|
And the following is the most common example of a resource shortage error:
|
||||||
|
```
|
||||||
|
Error: Too many open files (os error 24)
|
||||||
|
Error: Os { code: 24, kind: Other, message: "Too many open files" }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing Application
|
||||||
|
|
||||||
|
To test your application for these errors try the following (this works
|
||||||
|
on unixes only).
|
||||||
|
|
||||||
|
Lower limits and start the application:
|
||||||
|
```
|
||||||
|
$ ulimit -n 100
|
||||||
|
$ cargo run --example your_app
|
||||||
|
Compiling your_app v0.1.0 (/work)
|
||||||
|
Finished dev [unoptimized + debuginfo] target(s) in 5.47s
|
||||||
|
Running `target/debug/examples/your_app`
|
||||||
|
Server is listening on: http://127.0.0.1:1234
|
||||||
|
```
|
||||||
|
Then in another console run the [`wrk`] benchmark tool:
|
||||||
|
```
|
||||||
|
$ wrk -c 1000 http://127.0.0.1:1234
|
||||||
|
Running 10s test @ http://localhost:8080/
|
||||||
|
2 threads and 1000 connections
|
||||||
|
$ telnet localhost 1234
|
||||||
|
Trying ::1...
|
||||||
|
Connected to localhost.
|
||||||
|
```
|
||||||
|
|
||||||
|
Important is to check the following things:
|
||||||
|
|
||||||
|
1. The application doesn't crash on error (but may log errors, see below)
|
||||||
|
2. It's possible to connect to the application again once load is stopped
|
||||||
|
(few seconds after `wrk`). This is what `telnet` does in example above,
|
||||||
|
make sure it prints `Connected to <hostname>`.
|
||||||
|
3. The `Too many open files` error is logged in the appropriate log. This
|
||||||
|
requires to set "maximum number of simultaneous connections" parameter (see
|
||||||
|
below) of your application to a value greater then `100` for this example.
|
||||||
|
4. Check CPU usage of the app while doing a test. It should not occupy 100%
|
||||||
|
of a single CPU core (it's unlikely that you can exhaust CPU by 1000
|
||||||
|
connections in Rust, so this means error handling is not right).
|
||||||
|
|
||||||
|
#### Testing non-HTTP applications
|
||||||
|
|
||||||
|
If it's possible, use the appropriate benchmark tool and set the appropriate
|
||||||
|
number of connections. For example `redis-benchmark` has a `-c` parameter for
|
||||||
|
that, if you implement redis protocol.
|
||||||
|
|
||||||
|
Alternatively, can still use `wrk`, just make sure that connection is not
|
||||||
|
immediately closed. If it is, put a temporary timeout before handing
|
||||||
|
the connection to the protocol handler, like this:
|
||||||
|
|
||||||
|
```rust,edition2018
|
||||||
|
# extern crate async_std;
|
||||||
|
# use std::time::Duration;
|
||||||
|
# use async_std::{
|
||||||
|
# net::{TcpListener, ToSocketAddrs},
|
||||||
|
# prelude::*,
|
||||||
|
# };
|
||||||
|
#
|
||||||
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
#
|
||||||
|
#async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
|
||||||
|
# let listener = TcpListener::bind(addr).await?;
|
||||||
|
# let mut incoming = listener.incoming();
|
||||||
|
while let Some(stream) = incoming.next().await {
|
||||||
|
task::spawn(async {
|
||||||
|
task::sleep(Duration::from_secs(10)).await; // 1
|
||||||
|
connection_loop(stream).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
# Ok(())
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Make sure the sleep coroutine is inside the spawned task, not in the loop.
|
||||||
|
|
||||||
|
[`wrk`]: https://github.com/wg/wrk
|
||||||
|
|
||||||
|
|
||||||
|
### Handling Errors Manually
|
||||||
|
|
||||||
|
Here is how basic accept loop could look like:
|
||||||
|
|
||||||
|
```rust,edition2018
|
||||||
|
# extern crate async_std;
|
||||||
|
# use std::time::Duration;
|
||||||
|
# use async_std::{
|
||||||
|
# net::{TcpListener, ToSocketAddrs},
|
||||||
|
# prelude::*,
|
||||||
|
# };
|
||||||
|
#
|
||||||
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
#
|
||||||
|
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
|
||||||
|
let listener = TcpListener::bind(addr).await?;
|
||||||
|
let mut incoming = listener.incoming();
|
||||||
|
while let Some(result) = incoming.next().await {
|
||||||
|
let stream = match stream {
|
||||||
|
Err(ref e) if is_connection_error(e) => continue, // 1
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {}. Pausing for 500ms."); // 3
|
||||||
|
task::sleep(Duration::from_millis(500)).await; // 2
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Ok(s) => s,
|
||||||
|
};
|
||||||
|
// body
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Ignore per-connection errors.
|
||||||
|
2. Sleep and continue on resource shortage.
|
||||||
|
3. It's important to log the message, because these errors commonly mean the
|
||||||
|
misconfiguration of the system and are helpful for operations people running
|
||||||
|
the application.
|
||||||
|
|
||||||
|
Be sure to [test your application](#testing-application).
|
||||||
|
|
||||||
|
|
||||||
|
### External Crates
|
||||||
|
|
||||||
|
The crate [`async-listen`] has a helper to achieve this task:
|
||||||
|
```rust,edition2018
|
||||||
|
# extern crate async_std;
|
||||||
|
# extern crate async_listen;
|
||||||
|
# use std::time::Duration;
|
||||||
|
# use async_std::{
|
||||||
|
# net::{TcpListener, ToSocketAddrs},
|
||||||
|
# prelude::*,
|
||||||
|
# };
|
||||||
|
#
|
||||||
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
#
|
||||||
|
use async_listen::{ListenExt, error_hint};
|
||||||
|
|
||||||
|
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
|
||||||
|
|
||||||
|
let listener = TcpListener::bind(addr).await?;
|
||||||
|
let mut incoming = listener
|
||||||
|
.incoming()
|
||||||
|
.log_warnings(log_accept_error) // 1
|
||||||
|
.handle_errors(Duration::from_millis(500));
|
||||||
|
while let Some(socket) = incoming.next().await { // 2
|
||||||
|
// body
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_accept_error(e: &io::Error) {
|
||||||
|
eprintln!("Error: {}. Listener paused for 0.5s. {}", e, error_hint(e)) // 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Logs resource shortages (`async-listen` calls them warnings). If you use
|
||||||
|
`log` crate or any other in your app this should go to the log.
|
||||||
|
2. Stream yields sockets without `Result` wrapper after `handle_errors` because
|
||||||
|
all errors are already handled.
|
||||||
|
3. Together with the error we print a hint, which explains some errors for end
|
||||||
|
users. For example, it recommends increasing open file limit and gives
|
||||||
|
a link.
|
||||||
|
|
||||||
|
[`async-listen`]: https://crates.io/crates/async-listen/
|
||||||
|
|
||||||
|
Be sure to [test your application](#testing-application).
|
||||||
|
|
||||||
|
|
||||||
|
## Connections Limit
|
||||||
|
|
||||||
|
Even if you've applied everything described in
|
||||||
|
[Handling Errors](#handling-errors) section, there is still a problem.
|
||||||
|
|
||||||
|
Let's imagine you have a server that needs to open a file to process
|
||||||
|
client request. At some point, you might encounter the following situation:
|
||||||
|
|
||||||
|
1. There are as many client connection as max file descriptors allowed for
|
||||||
|
the application.
|
||||||
|
2. Listener gets `Too many open files` error so it sleeps.
|
||||||
|
3. Some client sends a request via the previously open connection.
|
||||||
|
4. Opening a file to serve request fails, because of the same
|
||||||
|
`Too many open files` error, until some other client drops a connection.
|
||||||
|
|
||||||
|
There are many more possible situations, this is just a small illustation that
|
||||||
|
limiting number of connections is very useful. Generally, it's one of the ways
|
||||||
|
to control resources used by a server and avoiding some kinds of deny of
|
||||||
|
service (DoS) attacks.
|
||||||
|
|
||||||
|
### `async-listen` crate
|
||||||
|
|
||||||
|
Limiting maximum number of simultaneous connections with [`async-listen`]
|
||||||
|
looks like the following:
|
||||||
|
|
||||||
|
```rust,edition2018
|
||||||
|
# extern crate async_std;
|
||||||
|
# extern crate async_listen;
|
||||||
|
# use std::time::Duration;
|
||||||
|
# use async_std::{
|
||||||
|
# net::{TcpListener, TcpStream, ToSocketAddrs},
|
||||||
|
# prelude::*,
|
||||||
|
# };
|
||||||
|
#
|
||||||
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
#
|
||||||
|
use async_listen::{ListenExt, Token, error_hint};
|
||||||
|
|
||||||
|
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
|
||||||
|
|
||||||
|
let listener = TcpListener::bind(addr).await?;
|
||||||
|
let mut incoming = listener
|
||||||
|
.incoming()
|
||||||
|
.log_warnings(log_accept_error)
|
||||||
|
.handle_errors(Duration::from_millis(500)) // 1
|
||||||
|
.backpressure(100);
|
||||||
|
while let Some((token, socket)) = incoming.next().await { // 2
|
||||||
|
task::spawn(async move {
|
||||||
|
connection_loop(&token, stream).await; // 3
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn connection_loop(_token: &Token, stream: TcpStream) { // 4
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
# fn log_accept_error(e: &io::Error) {
|
||||||
|
# eprintln!("Error: {}. Listener paused for 0.5s. {}", e, error_hint(e));
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
1. We need to handle errors first, because [`backpressure`] helper expects
|
||||||
|
stream of `TcpStream` rather than `Result`.
|
||||||
|
2. The token yielded by a new stream is what is counted by backpressure helper.
|
||||||
|
I.e. if you drop a token, new connection can be established.
|
||||||
|
3. We give the connection loop a reference to token to bind token's lifetime to
|
||||||
|
the lifetime of the connection.
|
||||||
|
4. The token itsellf in the function can be ignored, hence `_token`
|
||||||
|
|
||||||
|
[`backpressure`]: https://docs.rs/async-listen/0.1.2/async_listen/trait.ListenExt.html#method.backpressure
|
||||||
|
|
||||||
|
Be sure to [test this behavior](#testing-application).
|
|
@ -4,16 +4,15 @@ At this point, we only need to start the broker to get a fully-functioning (in t
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
use async_std::{
|
use async_std::{
|
||||||
io::{self, BufReader},
|
io::BufReader,
|
||||||
net::{TcpListener, TcpStream, ToSocketAddrs},
|
net::{TcpListener, TcpStream, ToSocketAddrs},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
task,
|
task,
|
||||||
};
|
};
|
||||||
use futures_channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use futures_util::SinkExt;
|
use futures::sink::SinkExt;
|
||||||
use std::{
|
use std::{
|
||||||
collections::hash_map::{HashMap, Entry},
|
collections::hash_map::{HashMap, Entry},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
|
|
@ -22,16 +22,15 @@ Let's add waiting to the server:
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
# use async_std::{
|
# use async_std::{
|
||||||
# io::{self, BufReader},
|
# io::{self, BufReader},
|
||||||
# net::{TcpListener, TcpStream, ToSocketAddrs},
|
# net::{TcpListener, TcpStream, ToSocketAddrs},
|
||||||
# prelude::*,
|
# prelude::*,
|
||||||
# task,
|
# task,
|
||||||
# };
|
# };
|
||||||
# use futures_channel::mpsc;
|
# use futures::channel::mpsc;
|
||||||
# use futures_util::SinkExt;
|
# use futures::sink::SinkExt;
|
||||||
# use std::{
|
# use std::{
|
||||||
# collections::hash_map::{HashMap, Entry},
|
# collections::hash_map::{HashMap, Entry},
|
||||||
# sync::Arc,
|
# sync::Arc,
|
||||||
|
@ -156,16 +155,15 @@ And to the broker:
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
# use async_std::{
|
# use async_std::{
|
||||||
# io::{self, BufReader},
|
# io::{self, BufReader},
|
||||||
# net::{TcpListener, TcpStream, ToSocketAddrs},
|
# net::{TcpListener, TcpStream, ToSocketAddrs},
|
||||||
# prelude::*,
|
# prelude::*,
|
||||||
# task,
|
# task,
|
||||||
# };
|
# };
|
||||||
# use futures_channel::mpsc;
|
# use futures::channel::mpsc;
|
||||||
# use futures_util::SinkExt;
|
# use futures::sink::SinkExt;
|
||||||
# use std::{
|
# use std::{
|
||||||
# collections::hash_map::{HashMap, Entry},
|
# collections::hash_map::{HashMap, Entry},
|
||||||
# sync::Arc,
|
# sync::Arc,
|
||||||
|
|
|
@ -2,25 +2,24 @@
|
||||||
## Connecting Readers and Writers
|
## Connecting Readers and Writers
|
||||||
|
|
||||||
So how do we make sure that messages read in `connection_loop` flow into the relevant `connection_writer_loop`?
|
So how do we make sure that messages read in `connection_loop` flow into the relevant `connection_writer_loop`?
|
||||||
We should somehow maintain an `peers: HashMap<String, Sender<String>>` map which allows a client to find destination channels.
|
We should somehow maintain a `peers: HashMap<String, Sender<String>>` map which allows a client to find destination channels.
|
||||||
However, this map would be a bit of shared mutable state, so we'll have to wrap an `RwLock` over it and answer tough questions of what should happen if the client joins at the same moment as it receives a message.
|
However, this map would be a bit of shared mutable state, so we'll have to wrap an `RwLock` over it and answer tough questions of what should happen if the client joins at the same moment as it receives a message.
|
||||||
|
|
||||||
One trick to make reasoning about state simpler comes from the actor model.
|
One trick to make reasoning about state simpler comes from the actor model.
|
||||||
We can create a dedicated broker tasks which owns the `peers` map and communicates with other tasks by channels.
|
We can create a dedicated broker task which owns the `peers` map and communicates with other tasks using channels.
|
||||||
By hiding `peers` inside such an "actor" task, we remove the need for mutxes and also make serialization point explicit.
|
By hiding `peers` inside such an "actor" task, we remove the need for mutexes and also make the serialization point explicit.
|
||||||
The order of events "Bob sends message to Alice" and "Alice joins" is determined by the order of the corresponding events in the broker's event queue.
|
The order of events "Bob sends message to Alice" and "Alice joins" is determined by the order of the corresponding events in the broker's event queue.
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
# use async_std::{
|
# use async_std::{
|
||||||
# net::TcpStream,
|
# net::TcpStream,
|
||||||
# prelude::*,
|
# prelude::*,
|
||||||
# task,
|
# task,
|
||||||
# };
|
# };
|
||||||
# use futures_channel::mpsc;
|
# use futures::channel::mpsc;
|
||||||
# use futures_util::sink::SinkExt;
|
# use futures::sink::SinkExt;
|
||||||
# use std::sync::Arc;
|
# use std::sync::Arc;
|
||||||
#
|
#
|
||||||
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
@ -93,9 +92,9 @@ async fn broker_loop(mut events: Receiver<Event>) -> Result<()> {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Broker should handle two types of events: a message or an arrival of a new peer.
|
1. The broker task should handle two types of events: a message or an arrival of a new peer.
|
||||||
2. Internal state of the broker is a `HashMap`.
|
2. The internal state of the broker is a `HashMap`.
|
||||||
Note how we don't need a `Mutex` here and can confidently say, at each iteration of the broker's loop, what is the current set of peers
|
Note how we don't need a `Mutex` here and can confidently say, at each iteration of the broker's loop, what is the current set of peers
|
||||||
3. To handle a message, we send it over a channel to each destination
|
3. To handle a message, we send it over a channel to each destination
|
||||||
4. To handle new peer, we first register it in the peer's map ...
|
4. To handle a new peer, we first register it in the peer's map ...
|
||||||
5. ... and then spawn a dedicated task to actually write the messages to the socket.
|
5. ... and then spawn a dedicated task to actually write the messages to the socket.
|
||||||
|
|
|
@ -19,11 +19,10 @@ First, let's add a shutdown channel to the `connection_loop`:
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
# use async_std::net::TcpStream;
|
# use async_std::net::TcpStream;
|
||||||
# use futures_channel::mpsc;
|
# use futures::channel::mpsc;
|
||||||
# use futures_util::SinkExt;
|
# use futures::sink::SinkExt;
|
||||||
# use std::sync::Arc;
|
# use std::sync::Arc;
|
||||||
#
|
#
|
||||||
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
@ -61,8 +60,8 @@ async fn connection_loop(mut broker: Sender<Event>, stream: Arc<TcpStream>) -> R
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
1. To enforce that no messages are send along the shutdown channel, we use an uninhabited type.
|
1. To enforce that no messages are sent along the shutdown channel, we use an uninhabited type.
|
||||||
2. We pass the shutdown channel to the writer task
|
2. We pass the shutdown channel to the writer task.
|
||||||
3. In the reader, we create a `_shutdown_sender` whose only purpose is to get dropped.
|
3. In the reader, we create a `_shutdown_sender` whose only purpose is to get dropped.
|
||||||
|
|
||||||
In the `connection_writer_loop`, we now need to choose between shutdown and message channels.
|
In the `connection_writer_loop`, we now need to choose between shutdown and message channels.
|
||||||
|
@ -70,17 +69,14 @@ We use the `select` macro for this purpose:
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
# use async_std::{net::TcpStream, prelude::*};
|
# use async_std::{net::TcpStream, prelude::*};
|
||||||
use futures_channel::mpsc;
|
# use futures::channel::mpsc;
|
||||||
use futures_util::{select, FutureExt};
|
use futures::{select, FutureExt};
|
||||||
# use std::sync::Arc;
|
# use std::sync::Arc;
|
||||||
|
|
||||||
# type Receiver<T> = mpsc::UnboundedReceiver<T>;
|
# type Receiver<T> = mpsc::UnboundedReceiver<T>;
|
||||||
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
# type Sender<T> = mpsc::UnboundedSender<T>;
|
# type Sender<T> = mpsc::UnboundedSender<T>;
|
||||||
|
|
||||||
# #[derive(Debug)]
|
# #[derive(Debug)]
|
||||||
# enum Void {} // 1
|
# enum Void {} // 1
|
||||||
|
|
||||||
|
@ -114,7 +110,7 @@ async fn connection_writer_loop(
|
||||||
|
|
||||||
Another problem is that between the moment we detect disconnection in `connection_writer_loop` and the moment when we actually remove the peer from the `peers` map, new messages might be pushed into the peer's channel.
|
Another problem is that between the moment we detect disconnection in `connection_writer_loop` and the moment when we actually remove the peer from the `peers` map, new messages might be pushed into the peer's channel.
|
||||||
To not lose these messages completely, we'll return the messages channel back to the broker.
|
To not lose these messages completely, we'll return the messages channel back to the broker.
|
||||||
This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infailable.
|
This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infallible.
|
||||||
|
|
||||||
## Final Code
|
## Final Code
|
||||||
|
|
||||||
|
@ -122,16 +118,16 @@ The final code looks like this:
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
use async_std::{
|
use async_std::{
|
||||||
io::BufReader,
|
io::BufReader,
|
||||||
net::{TcpListener, TcpStream, ToSocketAddrs},
|
net::{TcpListener, TcpStream, ToSocketAddrs},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
task,
|
task,
|
||||||
};
|
};
|
||||||
use futures_channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use futures_util::{select, FutureExt, SinkExt};
|
use futures::sink::SinkExt;
|
||||||
|
use futures::{select, FutureExt};
|
||||||
use std::{
|
use std::{
|
||||||
collections::hash_map::{Entry, HashMap},
|
collections::hash_map::{Entry, HashMap},
|
||||||
future::Future,
|
future::Future,
|
||||||
|
|
|
@ -1,29 +1,27 @@
|
||||||
## Implementing a client
|
## Implementing a client
|
||||||
|
|
||||||
Let's now implement the client for the chat.
|
Since the protocol is line-based, implementing a client for the chat is straightforward:
|
||||||
Because the protocol is line-based, the implementation is pretty straightforward:
|
|
||||||
|
|
||||||
* Lines read from stdin should be sent over the socket.
|
* Lines read from stdin should be sent over the socket.
|
||||||
* Lines read from the socket should be echoed to stdout.
|
* Lines read from the socket should be echoed to stdout.
|
||||||
|
|
||||||
Unlike the server, the client needs only limited concurrency, as it interacts with only a single user.
|
Although async does not significantly affect client performance (as unlike the server, the client interacts solely with one user and only needs limited concurrency), async is still useful for managing concurrency!
|
||||||
For this reason, async doesn't bring a lot of performance benefits in this case.
|
|
||||||
|
The client has to read from stdin and the socket *simultaneously*.
|
||||||
|
Programming this with threads is cumbersome, especially when implementing a clean shutdown.
|
||||||
|
With async, the `select!` macro is all that is needed.
|
||||||
|
|
||||||
However, async is still useful for managing concurrency!
|
|
||||||
Specifically, the client should *simultaneously* read from stdin and from the socket.
|
|
||||||
Programming this with threads is cumbersome, especially when implementing clean shutdown.
|
|
||||||
With async, we can just use the `select!` macro.
|
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_util;
|
# extern crate futures;
|
||||||
use async_std::{
|
use async_std::{
|
||||||
io::{stdin, BufReader},
|
io::{stdin, BufReader},
|
||||||
net::{TcpStream, ToSocketAddrs},
|
net::{TcpStream, ToSocketAddrs},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
task,
|
task,
|
||||||
};
|
};
|
||||||
use futures_util::{select, FutureExt};
|
use futures::{select, FutureExt};
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
# Tutorial: Writing a chat
|
# Tutorial: Writing a chat
|
||||||
|
|
||||||
Nothing is as simple as a chat server, right? Not quite, chat servers
|
Nothing is simpler than creating a chat server, right?
|
||||||
already expose you to all the fun of asynchronous programming: how
|
Not quite, chat servers expose you to all the fun of asynchronous programming:
|
||||||
do you handle clients connecting concurrently. How do you handle them disconnecting?
|
|
||||||
|
|
||||||
How do you distribute the messages?
|
How will the server handle clients connecting concurrently?
|
||||||
|
|
||||||
In this tutorial, we will show you how to write one in `async-std`.
|
How will it handle them disconnecting?
|
||||||
|
|
||||||
|
How will it distribute the messages?
|
||||||
|
|
||||||
|
This tutorial explains how to write a chat server in `async-std`.
|
||||||
|
|
||||||
You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/master/examples/a-chat).
|
You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/master/examples/a-chat).
|
||||||
|
|
|
@ -10,14 +10,18 @@ We need to:
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# use async_std::{
|
# use async_std::{
|
||||||
# io::BufReader,
|
# net::{TcpListener, ToSocketAddrs},
|
||||||
# net::{TcpListener, TcpStream, ToSocketAddrs},
|
|
||||||
# prelude::*,
|
# prelude::*,
|
||||||
# task,
|
# task,
|
||||||
# };
|
# };
|
||||||
#
|
#
|
||||||
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
#
|
#
|
||||||
|
use async_std::{
|
||||||
|
io::BufReader,
|
||||||
|
net::TcpStream,
|
||||||
|
};
|
||||||
|
|
||||||
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
|
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
|
||||||
let listener = TcpListener::bind(addr).await?;
|
let listener = TcpListener::bind(addr).await?;
|
||||||
let mut incoming = listener.incoming();
|
let mut incoming = listener.incoming();
|
||||||
|
@ -46,7 +50,7 @@ async fn connection_loop(stream: TcpStream) -> Result<()> {
|
||||||
Some(idx) => (&line[..idx], line[idx + 1 ..].trim()),
|
Some(idx) => (&line[..idx], line[idx + 1 ..].trim()),
|
||||||
};
|
};
|
||||||
let dest: Vec<String> = dest.split(',').map(|name| name.trim().to_string()).collect();
|
let dest: Vec<String> = dest.split(',').map(|name| name.trim().to_string()).collect();
|
||||||
let msg: String = msg.trim().to_string();
|
let msg: String = msg.to_string();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -107,7 +111,7 @@ We can "fix" it by waiting for the task to be joined, like this:
|
||||||
#
|
#
|
||||||
# async move |stream| {
|
# async move |stream| {
|
||||||
let handle = task::spawn(connection_loop(stream));
|
let handle = task::spawn(connection_loop(stream));
|
||||||
handle.await
|
handle.await?
|
||||||
# };
|
# };
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -130,7 +134,7 @@ So let's use a helper function for this:
|
||||||
# };
|
# };
|
||||||
fn spawn_and_log_error<F>(fut: F) -> task::JoinHandle<()>
|
fn spawn_and_log_error<F>(fut: F) -> task::JoinHandle<()>
|
||||||
where
|
where
|
||||||
F: Future<Output = io::Result<()>> + Send + 'static,
|
F: Future<Output = Result<()>> + Send + 'static,
|
||||||
{
|
{
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
if let Err(e) = fut.await {
|
if let Err(e) = fut.await {
|
||||||
|
|
|
@ -13,14 +13,13 @@ if Alice and Charley send two messages to Bob at the same time, Bob will see the
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
# use async_std::{
|
# use async_std::{
|
||||||
# net::TcpStream,
|
# net::TcpStream,
|
||||||
# prelude::*,
|
# prelude::*,
|
||||||
# };
|
# };
|
||||||
use futures_channel::mpsc; // 1
|
use futures::channel::mpsc; // 1
|
||||||
use futures_util::sink::SinkExt;
|
use futures::sink::SinkExt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
|
|
@ -12,7 +12,7 @@ After that, the client can send messages to other clients using the following sy
|
||||||
login1, login2, ... loginN: message
|
login1, login2, ... loginN: message
|
||||||
```
|
```
|
||||||
|
|
||||||
Each of the specified clients than receives a `from login: message` message.
|
Each of the specified clients then receives a `from login: message` message.
|
||||||
|
|
||||||
A possible session might look like this
|
A possible session might look like this
|
||||||
|
|
||||||
|
@ -38,18 +38,10 @@ $ cargo new a-chat
|
||||||
$ cd a-chat
|
$ cd a-chat
|
||||||
```
|
```
|
||||||
|
|
||||||
At the moment `async-std` requires Rust nightly, so let's add a rustup override for convenience:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ rustup override add nightly
|
|
||||||
$ rustc --version
|
|
||||||
rustc 1.38.0-nightly (c4715198b 2019-08-05)
|
|
||||||
```
|
|
||||||
|
|
||||||
Add the following lines to `Cargo.toml`:
|
Add the following lines to `Cargo.toml`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures-preview = { version = "0.3.0-alpha.19", features = [ "async-await" ] }
|
futures = "0.3.0"
|
||||||
async-std = "0.99"
|
async-std = "1"
|
||||||
```
|
```
|
||||||
|
|
|
@ -8,6 +8,6 @@ fn main() -> Result<()> {
|
||||||
match (args.nth(1).as_ref().map(String::as_str), args.next()) {
|
match (args.nth(1).as_ref().map(String::as_str), args.next()) {
|
||||||
(Some("client"), None) => client::main(),
|
(Some("client"), None) => client::main(),
|
||||||
(Some("server"), None) => server::main(),
|
(Some("server"), None) => server::main(),
|
||||||
_ => Err("Usage: a-chat [client|server]")?,
|
_ => Err("Usage: a-chat [client|server]".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ async fn connection_loop(mut broker: Sender<Event>, stream: TcpStream) -> Result
|
||||||
let mut lines = reader.lines();
|
let mut lines = reader.lines();
|
||||||
|
|
||||||
let name = match lines.next().await {
|
let name = match lines.next().await {
|
||||||
None => Err("peer disconnected immediately")?,
|
None => return Err("peer disconnected immediately".into()),
|
||||||
Some(line) => line?,
|
Some(line) => line?,
|
||||||
};
|
};
|
||||||
let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::<Void>();
|
let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::<Void>();
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
/* TODO: Once the next version of surf released, re-enable this example.
|
|
||||||
//! Sends an HTTP request to the Rust website.
|
|
||||||
|
|
||||||
use async_std::task;
|
use async_std::task;
|
||||||
|
|
||||||
fn main() -> Result<(), surf::Exception> {
|
fn main() -> Result<(), surf::Exception> {
|
||||||
|
@ -18,6 +15,3 @@ fn main() -> Result<(), surf::Exception> {
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
|
@ -14,8 +14,9 @@ use async_std::task;
|
||||||
async fn process(stream: TcpStream) -> io::Result<()> {
|
async fn process(stream: TcpStream) -> io::Result<()> {
|
||||||
println!("Accepted from: {}", stream.peer_addr()?);
|
println!("Accepted from: {}", stream.peer_addr()?);
|
||||||
|
|
||||||
let (reader, writer) = &mut (&stream, &stream);
|
let mut reader = stream.clone();
|
||||||
io::copy(reader, writer).await?;
|
let mut writer = stream;
|
||||||
|
io::copy(&mut reader, &mut writer).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
45
examples/tcp-ipv4-and-6-echo.rs
Normal file
45
examples/tcp-ipv4-and-6-echo.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
//! TCP echo server, accepting connections both on both ipv4 and ipv6 sockets.
|
||||||
|
//!
|
||||||
|
//! To send messages, do:
|
||||||
|
//!
|
||||||
|
//! ```sh
|
||||||
|
//! $ nc 127.0.0.1 8080
|
||||||
|
//! $ nc ::1 8080
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use async_std::io;
|
||||||
|
use async_std::net::{TcpListener, TcpStream};
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::task;
|
||||||
|
|
||||||
|
async fn process(stream: TcpStream) -> io::Result<()> {
|
||||||
|
println!("Accepted from: {}", stream.peer_addr()?);
|
||||||
|
|
||||||
|
let mut reader = stream.clone();
|
||||||
|
let mut writer = stream;
|
||||||
|
io::copy(&mut reader, &mut writer).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> io::Result<()> {
|
||||||
|
task::block_on(async {
|
||||||
|
let ipv4_listener = TcpListener::bind("127.0.0.1:8080").await?;
|
||||||
|
println!("Listening on {}", ipv4_listener.local_addr()?);
|
||||||
|
let ipv6_listener = TcpListener::bind("[::1]:8080").await?;
|
||||||
|
println!("Listening on {}", ipv6_listener.local_addr()?);
|
||||||
|
|
||||||
|
let ipv4_incoming = ipv4_listener.incoming();
|
||||||
|
let ipv6_incoming = ipv6_listener.incoming();
|
||||||
|
|
||||||
|
let mut incoming = ipv4_incoming.merge(ipv6_incoming);
|
||||||
|
|
||||||
|
while let Some(stream) = incoming.next().await {
|
||||||
|
let stream = stream?;
|
||||||
|
task::spawn(async {
|
||||||
|
process(stream).await.unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
|
@ -2,13 +2,16 @@ use std::collections::BinaryHeap;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<T: Ord> Extend<T> for BinaryHeap<T> {
|
impl<T: Ord + Send> stream::Extend<T> for BinaryHeap<T> {
|
||||||
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
|
fn extend<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
|
||||||
|
where
|
||||||
|
<S as IntoStream>::IntoStream: Send,
|
||||||
|
{
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
self.reserve(stream.size_hint().0);
|
self.reserve(stream.size_hint().0);
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
use std::collections::BinaryHeap;
|
use std::collections::BinaryHeap;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<T: Ord> FromStream<T> for BinaryHeap<T> {
|
impl<T: Ord + Send> FromStream<T> for BinaryHeap<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = T>>(
|
fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
|
||||||
where
|
where
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
<S as IntoStream>::IntoStream: Send,
|
||||||
{
|
{
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = BinaryHeap::new();
|
let mut out = BinaryHeap::new();
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,16 @@ use std::collections::BTreeMap;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<K: Ord, V> Extend<(K, V)> for BTreeMap<K, V> {
|
impl<K: Ord + Send, V: Send> stream::Extend<(K, V)> for BTreeMap<K, V> {
|
||||||
fn stream_extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
|
fn extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
|
||||||
|
where
|
||||||
|
<S as IntoStream>::IntoStream: Send,
|
||||||
|
{
|
||||||
Box::pin(stream.into_stream().for_each(move |(k, v)| {
|
Box::pin(stream.into_stream().for_each(move |(k, v)| {
|
||||||
self.insert(k, v);
|
self.insert(k, v);
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<K: Ord, V> FromStream<(K, V)> for BTreeMap<K, V> {
|
impl<K: Ord + Send, V: Send> FromStream<(K, V)> for BTreeMap<K, V> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = (K, V)>>(
|
fn from_stream<'a, S: IntoStream<Item = (K, V)> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
|
||||||
where
|
where
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
<S as IntoStream>::IntoStream: Send,
|
||||||
{
|
{
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = BTreeMap::new();
|
let mut out = BTreeMap::new();
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,16 @@ use std::collections::BTreeSet;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<T: Ord> Extend<T> for BTreeSet<T> {
|
impl<T: Ord + Send> stream::Extend<T> for BTreeSet<T> {
|
||||||
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
|
fn extend<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
|
||||||
|
where
|
||||||
|
<S as IntoStream>::IntoStream: Send,
|
||||||
|
{
|
||||||
Box::pin(stream.into_stream().for_each(move |item| {
|
Box::pin(stream.into_stream().for_each(move |item| {
|
||||||
self.insert(item);
|
self.insert(item);
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<T: Ord> FromStream<T> for BTreeSet<T> {
|
impl<T: Ord + Send> FromStream<T> for BTreeSet<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = T>>(
|
fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
|
||||||
where
|
where
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
<S as IntoStream>::IntoStream: Send,
|
||||||
{
|
{
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = BTreeSet::new();
|
let mut out = BTreeSet::new();
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,21 @@ use std::hash::{BuildHasher, Hash};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<K, V, H> Extend<(K, V)> for HashMap<K, V, H>
|
impl<K, V, H> stream::Extend<(K, V)> for HashMap<K, V, H>
|
||||||
where
|
where
|
||||||
K: Eq + Hash,
|
K: Eq + Hash + Send,
|
||||||
H: BuildHasher + Default,
|
V: Send,
|
||||||
|
H: BuildHasher + Default + Send,
|
||||||
{
|
{
|
||||||
fn stream_extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
|
fn extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
|
||||||
|
where
|
||||||
|
<S as IntoStream>::IntoStream: Send,
|
||||||
|
{
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
// The following is adapted from the hashbrown source code:
|
// The following is adapted from the hashbrown source code:
|
||||||
|
|
|
@ -2,27 +2,27 @@ use std::collections::HashMap;
|
||||||
use std::hash::{BuildHasher, Hash};
|
use std::hash::{BuildHasher, Hash};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<K, V, H> FromStream<(K, V)> for HashMap<K, V, H>
|
impl<K, V, H> FromStream<(K, V)> for HashMap<K, V, H>
|
||||||
where
|
where
|
||||||
K: Eq + Hash,
|
K: Eq + Hash + Send,
|
||||||
H: BuildHasher + Default,
|
H: BuildHasher + Default + Send,
|
||||||
|
V: Send,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = (K, V)>>(
|
fn from_stream<'a, S: IntoStream<Item = (K, V)> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
|
||||||
where
|
where
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
<S as IntoStream>::IntoStream: Send,
|
||||||
{
|
{
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = HashMap::with_hasher(Default::default());
|
let mut out = HashMap::with_hasher(Default::default());
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,20 @@ use std::hash::{BuildHasher, Hash};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<T, H> Extend<T> for HashSet<T, H>
|
impl<T, H> stream::Extend<T> for HashSet<T, H>
|
||||||
where
|
where
|
||||||
T: Eq + Hash,
|
T: Eq + Hash + Send,
|
||||||
H: BuildHasher + Default,
|
H: BuildHasher + Default + Send,
|
||||||
{
|
{
|
||||||
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
|
fn extend<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
|
||||||
|
where
|
||||||
|
<S as IntoStream>::IntoStream: Send,
|
||||||
|
{
|
||||||
// The Extend impl for HashSet in the standard library delegates to the internal HashMap.
|
// The Extend impl for HashSet in the standard library delegates to the internal HashMap.
|
||||||
// Thus, this impl is just a copy of the async Extend impl for HashMap in this crate.
|
// Thus, this impl is just a copy of the async Extend impl for HashMap in this crate.
|
||||||
|
|
||||||
|
|
|
@ -2,27 +2,26 @@ use std::collections::HashSet;
|
||||||
use std::hash::{BuildHasher, Hash};
|
use std::hash::{BuildHasher, Hash};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<T, H> FromStream<T> for HashSet<T, H>
|
impl<T, H> FromStream<T> for HashSet<T, H>
|
||||||
where
|
where
|
||||||
T: Eq + Hash,
|
T: Eq + Hash + Send,
|
||||||
H: BuildHasher + Default,
|
H: BuildHasher + Default + Send,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = T>>(
|
fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
|
||||||
where
|
where
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
<S as IntoStream>::IntoStream: Send,
|
||||||
{
|
{
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = HashSet::with_hasher(Default::default());
|
let mut out = HashSet::with_hasher(Default::default());
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,16 @@ use std::collections::LinkedList;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<T> Extend<T> for LinkedList<T> {
|
impl<T: Send> stream::Extend<T> for LinkedList<T> {
|
||||||
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
|
fn extend<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
|
||||||
|
where
|
||||||
|
<S as IntoStream>::IntoStream: Send,
|
||||||
|
{
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
Box::pin(stream.for_each(move |item| self.push_back(item)))
|
Box::pin(stream.for_each(move |item| self.push_back(item)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
use std::collections::LinkedList;
|
use std::collections::LinkedList;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<T> FromStream<T> for LinkedList<T> {
|
impl<T: Send> FromStream<T> for LinkedList<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = T>>(
|
fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
|
||||||
where
|
where
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
<S as IntoStream>::IntoStream: Send,
|
||||||
{
|
{
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = LinkedList::new();
|
let mut out = LinkedList::new();
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,16 @@ use std::collections::VecDeque;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<T> Extend<T> for VecDeque<T> {
|
impl<T: Send> stream::Extend<T> for VecDeque<T> {
|
||||||
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
|
fn extend<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
|
||||||
|
where
|
||||||
|
<S as IntoStream>::IntoStream: Send,
|
||||||
|
{
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
self.reserve(stream.size_hint().0);
|
self.reserve(stream.size_hint().0);
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<T> FromStream<T> for VecDeque<T> {
|
impl<T: Send> FromStream<T> for VecDeque<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = T>>(
|
fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
|
||||||
where
|
where
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
<S as IntoStream>::IntoStream: Send,
|
||||||
{
|
{
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = VecDeque::new();
|
let mut out = VecDeque::new();
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::{Path, PathBuf};
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Returns the canonical form of a path.
|
/// Returns the canonical form of a path.
|
||||||
///
|
///
|
||||||
|
@ -33,5 +33,10 @@ use crate::task::blocking;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
|
pub async fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::canonicalize(path) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::canonicalize(&path)
|
||||||
|
.map(Into::into)
|
||||||
|
.context(|| format!("could not canonicalize `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Copies the contents and permissions of a file to a new location.
|
/// Copies the contents and permissions of a file to a new location.
|
||||||
///
|
///
|
||||||
|
@ -42,5 +42,9 @@ use crate::task::blocking;
|
||||||
pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
|
pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
|
||||||
let from = from.as_ref().to_owned();
|
let from = from.as_ref().to_owned();
|
||||||
let to = to.as_ref().to_owned();
|
let to = to.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::copy(&from, &to) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::copy(&from, &to)
|
||||||
|
.context(|| format!("could not copy `{}` to `{}`", from.display(), to.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Creates a new directory.
|
/// Creates a new directory.
|
||||||
///
|
///
|
||||||
|
@ -35,5 +35,9 @@ use crate::task::blocking;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::create_dir(path) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::create_dir(&path)
|
||||||
|
.context(|| format!("could not create directory `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Creates a new directory and all of its parents if they are missing.
|
/// Creates a new directory and all of its parents if they are missing.
|
||||||
///
|
///
|
||||||
|
@ -30,5 +30,9 @@ use crate::task::blocking;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::create_dir_all(path) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::create_dir_all(&path)
|
||||||
|
.context(|| format!("could not create directory path `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
use std::path::Path;
|
use std::future::Future;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
/// A builder for creating directories with configurable options.
|
/// A builder for creating directories with configurable options.
|
||||||
///
|
///
|
||||||
|
@ -109,26 +107,17 @@ impl DirBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
async move { blocking::spawn(async move { builder.create(path) }).await }
|
async move { spawn_blocking(move || builder.create(path)).await }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_if! {
|
cfg_unix! {
|
||||||
if #[cfg(feature = "docs")] {
|
use crate::os::unix::fs::DirBuilderExt;
|
||||||
use crate::os::unix::fs::DirBuilderExt;
|
|
||||||
} else if #[cfg(unix)] {
|
|
||||||
use std::os::unix::fs::DirBuilderExt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(unix)))]
|
impl DirBuilderExt for DirBuilder {
|
||||||
cfg_if! {
|
fn mode(&mut self, mode: u32) -> &mut Self {
|
||||||
if #[cfg(any(unix, feature = "docs"))] {
|
self.mode = Some(mode);
|
||||||
impl DirBuilderExt for DirBuilder {
|
self
|
||||||
fn mode(&mut self, mode: u32) -> &mut Self {
|
|
||||||
self.mode = Some(mode);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
|
|
||||||
use crate::fs::{FileType, Metadata};
|
use crate::fs::{FileType, Metadata};
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::PathBuf;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
/// An entry in a directory.
|
/// An entry in a directory.
|
||||||
///
|
///
|
||||||
|
@ -50,7 +48,7 @@ impl DirEntry {
|
||||||
/// # Ok(()) }) }
|
/// # Ok(()) }) }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn path(&self) -> PathBuf {
|
pub fn path(&self) -> PathBuf {
|
||||||
self.0.path()
|
self.0.path().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the metadata for this entry.
|
/// Reads the metadata for this entry.
|
||||||
|
@ -89,7 +87,7 @@ impl DirEntry {
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn metadata(&self) -> io::Result<Metadata> {
|
pub async fn metadata(&self) -> io::Result<Metadata> {
|
||||||
let inner = self.0.clone();
|
let inner = self.0.clone();
|
||||||
blocking::spawn(async move { inner.metadata() }).await
|
spawn_blocking(move || inner.metadata()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the file type for this entry.
|
/// Reads the file type for this entry.
|
||||||
|
@ -127,7 +125,7 @@ impl DirEntry {
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn file_type(&self) -> io::Result<FileType> {
|
pub async fn file_type(&self) -> io::Result<FileType> {
|
||||||
let inner = self.0.clone();
|
let inner = self.0.clone();
|
||||||
blocking::spawn(async move { inner.file_type() }).await
|
spawn_blocking(move || inner.file_type()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the bare name of this entry without the leading path.
|
/// Returns the bare name of this entry without the leading path.
|
||||||
|
@ -160,21 +158,18 @@ impl fmt::Debug for DirEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_if! {
|
impl Clone for DirEntry {
|
||||||
if #[cfg(feature = "docs")] {
|
fn clone(&self) -> Self {
|
||||||
use crate::os::unix::fs::DirEntryExt;
|
DirEntry(self.0.clone())
|
||||||
} else if #[cfg(unix)] {
|
|
||||||
use std::os::unix::fs::DirEntryExt;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(unix)))]
|
cfg_unix! {
|
||||||
cfg_if! {
|
use crate::os::unix::fs::DirEntryExt;
|
||||||
if #[cfg(any(unix, feature = "docs"))] {
|
|
||||||
impl DirEntryExt for DirEntry {
|
impl DirEntryExt for DirEntry {
|
||||||
fn ino(&self) -> u64 {
|
fn ino(&self) -> u64 {
|
||||||
self.0.ino()
|
self.0.ino()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
183
src/fs/file.rs
183
src/fs/file.rs
|
@ -3,18 +3,17 @@ use std::cmp;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{Read as _, Seek as _, Write as _};
|
use std::io::{Read as _, Seek as _, Write as _};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::path::Path;
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
|
|
||||||
use crate::fs::{Metadata, Permissions};
|
use crate::fs::{Metadata, Permissions};
|
||||||
use crate::future;
|
use crate::future;
|
||||||
use crate::io::{self, Read, Seek, SeekFrom, Write};
|
use crate::io::{self, Read, Seek, SeekFrom, Write};
|
||||||
|
use crate::path::Path;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::task::{self, blocking, Context, Poll, Waker};
|
use crate::task::{spawn_blocking, Context, Poll, Waker};
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// An open file on the filesystem.
|
/// An open file on the filesystem.
|
||||||
///
|
///
|
||||||
|
@ -68,6 +67,23 @@ pub struct File {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
|
/// Creates an async file handle.
|
||||||
|
pub(crate) fn new(file: std::fs::File, is_flushed: bool) -> File {
|
||||||
|
let file = Arc::new(file);
|
||||||
|
|
||||||
|
File {
|
||||||
|
file: file.clone(),
|
||||||
|
lock: Lock::new(State {
|
||||||
|
file,
|
||||||
|
mode: Mode::Idle,
|
||||||
|
cache: Vec::new(),
|
||||||
|
is_flushed,
|
||||||
|
last_read_err: None,
|
||||||
|
last_write_err: None,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Opens a file in read-only mode.
|
/// Opens a file in read-only mode.
|
||||||
///
|
///
|
||||||
/// See the [`OpenOptions::open`] function for more options.
|
/// See the [`OpenOptions::open`] function for more options.
|
||||||
|
@ -97,8 +113,11 @@ impl File {
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
|
pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
let file = blocking::spawn(async move { std::fs::File::open(&path) }).await?;
|
let file = spawn_blocking(move || {
|
||||||
Ok(file.into())
|
std::fs::File::open(&path).context(|| format!("could not open `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(File::new(file, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opens a file in write-only mode.
|
/// Opens a file in write-only mode.
|
||||||
|
@ -132,8 +151,12 @@ impl File {
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
|
pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
let file = blocking::spawn(async move { std::fs::File::create(&path) }).await?;
|
let file = spawn_blocking(move || {
|
||||||
Ok(file.into())
|
std::fs::File::create(&path)
|
||||||
|
.context(|| format!("could not create `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(File::new(file, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synchronizes OS-internal buffered contents and metadata to disk.
|
/// Synchronizes OS-internal buffered contents and metadata to disk.
|
||||||
|
@ -165,7 +188,7 @@ impl File {
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
blocking::spawn(async move { state.file.sync_all() }).await
|
spawn_blocking(move || state.file.sync_all()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synchronizes OS-internal buffered contents to disk.
|
/// Synchronizes OS-internal buffered contents to disk.
|
||||||
|
@ -201,7 +224,7 @@ impl File {
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
blocking::spawn(async move { state.file.sync_data() }).await
|
spawn_blocking(move || state.file.sync_data()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Truncates or extends the file.
|
/// Truncates or extends the file.
|
||||||
|
@ -234,7 +257,7 @@ impl File {
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
blocking::spawn(async move { state.file.set_len(size) }).await
|
spawn_blocking(move || state.file.set_len(size)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the file's metadata.
|
/// Reads the file's metadata.
|
||||||
|
@ -253,7 +276,7 @@ impl File {
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn metadata(&self) -> io::Result<Metadata> {
|
pub async fn metadata(&self) -> io::Result<Metadata> {
|
||||||
let file = self.file.clone();
|
let file = self.file.clone();
|
||||||
blocking::spawn(async move { file.metadata() }).await
|
spawn_blocking(move || file.metadata()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the permissions on the file.
|
/// Changes the permissions on the file.
|
||||||
|
@ -282,7 +305,7 @@ impl File {
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
|
pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
|
||||||
let file = self.file.clone();
|
let file = self.file.clone();
|
||||||
blocking::spawn(async move { file.set_permissions(perm) }).await
|
spawn_blocking(move || file.set_permissions(perm)).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +315,7 @@ impl Drop for File {
|
||||||
// non-blocking fashion, but our only other option here is losing data remaining in the
|
// non-blocking fashion, but our only other option here is losing data remaining in the
|
||||||
// write cache. Good task schedulers should be resilient to occasional blocking hiccups in
|
// write cache. Good task schedulers should be resilient to occasional blocking hiccups in
|
||||||
// file destructors so we don't expect this to be a common problem in practice.
|
// file destructors so we don't expect this to be a common problem in practice.
|
||||||
let _ = task::block_on(self.flush());
|
let _ = smol::block_on(self.flush());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,83 +408,58 @@ impl Seek for &File {
|
||||||
|
|
||||||
impl From<std::fs::File> for File {
|
impl From<std::fs::File> for File {
|
||||||
fn from(file: std::fs::File) -> File {
|
fn from(file: std::fs::File) -> File {
|
||||||
let file = Arc::new(file);
|
File::new(file, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File {
|
cfg_unix! {
|
||||||
file: file.clone(),
|
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||||
lock: Lock::new(State {
|
|
||||||
file,
|
impl AsRawFd for File {
|
||||||
mode: Mode::Idle,
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
cache: Vec::new(),
|
self.file.as_raw_fd()
|
||||||
is_flushed: false,
|
}
|
||||||
last_read_err: None,
|
}
|
||||||
last_write_err: None,
|
|
||||||
}),
|
impl FromRawFd for File {
|
||||||
|
unsafe fn from_raw_fd(fd: RawFd) -> File {
|
||||||
|
std::fs::File::from_raw_fd(fd).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoRawFd for File {
|
||||||
|
fn into_raw_fd(self) -> RawFd {
|
||||||
|
let file = self.file.clone();
|
||||||
|
drop(self);
|
||||||
|
Arc::try_unwrap(file)
|
||||||
|
.expect("cannot acquire ownership of the file handle after drop")
|
||||||
|
.into_raw_fd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_if! {
|
cfg_windows! {
|
||||||
if #[cfg(feature = "docs")] {
|
use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
|
||||||
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
|
||||||
use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
|
|
||||||
} else if #[cfg(unix)] {
|
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
|
||||||
} else if #[cfg(windows)] {
|
|
||||||
use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(unix)))]
|
impl AsRawHandle for File {
|
||||||
cfg_if! {
|
fn as_raw_handle(&self) -> RawHandle {
|
||||||
if #[cfg(any(unix, feature = "docs"))] {
|
self.file.as_raw_handle()
|
||||||
impl AsRawFd for File {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.file.as_raw_fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromRawFd for File {
|
|
||||||
unsafe fn from_raw_fd(fd: RawFd) -> File {
|
|
||||||
std::fs::File::from_raw_fd(fd).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoRawFd for File {
|
|
||||||
fn into_raw_fd(self) -> RawFd {
|
|
||||||
let file = self.file.clone();
|
|
||||||
drop(self);
|
|
||||||
Arc::try_unwrap(file)
|
|
||||||
.expect("cannot acquire ownership of the file handle after drop")
|
|
||||||
.into_raw_fd()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(windows)))]
|
impl FromRawHandle for File {
|
||||||
cfg_if! {
|
unsafe fn from_raw_handle(handle: RawHandle) -> File {
|
||||||
if #[cfg(any(windows, feature = "docs"))] {
|
std::fs::File::from_raw_handle(handle).into()
|
||||||
impl AsRawHandle for File {
|
|
||||||
fn as_raw_handle(&self) -> RawHandle {
|
|
||||||
self.file.as_raw_handle()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromRawHandle for File {
|
impl IntoRawHandle for File {
|
||||||
unsafe fn from_raw_handle(handle: RawHandle) -> File {
|
fn into_raw_handle(self) -> RawHandle {
|
||||||
std::fs::File::from_raw_handle(handle).into()
|
let file = self.file.clone();
|
||||||
}
|
drop(self);
|
||||||
}
|
Arc::try_unwrap(file)
|
||||||
|
.expect("cannot acquire ownership of the file handle after drop")
|
||||||
impl IntoRawHandle for File {
|
.into_raw_handle()
|
||||||
fn into_raw_handle(self) -> RawHandle {
|
|
||||||
let file = self.file.clone();
|
|
||||||
drop(self);
|
|
||||||
Arc::try_unwrap(file)
|
|
||||||
.expect("cannot acquire ownership of the file handle after drop")
|
|
||||||
.into_raw_handle()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -675,7 +673,7 @@ impl LockGuard<State> {
|
||||||
if available > 0 || self.cache.is_empty() {
|
if available > 0 || self.cache.is_empty() {
|
||||||
// Copy data from the cache into the buffer.
|
// Copy data from the cache into the buffer.
|
||||||
let n = cmp::min(available, buf.len());
|
let n = cmp::min(available, buf.len());
|
||||||
buf[..n].copy_from_slice(&self.cache[start..n]);
|
buf[..n].copy_from_slice(&self.cache[start..(start + n)]);
|
||||||
|
|
||||||
// Move the read cursor forward.
|
// Move the read cursor forward.
|
||||||
self.mode = Mode::Reading(start + n);
|
self.mode = Mode::Reading(start + n);
|
||||||
|
@ -702,7 +700,7 @@ impl LockGuard<State> {
|
||||||
self.register(cx);
|
self.register(cx);
|
||||||
|
|
||||||
// Start a read operation asynchronously.
|
// Start a read operation asynchronously.
|
||||||
blocking::spawn(async move {
|
spawn_blocking(move || {
|
||||||
// Read some data from the file into the cache.
|
// Read some data from the file into the cache.
|
||||||
let res = {
|
let res = {
|
||||||
let State { file, cache, .. } = &mut *self;
|
let State { file, cache, .. } = &mut *self;
|
||||||
|
@ -743,7 +741,10 @@ impl LockGuard<State> {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
// Seek `n` bytes backwards. This call should not block because it only changes
|
// Seek `n` bytes backwards. This call should not block because it only changes
|
||||||
// the internal offset into the file and doesn't touch the actual file on disk.
|
// the internal offset into the file and doesn't touch the actual file on disk.
|
||||||
(&*self.file).seek(SeekFrom::Current(-(n as i64)))?;
|
//
|
||||||
|
// We ignore errors here because special files like `/dev/random` are not
|
||||||
|
// seekable.
|
||||||
|
let _ = (&*self.file).seek(SeekFrom::Current(-(n as i64)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch to idle mode.
|
// Switch to idle mode.
|
||||||
|
@ -811,7 +812,7 @@ impl LockGuard<State> {
|
||||||
self.register(cx);
|
self.register(cx);
|
||||||
|
|
||||||
// Start a write operation asynchronously.
|
// Start a write operation asynchronously.
|
||||||
blocking::spawn(async move {
|
spawn_blocking(move || {
|
||||||
match (&*self.file).write_all(&self.cache) {
|
match (&*self.file).write_all(&self.cache) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// Switch to idle mode.
|
// Switch to idle mode.
|
||||||
|
@ -844,7 +845,7 @@ impl LockGuard<State> {
|
||||||
self.register(cx);
|
self.register(cx);
|
||||||
|
|
||||||
// Start a flush operation asynchronously.
|
// Start a flush operation asynchronously.
|
||||||
blocking::spawn(async move {
|
spawn_blocking(move || {
|
||||||
match (&*self.file).flush() {
|
match (&*self.file).flush() {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
// Mark the file as flushed.
|
// Mark the file as flushed.
|
||||||
|
@ -866,3 +867,15 @@ impl LockGuard<State> {
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn async_file_drop() {
|
||||||
|
crate::task::block_on(async move {
|
||||||
|
File::open(file!()).await.unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,86 +1,84 @@
|
||||||
use cfg_if::cfg_if;
|
cfg_not_docs! {
|
||||||
|
pub use std::fs::FileType;
|
||||||
|
}
|
||||||
|
|
||||||
cfg_if! {
|
cfg_docs! {
|
||||||
if #[cfg(feature = "docs")] {
|
/// The type of a file or directory.
|
||||||
/// The type of a file or directory.
|
///
|
||||||
|
/// A file type is returned by [`Metadata::file_type`].
|
||||||
|
///
|
||||||
|
/// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`],
|
||||||
|
/// [`is_file`], and [`is_symlink`] can return `true`.
|
||||||
|
///
|
||||||
|
/// This type is a re-export of [`std::fs::FileType`].
|
||||||
|
///
|
||||||
|
/// [`Metadata::file_type`]: struct.Metadata.html#method.file_type
|
||||||
|
/// [`is_dir`]: #method.is_dir
|
||||||
|
/// [`is_file`]: #method.is_file
|
||||||
|
/// [`is_symlink`]: #method.is_symlink
|
||||||
|
/// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub struct FileType {
|
||||||
|
_private: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileType {
|
||||||
|
/// Returns `true` if this file type represents a regular directory.
|
||||||
///
|
///
|
||||||
/// A file type is returned by [`Metadata::file_type`].
|
/// If this file type represents a symbolic link, this method returns `false`.
|
||||||
///
|
///
|
||||||
/// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`],
|
/// # Examples
|
||||||
/// [`is_file`], and [`is_symlink`] can return `true`.
|
|
||||||
///
|
///
|
||||||
/// This type is a re-export of [`std::fs::FileType`].
|
/// ```no_run
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// #
|
||||||
|
/// use async_std::fs;
|
||||||
///
|
///
|
||||||
/// [`Metadata::file_type`]: struct.Metadata.html#method.file_type
|
/// let file_type = fs::metadata(".").await?.file_type();
|
||||||
/// [`is_dir`]: #method.is_dir
|
/// println!("{:?}", file_type.is_dir());
|
||||||
/// [`is_file`]: #method.is_file
|
/// #
|
||||||
/// [`is_symlink`]: #method.is_symlink
|
/// # Ok(()) }) }
|
||||||
/// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html
|
/// ```
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
pub fn is_dir(&self) -> bool {
|
||||||
pub struct FileType {
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
_private: (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileType {
|
/// Returns `true` if this file type represents a regular file.
|
||||||
/// Returns `true` if this file type represents a regular directory.
|
///
|
||||||
///
|
/// If this file type represents a symbolic link, this method returns `false`.
|
||||||
/// If this file type represents a symbolic link, this method returns `false`.
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// ```no_run
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
/// #
|
||||||
/// #
|
/// use async_std::fs;
|
||||||
/// use async_std::fs;
|
///
|
||||||
///
|
/// let file_type = fs::metadata("a.txt").await?.file_type();
|
||||||
/// let file_type = fs::metadata(".").await?.file_type();
|
/// println!("{:?}", file_type.is_file());
|
||||||
/// println!("{:?}", file_type.is_dir());
|
/// #
|
||||||
/// #
|
/// # Ok(()) }) }
|
||||||
/// # Ok(()) }) }
|
/// ```
|
||||||
/// ```
|
pub fn is_file(&self) -> bool {
|
||||||
pub fn is_dir(&self) -> bool {
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
unimplemented!()
|
}
|
||||||
}
|
|
||||||
|
/// Returns `true` if this file type represents a symbolic link.
|
||||||
/// Returns `true` if this file type represents a regular file.
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// If this file type represents a symbolic link, this method returns `false`.
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// # Examples
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
///
|
/// #
|
||||||
/// ```no_run
|
/// use async_std::fs;
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
///
|
||||||
/// #
|
/// let file_type = fs::metadata("a.txt").await?.file_type();
|
||||||
/// use async_std::fs;
|
/// println!("{:?}", file_type.is_symlink());
|
||||||
///
|
/// #
|
||||||
/// let file_type = fs::metadata("a.txt").await?.file_type();
|
/// # Ok(()) }) }
|
||||||
/// println!("{:?}", file_type.is_file());
|
/// ```
|
||||||
/// #
|
pub fn is_symlink(&self) -> bool {
|
||||||
/// # Ok(()) }) }
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
/// ```
|
|
||||||
pub fn is_file(&self) -> bool {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if this file type represents a symbolic link.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
||||||
/// #
|
|
||||||
/// use async_std::fs;
|
|
||||||
///
|
|
||||||
/// let file_type = fs::metadata("a.txt").await?.file_type();
|
|
||||||
/// println!("{:?}", file_type.is_symlink());
|
|
||||||
/// #
|
|
||||||
/// # Ok(()) }) }
|
|
||||||
/// ```
|
|
||||||
pub fn is_symlink(&self) -> bool {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
pub use std::fs::FileType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Creates a hard link on the filesystem.
|
/// Creates a hard link on the filesystem.
|
||||||
///
|
///
|
||||||
|
@ -33,5 +33,14 @@ use crate::task::blocking;
|
||||||
pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
|
pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
|
||||||
let from = from.as_ref().to_owned();
|
let from = from.as_ref().to_owned();
|
||||||
let to = to.as_ref().to_owned();
|
let to = to.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::hard_link(&from, &to) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::hard_link(&from, &to).context(|| {
|
||||||
|
format!(
|
||||||
|
"could not create a hard link from `{}` to `{}`",
|
||||||
|
from.display(),
|
||||||
|
to.display()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
/// Reads metadata for a path.
|
/// Reads metadata for a path.
|
||||||
///
|
///
|
||||||
|
@ -37,196 +34,196 @@ use crate::task::blocking;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
|
pub async fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::metadata(path) }).await
|
spawn_blocking(move || std::fs::metadata(path)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_if! {
|
cfg_not_docs! {
|
||||||
if #[cfg(feature = "docs")] {
|
pub use std::fs::Metadata;
|
||||||
use std::time::SystemTime;
|
}
|
||||||
|
|
||||||
use crate::fs::{FileType, Permissions};
|
cfg_docs! {
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
/// Metadata for a file or directory.
|
use crate::fs::{FileType, Permissions};
|
||||||
|
|
||||||
|
/// Metadata for a file or directory.
|
||||||
|
///
|
||||||
|
/// Metadata is returned by [`metadata`] and [`symlink_metadata`].
|
||||||
|
///
|
||||||
|
/// This type is a re-export of [`std::fs::Metadata`].
|
||||||
|
///
|
||||||
|
/// [`metadata`]: fn.metadata.html
|
||||||
|
/// [`symlink_metadata`]: fn.symlink_metadata.html
|
||||||
|
/// [`is_dir`]: #method.is_dir
|
||||||
|
/// [`is_file`]: #method.is_file
|
||||||
|
/// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Metadata {
|
||||||
|
_private: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metadata {
|
||||||
|
/// Returns the file type from this metadata.
|
||||||
///
|
///
|
||||||
/// Metadata is returned by [`metadata`] and [`symlink_metadata`].
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// This type is a re-export of [`std::fs::Metadata`].
|
/// ```no_run
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// #
|
||||||
|
/// use async_std::fs;
|
||||||
///
|
///
|
||||||
/// [`metadata`]: fn.metadata.html
|
/// let metadata = fs::metadata("a.txt").await?;
|
||||||
/// [`symlink_metadata`]: fn.symlink_metadata.html
|
/// println!("{:?}", metadata.file_type());
|
||||||
/// [`is_dir`]: #method.is_dir
|
/// #
|
||||||
/// [`is_file`]: #method.is_file
|
/// # Ok(()) }) }
|
||||||
/// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html
|
/// ```
|
||||||
#[derive(Clone, Debug)]
|
pub fn file_type(&self) -> FileType {
|
||||||
pub struct Metadata {
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
_private: (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Metadata {
|
/// Returns `true` if this metadata is for a regular directory.
|
||||||
/// Returns the file type from this metadata.
|
///
|
||||||
///
|
/// If this metadata is for a symbolic link, this method returns `false`.
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// ```no_run
|
///
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
/// ```no_run
|
||||||
/// #
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// use async_std::fs;
|
/// #
|
||||||
///
|
/// use async_std::fs;
|
||||||
/// let metadata = fs::metadata("a.txt").await?;
|
///
|
||||||
/// println!("{:?}", metadata.file_type());
|
/// let metadata = fs::metadata(".").await?;
|
||||||
/// #
|
/// println!("{:?}", metadata.is_dir());
|
||||||
/// # Ok(()) }) }
|
/// #
|
||||||
/// ```
|
/// # Ok(()) }) }
|
||||||
pub fn file_type(&self) -> FileType {
|
/// ```
|
||||||
unimplemented!()
|
pub fn is_dir(&self) -> bool {
|
||||||
}
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
|
}
|
||||||
/// Returns `true` if this metadata is for a regular directory.
|
|
||||||
///
|
/// Returns `true` if this metadata is for a regular file.
|
||||||
/// If this metadata is for a symbolic link, this method returns `false`.
|
///
|
||||||
///
|
/// If this metadata is for a symbolic link, this method returns `false`.
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// ```no_run
|
///
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
/// ```no_run
|
||||||
/// #
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// use async_std::fs;
|
/// #
|
||||||
///
|
/// use async_std::fs;
|
||||||
/// let metadata = fs::metadata(".").await?;
|
///
|
||||||
/// println!("{:?}", metadata.is_dir());
|
/// let metadata = fs::metadata("a.txt").await?;
|
||||||
/// #
|
/// println!("{:?}", metadata.is_file());
|
||||||
/// # Ok(()) }) }
|
/// #
|
||||||
/// ```
|
/// # Ok(()) }) }
|
||||||
pub fn is_dir(&self) -> bool {
|
/// ```
|
||||||
unimplemented!()
|
pub fn is_file(&self) -> bool {
|
||||||
}
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
|
}
|
||||||
/// Returns `true` if this metadata is for a regular file.
|
|
||||||
///
|
/// Returns the file size in bytes.
|
||||||
/// If this metadata is for a symbolic link, this method returns `false`.
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// ```no_run
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
/// #
|
||||||
/// #
|
/// use async_std::fs;
|
||||||
/// use async_std::fs;
|
///
|
||||||
///
|
/// let metadata = fs::metadata("a.txt").await?;
|
||||||
/// let metadata = fs::metadata("a.txt").await?;
|
/// println!("{}", metadata.len());
|
||||||
/// println!("{:?}", metadata.is_file());
|
/// #
|
||||||
/// #
|
/// # Ok(()) }) }
|
||||||
/// # Ok(()) }) }
|
/// ```
|
||||||
/// ```
|
pub fn len(&self) -> u64 {
|
||||||
pub fn is_file(&self) -> bool {
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
unimplemented!()
|
}
|
||||||
}
|
|
||||||
|
/// Returns the permissions from this metadata.
|
||||||
/// Returns the file size in bytes.
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// ```no_run
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
/// #
|
||||||
/// #
|
/// use async_std::fs;
|
||||||
/// use async_std::fs;
|
///
|
||||||
///
|
/// let metadata = fs::metadata("a.txt").await?;
|
||||||
/// let metadata = fs::metadata("a.txt").await?;
|
/// println!("{:?}", metadata.permissions());
|
||||||
/// println!("{}", metadata.len());
|
/// #
|
||||||
/// #
|
/// # Ok(()) }) }
|
||||||
/// # Ok(()) }) }
|
/// ```
|
||||||
/// ```
|
pub fn permissions(&self) -> Permissions {
|
||||||
pub fn len(&self) -> u64 {
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
unimplemented!()
|
}
|
||||||
}
|
|
||||||
|
/// Returns the last modification time.
|
||||||
/// Returns the permissions from this metadata.
|
///
|
||||||
///
|
/// # Errors
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// This data may not be available on all platforms, in which case an error will be
|
||||||
/// ```no_run
|
/// returned.
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
///
|
||||||
/// #
|
/// # Examples
|
||||||
/// use async_std::fs;
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// let metadata = fs::metadata("a.txt").await?;
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// println!("{:?}", metadata.permissions());
|
/// #
|
||||||
/// #
|
/// use async_std::fs;
|
||||||
/// # Ok(()) }) }
|
///
|
||||||
/// ```
|
/// let metadata = fs::metadata("a.txt").await?;
|
||||||
pub fn permissions(&self) -> Permissions {
|
/// println!("{:?}", metadata.modified());
|
||||||
unimplemented!()
|
/// #
|
||||||
}
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
/// Returns the last modification time.
|
pub fn modified(&self) -> io::Result<SystemTime> {
|
||||||
///
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
/// # Errors
|
}
|
||||||
///
|
|
||||||
/// This data may not be available on all platforms, in which case an error will be
|
/// Returns the last access time.
|
||||||
/// returned.
|
///
|
||||||
///
|
/// # Errors
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// This data may not be available on all platforms, in which case an error will be
|
||||||
/// ```no_run
|
/// returned.
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
///
|
||||||
/// #
|
/// # Examples
|
||||||
/// use async_std::fs;
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// let metadata = fs::metadata("a.txt").await?;
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// println!("{:?}", metadata.modified());
|
/// #
|
||||||
/// #
|
/// use async_std::fs;
|
||||||
/// # Ok(()) }) }
|
///
|
||||||
/// ```
|
/// let metadata = fs::metadata("a.txt").await?;
|
||||||
pub fn modified(&self) -> io::Result<SystemTime> {
|
/// println!("{:?}", metadata.accessed());
|
||||||
unimplemented!()
|
/// #
|
||||||
}
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
/// Returns the last access time.
|
pub fn accessed(&self) -> io::Result<SystemTime> {
|
||||||
///
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
/// # Errors
|
}
|
||||||
///
|
|
||||||
/// This data may not be available on all platforms, in which case an error will be
|
/// Returns the creation time.
|
||||||
/// returned.
|
///
|
||||||
///
|
/// # Errors
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// This data may not be available on all platforms, in which case an error will be
|
||||||
/// ```no_run
|
/// returned.
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
///
|
||||||
/// #
|
/// # Examples
|
||||||
/// use async_std::fs;
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// let metadata = fs::metadata("a.txt").await?;
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// println!("{:?}", metadata.accessed());
|
/// #
|
||||||
/// #
|
/// use async_std::fs;
|
||||||
/// # Ok(()) }) }
|
///
|
||||||
/// ```
|
/// let metadata = fs::metadata("a.txt").await?;
|
||||||
pub fn accessed(&self) -> io::Result<SystemTime> {
|
/// println!("{:?}", metadata.created());
|
||||||
unimplemented!()
|
/// #
|
||||||
}
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
/// Returns the creation time.
|
pub fn created(&self) -> io::Result<SystemTime> {
|
||||||
///
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// This data may not be available on all platforms, in which case an error will be
|
|
||||||
/// returned.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
||||||
/// #
|
|
||||||
/// use async_std::fs;
|
|
||||||
///
|
|
||||||
/// let metadata = fs::metadata("a.txt").await?;
|
|
||||||
/// println!("{:?}", metadata.created());
|
|
||||||
/// #
|
|
||||||
/// # Ok(()) }) }
|
|
||||||
/// ```
|
|
||||||
pub fn created(&self) -> io::Result<SystemTime> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
pub use std::fs::Metadata;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
//! This module is an async version of [`std::fs`].
|
//! This module is an async version of [`std::fs`].
|
||||||
//!
|
//!
|
||||||
//! [`os::unix::fs`]: ../os/unix/fs/index.html
|
//! [`os::unix::fs`]: ../os/unix/fs/index.html
|
||||||
|
//! [`os::windows::fs`]: ../os/windows/fs/index.html
|
||||||
//! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html
|
//! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html
|
||||||
//!
|
//!
|
||||||
//! # Platform-specific extensions
|
//! # Platform-specific extensions
|
||||||
//!
|
//!
|
||||||
//! * Unix: use the [`os::unix::fs`] module.
|
//! * Unix: use the [`os::unix::fs`] module.
|
||||||
|
//! * Windows: use the [`os::windows::fs`] module.
|
||||||
//!
|
//!
|
||||||
//! # Examples
|
//! # Examples
|
||||||
//!
|
//!
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
use std::path::Path;
|
use std::future::Future;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
|
|
||||||
use crate::fs::File;
|
use crate::fs::File;
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
/// A builder for opening files with configurable options.
|
/// A builder for opening files with configurable options.
|
||||||
///
|
///
|
||||||
|
@ -286,7 +284,10 @@ impl OpenOptions {
|
||||||
pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> {
|
pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
let options = self.0.clone();
|
let options = self.0.clone();
|
||||||
async move { blocking::spawn(async move { options.open(path).map(|f| f.into()) }).await }
|
async move {
|
||||||
|
let file = spawn_blocking(move || options.open(path)).await?;
|
||||||
|
Ok(File::new(file, true))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,27 +297,18 @@ impl Default for OpenOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_if! {
|
cfg_unix! {
|
||||||
if #[cfg(feature = "docs")] {
|
use crate::os::unix::fs::OpenOptionsExt;
|
||||||
use crate::os::unix::fs::OpenOptionsExt;
|
|
||||||
} else if #[cfg(unix)] {
|
|
||||||
use std::os::unix::fs::OpenOptionsExt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(unix)))]
|
impl OpenOptionsExt for OpenOptions {
|
||||||
cfg_if! {
|
fn mode(&mut self, mode: u32) -> &mut Self {
|
||||||
if #[cfg(any(unix, feature = "docs"))] {
|
self.0.mode(mode);
|
||||||
impl OpenOptionsExt for OpenOptions {
|
self
|
||||||
fn mode(&mut self, mode: u32) -> &mut Self {
|
}
|
||||||
self.0.mode(mode);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn custom_flags(&mut self, flags: i32) -> &mut Self {
|
fn custom_flags(&mut self, flags: i32) -> &mut Self {
|
||||||
self.0.custom_flags(flags);
|
self.0.custom_flags(flags);
|
||||||
self
|
self
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,58 +1,56 @@
|
||||||
use cfg_if::cfg_if;
|
cfg_not_docs! {
|
||||||
|
pub use std::fs::Permissions;
|
||||||
|
}
|
||||||
|
|
||||||
cfg_if! {
|
cfg_docs! {
|
||||||
if #[cfg(feature = "docs")] {
|
/// A set of permissions on a file or directory.
|
||||||
/// A set of permissions on a file or directory.
|
///
|
||||||
|
/// This type is a re-export of [`std::fs::Permissions`].
|
||||||
|
///
|
||||||
|
/// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub struct Permissions {
|
||||||
|
_private: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Permissions {
|
||||||
|
/// Returns the read-only flag.
|
||||||
///
|
///
|
||||||
/// This type is a re-export of [`std::fs::Permissions`].
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html
|
/// ```no_run
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
pub struct Permissions {
|
/// #
|
||||||
_private: (),
|
/// use async_std::fs;
|
||||||
|
///
|
||||||
|
/// let perm = fs::metadata("a.txt").await?.permissions();
|
||||||
|
/// println!("{:?}", perm.readonly());
|
||||||
|
/// #
|
||||||
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
|
pub fn readonly(&self) -> bool {
|
||||||
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Permissions {
|
/// Configures the read-only flag.
|
||||||
/// Returns the read-only flag.
|
///
|
||||||
///
|
/// [`fs::set_permissions`]: fn.set_permissions.html
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// ```no_run
|
///
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
/// ```no_run
|
||||||
/// #
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// use async_std::fs;
|
/// #
|
||||||
///
|
/// use async_std::fs;
|
||||||
/// let perm = fs::metadata("a.txt").await?.permissions();
|
///
|
||||||
/// println!("{:?}", perm.readonly());
|
/// let mut perm = fs::metadata("a.txt").await?.permissions();
|
||||||
/// #
|
/// perm.set_readonly(true);
|
||||||
/// # Ok(()) }) }
|
/// fs::set_permissions("a.txt", perm).await?;
|
||||||
/// ```
|
/// #
|
||||||
pub fn readonly(&self) -> bool {
|
/// # Ok(()) }) }
|
||||||
unimplemented!()
|
/// ```
|
||||||
}
|
pub fn set_readonly(&mut self, readonly: bool) {
|
||||||
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
/// Configures the read-only flag.
|
|
||||||
///
|
|
||||||
/// [`fs::set_permissions`]: fn.set_permissions.html
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
||||||
/// #
|
|
||||||
/// use async_std::fs;
|
|
||||||
///
|
|
||||||
/// let mut perm = fs::metadata("a.txt").await?.permissions();
|
|
||||||
/// perm.set_readonly(true);
|
|
||||||
/// fs::set_permissions("a.txt", perm).await?;
|
|
||||||
/// #
|
|
||||||
/// # Ok(()) }) }
|
|
||||||
/// ```
|
|
||||||
pub fn set_readonly(&mut self, readonly: bool) {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
pub use std::fs::Permissions;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Reads the entire contents of a file as raw bytes.
|
/// Reads the entire contents of a file as raw bytes.
|
||||||
///
|
///
|
||||||
|
@ -37,5 +37,8 @@ use crate::task::blocking;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
|
pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::read(path) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::read(&path).context(|| format!("could not read file `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use std::path::Path;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::fs::DirEntry;
|
use crate::fs::DirEntry;
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
|
use crate::path::Path;
|
||||||
use crate::stream::Stream;
|
use crate::stream::Stream;
|
||||||
use crate::task::{blocking, Context, JoinHandle, Poll};
|
use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Returns a stream of entries in a directory.
|
/// Returns a stream of entries in a directory.
|
||||||
///
|
///
|
||||||
|
@ -45,9 +46,12 @@ use crate::task::{blocking, Context, JoinHandle, Poll};
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
|
pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::read_dir(path) })
|
spawn_blocking(move || {
|
||||||
.await
|
std::fs::read_dir(&path)
|
||||||
.map(ReadDir::new)
|
.context(|| format!("could not read directory `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(ReadDir::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A stream of entries in a directory.
|
/// A stream of entries in a directory.
|
||||||
|
@ -91,7 +95,7 @@ impl Stream for ReadDir {
|
||||||
let mut inner = opt.take().unwrap();
|
let mut inner = opt.take().unwrap();
|
||||||
|
|
||||||
// Start the operation asynchronously.
|
// Start the operation asynchronously.
|
||||||
self.0 = State::Busy(blocking::spawn(async move {
|
self.0 = State::Busy(spawn_blocking(move || {
|
||||||
let next = inner.next();
|
let next = inner.next();
|
||||||
(inner, next)
|
(inner, next)
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::{Path, PathBuf};
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Reads a symbolic link and returns the path it points to.
|
/// Reads a symbolic link and returns the path it points to.
|
||||||
///
|
///
|
||||||
|
@ -29,5 +29,10 @@ use crate::task::blocking;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
|
pub async fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::read_link(path) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::read_link(&path)
|
||||||
|
.map(Into::into)
|
||||||
|
.context(|| format!("could not read link `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Reads the entire contents of a file as a string.
|
/// Reads the entire contents of a file as a string.
|
||||||
///
|
///
|
||||||
|
@ -38,5 +38,9 @@ use crate::task::blocking;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
|
pub async fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::read_to_string(path) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::read_to_string(&path)
|
||||||
|
.context(|| format!("could not read file `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Removes an empty directory.
|
/// Removes an empty directory.
|
||||||
///
|
///
|
||||||
|
@ -30,5 +30,9 @@ use crate::task::blocking;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::remove_dir(path) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::remove_dir(&path)
|
||||||
|
.context(|| format!("could not remove directory `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Removes a directory and all of its contents.
|
/// Removes a directory and all of its contents.
|
||||||
///
|
///
|
||||||
|
@ -30,5 +30,9 @@ use crate::task::blocking;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::remove_dir_all(path) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::remove_dir_all(&path)
|
||||||
|
.context(|| format!("could not remove directory `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Removes a file.
|
/// Removes a file.
|
||||||
///
|
///
|
||||||
|
@ -30,5 +30,9 @@ use crate::task::blocking;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::remove_file(path) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::remove_file(&path)
|
||||||
|
.context(|| format!("could not remove file `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Renames a file or directory to a new location.
|
/// Renames a file or directory to a new location.
|
||||||
///
|
///
|
||||||
|
@ -35,5 +35,14 @@ use crate::task::blocking;
|
||||||
pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
|
pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
|
||||||
let from = from.as_ref().to_owned();
|
let from = from.as_ref().to_owned();
|
||||||
let to = to.as_ref().to_owned();
|
let to = to.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::rename(&from, &to) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::rename(&from, &to).context(|| {
|
||||||
|
format!(
|
||||||
|
"could not rename `{}` to `{}`",
|
||||||
|
from.display(),
|
||||||
|
to.display()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::fs::Permissions;
|
use crate::fs::Permissions;
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
/// Changes the permissions of a file or directory.
|
/// Changes the permissions of a file or directory.
|
||||||
///
|
///
|
||||||
|
@ -33,5 +32,5 @@ use crate::task::blocking;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
|
pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::set_permissions(path, perm) }).await
|
spawn_blocking(move || std::fs::set_permissions(path, perm)).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::fs::Metadata;
|
use crate::fs::Metadata;
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
/// Reads metadata for a path without following symbolic links.
|
/// Reads metadata for a path without following symbolic links.
|
||||||
///
|
///
|
||||||
|
@ -35,5 +34,5 @@ use crate::task::blocking;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
|
pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::symlink_metadata(path) }).await
|
spawn_blocking(move || std::fs::symlink_metadata(path)).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Writes a slice of bytes as the new contents of a file.
|
/// Writes a slice of bytes as the new contents of a file.
|
||||||
///
|
///
|
||||||
|
@ -34,5 +34,9 @@ use crate::task::blocking;
|
||||||
pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
|
pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
let contents = contents.as_ref().to_owned();
|
let contents = contents.as_ref().to_owned();
|
||||||
blocking::spawn(async move { std::fs::write(path, contents) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::write(&path, contents)
|
||||||
|
.context(|| format!("could not write to file `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
43
src/future/future/delay.rs
Normal file
43
src/future/future/delay.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
use crate::utils::{timer_after, Timer};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct DelayFuture<F> {
|
||||||
|
#[pin]
|
||||||
|
future: F,
|
||||||
|
#[pin]
|
||||||
|
delay: Timer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> DelayFuture<F> {
|
||||||
|
pub fn new(future: F, dur: Duration) -> DelayFuture<F> {
|
||||||
|
let delay = timer_after(dur);
|
||||||
|
|
||||||
|
DelayFuture { future, delay }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Future> Future for DelayFuture<F> {
|
||||||
|
type Output = F::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
|
||||||
|
match this.delay.poll(cx) {
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
Poll::Ready(_) => match this.future.poll(cx) {
|
||||||
|
Poll::Ready(v) => Poll::Ready(v),
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
src/future/future/flatten.rs
Normal file
52
src/future/future/flatten.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use crate::future::IntoFuture;
|
||||||
|
use crate::task::{ready, Context, Poll};
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct FlattenFuture<Fut1, Fut2> {
|
||||||
|
state: State<Fut1, Fut2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum State<Fut1, Fut2> {
|
||||||
|
First(Fut1),
|
||||||
|
Second(Fut2),
|
||||||
|
Empty,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut1, Fut2> FlattenFuture<Fut1, Fut2> {
|
||||||
|
pub(crate) fn new(future: Fut1) -> FlattenFuture<Fut1, Fut2> {
|
||||||
|
FlattenFuture {
|
||||||
|
state: State::First(future),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut1> Future for FlattenFuture<Fut1, <Fut1::Output as IntoFuture>::Future>
|
||||||
|
where
|
||||||
|
Fut1: Future,
|
||||||
|
Fut1::Output: IntoFuture,
|
||||||
|
{
|
||||||
|
type Output = <Fut1::Output as IntoFuture>::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let Self { state } = unsafe { self.get_unchecked_mut() };
|
||||||
|
loop {
|
||||||
|
match state {
|
||||||
|
State::First(fut1) => {
|
||||||
|
let fut2 = ready!(unsafe { Pin::new_unchecked(fut1) }.poll(cx)).into_future();
|
||||||
|
*state = State::Second(fut2);
|
||||||
|
}
|
||||||
|
State::Second(fut2) => {
|
||||||
|
let v = ready!(unsafe { Pin::new_unchecked(fut2) }.poll(cx));
|
||||||
|
*state = State::Empty;
|
||||||
|
return Poll::Ready(v);
|
||||||
|
}
|
||||||
|
State::Empty => panic!("polled a completed future"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
src/future/future/join.rs
Normal file
60
src/future/future/join.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use crate::future::MaybeDone;
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct Join<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future,
|
||||||
|
{
|
||||||
|
#[pin] left: MaybeDone<L>,
|
||||||
|
#[pin] right: MaybeDone<R>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> Join<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(left: L, right: R) -> Self {
|
||||||
|
Self {
|
||||||
|
left: MaybeDone::new(left),
|
||||||
|
right: MaybeDone::new(right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> Future for Join<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future,
|
||||||
|
{
|
||||||
|
type Output = (L::Output, R::Output);
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
|
||||||
|
let mut left = this.left;
|
||||||
|
let mut right = this.right;
|
||||||
|
|
||||||
|
let is_left_ready = Future::poll(Pin::new(&mut left), cx).is_ready();
|
||||||
|
if is_left_ready && right.as_ref().output().is_some() {
|
||||||
|
return Poll::Ready((left.take().unwrap(), right.take().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_right_ready = Future::poll(Pin::new(&mut right), cx).is_ready();
|
||||||
|
if is_right_ready && left.as_ref().output().is_some() {
|
||||||
|
return Poll::Ready((left.take().unwrap(), right.take().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
432
src/future/future/mod.rs
Normal file
432
src/future/future/mod.rs
Normal file
|
@ -0,0 +1,432 @@
|
||||||
|
cfg_unstable! {
|
||||||
|
mod delay;
|
||||||
|
mod flatten;
|
||||||
|
mod race;
|
||||||
|
mod try_race;
|
||||||
|
mod join;
|
||||||
|
mod try_join;
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
use delay::DelayFuture;
|
||||||
|
use flatten::FlattenFuture;
|
||||||
|
use crate::future::IntoFuture;
|
||||||
|
use race::Race;
|
||||||
|
use try_race::TryRace;
|
||||||
|
use join::Join;
|
||||||
|
use try_join::TryJoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_unstable_default! {
|
||||||
|
use crate::future::timeout::TimeoutFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension_trait! {
|
||||||
|
use core::pin::Pin;
|
||||||
|
use core::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
A future represents an asynchronous computation.
|
||||||
|
|
||||||
|
A future is a value that may not have finished computing yet. This kind of
|
||||||
|
"asynchronous value" makes it possible for a thread to continue doing useful
|
||||||
|
work while it waits for the value to become available.
|
||||||
|
|
||||||
|
The [provided methods] do not really exist in the trait itself, but they become
|
||||||
|
available when [`FutureExt`] from the [prelude] is imported:
|
||||||
|
|
||||||
|
```
|
||||||
|
# #[allow(unused_imports)]
|
||||||
|
use async_std::prelude::*;
|
||||||
|
```
|
||||||
|
|
||||||
|
# The `poll` method
|
||||||
|
|
||||||
|
The core method of future, `poll`, *attempts* to resolve the future into a
|
||||||
|
final value. This method does not block if the value is not ready. Instead,
|
||||||
|
the current task is scheduled to be woken up when it's possible to make
|
||||||
|
further progress by `poll`ing again. The `context` passed to the `poll`
|
||||||
|
method can provide a [`Waker`], which is a handle for waking up the current
|
||||||
|
task.
|
||||||
|
|
||||||
|
When using a future, you generally won't call `poll` directly, but instead
|
||||||
|
`.await` the value.
|
||||||
|
|
||||||
|
[`Waker`]: ../task/struct.Waker.html
|
||||||
|
[provided methods]: #provided-methods
|
||||||
|
[`FutureExt`]: ../prelude/trait.FutureExt.html
|
||||||
|
[prelude]: ../prelude/index.html
|
||||||
|
"#]
|
||||||
|
pub trait Future {
|
||||||
|
#[doc = r#"
|
||||||
|
The type of value produced on completion.
|
||||||
|
"#]
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Attempt to resolve the future to a final value, registering
|
||||||
|
the current task for wakeup if the value is not yet available.
|
||||||
|
|
||||||
|
# Return value
|
||||||
|
|
||||||
|
This function returns:
|
||||||
|
|
||||||
|
- [`Poll::Pending`] if the future is not ready yet
|
||||||
|
- [`Poll::Ready(val)`] with the result `val` of this future if it
|
||||||
|
finished successfully.
|
||||||
|
|
||||||
|
Once a future has finished, clients should not `poll` it again.
|
||||||
|
|
||||||
|
When a future is not ready yet, `poll` returns `Poll::Pending` and
|
||||||
|
stores a clone of the [`Waker`] copied from the current [`Context`].
|
||||||
|
This [`Waker`] is then woken once the future can make progress.
|
||||||
|
For example, a future waiting for a socket to become
|
||||||
|
readable would call `.clone()` on the [`Waker`] and store it.
|
||||||
|
When a signal arrives elsewhere indicating that the socket is readable,
|
||||||
|
[`Waker::wake`] is called and the socket future's task is awoken.
|
||||||
|
Once a task has been woken up, it should attempt to `poll` the future
|
||||||
|
again, which may or may not produce a final value.
|
||||||
|
|
||||||
|
Note that on multiple calls to `poll`, only the [`Waker`] from the
|
||||||
|
[`Context`] passed to the most recent call should be scheduled to
|
||||||
|
receive a wakeup.
|
||||||
|
|
||||||
|
# Runtime characteristics
|
||||||
|
|
||||||
|
Futures alone are *inert*; they must be *actively* `poll`ed to make
|
||||||
|
progress, meaning that each time the current task is woken up, it should
|
||||||
|
actively re-`poll` pending futures that it still has an interest in.
|
||||||
|
|
||||||
|
The `poll` function is not called repeatedly in a tight loop -- instead,
|
||||||
|
it should only be called when the future indicates that it is ready to
|
||||||
|
make progress (by calling `wake()`). If you're familiar with the
|
||||||
|
`poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures
|
||||||
|
typically do *not* suffer the same problems of "all wakeups must poll
|
||||||
|
all events"; they are more like `epoll(4)`.
|
||||||
|
|
||||||
|
An implementation of `poll` should strive to return quickly, and should
|
||||||
|
not block. Returning quickly prevents unnecessarily clogging up
|
||||||
|
threads or event loops. If it is known ahead of time that a call to
|
||||||
|
`poll` may end up taking awhile, the work should be offloaded to a
|
||||||
|
thread pool (or something similar) to ensure that `poll` can return
|
||||||
|
quickly.
|
||||||
|
|
||||||
|
# Panics
|
||||||
|
|
||||||
|
Once a future has completed (returned `Ready` from `poll`), calling its
|
||||||
|
`poll` method again may panic, block forever, or cause other kinds of
|
||||||
|
problems; the `Future` trait places no requirements on the effects of
|
||||||
|
such a call. However, as the `poll` method is not marked `unsafe`,
|
||||||
|
Rust's usual rules apply: calls must never cause undefined behavior
|
||||||
|
(memory corruption, incorrect use of `unsafe` functions, or the like),
|
||||||
|
regardless of the future's state.
|
||||||
|
|
||||||
|
[`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending
|
||||||
|
[`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready
|
||||||
|
[`Context`]: ../task/struct.Context.html
|
||||||
|
[`Waker`]: ../task/struct.Waker.html
|
||||||
|
[`Waker::wake`]: ../task/struct.Waker.html#method.wake
|
||||||
|
"#]
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Extension methods for [`Future`].
|
||||||
|
|
||||||
|
[`Future`]: ../future/trait.Future.html
|
||||||
|
"#]
|
||||||
|
pub trait FutureExt: core::future::Future {
|
||||||
|
/// Returns a Future that delays execution for a specified time.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// use async_std::prelude::*;
|
||||||
|
/// use async_std::future;
|
||||||
|
/// use std::time::Duration;
|
||||||
|
///
|
||||||
|
/// let a = future::ready(1).delay(Duration::from_millis(2000));
|
||||||
|
/// dbg!(a.await);
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn delay(self, dur: Duration) -> impl Future<Output = Self::Output> [DelayFuture<Self>]
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
DelayFuture::new(self, dur)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flatten out the execution of this future when the result itself
|
||||||
|
/// can be converted into another future.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// use async_std::prelude::*;
|
||||||
|
///
|
||||||
|
/// let nested_future = async { async { 1 } };
|
||||||
|
/// let future = nested_future.flatten();
|
||||||
|
/// assert_eq!(future.await, 1);
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn flatten(
|
||||||
|
self,
|
||||||
|
) -> impl Future<Output = <Self::Output as IntoFuture>::Output>
|
||||||
|
[FlattenFuture<Self, <Self::Output as IntoFuture>::Future>]
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
<Self as Future>::Output: IntoFuture,
|
||||||
|
{
|
||||||
|
FlattenFuture::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Waits for one of two similarly-typed futures to complete.
|
||||||
|
|
||||||
|
Awaits multiple futures simultaneously, returning the output of the
|
||||||
|
first future that completes.
|
||||||
|
|
||||||
|
This function will return a new future which awaits for either one of both
|
||||||
|
futures to complete. If multiple futures are completed at the same time,
|
||||||
|
resolution will occur in the order that they have been passed.
|
||||||
|
|
||||||
|
Note that this function consumes all futures passed, and once a future is
|
||||||
|
completed, all other futures are dropped.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# async_std::task::block_on(async {
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::future;
|
||||||
|
|
||||||
|
let a = future::pending();
|
||||||
|
let b = future::ready(1u8);
|
||||||
|
let c = future::ready(2u8);
|
||||||
|
|
||||||
|
let f = a.race(b).race(c);
|
||||||
|
assert_eq!(f.await, 1u8);
|
||||||
|
# });
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn race<F>(
|
||||||
|
self,
|
||||||
|
other: F,
|
||||||
|
) -> impl Future<Output = <Self as std::future::Future>::Output> [Race<Self, F>]
|
||||||
|
where
|
||||||
|
Self: std::future::Future + Sized,
|
||||||
|
F: std::future::Future<Output = <Self as std::future::Future>::Output>,
|
||||||
|
{
|
||||||
|
Race::new(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Waits for one of two similarly-typed fallible futures to complete.
|
||||||
|
|
||||||
|
Awaits multiple futures simultaneously, returning all results once complete.
|
||||||
|
|
||||||
|
`try_race` is similar to [`race`], but keeps going if a future
|
||||||
|
resolved to an error until all futures have been resolved. In which case
|
||||||
|
an error is returned.
|
||||||
|
|
||||||
|
The ordering of which value is yielded when two futures resolve
|
||||||
|
simultaneously is intentionally left unspecified.
|
||||||
|
|
||||||
|
[`race`]: #method.race
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::future;
|
||||||
|
use std::io::{Error, ErrorKind};
|
||||||
|
|
||||||
|
let a = future::pending::<Result<_, Error>>();
|
||||||
|
let b = future::ready(Err(Error::from(ErrorKind::Other)));
|
||||||
|
let c = future::ready(Ok(1u8));
|
||||||
|
|
||||||
|
let f = a.try_race(b).try_race(c);
|
||||||
|
assert_eq!(f.await?, 1u8);
|
||||||
|
#
|
||||||
|
# Ok(()) }) }
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn try_race<F, T, E>(
|
||||||
|
self,
|
||||||
|
other: F
|
||||||
|
) -> impl Future<Output = <Self as std::future::Future>::Output> [TryRace<Self, F>]
|
||||||
|
where
|
||||||
|
Self: std::future::Future<Output = Result<T, E>> + Sized,
|
||||||
|
F: std::future::Future<Output = <Self as std::future::Future>::Output>,
|
||||||
|
{
|
||||||
|
TryRace::new(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Waits for two similarly-typed futures to complete.
|
||||||
|
|
||||||
|
Awaits multiple futures simultaneously, returning the output of the
|
||||||
|
futures once both complete.
|
||||||
|
|
||||||
|
This function returns a new future which polls both futures
|
||||||
|
concurrently.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# async_std::task::block_on(async {
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::future;
|
||||||
|
|
||||||
|
let a = future::ready(1u8);
|
||||||
|
let b = future::ready(2u16);
|
||||||
|
|
||||||
|
let f = a.join(b);
|
||||||
|
assert_eq!(f.await, (1u8, 2u16));
|
||||||
|
# });
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
#[cfg(any(feature = "unstable", feature = "docs"))]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn join<F>(
|
||||||
|
self,
|
||||||
|
other: F
|
||||||
|
) -> impl Future<Output = (<Self as std::future::Future>::Output, <F as std::future::Future>::Output)> [Join<Self, F>]
|
||||||
|
where
|
||||||
|
Self: std::future::Future + Sized,
|
||||||
|
F: std::future::Future,
|
||||||
|
{
|
||||||
|
Join::new(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Waits for two similarly-typed fallible futures to complete.
|
||||||
|
|
||||||
|
Awaits multiple futures simultaneously, returning all results once
|
||||||
|
complete.
|
||||||
|
|
||||||
|
`try_join` is similar to [`join`], but returns an error immediately
|
||||||
|
if a future resolves to an error.
|
||||||
|
|
||||||
|
[`join`]: #method.join
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::future;
|
||||||
|
|
||||||
|
let a = future::ready(Err::<u8, &str>("Error"));
|
||||||
|
let b = future::ready(Ok(1u8));
|
||||||
|
|
||||||
|
let f = a.try_join(b);
|
||||||
|
assert_eq!(f.await, Err("Error"));
|
||||||
|
|
||||||
|
let a = future::ready(Ok::<u8, String>(1u8));
|
||||||
|
let b = future::ready(Ok::<u16, String>(2u16));
|
||||||
|
|
||||||
|
let f = a.try_join(b);
|
||||||
|
assert_eq!(f.await, Ok((1u8, 2u16)));
|
||||||
|
#
|
||||||
|
# Ok(()) }) }
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
#[cfg(any(feature = "unstable", feature = "docs"))]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn try_join<F, A, B, E>(
|
||||||
|
self,
|
||||||
|
other: F
|
||||||
|
) -> impl Future<Output = Result<(A, B), E>> [TryJoin<Self, F>]
|
||||||
|
where
|
||||||
|
Self: std::future::Future<Output = Result<A, E>> + Sized,
|
||||||
|
F: std::future::Future<Output = Result<B, E>>,
|
||||||
|
{
|
||||||
|
TryJoin::new(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Waits for both the future and a timeout, if the timeout completes before
|
||||||
|
the future, it returns an TimeoutError.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```
|
||||||
|
# async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::future;
|
||||||
|
|
||||||
|
let fut = future::ready(0);
|
||||||
|
let dur = Duration::from_millis(100);
|
||||||
|
let res = fut.timeout(dur).await;
|
||||||
|
assert!(res.is_ok());
|
||||||
|
|
||||||
|
let fut = future::pending::<()>();
|
||||||
|
let dur = Duration::from_millis(100);
|
||||||
|
let res = fut.timeout(dur).await;
|
||||||
|
assert!(res.is_err())
|
||||||
|
#
|
||||||
|
# });
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
#[cfg(any(all(feature = "default", feature = "unstable"), feature = "docs"))]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn timeout(self, dur: Duration) -> impl Future<Output = Self::Output> [TimeoutFuture<Self>]
|
||||||
|
where Self: Sized
|
||||||
|
{
|
||||||
|
TimeoutFuture::new(self, dur)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Future + Unpin + ?Sized> Future for Box<F> {
|
||||||
|
type Output = F::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Future + Unpin + ?Sized> Future for &mut F {
|
||||||
|
type Output = F::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P> Future for Pin<P>
|
||||||
|
where
|
||||||
|
P: DerefMut + Unpin,
|
||||||
|
<P as Deref>::Target: Future,
|
||||||
|
{
|
||||||
|
type Output = <<P as Deref>::Target as Future>::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Future> Future for std::panic::AssertUnwindSafe<F> {
|
||||||
|
type Output = F::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
src/future/future/race.rs
Normal file
57
src/future/future/race.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use crate::future::MaybeDone;
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct Race<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>
|
||||||
|
{
|
||||||
|
#[pin] left: MaybeDone<L>,
|
||||||
|
#[pin] right: MaybeDone<R>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> Race<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(left: L, right: R) -> Self {
|
||||||
|
Self {
|
||||||
|
left: MaybeDone::new(left),
|
||||||
|
right: MaybeDone::new(right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> Future for Race<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>,
|
||||||
|
{
|
||||||
|
type Output = L::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
|
||||||
|
let mut left = this.left;
|
||||||
|
if Future::poll(Pin::new(&mut left), cx).is_ready() {
|
||||||
|
return Poll::Ready(left.take().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut right = this.right;
|
||||||
|
if Future::poll(Pin::new(&mut right), cx).is_ready() {
|
||||||
|
return Poll::Ready(right.take().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
72
src/future/future/try_join.rs
Normal file
72
src/future/future/try_join.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use crate::future::MaybeDone;
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct TryJoin<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future,
|
||||||
|
{
|
||||||
|
#[pin] left: MaybeDone<L>,
|
||||||
|
#[pin] right: MaybeDone<R>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> TryJoin<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(left: L, right: R) -> Self {
|
||||||
|
Self {
|
||||||
|
left: MaybeDone::new(left),
|
||||||
|
right: MaybeDone::new(right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R, A, B, E> Future for TryJoin<L, R>
|
||||||
|
where
|
||||||
|
L: Future<Output = Result<A, E>>,
|
||||||
|
R: Future<Output = Result<B, E>>,
|
||||||
|
{
|
||||||
|
type Output = Result<(A, B), E>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
|
||||||
|
let mut left = this.left;
|
||||||
|
let mut right = this.right;
|
||||||
|
|
||||||
|
if Future::poll(Pin::new(&mut left), cx).is_ready() {
|
||||||
|
if left.as_ref().output().unwrap().is_err() {
|
||||||
|
return Poll::Ready(Err(left.take().unwrap().err().unwrap()));
|
||||||
|
} else if right.as_ref().output().is_some() {
|
||||||
|
return Poll::Ready(Ok((
|
||||||
|
left.take().unwrap().ok().unwrap(),
|
||||||
|
right.take().unwrap().ok().unwrap(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if Future::poll(Pin::new(&mut right), cx).is_ready() {
|
||||||
|
if right.as_ref().output().unwrap().is_err() {
|
||||||
|
return Poll::Ready(Err(right.take().unwrap().err().unwrap()));
|
||||||
|
} else if left.as_ref().output().is_some() {
|
||||||
|
return Poll::Ready(Ok((
|
||||||
|
left.take().unwrap().ok().unwrap(),
|
||||||
|
right.take().unwrap().ok().unwrap(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
66
src/future/future/try_race.rs
Normal file
66
src/future/future/try_race.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use crate::future::MaybeDone;
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct TryRace<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>
|
||||||
|
{
|
||||||
|
#[pin] left: MaybeDone<L>,
|
||||||
|
#[pin] right: MaybeDone<R>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> TryRace<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(left: L, right: R) -> Self {
|
||||||
|
Self {
|
||||||
|
left: MaybeDone::new(left),
|
||||||
|
right: MaybeDone::new(right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R, T, E> Future for TryRace<L, R>
|
||||||
|
where
|
||||||
|
L: Future<Output = Result<T, E>>,
|
||||||
|
R: Future<Output = L::Output>,
|
||||||
|
{
|
||||||
|
type Output = L::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
let mut left_errored = false;
|
||||||
|
|
||||||
|
// Check if the left future is ready & successful. Continue if not.
|
||||||
|
let mut left = this.left;
|
||||||
|
if Future::poll(Pin::new(&mut left), cx).is_ready() {
|
||||||
|
if left.as_ref().output().unwrap().is_ok() {
|
||||||
|
return Poll::Ready(left.take().unwrap());
|
||||||
|
} else {
|
||||||
|
left_errored = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the right future is ready & successful. Return err if left
|
||||||
|
// future also resolved to err. Continue if not.
|
||||||
|
let mut right = this.right;
|
||||||
|
let is_ready = Future::poll(Pin::new(&mut right), cx).is_ready();
|
||||||
|
if is_ready && (right.as_ref().output().unwrap().is_ok() || left_errored) {
|
||||||
|
return Poll::Ready(right.take().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
53
src/future/into_future.rs
Normal file
53
src/future/into_future.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
/// Convert a type into a `Future`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use async_std::future::{Future, IntoFuture};
|
||||||
|
/// use async_std::io;
|
||||||
|
/// use async_std::pin::Pin;
|
||||||
|
///
|
||||||
|
/// struct Client;
|
||||||
|
///
|
||||||
|
/// impl Client {
|
||||||
|
/// pub async fn send(self) -> io::Result<()> {
|
||||||
|
/// // Send a request
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl IntoFuture for Client {
|
||||||
|
/// type Output = io::Result<()>;
|
||||||
|
///
|
||||||
|
/// type Future = Pin<Box<dyn Future<Output = Self::Output>>>;
|
||||||
|
///
|
||||||
|
/// fn into_future(self) -> Self::Future {
|
||||||
|
/// Box::pin(async {
|
||||||
|
/// self.send().await
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
pub trait IntoFuture {
|
||||||
|
/// The type of value produced on completion.
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
/// Which kind of future are we turning this into?
|
||||||
|
type Future: Future<Output = Self::Output>;
|
||||||
|
|
||||||
|
/// Create a future from a value
|
||||||
|
fn into_future(self) -> Self::Future;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Future> IntoFuture for T {
|
||||||
|
type Output = T::Output;
|
||||||
|
type Future = T;
|
||||||
|
|
||||||
|
fn into_future(self) -> Self::Future {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
79
src/future/maybe_done.rs
Normal file
79
src/future/maybe_done.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
//! A type that wraps a future to keep track of its completion status.
|
||||||
|
//!
|
||||||
|
//! This implementation was taken from the original `macro_rules` `join/try_join`
|
||||||
|
//! macros in the `futures-preview` crate.
|
||||||
|
|
||||||
|
use std::future::Future;
|
||||||
|
use std::mem;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
|
use futures_core::ready;
|
||||||
|
|
||||||
|
/// A future that may have completed.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum MaybeDone<Fut: Future> {
|
||||||
|
/// A not-yet-completed future
|
||||||
|
Future(Fut),
|
||||||
|
|
||||||
|
/// The output of the completed future
|
||||||
|
Done(Fut::Output),
|
||||||
|
|
||||||
|
/// The empty variant after the result of a [`MaybeDone`] has been
|
||||||
|
/// taken using the [`take`](MaybeDone::take) method.
|
||||||
|
Gone,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut: Future> MaybeDone<Fut> {
|
||||||
|
/// Create a new instance of `MaybeDone`.
|
||||||
|
pub(crate) fn new(future: Fut) -> MaybeDone<Fut> {
|
||||||
|
Self::Future(future)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an [`Option`] containing a reference to the output of the future.
|
||||||
|
/// The output of this method will be [`Some`] if and only if the inner
|
||||||
|
/// future has been completed and [`take`](MaybeDone::take)
|
||||||
|
/// has not yet been called.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn output(self: Pin<&Self>) -> Option<&Fut::Output> {
|
||||||
|
let this = self.get_ref();
|
||||||
|
match this {
|
||||||
|
MaybeDone::Done(res) => Some(res),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to take the output of a `MaybeDone` without driving it
|
||||||
|
/// towards completion.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn take(self: Pin<&mut Self>) -> Option<Fut::Output> {
|
||||||
|
unsafe {
|
||||||
|
let this = self.get_unchecked_mut();
|
||||||
|
match this {
|
||||||
|
MaybeDone::Done(_) => {}
|
||||||
|
MaybeDone::Future(_) | MaybeDone::Gone => return None,
|
||||||
|
};
|
||||||
|
if let MaybeDone::Done(output) = mem::replace(this, MaybeDone::Gone) {
|
||||||
|
Some(output)
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut: Future> Future for MaybeDone<Fut> {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let res = unsafe {
|
||||||
|
match Pin::as_mut(&mut self).get_unchecked_mut() {
|
||||||
|
MaybeDone::Future(a) => ready!(Pin::new_unchecked(a).poll(cx)),
|
||||||
|
MaybeDone::Done(_) => return Poll::Ready(()),
|
||||||
|
MaybeDone::Gone => panic!("MaybeDone polled after value taken"),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.set(MaybeDone::Done(res));
|
||||||
|
Poll::Ready(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,63 +4,71 @@
|
||||||
//!
|
//!
|
||||||
//! Often it's desireable to await multiple futures as if it was a single
|
//! Often it's desireable to await multiple futures as if it was a single
|
||||||
//! future. The `join` family of operations converts multiple futures into a
|
//! future. The `join` family of operations converts multiple futures into a
|
||||||
//! single future that returns all of their outputs. The `select` family of
|
//! single future that returns all of their outputs. The `race` family of
|
||||||
//! operations converts multiple future into a single future that returns the
|
//! operations converts multiple future into a single future that returns the
|
||||||
//! first output.
|
//! first output.
|
||||||
//!
|
//!
|
||||||
//! For operating on futures the following macros can be used:
|
//! For operating on futures the following functions can be used:
|
||||||
//!
|
//!
|
||||||
//! | Name | Return signature | When does it return? |
|
//! | Name | Return signature | When does it return? |
|
||||||
//! | --- | --- | --- |
|
//! | --- | --- | --- |
|
||||||
//! | `future::join` | `(T1, T2)` | Wait for all to complete
|
//! | [`Future::join`] | `(T1, T2)` | Wait for all to complete
|
||||||
//! | `future::select` | `T` | Return on first value
|
//! | [`Future::race`] | `T` | Return on first value
|
||||||
//!
|
//!
|
||||||
//! ## Fallible Futures Concurrency
|
//! ## Fallible Futures Concurrency
|
||||||
//!
|
//!
|
||||||
//! For operating on futures that return `Result` additional `try_` variants of
|
//! For operating on futures that return `Result` additional `try_` variants of
|
||||||
//! the macros mentioned before can be used. These macros are aware of `Result`,
|
//! the functions mentioned before can be used. These functions are aware of `Result`,
|
||||||
//! and will behave slightly differently from their base variants.
|
//! and will behave slightly differently from their base variants.
|
||||||
//!
|
//!
|
||||||
//! In the case of `try_join`, if any of the futures returns `Err` all
|
//! In the case of `try_join`, if any of the futures returns `Err` all
|
||||||
//! futures are dropped and an error is returned. This is referred to as
|
//! futures are dropped and an error is returned. This is referred to as
|
||||||
//! "short-circuiting".
|
//! "short-circuiting".
|
||||||
//!
|
//!
|
||||||
//! In the case of `try_select`, instead of returning the first future that
|
//! In the case of `try_race`, instead of returning the first future that
|
||||||
//! completes it returns the first future that _successfully_ completes. This
|
//! completes it returns the first future that _successfully_ completes. This
|
||||||
//! means `try_select` will keep going until any one of the futures returns
|
//! means `try_race` will keep going until any one of the futures returns
|
||||||
//! `Ok`, or _all_ futures have returned `Err`.
|
//! `Ok`, or _all_ futures have returned `Err`.
|
||||||
//!
|
//!
|
||||||
//! However sometimes it can be useful to use the base variants of the macros
|
//! However sometimes it can be useful to use the base variants of the functions
|
||||||
//! even on futures that return `Result`. Here is an overview of operations that
|
//! even on futures that return `Result`. Here is an overview of operations that
|
||||||
//! work on `Result`, and their respective semantics:
|
//! work on `Result`, and their respective semantics:
|
||||||
//!
|
//!
|
||||||
//! | Name | Return signature | When does it return? |
|
//! | Name | Return signature | When does it return? |
|
||||||
//! | --- | --- | --- |
|
//! | --- | --- | --- |
|
||||||
//! | `future::join` | `(Result<T, E>, Result<T, E>)` | Wait for all to complete
|
//! | [`Future::join`] | `(Result<T, E>, Result<T, E>)` | Wait for all to complete
|
||||||
//! | `future::try_join` | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete
|
//! | [`Future::try_join`] | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete
|
||||||
//! | `future::select` | `Result<T, E>` | Return on first value
|
//! | [`Future::race`] | `Result<T, E>` | Return on first value
|
||||||
//! | `future::try_select` | `Result<T, E>` | Return on first `Ok`, reject on last Err
|
//! | [`Future::try_race`] | `Result<T, E>` | Return on first `Ok`, reject on last Err
|
||||||
|
//!
|
||||||
|
//! [`Future::join`]: trait.Future.html#method.join
|
||||||
|
//! [`Future::try_join`]: trait.Future.html#method.try_join
|
||||||
|
//! [`Future::race`]: trait.Future.html#method.race
|
||||||
|
//! [`Future::try_race`]: trait.Future.html#method.try_race
|
||||||
|
|
||||||
#[doc(inline)]
|
cfg_alloc! {
|
||||||
pub use std::future::Future;
|
pub use future::Future;
|
||||||
|
pub(crate) mod future;
|
||||||
#[doc(inline)]
|
}
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
|
||||||
pub use async_macros::{join, select, try_join, try_select};
|
cfg_std! {
|
||||||
|
pub use pending::pending;
|
||||||
use cfg_if::cfg_if;
|
pub use poll_fn::poll_fn;
|
||||||
|
pub use ready::ready;
|
||||||
pub use pending::pending;
|
|
||||||
pub use poll_fn::poll_fn;
|
mod pending;
|
||||||
pub use ready::ready;
|
mod poll_fn;
|
||||||
|
mod ready;
|
||||||
mod pending;
|
}
|
||||||
mod poll_fn;
|
|
||||||
mod ready;
|
#[cfg(any(feature = "unstable", feature = "default"))]
|
||||||
|
pub use timeout::{timeout, TimeoutError};
|
||||||
cfg_if! {
|
#[cfg(any(feature = "unstable", feature = "default"))]
|
||||||
if #[cfg(any(feature = "unstable", feature = "docs"))] {
|
mod timeout;
|
||||||
mod timeout;
|
|
||||||
pub use timeout::{timeout, TimeoutError};
|
cfg_unstable! {
|
||||||
}
|
pub use into_future::IntoFuture;
|
||||||
|
pub(crate) use maybe_done::MaybeDone;
|
||||||
|
mod into_future;
|
||||||
|
mod maybe_done;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
use std::future::Future;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
/// Never resolves to a value.
|
/// Never resolves to a value.
|
||||||
|
@ -9,7 +9,7 @@ use crate::task::{Context, Poll};
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # fn main() { async_std::task::block_on(async {
|
/// # async_std::task::block_on(async {
|
||||||
/// #
|
/// #
|
||||||
/// use std::time::Duration;
|
/// use std::time::Duration;
|
||||||
///
|
///
|
||||||
|
@ -22,7 +22,7 @@ use crate::task::{Context, Poll};
|
||||||
/// let res: io::Result<()> = io::timeout(dur, fut).await;
|
/// let res: io::Result<()> = io::timeout(dur, fut).await;
|
||||||
/// assert!(res.is_err());
|
/// assert!(res.is_err());
|
||||||
/// #
|
/// #
|
||||||
/// # }) }
|
/// # })
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn pending<T>() -> T {
|
pub async fn pending<T>() -> T {
|
||||||
let fut = Pending {
|
let fut = Pending {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
/// Creates a new future wrapping around a function returning [`Poll`].
|
/// Creates a new future wrapping around a function returning [`Poll`].
|
||||||
|
@ -10,7 +10,7 @@ use crate::task::{Context, Poll};
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # fn main() { async_std::task::block_on(async {
|
/// # async_std::task::block_on(async {
|
||||||
/// #
|
/// #
|
||||||
/// use async_std::future;
|
/// use async_std::future;
|
||||||
/// use async_std::task::{Context, Poll};
|
/// use async_std::task::{Context, Poll};
|
||||||
|
@ -21,7 +21,7 @@ use crate::task::{Context, Poll};
|
||||||
///
|
///
|
||||||
/// assert_eq!(future::poll_fn(poll_greeting).await, "hello world");
|
/// assert_eq!(future::poll_fn(poll_greeting).await, "hello world");
|
||||||
/// #
|
/// #
|
||||||
/// # }) }
|
/// # })
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn poll_fn<F, T>(f: F) -> T
|
pub async fn poll_fn<F, T>(f: F) -> T
|
||||||
where
|
where
|
||||||
|
|
|
@ -7,13 +7,13 @@
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # fn main() { async_std::task::block_on(async {
|
/// # async_std::task::block_on(async {
|
||||||
/// #
|
/// #
|
||||||
/// use async_std::future;
|
/// use async_std::future;
|
||||||
///
|
///
|
||||||
/// assert_eq!(future::ready(10).await, 10);
|
/// assert_eq!(future::ready(10).await, 10);
|
||||||
/// #
|
/// #
|
||||||
/// # }) }
|
/// # })
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn ready<T>(val: T) -> T {
|
pub async fn ready<T>(val: T) -> T {
|
||||||
val
|
val
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures_timer::Delay;
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
use crate::utils::{timer_after, Timer};
|
||||||
|
|
||||||
/// Awaits a future or times out after a duration of time.
|
/// Awaits a future or times out after a duration of time.
|
||||||
///
|
///
|
||||||
|
@ -28,39 +29,41 @@ use crate::task::{Context, Poll};
|
||||||
/// #
|
/// #
|
||||||
/// # Ok(()) }) }
|
/// # Ok(()) }) }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
|
||||||
#[cfg(any(feature = "unstable", feature = "docs"))]
|
|
||||||
pub async fn timeout<F, T>(dur: Duration, f: F) -> Result<T, TimeoutError>
|
pub async fn timeout<F, T>(dur: Duration, f: F) -> Result<T, TimeoutError>
|
||||||
where
|
where
|
||||||
F: Future<Output = T>,
|
F: Future<Output = T>,
|
||||||
{
|
{
|
||||||
let f = TimeoutFuture {
|
TimeoutFuture::new(f, dur).await
|
||||||
future: f,
|
|
||||||
delay: Delay::new(dur),
|
|
||||||
};
|
|
||||||
f.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A future that times out after a duration of time.
|
pin_project! {
|
||||||
#[doc(hidden)]
|
/// A future that times out after a duration of time.
|
||||||
#[allow(missing_debug_implementations)]
|
pub struct TimeoutFuture<F> {
|
||||||
struct TimeoutFuture<F> {
|
#[pin]
|
||||||
future: F,
|
future: F,
|
||||||
delay: Delay,
|
#[pin]
|
||||||
|
delay: Timer,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> TimeoutFuture<F> {
|
impl<F> TimeoutFuture<F> {
|
||||||
pin_utils::unsafe_pinned!(future: F);
|
#[allow(dead_code)]
|
||||||
pin_utils::unsafe_pinned!(delay: Delay);
|
pub(super) fn new(future: F, dur: Duration) -> TimeoutFuture<F> {
|
||||||
|
TimeoutFuture {
|
||||||
|
future,
|
||||||
|
delay: timer_after(dur),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Future> Future for TimeoutFuture<F> {
|
impl<F: Future> Future for TimeoutFuture<F> {
|
||||||
type Output = Result<F::Output, TimeoutError>;
|
type Output = Result<F::Output, TimeoutError>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
match self.as_mut().future().poll(cx) {
|
let this = self.project();
|
||||||
|
match this.future.poll(cx) {
|
||||||
Poll::Ready(v) => Poll::Ready(Ok(v)),
|
Poll::Ready(v) => Poll::Ready(Ok(v)),
|
||||||
Poll::Pending => match self.delay().poll(cx) {
|
Poll::Pending => match this.delay.poll(cx) {
|
||||||
Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _private: () })),
|
Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _private: () })),
|
||||||
Poll::Pending => Poll::Pending,
|
Poll::Pending => Poll::Pending,
|
||||||
},
|
},
|
||||||
|
@ -69,8 +72,6 @@ impl<F: Future> Future for TimeoutFuture<F> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error returned when a future times out.
|
/// An error returned when a future times out.
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
|
||||||
#[cfg(any(feature = "unstable", feature = "docs"))]
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub struct TimeoutError {
|
pub struct TimeoutError {
|
||||||
_private: (),
|
_private: (),
|
||||||
|
|
|
@ -2,50 +2,55 @@ use std::mem;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
use super::read_until_internal;
|
use super::read_until_internal;
|
||||||
use crate::io::{self, BufRead};
|
use crate::io::{self, BufRead};
|
||||||
use crate::stream::Stream;
|
use crate::stream::Stream;
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
/// A stream of lines in a byte stream.
|
pin_project! {
|
||||||
///
|
/// A stream of lines in a byte stream.
|
||||||
/// This stream is created by the [`lines`] method on types that implement [`BufRead`].
|
///
|
||||||
///
|
/// This stream is created by the [`lines`] method on types that implement [`BufRead`].
|
||||||
/// This type is an async version of [`std::io::Lines`].
|
///
|
||||||
///
|
/// This type is an async version of [`std::io::Lines`].
|
||||||
/// [`lines`]: trait.BufRead.html#method.lines
|
///
|
||||||
/// [`BufRead`]: trait.BufRead.html
|
/// [`lines`]: trait.BufRead.html#method.lines
|
||||||
/// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html
|
/// [`BufRead`]: trait.BufRead.html
|
||||||
#[derive(Debug)]
|
/// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html
|
||||||
pub struct Lines<R> {
|
#[derive(Debug)]
|
||||||
pub(crate) reader: R,
|
pub struct Lines<R> {
|
||||||
pub(crate) buf: String,
|
#[pin]
|
||||||
pub(crate) bytes: Vec<u8>,
|
pub(crate) reader: R,
|
||||||
pub(crate) read: usize,
|
pub(crate) buf: String,
|
||||||
|
pub(crate) bytes: Vec<u8>,
|
||||||
|
pub(crate) read: usize,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: BufRead> Stream for Lines<R> {
|
impl<R: BufRead> Stream for Lines<R> {
|
||||||
type Item = io::Result<String>;
|
type Item = io::Result<String>;
|
||||||
|
|
||||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
let Self {
|
let this = self.project();
|
||||||
reader,
|
let n = futures_core::ready!(read_line_internal(
|
||||||
buf,
|
this.reader,
|
||||||
bytes,
|
cx,
|
||||||
read,
|
this.buf,
|
||||||
} = unsafe { self.get_unchecked_mut() };
|
this.bytes,
|
||||||
let reader = unsafe { Pin::new_unchecked(reader) };
|
this.read
|
||||||
let n = futures_core::ready!(read_line_internal(reader, cx, buf, bytes, read))?;
|
))?;
|
||||||
if n == 0 && buf.is_empty() {
|
if n == 0 && this.buf.is_empty() {
|
||||||
return Poll::Ready(None);
|
return Poll::Ready(None);
|
||||||
}
|
}
|
||||||
if buf.ends_with('\n') {
|
if this.buf.ends_with('\n') {
|
||||||
buf.pop();
|
this.buf.pop();
|
||||||
if buf.ends_with('\r') {
|
if this.buf.ends_with('\r') {
|
||||||
buf.pop();
|
this.buf.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Poll::Ready(Some(Ok(mem::replace(buf, String::new()))))
|
Poll::Ready(Some(Ok(mem::replace(this.buf, String::new()))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,23 @@
|
||||||
mod lines;
|
mod lines;
|
||||||
mod read_line;
|
mod read_line;
|
||||||
mod read_until;
|
mod read_until;
|
||||||
|
mod split;
|
||||||
|
|
||||||
pub use lines::Lines;
|
pub use lines::Lines;
|
||||||
|
pub use split::Split;
|
||||||
|
|
||||||
use read_line::ReadLineFuture;
|
use read_line::ReadLineFuture;
|
||||||
use read_until::ReadUntilFuture;
|
use read_until::ReadUntilFuture;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
use crate::utils::extension_trait;
|
|
||||||
|
|
||||||
cfg_if! {
|
|
||||||
if #[cfg(feature = "docs")] {
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension_trait! {
|
extension_trait! {
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
#[doc = r#"
|
#[doc = r#"
|
||||||
Allows reading from a buffered byte stream.
|
Allows reading from a buffered byte stream.
|
||||||
|
|
||||||
|
@ -29,19 +25,21 @@ extension_trait! {
|
||||||
[`std::io::BufRead`].
|
[`std::io::BufRead`].
|
||||||
|
|
||||||
The [provided methods] do not really exist in the trait itself, but they become
|
The [provided methods] do not really exist in the trait itself, but they become
|
||||||
available when the prelude is imported:
|
available when [`BufReadExt`] from the [prelude] is imported:
|
||||||
|
|
||||||
```
|
```
|
||||||
# #[allow(unused_imports)]
|
# #[allow(unused_imports)]
|
||||||
use async_std::prelude::*;
|
use async_std::io::prelude::*;
|
||||||
```
|
```
|
||||||
|
|
||||||
[`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html
|
[`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html
|
||||||
[`futures::io::AsyncBufRead`]:
|
[`futures::io::AsyncBufRead`]:
|
||||||
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html
|
https://docs.rs/futures/0.3/futures/io/trait.AsyncBufRead.html
|
||||||
[provided methods]: #provided-methods
|
[provided methods]: #provided-methods
|
||||||
|
[`BufReadExt`]: ../io/prelude/trait.BufReadExt.html
|
||||||
|
[prelude]: ../prelude/index.html
|
||||||
"#]
|
"#]
|
||||||
pub trait BufRead [BufReadExt: futures_io::AsyncBufRead] {
|
pub trait BufRead {
|
||||||
#[doc = r#"
|
#[doc = r#"
|
||||||
Returns the contents of the internal buffer, filling it with more data from the
|
Returns the contents of the internal buffer, filling it with more data from the
|
||||||
inner reader if it is empty.
|
inner reader if it is empty.
|
||||||
|
@ -64,7 +62,14 @@ extension_trait! {
|
||||||
should no longer be returned in calls to `read`.
|
should no longer be returned in calls to `read`.
|
||||||
"#]
|
"#]
|
||||||
fn consume(self: Pin<&mut Self>, amt: usize);
|
fn consume(self: Pin<&mut Self>, amt: usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Extension methods for [`BufRead`].
|
||||||
|
|
||||||
|
[`BufRead`]: ../trait.BufRead.html
|
||||||
|
"#]
|
||||||
|
pub trait BufReadExt: futures_io::AsyncBufRead {
|
||||||
#[doc = r#"
|
#[doc = r#"
|
||||||
Reads all bytes into `buf` until the delimiter `byte` or EOF is reached.
|
Reads all bytes into `buf` until the delimiter `byte` or EOF is reached.
|
||||||
|
|
||||||
|
@ -226,6 +231,57 @@ extension_trait! {
|
||||||
read: 0,
|
read: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Returns a stream over the contents of this reader split on the byte `byte`.
|
||||||
|
|
||||||
|
The stream returned from this function will return instances of
|
||||||
|
[`io::Result`]`<`[`Vec<u8>`]`>`. Each vector returned will *not* have
|
||||||
|
the delimiter byte at the end.
|
||||||
|
|
||||||
|
This function will yield errors whenever [`read_until`] would have
|
||||||
|
also yielded an error.
|
||||||
|
|
||||||
|
[`io::Result`]: type.Result.html
|
||||||
|
[`Vec<u8>`]: ../vec/struct.Vec.html
|
||||||
|
[`read_until`]: #method.read_until
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
[`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In
|
||||||
|
this example, we use [`Cursor`] to iterate over all hyphen delimited
|
||||||
|
segments in a byte slice
|
||||||
|
|
||||||
|
[`Cursor`]: struct.Cursor.html
|
||||||
|
|
||||||
|
```
|
||||||
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::io;
|
||||||
|
|
||||||
|
let cursor = io::Cursor::new(b"lorem-ipsum-dolor");
|
||||||
|
|
||||||
|
let mut split_iter = cursor.split(b'-').map(|l| l.unwrap());
|
||||||
|
assert_eq!(split_iter.next().await, Some(b"lorem".to_vec()));
|
||||||
|
assert_eq!(split_iter.next().await, Some(b"ipsum".to_vec()));
|
||||||
|
assert_eq!(split_iter.next().await, Some(b"dolor".to_vec()));
|
||||||
|
assert_eq!(split_iter.next().await, None);
|
||||||
|
#
|
||||||
|
# Ok(()) }) }
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
fn split(self, byte: u8) -> Split<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Split {
|
||||||
|
reader: self,
|
||||||
|
buf: Vec::new(),
|
||||||
|
delim: byte,
|
||||||
|
read: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: BufRead + Unpin + ?Sized> BufRead for Box<T> {
|
impl<T: BufRead + Unpin + ?Sized> BufRead for Box<T> {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use super::read_until_internal;
|
use super::read_until_internal;
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, BufRead};
|
use crate::io::{self, BufRead};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
@ -37,8 +37,12 @@ impl<T: BufRead + Unpin + ?Sized> Future for ReadLineFuture<'_, T> {
|
||||||
))
|
))
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
debug_assert!(buf.is_empty());
|
#[allow(clippy::debug_assert_with_mut_call)]
|
||||||
debug_assert_eq!(*read, 0);
|
{
|
||||||
|
debug_assert!(buf.is_empty());
|
||||||
|
debug_assert_eq!(*read, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`.
|
// Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`.
|
||||||
mem::swap(unsafe { buf.as_mut_vec() }, bytes);
|
mem::swap(unsafe { buf.as_mut_vec() }, bytes);
|
||||||
Poll::Ready(ret)
|
Poll::Ready(ret)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use super::read_until_internal;
|
use super::read_until_internal;
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, BufRead};
|
use crate::io::{self, BufRead};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
|
51
src/io/buf_read/split.rs
Normal file
51
src/io/buf_read/split.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use std::mem;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use super::read_until_internal;
|
||||||
|
use crate::io::{self, BufRead};
|
||||||
|
use crate::stream::Stream;
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
/// A stream over the contents of an instance of [`BufRead`] split on a particular byte.
|
||||||
|
///
|
||||||
|
/// This stream is created by the [`split`] method on types that implement [`BufRead`].
|
||||||
|
///
|
||||||
|
/// This type is an async version of [`std::io::Split`].
|
||||||
|
///
|
||||||
|
/// [`split`]: trait.BufRead.html#method.lines
|
||||||
|
/// [`BufRead`]: trait.BufRead.html
|
||||||
|
/// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Split<R> {
|
||||||
|
#[pin]
|
||||||
|
pub(crate) reader: R,
|
||||||
|
pub(crate) buf: Vec<u8>,
|
||||||
|
pub(crate) read: usize,
|
||||||
|
pub(crate) delim: u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: BufRead> Stream for Split<R> {
|
||||||
|
type Item = io::Result<Vec<u8>>;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
let this = self.project();
|
||||||
|
let n = futures_core::ready!(read_until_internal(
|
||||||
|
this.reader,
|
||||||
|
cx,
|
||||||
|
*this.delim,
|
||||||
|
this.buf,
|
||||||
|
this.read
|
||||||
|
))?;
|
||||||
|
if n == 0 && this.buf.is_empty() {
|
||||||
|
return Poll::Ready(None);
|
||||||
|
}
|
||||||
|
if this.buf[this.buf.len() - 1] == *this.delim {
|
||||||
|
this.buf.pop();
|
||||||
|
}
|
||||||
|
Poll::Ready(Some(Ok(mem::replace(this.buf, vec![]))))
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,51 +2,54 @@ use std::io::{IoSliceMut, Read as _};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::{cmp, fmt};
|
use std::{cmp, fmt};
|
||||||
|
|
||||||
use crate::io::{self, BufRead, Read, Seek, SeekFrom};
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::io::{self, BufRead, Read, Seek, SeekFrom, DEFAULT_BUF_SIZE};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
const DEFAULT_CAPACITY: usize = 8 * 1024;
|
pin_project! {
|
||||||
|
/// Adds buffering to any reader.
|
||||||
/// Adds buffering to any reader.
|
///
|
||||||
///
|
/// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader`
|
||||||
/// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader`
|
/// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer
|
||||||
/// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer
|
/// of the incoming byte stream.
|
||||||
/// of the incoming byte stream.
|
///
|
||||||
///
|
/// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to
|
||||||
/// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to
|
/// the same file or network socket. It does not help when reading very large amounts at once, or
|
||||||
/// the same file or network socket. It does not help when reading very large amounts at once, or
|
/// reading just one or a few times. It also provides no advantage when reading from a source that
|
||||||
/// reading just one or a few times. It also provides no advantage when reading from a source that
|
/// is already in memory, like a `Vec<u8>`.
|
||||||
/// is already in memory, like a `Vec<u8>`.
|
///
|
||||||
///
|
/// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating
|
||||||
/// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating
|
/// multiple instances of a `BufReader` on the same stream can cause data loss.
|
||||||
/// multiple instances of a `BufReader` on the same stream can cause data loss.
|
///
|
||||||
///
|
/// This type is an async version of [`std::io::BufReader`].
|
||||||
/// This type is an async version of [`std::io::BufReader`].
|
///
|
||||||
///
|
/// [`Read`]: trait.Read.html
|
||||||
/// [`Read`]: trait.Read.html
|
/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
|
||||||
/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// ```no_run
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
/// #
|
||||||
/// #
|
/// use async_std::fs::File;
|
||||||
/// use async_std::fs::File;
|
/// use async_std::io::BufReader;
|
||||||
/// use async_std::io::BufReader;
|
/// use async_std::prelude::*;
|
||||||
/// use async_std::prelude::*;
|
///
|
||||||
///
|
/// let mut file = BufReader::new(File::open("a.txt").await?);
|
||||||
/// let mut file = BufReader::new(File::open("a.txt").await?);
|
///
|
||||||
///
|
/// let mut line = String::new();
|
||||||
/// let mut line = String::new();
|
/// file.read_line(&mut line).await?;
|
||||||
/// file.read_line(&mut line).await?;
|
/// #
|
||||||
/// #
|
/// # Ok(()) }) }
|
||||||
/// # Ok(()) }) }
|
/// ```
|
||||||
/// ```
|
pub struct BufReader<R> {
|
||||||
pub struct BufReader<R> {
|
#[pin]
|
||||||
inner: R,
|
inner: R,
|
||||||
buf: Box<[u8]>,
|
buf: Box<[u8]>,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
cap: usize,
|
cap: usize,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: io::Read> BufReader<R> {
|
impl<R: io::Read> BufReader<R> {
|
||||||
|
@ -67,7 +70,7 @@ impl<R: io::Read> BufReader<R> {
|
||||||
/// # Ok(()) }) }
|
/// # Ok(()) }) }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(inner: R) -> BufReader<R> {
|
pub fn new(inner: R) -> BufReader<R> {
|
||||||
BufReader::with_capacity(DEFAULT_CAPACITY, inner)
|
BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new buffered reader with the specified capacity.
|
/// Creates a new buffered reader with the specified capacity.
|
||||||
|
@ -95,10 +98,6 @@ impl<R: io::Read> BufReader<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> BufReader<R> {
|
impl<R> BufReader<R> {
|
||||||
pin_utils::unsafe_pinned!(inner: R);
|
|
||||||
pin_utils::unsafe_unpinned!(pos: usize);
|
|
||||||
pin_utils::unsafe_unpinned!(cap: usize);
|
|
||||||
|
|
||||||
/// Gets a reference to the underlying reader.
|
/// Gets a reference to the underlying reader.
|
||||||
///
|
///
|
||||||
/// It is inadvisable to directly read from the underlying reader.
|
/// It is inadvisable to directly read from the underlying reader.
|
||||||
|
@ -141,6 +140,13 @@ impl<R> BufReader<R> {
|
||||||
&mut self.inner
|
&mut self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a pinned mutable reference to the underlying reader.
|
||||||
|
///
|
||||||
|
/// It is inadvisable to directly read from the underlying reader.
|
||||||
|
fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> {
|
||||||
|
self.project().inner
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a reference to the internal buffer.
|
/// Returns a reference to the internal buffer.
|
||||||
///
|
///
|
||||||
/// This function will not attempt to fill the buffer if it is empty.
|
/// This function will not attempt to fill the buffer if it is empty.
|
||||||
|
@ -185,9 +191,10 @@ impl<R> BufReader<R> {
|
||||||
|
|
||||||
/// Invalidates all data in the internal buffer.
|
/// Invalidates all data in the internal buffer.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn discard_buffer(mut self: Pin<&mut Self>) {
|
fn discard_buffer(self: Pin<&mut Self>) {
|
||||||
*self.as_mut().pos() = 0;
|
let this = self.project();
|
||||||
*self.cap() = 0;
|
*this.pos = 0;
|
||||||
|
*this.cap = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +208,7 @@ impl<R: Read> Read for BufReader<R> {
|
||||||
// (larger than our internal buffer), bypass our internal buffer
|
// (larger than our internal buffer), bypass our internal buffer
|
||||||
// entirely.
|
// entirely.
|
||||||
if self.pos == self.cap && buf.len() >= self.buf.len() {
|
if self.pos == self.cap && buf.len() >= self.buf.len() {
|
||||||
let res = futures_core::ready!(self.as_mut().inner().poll_read(cx, buf));
|
let res = futures_core::ready!(self.as_mut().get_pin_mut().poll_read(cx, buf));
|
||||||
self.discard_buffer();
|
self.discard_buffer();
|
||||||
return Poll::Ready(res);
|
return Poll::Ready(res);
|
||||||
}
|
}
|
||||||
|
@ -218,7 +225,8 @@ impl<R: Read> Read for BufReader<R> {
|
||||||
) -> Poll<io::Result<usize>> {
|
) -> Poll<io::Result<usize>> {
|
||||||
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
|
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
|
||||||
if self.pos == self.cap && total_len >= self.buf.len() {
|
if self.pos == self.cap && total_len >= self.buf.len() {
|
||||||
let res = futures_core::ready!(self.as_mut().inner().poll_read_vectored(cx, bufs));
|
let res =
|
||||||
|
futures_core::ready!(self.as_mut().get_pin_mut().poll_read_vectored(cx, bufs));
|
||||||
self.discard_buffer();
|
self.discard_buffer();
|
||||||
return Poll::Ready(res);
|
return Poll::Ready(res);
|
||||||
}
|
}
|
||||||
|
@ -234,28 +242,23 @@ impl<R: Read> BufRead for BufReader<R> {
|
||||||
self: Pin<&'a mut Self>,
|
self: Pin<&'a mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<io::Result<&'a [u8]>> {
|
) -> Poll<io::Result<&'a [u8]>> {
|
||||||
let Self {
|
let mut this = self.project();
|
||||||
inner,
|
|
||||||
buf,
|
|
||||||
cap,
|
|
||||||
pos,
|
|
||||||
} = unsafe { self.get_unchecked_mut() };
|
|
||||||
let mut inner = unsafe { Pin::new_unchecked(inner) };
|
|
||||||
|
|
||||||
// If we've reached the end of our internal buffer then we need to fetch
|
// If we've reached the end of our internal buffer then we need to fetch
|
||||||
// some more data from the underlying reader.
|
// some more data from the underlying reader.
|
||||||
// Branch using `>=` instead of the more correct `==`
|
// Branch using `>=` instead of the more correct `==`
|
||||||
// to tell the compiler that the pos..cap slice is always valid.
|
// to tell the compiler that the pos..cap slice is always valid.
|
||||||
if *pos >= *cap {
|
if *this.pos >= *this.cap {
|
||||||
debug_assert!(*pos == *cap);
|
debug_assert!(*this.pos == *this.cap);
|
||||||
*cap = futures_core::ready!(inner.as_mut().poll_read(cx, buf))?;
|
*this.cap = futures_core::ready!(this.inner.as_mut().poll_read(cx, this.buf))?;
|
||||||
*pos = 0;
|
*this.pos = 0;
|
||||||
}
|
}
|
||||||
Poll::Ready(Ok(&buf[*pos..*cap]))
|
Poll::Ready(Ok(&this.buf[*this.pos..*this.cap]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume(mut self: Pin<&mut Self>, amt: usize) {
|
fn consume(self: Pin<&mut Self>, amt: usize) {
|
||||||
*self.as_mut().pos() = cmp::min(self.pos + amt, self.cap);
|
let this = self.project();
|
||||||
|
*this.pos = cmp::min(*this.pos + amt, *this.cap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,24 +308,26 @@ impl<R: Seek> Seek for BufReader<R> {
|
||||||
if let Some(offset) = n.checked_sub(remainder) {
|
if let Some(offset) = n.checked_sub(remainder) {
|
||||||
result = futures_core::ready!(
|
result = futures_core::ready!(
|
||||||
self.as_mut()
|
self.as_mut()
|
||||||
.inner()
|
.get_pin_mut()
|
||||||
.poll_seek(cx, SeekFrom::Current(offset))
|
.poll_seek(cx, SeekFrom::Current(offset))
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
// seek backwards by our remainder, and then by the offset
|
// seek backwards by our remainder, and then by the offset
|
||||||
futures_core::ready!(
|
futures_core::ready!(
|
||||||
self.as_mut()
|
self.as_mut()
|
||||||
.inner()
|
.get_pin_mut()
|
||||||
.poll_seek(cx, SeekFrom::Current(-remainder))
|
.poll_seek(cx, SeekFrom::Current(-remainder))
|
||||||
)?;
|
)?;
|
||||||
self.as_mut().discard_buffer();
|
self.as_mut().discard_buffer();
|
||||||
result = futures_core::ready!(
|
result = futures_core::ready!(
|
||||||
self.as_mut().inner().poll_seek(cx, SeekFrom::Current(n))
|
self.as_mut()
|
||||||
|
.get_pin_mut()
|
||||||
|
.poll_seek(cx, SeekFrom::Current(n))
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Seeking with Start/End doesn't care about our buffer length.
|
// Seeking with Start/End doesn't care about our buffer length.
|
||||||
result = futures_core::ready!(self.as_mut().inner().poll_seek(cx, pos))?;
|
result = futures_core::ready!(self.as_mut().get_pin_mut().poll_seek(cx, pos))?;
|
||||||
}
|
}
|
||||||
self.discard_buffer();
|
self.discard_buffer();
|
||||||
Poll::Ready(Ok(result))
|
Poll::Ready(Ok(result))
|
||||||
|
|
|
@ -1,92 +1,121 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use futures_core::ready;
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
use crate::io::{self, Seek, SeekFrom, Write};
|
use crate::io::write::WriteExt;
|
||||||
use crate::task::{Context, Poll};
|
use crate::io::{self, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE};
|
||||||
|
use crate::task::{Context, Poll, ready};
|
||||||
|
|
||||||
const DEFAULT_CAPACITY: usize = 8 * 1024;
|
pin_project! {
|
||||||
|
/// Wraps a writer and buffers its output.
|
||||||
|
///
|
||||||
|
/// It can be excessively inefficient to work directly with something that
|
||||||
|
/// implements [`Write`]. For example, every call to
|
||||||
|
/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
|
||||||
|
/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying
|
||||||
|
/// writer in large, infrequent batches.
|
||||||
|
///
|
||||||
|
/// `BufWriter` can improve the speed of programs that make *small* and
|
||||||
|
/// *repeated* write calls to the same file or network socket. It does not
|
||||||
|
/// help when writing very large amounts at once, or writing just one or a few
|
||||||
|
/// times. It also provides no advantage when writing to a destination that is
|
||||||
|
/// in memory, like a `Vec<u8>`.
|
||||||
|
///
|
||||||
|
/// Unlike the `BufWriter` type in `std`, this type does not write out the
|
||||||
|
/// contents of its buffer when it is dropped. Therefore, it is absolutely
|
||||||
|
/// critical that users explicitly flush the buffer before dropping a
|
||||||
|
/// `BufWriter`.
|
||||||
|
///
|
||||||
|
/// This type is an async version of [`std::io::BufWriter`].
|
||||||
|
///
|
||||||
|
/// [`std::io::BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Let's write the numbers one through ten to a [`TcpStream`]:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// use async_std::net::TcpStream;
|
||||||
|
/// use async_std::prelude::*;
|
||||||
|
///
|
||||||
|
/// let mut stream = TcpStream::connect("127.0.0.1:34254").await?;
|
||||||
|
///
|
||||||
|
/// for i in 0..10 {
|
||||||
|
/// let arr = [i+1];
|
||||||
|
/// stream.write(&arr).await?;
|
||||||
|
/// }
|
||||||
|
/// #
|
||||||
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Because we're not buffering, we write each one in turn, incurring the
|
||||||
|
/// overhead of a system call per byte written. We can fix this with a
|
||||||
|
/// `BufWriter`:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// use async_std::io::BufWriter;
|
||||||
|
/// use async_std::net::TcpStream;
|
||||||
|
/// use async_std::prelude::*;
|
||||||
|
///
|
||||||
|
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?);
|
||||||
|
///
|
||||||
|
/// for i in 0..10 {
|
||||||
|
/// let arr = [i+1];
|
||||||
|
/// stream.write(&arr).await?;
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// stream.flush().await?;
|
||||||
|
/// #
|
||||||
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped
|
||||||
|
/// together by the buffer, and will all be written out in one system call when
|
||||||
|
/// the `stream` is dropped.
|
||||||
|
///
|
||||||
|
/// [`Write`]: trait.Write.html
|
||||||
|
/// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write
|
||||||
|
/// [`TcpStream`]: ../net/struct.TcpStream.html
|
||||||
|
/// [`flush`]: trait.Write.html#tymethod.flush
|
||||||
|
pub struct BufWriter<W> {
|
||||||
|
#[pin]
|
||||||
|
inner: W,
|
||||||
|
buf: Vec<u8>,
|
||||||
|
written: usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Wraps a writer and buffers its output.
|
/// An error returned by `into_inner` which combines an error that
|
||||||
///
|
/// happened while writing out the buffer, and the buffered writer object
|
||||||
/// It can be excessively inefficient to work directly with something that
|
/// which may be used to recover from the condition.
|
||||||
/// implements [`Write`]. For example, every call to
|
|
||||||
/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
|
|
||||||
/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying
|
|
||||||
/// writer in large, infrequent batches.
|
|
||||||
///
|
|
||||||
/// `BufWriter` can improve the speed of programs that make *small* and
|
|
||||||
/// *repeated* write calls to the same file or network socket. It does not
|
|
||||||
/// help when writing very large amounts at once, or writing just one or a few
|
|
||||||
/// times. It also provides no advantage when writing to a destination that is
|
|
||||||
/// in memory, like a `Vec<u8>`.
|
|
||||||
///
|
|
||||||
/// When the `BufWriter` is dropped, the contents of its buffer will be written
|
|
||||||
/// out. However, any errors that happen in the process of flushing the buffer
|
|
||||||
/// when the writer is dropped will be ignored. Code that wishes to handle such
|
|
||||||
/// errors must manually call [`flush`] before the writer is dropped.
|
|
||||||
///
|
|
||||||
/// This type is an async version of [`std::io::BufReader`].
|
|
||||||
///
|
|
||||||
/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
|
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// Let's write the numbers one through ten to a [`TcpStream`]:
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
||||||
/// use async_std::net::TcpStream;
|
|
||||||
/// use async_std::prelude::*;
|
|
||||||
///
|
|
||||||
/// let mut stream = TcpStream::connect("127.0.0.1:34254").await?;
|
|
||||||
///
|
|
||||||
/// for i in 0..10 {
|
|
||||||
/// let arr = [i+1];
|
|
||||||
/// stream.write(&arr).await?;
|
|
||||||
/// }
|
|
||||||
/// #
|
|
||||||
/// # Ok(()) }) }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Because we're not buffering, we write each one in turn, incurring the
|
|
||||||
/// overhead of a system call per byte written. We can fix this with a
|
|
||||||
/// `BufWriter`:
|
|
||||||
///
|
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// use async_std::io::BufWriter;
|
/// use async_std::io::BufWriter;
|
||||||
/// use async_std::net::TcpStream;
|
/// use async_std::net::TcpStream;
|
||||||
/// use async_std::prelude::*;
|
|
||||||
///
|
///
|
||||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?);
|
/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34251").await?);
|
||||||
/// for i in 0..10 {
|
///
|
||||||
/// let arr = [i+1];
|
/// // unwrap the TcpStream and flush the buffer
|
||||||
/// stream.write(&arr).await?;
|
/// let stream = match buf_writer.into_inner().await {
|
||||||
|
/// Ok(s) => s,
|
||||||
|
/// Err(e) => {
|
||||||
|
/// // Here, e is an IntoInnerError
|
||||||
|
/// panic!("An error occurred");
|
||||||
|
/// }
|
||||||
/// };
|
/// };
|
||||||
/// #
|
/// #
|
||||||
/// # Ok(()) }) }
|
/// # Ok(()) }) }
|
||||||
/// ```
|
///```
|
||||||
///
|
#[derive(Debug)]
|
||||||
/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped
|
pub struct IntoInnerError<W>(W, crate::io::Error);
|
||||||
/// together by the buffer, and will all be written out in one system call when
|
|
||||||
/// the `stream` is dropped.
|
|
||||||
///
|
|
||||||
/// [`Write`]: trait.Write.html
|
|
||||||
/// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write
|
|
||||||
/// [`TcpStream`]: ../net/struct.TcpStream.html
|
|
||||||
/// [`flush`]: trait.Write.html#tymethod.flush
|
|
||||||
pub struct BufWriter<W> {
|
|
||||||
inner: W,
|
|
||||||
buf: Vec<u8>,
|
|
||||||
written: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: Write> BufWriter<W> {
|
impl<W: Write> BufWriter<W> {
|
||||||
pin_utils::unsafe_pinned!(inner: W);
|
|
||||||
pin_utils::unsafe_unpinned!(buf: Vec<u8>);
|
|
||||||
|
|
||||||
/// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB,
|
/// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB,
|
||||||
/// but may change in the future.
|
/// but may change in the future.
|
||||||
///
|
///
|
||||||
|
@ -103,7 +132,7 @@ impl<W: Write> BufWriter<W> {
|
||||||
/// # Ok(()) }) }
|
/// # Ok(()) }) }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(inner: W) -> BufWriter<W> {
|
pub fn new(inner: W) -> BufWriter<W> {
|
||||||
BufWriter::with_capacity(DEFAULT_CAPACITY, inner)
|
BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `BufWriter` with the specified buffer capacity.
|
/// Creates a new `BufWriter` with the specified buffer capacity.
|
||||||
|
@ -174,14 +203,41 @@ impl<W: Write> BufWriter<W> {
|
||||||
&mut self.inner
|
&mut self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a pinned mutable reference to the underlying writer.
|
||||||
|
///
|
||||||
|
/// It is inadvisable to directly write to the underlying writer.
|
||||||
|
fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> {
|
||||||
|
self.project().inner
|
||||||
|
}
|
||||||
|
|
||||||
/// Consumes BufWriter, returning the underlying writer
|
/// Consumes BufWriter, returning the underlying writer
|
||||||
///
|
///
|
||||||
/// This method will not write leftover data, it will be lost.
|
/// This method will not write leftover data, it will be lost.
|
||||||
/// For method that will attempt to write before returning the writer see [`poll_into_inner`]
|
/// For method that will attempt to write before returning the writer see [`poll_into_inner`]
|
||||||
///
|
///
|
||||||
/// [`poll_into_inner`]: #method.poll_into_inner
|
/// [`poll_into_inner`]: #method.poll_into_inner
|
||||||
pub fn into_inner(self) -> W {
|
/// # Examples
|
||||||
self.inner
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// use async_std::io::BufWriter;
|
||||||
|
/// use async_std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34251").await?);
|
||||||
|
///
|
||||||
|
/// // unwrap the TcpStream and flush the buffer
|
||||||
|
/// let stream = buf_writer.into_inner().await.unwrap();
|
||||||
|
/// #
|
||||||
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
|
pub async fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>>
|
||||||
|
where
|
||||||
|
Self: Unpin,
|
||||||
|
{
|
||||||
|
match self.flush().await {
|
||||||
|
Err(e) => Err(IntoInnerError(self, e)),
|
||||||
|
Ok(()) => Ok(self.inner),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the internally buffered data.
|
/// Returns a reference to the internally buffered data.
|
||||||
|
@ -210,16 +266,15 @@ impl<W: Write> BufWriter<W> {
|
||||||
///
|
///
|
||||||
/// [`LineWriter`]: struct.LineWriter.html
|
/// [`LineWriter`]: struct.LineWriter.html
|
||||||
fn poll_flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
fn poll_flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
let Self {
|
let mut this = self.project();
|
||||||
inner,
|
let len = this.buf.len();
|
||||||
buf,
|
|
||||||
written,
|
|
||||||
} = unsafe { Pin::get_unchecked_mut(self) };
|
|
||||||
let mut inner = unsafe { Pin::new_unchecked(inner) };
|
|
||||||
let len = buf.len();
|
|
||||||
let mut ret = Ok(());
|
let mut ret = Ok(());
|
||||||
while *written < len {
|
while *this.written < len {
|
||||||
match inner.as_mut().poll_write(cx, &buf[*written..]) {
|
match this
|
||||||
|
.inner
|
||||||
|
.as_mut()
|
||||||
|
.poll_write(cx, &this.buf[*this.written..])
|
||||||
|
{
|
||||||
Poll::Ready(Ok(0)) => {
|
Poll::Ready(Ok(0)) => {
|
||||||
ret = Err(io::Error::new(
|
ret = Err(io::Error::new(
|
||||||
io::ErrorKind::WriteZero,
|
io::ErrorKind::WriteZero,
|
||||||
|
@ -227,7 +282,7 @@ impl<W: Write> BufWriter<W> {
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Poll::Ready(Ok(n)) => *written += n,
|
Poll::Ready(Ok(n)) => *this.written += n,
|
||||||
Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::Interrupted => {}
|
Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||||
Poll::Ready(Err(e)) => {
|
Poll::Ready(Err(e)) => {
|
||||||
ret = Err(e);
|
ret = Err(e);
|
||||||
|
@ -236,10 +291,10 @@ impl<W: Write> BufWriter<W> {
|
||||||
Poll::Pending => return Poll::Pending,
|
Poll::Pending => return Poll::Pending,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if *written > 0 {
|
if *this.written > 0 {
|
||||||
buf.drain(..*written);
|
this.buf.drain(..*this.written);
|
||||||
}
|
}
|
||||||
*written = 0;
|
*this.written = 0;
|
||||||
Poll::Ready(ret)
|
Poll::Ready(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,26 +309,26 @@ impl<W: Write> Write for BufWriter<W> {
|
||||||
ready!(self.as_mut().poll_flush_buf(cx))?;
|
ready!(self.as_mut().poll_flush_buf(cx))?;
|
||||||
}
|
}
|
||||||
if buf.len() >= self.buf.capacity() {
|
if buf.len() >= self.buf.capacity() {
|
||||||
self.inner().poll_write(cx, buf)
|
self.get_pin_mut().poll_write(cx, buf)
|
||||||
} else {
|
} else {
|
||||||
Pin::new(&mut *self.buf()).poll_write(cx, buf)
|
Pin::new(&mut *self.project().buf).poll_write(cx, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
ready!(self.as_mut().poll_flush_buf(cx))?;
|
ready!(self.as_mut().poll_flush_buf(cx))?;
|
||||||
self.inner().poll_flush(cx)
|
self.get_pin_mut().poll_flush(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
ready!(self.as_mut().poll_flush_buf(cx))?;
|
ready!(self.as_mut().poll_flush_buf(cx))?;
|
||||||
self.inner().poll_close(cx)
|
self.get_pin_mut().poll_close(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write + fmt::Debug> fmt::Debug for BufWriter<W> {
|
impl<W: Write + fmt::Debug> fmt::Debug for BufWriter<W> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("BufReader")
|
f.debug_struct("BufWriter")
|
||||||
.field("writer", &self.inner)
|
.field("writer", &self.inner)
|
||||||
.field("buf", &self.buf)
|
.field("buf", &self.buf)
|
||||||
.finish()
|
.finish()
|
||||||
|
@ -290,6 +345,6 @@ impl<W: Write + Seek> Seek for BufWriter<W> {
|
||||||
pos: SeekFrom,
|
pos: SeekFrom,
|
||||||
) -> Poll<io::Result<u64>> {
|
) -> Poll<io::Result<u64>> {
|
||||||
ready!(self.as_mut().poll_flush_buf(cx))?;
|
ready!(self.as_mut().poll_flush_buf(cx))?;
|
||||||
self.inner().poll_seek(cx, pos)
|
self.get_pin_mut().poll_seek(cx, pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
136
src/io/copy.rs
136
src/io/copy.rs
|
@ -1,8 +1,11 @@
|
||||||
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::future::Future;
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
use crate::io::{self, BufRead, BufReader, Read, Write};
|
use crate::io::{self, BufRead, BufReader, Read, Write};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Copies the entire contents of a reader into a writer.
|
/// Copies the entire contents of a reader into a writer.
|
||||||
///
|
///
|
||||||
|
@ -41,52 +44,44 @@ use crate::task::{Context, Poll};
|
||||||
/// #
|
/// #
|
||||||
/// # Ok(()) }) }
|
/// # Ok(()) }) }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[cfg(any(feature = "docs", not(feature = "unstable")))]
|
||||||
pub async fn copy<R, W>(reader: &mut R, writer: &mut W) -> io::Result<u64>
|
pub async fn copy<R, W>(reader: &mut R, writer: &mut W) -> io::Result<u64>
|
||||||
where
|
where
|
||||||
R: Read + Unpin + ?Sized,
|
R: Read + Unpin + ?Sized,
|
||||||
W: Write + Unpin + ?Sized,
|
W: Write + Unpin + ?Sized,
|
||||||
{
|
{
|
||||||
pub struct CopyFuture<'a, R, W: ?Sized> {
|
pin_project! {
|
||||||
reader: R,
|
struct CopyFuture<R, W> {
|
||||||
writer: &'a mut W,
|
#[pin]
|
||||||
amt: u64,
|
reader: R,
|
||||||
}
|
#[pin]
|
||||||
|
writer: W,
|
||||||
impl<R, W: Unpin + ?Sized> CopyFuture<'_, R, W> {
|
amt: u64,
|
||||||
fn project(self: Pin<&mut Self>) -> (Pin<&mut R>, Pin<&mut W>, &mut u64) {
|
|
||||||
unsafe {
|
|
||||||
let this = self.get_unchecked_mut();
|
|
||||||
(
|
|
||||||
Pin::new_unchecked(&mut this.reader),
|
|
||||||
Pin::new(&mut *this.writer),
|
|
||||||
&mut this.amt,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R, W> Future for CopyFuture<'_, R, W>
|
impl<R, W> Future for CopyFuture<R, W>
|
||||||
where
|
where
|
||||||
R: BufRead,
|
R: BufRead,
|
||||||
W: Write + Unpin + ?Sized,
|
W: Write + Unpin,
|
||||||
{
|
{
|
||||||
type Output = io::Result<u64>;
|
type Output = io::Result<u64>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
let (mut reader, mut writer, amt) = self.project();
|
let mut this = self.project();
|
||||||
loop {
|
loop {
|
||||||
let buffer = futures_core::ready!(reader.as_mut().poll_fill_buf(cx))?;
|
let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?;
|
||||||
if buffer.is_empty() {
|
if buffer.is_empty() {
|
||||||
futures_core::ready!(writer.as_mut().poll_flush(cx))?;
|
futures_core::ready!(this.writer.as_mut().poll_flush(cx))?;
|
||||||
return Poll::Ready(Ok(*amt));
|
return Poll::Ready(Ok(*this.amt));
|
||||||
}
|
}
|
||||||
|
|
||||||
let i = futures_core::ready!(writer.as_mut().poll_write(cx, buffer))?;
|
let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?;
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
|
return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
|
||||||
}
|
}
|
||||||
*amt += i as u64;
|
*this.amt += i as u64;
|
||||||
reader.as_mut().consume(i);
|
this.reader.as_mut().consume(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,5 +91,92 @@ where
|
||||||
writer,
|
writer,
|
||||||
amt: 0,
|
amt: 0,
|
||||||
};
|
};
|
||||||
future.await
|
future.await.context(|| String::from("io::copy failed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copies the entire contents of a reader into a writer.
|
||||||
|
///
|
||||||
|
/// This function will continuously read data from `reader` and then
|
||||||
|
/// write it into `writer` in a streaming fashion until `reader`
|
||||||
|
/// returns EOF.
|
||||||
|
///
|
||||||
|
/// On success, the total number of bytes that were copied from
|
||||||
|
/// `reader` to `writer` is returned.
|
||||||
|
///
|
||||||
|
/// If you’re wanting to copy the contents of one file to another and you’re
|
||||||
|
/// working with filesystem paths, see the [`fs::copy`] function.
|
||||||
|
///
|
||||||
|
/// This function is an async version of [`std::io::copy`].
|
||||||
|
///
|
||||||
|
/// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
|
||||||
|
/// [`fs::copy`]: ../fs/fn.copy.html
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This function will return an error immediately if any call to `read` or
|
||||||
|
/// `write` returns an error. All instances of `ErrorKind::Interrupted` are
|
||||||
|
/// handled by this function and the underlying operation is retried.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// #
|
||||||
|
/// use async_std::io;
|
||||||
|
///
|
||||||
|
/// let mut reader: &[u8] = b"hello";
|
||||||
|
/// let mut writer = io::stdout();
|
||||||
|
///
|
||||||
|
/// io::copy(&mut reader, &mut writer).await?;
|
||||||
|
/// #
|
||||||
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
|
#[cfg(all(feature = "unstable", not(feature = "docs")))]
|
||||||
|
pub async fn copy<R, W>(reader: R, writer: W) -> io::Result<u64>
|
||||||
|
where
|
||||||
|
R: Read + Unpin,
|
||||||
|
W: Write + Unpin,
|
||||||
|
{
|
||||||
|
pin_project! {
|
||||||
|
struct CopyFuture<R, W> {
|
||||||
|
#[pin]
|
||||||
|
reader: R,
|
||||||
|
#[pin]
|
||||||
|
writer: W,
|
||||||
|
amt: u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, W> Future for CopyFuture<R, W>
|
||||||
|
where
|
||||||
|
R: BufRead,
|
||||||
|
W: Write + Unpin,
|
||||||
|
{
|
||||||
|
type Output = io::Result<u64>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let mut this = self.project();
|
||||||
|
loop {
|
||||||
|
let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?;
|
||||||
|
if buffer.is_empty() {
|
||||||
|
futures_core::ready!(this.writer.as_mut().poll_flush(cx))?;
|
||||||
|
return Poll::Ready(Ok(*this.amt));
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?;
|
||||||
|
if i == 0 {
|
||||||
|
return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
|
||||||
|
}
|
||||||
|
*this.amt += i as u64;
|
||||||
|
this.reader.as_mut().consume(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let future = CopyFuture {
|
||||||
|
reader: BufReader::new(reader),
|
||||||
|
writer,
|
||||||
|
amt: 0,
|
||||||
|
};
|
||||||
|
future.await.context(|| String::from("io::copy failed"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,10 @@ pub fn empty() -> Empty {
|
||||||
|
|
||||||
/// A reader that contains no data.
|
/// A reader that contains no data.
|
||||||
///
|
///
|
||||||
/// This reader is constructed by the [`sink`] function.
|
/// This reader is created by the [`empty`] function. See its
|
||||||
|
/// documentation for more.
|
||||||
///
|
///
|
||||||
/// [`sink`]: fn.sink.html
|
/// [`empty`]: fn.empty.html
|
||||||
pub struct Empty {
|
pub struct Empty {
|
||||||
_private: (),
|
_private: (),
|
||||||
}
|
}
|
||||||
|
|
351
src/io/mod.rs
351
src/io/mod.rs
|
@ -1,59 +1,330 @@
|
||||||
//! Basic input and output.
|
//! Traits, helpers, and type definitions for core I/O functionality.
|
||||||
|
//!
|
||||||
|
//! The `async_std::io` module contains a number of common things you'll need
|
||||||
|
//! when doing input and output. The most core part of this module is
|
||||||
|
//! the [`Read`] and [`Write`] traits, which provide the
|
||||||
|
//! most general interface for reading and writing input and output.
|
||||||
//!
|
//!
|
||||||
//! This module is an async version of [`std::io`].
|
//! This module is an async version of [`std::io`].
|
||||||
//!
|
//!
|
||||||
//! [`std::io`]: https://doc.rust-lang.org/std/io/index.html
|
//! [`std::io`]: https://doc.rust-lang.org/std/io/index.html
|
||||||
//!
|
//!
|
||||||
//! # Examples
|
//! # Read and Write
|
||||||
//!
|
//!
|
||||||
//! Read a line from the standard input:
|
//! Because they are traits, [`Read`] and [`Write`] are implemented by a number
|
||||||
|
//! of other types, and you can implement them for your types too. As such,
|
||||||
|
//! you'll see a few different types of I/O throughout the documentation in
|
||||||
|
//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec<T>`]s. For
|
||||||
|
//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on
|
||||||
|
//! [`File`]s:
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
|
//! use async_std::fs::File;
|
||||||
|
//! use async_std::prelude::*;
|
||||||
|
//!
|
||||||
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
//! #
|
//! #
|
||||||
//! use async_std::io;
|
//! let mut f = File::open("foo.txt").await?;
|
||||||
|
//! let mut buffer = [0; 10];
|
||||||
//!
|
//!
|
||||||
//! let stdin = io::stdin();
|
//! // read up to 10 bytes
|
||||||
//! let mut line = String::new();
|
//! let n = f.read(&mut buffer).await?;
|
||||||
//! stdin.read_line(&mut line).await?;
|
//!
|
||||||
|
//! println!("The bytes: {:?}", &buffer[..n]);
|
||||||
//! #
|
//! #
|
||||||
//! # Ok(()) }) }
|
//! # Ok(()) }) }
|
||||||
//! ```
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [`Read`] and [`Write`] are so important, implementors of the two traits have a
|
||||||
|
//! nickname: readers and writers. So you'll sometimes see 'a reader' instead
|
||||||
|
//! of 'a type that implements the [`Read`] trait'. Much easier!
|
||||||
|
//!
|
||||||
|
//! ## Seek and BufRead
|
||||||
|
//!
|
||||||
|
//! Beyond that, there are two important traits that are provided: [`Seek`]
|
||||||
|
//! and [`BufRead`]. Both of these build on top of a reader to control
|
||||||
|
//! how the reading happens. [`Seek`] lets you control where the next byte is
|
||||||
|
//! coming from:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use async_std::fs::File;
|
||||||
|
//! use async_std::io::SeekFrom;
|
||||||
|
//! use async_std::prelude::*;
|
||||||
|
//!
|
||||||
|
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
//! #
|
||||||
|
//! let mut f = File::open("foo.txt").await?;
|
||||||
|
//! let mut buffer = [0; 10];
|
||||||
|
//!
|
||||||
|
//! // skip to the last 10 bytes of the file
|
||||||
|
//! f.seek(SeekFrom::End(-10)).await?;
|
||||||
|
//!
|
||||||
|
//! // read up to 10 bytes
|
||||||
|
//! let n = f.read(&mut buffer).await?;
|
||||||
|
//!
|
||||||
|
//! println!("The bytes: {:?}", &buffer[..n]);
|
||||||
|
//! #
|
||||||
|
//! # Ok(()) }) }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but
|
||||||
|
//! to show it off, we'll need to talk about buffers in general. Keep reading!
|
||||||
|
//!
|
||||||
|
//! ## BufReader and BufWriter
|
||||||
|
//!
|
||||||
|
//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be
|
||||||
|
//! making near-constant calls to the operating system. To help with this,
|
||||||
|
//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap
|
||||||
|
//! readers and writers. The wrapper uses a buffer, reducing the number of
|
||||||
|
//! calls and providing nicer methods for accessing exactly what you want.
|
||||||
|
//!
|
||||||
|
//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra
|
||||||
|
//! methods to any reader:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use async_std::fs::File;
|
||||||
|
//! use async_std::io::BufReader;
|
||||||
|
//! use async_std::prelude::*;
|
||||||
|
//!
|
||||||
|
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
//! #
|
||||||
|
//! let f = File::open("foo.txt").await?;
|
||||||
|
//! let mut reader = BufReader::new(f);
|
||||||
|
//! let mut buffer = String::new();
|
||||||
|
//!
|
||||||
|
//! // read a line into buffer
|
||||||
|
//! reader.read_line(&mut buffer).await?;
|
||||||
|
//!
|
||||||
|
//! println!("{}", buffer);
|
||||||
|
//! #
|
||||||
|
//! # Ok(()) }) }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call
|
||||||
|
//! to [`write`][`Write::write`]:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use async_std::fs::File;
|
||||||
|
//! use async_std::io::prelude::*;
|
||||||
|
//! use async_std::io::BufWriter;
|
||||||
|
//!
|
||||||
|
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
//! #
|
||||||
|
//! let f = File::create("foo.txt").await?;
|
||||||
|
//! {
|
||||||
|
//! let mut writer = BufWriter::new(f);
|
||||||
|
//!
|
||||||
|
//! // write a byte to the buffer
|
||||||
|
//! writer.write(&[42]).await?;
|
||||||
|
//! } // the buffer is flushed once writer goes out of scope
|
||||||
|
//! //
|
||||||
|
//! #
|
||||||
|
//! # Ok(()) }) }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Standard input and output
|
||||||
|
//!
|
||||||
|
//! A very common source of input is standard input:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use async_std::io;
|
||||||
|
//!
|
||||||
|
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
//! #
|
||||||
|
//! let mut input = String::new();
|
||||||
|
//!
|
||||||
|
//! io::stdin().read_line(&mut input).await?;
|
||||||
|
//!
|
||||||
|
//! println!("You typed: {}", input.trim());
|
||||||
|
//! #
|
||||||
|
//! # Ok(()) }) }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Note that you cannot use the [`?` operator] in functions that do not return
|
||||||
|
//! a [`Result<T, E>`][`Result`]. Instead, you can call [`.unwrap()`]
|
||||||
|
//! or `match` on the return value to catch any possible errors:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use async_std::io;
|
||||||
|
//!
|
||||||
|
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
//! #
|
||||||
|
//! let mut input = String::new();
|
||||||
|
//!
|
||||||
|
//! io::stdin().read_line(&mut input).await.unwrap();
|
||||||
|
//! #
|
||||||
|
//! # Ok(()) }) }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! And a very common source of output is standard output:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use async_std::io;
|
||||||
|
//! use async_std::io::prelude::*;
|
||||||
|
//!
|
||||||
|
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
//! #
|
||||||
|
//! io::stdout().write(&[42]).await?;
|
||||||
|
//! #
|
||||||
|
//! # Ok(()) }) }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Of course, using [`io::stdout`] directly is less common than something like
|
||||||
|
//! [`println!`].
|
||||||
|
//!
|
||||||
|
//! ## Iterator types
|
||||||
|
//!
|
||||||
|
//! A large number of the structures provided by `std::io` are for various
|
||||||
|
//! ways of iterating over I/O. For example, [`Lines`] is used to split over
|
||||||
|
//! lines:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use async_std::fs::File;
|
||||||
|
//! use async_std::io::BufReader;
|
||||||
|
//! use async_std::prelude::*;
|
||||||
|
//!
|
||||||
|
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
//! #
|
||||||
|
//! let f = File::open("foo.txt").await?;
|
||||||
|
//! let reader = BufReader::new(f);
|
||||||
|
//!
|
||||||
|
//! let mut lines = reader.lines();
|
||||||
|
//! while let Some(line) = lines.next().await {
|
||||||
|
//! println!("{}", line?);
|
||||||
|
//! }
|
||||||
|
//! #
|
||||||
|
//! # Ok(()) }) }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Functions
|
||||||
|
//!
|
||||||
|
//! There are a number of [functions][functions-list] that offer access to various
|
||||||
|
//! features. For example, we can use three of these functions to copy everything
|
||||||
|
//! from standard input to standard output:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use async_std::io;
|
||||||
|
//!
|
||||||
|
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
//! #
|
||||||
|
//! io::copy(&mut io::stdin(), &mut io::stdout()).await?;
|
||||||
|
//! #
|
||||||
|
//! # Ok(()) }) }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [functions-list]: #functions-1
|
||||||
|
//!
|
||||||
|
//! ## io::Result
|
||||||
|
//!
|
||||||
|
//! Last, but certainly not least, is [`io::Result`]. This type is used
|
||||||
|
//! as the return type of many `std::io` functions that can cause an error, and
|
||||||
|
//! can be returned from your own functions as well. Many of the examples in this
|
||||||
|
//! module use the [`?` operator]:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! #![allow(dead_code)]
|
||||||
|
//! use async_std::io;
|
||||||
|
//!
|
||||||
|
//! async fn read_input() -> io::Result<()> {
|
||||||
|
//! let mut input = String::new();
|
||||||
|
//!
|
||||||
|
//! io::stdin().read_line(&mut input).await?;
|
||||||
|
//!
|
||||||
|
//! println!("You typed: {}", input.trim());
|
||||||
|
//!
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! The return type of `read_input`, [`io::Result<()>`][`io::Result`], is a very
|
||||||
|
//! common type for functions which don't have a 'real' return value, but do want to
|
||||||
|
//! return errors if they happen. In this case, the only purpose of this function is
|
||||||
|
//! to read the line and print it, so we use `()`.
|
||||||
|
//!
|
||||||
|
//! ## Platform-specific behavior
|
||||||
|
//!
|
||||||
|
//! Many I/O functions throughout the standard library are documented to indicate
|
||||||
|
//! what various library or syscalls they are delegated to. This is done to help
|
||||||
|
//! applications both understand what's happening under the hood as well as investigate
|
||||||
|
//! any possibly unclear semantics. Note, however, that this is informative, not a binding
|
||||||
|
//! contract. The implementation of many of these functions are subject to change over
|
||||||
|
//! time and may call fewer or more syscalls/library functions.
|
||||||
|
//!
|
||||||
|
//! [`Read`]: trait.Read.html
|
||||||
|
//! [`Write`]: trait.Write.html
|
||||||
|
//! [`Seek`]: trait.Seek.html
|
||||||
|
//! [`BufRead`]: trait.BufRead.html
|
||||||
|
//! [`File`]: ../fs/struct.File.html
|
||||||
|
//! [`TcpStream`]: ../net/struct.TcpStream.html
|
||||||
|
//! [`Vec<T>`]: ../vec/struct.Vec.html
|
||||||
|
//! [`BufReader`]: struct.BufReader.html
|
||||||
|
//! [`BufWriter`]: struct.BufWriter.html
|
||||||
|
//! [`Write::write`]: trait.Write.html#tymethod.write
|
||||||
|
//! [`io::stdout`]: fn.stdout.html
|
||||||
|
//! [`println!`]: ../macro.println.html
|
||||||
|
//! [`Lines`]: struct.Lines.html
|
||||||
|
//! [`io::Result`]: type.Result.html
|
||||||
|
//! [`?` operator]: https://doc.rust-lang.org/stable/book/appendix-02-operators.html
|
||||||
|
//! [`Read::read`]: trait.Read.html#tymethod.read
|
||||||
|
//! [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
|
||||||
|
//! [`.unwrap()`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap
|
||||||
|
|
||||||
#[doc(inline)]
|
const DEFAULT_BUF_SIZE: usize = 8 * 1024;
|
||||||
pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom};
|
|
||||||
|
|
||||||
pub use buf_read::{BufRead, Lines};
|
cfg_std! {
|
||||||
pub use buf_reader::BufReader;
|
#[doc(inline)]
|
||||||
pub use buf_writer::BufWriter;
|
pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom};
|
||||||
pub use copy::copy;
|
|
||||||
pub use cursor::Cursor;
|
|
||||||
pub use empty::{empty, Empty};
|
|
||||||
pub use read::Read;
|
|
||||||
pub use repeat::{repeat, Repeat};
|
|
||||||
pub use seek::Seek;
|
|
||||||
pub use sink::{sink, Sink};
|
|
||||||
pub use stderr::{stderr, Stderr};
|
|
||||||
pub use stdin::{stdin, Stdin};
|
|
||||||
pub use stdout::{stdout, Stdout};
|
|
||||||
pub use timeout::timeout;
|
|
||||||
pub use write::Write;
|
|
||||||
|
|
||||||
pub mod prelude;
|
pub use buf_read::{BufRead, Lines, Split};
|
||||||
|
pub use buf_reader::BufReader;
|
||||||
|
pub use buf_writer::{BufWriter, IntoInnerError};
|
||||||
|
pub use copy::copy;
|
||||||
|
pub use cursor::Cursor;
|
||||||
|
pub use empty::{empty, Empty};
|
||||||
|
pub use read::*;
|
||||||
|
pub use repeat::{repeat, Repeat};
|
||||||
|
pub use seek::Seek;
|
||||||
|
pub use sink::{sink, Sink};
|
||||||
|
pub use write::Write;
|
||||||
|
|
||||||
pub(crate) mod buf_read;
|
pub mod prelude;
|
||||||
pub(crate) mod read;
|
|
||||||
pub(crate) mod seek;
|
|
||||||
pub(crate) mod write;
|
|
||||||
|
|
||||||
mod buf_reader;
|
pub(crate) mod buf_read;
|
||||||
mod buf_writer;
|
pub(crate) mod read;
|
||||||
mod copy;
|
pub(crate) mod seek;
|
||||||
mod cursor;
|
pub(crate) mod write;
|
||||||
mod empty;
|
pub(crate) mod utils;
|
||||||
mod repeat;
|
|
||||||
mod sink;
|
mod buf_reader;
|
||||||
mod stderr;
|
mod buf_writer;
|
||||||
mod stdin;
|
mod copy;
|
||||||
mod stdout;
|
mod cursor;
|
||||||
mod timeout;
|
mod empty;
|
||||||
|
mod repeat;
|
||||||
|
mod sink;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_default! {
|
||||||
|
// For use in the print macros.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[cfg(not(target_os = "unknown"))]
|
||||||
|
pub use stdio::{_eprint, _print};
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "unknown"))]
|
||||||
|
pub use stderr::{stderr, Stderr};
|
||||||
|
#[cfg(not(target_os = "unknown"))]
|
||||||
|
pub use stdin::{stdin, Stdin};
|
||||||
|
#[cfg(not(target_os = "unknown"))]
|
||||||
|
pub use stdout::{stdout, Stdout};
|
||||||
|
pub use timeout::timeout;
|
||||||
|
|
||||||
|
mod timeout;
|
||||||
|
#[cfg(not(target_os = "unknown"))]
|
||||||
|
mod stderr;
|
||||||
|
#[cfg(not(target_os = "unknown"))]
|
||||||
|
mod stdin;
|
||||||
|
#[cfg(not(target_os = "unknown"))]
|
||||||
|
mod stdio;
|
||||||
|
#[cfg(not(target_os = "unknown"))]
|
||||||
|
mod stdout;
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//! The async I/O Prelude
|
//! The async I/O prelude.
|
||||||
//!
|
//!
|
||||||
//! The purpose of this module is to alleviate imports of many common I/O traits
|
//! The purpose of this module is to alleviate imports of many common I/O traits
|
||||||
//! by adding a glob import to the top of I/O heavy modules:
|
//! by adding a glob import to the top of I/O heavy modules:
|
||||||
|
@ -17,11 +17,11 @@ pub use crate::io::Seek;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use crate::io::Write;
|
pub use crate::io::Write;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(inline)]
|
||||||
pub use crate::io::buf_read::BufReadExt as _;
|
pub use crate::io::buf_read::BufReadExt;
|
||||||
#[doc(hidden)]
|
#[doc(inline)]
|
||||||
pub use crate::io::read::ReadExt as _;
|
pub use crate::io::read::ReadExt;
|
||||||
#[doc(hidden)]
|
#[doc(inline)]
|
||||||
pub use crate::io::seek::SeekExt as _;
|
pub use crate::io::seek::SeekExt;
|
||||||
#[doc(hidden)]
|
#[doc(inline)]
|
||||||
pub use crate::io::write::WriteExt as _;
|
pub use crate::io::write::WriteExt;
|
||||||
|
|
|
@ -32,7 +32,7 @@ impl<T: Read + Unpin> Stream for Bytes<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, default))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
use crate::io::IoSliceMut;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::io::{self, BufRead, Read};
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::io::{self, BufRead, IoSliceMut, Read};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
/// Adaptor to chain together two readers.
|
pin_project! {
|
||||||
///
|
/// Adaptor to chain together two readers.
|
||||||
/// This struct is generally created by calling [`chain`] on a reader.
|
///
|
||||||
/// Please see the documentation of [`chain`] for more details.
|
/// This struct is generally created by calling [`chain`] on a reader.
|
||||||
///
|
/// Please see the documentation of [`chain`] for more details.
|
||||||
/// [`chain`]: trait.Read.html#method.chain
|
///
|
||||||
pub struct Chain<T, U> {
|
/// [`chain`]: trait.Read.html#method.chain
|
||||||
pub(crate) first: T,
|
pub struct Chain<T, U> {
|
||||||
pub(crate) second: U,
|
#[pin]
|
||||||
pub(crate) done_first: bool,
|
pub(crate) first: T,
|
||||||
|
#[pin]
|
||||||
|
pub(crate) second: U,
|
||||||
|
pub(crate) done_first: bool,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Chain<T, U> {
|
impl<T, U> Chain<T, U> {
|
||||||
|
@ -98,81 +103,69 @@ impl<T: fmt::Debug, U: fmt::Debug> fmt::Debug for Chain<T, U> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Read + Unpin, U: Read + Unpin> Read for Chain<T, U> {
|
impl<T: Read, U: Read> Read for Chain<T, U> {
|
||||||
fn poll_read(
|
fn poll_read(
|
||||||
mut self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
buf: &mut [u8],
|
buf: &mut [u8],
|
||||||
) -> Poll<io::Result<usize>> {
|
) -> Poll<io::Result<usize>> {
|
||||||
if !self.done_first {
|
let this = self.project();
|
||||||
let rd = Pin::new(&mut self.first);
|
if !*this.done_first {
|
||||||
|
match futures_core::ready!(this.first.poll_read(cx, buf)) {
|
||||||
match futures_core::ready!(rd.poll_read(cx, buf)) {
|
Ok(0) if !buf.is_empty() => *this.done_first = true,
|
||||||
Ok(0) if !buf.is_empty() => self.done_first = true,
|
|
||||||
Ok(n) => return Poll::Ready(Ok(n)),
|
Ok(n) => return Poll::Ready(Ok(n)),
|
||||||
Err(err) => return Poll::Ready(Err(err)),
|
Err(err) => return Poll::Ready(Err(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let rd = Pin::new(&mut self.second);
|
this.second.poll_read(cx, buf)
|
||||||
rd.poll_read(cx, buf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_read_vectored(
|
fn poll_read_vectored(
|
||||||
mut self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
bufs: &mut [IoSliceMut<'_>],
|
bufs: &mut [IoSliceMut<'_>],
|
||||||
) -> Poll<io::Result<usize>> {
|
) -> Poll<io::Result<usize>> {
|
||||||
if !self.done_first {
|
let this = self.project();
|
||||||
let rd = Pin::new(&mut self.first);
|
if !*this.done_first {
|
||||||
|
match futures_core::ready!(this.first.poll_read_vectored(cx, bufs)) {
|
||||||
match futures_core::ready!(rd.poll_read_vectored(cx, bufs)) {
|
Ok(0) if !bufs.is_empty() => *this.done_first = true,
|
||||||
Ok(0) if !bufs.is_empty() => self.done_first = true,
|
|
||||||
Ok(n) => return Poll::Ready(Ok(n)),
|
Ok(n) => return Poll::Ready(Ok(n)),
|
||||||
Err(err) => return Poll::Ready(Err(err)),
|
Err(err) => return Poll::Ready(Err(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let rd = Pin::new(&mut self.second);
|
this.second.poll_read_vectored(cx, bufs)
|
||||||
rd.poll_read_vectored(cx, bufs)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: BufRead + Unpin, U: BufRead + Unpin> BufRead for Chain<T, U> {
|
impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> {
|
||||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
||||||
let Self {
|
let this = self.project();
|
||||||
first,
|
if !*this.done_first {
|
||||||
second,
|
match futures_core::ready!(this.first.poll_fill_buf(cx)) {
|
||||||
done_first,
|
|
||||||
} = unsafe { self.get_unchecked_mut() };
|
|
||||||
|
|
||||||
if !*done_first {
|
|
||||||
let first = unsafe { Pin::new_unchecked(first) };
|
|
||||||
match futures_core::ready!(first.poll_fill_buf(cx)) {
|
|
||||||
Ok(buf) if buf.is_empty() => {
|
Ok(buf) if buf.is_empty() => {
|
||||||
*done_first = true;
|
*this.done_first = true;
|
||||||
}
|
}
|
||||||
Ok(buf) => return Poll::Ready(Ok(buf)),
|
Ok(buf) => return Poll::Ready(Ok(buf)),
|
||||||
Err(err) => return Poll::Ready(Err(err)),
|
Err(err) => return Poll::Ready(Err(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let second = unsafe { Pin::new_unchecked(second) };
|
this.second.poll_fill_buf(cx)
|
||||||
second.poll_fill_buf(cx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume(mut self: Pin<&mut Self>, amt: usize) {
|
fn consume(self: Pin<&mut Self>, amt: usize) {
|
||||||
if !self.done_first {
|
let this = self.project();
|
||||||
let rd = Pin::new(&mut self.first);
|
if !*this.done_first {
|
||||||
rd.consume(amt)
|
this.first.consume(amt)
|
||||||
} else {
|
} else {
|
||||||
let rd = Pin::new(&mut self.second);
|
this.second.consume(amt)
|
||||||
rd.consume(amt)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, default))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
|
@ -13,23 +13,21 @@ use read_to_end::{read_to_end_internal, ReadToEndFuture};
|
||||||
use read_to_string::ReadToStringFuture;
|
use read_to_string::ReadToStringFuture;
|
||||||
use read_vectored::ReadVectoredFuture;
|
use read_vectored::ReadVectoredFuture;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use crate::io::IoSliceMut;
|
use crate::io::IoSliceMut;
|
||||||
use crate::utils::extension_trait;
|
|
||||||
|
|
||||||
cfg_if! {
|
pub use bytes::Bytes;
|
||||||
if #[cfg(feature = "docs")] {
|
pub use chain::Chain;
|
||||||
use std::pin::Pin;
|
pub use take::Take;
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use crate::io;
|
|
||||||
use crate::task::{Context, Poll};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension_trait! {
|
extension_trait! {
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use crate::io;
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
#[doc = r#"
|
#[doc = r#"
|
||||||
Allows reading from a byte stream.
|
Allows reading from a byte stream.
|
||||||
|
|
||||||
|
@ -37,7 +35,7 @@ extension_trait! {
|
||||||
[`std::io::Read`].
|
[`std::io::Read`].
|
||||||
|
|
||||||
Methods other than [`poll_read`] and [`poll_read_vectored`] do not really exist in the
|
Methods other than [`poll_read`] and [`poll_read_vectored`] do not really exist in the
|
||||||
trait itself, but they become available when the prelude is imported:
|
trait itself, but they become available when [`ReadExt`] from the [prelude] is imported:
|
||||||
|
|
||||||
```
|
```
|
||||||
# #[allow(unused_imports)]
|
# #[allow(unused_imports)]
|
||||||
|
@ -46,11 +44,13 @@ extension_trait! {
|
||||||
|
|
||||||
[`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
|
[`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
|
||||||
[`futures::io::AsyncRead`]:
|
[`futures::io::AsyncRead`]:
|
||||||
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html
|
https://docs.rs/futures/0.3/futures/io/trait.AsyncRead.html
|
||||||
[`poll_read`]: #tymethod.poll_read
|
[`poll_read`]: #tymethod.poll_read
|
||||||
[`poll_read_vectored`]: #method.poll_read_vectored
|
[`poll_read_vectored`]: #method.poll_read_vectored
|
||||||
|
[`ReadExt`]: ../io/prelude/trait.ReadExt.html
|
||||||
|
[prelude]: ../prelude/index.html
|
||||||
"#]
|
"#]
|
||||||
pub trait Read [ReadExt: futures_io::AsyncRead] {
|
pub trait Read {
|
||||||
#[doc = r#"
|
#[doc = r#"
|
||||||
Attempt to read from the `AsyncRead` into `buf`.
|
Attempt to read from the `AsyncRead` into `buf`.
|
||||||
"#]
|
"#]
|
||||||
|
@ -70,7 +70,14 @@ extension_trait! {
|
||||||
) -> Poll<io::Result<usize>> {
|
) -> Poll<io::Result<usize>> {
|
||||||
unreachable!("this impl only appears in the rendered docs")
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Extension methods for [`Read`].
|
||||||
|
|
||||||
|
[`Read`]: ../trait.Read.html
|
||||||
|
"#]
|
||||||
|
pub trait ReadExt: futures_io::AsyncRead {
|
||||||
#[doc = r#"
|
#[doc = r#"
|
||||||
Reads some bytes from the byte stream.
|
Reads some bytes from the byte stream.
|
||||||
|
|
||||||
|
@ -271,7 +278,7 @@ extension_trait! {
|
||||||
This function returns a new instance of `Read` which will read at most
|
This function returns a new instance of `Read` which will read at most
|
||||||
`limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any
|
`limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any
|
||||||
read errors will not count towards the number of bytes read and future
|
read errors will not count towards the number of bytes read and future
|
||||||
calls to [`read()`] may succeed.
|
calls to [`read`] may succeed.
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
|
@ -279,7 +286,7 @@ extension_trait! {
|
||||||
|
|
||||||
[`File`]: ../fs/struct.File.html
|
[`File`]: ../fs/struct.File.html
|
||||||
[`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok
|
[`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok
|
||||||
[`read()`]: tymethod.read
|
[`read`]: tymethod.read
|
||||||
|
|
||||||
```no_run
|
```no_run
|
||||||
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
@ -298,43 +305,43 @@ extension_trait! {
|
||||||
# Ok(()) }) }
|
# Ok(()) }) }
|
||||||
```
|
```
|
||||||
"#]
|
"#]
|
||||||
fn take(self, limit: u64) -> take::Take<Self>
|
fn take(self, limit: u64) -> Take<Self>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
take::Take { inner: self, limit }
|
Take { inner: self, limit }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc = r#"
|
#[doc = r#"
|
||||||
Creates a "by reference" adaptor for this instance of `Read`.
|
Creates a "by reference" adaptor for this instance of `Read`.
|
||||||
|
|
||||||
The returned adaptor also implements `Read` and will simply borrow this
|
The returned adaptor also implements `Read` and will simply borrow this
|
||||||
current reader.
|
current reader.
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
[`File`][file]s implement `Read`:
|
[`File`][file]s implement `Read`:
|
||||||
|
|
||||||
[file]: ../fs/struct.File.html
|
[file]: ../fs/struct.File.html
|
||||||
|
|
||||||
```no_run
|
```no_run
|
||||||
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
#
|
#
|
||||||
use async_std::prelude::*;
|
use async_std::prelude::*;
|
||||||
use async_std::fs::File;
|
use async_std::fs::File;
|
||||||
|
|
||||||
let mut f = File::open("foo.txt").await?;
|
let mut f = File::open("foo.txt").await?;
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
let mut other_buffer = Vec::new();
|
let mut other_buffer = Vec::new();
|
||||||
|
|
||||||
{
|
{
|
||||||
let reference = f.by_ref();
|
let reference = f.by_ref();
|
||||||
|
|
||||||
// read at most 5 bytes
|
// read at most 5 bytes
|
||||||
reference.take(5).read_to_end(&mut buffer).await?;
|
reference.take(5).read_to_end(&mut buffer).await?;
|
||||||
|
|
||||||
} // drop our &mut reference so we can use f again
|
} // drop our &mut reference so we can use f again
|
||||||
|
|
||||||
// original file still usable, read the rest
|
// original file still usable, read the rest
|
||||||
f.read_to_end(&mut other_buffer).await?;
|
f.read_to_end(&mut other_buffer).await?;
|
||||||
#
|
#
|
||||||
|
@ -346,27 +353,27 @@ extension_trait! {
|
||||||
|
|
||||||
#[doc = r#"
|
#[doc = r#"
|
||||||
Transforms this `Read` instance to a `Stream` over its bytes.
|
Transforms this `Read` instance to a `Stream` over its bytes.
|
||||||
|
|
||||||
The returned type implements `Stream` where the `Item` is
|
The returned type implements `Stream` where the `Item` is
|
||||||
`Result<u8, io::Error>`.
|
`Result<u8, io::Error>`.
|
||||||
The yielded item is `Ok` if a byte was successfully read and `Err`
|
The yielded item is `Ok` if a byte was successfully read and `Err`
|
||||||
otherwise. EOF is mapped to returning `None` from this iterator.
|
otherwise. EOF is mapped to returning `None` from this iterator.
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
[`File`][file]s implement `Read`:
|
[`File`][file]s implement `Read`:
|
||||||
|
|
||||||
[file]: ../fs/struct.File.html
|
[file]: ../fs/struct.File.html
|
||||||
|
|
||||||
```no_run
|
```no_run
|
||||||
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
#
|
#
|
||||||
use async_std::prelude::*;
|
use async_std::prelude::*;
|
||||||
use async_std::fs::File;
|
use async_std::fs::File;
|
||||||
|
|
||||||
let f = File::open("foo.txt").await?;
|
let f = File::open("foo.txt").await?;
|
||||||
let mut s = f.bytes();
|
let mut s = f.bytes();
|
||||||
|
|
||||||
while let Some(byte) = s.next().await {
|
while let Some(byte) = s.next().await {
|
||||||
println!("{}", byte.unwrap());
|
println!("{}", byte.unwrap());
|
||||||
}
|
}
|
||||||
|
@ -374,35 +381,35 @@ extension_trait! {
|
||||||
# Ok(()) }) }
|
# Ok(()) }) }
|
||||||
```
|
```
|
||||||
"#]
|
"#]
|
||||||
fn bytes(self) -> bytes::Bytes<Self> where Self: Sized {
|
fn bytes(self) -> Bytes<Self> where Self: Sized {
|
||||||
bytes::Bytes { inner: self }
|
Bytes { inner: self }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc = r#"
|
#[doc = r#"
|
||||||
Creates an adaptor which will chain this stream with another.
|
Creates an adaptor which will chain this stream with another.
|
||||||
|
|
||||||
The returned `Read` instance will first read all bytes from this object
|
The returned `Read` instance will first read all bytes from this object
|
||||||
until EOF is encountered. Afterwards the output is equivalent to the
|
until EOF is encountered. Afterwards the output is equivalent to the
|
||||||
output of `next`.
|
output of `next`.
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
[`File`][file]s implement `Read`:
|
[`File`][file]s implement `Read`:
|
||||||
|
|
||||||
[file]: ../fs/struct.File.html
|
[file]: ../fs/struct.File.html
|
||||||
|
|
||||||
```no_run
|
```no_run
|
||||||
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
#
|
#
|
||||||
use async_std::prelude::*;
|
use async_std::prelude::*;
|
||||||
use async_std::fs::File;
|
use async_std::fs::File;
|
||||||
|
|
||||||
let f1 = File::open("foo.txt").await?;
|
let f1 = File::open("foo.txt").await?;
|
||||||
let f2 = File::open("bar.txt").await?;
|
let f2 = File::open("bar.txt").await?;
|
||||||
|
|
||||||
let mut handle = f1.chain(f2);
|
let mut handle = f1.chain(f2);
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
|
|
||||||
// read the value into a String. We could use any Read method here,
|
// read the value into a String. We could use any Read method here,
|
||||||
// this is just one example.
|
// this is just one example.
|
||||||
handle.read_to_string(&mut buffer).await?;
|
handle.read_to_string(&mut buffer).await?;
|
||||||
|
@ -410,8 +417,8 @@ extension_trait! {
|
||||||
# Ok(()) }) }
|
# Ok(()) }) }
|
||||||
```
|
```
|
||||||
"#]
|
"#]
|
||||||
fn chain<R: Read>(self, next: R) -> chain::Chain<Self, R> where Self: Sized {
|
fn chain<R: Read>(self, next: R) -> Chain<Self, R> where Self: Sized {
|
||||||
chain::Chain { first: self, second: next, done_first: false }
|
Chain { first: self, second: next, done_first: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -470,13 +477,13 @@ unsafe fn initialize<R: futures_io::AsyncRead>(_reader: &R, buf: &mut [u8]) {
|
||||||
std::ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len())
|
std::ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, not(target_os = "unknown")))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_by_ref() -> io::Result<()> {
|
fn test_read_by_ref() {
|
||||||
crate::task::block_on(async {
|
crate::task::block_on(async {
|
||||||
let mut f = io::Cursor::new(vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8]);
|
let mut f = io::Cursor::new(vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8]);
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
|
@ -486,14 +493,13 @@ mod tests {
|
||||||
let reference = f.by_ref();
|
let reference = f.by_ref();
|
||||||
|
|
||||||
// read at most 5 bytes
|
// read at most 5 bytes
|
||||||
assert_eq!(reference.take(5).read_to_end(&mut buffer).await?, 5);
|
assert_eq!(reference.take(5).read_to_end(&mut buffer).await.unwrap(), 5);
|
||||||
assert_eq!(&buffer, &[0, 1, 2, 3, 4])
|
assert_eq!(&buffer, &[0, 1, 2, 3, 4])
|
||||||
} // drop our &mut reference so we can use f again
|
} // drop our &mut reference so we can use f again
|
||||||
|
|
||||||
// original file still usable, read the rest
|
// original file still usable, read the rest
|
||||||
assert_eq!(f.read_to_end(&mut other_buffer).await?, 4);
|
assert_eq!(f.read_to_end(&mut other_buffer).await.unwrap(), 4);
|
||||||
assert_eq!(&other_buffer, &[5, 6, 7, 8]);
|
assert_eq!(&other_buffer, &[5, 6, 7, 8]);
|
||||||
Ok(())
|
});
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, Read};
|
use crate::io::{self, Read};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, Read};
|
use crate::io::{self, Read};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, Read};
|
use crate::io::{self, Read};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use super::read_to_end_internal;
|
use super::read_to_end_internal;
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, Read};
|
use crate::io::{self, Read};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
@ -37,7 +37,11 @@ impl<T: Read + Unpin + ?Sized> Future for ReadToStringFuture<'_, T> {
|
||||||
))
|
))
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
debug_assert!(buf.is_empty());
|
#[allow(clippy::debug_assert_with_mut_call)]
|
||||||
|
{
|
||||||
|
debug_assert!(buf.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
// Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`.
|
// Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`.
|
||||||
mem::swap(unsafe { buf.as_mut_vec() }, bytes);
|
mem::swap(unsafe { buf.as_mut_vec() }, bytes);
|
||||||
Poll::Ready(ret)
|
Poll::Ready(ret)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, IoSliceMut, Read};
|
use crate::io::{self, IoSliceMut, Read};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue