forked from mirror/async-std
Compare commits
469 Commits
bench-mute
...
master
Author | SHA1 | Date |
---|---|---|
Yoshua Wuyts | 8aa5921dfa | 5 years ago |
Jonathas-Conceicao | cd7fb9dec2 | 5 years ago |
Yoshua Wuyts | c82b1efb69 | 5 years ago |
Friedel Ziegelmayer | 8c4b425136 | 5 years ago |
Thibault Martinez | 2ab08ebbbc | 5 years ago |
Friedel Ziegelmayer | 0e7650a421 | 5 years ago |
dignifiedquire | 8f17e9275b | 5 years ago |
dignifiedquire | 18dffe8b43 | 5 years ago |
Friedel Ziegelmayer | 43de93312c | 5 years ago |
Gary Guo | 2e7e804736 | 5 years ago |
Friedel Ziegelmayer | 17ab958ac2 | 5 years ago |
Friedel Ziegelmayer | caa76af745 | 5 years ago |
dignifiedquire | e495ba46b3 | 5 years ago |
Afirez | 0c2ce52ac4 | 5 years ago |
Friedel Ziegelmayer | 5f418f07ac | 5 years ago |
dignifiedquire | 06a2fb8c4f | 5 years ago |
dignifiedquire | 1c1c168e1b | 5 years ago |
Friedel Ziegelmayer | 5d55fa7a47 | 5 years ago |
dignifiedquire | 093d640ad7 | 5 years ago |
Oleg Nosov | 42425f6c1a | 5 years ago |
Yoshua Wuyts | a602a91d83 | 5 years ago |
Afirez | 9fa3ce3fd6 | 5 years ago |
Oleg Nosov | df22d87d09 | 5 years ago |
Oleg Nosov | 924e5a3f41 | 5 years ago |
Oleg Nosov | 2323ac9a8e | 5 years ago |
Friedel Ziegelmayer | 5c2a3de9e7 | 5 years ago |
dignifiedquire | e9c6ea873c | 5 years ago |
Friedel Ziegelmayer | 0d98aac8f7 | 5 years ago |
Thibault Martinez | 4555f193a5 | 5 years ago |
Yoshua Wuyts | 61fc2bae72 | 5 years ago |
dignifiedquire | 5a1a681d68 | 5 years ago |
Friedel Ziegelmayer | e12cf80ab0 | 5 years ago |
Friedel Ziegelmayer | 631105b650 | 5 years ago |
Friedel Ziegelmayer | 0897b9184a | 5 years ago |
Friedel Ziegelmayer | 6ca7b0977c | 5 years ago |
Konrad Borowski | 721760a7a6 | 5 years ago |
dignifiedquire | 8389041414 | 5 years ago |
dignifiedquire | 8943ba82dd | 5 years ago |
dignifiedquire | 52c72426c1 | 5 years ago |
Yoshua Wuyts | 0df3c02b81 | 5 years ago |
Yoshua Wuyts | 166c469d1c | 5 years ago |
Friedel Ziegelmayer | 0ec027dbff | 5 years ago |
jerry73204 | d60e7cc27d | 5 years ago |
Friedel Ziegelmayer | 6d2a43e336 | 5 years ago |
dignifiedquire | e1c8638173 | 5 years ago |
dignifiedquire | 06eea4225b | 5 years ago |
Friedel Ziegelmayer | 252140839b | 5 years ago |
Heinz N. Gies | 69806403c6 | 5 years ago |
Friedel Ziegelmayer | 955befd746 | 5 years ago |
nasa | 70dac51938 | 5 years ago |
k-nasa | d30603affe | 5 years ago |
dignifiedquire | c9ecb5bbbd | 5 years ago |
Jacob Rothstein | 9e6a76af04 | 5 years ago |
Friedel Ziegelmayer | 2b6c7fedff | 5 years ago |
Friedel Ziegelmayer | b3277954c7 | 5 years ago |
Azriel Hoh | baead51a28 | 5 years ago |
Azriel Hoh | e9621af345 | 5 years ago |
Azriel Hoh | d3e59370e7 | 5 years ago |
Jacob Rothstein | cd5e17fe87 | 5 years ago |
Friedel Ziegelmayer | e20b0f0d75 | 5 years ago |
dignifiedquire | 19170aead4 | 5 years ago |
dignifiedquire | 2762ec5800 | 5 years ago |
dignifiedquire | 247c94ca06 | 5 years ago |
Friedel Ziegelmayer | e404dcdd03 | 5 years ago |
dignifiedquire | bd6a7e200b | 5 years ago |
Friedel Ziegelmayer | e4c4c93d29 | 5 years ago |
Thayne McCombs | 6f6fced103 | 5 years ago |
Friedel Ziegelmayer | 10f7abb3b6 | 5 years ago |
dignifiedquire | 27c605b4c9 | 5 years ago |
dignifiedquire | faea222b9c | 5 years ago |
dignifiedquire | 1214bc2dee | 5 years ago |
dignifiedquire | 26f62aafd9 | 5 years ago |
dignifiedquire | e0928463b1 | 5 years ago |
dignifiedquire | 92532612b7 | 5 years ago |
dignifiedquire | 1a6d4f6a2f | 5 years ago |
dignifiedquire | 7a9afbd81c | 5 years ago |
dignifiedquire | 280b1a4344 | 5 years ago |
dignifiedquire | 48dd683535 | 5 years ago |
dignifiedquire | 804a52b7fd | 5 years ago |
dignifiedquire | e4df1405c1 | 5 years ago |
dignifiedquire | 2cd2ba3530 | 5 years ago |
dignifiedquire | 3161a4e449 | 5 years ago |
dignifiedquire | 228cc59b3b | 5 years ago |
dignifiedquire | 0a7a52aed5 | 5 years ago |
dignifiedquire | 10c8b9a6d8 | 5 years ago |
dignifiedquire | fd6ae40817 | 5 years ago |
dignifiedquire | ab9d6554aa | 5 years ago |
dignifiedquire | f5fa0d7e4e | 5 years ago |
dignifiedquire | b96afc41dc | 5 years ago |
dignifiedquire | 75ab7219df | 5 years ago |
dignifiedquire | e082634b5e | 5 years ago |
dignifiedquire | fc9ee0dfdd | 5 years ago |
dignifiedquire | 1308fbdf55 | 5 years ago |
dignifiedquire | 690ab16587 | 5 years ago |
Florian Gilcher | 370642ef3e | 5 years ago |
Sunli | 100c3423c1 | 5 years ago |
nasa | 7999e6bf4b | 5 years ago |
Fangdun Cai | e707ea96e0 | 5 years ago |
Friedel Ziegelmayer | b446cd0230 | 5 years ago |
Thayne McCombs | db438abb8f | 5 years ago |
dignifiedquire | a4e07e345c | 5 years ago |
Yoshua Wuyts | aebba2bd95 | 5 years ago |
dignifiedquire | 0c9a66c1f6 | 5 years ago |
Friedel Ziegelmayer | fc4e472599 | 5 years ago |
nasa | 6674dc0edf | 5 years ago |
k-nasa | 088aa5662c | 5 years ago |
Devashish Dixit | 68fa054517 | 5 years ago |
k-nasa | b88138b5d7 | 5 years ago |
k-nasa | 11ee2a8985 | 5 years ago |
k-nasa | 322911142c | 5 years ago |
k-nasa | cfaec2aa95 | 5 years ago |
sunli | 57c648cf01 | 5 years ago |
k-nasa | 6d3ca5a06f | 5 years ago |
k-nasa | f960776846 | 5 years ago |
k-nasa | 5c6741724f | 5 years ago |
k-nasa | 24c5dbf949 | 5 years ago |
nasa | 2dbebe54ed | 5 years ago |
k-nasa | d7ee29a03f | 5 years ago |
k-nasa | 2b44c1be2e | 5 years ago |
k-nasa | b1ec1ea930 | 5 years ago |
k-nasa | 2ab075d027 | 5 years ago |
k-nasa | c0f18600cf | 5 years ago |
k-nasa | 6c8237276b | 5 years ago |
k-nasa | 98cbf7f8eb | 5 years ago |
k-nasa | 84e5c5f351 | 5 years ago |
Yoshua Wuyts | 3ff9e98f20 | 5 years ago |
Yoshua Wuyts | b7c7efc797 | 5 years ago |
Yoshua Wuyts | 19fd7a4084 | 5 years ago |
Yoshua Wuyts | 7885c245c5 | 5 years ago |
Yoshua Wuyts | 7b7b959a6e | 5 years ago |
Yoshua Wuyts | 32dce319d3 | 5 years ago |
Yoshua Wuyts | 49dd02b4de | 5 years ago |
Yoshua Wuyts | bb11c676a1 | 5 years ago |
Yoshua Wuyts | e026b7579a | 5 years ago |
Yoshua Wuyts | 51dd7ceb72 | 5 years ago |
k-nasa | 8931d1464e | 5 years ago |
nasa | cc19592f80 | 5 years ago |
nasa | f69887a50d | 5 years ago |
k-nasa | 0b0531057d | 5 years ago |
Yoshua Wuyts | 61f9483cc5 | 5 years ago |
k-nasa | f33d7f40ab | 5 years ago |
k-nasa | e3bf89fc05 | 5 years ago |
k-nasa | ec4b09ecd0 | 5 years ago |
k-nasa | b95bd6c1fe | 5 years ago |
k-nasa | 1e18839f1f | 5 years ago |
k-nasa | f31878655e | 5 years ago |
k-nasa | 9a62df143f | 5 years ago |
k-nasa | 75223905bd | 5 years ago |
k-nasa | be60dd9fe7 | 5 years ago |
k-nasa | 23b7c174f3 | 5 years ago |
Yoshua Wuyts | 9167d42f4b | 5 years ago |
Yoshua Wuyts | 4034d58709 | 5 years ago |
abhi | 4742f461fe | 5 years ago |
nasa | efab39eeaf | 5 years ago |
k-nasa | bd60cd9f81 | 5 years ago |
sunli | b9e4b6da3e | 5 years ago |
Yoshua Wuyts | eb03f37e43 | 5 years ago |
Yoshua Wuyts | d87e283215 | 5 years ago |
Yoshua Wuyts | 283a54a155 | 5 years ago |
Yoshua Wuyts | 3719484eba | 5 years ago |
Katharina Fey | aae835cc14 | 5 years ago |
Oleg Nosov | 68063adddf | 5 years ago |
Oleg Nosov | d7cab38b67 | 5 years ago |
Oleg Nosov | 32068942a6 | 5 years ago |
Oleg Nosov | 85c32ef9d2 | 5 years ago |
Oleg Nosov | b68be72763 | 5 years ago |
Oleg Nosov | c80915e216 | 5 years ago |
Oleg Nosov | 303ac90b7c | 5 years ago |
Yoshua Wuyts | d026c44ea3 | 5 years ago |
Yoshua Wuyts | 125fa5b0a0 | 5 years ago |
Yoshua Wuyts | 39f2c6da78 | 5 years ago |
k-nasa | 3e24e0ba4e | 5 years ago |
k-nasa | 0d90cb07b9 | 5 years ago |
k-nasa | f789f9d4f6 | 5 years ago |
k-nasa | ef985bc72e | 5 years ago |
Stjepan Glavina | 1d875836a2 | 5 years ago |
k-nasa | 7efe7caf66 | 5 years ago |
k-nasa | 22d929d481 | 5 years ago |
k-nasa | d622ec5d35 | 5 years ago |
k-nasa | 880b7ee987 | 5 years ago |
k-nasa | 1762de285b | 5 years ago |
k-nasa | 6aa55fde59 | 5 years ago |
k-nasa | 41f114d9fe | 5 years ago |
k-nasa | 3d32fd81f4 | 5 years ago |
k-nasa | 51b84a7620 | 5 years ago |
k-nasa | 4996f29778 | 5 years ago |
Toralf Wittner | 57974ae0b7 | 5 years ago |
Yoshua Wuyts | 57f9fb7e93 | 5 years ago |
Yoshua Wuyts | 6c1b5eb3ed | 5 years ago |
Yoshua Wuyts | beb8d240c2 | 5 years ago |
ninj | b258215952 | 5 years ago |
Florian Gilcher | 1ababac97f | 5 years ago |
Florian Gilcher | f9fe5c90cf | 5 years ago |
Florian Gilcher | 84fe94444b | 5 years ago |
Florian Gilcher | cad2880eb8 | 5 years ago |
Taiki Endo | 6b860c370a | 5 years ago |
Katharina Fey | 81aa6d152a | 5 years ago |
k-nasa | 2221441a4c | 5 years ago |
Oleg Nosov | ed7ddacb28 | 5 years ago |
nasa | d283352a9a | 5 years ago |
Oleg Nosov | ed248017b4 | 5 years ago |
Yoshua Wuyts | 0eb5ca14ac | 5 years ago |
Oleg Nosov | 38de0bfd22 | 5 years ago |
Oleg Nosov | 134089af2c | 5 years ago |
k-nasa | b72dd83726 | 5 years ago |
k-nasa | ee102dfc9e | 5 years ago |
nasa | 1071e82132 | 5 years ago |
noah | 0a52864764 | 5 years ago |
noah | 76993dd755 | 5 years ago |
Yoshua Wuyts | 133e30e6f6 | 5 years ago |
nasa | 76ed174fd5 | 5 years ago |
k-nasa | f53fcbb706 | 5 years ago |
k-nasa | e9357c0307 | 5 years ago |
Qifan Lu | 879e14c6ab | 5 years ago |
Qifan Lu | f8dd3d9816 | 5 years ago |
noah | a4f6806605 | 5 years ago |
Yoshua Wuyts | 5d5064b871 | 5 years ago |
nasa | 0ed0d63094 | 5 years ago |
Paul Colomiets | 0029037883 | 5 years ago |
Oleg Nosov | fb567a3a09 | 5 years ago |
Oleg Nosov | 83afbab2ef | 5 years ago |
Paul Colomiets | c8c075615c | 5 years ago |
Yoshua Wuyts | 98d45f4be1 | 5 years ago |
dignifiedquire | 9c6ab5e7c3 | 5 years ago |
dignifiedquire | 9c9ab90da3 | 5 years ago |
dignifiedquire | 5bf3d95313 | 5 years ago |
Yoshua Wuyts | 1f78efec64 | 5 years ago |
Yoshua Wuyts | 383057b8ea | 5 years ago |
Yoshua Wuyts | 763862acc7 | 5 years ago |
Yoshua Wuyts | e2bb79c207 | 5 years ago |
Yoshua Wuyts | 57a62797f2 | 5 years ago |
Yoshua Wuyts | dfb0c8124c | 5 years ago |
Ryan Leckey | d806a09599 | 5 years ago |
Alfie John | af2d46d9b9 | 5 years ago |
Artem Varaksa | 65d7950df1 | 5 years ago |
nasa | 6d69a3e368 | 5 years ago |
Stefano Probst | c3d5dba1b5 | 5 years ago |
Katharina Fey | 081166f204 | 5 years ago |
Yoshua Wuyts | fee3b6f603 | 5 years ago |
Yoshua Wuyts | b3942ecfa8 | 5 years ago |
Yoshua Wuyts | 86d3d74180 | 5 years ago |
Yoshua Wuyts | 3fd6d8b02e | 5 years ago |
nasa | 0d4b4cd260 | 5 years ago |
Miguel Pérez García | ef021dcb2b | 5 years ago |
Miguel Pérez García | eedf1d3367 | 5 years ago |
Miguel Pérez García | 97b4901b75 | 5 years ago |
Miguel Pérez García | 1eeb1019e9 | 5 years ago |
Miguel Pérez García | 980a1f7834 | 5 years ago |
nasa | d2c25f483a | 5 years ago |
nasa | d8befe24e8 | 5 years ago |
Florian Gilcher | c7cf1934db | 5 years ago |
Yoshua Wuyts | 37d8a013de | 5 years ago |
k-nasa | 43f4f393af | 5 years ago |
Stjepan Glavina | ceba324bef | 5 years ago |
Stjepan Glavina | 36d24cd0e1 | 5 years ago |
Yoshua Wuyts | 61eb52cb36 | 5 years ago |
Yoshua Wuyts | 6f4dcad6a0 | 5 years ago |
Yoshua Wuyts | 60de8e1082 | 5 years ago |
Yoshua Wuyts | cac4e081cc | 5 years ago |
Yoshua Wuyts | 8ad1d23116 | 5 years ago |
Yoshua Wuyts | 019aa14898 | 5 years ago |
Yoshua Wuyts | b7e55762d8 | 5 years ago |
Yoshua Wuyts | c70552ead5 | 5 years ago |
Fenhl | 07eb2c1280 | 5 years ago |
Gary Guo | 732ef10f98 | 5 years ago |
Gary Guo | 499a44ab3b | 5 years ago |
Yoshua Wuyts | 761029cd08 | 5 years ago |
Yoshua Wuyts | 83a488b290 | 5 years ago |
Yoshua Wuyts | 2f0907714d | 5 years ago |
Yoshua Wuyts | 055c64e8a7 | 5 years ago |
Yoshua Wuyts | 96d6fc43d6 | 5 years ago |
Yoshua Wuyts | 3d3bf914ea | 5 years ago |
Miguel Pérez García | 84b6d2b276 | 5 years ago |
Miguel Pérez García | 8de9f9b8e1 | 5 years ago |
Felipe Sere | 182fe6896f | 5 years ago |
Felipe Sere | b0038e11be | 5 years ago |
Felipe Sere | 8e5dedec34 | 5 years ago |
Felipe Sere | 41cf0f855b | 5 years ago |
Felipe Sere | f9a4c35fd6 | 5 years ago |
Felipe Sere | 6e8236d0e1 | 5 years ago |
Felipe Sere | 892c6008c2 | 5 years ago |
Felipe Sere | abd360893c | 5 years ago |
Felipe Sere | 94893d2924 | 5 years ago |
Felipe Sere | 02aa2f3d2a | 5 years ago |
Felipe Sere | ee2f52f3ce | 5 years ago |
Felipe Sere | 55194edbf7 | 5 years ago |
Felipe Sere | c4b9a7f680 | 5 years ago |
Felipe Sere | aabfefd015 | 5 years ago |
Felipe Sere | cc493df433 | 5 years ago |
Felipe Sere | 78bafbb88f | 5 years ago |
Felipe Sere | d0ef48c753 | 5 years ago |
Felipe Sere | fa288931c6 | 5 years ago |
Toralf Wittner | c90732a805 | 5 years ago |
Yoshua Wuyts | 63b6a2b961 | 5 years ago |
Yoshua Wuyts | 1103c17e16 | 5 years ago |
Yoshua Wuyts | a0f3b3b753 | 5 years ago |
Tomasz Miąsko | f06ab9fbc4 | 5 years ago |
svengrim | 447c17128f | 5 years ago |
k-nasa | a04157850b | 5 years ago |
nasa | 9311fd7fae | 5 years ago |
nasa | f7b21a3e8d | 5 years ago |
Dung Pham | f0bdcfec25 | 5 years ago |
Miguel Pérez García | 33e7c87dfc | 5 years ago |
Miguel Pérez García | cc85533f7c | 5 years ago |
Miguel Pérez García | 4670388a56 | 5 years ago |
Miguel Pérez García | c14c377974 | 5 years ago |
Miguel Pérez García | 54fa559554 | 5 years ago |
Yoshua Wuyts | bce8688763 | 5 years ago |
Bryant Luk | fd86effb63 | 5 years ago |
nasa | 128a6bc6ce | 5 years ago |
Yoshua Wuyts | d51a135015 | 5 years ago |
Miguel Pérez García | 1c2055fff0 | 5 years ago |
Povilas Balciunas | 81e3c41826 | 5 years ago |
k-nasa | fb1fb6c903 | 5 years ago |
k-nasa | c85e2496b1 | 5 years ago |
k-nasa | 7d9a063002 | 5 years ago |
k-nasa | 44e38eae59 | 5 years ago |
k-nasa | fe04cf26b6 | 5 years ago |
k-nasa | 556d7992ce | 5 years ago |
k-nasa | da965e9ba4 | 5 years ago |
k-nasa | 9f7c1833dc | 5 years ago |
linkmauve | 55560ea9b4 | 5 years ago |
Stjepan Glavina | bf9ee88815 | 5 years ago |
Stjepan Glavina | 9627826756 | 5 years ago |
Stjepan Glavina | 4ed15d67c9 | 5 years ago |
Stjepan Glavina | 0165d7f6d1 | 5 years ago |
Yoshua Wuyts | dba416608a | 5 years ago |
Yoshua Wuyts | 68005661d9 | 5 years ago |
nasa | 794e331761 | 5 years ago |
Yoshua Wuyts | 63f7ea3081 | 5 years ago |
k-nasa | 32765ece41 | 5 years ago |
k-nasa | 635c592950 | 5 years ago |
k-nasa | 3b055f364e | 5 years ago |
Yoshua Wuyts | 46cafffc31 | 5 years ago |
boats | 0f30ab8c0a | 5 years ago |
Yoshua Wuyts | e66e2e2b8f | 5 years ago |
Pascal Hertleif | 56538ebd91 | 5 years ago |
Pascal Hertleif | aa7d1c27a4 | 5 years ago |
Yoshua Wuyts | 850b8ae9d0 | 5 years ago |
Yoshua Wuyts | ac7a796f82 | 5 years ago |
Miguel Pérez García | c1f7be5d42 | 5 years ago |
Yoshua Wuyts | 50cefce803 | 5 years ago |
Yoshua Wuyts | 3780ff7b44 | 5 years ago |
Yoshua Wuyts | c9a2e74789 | 5 years ago |
Yoshua Wuyts | cffacf7fa3 | 5 years ago |
Yoshua Wuyts | 02e1d7e5ea | 5 years ago |
laizy | ec5415358f | 5 years ago |
Alejandro Martinez Ruiz | ba1ee2d204 | 5 years ago |
Stjepan Glavina | 16edec3464 | 5 years ago |
Pascal Hertleif | e01f07d72a | 5 years ago |
Yoshua Wuyts | b3d30de4a1 | 5 years ago |
Yoshua Wuyts | 6f19165e0e | 5 years ago |
nasa | d146d95a39 | 5 years ago |
Stjepan Glavina | 5fba3a0928 | 5 years ago |
Yoshua Wuyts | 2ca03cabe6 | 5 years ago |
hhggit | 72ed4eb4fd | 5 years ago |
Yoshua Wuyts | 77800ab3f9 | 5 years ago |
Yoshua Wuyts | 8ea920c9f0 | 5 years ago |
Yoshua Wuyts | d1189f9974 | 5 years ago |
Yoshua Wuyts | 3f8ec5a007 | 5 years ago |
nasa | 3bc4d293dd | 5 years ago |
Yoshua Wuyts | f24b3a4520 | 5 years ago |
razican | 72ca2c1a24 | 5 years ago |
k-nasa | b5e66c4f93 | 5 years ago |
k-nasa | 080875edc9 | 5 years ago |
k-nasa | ca71ad073b | 5 years ago |
k-nasa | 667bbc1019 | 5 years ago |
k-nasa | 64b2e10b93 | 5 years ago |
k-nasa | 314a75da28 | 5 years ago |
Pascal Hertleif | c704643296 | 5 years ago |
Yoshua Wuyts | c6622475b2 | 5 years ago |
Stjepan Glavina | 65afd41a33 | 5 years ago |
Stjepan Glavina | d3e7f32a30 | 5 years ago |
Razican | f6829859fe | 5 years ago |
hhggit | 2c9b558d14 | 5 years ago |
Pascal Hertleif | 99ddfb3f93 | 5 years ago |
Pascal Hertleif | 8ce3e78952 | 5 years ago |
sclaire-1 | b2aaa8b825 | 5 years ago |
yjhmelody | 223fcc30ee | 5 years ago |
k-nasa | 76975a4441 | 5 years ago |
Yoshua Wuyts | 355e2eded8 | 5 years ago |
Yoshua Wuyts | 6cc9e4dd2b | 5 years ago |
Yoshua Wuyts | ee23ba6e94 | 5 years ago |
Yoshua Wuyts | d68dc659b2 | 5 years ago |
Yoshua Wuyts | b5c3fb8bb5 | 5 years ago |
k-nasa | 7d616c695d | 5 years ago |
yjhmelody | a69b3a8a9e | 5 years ago |
k-nasa | a05b6a3810 | 5 years ago |
k-nasa | 6cbf48f12d | 5 years ago |
k-nasa | 91ee4c7b9f | 5 years ago |
k-nasa | 603b3c5085 | 5 years ago |
Yoshua Wuyts | 693a7257b8 | 5 years ago |
Yoshua Wuyts | 8779c04dc7 | 5 years ago |
Yoshua Wuyts | 3564be9c0c | 5 years ago |
k-nasa | df92c63337 | 5 years ago |
k-nasa | 31cf932d80 | 5 years ago |
Yoshua Wuyts | 3c6d41ccb4 | 5 years ago |
Yoshua Wuyts | 837604b833 | 5 years ago |
yjh | 74caed2d4b | 5 years ago |
yjhmelody | 76ec9c4563 | 5 years ago |
yjhmelody | d76b32e6d4 | 5 years ago |
yjhmelody | 11268a80fb | 5 years ago |
Yoshua Wuyts | c4ba11ff95 | 5 years ago |
k-nasa | de67bf0fd4 | 5 years ago |
k-nasa | 4ef55d4d7b | 5 years ago |
nasa | 77a0419a3e | 5 years ago |
Yoshua Wuyts | ce98834039 | 5 years ago |
Yoshua Wuyts | 30ff7b09b6 | 5 years ago |
Yoshua Wuyts | c58747b5fc | 5 years ago |
Yoshua Wuyts | 31f129ebe7 | 5 years ago |
Yoshua Wuyts | f49d7cbbb8 | 5 years ago |
Felipe Sere | 64216b8e6b | 5 years ago |
Felipe Sere | 786a52a09d | 5 years ago |
Felipe Sere | f14b37ff17 | 5 years ago |
Felipe Sere | 7677e9a3df | 5 years ago |
Felipe Sere | bfb42b432e | 5 years ago |
Felipe Sere | 4c09cdbeac | 5 years ago |
Felipe Sere | 243cdd7ff1 | 5 years ago |
Felipe Sere | a257b7018c | 5 years ago |
Felipe Sere | af928163e4 | 5 years ago |
Felipe Sere | 8d97e0f974 | 5 years ago |
Felipe Sere | 266754897e | 5 years ago |
Felipe Sere | 554d5cfbc1 | 5 years ago |
Felipe Sere | 8b662b659d | 5 years ago |
Felipe Sere | 95a3e53fcd | 5 years ago |
Felipe Sere | 02b261de10 | 5 years ago |
Felipe Sere | fe3c9ef626 | 5 years ago |
Yoshua Wuyts | 4e1d79adb1 | 5 years ago |
Yoshua Wuyts | 1546448800 | 5 years ago |
Yoshua Wuyts | 338273eb18 | 5 years ago |
Wouter Geraedts | dda65cbff0 | 5 years ago |
Johannes Weissmann | 9ebe41f2d6 | 5 years ago |
Wouter Geraedts | 90c67c223a | 5 years ago |
sclaire-1 | 8473b738d0 | 5 years ago |
yjhmelody | 5adb112a00 | 5 years ago |
yjhmelody | 9d634cb2a7 | 5 years ago |
yjhmelody | 879af6dc85 | 5 years ago |
Yoshua Wuyts | 79962e20a5 | 5 years ago |
Yoshua Wuyts | 1431ee0422 | 5 years ago |
Yoshua Wuyts | 2dfdc1c482 | 5 years ago |
Wouter Geraedts | c5b3a98e5b | 5 years ago |
Wouter Geraedts | 4ab7b213de | 5 years ago |
Wouter Geraedts | 6990c1403f | 5 years ago |
Wouter Geraedts | 77a1849303 | 5 years ago |
Wouter Geraedts | a722de1a10 | 5 years ago |
Wouter Geraedts | 88cbf2c119 | 5 years ago |
Wouter Geraedts | 6f6d5e9d20 | 5 years ago |
Wouter Geraedts | 7c7386735e | 5 years ago |
Johannes Weissmann | 37922408e5 | 5 years ago |
Johannes Weissmann | 7d2282dbd2 | 5 years ago |
Johannes Weissmann | 60f822bee5 | 5 years ago |
Wouter Geraedts | ef958f0408 | 5 years ago |
Wouter Geraedts | 139a34b685 | 5 years ago |
Wouter Geraedts | b591fc68bd | 5 years ago |
razican | 5d558ca213 | 5 years ago |
razican | e48e463736 | 5 years ago |
razican | 0c37d4af10 | 5 years ago |
razican | 79bbf4938d | 5 years ago |
Wouter Geraedts | 14d7d3bf9c | 5 years ago |
Johannes Weissmann | 75546ef831 | 5 years ago |
Johannes Weissmann | 6608d39c59 | 5 years ago |
Wouter Geraedts | 1fd05a157f | 5 years ago |
Wouter Geraedts | 1c843a8124 | 5 years ago |
Johannes Weissmann | 97094b2a1c | 5 years ago |
Wouter Geraedts | ced5281b73 | 5 years ago |
Johannes Weissmann | a9a7bdc290 | 5 years ago |
Wouter Geraedts | a2393501c5 | 5 years ago |
Yoshua Wuyts | 483ded0e1c | 5 years ago |
Yoshua Wuyts | 064fdf020f | 5 years ago |
@ -0,0 +1,3 @@
|
|||||||
|
Our contribution policy can be found at [async.rs/contribute][policy].
|
||||||
|
|
||||||
|
[policy]: https://async.rs/contribute/
|
@ -1,125 +1,40 @@
|
|||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
|
|
||||||
mod async_std {
|
extern crate test;
|
||||||
extern crate test;
|
|
||||||
|
|
||||||
use async_std::sync::{Arc, Mutex};
|
use async_std::sync::{Arc, Mutex};
|
||||||
use async_std::task;
|
use async_std::task;
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn create(b: &mut Bencher) {
|
fn create(b: &mut Bencher) {
|
||||||
b.iter(|| Mutex::new(()));
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod std {
|
#[bench]
|
||||||
extern crate test;
|
fn contention(b: &mut Bencher) {
|
||||||
|
b.iter(|| task::block_on(run(10, 1000)));
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::thread;
|
|
||||||
use test::Bencher;
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn create(b: &mut Bencher) {
|
|
||||||
b.iter(|| Mutex::new(()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn contention(b: &mut Bencher) {
|
|
||||||
b.iter(|| run(10, 1000));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn no_contention(b: &mut Bencher) {
|
|
||||||
b.iter(|| run(1, 10000));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(thread: usize, iter: usize) {
|
|
||||||
let m = Arc::new(Mutex::new(()));
|
|
||||||
let mut threads = Vec::new();
|
|
||||||
|
|
||||||
for _ in 0..thread {
|
|
||||||
let m = m.clone();
|
|
||||||
threads.push(thread::spawn(move || {
|
|
||||||
for _ in 0..iter {
|
|
||||||
let _ = m.lock().unwrap();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
for t in threads {
|
|
||||||
t.join().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod parking_lot {
|
#[bench]
|
||||||
extern crate test;
|
fn no_contention(b: &mut Bencher) {
|
||||||
|
b.iter(|| task::block_on(run(1, 10000)));
|
||||||
use parking_lot::Mutex;
|
}
|
||||||
use std::sync::Arc;
|
|
||||||
use std::thread;
|
|
||||||
use test::Bencher;
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn create(b: &mut Bencher) {
|
|
||||||
b.iter(|| Mutex::new(()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
async fn run(task: usize, iter: usize) {
|
||||||
fn contention(b: &mut Bencher) {
|
let m = Arc::new(Mutex::new(()));
|
||||||
b.iter(|| run(10, 1000));
|
let mut tasks = Vec::new();
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
for _ in 0..task {
|
||||||
fn no_contention(b: &mut Bencher) {
|
let m = m.clone();
|
||||||
b.iter(|| run(1, 10000));
|
tasks.push(task::spawn(async move {
|
||||||
|
for _ in 0..iter {
|
||||||
|
let _ = m.lock().await;
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(thread: usize, iter: usize) {
|
for t in tasks {
|
||||||
let m = Arc::new(Mutex::new(()));
|
t.await;
|
||||||
let mut threads = Vec::new();
|
|
||||||
|
|
||||||
for _ in 0..thread {
|
|
||||||
let m = m.clone();
|
|
||||||
threads.push(thread::spawn(move || {
|
|
||||||
for _ in 0..iter {
|
|
||||||
let _ = m.lock();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
for t in threads {
|
|
||||||
t.join().unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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).
|
@ -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).
|
||||||
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
use crate::utils::Context;
|
||||||
|
|
||||||
|
use std::{error::Error as StdError, fmt, io};
|
||||||
|
|
||||||
|
/// Wrap `std::io::Error` with additional message
|
||||||
|
///
|
||||||
|
/// Keeps the original error kind and stores the original I/O error as `source`.
|
||||||
|
impl<T> Context for Result<T, std::io::Error> {
|
||||||
|
fn context(self, message: impl Fn() -> String) -> Self {
|
||||||
|
self.map_err(|e| VerboseError::wrap(e, message()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct VerboseError {
|
||||||
|
source: io::Error,
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VerboseError {
|
||||||
|
pub(crate) fn wrap(source: io::Error, message: impl Into<String>) -> io::Error {
|
||||||
|
io::Error::new(
|
||||||
|
source.kind(),
|
||||||
|
VerboseError {
|
||||||
|
source,
|
||||||
|
message: message.into(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for VerboseError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StdError for VerboseError {
|
||||||
|
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||||
|
Some(&self.source)
|
||||||
|
}
|
||||||
|
}
|
@ -1,315 +0,0 @@
|
|||||||
use std::fmt;
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use mio::{self, Evented};
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use slab::Slab;
|
|
||||||
|
|
||||||
use crate::io;
|
|
||||||
use crate::task::{Context, Poll, Waker};
|
|
||||||
use crate::utils::abort_on_panic;
|
|
||||||
|
|
||||||
/// Data associated with a registered I/O handle.
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Entry {
|
|
||||||
/// A unique identifier.
|
|
||||||
token: mio::Token,
|
|
||||||
|
|
||||||
/// Tasks that are blocked on reading from this I/O handle.
|
|
||||||
readers: Mutex<Vec<Waker>>,
|
|
||||||
|
|
||||||
/// Thasks that are blocked on writing to this I/O handle.
|
|
||||||
writers: Mutex<Vec<Waker>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The state of a networking driver.
|
|
||||||
struct Reactor {
|
|
||||||
/// A mio instance that polls for new events.
|
|
||||||
poller: mio::Poll,
|
|
||||||
|
|
||||||
/// A collection of registered I/O handles.
|
|
||||||
entries: Mutex<Slab<Arc<Entry>>>,
|
|
||||||
|
|
||||||
/// Dummy I/O handle that is only used to wake up the polling thread.
|
|
||||||
notify_reg: (mio::Registration, mio::SetReadiness),
|
|
||||||
|
|
||||||
/// An identifier for the notification handle.
|
|
||||||
notify_token: mio::Token,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reactor {
|
|
||||||
/// Creates a new reactor for polling I/O events.
|
|
||||||
fn new() -> io::Result<Reactor> {
|
|
||||||
let poller = mio::Poll::new()?;
|
|
||||||
let notify_reg = mio::Registration::new2();
|
|
||||||
|
|
||||||
let mut reactor = Reactor {
|
|
||||||
poller,
|
|
||||||
entries: Mutex::new(Slab::new()),
|
|
||||||
notify_reg,
|
|
||||||
notify_token: mio::Token(0),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Register a dummy I/O handle for waking up the polling thread.
|
|
||||||
let entry = reactor.register(&reactor.notify_reg.0)?;
|
|
||||||
reactor.notify_token = entry.token;
|
|
||||||
|
|
||||||
Ok(reactor)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Registers an I/O event source and returns its associated entry.
|
|
||||||
fn register(&self, source: &dyn Evented) -> io::Result<Arc<Entry>> {
|
|
||||||
let mut entries = self.entries.lock().unwrap();
|
|
||||||
|
|
||||||
// Reserve a vacant spot in the slab and use its key as the token value.
|
|
||||||
let vacant = entries.vacant_entry();
|
|
||||||
let token = mio::Token(vacant.key());
|
|
||||||
|
|
||||||
// Allocate an entry and insert it into the slab.
|
|
||||||
let entry = Arc::new(Entry {
|
|
||||||
token,
|
|
||||||
readers: Mutex::new(Vec::new()),
|
|
||||||
writers: Mutex::new(Vec::new()),
|
|
||||||
});
|
|
||||||
vacant.insert(entry.clone());
|
|
||||||
|
|
||||||
// Register the I/O event source in the poller.
|
|
||||||
let interest = mio::Ready::all();
|
|
||||||
let opts = mio::PollOpt::edge();
|
|
||||||
self.poller.register(source, token, interest, opts)?;
|
|
||||||
|
|
||||||
Ok(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deregisters an I/O event source associated with an entry.
|
|
||||||
fn deregister(&self, source: &dyn Evented, entry: &Entry) -> io::Result<()> {
|
|
||||||
// Deregister the I/O object from the mio instance.
|
|
||||||
self.poller.deregister(source)?;
|
|
||||||
|
|
||||||
// Remove the entry associated with the I/O object.
|
|
||||||
self.entries.lock().unwrap().remove(entry.token.0);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn notify(&self) {
|
|
||||||
// self.notify_reg
|
|
||||||
// .1
|
|
||||||
// .set_readiness(mio::Ready::readable())
|
|
||||||
// .unwrap();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The state of the global networking driver.
|
|
||||||
static REACTOR: Lazy<Reactor> = Lazy::new(|| {
|
|
||||||
// Spawn a thread that waits on the poller for new events and wakes up tasks blocked on I/O
|
|
||||||
// handles.
|
|
||||||
std::thread::Builder::new()
|
|
||||||
.name("async-std/net".to_string())
|
|
||||||
.spawn(move || {
|
|
||||||
// If the driver thread panics, there's not much we can do. It is not a
|
|
||||||
// recoverable error and there is no place to propagate it into so we just abort.
|
|
||||||
abort_on_panic(|| {
|
|
||||||
main_loop().expect("async networking thread has panicked");
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.expect("cannot start a thread driving blocking tasks");
|
|
||||||
|
|
||||||
Reactor::new().expect("cannot initialize reactor")
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Waits on the poller for new events and wakes up tasks blocked on I/O handles.
|
|
||||||
fn main_loop() -> io::Result<()> {
|
|
||||||
let reactor = &REACTOR;
|
|
||||||
let mut events = mio::Events::with_capacity(1000);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Block on the poller until at least one new event comes in.
|
|
||||||
reactor.poller.poll(&mut events, None)?;
|
|
||||||
|
|
||||||
// Lock the entire entry table while we're processing new events.
|
|
||||||
let entries = reactor.entries.lock().unwrap();
|
|
||||||
|
|
||||||
for event in events.iter() {
|
|
||||||
let token = event.token();
|
|
||||||
|
|
||||||
if token == reactor.notify_token {
|
|
||||||
// If this is the notification token, we just need the notification state.
|
|
||||||
reactor.notify_reg.1.set_readiness(mio::Ready::empty())?;
|
|
||||||
} else {
|
|
||||||
// Otherwise, look for the entry associated with this token.
|
|
||||||
if let Some(entry) = entries.get(token.0) {
|
|
||||||
// Set the readiness flags from this I/O event.
|
|
||||||
let readiness = event.readiness();
|
|
||||||
|
|
||||||
// Wake up reader tasks blocked on this I/O handle.
|
|
||||||
if !(readiness & reader_interests()).is_empty() {
|
|
||||||
for w in entry.readers.lock().unwrap().drain(..) {
|
|
||||||
w.wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wake up writer tasks blocked on this I/O handle.
|
|
||||||
if !(readiness & writer_interests()).is_empty() {
|
|
||||||
for w in entry.writers.lock().unwrap().drain(..) {
|
|
||||||
w.wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An I/O handle powered by the networking driver.
|
|
||||||
///
|
|
||||||
/// This handle wraps an I/O event source and exposes a "futurized" interface on top of it,
|
|
||||||
/// implementing traits `AsyncRead` and `AsyncWrite`.
|
|
||||||
pub struct Watcher<T: Evented> {
|
|
||||||
/// Data associated with the I/O handle.
|
|
||||||
entry: Arc<Entry>,
|
|
||||||
|
|
||||||
/// The I/O event source.
|
|
||||||
source: Option<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Evented> Watcher<T> {
|
|
||||||
/// Creates a new I/O handle.
|
|
||||||
///
|
|
||||||
/// The provided I/O event source will be kept registered inside the reactor's poller for the
|
|
||||||
/// lifetime of the returned I/O handle.
|
|
||||||
pub fn new(source: T) -> Watcher<T> {
|
|
||||||
Watcher {
|
|
||||||
entry: REACTOR
|
|
||||||
.register(&source)
|
|
||||||
.expect("cannot register an I/O event source"),
|
|
||||||
source: Some(source),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the inner I/O event source.
|
|
||||||
pub fn get_ref(&self) -> &T {
|
|
||||||
self.source.as_ref().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Polls the inner I/O source for a non-blocking read operation.
|
|
||||||
///
|
|
||||||
/// If the operation returns an error of the `io::ErrorKind::WouldBlock` kind, the current task
|
|
||||||
/// will be registered for wakeup when the I/O source becomes readable.
|
|
||||||
pub fn poll_read_with<'a, F, R>(&'a self, cx: &mut Context<'_>, mut f: F) -> Poll<io::Result<R>>
|
|
||||||
where
|
|
||||||
F: FnMut(&'a T) -> io::Result<R>,
|
|
||||||
{
|
|
||||||
// If the operation isn't blocked, return its result.
|
|
||||||
match f(self.source.as_ref().unwrap()) {
|
|
||||||
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
|
|
||||||
res => return Poll::Ready(res),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock the waker list.
|
|
||||||
let mut list = self.entry.readers.lock().unwrap();
|
|
||||||
|
|
||||||
// Try running the operation again.
|
|
||||||
match f(self.source.as_ref().unwrap()) {
|
|
||||||
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
|
|
||||||
res => return Poll::Ready(res),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the task if it isn't registered already.
|
|
||||||
if list.iter().all(|w| !w.will_wake(cx.waker())) {
|
|
||||||
list.push(cx.waker().clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Polls the inner I/O source for a non-blocking write operation.
|
|
||||||
///
|
|
||||||
/// If the operation returns an error of the `io::ErrorKind::WouldBlock` kind, the current task
|
|
||||||
/// will be registered for wakeup when the I/O source becomes writable.
|
|
||||||
pub fn poll_write_with<'a, F, R>(
|
|
||||||
&'a self,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
mut f: F,
|
|
||||||
) -> Poll<io::Result<R>>
|
|
||||||
where
|
|
||||||
F: FnMut(&'a T) -> io::Result<R>,
|
|
||||||
{
|
|
||||||
// If the operation isn't blocked, return its result.
|
|
||||||
match f(self.source.as_ref().unwrap()) {
|
|
||||||
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
|
|
||||||
res => return Poll::Ready(res),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock the waker list.
|
|
||||||
let mut list = self.entry.writers.lock().unwrap();
|
|
||||||
|
|
||||||
// Try running the operation again.
|
|
||||||
match f(self.source.as_ref().unwrap()) {
|
|
||||||
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
|
|
||||||
res => return Poll::Ready(res),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the task if it isn't registered already.
|
|
||||||
if list.iter().all(|w| !w.will_wake(cx.waker())) {
|
|
||||||
list.push(cx.waker().clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deregisters and returns the inner I/O source.
|
|
||||||
///
|
|
||||||
/// This method is typically used to convert `Watcher`s to raw file descriptors/handles.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn into_inner(mut self) -> T {
|
|
||||||
let source = self.source.take().unwrap();
|
|
||||||
REACTOR
|
|
||||||
.deregister(&source, &self.entry)
|
|
||||||
.expect("cannot deregister I/O event source");
|
|
||||||
source
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Evented> Drop for Watcher<T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(ref source) = self.source {
|
|
||||||
REACTOR
|
|
||||||
.deregister(source, &self.entry)
|
|
||||||
.expect("cannot deregister I/O event source");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Evented + fmt::Debug> fmt::Debug for Watcher<T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("Watcher")
|
|
||||||
.field("entry", &self.entry)
|
|
||||||
.field("source", &self.source)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mask containing flags that interest tasks reading from I/O handles.
|
|
||||||
#[inline]
|
|
||||||
fn reader_interests() -> mio::Ready {
|
|
||||||
mio::Ready::all() - mio::Ready::writable()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mask containing flags that interest tasks writing into I/O handles.
|
|
||||||
#[inline]
|
|
||||||
fn writer_interests() -> mio::Ready {
|
|
||||||
mio::Ready::writable() | hup()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a flag containing the hangup status.
|
|
||||||
#[inline]
|
|
||||||
fn hup() -> mio::Ready {
|
|
||||||
#[cfg(unix)]
|
|
||||||
let ready = mio::unix::UnixReady::hup().into();
|
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
|
||||||
let ready = mio::Ready::empty();
|
|
||||||
|
|
||||||
ready
|
|
||||||
}
|
|
@ -0,0 +1,55 @@
|
|||||||
|
//! Windows-specific filesystem extensions.
|
||||||
|
|
||||||
|
use crate::io;
|
||||||
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
|
/// Creates a new directory symbolic link on the filesystem.
|
||||||
|
///
|
||||||
|
/// The `dst` path will be a directory symbolic link pointing to the `src` path.
|
||||||
|
///
|
||||||
|
/// This function is an async version of [`std::os::windows::fs::symlink_dir`].
|
||||||
|
///
|
||||||
|
/// [`std::os::windows::fs::symlink_dir`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// #
|
||||||
|
/// use async_std::os::windows::fs::symlink_dir;
|
||||||
|
///
|
||||||
|
/// symlink_dir("a", "b").await?;
|
||||||
|
/// #
|
||||||
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
|
pub async fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
|
||||||
|
let src = src.as_ref().to_owned();
|
||||||
|
let dst = dst.as_ref().to_owned();
|
||||||
|
spawn_blocking(move || std::os::windows::fs::symlink_dir(&src, &dst)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new file symbolic link on the filesystem.
|
||||||
|
///
|
||||||
|
/// The `dst` path will be a file symbolic link pointing to the `src` path.
|
||||||
|
///
|
||||||
|
/// This function is an async version of [`std::os::windows::fs::symlink_file`].
|
||||||
|
///
|
||||||
|
/// [`std::os::windows::fs::symlink_file`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// #
|
||||||
|
/// use async_std::os::windows::fs::symlink_file;
|
||||||
|
///
|
||||||
|
/// symlink_file("a.txt", "b.txt").await?;
|
||||||
|
/// #
|
||||||
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
|
pub async fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
|
||||||
|
let src = src.as_ref().to_owned();
|
||||||
|
let dst = dst.as_ref().to_owned();
|
||||||
|
spawn_blocking(move || std::os::windows::fs::symlink_file(&src, &dst)).await
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
//! The runtime.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
use crate::future;
|
||||||
|
|
||||||
|
/// Dummy runtime struct.
|
||||||
|
pub struct Runtime {}
|
||||||
|
|
||||||
|
/// The global runtime.
|
||||||
|
pub static RUNTIME: Lazy<Runtime> = Lazy::new(|| {
|
||||||
|
// Create an executor thread pool.
|
||||||
|
|
||||||
|
let thread_count = env::var("ASYNC_STD_THREAD_COUNT")
|
||||||
|
.map(|env| {
|
||||||
|
env.parse()
|
||||||
|
.expect("ASYNC_STD_THREAD_COUNT must be a number")
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|_| num_cpus::get())
|
||||||
|
.max(1);
|
||||||
|
|
||||||
|
let thread_name = env::var("ASYNC_STD_THREAD_NAME").unwrap_or("async-std/runtime".to_string());
|
||||||
|
|
||||||
|
for _ in 0..thread_count {
|
||||||
|
thread::Builder::new()
|
||||||
|
.name(thread_name.clone())
|
||||||
|
.spawn(|| crate::task::block_on(future::pending::<()>()))
|
||||||
|
.expect("cannot start a runtime thread");
|
||||||
|
}
|
||||||
|
Runtime {}
|
||||||
|
});
|
@ -1,24 +0,0 @@
|
|||||||
use crate::stream::Stream;
|
|
||||||
|
|
||||||
use std::pin::Pin;
|
|
||||||
use std::task::{Context, Poll};
|
|
||||||
|
|
||||||
/// A stream able to yield elements from both ends.
|
|
||||||
///
|
|
||||||
/// Something that implements `DoubleEndedStream` has one extra capability
|
|
||||||
/// over something that implements [`Stream`]: the ability to also take
|
|
||||||
/// `Item`s from the back, as well as the front.
|
|
||||||
///
|
|
||||||
/// [`Stream`]: trait.Stream.html
|
|
||||||
#[cfg(feature = "unstable")]
|
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
|
||||||
pub trait DoubleEndedStream: Stream {
|
|
||||||
/// Removes and returns an element from the end of the stream.
|
|
||||||
///
|
|
||||||
/// Returns `None` when there are no more elements.
|
|
||||||
///
|
|
||||||
/// The [trait-level] docs contain more details.
|
|
||||||
///
|
|
||||||
/// [trait-level]: trait.DoubleEndedStream.html
|
|
||||||
fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
|
|
||||||
}
|
|
@ -0,0 +1,246 @@
|
|||||||
|
use crate::stream::Stream;
|
||||||
|
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
|
mod next_back;
|
||||||
|
mod nth_back;
|
||||||
|
mod rfind;
|
||||||
|
mod rfold;
|
||||||
|
mod try_rfold;
|
||||||
|
|
||||||
|
use next_back::NextBackFuture;
|
||||||
|
use nth_back::NthBackFuture;
|
||||||
|
use rfind::RFindFuture;
|
||||||
|
use rfold::RFoldFuture;
|
||||||
|
use try_rfold::TryRFoldFuture;
|
||||||
|
|
||||||
|
/// A stream able to yield elements from both ends.
|
||||||
|
///
|
||||||
|
/// Something that implements `DoubleEndedStream` has one extra capability
|
||||||
|
/// over something that implements [`Stream`]: the ability to also take
|
||||||
|
/// `Item`s from the back, as well as the front.
|
||||||
|
///
|
||||||
|
/// [`Stream`]: trait.Stream.html
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
pub trait DoubleEndedStream: Stream {
|
||||||
|
#[doc = r#"
|
||||||
|
Attempts to receive the next item from the back of the stream.
|
||||||
|
|
||||||
|
There are several possible return values:
|
||||||
|
|
||||||
|
* `Poll::Pending` means this stream's next_back value is not ready yet.
|
||||||
|
* `Poll::Ready(None)` means this stream has been exhausted.
|
||||||
|
* `Poll::Ready(Some(item))` means `item` was received out of the stream.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# fn main() { async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::stream;
|
||||||
|
use async_std::task::{Context, Poll};
|
||||||
|
|
||||||
|
fn increment(
|
||||||
|
s: impl DoubleEndedStream<Item = i32> + Unpin,
|
||||||
|
) -> impl DoubleEndedStream<Item = i32> + Unpin {
|
||||||
|
struct Increment<S>(S);
|
||||||
|
|
||||||
|
impl<S: DoubleEndedStream<Item = i32> + Unpin> Stream for Increment<S> {
|
||||||
|
type Item = S::Item;
|
||||||
|
|
||||||
|
fn poll_next(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
) -> Poll<Option<Self::Item>> {
|
||||||
|
match Pin::new(&mut self.0).poll_next(cx) {
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
Poll::Ready(None) => Poll::Ready(None),
|
||||||
|
Poll::Ready(Some(item)) => Poll::Ready(Some(item + 1)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: DoubleEndedStream<Item = i32> + Unpin> DoubleEndedStream for Increment<S> {
|
||||||
|
fn poll_next_back(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
) -> Poll<Option<Self::Item>> {
|
||||||
|
match Pin::new(&mut self.0).poll_next_back(cx) {
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
Poll::Ready(None) => Poll::Ready(None),
|
||||||
|
Poll::Ready(Some(item)) => Poll::Ready(Some(item + 1)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Increment(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut s = increment(stream::once(7));
|
||||||
|
|
||||||
|
assert_eq!(s.next_back().await, Some(8));
|
||||||
|
assert_eq!(s.next_back().await, None);
|
||||||
|
#
|
||||||
|
# }) }
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Advances the stream and returns the next value.
|
||||||
|
|
||||||
|
Returns [`None`] when iteration is finished. Individual stream implementations may
|
||||||
|
choose to resume iteration, and so calling `next()` again may or may not eventually
|
||||||
|
start returning more values.
|
||||||
|
|
||||||
|
[`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# fn main() { async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::stream;
|
||||||
|
|
||||||
|
let mut s = stream::from_iter(vec![7u8]);
|
||||||
|
|
||||||
|
assert_eq!(s.next_back().await, Some(7));
|
||||||
|
assert_eq!(s.next_back().await, None);
|
||||||
|
#
|
||||||
|
# }) }
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
fn next_back(&mut self) -> NextBackFuture<'_, Self>
|
||||||
|
where
|
||||||
|
Self: Unpin,
|
||||||
|
{
|
||||||
|
NextBackFuture { stream: self }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Returns the nth element from the back of the stream.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
Basic usage:
|
||||||
|
|
||||||
|
```
|
||||||
|
# fn main() { async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::stream;
|
||||||
|
|
||||||
|
let mut s = stream::from_iter(vec![1u8, 2, 3, 4, 5]);
|
||||||
|
|
||||||
|
let second = s.nth_back(1).await;
|
||||||
|
assert_eq!(second, Some(4));
|
||||||
|
#
|
||||||
|
# }) }
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
fn nth_back(&mut self, n: usize) -> NthBackFuture<'_, Self>
|
||||||
|
where
|
||||||
|
Self: Unpin + Sized,
|
||||||
|
{
|
||||||
|
NthBackFuture::new(self, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Returns the the frist element from the right that matches the predicate.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
Basic usage:
|
||||||
|
|
||||||
|
```
|
||||||
|
# fn main() { async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::stream;
|
||||||
|
|
||||||
|
let mut s = stream::from_iter(vec![1u8, 2, 3, 4, 5]);
|
||||||
|
|
||||||
|
let second = s.rfind(|v| v % 2 == 0).await;
|
||||||
|
assert_eq!(second, Some(4));
|
||||||
|
#
|
||||||
|
# }) }
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
fn rfind<P>(&mut self, p: P) -> RFindFuture<'_, Self, P>
|
||||||
|
where
|
||||||
|
Self: Unpin + Sized,
|
||||||
|
P: FnMut(&Self::Item) -> bool,
|
||||||
|
{
|
||||||
|
RFindFuture::new(self, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
Basic usage:
|
||||||
|
|
||||||
|
```
|
||||||
|
# fn main() { async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::stream;
|
||||||
|
|
||||||
|
let s = stream::from_iter(vec![1u8, 2, 3, 4, 5]);
|
||||||
|
|
||||||
|
let second = s.rfold(0, |acc, v| v + acc).await;
|
||||||
|
|
||||||
|
assert_eq!(second, 15);
|
||||||
|
#
|
||||||
|
# }) }
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
fn rfold<B, F>(self, accum: B, f: F) -> RFoldFuture<Self, F, B>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
F: FnMut(B, Self::Item) -> B,
|
||||||
|
{
|
||||||
|
RFoldFuture::new(self, accum, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
A combinator that applies a function as long as it returns successfully, producing a single, final value.
|
||||||
|
Immediately returns the error when the function returns unsuccessfully.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
Basic usage:
|
||||||
|
|
||||||
|
```
|
||||||
|
# fn main() { async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::stream;
|
||||||
|
|
||||||
|
let s = stream::from_iter(vec![1u8, 2, 3, 4, 5]);
|
||||||
|
let sum = s.try_rfold(0, |acc, v| {
|
||||||
|
if (acc+v) % 2 == 1 {
|
||||||
|
Ok(v+3)
|
||||||
|
} else {
|
||||||
|
Err("fail")
|
||||||
|
}
|
||||||
|
}).await;
|
||||||
|
|
||||||
|
assert_eq!(sum, Err("fail"));
|
||||||
|
#
|
||||||
|
# }) }
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
fn try_rfold<B, F, E>(self, accum: B, f: F) -> TryRFoldFuture<Self, F, B>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
F: FnMut(B, Self::Item) -> Result<B, E>,
|
||||||
|
{
|
||||||
|
TryRFoldFuture::new(self, accum, f)
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue