Compare commits

...

485 commits

Author SHA1 Message Date
Yoshua Wuyts
8aa5921dfa
Merge pull request #833 from OSSystems/topic/fix_channel_doc
channel doc: Fix misleading reference to None return on Receiver
2020-07-14 10:40:40 +02:00
Jonathas-Conceicao
cd7fb9dec2 channel doc: Fix misleading reference to None return on Receiver
Signed-off-by: Jonathas-Conceicao <jonathas.conceicao@ossystems.com.br>
2020-07-13 10:52:44 -03:00
Yoshua Wuyts
c82b1efb69
fix(stream): add send guards on collect
Closes #639 

Co-authored-by: dignifiedquire <me@dignifiedquire.com>
2020-06-27 16:46:14 +02:00
Friedel Ziegelmayer
8c4b425136
Merge pull request #826 from thibault-martinez/kv-log-macro-1.0.6
Update kv-log-macro to 1.0.6
2020-06-27 15:25:26 +02:00
Thibault Martinez
2ab08ebbbc Update kv-log-macro to 1.0.6 2020-06-27 14:23:54 +02:00
Friedel Ziegelmayer
0e7650a421
Merge pull request #822 from async-rs/async-extern-1 2020-06-26 16:43:49 +02:00
dignifiedquire
8f17e9275b test: try to stabilize CI 2020-06-26 12:48:23 +02:00
dignifiedquire
18dffe8b43 refactor: switch to async-mutex for Mutex implementation 2020-06-26 10:30:58 +02:00
Friedel Ziegelmayer
43de93312c
Merge pull request #825 from nbdd0121/master
Fix unused_mut warning in nightly
2020-06-25 19:02:28 +02:00
Gary Guo
2e7e804736 Fix unused_mut warning in nightly 2020-06-25 17:44:39 +01:00
Friedel Ziegelmayer
17ab958ac2
Merge pull request #820 from zhaxzhax/docs-#815 2020-06-21 21:36:09 +02:00
Friedel Ziegelmayer
caa76af745
Merge pull request #821 from async-rs/1-6-2
chore: release v1.6.2
2020-06-19 12:52:36 +02:00
dignifiedquire
e495ba46b3 chore: release v1.6.2 2020-06-19 12:15:42 +02:00
Afirez
0c2ce52ac4 fix doc missing in #815 2020-06-18 20:30:11 +08:00
Friedel Ziegelmayer
5f418f07ac
Merge pull request #819 from async-rs/fix-sockets 2020-06-18 13:58:18 +02:00
dignifiedquire
06a2fb8c4f fix export 2020-06-18 13:10:37 +02:00
dignifiedquire
1c1c168e1b fix(timer): ensure the runtime is working for timers 2020-06-18 12:37:14 +02:00
Friedel Ziegelmayer
5d55fa7a47
Merge pull request #701 from olegnn/flat_map_fixed 2020-06-18 12:18:47 +02:00
dignifiedquire
093d640ad7 fix(net): ensure the reactor and runtime are running
If this is not done, then reactor is not running, resulting in the sockets not actually connecting.

Closes #818
2020-06-18 12:11:37 +02:00
Oleg Nosov
42425f6c1a
Another hotfix 2020-06-14 18:42:18 +03:00
Yoshua Wuyts
a602a91d83
Merge pull request #816 from zhaxzhax/add-udpscket-peeraddr
Add UdpSocket::PeerAddr #307
2020-06-14 15:26:48 +02:00
Afirez
9fa3ce3fd6 Add UdpSocket::PeerAddr #307 2020-06-14 18:45:27 +08:00
Oleg Nosov
df22d87d09
Removed unnecessary links + hotfix 2020-06-12 18:21:16 +03:00
Oleg Nosov
924e5a3f41
Merge remote-tracking branch 'origin/master' into flat_map_fixed 2020-06-12 18:13:01 +03:00
Oleg Nosov
2323ac9a8e
Apply suggestions from code review
Co-authored-by: nasa <htilcs1115@gmail.com>
2020-06-12 18:03:07 +03:00
Friedel Ziegelmayer
5c2a3de9e7
Merge pull request #814 from async-rs/1-6-1
chore: release v1.6.1
2020-06-11 19:44:40 +02:00
dignifiedquire
e9c6ea873c chore: release v1.6.1 2020-06-11 13:17:31 +02:00
Friedel Ziegelmayer
0d98aac8f7
Merge pull request #812 from thibault-martinez/gh-actions-cache-v2 2020-06-09 12:55:06 +02:00
Thibault Martinez
4555f193a5 ci: update actions/cache to v2 2020-06-07 18:15:43 +02:00
Yoshua Wuyts
61fc2bae72
Merge pull request #809 from async-rs/fix/recursive-block-2
fix(rt): use task::block_on on spawned threads
2020-06-07 10:37:28 +02:00
dignifiedquire
5a1a681d68 fix(rt): use task::block_on on spawned threads
This makes sure to capture threads into the recursive block_on detection.
2020-06-04 18:25:07 +02:00
Friedel Ziegelmayer
e12cf80ab0
fix: allow for recursive block-on calls
Fixes #798,#795,#760
2020-06-04 13:19:03 +02:00
Friedel Ziegelmayer
631105b650
Merge pull request #806 from async-rs/fix-feature-unstable 2020-06-04 12:23:32 +02:00
Friedel Ziegelmayer
0897b9184a
Merge pull request #804 from async-rs/tokio02-feature 2020-06-04 12:23:12 +02:00
Friedel Ziegelmayer
6ca7b0977c
Merge pull request #807 from xfix/remove-stdio-lock-methods
Remove stdio lock methods
2020-06-04 12:22:23 +02:00
Konrad Borowski
721760a7a6 Remove stdio lock methods
Fixes #805.
2020-06-04 09:05:14 +02:00
dignifiedquire
8389041414 fix 2020-06-03 18:50:12 +02:00
dignifiedquire
8943ba82dd fix nostd 2020-06-03 18:43:19 +02:00
dignifiedquire
52c72426c1 fix: do not require the runtime to use unstable features 2020-06-03 18:38:20 +02:00
Yoshua Wuyts
0df3c02b81 check tokio02 features 2020-06-03 12:40:02 +02:00
Yoshua Wuyts
166c469d1c Add the tokio02 feature flag 2020-06-03 12:09:33 +02:00
Friedel Ziegelmayer
0ec027dbff
Merge pull request #802 from jerry73204/fix-reading-buf-bug 2020-05-29 13:32:48 +02:00
jerry73204
d60e7cc27d Fix wrong slice index when reading a file 2020-05-29 19:18:06 +08:00
Friedel Ziegelmayer
6d2a43e336
Merge pull request #794 from async-rs/1-6-0 2020-05-22 23:19:41 +02:00
dignifiedquire
e1c8638173 chore: release v1.6.0 2020-05-22 23:08:42 +02:00
dignifiedquire
06eea4225b feat: add PartialEq and Eq for channel Errors
Closes #792
2020-05-22 22:08:23 +02:00
Friedel Ziegelmayer
252140839b
Merge pull request #791 from Licenser/patch-1 2020-05-22 16:46:32 +02:00
Heinz N. Gies
69806403c6
Fix readme for BufRead
The `BufRead` readme points to `BufReadExt` being in `async_std::prelude` while it currently lives in `async_std::io::prelude`
2020-05-20 14:24:06 +02:00
Friedel Ziegelmayer
955befd746
Merge pull request #790 from async-rs/1-6-0-beta-2 2020-05-20 11:18:38 +02:00
nasa
70dac51938
Merge pull request #729 from k-nasa/fix_doc_test
Fix doc test
2020-05-20 15:33:29 +09:00
k-nasa
d30603affe Merge branch 'master' into fix_doc_test 2020-05-20 13:50:39 +09:00
dignifiedquire
c9ecb5bbbd prepare v1.6.0-beta.2 2020-05-19 11:29:36 +02:00
Jacob Rothstein
9e6a76af04
feat: add env vars to configure the runtime threadpool size and name 2020-05-19 11:16:01 +02:00
Friedel Ziegelmayer
2b6c7fedff
Merge pull request #772 from jbr/unixstream-clone 2020-05-13 18:03:47 +02:00
Friedel Ziegelmayer
b3277954c7
Merge pull request #776 from azriel91/bugfix/775/wasm-timer-delay 2020-05-13 18:03:04 +02:00
Azriel Hoh
baead51a28 Reduces duration in timeout test.
Tries to get CI to pass.
2020-05-13 10:38:40 +12:00
Azriel Hoh
e9621af345 Updates CHANGELOG.md. 2020-05-13 10:37:19 +12:00
Azriel Hoh
d3e59370e7 Switches wasm-timer for futures-timer. 2020-05-13 10:14:05 +12:00
Jacob Rothstein
cd5e17fe87
make UnixStream Clone 2020-05-10 18:21:45 -07:00
Friedel Ziegelmayer
e20b0f0d75
Merge pull request #768 from async-rs/fix/file-block
fix(fs): use smol::block_on for drop handling of File
2020-05-09 16:28:10 +02:00
dignifiedquire
19170aead4 use local file 2020-05-09 11:44:16 +02:00
dignifiedquire
2762ec5800 fix(fs): use smol::block_on for drop handling of File
Ref #766
2020-05-09 11:36:13 +02:00
dignifiedquire
247c94ca06 docs(changelog): add missing link 2020-05-07 23:34:49 +02:00
Friedel Ziegelmayer
e404dcdd03
Merge pull request #765 from async-rs/feat/1-6-0 2020-05-07 23:32:47 +02:00
dignifiedquire
bd6a7e200b prepare v1.6.0-beta.1 2020-05-07 23:20:56 +02:00
Friedel Ziegelmayer
e4c4c93d29
Test and fix 32 bit targets 2020-05-07 23:20:44 +02:00
Thayne McCombs
6f6fced103
feat: implement Barrier using Condvar 2020-05-07 22:26:46 +02:00
Friedel Ziegelmayer
10f7abb3b6
Merge pull request #757 from dignifiedquire/feat/smol 2020-05-07 21:21:11 +02:00
dignifiedquire
27c605b4c9 cr: bring back trace call 2020-05-07 20:56:52 +02:00
dignifiedquire
faea222b9c fix: use run instead of block_on 2020-05-02 20:27:50 +02:00
dignifiedquire
1214bc2dee increase timeouts 2020-05-02 20:27:50 +02:00
dignifiedquire
26f62aafd9 make wasm deps part of std 2020-05-02 20:27:50 +02:00
dignifiedquire
e0928463b1 fix windows traits 2020-05-02 20:27:50 +02:00
dignifiedquire
92532612b7 mark spawn_local unstable 2020-05-02 20:27:50 +02:00
dignifiedquire
1a6d4f6a2f fix windows trait declarations for rawsocket 2020-05-02 20:27:50 +02:00
dignifiedquire
7a9afbd81c update smol 2020-05-02 20:27:50 +02:00
dignifiedquire
280b1a4344 remove invalid doc comment 2020-05-02 20:27:50 +02:00
dignifiedquire
48dd683535 fix feature settings 2020-05-02 20:27:50 +02:00
dignifiedquire
804a52b7fd use published smol 2020-05-02 20:27:50 +02:00
dignifiedquire
e4df1405c1 feat: add basic wasm support 2020-05-02 20:27:50 +02:00
dignifiedquire
2cd2ba3530 remove unused dependencies 2020-05-02 20:27:50 +02:00
dignifiedquire
3161a4e449 add some missing windows imports 2020-05-02 20:27:50 +02:00
dignifiedquire
228cc59b3b feat: add spawn_local 2020-05-02 20:27:50 +02:00
dignifiedquire
0a7a52aed5 update to work on smol/master 2020-05-02 20:27:50 +02:00
dignifiedquire
10c8b9a6d8 silence must use 2020-05-02 20:27:50 +02:00
dignifiedquire
fd6ae40817 add timeout stress test 2020-05-02 20:27:50 +02:00
dignifiedquire
ab9d6554aa switch to smol::Timer 2020-05-02 20:27:50 +02:00
dignifiedquire
f5fa0d7e4e avoid boxing futures 2020-05-02 20:27:50 +02:00
dignifiedquire
b96afc41dc implement task locals 2020-05-02 20:27:50 +02:00
dignifiedquire
75ab7219df bring back random 2020-05-02 20:27:50 +02:00
dignifiedquire
e082634b5e fix spawning 2020-05-02 20:27:50 +02:00
dignifiedquire
fc9ee0dfdd keep std::sync::Arc 2020-05-02 20:27:50 +02:00
dignifiedquire
1308fbdf55 switch to smol instead of an internal runtime 2020-05-02 20:27:50 +02:00
dignifiedquire
690ab16587 add dependency 2020-05-02 20:27:50 +02:00
Florian Gilcher
370642ef3e
Merge pull request #734 from sunli829/master
Add async-graphql to the ecosystems inside the readme
2020-04-28 13:17:52 +02:00
Sunli
100c3423c1
Apply suggestions from code review
Thank you.😁

Co-Authored-By: Friedel Ziegelmayer <me@dignifiedquire.com>
2020-04-27 13:49:53 +08:00
nasa
7999e6bf4b
ci: speed up github actions 2020-04-26 18:23:09 +02:00
Fangdun Cai
e707ea96e0
docs(readme): add ci status badge 2020-04-26 18:18:21 +02:00
Friedel Ziegelmayer
b446cd0230
Merge pull request #748 from async-rs/fix/scheduler-2
fix(rt): bring back dynamic machines
2020-04-12 15:22:51 +02:00
Thayne McCombs
db438abb8f
Implement async_std::sync::Condvar (#369)
* Implement async_std::sync::Condvar

Part of #217

* More rigourous detection of notification for condvar

* Use state of Waker instead of AtomicUsize to keep track of if task was
notified.

* Add test for notify_all

* Implement wait_timeout_until

And add warnings about spurious wakeups to wait and wait_timeout

* Use WakerSet for Condvar

This should also address concerns about spurious wakeups.

* Add test for wait_timeout with no lock held

* Add comments describing AwaitNotify struct

And remove an unnneded comment in a Debug implementation
2020-04-12 13:35:18 +02:00
dignifiedquire
a4e07e345c fix(rt): bring back dynamic machines
Even if we do not make use of the progress blocking, we do need to make use of the dynamic restarting of machines as far as I understand.

Keeps the perf, while removing the regression from #747
2020-04-10 02:22:03 +02:00
Yoshua Wuyts
aebba2bd95
Merge pull request #747 from async-rs/fix/scheduler-perf
Fix new scheduler loop
2020-04-09 17:26:46 +02:00
dignifiedquire
0c9a66c1f6 fix scheduler loop
This now matches more closely the logic as implemented in #631, and fixes the performance regression as far as I have observed.

Closes #746
2020-04-09 17:02:27 +02:00
Friedel Ziegelmayer
fc4e472599
Merge pull request #733 from k-nasa/new-scheduler
New scheduler
2020-04-07 22:50:04 +02:00
nasa
6674dc0edf
Merge pull request #739 from devashishdxt/futures-timer-update
Update futures-timer to 3.0.2
2020-04-03 13:41:12 +09:00
k-nasa
088aa5662c refactor: Remove wrapping cell 2020-04-03 13:38:07 +09:00
Devashish Dixit
68fa054517 Update futures-timer to 3.0.2 2020-03-30 19:30:00 +08:00
k-nasa
b88138b5d7 kick ci 2020-03-27 17:03:16 +09:00
k-nasa
11ee2a8985 fix 2020-03-22 19:25:40 +09:00
k-nasa
322911142c lock processor and remove unsafe Send, Sync 2020-03-22 19:20:01 +09:00
k-nasa
cfaec2aa95 re add spin_lock 2020-03-22 19:19:17 +09:00
sunli
57c648cf01 Add async-graphql to the ecosystems inside the readme 2020-03-21 15:49:15 +08:00
k-nasa
6d3ca5a06f remove poll function 2020-03-21 14:19:38 +09:00
k-nasa
f960776846 fix 2020-03-21 13:40:59 +09:00
k-nasa
5c6741724f Merge branch 'master' into new-scheduler 2020-03-20 23:17:12 +09:00
k-nasa
24c5dbf949 Remove scheduler state 2020-03-20 23:13:20 +09:00
nasa
2dbebe54ed
Merge pull request #721 from k-nasa/update_dep_crate
update dependence crates
2020-03-20 12:33:56 +09:00
k-nasa
d7ee29a03f fix test code 2020-03-19 19:16:12 +09:00
k-nasa
2b44c1be2e refactor: swap to swap_and_compare 2020-03-19 18:41:00 +09:00
k-nasa
b1ec1ea930 Move Spinlock to sync module 2020-03-19 18:39:01 +09:00
k-nasa
2ab075d027 refactor 2020-03-19 11:50:19 +09:00
k-nasa
c0f18600cf run ignored test 2020-03-18 23:07:07 +09:00
k-nasa
6c8237276b fix doc test 2020-03-18 23:02:59 +09:00
k-nasa
98cbf7f8eb Restore task::spawn_blocking 2020-03-17 20:54:16 +09:00
k-nasa
84e5c5f351 Merge branch 'master' into new-scheduler 2020-03-17 20:38:19 +09:00
Yoshua Wuyts
3ff9e98f20
Merge pull request #585 from async-rs/try_channels
expose try_recv and try_send on channels
2020-03-16 02:38:00 +01:00
Yoshua Wuyts
b7c7efc797 Update try_channel doctests 2020-03-16 00:05:39 +01:00
Yoshua Wuyts
19fd7a4084 fix channel tests
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2020-03-15 23:53:03 +01:00
Yoshua Wuyts
7885c245c5 recverror
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2020-03-15 23:53:03 +01:00
Yoshua Wuyts
7b7b959a6e mark channel errs as unstable
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2020-03-15 23:53:03 +01:00
Yoshua Wuyts
32dce319d3 expose try_recv and try_send on channels
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2020-03-15 23:53:03 +01:00
Yoshua Wuyts
49dd02b4de Make the split struct public 2020-03-15 23:51:19 +01:00
Yoshua Wuyts
bb11c676a1 doctests pass 2020-03-15 23:46:36 +01:00
Yoshua Wuyts
e026b7579a
Merge pull request #703 from spacekookie/recv-docs
channel/recv: improving function docs and code example
2020-03-15 14:41:07 +01:00
Yoshua Wuyts
51dd7ceb72
Merge pull request #727 from async-rs/revert-719-remove_re_export
Revert "Stabilize most stream method and remove unnecessary macros"
2020-03-15 13:27:39 +01:00
k-nasa
8931d1464e fix ci 2020-03-14 22:46:22 +09:00
nasa
cc19592f80
Revert "Stabilize most stream method and remove unnecessary macros" 2020-03-12 18:34:09 +09:00
nasa
f69887a50d
Update Cargo.toml 2020-03-09 09:09:17 +09:00
k-nasa
0b0531057d feat: update dependence crates 2020-03-08 20:46:26 +09:00
Yoshua Wuyts
61f9483cc5
Merge pull request #719 from k-nasa/remove_re_export
Stabilize most stream method and remove unnecessary macros
2020-03-06 14:31:06 +01:00
k-nasa
f33d7f40ab fix test 2020-03-06 09:53:28 +09:00
k-nasa
e3bf89fc05 $cargo fmt 2020-03-05 18:49:58 +09:00
k-nasa
ec4b09ecd0 fix test code 2020-03-05 18:47:53 +09:00
k-nasa
b95bd6c1fe fix: Remove unnecessary io modules 2020-03-05 10:34:41 +09:00
k-nasa
1e18839f1f fix warning 2020-03-04 08:55:37 +09:00
k-nasa
f31878655e fix: Stabilize stream method 2020-03-04 08:30:45 +09:00
k-nasa
9a62df143f add whitespace 2020-03-03 23:14:25 +09:00
k-nasa
75223905bd fix: Stabilize stream most method 2020-03-03 23:12:09 +09:00
k-nasa
be60dd9fe7 fix: Remove unnecessary re-export and macros 2020-03-03 22:50:26 +09:00
k-nasa
23b7c174f3 feat: Stabilize io::Std*Lock 2020-03-03 22:46:18 +09:00
Yoshua Wuyts
9167d42f4b
Merge pull request #708 from sunli829/master
Add Xactor to the ecosystems inside the readme
2020-03-02 13:20:14 +01:00
Yoshua Wuyts
4034d58709
Merge pull request #714 from abhijeetbhagat/patch-1
Add missing ? operator after handle.await
2020-03-02 13:11:15 +01:00
abhi
4742f461fe
Add missing ? operator after handle.await
According to line#118, there should be a `?` operator after `await`.
2020-02-22 15:17:06 +05:30
nasa
efab39eeaf
Merge pull request #710 from k-nasa/fix_ci_failed
Fixed ci failing
2020-02-20 09:33:44 +09:00
k-nasa
bd60cd9f81 run cargo fmt 2020-02-20 09:03:36 +09:00
sunli
b9e4b6da3e Add Xactor to the ecosystems inside the readme 2020-02-19 14:36:07 +08:00
Yoshua Wuyts
eb03f37e43
Merge pull request #697 from async-rs/core-docs
Document the core feature
2020-02-17 13:38:39 +01:00
Yoshua Wuyts
d87e283215
Update src/lib.rs 2020-02-17 13:38:24 +01:00
Yoshua Wuyts
283a54a155
Update src/lib.rs 2020-02-17 13:36:59 +01:00
Yoshua Wuyts
3719484eba
Update src/lib.rs
Co-Authored-By: nasa <htilcs1115@gmail.com>
2020-02-17 13:36:23 +01:00
Katharina Fey
aae835cc14
channel/recv: improving function docs and code example
At the moment it's not clear when and why recv returns Option<T>,
instead of just T. This changed comment makes it clear that None will
only be returned once no data will ever be sent again (i.e. after all
senders are gone).
2020-02-12 01:38:20 +01:00
Oleg Nosov
68063adddf
Add link to tests 2020-02-08 16:22:11 +03:00
Oleg Nosov
d7cab38b67
core => std 2020-02-08 15:49:01 +03:00
Oleg Nosov
32068942a6
Fixed flatten 2020-02-08 15:44:25 +03:00
Oleg Nosov
85c32ef9d2
Use assert without if-clause 2020-02-07 22:45:15 +03:00
Oleg Nosov
b68be72763
Use assert instead of panic 2020-02-07 22:42:59 +03:00
Oleg Nosov
c80915e216
Dont spawn thread in tests 2020-02-07 22:22:38 +03:00
Oleg Nosov
303ac90b7c
Fixed flat_map 2020-02-07 22:12:42 +03:00
Yoshua Wuyts
d026c44ea3 Document the core feature
Follow-up to https://github.com/async-rs/async-std/pull/680
2020-02-04 11:07:50 +01:00
Yoshua Wuyts
125fa5b0a0
Merge pull request #680 from k-nasa/no_std
Some modules support no_std
2020-02-04 11:02:19 +01:00
Yoshua Wuyts
39f2c6da78
V1.5.0 (#694)
* Update CHANGELOG.md

* v1.5.0

* Update CHANGELOG.md
2020-02-03 16:45:00 +01:00
k-nasa
3e24e0ba4e ci: fix no-std check 2020-02-01 16:45:59 +09:00
k-nasa
0d90cb07b9 fix: Move extern crate alloc to lib.rs 2020-02-01 09:49:54 +09:00
k-nasa
f789f9d4f6 Select future-core featue according to feature 2020-02-01 09:47:33 +09:00
k-nasa
ef985bc72e ci: fix no_std ci 2020-02-01 09:45:41 +09:00
Stjepan Glavina
1d875836a2
Implement Clone for TcpStream (#689)
* Implement Clone for TcpStream

* Update examples

* Remove accidentally added examples
2020-01-28 18:14:16 +01:00
k-nasa
7efe7caf66 fix: Change feature name no-std to alloc 2020-01-28 15:58:46 +09:00
k-nasa
22d929d481 fix import Future 2020-01-28 15:58:46 +09:00
k-nasa
d622ec5d35 feat: Make the stream module no_std 2020-01-28 15:58:46 +09:00
k-nasa
880b7ee987 remove crate::prelude import 2020-01-28 15:58:46 +09:00
k-nasa
1762de285b feat: Make the future module no_std 2020-01-28 15:58:46 +09:00
k-nasa
6aa55fde59 feat: Make the task module no_std 2020-01-28 15:58:46 +09:00
k-nasa
41f114d9fe ci: Add no-std check 2020-01-28 15:58:46 +09:00
k-nasa
3d32fd81f4 feat: Make the utils module no_std 2020-01-28 15:58:46 +09:00
k-nasa
51b84a7620 feat: Add no_std attribute when not std feature 2020-01-28 15:58:46 +09:00
k-nasa
4996f29778 feat: Add no-std feature 2020-01-28 15:58:46 +09:00
Toralf Wittner
57974ae0b7 Use non-blocking connect for TcpStream. (#687)
* Use non-blocking connect for TcpStream.

Instead of spawning a background thread which is unaware of any timeouts
but continues to run until the TCP stack decides that the remote is not
reachable we use mio's non-blocking connect.

mio's `TcpStream::connect` returns immediately but the actual connection
is usually just in progress and we have to be sure the socket is
writeable before we can consider the connection as established.

* Add Watcher::{poll_read_ready, poll_write_ready}.

Following a suggestion of @stjepang we offer methods to check for
read/write readiness of a `Watcher` instead of the previous approach to
accept a set of `Waker`s when registering an event source. The changes
relative to master are smaller and both methods look more useful in
other contexts. Also the code is more robust w.r.t. wakeups of the
`Waker` from clones outside the `Reactor`.

I am not sure if we need to add protection mechanisms against spurious
wakeups from mio. Currently we treat the `Poll::Ready(())` of
`Watcher::poll_write_ready` as proof that the non-blocking connect has
finished, but if the event from mio was a spurious one, it might still
be ongoing.
2020-01-27 23:13:13 +01:00
Yoshua Wuyts
57f9fb7e93
Merge pull request #682 from k-nasa/impl_clone_for_dir_entry
Implement Clone trait for DirEntry
2020-01-27 13:10:20 +01:00
Yoshua Wuyts
6c1b5eb3ed
Merge pull request #667 from olegnn/option_take_while
Use `take_while` instead of `scan` in `impl` of `Product`, `Sum` and `FromStream` for `Option` and `Result`
2020-01-27 11:54:20 +01:00
Yoshua Wuyts
beb8d240c2
Merge pull request #688 from ninj/patch-1
fix syntax problem for task::sleep
2020-01-25 23:40:43 +01:00
ninj
b258215952
fix syntax problem for task::sleep 2020-01-25 22:13:26 +00:00
Florian Gilcher
1ababac97f
Merge branch 'accept_loop_pattern' 2020-01-22 12:47:41 +01:00
Florian Gilcher
f9fe5c90cf
Fix some typos in accept-loop pattern chapter 2020-01-22 12:47:18 +01:00
Florian Gilcher
84fe94444b
Merge pull request #675 from k-nasa/add_timeout_example
Add stream::timeout example when timeout error
2020-01-21 16:02:28 +01:00
Florian Gilcher
cad2880eb8
Merge pull request #550 from sclaire-1/master
Edit tutorial: implementing_a_client.md
2020-01-21 15:44:44 +01:00
Taiki Endo
6b860c370a Remove usage of unstable format_code_in_doc_comments option (#685) 2020-01-20 21:41:48 +01:00
Katharina Fey
81aa6d152a Changing task::block_on to park after a single poll (#684)
This was previously discussed in #605 and others as a source of high
CPU load when sleeping tasks because of the overhead created by
retrying a future in short succession.
2020-01-20 20:40:01 +01:00
k-nasa
2221441a4c feat: Implement Clone trait for DirEntry 2020-01-18 08:36:54 +09:00
Oleg Nosov
ed7ddacb28
Rewrote Results implementation using take_while and filter_map 2020-01-17 17:19:51 +03:00
nasa
d283352a9a update broadcastor to 1.0.0 (#681) 2020-01-16 13:07:11 -05:00
Oleg Nosov
ed248017b4
Use internal scan state in Results implementation 2020-01-15 12:06:50 +03:00
Yoshua Wuyts
0eb5ca14ac
Merge pull request #676 from k-nasa/async_task_bump_to1.2.1
update async-task to 1.2.1
2020-01-15 08:09:19 +01:00
Oleg Nosov
38de0bfd22
Use std::convert::identity 2020-01-15 09:43:01 +03:00
Oleg Nosov
134089af2c
Use filter_map(identity) + other fixes 2020-01-15 08:57:43 +03:00
k-nasa
b72dd83726 update async-task to 1.2.1 2020-01-15 11:00:03 +09:00
k-nasa
ee102dfc9e docs: Add stream::timeout example when timeout error 2020-01-15 10:41:39 +09:00
nasa
1071e82132
Merge pull request #671 from Noah-Kennedy/udp-socket-send-doc
Fix docs for UdpSocket::send
2020-01-15 10:41:04 +09:00
noah
0a52864764 Revert "Fixes https://github.com/async-rs/async-std/issues/652"
This reverts commit a4f68066
2020-01-14 10:59:17 -06:00
noah
76993dd755 Revert "Fixes https://github.com/async-rs/async-std/issues/652"
This reverts commit a4f68066
2020-01-14 10:55:10 -06:00
Yoshua Wuyts
133e30e6f6
Merge pull request #615 from lqf96/pending-stream
Add an implementation of pending stream
2020-01-14 15:58:27 +01:00
nasa
76ed174fd5 Version up of dependent crate (#672) 2020-01-14 15:26:22 +01:00
k-nasa
f53fcbb706 test,docs: Add stream::pending example code 2020-01-14 10:18:14 +09:00
k-nasa
e9357c0307 style: Run cargo fmt 2020-01-14 09:49:34 +09:00
Qifan Lu
879e14c6ab Remove size_hint from Stream impl 2020-01-14 09:41:43 +09:00
Qifan Lu
f8dd3d9816 Add stream::pending::{pending, Pending} 2020-01-14 09:41:43 +09:00
noah
a4f6806605 Fixes https://github.com/async-rs/async-std/issues/652 2020-01-13 17:47:51 -06:00
Yoshua Wuyts
5d5064b871 add FromStream Result example (#643)
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2020-01-13 21:42:31 +01:00
nasa
0ed0d63094 Remove unnecessary trait bound on FlatMap (#651)
* Remove unnecessary trait bound on FlatMap

* test: upgrade test code
2020-01-13 19:49:52 +01:00
Paul Colomiets
0029037883 async-listen crate: Add error_hint() invocation 2020-01-13 08:46:32 +02:00
Oleg Nosov
fb567a3a09
Recovered comments 2020-01-12 17:53:16 +03:00
Oleg Nosov
83afbab2ef
Use take_while instead of scan for Option 2020-01-12 17:35:57 +03:00
Paul Colomiets
c8c075615c book: Add Production-ready Accept Loop section
Part of the #658 work
2020-01-12 03:47:37 +02:00
Yoshua Wuyts
98d45f4be1
Merge pull request #647 from dignifiedquire/feat/unstable-without-default
feat: do not require default feature for unstable
2020-01-11 12:24:00 +01:00
dignifiedquire
9c6ab5e7c3 fix 2020-01-11 11:57:42 +01:00
dignifiedquire
9c9ab90da3 feature gate random 2020-01-11 11:49:52 +01:00
dignifiedquire
5bf3d95313 feat: do not require default feature for unstable 2020-01-11 11:44:51 +01:00
Yoshua Wuyts
1f78efec64
Merge pull request #660 from mehcode/mehcode-ecosystem
Showcase the ecosystem
2020-01-10 13:20:30 +01:00
Yoshua Wuyts
383057b8ea
Merge pull request #659 from alfiedotwtf/master
Tiny grammar fix
2020-01-07 17:09:49 +01:00
Yoshua Wuyts
763862acc7
Merge pull request #661 from async-rs/fix-ci
remove usage of deprecated Error method to fix CI
2020-01-07 17:09:03 +01:00
Yoshua Wuyts
e2bb79c207
Merge pull request #648 from spacekookie/master
Fixing inaccurate function description in udp::recv
2020-01-07 14:23:14 +01:00
Yoshua Wuyts
57a62797f2
Merge pull request #655 from u32i64/patch-1
Fix crate documentation typo
2020-01-07 14:22:52 +01:00
Yoshua Wuyts
dfb0c8124c
remove usage of deprecated method
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2020-01-07 14:21:17 +01:00
Ryan Leckey
d806a09599 Add a section on the async ecosystem to showcase crates that use async-std 2020-01-06 23:17:13 -08:00
Alfie John
af2d46d9b9 Tiny grammar fix 2020-01-07 13:29:30 +11:00
Artem Varaksa
65d7950df1
Fix crate documentation typo 2020-01-01 15:36:47 +03:00
nasa
6d69a3e368
Merge pull request #650 from senden9/patch-1
Fix typo in stream documentation
2019-12-29 14:41:56 +09:00
Stefano Probst
c3d5dba1b5
Fix typo in stream documentation 2019-12-28 17:27:37 +01:00
Katharina Fey
081166f204
Fixing inaccurate function description in udp::recv 2019-12-27 03:06:41 +01:00
Yoshua Wuyts
fee3b6f603
Merge pull request #645 from async-rs/remove-tokio-mention
update stream::Interval internal comments left over from migration
2019-12-24 15:05:39 +01:00
Yoshua Wuyts
b3942ecfa8
remove tokio mention
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-12-24 14:39:55 +01:00
Yoshua Wuyts
86d3d74180
Merge pull request #637 from async-rs/v1.4.0
1.4.0
2019-12-20 16:30:45 +01:00
Yoshua Wuyts
3fd6d8b02e
1.4.0
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-12-20 11:58:32 +01:00
nasa
0d4b4cd260
Merge pull request #600 from miker1423/future-timeout
Adding timeout extension method to Future trait
2019-12-20 14:26:52 +09:00
Miguel Pérez García
ef021dcb2b Changing test condition 2019-12-18 07:18:57 -06:00
Miguel Pérez García
eedf1d3367 Fixing docs 2019-12-17 23:17:02 -06:00
Miguel Pérez García
97b4901b75 Fixing tests 2019-12-17 23:12:09 -06:00
Miguel Pérez García
1eeb1019e9 Fixing example 2019-12-17 23:05:06 -06:00
Miguel Pérez García
980a1f7834 Correcting docs on function 2019-12-17 22:46:25 -06:00
nasa
d2c25f483a
Merge pull request #635 from async-rs/revert-629-update-log
Revert "upgrade log, remove kv-log-macro"
2019-12-18 08:30:56 +09:00
nasa
d8befe24e8
Revert "upgrade log, remove kv-log-macro" 2019-12-18 08:01:09 +09:00
Florian Gilcher
c7cf1934db
Merge pull request #629 from async-rs/update-log
upgrade log, remove kv-log-macro
2019-12-17 22:05:51 +01:00
Yoshua Wuyts
37d8a013de
Merge pull request #633 from k-nasa/fix_io_export
fix missing export for the return value
2019-12-17 16:52:59 +01:00
k-nasa
43f4f393af fix missing export for the return value 2019-12-17 22:48:14 +09:00
Stjepan Glavina
ceba324bef Fix feature flags 2019-12-16 15:53:31 +01:00
Stjepan Glavina
36d24cd0e1 New scheduler resilient to blocking 2019-12-16 13:57:27 +01:00
Yoshua Wuyts
61eb52cb36
Merge pull request #625 from nbdd0121/sync_unsized
Use ?Sized in Mutex and RwLock
2019-12-16 12:01:45 +01:00
Yoshua Wuyts
6f4dcad6a0
Merge pull request #630 from async-rs/fix-ci-2
fix ci
2019-12-16 11:41:07 +01:00
Yoshua Wuyts
60de8e1082
up time limits
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-12-16 11:29:21 +01:00
Yoshua Wuyts
cac4e081cc
Merge pull request #628 from fenhl/patch-1
Make WriteFmtFuture must_use
2019-12-16 11:22:30 +01:00
Yoshua Wuyts
8ad1d23116
fix ci
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-12-16 10:43:19 +01:00
Yoshua Wuyts
019aa14898
Merge pull request #621 from async-rs/fix-doc-hiccup
fix stream doc hiccup
2019-12-16 10:38:36 +01:00
Yoshua Wuyts
b7e55762d8
upgrade log, remove kv-log-macro
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-12-16 10:04:39 +01:00
Yoshua Wuyts
c70552ead5
unpub double_ended_stream
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-12-16 09:37:14 +01:00
Fenhl
07eb2c1280
Make WriteFmtFuture must_use
Fixes #627. Thanks to @jebrosen for pointing out the location of the issue.
2019-12-14 17:43:22 +00:00
Gary Guo
732ef10f98 Make code compile 2019-12-14 23:42:14 +08:00
Gary Guo
499a44ab3b Use ?Sized in Mutex and RwLock 2019-12-14 23:34:55 +08:00
Yoshua Wuyts
761029cd08
fix stream doc hiccup
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-12-13 15:28:43 +01:00
Yoshua Wuyts
83a488b290
Merge pull request #597 from k-nasa/fix_doc_test
Fix failed doc test and enable doc test on CI
2019-12-13 15:03:22 +01:00
Yoshua Wuyts
2f0907714d
Merge pull request #617 from async-rs/1.3.0
1.3.0
2019-12-13 09:50:32 +01:00
Yoshua Wuyts
055c64e8a7
1.3.0
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-12-13 09:44:06 +01:00
Yoshua Wuyts
96d6fc43d6
Merge pull request #618 from twittner/poll_close_shutdown
TcpStream: Shutdown write direction in poll_close.
2019-12-13 09:32:48 +01:00
Yoshua Wuyts
3d3bf914ea
Merge pull request #562 from felipesere/double_ended_ext
DoubleEndedStream extension
2019-12-13 09:17:16 +01:00
Miguel Pérez García
84b6d2b276 Removing duplicated tests 2019-12-12 18:34:02 -06:00
Miguel Pérez García
8de9f9b8e1 Merge branch 'future-timeout' of https://github.com/miker1423/async-std into future-timeout 2019-12-12 18:31:45 -06:00
Felipe Sere
182fe6896f No need for a custom impl for FromIter for DoubleEndedStream 2019-12-12 20:52:38 +01:00
Felipe Sere
b0038e11be Only implement the DoubleEndedStream for once when the flag is on 2019-12-12 20:26:38 +01:00
Felipe Sere
8e5dedec34 Restructure package. No longer use a extension trait to match std.
Still outstanding: How do I hide the concrete structs from the trait?
2019-12-12 20:26:38 +01:00
Felipe Sere
41cf0f855b Make Once a DoubleEndedStream 2019-12-12 20:26:38 +01:00
Felipe Sere
f9a4c35fd6 Silence warning about missing docs for the double_ended module 2019-12-12 20:26:38 +01:00
Felipe Sere
6e8236d0e1 Document from_iter for DoubleEndedStream 2019-12-12 20:26:38 +01:00
Felipe Sere
892c6008c2 Replace sample with a hidden from_iter implementation for double-ended-stream 2019-12-12 20:26:38 +01:00
Felipe Sere
abd360893c Disable docs and Debug for unexposed structs 2019-12-12 20:26:38 +01:00
Felipe Sere
94893d2924 Move more of the documentation 2019-12-12 20:26:38 +01:00
Felipe Sere
02aa2f3d2a Fix next_back 2019-12-12 20:26:38 +01:00
Felipe Sere
ee2f52f3ce Add next_back 2019-12-12 20:26:38 +01:00
Felipe Sere
55194edbf7 Add try_rfold 2019-12-12 20:26:38 +01:00
Felipe Sere
c4b9a7f680 Add samples for some of the functions 2019-12-12 20:26:38 +01:00
Felipe Sere
aabfefd015 Add a sample implementation of a double ended stream 2019-12-12 20:26:38 +01:00
Felipe Sere
cc493df433 Sketch out rfold 2019-12-12 20:26:38 +01:00
Felipe Sere
78bafbb88f Sketch outch rfind 2019-12-12 20:26:38 +01:00
Felipe Sere
d0ef48c753 Sketch out nth_back 2019-12-12 20:26:38 +01:00
Felipe Sere
fa288931c6 Skeleton for DoubleEndedStreamExt trait 2019-12-12 20:26:38 +01:00
Toralf Wittner
c90732a805 TcpStream: Shutdown write direction in poll_close.
Fixes #599.
2019-12-12 17:37:38 +01:00
Yoshua Wuyts
63b6a2b961
Merge pull request #614 from killzoner/doc-typo-await
fix: Fix typo in documentation
2019-12-12 12:04:23 +01:00
Yoshua Wuyts
1103c17e16
Merge pull request #613 from k-nasa/fix_readme
Readme example simply compile
2019-12-12 12:03:45 +01:00
Yoshua Wuyts
a0f3b3b753 Remove unused macros (#610)
* replace async-macros with internals only

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* clean up MaybeDone

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* inline futures_core::ready

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* remove big commented blob

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-12-11 12:49:22 +01:00
Tomasz Miąsko
f06ab9fbc4 Remove mention of task stack size configuration (#612) 2019-12-11 12:36:50 +01:00
svengrim
447c17128f
fix: Fix typo in documentation 2019-12-10 14:37:32 +01:00
k-nasa
a04157850b fix readme 2019-12-08 15:15:29 +09:00
nasa
9311fd7fae
Merge pull request #595 from linkmauve/patch-1
docs: Replace mention of futures-preview crate
2019-12-07 23:22:03 +09:00
nasa
f7b21a3e8d
Merge pull request #608 from dungph/master
fix link to `task/block_on` on README.md
2019-12-07 10:45:44 +09:00
Dung Pham
f0bdcfec25
fix link 2019-12-06 13:06:44 +07:00
Miguel Pérez García
33e7c87dfc Adding example to docs 2019-12-05 21:19:02 -06:00
Miguel Pérez García
cc85533f7c fixing format 2019-12-05 21:15:32 -06:00
Miguel Pérez García
4670388a56 Adding tests 2019-12-05 08:10:06 -06:00
Miguel Pérez García
c14c377974 Changing method signature 2019-12-05 08:09:58 -06:00
Miguel Pérez García
54fa559554 Changing scope of disclosure 2019-12-05 08:09:20 -06:00
Yoshua Wuyts
bce8688763
Merge pull request #603 from bluk/udp-socket-recv-doc
Change recv_from to recv in UdpSocket::recv doc
2019-12-03 06:25:26 +09:00
Bryant Luk
fd86effb63
Change recv_from to recv in UdpSocket::recv doc 2019-12-02 13:04:19 -06:00
nasa
128a6bc6ce
Merge pull request #598 from povilasb/fix/docs
Fix a link in the docs
2019-11-30 12:48:10 +09:00
Yoshua Wuyts
d51a135015
Merge pull request #309 from async-rs/stream-delay
Stream::delay
2019-11-30 11:25:41 +09:00
Miguel Pérez García
1c2055fff0 Merge remote-tracking branch 'original/master' into future-timeout 2019-11-29 18:00:00 -06:00
Povilas Balciunas
81e3c41826 Fix a link in the docs 2019-11-29 11:52:54 +13:00
k-nasa
fb1fb6c903 test: Test the delay time 2019-11-28 22:53:24 +09:00
k-nasa
c85e2496b1 Enable doc test on ci 2019-11-28 20:54:07 +09:00
k-nasa
7d9a063002 fix cargo test arguments on ci 2019-11-28 20:53:47 +09:00
k-nasa
44e38eae59 fix open_file test code 2019-11-28 19:52:46 +09:00
k-nasa
fe04cf26b6 test: fix stream::throttle doc test 2019-11-28 16:16:39 +09:00
k-nasa
556d7992ce test: fix failed doc test 2019-11-28 15:57:22 +09:00
k-nasa
da965e9ba4 fix indent 2019-11-28 15:54:13 +09:00
k-nasa
9f7c1833dc fix module 2019-11-28 10:37:04 +09:00
linkmauve
55560ea9b4
docs: Replace mention of futures-preview crate
It is now stable in 0.3.
2019-11-27 19:35:27 +01:00
Stjepan Glavina
bf9ee88815 Fix a typo 2019-11-27 16:29:29 +01:00
Stjepan Glavina
9627826756 Bump the version to 1.2.0 2019-11-27 16:14:43 +01:00
Stjepan Glavina
4ed15d67c9 Fix links in the changelog 2019-11-27 16:12:31 +01:00
Stjepan Glavina
0165d7f6d1 Add missing items to the changelog 2019-11-27 16:08:57 +01:00
Yoshua Wuyts
dba416608a 1.2.0 (#589)
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-27 16:05:15 +01:00
Yoshua Wuyts
68005661d9 fix Stream::throttle hot loop (#584)
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-27 15:47:27 +01:00
nasa
794e331761 Refactor join type (#577)
* refactor: update future join type

* test: update future join test

* update future::try_join
2019-11-27 13:38:38 +01:00
Yoshua Wuyts
63f7ea3081
Merge pull request #587 from async-rs/contributing
link to our contribution guidelines
2019-11-27 13:21:40 +01:00
k-nasa
32765ece41 test: Add stream::delay test code 2019-11-27 14:26:25 +09:00
k-nasa
635c592950 feat: Add stream::delay 2019-11-27 14:26:04 +09:00
k-nasa
3b055f364e Merge branch 'master' into stream-delay 2019-11-27 13:50:05 +09:00
Yoshua Wuyts
46cafffc31
Merge pull request #571 from killercup/more-errors
Add context to more errors
2019-11-27 01:54:27 +01:00
boats
0f30ab8c0a Fix the docs and Debug output of BufWriter. (#588)
The BufWriter docs inaccurately stated that it flushes on drop, which it does
not do. This PR changes the docs, as well as the example, to highlight that
the user must explicitly flush a bufwriter.

There were also two places where the BufWriter code referred to it as a
BufReader: in the link to the std docs, and in the Debug output. Those have
also been fixed.
2019-11-26 14:23:10 +01:00
Yoshua Wuyts
e66e2e2b8f
link to our contribution guidelines
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-26 11:42:02 +01:00
Pascal Hertleif
56538ebd91 Improve verbose errors for socket addresses
Moves the point of adding error context to the net::addr module so that
we have access to the raw address input and can include it in the error
message.
2019-11-25 23:30:31 +01:00
Pascal Hertleif
aa7d1c27a4
Verbose errors: Apply suggestions
Co-Authored-By: Yoshua Wuyts <yoshuawuyts+github@gmail.com>
2019-11-25 21:18:40 +01:00
Yoshua Wuyts
850b8ae9d0
Merge pull request #543 from k-nasa/stream_unzip
Add stream unzip
2019-11-25 20:25:44 +01:00
Yoshua Wuyts
ac7a796f82
Merge pull request #537 from k-nasa/ci_master
Enable CI on master branch
2019-11-25 12:53:37 +01:00
Miguel Pérez García
c1f7be5d42 Adding timeout extension method to Future trait 2019-11-23 11:40:07 -06:00
Yoshua Wuyts
50cefce803
Merge pull request #561 from async-rs/1.1.0
1.1.0
2019-11-21 22:01:34 +01:00
Yoshua Wuyts
3780ff7b44
1.1.0
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

changelog

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-21 21:49:32 +01:00
Yoshua Wuyts
c9a2e74789
Merge pull request #523 from async-rs/update-lib-example
polish lib.rs examples
2019-11-21 21:24:54 +01:00
Yoshua Wuyts
cffacf7fa3
feedback from review
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-21 21:21:19 +01:00
Yoshua Wuyts
02e1d7e5ea
Merge pull request #574 from stjepang/ignore-unread-errors
Ignore seek errors in poll_unread
2019-11-21 18:32:55 +01:00
laizy
ec5415358f simplify AllFuture and AnyFuture (#572) 2019-11-21 18:03:23 +01:00
Alejandro Martinez Ruiz
ba1ee2d204 Fix a-chat tutorial issues (#573)
* tutorial/receiving_messages: fix future output type bound

* tutorial/receiving_messages: remove unneeded message trimming

Trimming was done twice on messages, so one of the two instances can
be removed. I personally think removing the first instance, in which
we are splitting names from messages makes the code more readable
than removing the second instance, but other examples further in
the tutorial show the second instance removed.

* tutorial/receiving_messages: declare use of TcpStream and io::BufReader

Readers couldn't see the `use` lines corresponding to these two
structures.

* tutorial/connecting_readers_and_writers: typos and grammar fixes

* tutorial/all_together: remove unneeded use async_std::io

* tutorial: use SinkExt consistently from futures::sink::SinkExt

* tutorial/handling_disconnection: hide mpsc use clause and remove empty lines

The empty lines translate to the output making it look weird.

* tutorial/handling_disconnection: fix typos

* tutorial/handling_disconnection: use ? in broker_handle.await

We were happy to return an Err variant from the broker_handle before
and nothing has changed in this regard, so bubbling it up to run().
2019-11-21 18:03:10 +01:00
Stjepan Glavina
16edec3464 Ignore seek errors in poll_unread 2019-11-21 17:50:30 +01:00
Pascal Hertleif
e01f07d72a Add context to more errors
cc #569
2019-11-21 00:27:47 +01:00
Yoshua Wuyts
b3d30de4a1 mark windows fs APIs as "unstable" (#567)
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-20 14:58:49 +01:00
Yoshua Wuyts
6f19165e0e
Merge pull request #568 from stjepang/fix-random
Fix rng use in Stream::merge
2019-11-20 14:08:16 +01:00
nasa
d146d95a39
Update src/stream/stream/mod.rs
Co-Authored-By: Taiki Endo <te316e89@gmail.com>
2019-11-20 21:38:42 +09:00
Stjepan Glavina
5fba3a0928 Fix rng use in Stream::merge 2019-11-20 13:24:18 +01:00
Yoshua Wuyts
2ca03cabe6
Merge pull request #552 from hhggit/win_symlink
add os::windows::symlink_{dir,file}
2019-11-20 13:17:39 +01:00
hhggit
72ed4eb4fd
Update mod.rs 2019-11-20 19:51:01 +08:00
Yoshua Wuyts
77800ab3f9
Merge pull request #526 from yjhmelody/refactor-dir
refactor io dir to be same with std and export IntoInnerError
2019-11-20 12:35:04 +01:00
Yoshua Wuyts
8ea920c9f0
Merge pull request #546 from k-nasa/fix_clippy
Fix clippy warn
2019-11-20 12:26:28 +01:00
Yoshua Wuyts
d1189f9974
Merge pull request #548 from yjhmelody/fix-stream-code-style
fix stream code style
2019-11-20 12:25:11 +01:00
Yoshua Wuyts
3f8ec5a007
Merge pull request #551 from killercup/feature/verbose-errors
verbose errors feature
2019-11-20 12:22:58 +01:00
nasa
3bc4d293dd
Merge pull request #503 from Razican/random_merge
Randomize Stream::merge to improve the throughput.
2019-11-20 08:42:50 +09:00
Yoshua Wuyts
f24b3a4520
Merge pull request #559 from k-nasa/fix_max_by_key
Fix stream max_by_key and min_by_key
2019-11-19 23:43:46 +01:00
razican
72ca2c1a24
Improved the code with some minor changes 2019-11-19 21:14:56 +01:00
k-nasa
b5e66c4f93 refactor: Refactoring option type handling 2019-11-20 00:38:51 +09:00
k-nasa
080875edc9 update min_by_key doc 2019-11-20 00:25:48 +09:00
k-nasa
ca71ad073b fix stream min_by_key mistake 2019-11-20 00:25:35 +09:00
k-nasa
667bbc1019 doc: update doc test 2019-11-20 00:18:11 +09:00
k-nasa
64b2e10b93 fix max_by_key mistake 2019-11-20 00:17:29 +09:00
k-nasa
314a75da28 fix typo 2019-11-20 00:00:54 +09:00
Pascal Hertleif
c704643296 Remove verbose-errors cargo feature 2019-11-19 13:15:48 +01:00
Yoshua Wuyts
c6622475b2
Merge pull request #555 from stjepang/optimize-cargo-check
Macro optimization to reduce compilation times
2019-11-19 05:15:58 +01:00
Stjepan Glavina
65afd41a33
Once doesn't need Unpin bound (#554) 2019-11-19 05:04:18 +01:00
Stjepan Glavina
d3e7f32a30 Macro optimization to reduce compilation times 2019-11-18 15:47:45 +00:00
Razican
f6829859fe
Fixed deduplication of code 2019-11-18 16:39:21 +01:00
hhggit
2c9b558d14 add os::windows::symlink_{dir,file} 2019-11-18 10:20:07 +08:00
Pascal Hertleif
99ddfb3f93 Wrap code more clearly in cfg blocks 2019-11-17 23:11:11 +01:00
Pascal Hertleif
8ce3e78952 verbose errors feature
This adds a new "verbose-errors" feature flag to async-std that enables
wrapping certain errors in structures with more context. As an example,
we use it in `fs::File::{open,create}` to add the given path to the
error message (something that is lacking in std to annoyance of many).
2019-11-17 21:54:44 +01:00
sclaire-1
b2aaa8b825
Edit tutorial: implementing_a_client.md
Edited to improve reading flow
2019-11-16 13:02:17 -08:00
yjhmelody
223fcc30ee fix code style for stream 2019-11-17 00:35:56 +08:00
k-nasa
76975a4441 Merge branch 'master' into fix_clippy 2019-11-16 02:41:39 +09:00
Yoshua Wuyts
355e2eded8
Merge pull request #547 from async-rs/unpin-successors-test
remove pin_mut from successors test
2019-11-15 18:24:58 +01:00
Yoshua Wuyts
6cc9e4dd2b
Merge pull request #544 from async-rs/update-futures-timer
update futures-timer & other deps
2019-11-15 18:12:03 +01:00
Yoshua Wuyts
ee23ba6e94
Merge pull request #545 from yjhmelody/stream-partition-patch
use `as_mut` for stream-partition
2019-11-15 18:08:51 +01:00
Yoshua Wuyts
d68dc659b2
remove pin_mut from successors test
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-15 18:08:00 +01:00
Yoshua Wuyts
b5c3fb8bb5
Merge pull request #363 from felipesere/async-successors
Async successors
2019-11-15 18:04:10 +01:00
k-nasa
7d616c695d refactor: change to as_mut 2019-11-16 01:58:45 +09:00
yjhmelody
a69b3a8a9e use as_mut for stream-partition 2019-11-16 00:54:50 +08:00
k-nasa
a05b6a3810 fix: mutable ref 2019-11-16 01:36:53 +09:00
k-nasa
6cbf48f12d fix clippy warn 2019-11-16 01:29:16 +09:00
k-nasa
91ee4c7b9f doc: Add stream unzip doc 2019-11-16 01:16:48 +09:00
k-nasa
603b3c5085 add: Add stream unzip 2019-11-16 01:16:35 +09:00
Yoshua Wuyts
693a7257b8
Merge pull request #538 from k-nasa/stream_by_ref
Add stream by_ref
2019-11-15 17:14:17 +01:00
Yoshua Wuyts
8779c04dc7
upgrade all deps
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-15 16:59:58 +01:00
Yoshua Wuyts
3564be9c0c
update futures-timer dep
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-15 16:58:33 +01:00
k-nasa
df92c63337 fix: Add unstable features 2019-11-16 00:29:54 +09:00
k-nasa
31cf932d80 wip: Add stream unzip 2019-11-16 00:24:59 +09:00
Yoshua Wuyts
3c6d41ccb4
Merge pull request #541 from yjhmelody/stream-partition
add stream-partition
2019-11-15 14:27:42 +01:00
Yoshua Wuyts
837604b833
Merge pull request #542 from yjhmelody/update-doc
update doc url
2019-11-15 14:26:36 +01:00
yjh
74caed2d4b
Update src/io/seek/mod.rs
Co-Authored-By: Taiki Endo <te316e89@gmail.com>
2019-11-15 18:22:06 +08:00
yjhmelody
76ec9c4563 update doc url 2019-11-15 14:33:34 +08:00
yjhmelody
d76b32e6d4 make it unstable and fix trait bound 2019-11-15 14:23:34 +08:00
yjhmelody
11268a80fb add stream-partition 2019-11-15 12:28:03 +08:00
Yoshua Wuyts
c4ba11ff95
Merge pull request #535 from async-rs/docs-sender-recv
backlink channel types
2019-11-15 03:35:43 +01:00
k-nasa
de67bf0fd4 feat: Add stream by_ref 2019-11-15 11:17:39 +09:00
k-nasa
4ef55d4d7b Enable CI on master branch 2019-11-15 09:01:41 +09:00
nasa
77a0419a3e
Merge pull request #536 from async-rs/count-unstable
mark Stream::count as unstable
2019-11-15 08:41:29 +09:00
Yoshua Wuyts
ce98834039
Merge pull request #531 from sclaire-1/master
Edit tutorial index.md
2019-11-14 22:56:13 +01:00
Yoshua Wuyts
30ff7b09b6
mark Stream::count as unstable
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-14 22:45:46 +01:00
Yoshua Wuyts
c58747b5fc
Merge pull request #368 from starsheriff/stream_count
add stream::count
2019-11-14 22:40:45 +01:00
Yoshua Wuyts
31f129ebe7
backlink channel types
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-14 22:37:04 +01:00
Yoshua Wuyts
f49d7cbbb8
Merge pull request #533 from async-rs/remove-throttle-example
remove standalone throttle example
2019-11-14 22:30:48 +01:00
Felipe Sere
64216b8e6b Take a normal closure, not an async one 2019-11-14 21:49:24 +01:00
Felipe Sere
786a52a09d Slight miss-merge 2019-11-14 21:37:51 +01:00
Felipe Sere
f14b37ff17 Remoe the T: Copy bound on the item 2019-11-14 21:35:38 +01:00
Felipe Sere
7677e9a3df Make the closure take a borrow to the value 2019-11-14 21:35:38 +01:00
Felipe Sere
bfb42b432e Rearrange docs to match 'repeat' 2019-11-14 21:35:38 +01:00
Felipe Sere
4c09cdbeac Mark successors as unstable 2019-11-14 21:35:38 +01:00
Felipe Sere
243cdd7ff1 Slight miss-merge 2019-11-14 21:35:19 +01:00
Felipe Sere
a257b7018c Rename some variables to match iter 2019-11-14 21:35:19 +01:00
Felipe Sere
af928163e4 Got further! Thx Josh! 2019-11-14 21:35:19 +01:00
Felipe Sere
8d97e0f974 Only produes empty value if next is ever a 'None' 2019-11-14 21:35:19 +01:00
Felipe Sere
266754897e Rename the module to 'successors' 2019-11-14 21:35:19 +01:00
Felipe Sere
554d5cfbc1 Slight renamings 2019-11-14 21:34:57 +01:00
Felipe Sere
8b662b659d Run rustfmt 2019-11-14 21:34:19 +01:00
Felipe Sere
95a3e53fcd Only use the Option of the future to decide to construct a new one 2019-11-14 21:33:45 +01:00
Felipe Sere
02b261de10 It compiles! Store the future and poll it instead of creating multiple new ones 2019-11-14 21:33:45 +01:00
Felipe Sere
fe3c9ef626 First attempt at successor 2019-11-14 21:33:45 +01:00
Yoshua Wuyts
4e1d79adb1
Merge pull request #524 from yjhmelody/stream-max
Add Stream max
2019-11-14 21:28:36 +01:00
Yoshua Wuyts
1546448800
remove throttle example
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-14 21:27:20 +01:00
Yoshua Wuyts
338273eb18
Merge pull request #356 from Wassasin/342-stream-throttle
Implemented StreamExt::throttle
2019-11-14 21:25:34 +01:00
Wouter Geraedts
dda65cbff0 Start throttle measurement before initialisation 2019-11-14 11:29:49 +01:00
Johannes Weissmann
9ebe41f2d6
Update src/stream/stream/mod.rs
Co-Authored-By: nasa <htilcs1115@gmail.com>
2019-11-14 10:34:09 +01:00
Wouter Geraedts
90c67c223a Decreased throttle test time to original values; only test lower bound 2019-11-14 10:26:56 +01:00
sclaire-1
8473b738d0
Edit tutorial index.md
Edited the structure of sentences to make it easier to read
2019-11-13 16:33:44 -08:00
Stjepan Glavina
0c2282ffdc
Optimization: a slot for the next task to run (#529)
* Optimization: a slot for the next task to run

* Only notify workers when a task is pushed into a queue
2019-11-13 20:32:37 +01:00
Florian Gilcher
d546ee3d92
Merge pull request #528 from skade/remove-nightly-note
Update version requirements in the tutorial
2019-11-13 16:31:42 +01:00
Florian Gilcher
6338341369
Merge pull request #520 from gierlachg/stream_pinning
Cleaning up stream pinning.
2019-11-13 15:56:48 +01:00
Florian Gilcher
6f4bea07a1
Update version requirements in the tutorial 2019-11-13 15:33:52 +01:00
yjhmelody
5adb112a00 export IntoInnerError for io 2019-11-13 13:52:16 +08:00
yjhmelody
9d634cb2a7 refactor io dir to be same with std 2019-11-13 12:42:59 +08:00
yjhmelody
879af6dc85 Add Stream max 2019-11-13 10:50:09 +08:00
Yoshua Wuyts
79962e20a5
enable attributes feature
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-12 23:37:43 +01:00
Yoshua Wuyts
1431ee0422
polish README.md examples
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-12 23:26:15 +01:00
Yoshua Wuyts
2dfdc1c482
polish lib.rs examples
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-12 23:10:23 +01:00
Grzegorz Gierlach
e442eba625 Cleaning up stream pinning. 2019-11-12 19:51:58 +01:00
Grzegorz Gierlach
f0875d2dca Cleaning up stream pinning. 2019-11-12 19:34:08 +01:00
Devashish Dixit
f611ceccc8 Run cargo fmt for doc comments (#515) 2019-11-12 17:47:03 +01:00
Yoshua Wuyts
74a7d93611 upgrade async-macros to 2.0.0 (#519)
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-12 17:34:13 +01:00
Wouter Geraedts
c5b3a98e5b Increased throttle test to 10x time 2019-11-12 16:22:25 +01:00
Yoshua Wuyts
3151a70b77
Merge pull request #517 from Alexendoo/surf
Enable surf example
2019-11-12 15:59:00 +01:00
Wouter Geraedts
4ab7b213de Updated example to be consistent; added timing measurements to throttle 2019-11-12 15:38:02 +01:00
Wouter Geraedts
6990c1403f Reimplemented throttle to never drop Delay, added boolean flag 2019-11-12 15:07:20 +01:00
Wouter Geraedts
77a1849303 Merge branch '342-stream-throttle' of github.com:Wassasin/async-std into 342-stream-throttle 2019-11-12 15:03:54 +01:00
Wouter Geraedts
a722de1a10 Merge remote-tracking branch 'upstream/master' into 342-stream-throttle 2019-11-12 14:51:24 +01:00
Wouter Geraedts
88cbf2c119 Change throttle test to run in milliseconds 2019-11-12 14:51:11 +01:00
Wouter Geraedts
6f6d5e9d20
Updated throttle fn comments.
Co-Authored-By: Yoshua Wuyts <yoshuawuyts+github@gmail.com>
2019-11-12 14:35:03 +01:00
Wouter Geraedts
7c7386735e
Wrap around throttle comment
Co-Authored-By: Yoshua Wuyts <yoshuawuyts+github@gmail.com>
2019-11-12 14:34:31 +01:00
Alex Macleod
1b7d5bea6b Enable surf example
1.0.3 has been released with the required change
2019-11-12 12:19:11 +00:00
Yoshua Wuyts
46c58b214c
Merge pull request #514 from async-rs/stabilize-yield-now
stabilize task::yield_now
2019-11-12 02:32:55 +01:00
Yoshua Wuyts
0d5c7a217f
stabilize task::yield_now
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-12 02:10:55 +01:00
Yoshua Wuyts
5017117326
Merge pull request #513 from async-rs/fix-changelog
fix changelog
2019-11-12 01:55:34 +01:00
Yoshua Wuyts
9d7b2d6696
fix changelog
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-12 01:36:03 +01:00
Yoshua Wuyts
643b678ea5
Merge pull request #511 from async-rs/1.0.1
1.0.1
2019-11-12 01:13:23 +01:00
Yoshua Wuyts
b5b2b5a0a3
1.0.1
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-11-12 00:48:26 +01:00
Johannes Weissmann
37922408e5 use pin_project 2019-11-11 22:17:29 +01:00
Johannes Weissmann
7d2282dbd2 fix merge conflict 2019-11-11 22:11:06 +01:00
Johannes Weissmann
60f822bee5 Merge branch 'master' into stream_count 2019-11-11 22:06:09 +01:00
Wouter Geraedts
ef958f0408 Use pin_project_lite instead for throttle 2019-11-11 13:09:41 +01:00
Wouter Geraedts
139a34b685 Make throttle an unstable feature 2019-11-11 12:26:32 +01:00
Wouter Geraedts
b591fc68bd Changed semantics of throttle to non-dropping variant with backpressure 2019-11-11 12:17:00 +01:00
razican
5d558ca213
Fixed test, order is no longer guaranteed 2019-11-11 11:39:30 +01:00
razican
e48e463736
Duplicating code due to strange Rust error. 2019-11-11 11:26:40 +01:00
razican
0c37d4af10
Anonymous function to avoid type issues 2019-11-11 11:25:50 +01:00
razican
79bbf4938d
Randomize Stream::merge to improve the throughput. Implements #490. 2019-11-11 10:44:12 +01:00
Wouter Geraedts
14d7d3bf9c Merge remote-tracking branch 'upstream/master' into 342-stream-throttle 2019-11-11 10:15:27 +01:00
Johannes Weissmann
75546ef831 Merge branch 'master' into stream_count 2019-10-26 23:18:04 +02:00
Johannes Weissmann
6608d39c59 remove Stream trait bound 2019-10-26 21:58:34 +02:00
Wouter Geraedts
1fd05a157f Reset delay to prevent poll after ready 2019-10-23 22:34:39 +02:00
Wouter Geraedts
1c843a8124 Re-implemented Throttle to keep last value in memory 2019-10-23 22:27:51 +02:00
Johannes Weissmann
97094b2a1c remove Sized constraint 2019-10-20 22:03:51 +02:00
Wouter Geraedts
ced5281b73 Merge remote-tracking branch 'upstream/master' into 342-stream-throttle 2019-10-20 16:50:31 +02:00
Johannes Weissmann
a9a7bdc290 add stream::count 2019-10-18 07:23:52 +02:00
Wouter Geraedts
a2393501c5 Implemented StreamExt::throttle 2019-10-16 18:43:34 +02:00
Yoshua Wuyts
483ded0e1c
fix example
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-10-12 01:38:53 +02:00
Yoshua Wuyts
064fdf020f
Stream::delay
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2019-10-12 01:35:41 +02:00
221 changed files with 5345 additions and 2745 deletions

3
.github/CONTRIBUTING.md vendored Normal file
View file

@ -0,0 +1,3 @@
Our contribution policy can be found at [async.rs/contribute][policy].
[policy]: https://async.rs/contribute/

View file

@ -4,6 +4,7 @@ on:
pull_request: pull_request:
push: push:
branches: branches:
- master
- staging - staging
- trying - trying
@ -28,6 +29,24 @@ 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:
@ -39,6 +58,15 @@ jobs:
with: with:
command: check command: check
args: --features unstable --all --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 - name: check bench
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
if: matrix.rust == 'nightly' if: matrix.rust == 'nightly'
@ -62,7 +90,69 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --all --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
@ -91,15 +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
# - name: Install rust
# run: rustup update beta && rustup default beta
# - name: Install clippy
# run: rustup component add clippy
# - name: clippy
# run: cargo clippy --all --features unstable

View file

@ -7,6 +7,323 @@ 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 # [1.0.0] - 2019-11-11
[API Documentation](https://docs.rs/async-std/1.0.0/async-std) [API Documentation](https://docs.rs/async-std/1.0.0/async-std)
@ -429,7 +746,18 @@ task::blocking(async {
- Initial beta release - Initial beta release
[Unreleased]: https://github.com/async-rs/async-std/compare/v1.0.0...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 [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.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.11]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11

View file

@ -1,9 +1,10 @@
[package] [package]
name = "async-std" name = "async-std"
version = "1.0.0" 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"
@ -24,59 +25,74 @@ rustdoc-args = ["--cfg", "feature=\"docs\""]
default = [ default = [
"std", "std",
"async-task", "async-task",
"crossbeam-channel",
"crossbeam-deque",
"futures-timer",
"kv-log-macro", "kv-log-macro",
"log", "log",
"mio",
"mio-uds",
"num_cpus", "num_cpus",
"pin-project-lite", "pin-project-lite",
"smol",
]
docs = ["attributes", "unstable", "default"]
unstable = [
"std",
"futures-timer",
] ]
docs = ["attributes", "unstable"]
unstable = ["default", "broadcaster"]
attributes = ["async-attributes"] attributes = ["async-attributes"]
std = [ std = [
"async-macros", "alloc",
"crossbeam-utils", "crossbeam-utils",
"futures-core", "futures-core/std",
"futures-io", "futures-io",
"memchr", "memchr",
"once_cell", "once_cell",
"pin-project-lite",
"pin-utils", "pin-utils",
"slab", "slab",
"wasm-bindgen-futures",
"futures-channel",
"async-mutex",
] ]
alloc = [
"futures-core/alloc",
"pin-project-lite",
]
tokio02 = ["smol/tokio02"]
[dependencies] [dependencies]
async-attributes = { version = "1.1.0", optional = true } async-attributes = { version = "1.1.1", optional = true }
async-macros = { version = "1.0.0", optional = true } async-task = { version = "3.0.0", optional = true }
async-task = { version = "1.0.0", optional = true } async-mutex = { version = "1.1.3", optional = true }
broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } crossbeam-utils = { version = "0.7.2", optional = true }
crossbeam-channel = { version = "0.3.9", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false }
crossbeam-deque = { version = "0.7.1", optional = true } futures-io = { version = "0.3.4", optional = true }
crossbeam-utils = { version = "0.6.6", optional = true } kv-log-macro = { version = "1.0.6", optional = true }
futures-core = { version = "0.3.0", optional = true }
futures-io = { version = "0.3.0", optional = true }
futures-timer = { version = "1.0.2", optional = true }
kv-log-macro = { version = "1.0.4", optional = true }
log = { version = "0.4.8", features = ["kv_unstable"], optional = true } log = { version = "0.4.8", features = ["kv_unstable"], optional = true }
memchr = { version = "2.2.1", optional = true } memchr = { version = "2.3.3", optional = true }
mio = { version = "0.6.19", optional = true } num_cpus = { version = "1.12.0", optional = true }
mio-uds = { version = "0.6.7", optional = true } once_cell = { version = "1.3.1", optional = true }
num_cpus = { version = "1.10.1", optional = true } pin-project-lite = { version = "0.1.4", optional = true }
once_cell = { version = "1.2.0", optional = true }
pin-project-lite = { version = "0.1", optional = true }
pin-utils = { version = "0.1.0-alpha.4", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true }
slab = { version = "0.4.2", optional = true } slab = { version = "0.4.2", optional = true }
futures-timer = { version = "3.0.2", optional = true }
# Devdepencency, but they are not allowed to be optional :/
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"
rand = "0.7.2" rand = "0.7.3"
# surf = "1.0.2"
tempdir = "0.3.7" tempdir = "0.3.7"
futures = "0.3.0" futures = "0.3.4"
rand_xorshift = "0.2.0"
[[test]] [[test]]
name = "stream" name = "stream"
@ -85,3 +101,7 @@ required-features = ["unstable"]
[[example]] [[example]]
name = "tcp-ipv4-and-6-echo" name = "tcp-ipv4-and-6-echo"
required-features = ["unstable"] required-features = ["unstable"]
[[example]]
name = "surf-web"
required-features = ["surf"]

View file

@ -8,6 +8,11 @@
<br /> <br />
<div align="center"> <div align="center">
<!-- 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 --> <!-- Crates version -->
<a href="https://crates.io/crates/async-std"> <a href="https://crates.io/crates/async-std">
<img src="https://img.shields.io/crates/v/async-std.svg?style=flat-square" <img src="https://img.shields.io/crates/v/async-std.svg?style=flat-square"
@ -77,17 +82,22 @@ syntax.
```rust ```rust
use async_std::task; use async_std::task;
fn main() { async fn say_hello() {
task::block_on(async {
println!("Hello, world!"); println!("Hello, world!");
}) }
fn main() {
task::block_on(say_hello())
} }
``` ```
More examples, including networking and file access, can be found in our More examples, including networking and file access, can be found in our
[`examples`] directory. [`examples`] directory and in our [documentation].
[`examples`]: https://github.com/async-rs/async-std/tree/master/examples [`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 ## Philosophy
@ -115,6 +125,24 @@ 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 [features documentation]: https://docs.rs/async-std/#features
## 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.
* [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.
* [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
<sup> <sup>

View file

@ -2,9 +2,7 @@
extern crate test; extern crate test;
use std::sync::Arc; use async_std::sync::{Arc, Mutex};
use async_std::sync::Mutex;
use async_std::task; use async_std::task;
use test::Bencher; use test::Bencher;

View file

@ -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)

View file

@ -4,13 +4,13 @@ Rust has two kinds of types commonly referred to as `Future`:
- the first is `std::future::Future` from Rusts [standard library](https://doc.rust-lang.org/std/future/trait.Future.html). - the first is `std::future::Future` from Rusts [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 Rusts 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 Rusts 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

View 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).

View file

@ -6,13 +6,13 @@ At this point, we only need to start the broker to get a fully-functioning (in t
# extern crate async_std; # extern crate async_std;
# extern crate futures; # extern crate futures;
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::SinkExt; use futures::sink::SinkExt;
use std::{ use std::{
collections::hash_map::{HashMap, Entry}, collections::hash_map::{HashMap, Entry},
sync::Arc, sync::Arc,

View file

@ -30,7 +30,7 @@ Let's add waiting to the server:
# task, # task,
# }; # };
# use futures::channel::mpsc; # use futures::channel::mpsc;
# use futures::SinkExt; # use futures::sink::SinkExt;
# use std::{ # use std::{
# collections::hash_map::{HashMap, Entry}, # collections::hash_map::{HashMap, Entry},
# sync::Arc, # sync::Arc,
@ -163,7 +163,7 @@ And to the broker:
# task, # task,
# }; # };
# use futures::channel::mpsc; # use futures::channel::mpsc;
# use futures::SinkExt; # use futures::sink::SinkExt;
# use std::{ # use std::{
# collections::hash_map::{HashMap, Entry}, # collections::hash_map::{HashMap, Entry},
# sync::Arc, # sync::Arc,

View file

@ -2,12 +2,12 @@
## 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
@ -92,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.

View file

@ -22,7 +22,7 @@ First, let's add a shutdown channel to the `connection_loop`:
# extern crate futures; # extern crate futures;
# use async_std::net::TcpStream; # use async_std::net::TcpStream;
# use futures::channel::mpsc; # use futures::channel::mpsc;
# use futures::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>>;
@ -60,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.
@ -71,14 +71,12 @@ We use the `select` macro for this purpose:
# extern crate async_std; # extern crate async_std;
# extern crate futures; # extern crate futures;
# use async_std::{net::TcpStream, prelude::*}; # use async_std::{net::TcpStream, prelude::*};
use futures::channel::mpsc; # use futures::channel::mpsc;
use futures::{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
@ -112,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
@ -128,7 +126,8 @@ use async_std::{
task, task,
}; };
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::{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,

View file

@ -1,18 +1,16 @@
## 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;

View file

@ -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).

View file

@ -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 {

View file

@ -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 = "0.3.0" futures = "0.3.0"
async-std = "1.0.0" async-std = "1"
``` ```

View file

@ -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() {}

View file

@ -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(())
} }

View file

@ -15,8 +15,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(())
} }

View file

@ -4,11 +4,14 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{self, IntoStream}; use crate::stream::{self, IntoStream};
impl<T: Ord> stream::Extend<T> for BinaryHeap<T> { impl<T: Ord + Send> stream::Extend<T> for BinaryHeap<T> {
fn 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);

View file

@ -4,16 +4,17 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{self, FromStream, IntoStream}; 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> + 'a>( fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = Self> + 'a>> { ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
where
<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();
stream::extend(&mut out, stream).await; stream::extend(&mut out, stream).await;
out out

View file

@ -4,11 +4,14 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{self, IntoStream}; use crate::stream::{self, IntoStream};
impl<K: Ord, V> stream::Extend<(K, V)> for BTreeMap<K, V> { impl<K: Ord + Send, V: Send> stream::Extend<(K, V)> for BTreeMap<K, V> {
fn 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);
})) }))

View file

@ -4,16 +4,17 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{self, FromStream, IntoStream}; 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)> + 'a>( fn from_stream<'a, S: IntoStream<Item = (K, V)> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = Self> + 'a>> { ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
where
<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();
stream::extend(&mut out, stream).await; stream::extend(&mut out, stream).await;
out out

View file

@ -4,11 +4,14 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{self, IntoStream}; use crate::stream::{self, IntoStream};
impl<T: Ord> stream::Extend<T> for BTreeSet<T> { impl<T: Ord + Send> stream::Extend<T> for BTreeSet<T> {
fn 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);
})) }))

View file

@ -4,16 +4,17 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{self, FromStream, IntoStream}; 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> + 'a>( fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = Self> + 'a>> { ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
where
<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();
stream::extend(&mut out, stream).await; stream::extend(&mut out, stream).await;
out out

View file

@ -7,13 +7,17 @@ use crate::stream::{self, IntoStream};
impl<K, V, H> stream::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 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:

View file

@ -7,18 +7,20 @@ 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)> + 'a>( fn from_stream<'a, S: IntoStream<Item = (K, V)> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = Self> + 'a>> { ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
where
<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());
stream::extend(&mut out, stream).await; stream::extend(&mut out, stream).await;
out out

View file

@ -7,13 +7,16 @@ use crate::stream::{self, IntoStream};
impl<T, H> stream::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 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.

View file

@ -7,18 +7,19 @@ 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> + 'a>( fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = Self> + 'a>> { ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
where
<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());
stream::extend(&mut out, stream).await; stream::extend(&mut out, stream).await;
out out

View file

@ -4,11 +4,14 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{self, IntoStream}; use crate::stream::{self, IntoStream};
impl<T> stream::Extend<T> for LinkedList<T> { impl<T: Send> stream::Extend<T> for LinkedList<T> {
fn 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)))
} }

View file

@ -4,16 +4,17 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{self, FromStream, IntoStream}; 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> + 'a>( fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = Self> + 'a>> { ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
where
<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();
stream::extend(&mut out, stream).await; stream::extend(&mut out, stream).await;
out out

View file

@ -4,11 +4,14 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{self, IntoStream}; use crate::stream::{self, IntoStream};
impl<T> stream::Extend<T> for VecDeque<T> { impl<T: Send> stream::Extend<T> for VecDeque<T> {
fn 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);

View file

@ -4,16 +4,17 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{self, FromStream, IntoStream}; 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> + 'a>( fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = Self> + 'a>> { ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
where
<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();
stream::extend(&mut out, stream).await; stream::extend(&mut out, stream).await;
out out

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::{Path, PathBuf}; use crate::path::{Path, PathBuf};
use crate::task::spawn_blocking; use crate::task::spawn_blocking;
use crate::utils::Context as _;
/// Returns the canonical form of a path. /// Returns the canonical form of a path.
/// ///
@ -32,5 +33,10 @@ use crate::task::spawn_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();
spawn_blocking(move || std::fs::canonicalize(&path).map(Into::into)).await spawn_blocking(move || {
std::fs::canonicalize(&path)
.map(Into::into)
.context(|| format!("could not canonicalize `{}`", path.display()))
})
.await
} }

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking; 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.
/// ///
@ -41,5 +42,9 @@ use crate::task::spawn_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();
spawn_blocking(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
} }

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking; use crate::task::spawn_blocking;
use crate::utils::Context as _;
/// Creates a new directory. /// Creates a new directory.
/// ///
@ -34,5 +35,9 @@ use crate::task::spawn_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();
spawn_blocking(move || std::fs::create_dir(path)).await spawn_blocking(move || {
std::fs::create_dir(&path)
.context(|| format!("could not create directory `{}`", path.display()))
})
.await
} }

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking; 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.
/// ///
@ -29,5 +30,9 @@ use crate::task::spawn_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();
spawn_blocking(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
} }

View file

@ -158,6 +158,12 @@ impl fmt::Debug for DirEntry {
} }
} }
impl Clone for DirEntry {
fn clone(&self) -> Self {
DirEntry(self.0.clone())
}
}
cfg_unix! { cfg_unix! {
use crate::os::unix::fs::DirEntryExt; use crate::os::unix::fs::DirEntryExt;

View file

@ -12,7 +12,8 @@ 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::path::Path;
use crate::prelude::*; use crate::prelude::*;
use crate::task::{self, spawn_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.
/// ///
@ -112,7 +113,10 @@ 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 = spawn_blocking(move || std::fs::File::open(&path)).await?; let file = spawn_blocking(move || {
std::fs::File::open(&path).context(|| format!("could not open `{}`", path.display()))
})
.await?;
Ok(File::new(file, true)) Ok(File::new(file, true))
} }
@ -147,7 +151,11 @@ 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 = spawn_blocking(move || std::fs::File::create(&path)).await?; let file = spawn_blocking(move || {
std::fs::File::create(&path)
.context(|| format!("could not create `{}`", path.display()))
})
.await?;
Ok(File::new(file, true)) Ok(File::new(file, true))
} }
@ -307,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());
} }
} }
@ -665,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);
@ -733,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.
@ -856,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();
});
}
}

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking; use crate::task::spawn_blocking;
use crate::utils::Context as _;
/// Creates a hard link on the filesystem. /// Creates a hard link on the filesystem.
/// ///
@ -32,5 +33,14 @@ use crate::task::spawn_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();
spawn_blocking(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
} }

View file

@ -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
//! //!

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking; 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.
/// ///
@ -36,5 +37,8 @@ use crate::task::spawn_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();
spawn_blocking(move || std::fs::read(path)).await spawn_blocking(move || {
std::fs::read(&path).context(|| format!("could not read file `{}`", path.display()))
})
.await
} }

View file

@ -1,11 +1,12 @@
use std::pin::Pin;
use std::future::Future; use std::future::Future;
use std::pin::Pin;
use crate::fs::DirEntry; use crate::fs::DirEntry;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{spawn_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,7 +46,10 @@ use crate::task::{spawn_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();
spawn_blocking(move || std::fs::read_dir(path)) spawn_blocking(move || {
std::fs::read_dir(&path)
.context(|| format!("could not read directory `{}`", path.display()))
})
.await .await
.map(ReadDir::new) .map(ReadDir::new)
} }

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::{Path, PathBuf}; use crate::path::{Path, PathBuf};
use crate::task::spawn_blocking; 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.
/// ///
@ -28,5 +29,10 @@ use crate::task::spawn_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();
spawn_blocking(move || std::fs::read_link(path).map(Into::into)).await spawn_blocking(move || {
std::fs::read_link(&path)
.map(Into::into)
.context(|| format!("could not read link `{}`", path.display()))
})
.await
} }

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking; 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.
/// ///
@ -37,5 +38,9 @@ use crate::task::spawn_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();
spawn_blocking(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
} }

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking; use crate::task::spawn_blocking;
use crate::utils::Context as _;
/// Removes an empty directory. /// Removes an empty directory.
/// ///
@ -29,5 +30,9 @@ use crate::task::spawn_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();
spawn_blocking(move || std::fs::remove_dir(path)).await spawn_blocking(move || {
std::fs::remove_dir(&path)
.context(|| format!("could not remove directory `{}`", path.display()))
})
.await
} }

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking; 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.
/// ///
@ -29,5 +30,9 @@ use crate::task::spawn_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();
spawn_blocking(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
} }

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking; use crate::task::spawn_blocking;
use crate::utils::Context as _;
/// Removes a file. /// Removes a file.
/// ///
@ -29,5 +30,9 @@ use crate::task::spawn_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();
spawn_blocking(move || std::fs::remove_file(path)).await spawn_blocking(move || {
std::fs::remove_file(&path)
.context(|| format!("could not remove file `{}`", path.display()))
})
.await
} }

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking; 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.
/// ///
@ -34,5 +35,14 @@ use crate::task::spawn_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();
spawn_blocking(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
} }

View file

@ -1,6 +1,7 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking; 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.
/// ///
@ -33,5 +34,9 @@ use crate::task::spawn_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();
spawn_blocking(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
} }

View file

@ -2,10 +2,10 @@ 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 pin_project_lite::pin_project;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
use crate::utils::{timer_after, Timer};
pin_project! { pin_project! {
#[doc(hidden)] #[doc(hidden)]
@ -14,13 +14,13 @@ pin_project! {
#[pin] #[pin]
future: F, future: F,
#[pin] #[pin]
delay: Delay, delay: Timer,
} }
} }
impl<F> DelayFuture<F> { impl<F> DelayFuture<F> {
pub fn new(future: F, dur: Duration) -> DelayFuture<F> { pub fn new(future: F, dur: Duration) -> DelayFuture<F> {
let delay = Delay::new(dur); let delay = timer_after(dur);
DelayFuture { future, delay } DelayFuture { future, delay }
} }

View file

@ -1,6 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use async_macros::MaybeDone; use crate::future::MaybeDone;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -12,7 +12,7 @@ pin_project! {
pub struct Join<L, R> pub struct Join<L, R>
where where
L: Future, L: Future,
R: Future<Output = L::Output> R: Future,
{ {
#[pin] left: MaybeDone<L>, #[pin] left: MaybeDone<L>,
#[pin] right: MaybeDone<R>, #[pin] right: MaybeDone<R>,
@ -22,7 +22,7 @@ pin_project! {
impl<L, R> Join<L, R> impl<L, R> Join<L, R>
where where
L: Future, L: Future,
R: Future<Output = L::Output>, R: Future,
{ {
pub(crate) fn new(left: L, right: R) -> Self { pub(crate) fn new(left: L, right: R) -> Self {
Self { Self {
@ -35,7 +35,7 @@ where
impl<L, R> Future for Join<L, R> impl<L, R> Future for Join<L, R>
where where
L: Future, L: Future,
R: Future<Output = L::Output>, R: Future,
{ {
type Output = (L::Output, R::Output); type Output = (L::Output, R::Output);
@ -45,17 +45,15 @@ where
let mut left = this.left; let mut left = this.left;
let mut right = this.right; let mut right = this.right;
if Future::poll(Pin::new(&mut left), cx).is_ready() { let is_left_ready = Future::poll(Pin::new(&mut left), cx).is_ready();
if right.as_ref().output().is_some() { if is_left_ready && right.as_ref().output().is_some() {
return Poll::Ready((left.take().unwrap(), right.take().unwrap())); return Poll::Ready((left.take().unwrap(), right.take().unwrap()));
} }
}
if Future::poll(Pin::new(&mut right), cx).is_ready() { let is_right_ready = Future::poll(Pin::new(&mut right), cx).is_ready();
if left.as_ref().output().is_some() { if is_right_ready && left.as_ref().output().is_some() {
return Poll::Ready((left.take().unwrap(), right.take().unwrap())); return Poll::Ready((left.take().unwrap(), right.take().unwrap()));
} }
}
Poll::Pending Poll::Pending
} }

View file

@ -7,7 +7,6 @@ cfg_unstable! {
mod try_join; mod try_join;
use std::time::Duration; use std::time::Duration;
use delay::DelayFuture; use delay::DelayFuture;
use flatten::FlattenFuture; use flatten::FlattenFuture;
use crate::future::IntoFuture; use crate::future::IntoFuture;
@ -17,9 +16,13 @@ cfg_unstable! {
use try_join::TryJoin; use try_join::TryJoin;
} }
cfg_unstable_default! {
use crate::future::timeout::TimeoutFuture;
}
extension_trait! { extension_trait! {
use std::pin::Pin; use core::pin::Pin;
use std::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -133,7 +136,7 @@ extension_trait! {
[`Future`]: ../future/trait.Future.html [`Future`]: ../future/trait.Future.html
"#] "#]
pub trait FutureExt: std::future::Future { pub trait FutureExt: core::future::Future {
/// Returns a Future that delays execution for a specified time. /// Returns a Future that delays execution for a specified time.
/// ///
/// # Examples /// # Examples
@ -148,7 +151,7 @@ extension_trait! {
/// dbg!(a.await); /// dbg!(a.await);
/// # }) /// # })
/// ``` /// ```
#[cfg(all(feature = "default", feature = "unstable"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
fn delay(self, dur: Duration) -> impl Future<Output = Self::Output> [DelayFuture<Self>] fn delay(self, dur: Duration) -> impl Future<Output = Self::Output> [DelayFuture<Self>]
where where
@ -289,10 +292,10 @@ extension_trait! {
use async_std::future; use async_std::future;
let a = future::ready(1u8); let a = future::ready(1u8);
let b = future::ready(2u8); let b = future::ready(2u16);
let f = a.join(b); let f = a.join(b);
assert_eq!(f.await, (1u8, 2u8)); assert_eq!(f.await, (1u8, 2u16));
# }); # });
``` ```
"#] "#]
@ -304,7 +307,7 @@ extension_trait! {
) -> impl Future<Output = (<Self as std::future::Future>::Output, <F as std::future::Future>::Output)> [Join<Self, F>] ) -> impl Future<Output = (<Self as std::future::Future>::Output, <F as std::future::Future>::Output)> [Join<Self, F>]
where where
Self: std::future::Future + Sized, Self: std::future::Future + Sized,
F: std::future::Future<Output = <Self as std::future::Future>::Output>, F: std::future::Future,
{ {
Join::new(self, other) Join::new(self, other)
} }
@ -328,33 +331,67 @@ extension_trait! {
use async_std::prelude::*; use async_std::prelude::*;
use async_std::future; use async_std::future;
let a = future::ready(Err("Error")); let a = future::ready(Err::<u8, &str>("Error"));
let b = future::ready(Ok(1u8)); let b = future::ready(Ok(1u8));
let f = a.try_join(b); let f = a.try_join(b);
assert_eq!(f.await, Err("Error")); assert_eq!(f.await, Err("Error"));
let a = future::ready(Ok::<u8, String>(1u8)); let a = future::ready(Ok::<u8, String>(1u8));
let b = future::ready(Ok::<u8, String>(2u8)); let b = future::ready(Ok::<u16, String>(2u16));
let f = a.try_join(b); let f = a.try_join(b);
assert_eq!(f.await, Ok((1u8, 2u8))); assert_eq!(f.await, Ok((1u8, 2u16)));
# #
# Ok(()) }) } # Ok(()) }) }
``` ```
"#] "#]
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(any(feature = "unstable", feature = "docs"))]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
fn try_join<F, T, E>( fn try_join<F, A, B, E>(
self, self,
other: F other: F
) -> impl Future<Output = Result<(T, T), E>> [TryJoin<Self, F>] ) -> impl Future<Output = Result<(A, B), E>> [TryJoin<Self, F>]
where where
Self: std::future::Future<Output = Result<T, E>> + Sized, Self: std::future::Future<Output = Result<A, E>> + Sized,
F: std::future::Future<Output = <Self as std::future::Future>::Output>, F: std::future::Future<Output = Result<B, E>>,
{ {
TryJoin::new(self, other) 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> { impl<F: Future + Unpin + ?Sized> Future for Box<F> {

View file

@ -1,7 +1,7 @@
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use async_macros::MaybeDone; use crate::future::MaybeDone;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

View file

@ -1,6 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use async_macros::MaybeDone; use crate::future::MaybeDone;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -12,7 +12,7 @@ pin_project! {
pub struct TryJoin<L, R> pub struct TryJoin<L, R>
where where
L: Future, L: Future,
R: Future<Output = L::Output> R: Future,
{ {
#[pin] left: MaybeDone<L>, #[pin] left: MaybeDone<L>,
#[pin] right: MaybeDone<R>, #[pin] right: MaybeDone<R>,
@ -22,7 +22,7 @@ pin_project! {
impl<L, R> TryJoin<L, R> impl<L, R> TryJoin<L, R>
where where
L: Future, L: Future,
R: Future<Output = L::Output>, R: Future,
{ {
pub(crate) fn new(left: L, right: R) -> Self { pub(crate) fn new(left: L, right: R) -> Self {
Self { Self {
@ -32,12 +32,12 @@ where
} }
} }
impl<L, R, T, E> Future for TryJoin<L, R> impl<L, R, A, B, E> Future for TryJoin<L, R>
where where
L: Future<Output = Result<T, E>>, L: Future<Output = Result<A, E>>,
R: Future<Output = L::Output>, R: Future<Output = Result<B, E>>,
{ {
type Output = Result<(T, T), E>; type Output = Result<(A, B), E>;
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 this = self.project(); let this = self.project();

View file

@ -1,6 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use async_macros::MaybeDone; use crate::future::MaybeDone;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

79
src/future/maybe_done.rs Normal file
View 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(())
}
}

View file

@ -46,22 +46,29 @@
//! [`Future::race`]: trait.Future.html#method.race //! [`Future::race`]: trait.Future.html#method.race
//! [`Future::try_race`]: trait.Future.html#method.try_race //! [`Future::try_race`]: trait.Future.html#method.try_race
cfg_alloc! {
pub use future::Future; pub use future::Future;
pub(crate) mod future;
}
cfg_std! {
pub use pending::pending; pub use pending::pending;
pub use poll_fn::poll_fn; pub use poll_fn::poll_fn;
pub use ready::ready; pub use ready::ready;
pub(crate) mod future;
mod pending; mod pending;
mod poll_fn; mod poll_fn;
mod ready; mod ready;
cfg_default! {
pub use timeout::{timeout, TimeoutError};
mod timeout;
} }
#[cfg(any(feature = "unstable", feature = "default"))]
pub use timeout::{timeout, TimeoutError};
#[cfg(any(feature = "unstable", feature = "default"))]
mod timeout;
cfg_unstable! { cfg_unstable! {
pub use into_future::IntoFuture; pub use into_future::IntoFuture;
pub(crate) use maybe_done::MaybeDone;
mod into_future; mod into_future;
mod maybe_done;
} }

View file

@ -1,13 +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 std::future::Future;
use futures_timer::Delay;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
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.
/// ///
@ -33,20 +33,26 @@ 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
} }
pin_project! { pin_project! {
/// A future that times out after a duration of time. /// A future that times out after a duration of time.
struct TimeoutFuture<F> { pub struct TimeoutFuture<F> {
#[pin] #[pin]
future: F, future: F,
#[pin] #[pin]
delay: Delay, delay: Timer,
}
}
impl<F> TimeoutFuture<F> {
#[allow(dead_code)]
pub(super) fn new(future: F, dur: Duration) -> TimeoutFuture<F> {
TimeoutFuture {
future,
delay: timer_after(dur),
}
} }
} }

View file

@ -29,12 +29,12 @@ extension_trait! {
``` ```
# #[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 [`BufReadExt`]: ../io/prelude/trait.BufReadExt.html
[prelude]: ../prelude/index.html [prelude]: ../prelude/index.html

View file

@ -4,11 +4,9 @@ use std::{cmp, fmt};
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::io::{self, BufRead, Read, Seek, SeekFrom}; 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! { pin_project! {
/// Adds buffering to any reader. /// Adds buffering to any reader.
/// ///
@ -72,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.

View file

@ -4,11 +4,9 @@ use std::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::io::write::WriteExt; use crate::io::write::WriteExt;
use crate::io::{self, Seek, SeekFrom, Write}; use crate::io::{self, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE};
use crate::task::{Context, Poll, ready}; use crate::task::{Context, Poll, ready};
const DEFAULT_CAPACITY: usize = 8 * 1024;
pin_project! { pin_project! {
/// Wraps a writer and buffers its output. /// Wraps a writer and buffers its output.
/// ///
@ -24,14 +22,14 @@ pin_project! {
/// times. It also provides no advantage when writing to a destination that is /// times. It also provides no advantage when writing to a destination that is
/// in memory, like a `Vec<u8>`. /// in memory, like a `Vec<u8>`.
/// ///
/// When the `BufWriter` is dropped, the contents of its buffer will be written /// Unlike the `BufWriter` type in `std`, this type does not write out the
/// out. However, any errors that happen in the process of flushing the buffer /// contents of its buffer when it is dropped. Therefore, it is absolutely
/// when the writer is dropped will be ignored. Code that wishes to handle such /// critical that users explicitly flush the buffer before dropping a
/// errors must manually call [`flush`] before the writer is dropped. /// `BufWriter`.
/// ///
/// This type is an async version of [`std::io::BufReader`]. /// This type is an async version of [`std::io::BufWriter`].
/// ///
/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html /// [`std::io::BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html
/// ///
/// # Examples /// # Examples
/// ///
@ -63,10 +61,13 @@ pin_project! {
/// use async_std::prelude::*; /// use async_std::prelude::*;
/// ///
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?); /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?);
///
/// for i in 0..10 { /// for i in 0..10 {
/// let arr = [i+1]; /// let arr = [i+1];
/// stream.write(&arr).await?; /// stream.write(&arr).await?;
/// }; /// };
///
/// stream.flush().await?;
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
@ -87,8 +88,32 @@ pin_project! {
} }
} }
/// An error returned by `into_inner` which combines an error that
/// happened while writing out the buffer, and the buffered writer object
/// which may be used to recover from the condition.
///
/// # Examples
///
/// ```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 = match buf_writer.into_inner().await {
/// Ok(s) => s,
/// Err(e) => {
/// // Here, e is an IntoInnerError
/// panic!("An error occurred");
/// }
/// };
/// #
/// # Ok(()) }) }
///```
#[derive(Debug)] #[derive(Debug)]
pub struct IntoInnerError<W>(W, std::io::Error); pub struct IntoInnerError<W>(W, crate::io::Error);
impl<W: Write> BufWriter<W> { impl<W: Write> BufWriter<W> {
/// 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,
@ -107,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.
@ -303,7 +328,7 @@ impl<W: Write> Write for BufWriter<W> {
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()

View file

@ -1,10 +1,11 @@
use std::pin::Pin;
use std::future::Future; use std::future::Future;
use std::pin::Pin;
use pin_project_lite::pin_project; 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.
/// ///
@ -90,7 +91,7 @@ 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. /// Copies the entire contents of a reader into a writer.
@ -177,5 +178,5 @@ where
writer, writer,
amt: 0, amt: 0,
}; };
future.await future.await.context(|| String::from("io::copy failed"))
} }

View file

@ -105,8 +105,8 @@
//! //!
//! ```no_run //! ```no_run
//! use async_std::fs::File; //! use async_std::fs::File;
//! use async_std::io::BufWriter;
//! use async_std::io::prelude::*; //! use async_std::io::prelude::*;
//! use async_std::io::BufWriter;
//! //!
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
//! # //! #
@ -116,8 +116,8 @@
//! //!
//! // write a byte to the buffer //! // write a byte to the buffer
//! writer.write(&[42]).await?; //! writer.write(&[42]).await?;
//!
//! } // the buffer is flushed once writer goes out of scope //! } // the buffer is flushed once writer goes out of scope
//! //
//! # //! #
//! # Ok(()) }) } //! # Ok(()) }) }
//! ``` //! ```
@ -269,17 +269,19 @@
//! [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html //! [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
//! [`.unwrap()`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap //! [`.unwrap()`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap
const DEFAULT_BUF_SIZE: usize = 8 * 1024;
cfg_std! { cfg_std! {
#[doc(inline)] #[doc(inline)]
pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom};
pub use buf_read::{BufRead, Lines}; pub use buf_read::{BufRead, Lines, Split};
pub use buf_reader::BufReader; pub use buf_reader::BufReader;
pub use buf_writer::BufWriter; pub use buf_writer::{BufWriter, IntoInnerError};
pub use copy::copy; pub use copy::copy;
pub use cursor::Cursor; pub use cursor::Cursor;
pub use empty::{empty, Empty}; pub use empty::{empty, Empty};
pub use read::Read; pub use read::*;
pub use repeat::{repeat, Repeat}; pub use repeat::{repeat, Repeat};
pub use seek::Seek; pub use seek::Seek;
pub use sink::{sink, Sink}; pub use sink::{sink, Sink};
@ -291,6 +293,7 @@ cfg_std! {
pub(crate) mod read; pub(crate) mod read;
pub(crate) mod seek; pub(crate) mod seek;
pub(crate) mod write; pub(crate) mod write;
pub(crate) mod utils;
mod buf_reader; mod buf_reader;
mod buf_writer; mod buf_writer;
@ -304,22 +307,24 @@ cfg_std! {
cfg_default! { cfg_default! {
// For use in the print macros. // For use in the print macros.
#[doc(hidden)] #[doc(hidden)]
#[cfg(not(target_os = "unknown"))]
pub use stdio::{_eprint, _print}; pub use stdio::{_eprint, _print};
#[cfg(not(target_os = "unknown"))]
pub use stderr::{stderr, Stderr}; pub use stderr::{stderr, Stderr};
#[cfg(not(target_os = "unknown"))]
pub use stdin::{stdin, Stdin}; pub use stdin::{stdin, Stdin};
#[cfg(not(target_os = "unknown"))]
pub use stdout::{stdout, Stdout}; pub use stdout::{stdout, Stdout};
pub use timeout::timeout; pub use timeout::timeout;
mod timeout; mod timeout;
#[cfg(not(target_os = "unknown"))]
mod stderr; mod stderr;
#[cfg(not(target_os = "unknown"))]
mod stdin; mod stdin;
#[cfg(not(target_os = "unknown"))]
mod stdio; mod stdio;
#[cfg(not(target_os = "unknown"))]
mod stdout; mod stdout;
} }
cfg_unstable! {
pub use stderr::StderrLock;
pub use stdin::StdinLock;
pub use stdout::StdoutLock;
}

View file

@ -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::*;

View file

@ -165,7 +165,7 @@ impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> {
} }
} }
#[cfg(test)] #[cfg(all(test, default))]
mod tests { mod tests {
use crate::io; use crate::io;
use crate::prelude::*; use crate::prelude::*;

View file

@ -17,6 +17,10 @@ use std::mem;
use crate::io::IoSliceMut; use crate::io::IoSliceMut;
pub use bytes::Bytes;
pub use chain::Chain;
pub use take::Take;
extension_trait! { extension_trait! {
use std::pin::Pin; use std::pin::Pin;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
@ -40,7 +44,7 @@ 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 [`ReadExt`]: ../io/prelude/trait.ReadExt.html
@ -301,11 +305,11 @@ 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#"
@ -377,8 +381,8 @@ 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#"
@ -413,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 }
} }
} }
@ -473,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();
@ -489,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(()) });
})
} }
} }

View file

@ -218,7 +218,7 @@ impl<T: BufRead> BufRead for Take<T> {
} }
} }
#[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::*;

View file

@ -27,7 +27,7 @@ extension_trait! {
[`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html [`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html
[`futures::io::AsyncSeek`]: [`futures::io::AsyncSeek`]:
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html https://docs.rs/futures/0.3/futures/io/trait.AsyncSeek.html
[provided methods]: #provided-methods [provided methods]: #provided-methods
[`SeekExt`]: ../io/prelude/trait.SeekExt.html [`SeekExt`]: ../io/prelude/trait.SeekExt.html
[prelude]: ../prelude/index.html [prelude]: ../prelude/index.html

View file

@ -5,11 +5,6 @@ use std::future::Future;
use crate::io::{self, Write}; use crate::io::{self, Write};
use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
cfg_unstable! {
use once_cell::sync::Lazy;
use std::io::Write as _;
}
/// Constructs a new handle to the standard error of the current process. /// Constructs a new handle to the standard error of the current process.
/// ///
/// This function is an async version of [`std::io::stderr`]. /// This function is an async version of [`std::io::stderr`].
@ -58,22 +53,6 @@ pub fn stderr() -> Stderr {
#[derive(Debug)] #[derive(Debug)]
pub struct Stderr(Mutex<State>); pub struct Stderr(Mutex<State>);
/// A locked reference to the Stderr handle.
///
/// This handle implements the [`Write`] traits, and is constructed via the [`Stderr::lock`]
/// method.
///
/// [`Write`]: trait.Read.html
/// [`Stderr::lock`]: struct.Stderr.html#method.lock
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[derive(Debug)]
pub struct StderrLock<'a>(std::io::StderrLock<'a>);
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
unsafe impl Send for StderrLock<'_> {}
/// The state of the asynchronous stderr. /// The state of the asynchronous stderr.
/// ///
/// The stderr can be either idle or busy performing an asynchronous operation. /// The stderr can be either idle or busy performing an asynchronous operation.
@ -108,42 +87,14 @@ enum Operation {
Flush(io::Result<()>), Flush(io::Result<()>),
} }
impl Stderr {
/// Locks this handle to the standard error stream, returning a writable guard.
///
/// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data.
///
/// # Examples
///
/// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::io;
/// use async_std::prelude::*;
///
/// let stderr = io::stderr();
/// let mut handle = stderr.lock().await;
///
/// handle.write_all(b"hello world").await?;
/// #
/// # Ok(()) }) }
/// ```
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub async fn lock(&self) -> StderrLock<'static> {
static STDERR: Lazy<std::io::Stderr> = Lazy::new(std::io::stderr);
spawn_blocking(move || StderrLock(STDERR.lock())).await
}
}
impl Write for Stderr { impl Write for Stderr {
fn poll_write( fn poll_write(
mut self: Pin<&mut Self>, self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &[u8], buf: &[u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
let state = &mut *self.0.lock().unwrap(); let mut state_guard = self.0.lock().unwrap();
let state = &mut *state_guard;
loop { loop {
match state { match state {
@ -187,8 +138,9 @@ impl Write for Stderr {
} }
} }
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let state = &mut *self.0.lock().unwrap(); let mut state_guard = self.0.lock().unwrap();
let state = &mut *state_guard;
loop { loop {
match state { match state {
@ -239,23 +191,3 @@ cfg_windows! {
} }
} }
} }
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
impl io::Write for StderrLock<'_> {
fn poll_write(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Poll::Ready(self.0.write(buf))
}
fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(self.0.flush())
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_flush(cx)
}
}

View file

@ -1,15 +1,11 @@
use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Mutex; use std::sync::Mutex;
use std::future::Future;
use crate::future; use crate::future;
use crate::io::{self, Read}; use crate::io::{self, Read};
use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
use crate::utils::Context as _;
cfg_unstable! {
use once_cell::sync::Lazy;
use std::io::Read as _;
}
/// Constructs a new handle to the standard input of the current process. /// Constructs a new handle to the standard input of the current process.
/// ///
@ -60,21 +56,6 @@ pub fn stdin() -> Stdin {
#[derive(Debug)] #[derive(Debug)]
pub struct Stdin(Mutex<State>); pub struct Stdin(Mutex<State>);
/// A locked reference to the Stdin handle.
///
/// This handle implements the [`Read`] traits, and is constructed via the [`Stdin::lock`] method.
///
/// [`Read`]: trait.Read.html
/// [`Stdin::lock`]: struct.Stdin.html#method.lock
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(feature = "unstable")]
#[derive(Debug)]
pub struct StdinLock<'a>(std::io::StdinLock<'a>);
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
unsafe impl Send for StdinLock<'_> {}
/// The state of the asynchronous stdin. /// The state of the asynchronous stdin.
/// ///
/// The stdin can be either idle or busy performing an asynchronous operation. /// The stdin can be either idle or busy performing an asynchronous operation.
@ -162,45 +143,18 @@ impl Stdin {
} }
}) })
.await .await
} .context(|| String::from("could not read line on stdin"))
/// Locks this handle to the standard input stream, returning a readable guard.
///
/// The lock is released when the returned lock goes out of scope. The returned guard also implements the Read trait for accessing the underlying data.
///
/// # Examples
///
/// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::io;
/// use async_std::prelude::*;
///
/// let mut buffer = String::new();
///
/// let stdin = io::stdin();
/// let mut handle = stdin.lock().await;
///
/// handle.read_to_string(&mut buffer).await?;
/// #
/// # Ok(()) }) }
/// ```
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub async fn lock(&self) -> StdinLock<'static> {
static STDIN: Lazy<std::io::Stdin> = Lazy::new(std::io::stdin);
spawn_blocking(move || StdinLock(STDIN.lock())).await
} }
} }
impl Read for Stdin { impl Read for Stdin {
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>> {
let state = &mut *self.0.lock().unwrap(); let mut state_guard = self.0.lock().unwrap();
let state = &mut *state_guard;
loop { loop {
match state { match state {
@ -263,15 +217,3 @@ cfg_windows! {
} }
} }
} }
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
impl Read for StdinLock<'_> {
fn poll_read(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Poll::Ready(self.0.read(buf))
}
}

View file

@ -5,11 +5,6 @@ use std::future::Future;
use crate::io::{self, Write}; use crate::io::{self, Write};
use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
cfg_unstable! {
use once_cell::sync::Lazy;
use std::io::Write as _;
}
/// Constructs a new handle to the standard output of the current process. /// Constructs a new handle to the standard output of the current process.
/// ///
/// This function is an async version of [`std::io::stdout`]. /// This function is an async version of [`std::io::stdout`].
@ -58,22 +53,6 @@ pub fn stdout() -> Stdout {
#[derive(Debug)] #[derive(Debug)]
pub struct Stdout(Mutex<State>); pub struct Stdout(Mutex<State>);
/// A locked reference to the Stderr handle.
///
/// This handle implements the [`Write`] traits, and is constructed via the [`Stdout::lock`]
/// method.
///
/// [`Write`]: trait.Read.html
/// [`Stdout::lock`]: struct.Stdout.html#method.lock
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[derive(Debug)]
pub struct StdoutLock<'a>(std::io::StdoutLock<'a>);
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
unsafe impl Send for StdoutLock<'_> {}
/// The state of the asynchronous stdout. /// The state of the asynchronous stdout.
/// ///
/// The stdout can be either idle or busy performing an asynchronous operation. /// The stdout can be either idle or busy performing an asynchronous operation.
@ -108,42 +87,14 @@ enum Operation {
Flush(io::Result<()>), Flush(io::Result<()>),
} }
impl Stdout {
/// Locks this handle to the standard error stream, returning a writable guard.
///
/// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data.
///
/// # Examples
///
/// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::io;
/// use async_std::prelude::*;
///
/// let stdout = io::stdout();
/// let mut handle = stdout.lock().await;
///
/// handle.write_all(b"hello world").await?;
/// #
/// # Ok(()) }) }
/// ```
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub async fn lock(&self) -> StdoutLock<'static> {
static STDOUT: Lazy<std::io::Stdout> = Lazy::new(std::io::stdout);
spawn_blocking(move || StdoutLock(STDOUT.lock())).await
}
}
impl Write for Stdout { impl Write for Stdout {
fn poll_write( fn poll_write(
mut self: Pin<&mut Self>, self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &[u8], buf: &[u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
let state = &mut *self.0.lock().unwrap(); let mut state_guard = self.0.lock().unwrap();
let state = &mut *state_guard;
loop { loop {
match state { match state {
@ -187,8 +138,9 @@ impl Write for Stdout {
} }
} }
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let state = &mut *self.0.lock().unwrap(); let mut state_guard = self.0.lock().unwrap();
let state = &mut *state_guard;
loop { loop {
match state { match state {
@ -239,23 +191,3 @@ cfg_windows! {
} }
} }
} }
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
impl Write for StdoutLock<'_> {
fn poll_write(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Poll::Ready(self.0.write(buf))
}
fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(self.0.flush())
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_flush(cx)
}
}

View file

@ -1,12 +1,12 @@
use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::time::Duration; use std::time::Duration;
use std::future::Future;
use futures_timer::Delay;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::io; use crate::io;
use crate::utils::{timer_after, Timer};
/// Awaits an I/O future or times out after a duration of time. /// Awaits an I/O future or times out after a duration of time.
/// ///
@ -37,7 +37,7 @@ where
F: Future<Output = io::Result<T>>, F: Future<Output = io::Result<T>>,
{ {
Timeout { Timeout {
timeout: Delay::new(dur), timeout: timer_after(dur),
future: f, future: f,
} }
.await .await
@ -53,7 +53,7 @@ pin_project! {
#[pin] #[pin]
future: F, future: F,
#[pin] #[pin]
timeout: Delay, timeout: Timer,
} }
} }

42
src/io/utils.rs Normal file
View file

@ -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)
}
}

View file

@ -35,7 +35,7 @@ extension_trait! {
[`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
[`futures::io::AsyncWrite`]: [`futures::io::AsyncWrite`]:
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncWrite.html https://docs.rs/futures/0.3/futures/io/trait.AsyncWrite.html
[`poll_write`]: #tymethod.poll_write [`poll_write`]: #tymethod.poll_write
[`poll_write_vectored`]: #method.poll_write_vectored [`poll_write_vectored`]: #method.poll_write_vectored
[`poll_flush`]: #tymethod.poll_flush [`poll_flush`]: #tymethod.poll_flush

View file

@ -6,6 +6,7 @@ use crate::task::{Context, Poll};
#[doc(hidden)] #[doc(hidden)]
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
#[must_use]
pub struct WriteFmtFuture<'a, T: Unpin + ?Sized> { pub struct WriteFmtFuture<'a, T: Unpin + ?Sized> {
pub(crate) writer: &'a mut T, pub(crate) writer: &'a mut T,
pub(crate) res: Option<io::Result<Vec<u8>>>, pub(crate) res: Option<io::Result<Vec<u8>>>,

View file

@ -47,7 +47,7 @@
//! encouraged to read it. The `async-std` source is generally high //! encouraged to read it. The `async-std` source is generally high
//! quality and a peek behind the curtains is often enlightening. //! quality and a peek behind the curtains is often enlightening.
//! //!
//! Modules in this crate are organized in the same way as in `async-std`, except blocking //! Modules in this crate are organized in the same way as in `std`, except blocking
//! functions have been replaced with async functions and threads have been replaced with //! functions have been replaced with async functions and threads have been replaced with
//! lightweight tasks. //! lightweight tasks.
//! //!
@ -131,18 +131,61 @@
//! //!
//! # Examples //! # Examples
//! //!
//! Spawn a task and block the current thread on its result: //! All examples require the [`"attributes"` feature](#features) to be enabled.
//! This feature is not enabled by default because it significantly impacts
//! compile times. See [`task::block_on`] for an alternative way to start
//! executing tasks.
//! //!
//! ``` //! Call an async function from the main function:
//! use async_std::task;
//! //!
//! fn main() { #![cfg_attr(feature = "attributes", doc = "```")]
//! task::block_on(async { #![cfg_attr(not(feature = "attributes"), doc = "```ignore")]
//! async fn say_hello() {
//! println!("Hello, world!"); //! println!("Hello, world!");
//! }) //! }
//!
//! #[async_std::main]
//! async fn main() {
//! say_hello().await;
//! } //! }
//! ``` //! ```
//! //!
//! Await two futures concurrently, and return a tuple of their output:
//!
#![cfg_attr(feature = "attributes", doc = "```")]
#![cfg_attr(not(feature = "attributes"), doc = "```ignore")]
//! use async_std::prelude::*;
//!
//! #[async_std::main]
//! async fn main() {
//! let a = async { 1u8 };
//! let b = async { 2u8 };
//! assert_eq!(a.join(b).await, (1u8, 2u8))
//! }
//! ```
//!
//! Create a UDP server that echoes back each received message to the sender:
//!
#![cfg_attr(feature = "attributes", doc = "```no_run")]
#![cfg_attr(not(feature = "attributes"), doc = "```ignore")]
//! use async_std::net::UdpSocket;
//!
//! #[async_std::main]
//! async fn main() -> std::io::Result<()> {
//! let socket = UdpSocket::bind("127.0.0.1:8080").await?;
//! println!("Listening on {}", socket.local_addr()?);
//!
//! let mut buf = vec![0u8; 1024];
//!
//! loop {
//! let (recv, peer) = socket.recv_from(&mut buf).await?;
//! let sent = socket.send_to(&buf[..recv], &peer).await?;
//! println!("Sent {} out of {} bytes to {}", sent, recv, peer);
//! }
//! }
//! ```
//! [`task::block_on`]: task/fn.block_on.html
//!
//! # Features //! # Features
//! //!
//! Items marked with //! Items marked with
@ -154,7 +197,7 @@
//! //!
//! ```toml //! ```toml
//! [dependencies.async-std] //! [dependencies.async-std]
//! version = "1.0.0" //! version = "1.6.2"
//! features = ["unstable"] //! features = ["unstable"]
//! ``` //! ```
//! //!
@ -167,20 +210,55 @@
//! //!
//! ```toml //! ```toml
//! [dependencies.async-std] //! [dependencies.async-std]
//! version = "1.0.0" //! version = "1.6.2"
//! features = ["attributes"] //! features = ["attributes"]
//! ``` //! ```
//! //!
//! Compatibility with the `tokio` runtime is possible using the `tokio02`
//! Cargo feature:
//!
//! ```toml
//! [dependencies.async-std]
//! version = "1.6.2"
//! features = ["tokio02"]
//! ```
//!
//! Additionally it's possible to only use the core traits and combinators by //! Additionally it's possible to only use the core traits and combinators by
//! only enabling the `std` Cargo feature: //! only enabling the `std` Cargo feature:
//! //!
//! ```toml //! ```toml
//! [dependencies.async-std] //! [dependencies.async-std]
//! version = "1.0.0" //! version = "1.6.2"
//! default-features = false //! default-features = false
//! features = ["std"] //! features = ["std"]
//! ``` //! ```
//!
//! And to use async-std on `no_std` targets that only support `alloc` only
//! enable the `alloc` Cargo feature:
//!
//! ```toml
//! [dependencies.async-std]
//! version = "1.6.2"
//! default-features = false
//! features = ["alloc"]
//! ```
//!
//! # Runtime configuration
//!
//! Several environment variables are available to tune the async-std
//! runtime:
//!
//! * `ASYNC_STD_THREAD_COUNT`: The number of threads that the
//! async-std runtime will start. By default, this is one per logical
//! cpu as reported by the [num_cpus](num_cpus) crate, which may be
//! different than the number of physical cpus. Async-std _will panic_
//! if this is set to any value other than a positive integer.
//! * `ASYNC_STD_THREAD_NAME`: The name that async-std's runtime
//! threads report to the operating system. The default value is
//! `"async-std/runtime"`.
//!
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "docs", feature(doc_cfg))] #![cfg_attr(feature = "docs", feature(doc_cfg))]
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
#![allow(clippy::mutex_atomic, clippy::module_inception)] #![allow(clippy::mutex_atomic, clippy::module_inception)]
@ -189,6 +267,8 @@
#![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")]
#![recursion_limit = "2048"] #![recursion_limit = "2048"]
extern crate alloc;
#[macro_use] #[macro_use]
mod utils; mod utils;
@ -200,24 +280,31 @@ pub use async_attributes::{main, test};
#[cfg(feature = "std")] #[cfg(feature = "std")]
mod macros; mod macros;
cfg_std! { cfg_alloc! {
pub mod task;
pub mod future; pub mod future;
pub mod stream;
}
cfg_std! {
pub mod io; pub mod io;
pub mod os; pub mod os;
pub mod prelude; pub mod prelude;
pub mod stream;
pub mod sync; pub mod sync;
pub mod task;
} }
cfg_default! { cfg_default! {
#[cfg(not(target_os = "unknown"))]
pub mod fs; pub mod fs;
pub mod path; pub mod path;
pub mod net; pub mod net;
#[cfg(not(target_os = "unknown"))]
pub(crate) mod rt;
} }
cfg_unstable! { cfg_unstable! {
pub mod pin; pub mod pin;
#[cfg(not(target_os = "unknown"))]
pub mod process; pub mod process;
mod unit; mod unit;
@ -226,7 +313,9 @@ cfg_unstable! {
mod option; mod option;
mod string; mod string;
mod collections; mod collections;
}
cfg_unstable_default! {
#[doc(inline)] #[doc(inline)]
pub use std::{write, writeln}; pub use std::{write, writeln};
} }

View file

@ -23,8 +23,8 @@
/// ``` /// ```
/// # async_std::task::block_on(async { /// # async_std::task::block_on(async {
/// # /// #
/// use async_std::prelude::*;
/// use async_std::io; /// use async_std::io;
/// use async_std::prelude::*;
/// use async_std::print; /// use async_std::print;
/// ///
/// print!("this ").await; /// print!("this ").await;
@ -181,8 +181,8 @@ macro_rules! eprintln {
/// # /// #
/// use std::cell::Cell; /// use std::cell::Cell;
/// ///
/// use async_std::task;
/// use async_std::prelude::*; /// use async_std::prelude::*;
/// use async_std::task;
/// ///
/// task_local! { /// task_local! {
/// static VAL: Cell<u32> = Cell::new(5); /// static VAL: Cell<u32> = Cell::new(5);

View file

@ -1,11 +1,12 @@
use std::future::Future;
use std::mem; use std::mem;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
use std::pin::Pin; use std::pin::Pin;
use std::future::Future;
use crate::io; use crate::io;
use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
use crate::utils::Context as ErrorContext;
cfg_not_docs! { cfg_not_docs! {
macro_rules! ret { macro_rules! ret {
@ -67,6 +68,18 @@ pub enum ToSocketAddrsFuture<I> {
Done, Done,
} }
/// Wrap `std::io::Error` with additional message
///
/// Keeps the original error kind and stores the original I/O error as `source`.
impl<T> ErrorContext for ToSocketAddrsFuture<T> {
fn context(self, message: impl Fn() -> String) -> Self {
match self {
ToSocketAddrsFuture::Ready(res) => ToSocketAddrsFuture::Ready(res.context(message)),
x => x,
}
}
}
impl<I: Iterator<Item = SocketAddr>> Future for ToSocketAddrsFuture<I> { impl<I: Iterator<Item = SocketAddr>> Future for ToSocketAddrsFuture<I> {
type Output = io::Result<I>; type Output = io::Result<I>;
@ -110,7 +123,9 @@ impl ToSocketAddrs for SocketAddrV4 {
impl Future<Output = Self::Iter>, impl Future<Output = Self::Iter>,
ToSocketAddrsFuture<Self::Iter> ToSocketAddrsFuture<Self::Iter>
) { ) {
SocketAddr::V4(*self).to_socket_addrs() SocketAddr::V4(*self)
.to_socket_addrs()
.context(|| format!("could not resolve address `{}`", self))
} }
} }
@ -123,7 +138,9 @@ impl ToSocketAddrs for SocketAddrV6 {
impl Future<Output = Self::Iter>, impl Future<Output = Self::Iter>,
ToSocketAddrsFuture<Self::Iter> ToSocketAddrsFuture<Self::Iter>
) { ) {
SocketAddr::V6(*self).to_socket_addrs() SocketAddr::V6(*self)
.to_socket_addrs()
.context(|| format!("could not resolve address `{}`", self))
} }
} }
@ -195,7 +212,9 @@ impl ToSocketAddrs for (&str, u16) {
let host = host.to_string(); let host = host.to_string();
let task = spawn_blocking(move || { let task = spawn_blocking(move || {
std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port)) let addr = (host.as_str(), port);
std::net::ToSocketAddrs::to_socket_addrs(&addr)
.context(|| format!("could not resolve address `{:?}`", addr))
}); });
ToSocketAddrsFuture::Resolving(task) ToSocketAddrsFuture::Resolving(task)
} }
@ -215,7 +234,10 @@ impl ToSocketAddrs for str {
} }
let addr = self.to_string(); let addr = self.to_string();
let task = spawn_blocking(move || std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())); let task = spawn_blocking(move || {
std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())
.context(|| format!("could not resolve address `{:?}`", addr))
});
ToSocketAddrsFuture::Resolving(task) ToSocketAddrsFuture::Resolving(task)
} }
} }

View file

@ -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
}

View file

@ -61,11 +61,16 @@ pub use std::net::Shutdown;
pub use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; pub use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
pub use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; pub use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
#[cfg(not(target_os = "unknown"))]
pub use addr::ToSocketAddrs; pub use addr::ToSocketAddrs;
#[cfg(not(target_os = "unknown"))]
pub use tcp::{Incoming, TcpListener, TcpStream}; pub use tcp::{Incoming, TcpListener, TcpStream};
#[cfg(not(target_os = "unknown"))]
pub use udp::UdpSocket; pub use udp::UdpSocket;
#[cfg(not(target_os = "unknown"))]
mod addr; mod addr;
pub(crate) mod driver; #[cfg(not(target_os = "unknown"))]
mod tcp; mod tcp;
#[cfg(not(target_os = "unknown"))]
mod udp; mod udp;

View file

@ -1,12 +1,13 @@
use std::net::SocketAddr;
use std::future::Future; use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin; use std::pin::Pin;
use crate::future; use smol::Async;
use crate::io; use crate::io;
use crate::net::driver::Watcher;
use crate::net::{TcpStream, ToSocketAddrs}; use crate::net::{TcpStream, ToSocketAddrs};
use crate::stream::Stream; use crate::stream::Stream;
use crate::sync::Arc;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A TCP socket server, listening for connections. /// A TCP socket server, listening for connections.
@ -48,7 +49,7 @@ use crate::task::{Context, Poll};
/// ``` /// ```
#[derive(Debug)] #[derive(Debug)]
pub struct TcpListener { pub struct TcpListener {
watcher: Watcher<mio::net::TcpListener>, watcher: Async<std::net::TcpListener>,
} }
impl TcpListener { impl TcpListener {
@ -74,14 +75,15 @@ impl TcpListener {
/// ///
/// [`local_addr`]: #method.local_addr /// [`local_addr`]: #method.local_addr
pub async fn bind<A: ToSocketAddrs>(addrs: A) -> io::Result<TcpListener> { pub async fn bind<A: ToSocketAddrs>(addrs: A) -> io::Result<TcpListener> {
let mut last_err = None; once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
for addr in addrs.to_socket_addrs().await? { let mut last_err = None;
match mio::net::TcpListener::bind(&addr) { let addrs = addrs.to_socket_addrs().await?;
Ok(mio_listener) => {
return Ok(TcpListener { for addr in addrs {
watcher: Watcher::new(mio_listener), match Async::<std::net::TcpListener>::bind(&addr) {
}); Ok(listener) => {
return Ok(TcpListener { watcher: listener });
} }
Err(err) => last_err = Some(err), Err(err) => last_err = Some(err),
} }
@ -112,13 +114,9 @@ impl TcpListener {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { pub async fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
let (io, addr) = let (stream, addr) = self.watcher.accept().await?;
future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.accept_std()))
.await?;
let mio_stream = mio::net::TcpStream::from_stream(io)?;
let stream = TcpStream { let stream = TcpStream {
watcher: Watcher::new(mio_stream), watcher: Arc::new(stream),
}; };
Ok((stream, addr)) Ok((stream, addr))
} }
@ -204,9 +202,10 @@ impl<'a> Stream for Incoming<'a> {
impl From<std::net::TcpListener> for TcpListener { impl From<std::net::TcpListener> for TcpListener {
/// Converts a `std::net::TcpListener` into its asynchronous equivalent. /// Converts a `std::net::TcpListener` into its asynchronous equivalent.
fn from(listener: std::net::TcpListener) -> TcpListener { fn from(listener: std::net::TcpListener) -> TcpListener {
let mio_listener = mio::net::TcpListener::from_std(listener).unwrap(); once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
TcpListener { TcpListener {
watcher: Watcher::new(mio_listener), watcher: Async::new(listener).expect("TcpListener is known to be good"),
} }
} }
} }
@ -228,29 +227,31 @@ cfg_unix! {
impl IntoRawFd for TcpListener { impl IntoRawFd for TcpListener {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.watcher.into_raw_fd()
} }
} }
} }
cfg_windows! { cfg_windows! {
// use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; use crate::os::windows::io::{
// AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket,
// impl AsRawSocket for TcpListener { };
// fn as_raw_socket(&self) -> RawSocket {
// self.raw_socket impl AsRawSocket for TcpListener {
// } fn as_raw_socket(&self) -> RawSocket {
// } self.watcher.as_raw_socket()
// }
// impl FromRawSocket for TcpListener { }
// unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener {
// net::TcpListener::from_raw_socket(handle).try_into().unwrap() impl FromRawSocket for TcpListener {
// } unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener {
// } std::net::TcpListener::from_raw_socket(handle).into()
// }
// impl IntoRawSocket for TcpListener { }
// fn into_raw_socket(self) -> RawSocket {
// self.raw_socket impl IntoRawSocket for TcpListener {
// } fn into_raw_socket(self) -> RawSocket {
// } self.watcher.into_raw_socket()
}
}
} }

View file

@ -1,12 +1,13 @@
use std::io::{IoSlice, IoSliceMut, Read as _, Write as _}; use std::io::{IoSlice, IoSliceMut};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::pin::Pin; use std::pin::Pin;
use crate::future; use smol::Async;
use crate::io::{self, Read, Write}; use crate::io::{self, Read, Write};
use crate::net::driver::Watcher;
use crate::net::ToSocketAddrs; use crate::net::ToSocketAddrs;
use crate::task::{spawn_blocking, Context, Poll}; use crate::sync::Arc;
use crate::task::{Context, Poll};
/// A TCP stream between a local and a remote socket. /// A TCP stream between a local and a remote socket.
/// ///
@ -22,9 +23,9 @@ use crate::task::{spawn_blocking, Context, Poll};
/// [`connect`]: struct.TcpStream.html#method.connect /// [`connect`]: struct.TcpStream.html#method.connect
/// [accepting]: struct.TcpListener.html#method.accept /// [accepting]: struct.TcpListener.html#method.accept
/// [listener]: struct.TcpListener.html /// [listener]: struct.TcpListener.html
/// [`AsyncRead`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html /// [`AsyncRead`]: https://docs.rs/futures/0.3/futures/io/trait.AsyncRead.html
/// [`AsyncWrite`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncWrite.html /// [`AsyncWrite`]: https://docs.rs/futures/0.3/futures/io/trait.AsyncWrite.html
/// [`futures::io`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/index.html /// [`futures::io`]: https://docs.rs/futures/0.3/futures/io/index.html
/// [`shutdown`]: struct.TcpStream.html#method.shutdown /// [`shutdown`]: struct.TcpStream.html#method.shutdown
/// [`std::net::TcpStream`]: https://doc.rust-lang.org/std/net/struct.TcpStream.html /// [`std::net::TcpStream`]: https://doc.rust-lang.org/std/net/struct.TcpStream.html
/// ///
@ -44,9 +45,9 @@ use crate::task::{spawn_blocking, Context, Poll};
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct TcpStream { pub struct TcpStream {
pub(super) watcher: Watcher<mio::net::TcpStream>, pub(super) watcher: Arc<Async<std::net::TcpStream>>,
} }
impl TcpStream { impl TcpStream {
@ -70,21 +71,22 @@ impl TcpStream {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn connect<A: ToSocketAddrs>(addrs: A) -> io::Result<TcpStream> { pub async fn connect<A: ToSocketAddrs>(addrs: A) -> io::Result<TcpStream> {
once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
let mut last_err = None; let mut last_err = None;
let addrs = addrs.to_socket_addrs().await?;
for addr in addrs.to_socket_addrs().await? { for addr in addrs {
let res = spawn_blocking(move || { match Async::<std::net::TcpStream>::connect(&addr).await {
let std_stream = std::net::TcpStream::connect(addr)?; Ok(stream) => {
let mio_stream = mio::net::TcpStream::from_stream(std_stream)?; return Ok(TcpStream {
Ok(TcpStream { watcher: Arc::new(stream),
watcher: Watcher::new(mio_stream), });
}) }
}) Err(e) => {
.await; last_err = Some(e);
continue;
match res { }
Ok(stream) => return Ok(stream),
Err(err) => last_err = Some(err),
} }
} }
@ -202,7 +204,7 @@ impl TcpStream {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { pub async fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.peek(buf))).await self.watcher.peek(buf).await
} }
/// Gets the value of the `TCP_NODELAY` option on this socket. /// Gets the value of the `TCP_NODELAY` option on this socket.
@ -305,7 +307,7 @@ impl Read for &TcpStream {
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut [u8], buf: &mut [u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
self.watcher.poll_read_with(cx, |mut inner| inner.read(buf)) Pin::new(&mut &*self.watcher).poll_read(cx, buf)
} }
} }
@ -341,25 +343,25 @@ impl Write for &TcpStream {
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &[u8], buf: &[u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
self.watcher Pin::new(&mut &*self.watcher).poll_write(cx, buf)
.poll_write_with(cx, |mut inner| inner.write(buf))
} }
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.watcher.poll_write_with(cx, |mut inner| inner.flush()) Pin::new(&mut &*self.watcher).poll_flush(cx)
} }
fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(())) Pin::new(&mut &*self.watcher).poll_close(cx)
} }
} }
impl From<std::net::TcpStream> for TcpStream { impl From<std::net::TcpStream> for TcpStream {
/// Converts a `std::net::TcpStream` into its asynchronous equivalent. /// Converts a `std::net::TcpStream` into its asynchronous equivalent.
fn from(stream: std::net::TcpStream) -> TcpStream { fn from(stream: std::net::TcpStream) -> TcpStream {
let mio_stream = mio::net::TcpStream::from_stream(stream).unwrap(); once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
TcpStream { TcpStream {
watcher: Watcher::new(mio_stream), watcher: Arc::new(Async::new(stream).expect("TcpStream is known to be good")),
} }
} }
} }
@ -381,29 +383,37 @@ cfg_unix! {
impl IntoRawFd for TcpStream { impl IntoRawFd for TcpStream {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() // TODO(stjepang): This does not mean `RawFd` is now the sole owner of the file
// descriptor because it's possible that there are other clones of this `TcpStream`
// using it at the same time. We should probably document that behavior.
self.as_raw_fd()
} }
} }
} }
cfg_windows! { cfg_windows! {
// use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; use crate::os::windows::io::{
// RawSocket, AsRawSocket, FromRawSocket, IntoRawSocket
// impl AsRawSocket for TcpStream { };
// fn as_raw_socket(&self) -> RawSocket {
// self.raw_socket impl AsRawSocket for TcpStream {
// } fn as_raw_socket(&self) -> RawSocket {
// } self.watcher.get_ref().as_raw_socket()
// }
// impl FromRawSocket for TcpStream { }
// unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream {
// net::TcpStream::from_raw_socket(handle).try_into().unwrap() impl FromRawSocket for TcpStream {
// } unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream {
// } std::net::TcpStream::from_raw_socket(handle).into()
// }
// impl IntoRawSocket for TcpListener { }
// fn into_raw_socket(self) -> RawSocket {
// self.raw_socket impl IntoRawSocket for TcpStream {
// } fn into_raw_socket(self) -> RawSocket {
// } // TODO(stjepang): This does not mean `RawFd` is now the sole owner of the file
// descriptor because it's possible that there are other clones of this `TcpStream`
// using it at the same time. We should probably document that behavior.
self.as_raw_socket()
}
}
} }

View file

@ -2,9 +2,10 @@ use std::io;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use crate::future; use smol::Async;
use crate::net::driver::Watcher;
use crate::net::ToSocketAddrs; use crate::net::ToSocketAddrs;
use crate::utils::Context as _;
/// A UDP socket. /// A UDP socket.
/// ///
@ -44,7 +45,7 @@ use crate::net::ToSocketAddrs;
/// ``` /// ```
#[derive(Debug)] #[derive(Debug)]
pub struct UdpSocket { pub struct UdpSocket {
watcher: Watcher<mio::net::UdpSocket>, watcher: Async<std::net::UdpSocket>,
} }
impl UdpSocket { impl UdpSocket {
@ -66,15 +67,16 @@ impl UdpSocket {
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> { pub async fn bind<A: ToSocketAddrs>(addrs: A) -> io::Result<UdpSocket> {
let mut last_err = None; once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
for addr in addr.to_socket_addrs().await? { let mut last_err = None;
match mio::net::UdpSocket::bind(&addr) { let addrs = addrs.to_socket_addrs().await?;
Ok(mio_socket) => {
return Ok(UdpSocket { for addr in addrs {
watcher: Watcher::new(mio_socket), match Async::<std::net::UdpSocket>::bind(&addr) {
}); Ok(socket) => {
return Ok(UdpSocket { watcher: socket });
} }
Err(err) => last_err = Some(err), Err(err) => last_err = Some(err),
} }
@ -88,6 +90,32 @@ impl UdpSocket {
})) }))
} }
/// Returns the peer address that this listener is connected to.
///
/// This can be useful, for example, when connect to port 0 to figure out which port was
/// actually connected.
///
/// # Examples
///
/// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::net::UdpSocket;
///
/// let socket1 = UdpSocket::bind("127.0.0.1:0").await?;
/// let socket2 = UdpSocket::bind("127.0.0.1:0").await?;
/// socket1.connect(socket2.local_addr()?).await?;
/// let addr = socket1.peer_addr()?;
/// #
/// # Ok(()) }) }
/// ```
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.watcher
.get_ref()
.peer_addr()
.context(|| String::from("could not get peer address"))
}
/// Returns the local address that this listener is bound to. /// Returns the local address that this listener is bound to.
/// ///
/// This can be useful, for example, when binding to port 0 to figure out which port was /// This can be useful, for example, when binding to port 0 to figure out which port was
@ -106,7 +134,10 @@ impl UdpSocket {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn local_addr(&self) -> io::Result<SocketAddr> { pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.watcher.get_ref().local_addr() self.watcher
.get_ref()
.local_addr()
.context(|| String::from("could not get local address"))
} }
/// Sends data on the socket to the given address. /// Sends data on the socket to the given address.
@ -146,11 +177,10 @@ impl UdpSocket {
} }
}; };
future::poll_fn(|cx| {
self.watcher self.watcher
.poll_write_with(cx, |inner| inner.send_to(buf, &addr)) .send_to(buf, addr)
})
.await .await
.context(|| format!("could not send packet to {}", addr))
} }
/// Receives data from the socket. /// Receives data from the socket.
@ -173,11 +203,7 @@ impl UdpSocket {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
future::poll_fn(|cx| { self.watcher.recv_from(buf).await
self.watcher
.poll_read_with(cx, |inner| inner.recv_from(buf))
})
.await
} }
/// Connects the UDP socket to a remote address. /// Connects the UDP socket to a remote address.
@ -204,8 +230,12 @@ impl UdpSocket {
/// ``` /// ```
pub async fn connect<A: ToSocketAddrs>(&self, addrs: A) -> io::Result<()> { pub async fn connect<A: ToSocketAddrs>(&self, addrs: A) -> io::Result<()> {
let mut last_err = None; let mut last_err = None;
let addrs = addrs
.to_socket_addrs()
.await
.context(|| String::from("could not resolve addresses"))?;
for addr in addrs.to_socket_addrs().await? { for addr in addrs {
// TODO(stjepang): connect on the blocking pool // TODO(stjepang): connect on the blocking pool
match self.watcher.get_ref().connect(addr) { match self.watcher.get_ref().connect(addr) {
Ok(()) => return Ok(()), Ok(()) => return Ok(()),
@ -221,9 +251,12 @@ impl UdpSocket {
})) }))
} }
/// Sends data on the socket to the given address. /// Sends data on the socket to the remote address to which it is connected.
/// ///
/// On success, returns the number of bytes written. /// The [`connect`] method will connect this socket to a remote address.
/// This method will fail if the socket is not connected.
///
/// [`connect`]: #method.connect
/// ///
/// # Examples /// # Examples
/// ///
@ -232,28 +265,21 @@ impl UdpSocket {
/// # /// #
/// use async_std::net::UdpSocket; /// use async_std::net::UdpSocket;
/// ///
/// const THE_MERCHANT_OF_VENICE: &[u8] = b" /// let socket = UdpSocket::bind("127.0.0.1:34254").await?;
/// If you prick us, do we not bleed? /// socket.connect("127.0.0.1:8080").await?;
/// If you tickle us, do we not laugh? /// let bytes = socket.send(b"Hi there!").await?;
/// If you poison us, do we not die?
/// And if you wrong us, shall we not revenge?
/// ";
/// ///
/// let socket = UdpSocket::bind("127.0.0.1:0").await?; /// println!("Sent {} bytes", bytes);
///
/// let addr = "127.0.0.1:7878";
/// let sent = socket.send_to(THE_MERCHANT_OF_VENICE, &addr).await?;
/// println!("Sent {} bytes to {}", sent, addr);
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn send(&self, buf: &[u8]) -> io::Result<usize> { pub async fn send(&self, buf: &[u8]) -> io::Result<usize> {
future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))).await self.watcher.send(buf).await
} }
/// Receives data from the socket. /// Receives data from the socket.
/// ///
/// On success, returns the number of bytes read and the origin. /// On success, returns the number of bytes read.
/// ///
/// # Examples /// # Examples
/// ///
@ -263,15 +289,16 @@ impl UdpSocket {
/// use async_std::net::UdpSocket; /// use async_std::net::UdpSocket;
/// ///
/// let socket = UdpSocket::bind("127.0.0.1:0").await?; /// let socket = UdpSocket::bind("127.0.0.1:0").await?;
/// socket.connect("127.0.0.1:8080").await?;
/// ///
/// let mut buf = vec![0; 1024]; /// let mut buf = vec![0; 1024];
/// let (n, peer) = socket.recv_from(&mut buf).await?; /// let n = socket.recv(&mut buf).await?;
/// println!("Received {} bytes from {}", n, peer); /// println!("Received {} bytes", n);
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn recv(&self, buf: &mut [u8]) -> io::Result<usize> { pub async fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))).await self.watcher.recv(buf).await
} }
/// Gets the value of the `SO_BROADCAST` option for this socket. /// Gets the value of the `SO_BROADCAST` option for this socket.
@ -454,9 +481,10 @@ impl UdpSocket {
impl From<std::net::UdpSocket> for UdpSocket { impl From<std::net::UdpSocket> for UdpSocket {
/// Converts a `std::net::UdpSocket` into its asynchronous equivalent. /// Converts a `std::net::UdpSocket` into its asynchronous equivalent.
fn from(socket: std::net::UdpSocket) -> UdpSocket { fn from(socket: std::net::UdpSocket) -> UdpSocket {
let mio_socket = mio::net::UdpSocket::from_socket(socket).unwrap(); once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
UdpSocket { UdpSocket {
watcher: Watcher::new(mio_socket), watcher: Async::new(socket).expect("UdpSocket is known to be good"),
} }
} }
} }
@ -478,29 +506,31 @@ cfg_unix! {
impl IntoRawFd for UdpSocket { impl IntoRawFd for UdpSocket {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.watcher.into_raw_fd()
} }
} }
} }
cfg_windows! { cfg_windows! {
// use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; use crate::os::windows::io::{
// RawSocket, AsRawSocket, IntoRawSocket, FromRawSocket
// impl AsRawSocket for UdpSocket { };
// fn as_raw_socket(&self) -> RawSocket {
// self.raw_socket impl AsRawSocket for UdpSocket {
// } fn as_raw_socket(&self) -> RawSocket {
// } self.watcher.get_ref().as_raw_socket()
// }
// impl FromRawSocket for UdpSocket { }
// unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket {
// net::UdpSocket::from_raw_socket(handle).into() impl FromRawSocket for UdpSocket {
// } unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket {
// } std::net::UdpSocket::from_raw_socket(handle).into()
// }
// impl IntoRawSocket for UdpSocket { }
// fn into_raw_socket(self) -> RawSocket {
// self.raw_socket impl IntoRawSocket for UdpSocket {
// } fn into_raw_socket(self) -> RawSocket {
// } self.watcher.into_raw_socket()
}
}
} }

View file

@ -2,8 +2,9 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{FromStream, IntoStream}; use crate::stream::{FromStream, IntoStream};
use std::convert::identity;
impl<T, V> FromStream<Option<T>> for Option<V> impl<T: Send, V> FromStream<Option<T>> for Option<V>
where where
V: FromStream<T>, V: FromStream<T>,
{ {
@ -13,30 +14,29 @@ where
#[inline] #[inline]
fn from_stream<'a, S: IntoStream<Item = Option<T>> + 'a>( fn from_stream<'a, S: IntoStream<Item = Option<T>> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = Self> + 'a>> { ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
where
<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); // Using `take_while` here because it is able to stop the stream early
// Using `scan` here because it is able to stop the stream early
// if a failure occurs // if a failure occurs
let mut found_error = false; let mut found_none = false;
let out: V = stream let out: V = stream
.scan((), |_, elem| { .take_while(|elem| {
match elem { elem.is_some() || {
Some(elem) => Some(elem), found_none = true;
None => { // Stop processing the stream on `None`
found_error = true; false
// Stop processing the stream on error
None
}
} }
}) })
.filter_map(identity)
.collect() .collect()
.await; .await;
if found_error { None } else { Some(out) } if found_none { None } else { Some(out) }
}) })
} }
} }

View file

@ -1,7 +1,8 @@
use std::pin::Pin; use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Stream, Product}; use crate::stream::{Product, Stream};
use std::convert::identity;
impl<T, U> Product<Option<U>> for Option<T> impl<T, U> Product<Option<U>> for Option<T>
where where
@ -36,31 +37,27 @@ where
``` ```
"#] "#]
fn product<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Option<T>> + 'a>> fn product<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Option<T>> + 'a>>
where S: Stream<Item = Option<U>> + 'a where
S: Stream<Item = Option<U>> + 'a,
{ {
Box::pin(async move { Box::pin(async move {
pin_utils::pin_mut!(stream); // Using `take_while` here because it is able to stop the stream early
// Using `scan` here because it is able to stop the stream early
// if a failure occurs // if a failure occurs
let mut found_none = false; let mut found_none = false;
let out = <T as Product<U>>::product(stream let out = <T as Product<U>>::product(
.scan((), |_, elem| { stream
match elem { .take_while(|elem| {
Some(elem) => Some(elem), elem.is_some() || {
None => {
found_none = true; found_none = true;
// Stop processing the stream on error // Stop processing the stream on `None`
None false
} }
} })
})).await; .filter_map(identity),
)
.await;
if found_none { if found_none { None } else { Some(out) }
None
} else {
Some(out)
}
}) })
} }
} }

View file

@ -2,6 +2,7 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Stream, Sum}; use crate::stream::{Stream, Sum};
use std::convert::identity;
impl<T, U> Sum<Option<U>> for Option<T> impl<T, U> Sum<Option<U>> for Option<T>
where where
@ -31,31 +32,27 @@ where
``` ```
"#] "#]
fn sum<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Option<T>> + 'a>> fn sum<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Option<T>> + 'a>>
where S: Stream<Item = Option<U>> + 'a where
S: Stream<Item = Option<U>> + 'a,
{ {
Box::pin(async move { Box::pin(async move {
pin_utils::pin_mut!(stream); // Using `take_while` here because it is able to stop the stream early
// Using `scan` here because it is able to stop the stream early
// if a failure occurs // if a failure occurs
let mut found_none = false; let mut found_none = false;
let out = <T as Sum<U>>::sum(stream let out = <T as Sum<U>>::sum(
.scan((), |_, elem| { stream
match elem { .take_while(|elem| {
Some(elem) => Some(elem), elem.is_some() || {
None => {
found_none = true; found_none = true;
// Stop processing the stream on error // Stop processing the stream on `None`
None false
} }
} })
})).await; .filter_map(identity),
)
.await;
if found_none { if found_none { None } else { Some(out) }
None
} else {
Some(out)
}
}) })
} }
} }

View file

@ -2,16 +2,14 @@
use std::fmt; use std::fmt;
use std::net::Shutdown; use std::net::Shutdown;
use std::os::unix::net::UnixDatagram as StdUnixDatagram;
use mio_uds; use smol::Async;
use super::SocketAddr; use super::SocketAddr;
use crate::future;
use crate::io; use crate::io;
use crate::net::driver::Watcher;
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking;
/// A Unix datagram socket. /// A Unix datagram socket.
/// ///
@ -42,13 +40,15 @@ use crate::task::spawn_blocking;
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub struct UnixDatagram { pub struct UnixDatagram {
watcher: Watcher<mio_uds::UnixDatagram>, watcher: Async<StdUnixDatagram>,
} }
impl UnixDatagram { impl UnixDatagram {
fn new(socket: mio_uds::UnixDatagram) -> UnixDatagram { fn new(socket: StdUnixDatagram) -> UnixDatagram {
once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
UnixDatagram { UnixDatagram {
watcher: Watcher::new(socket), watcher: Async::new(socket).expect("UnixDatagram is known to be good"),
} }
} }
@ -66,9 +66,11 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> { pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> {
once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let socket = spawn_blocking(move || mio_uds::UnixDatagram::bind(path)).await?; let socket = Async::<StdUnixDatagram>::bind(path)?;
Ok(UnixDatagram::new(socket)) Ok(UnixDatagram { watcher: socket })
} }
/// Creates a Unix datagram which is not bound to any address. /// Creates a Unix datagram which is not bound to any address.
@ -85,7 +87,7 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn unbound() -> io::Result<UnixDatagram> { pub fn unbound() -> io::Result<UnixDatagram> {
let socket = mio_uds::UnixDatagram::unbound()?; let socket = StdUnixDatagram::unbound()?;
Ok(UnixDatagram::new(socket)) Ok(UnixDatagram::new(socket))
} }
@ -105,7 +107,7 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> {
let (a, b) = mio_uds::UnixDatagram::pair()?; let (a, b) = StdUnixDatagram::pair()?;
let a = UnixDatagram::new(a); let a = UnixDatagram::new(a);
let b = UnixDatagram::new(b); let b = UnixDatagram::new(b);
Ok((a, b)) Ok((a, b))
@ -197,11 +199,7 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
future::poll_fn(|cx| { self.watcher.recv_from(buf).await
self.watcher
.poll_read_with(cx, |inner| inner.recv_from(buf))
})
.await
} }
/// Receives data from the socket. /// Receives data from the socket.
@ -222,7 +220,7 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn recv(&self, buf: &mut [u8]) -> io::Result<usize> { pub async fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))).await self.watcher.recv(buf).await
} }
/// Sends data on the socket to the specified address. /// Sends data on the socket to the specified address.
@ -242,11 +240,7 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> { pub async fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> {
future::poll_fn(|cx| { self.watcher.send_to(buf, path.as_ref()).await
self.watcher
.poll_write_with(cx, |inner| inner.send_to(buf, path.as_ref()))
})
.await
} }
/// Sends data on the socket to the socket's peer. /// Sends data on the socket to the socket's peer.
@ -267,7 +261,7 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn send(&self, buf: &[u8]) -> io::Result<usize> { pub async fn send(&self, buf: &[u8]) -> io::Result<usize> {
future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))).await self.watcher.send(buf).await
} }
/// Shut down the read, write, or both halves of this connection. /// Shut down the read, write, or both halves of this connection.
@ -312,31 +306,35 @@ impl fmt::Debug for UnixDatagram {
} }
} }
impl From<std::os::unix::net::UnixDatagram> for UnixDatagram { impl From<StdUnixDatagram> for UnixDatagram {
/// Converts a `std::os::unix::net::UnixDatagram` into its asynchronous equivalent. /// Converts a `std::os::unix::net::UnixDatagram` into its asynchronous equivalent.
fn from(datagram: std::os::unix::net::UnixDatagram) -> UnixDatagram { fn from(datagram: StdUnixDatagram) -> UnixDatagram {
let mio_datagram = mio_uds::UnixDatagram::from_datagram(datagram).unwrap(); once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
UnixDatagram { UnixDatagram {
watcher: Watcher::new(mio_datagram), watcher: Async::new(datagram).expect("UnixDatagram is known to be good"),
} }
} }
} }
impl AsRawFd for UnixDatagram { impl AsRawFd for UnixDatagram {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.watcher.get_ref().as_raw_fd() self.watcher.as_raw_fd()
} }
} }
impl FromRawFd for UnixDatagram { impl FromRawFd for UnixDatagram {
unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram {
let datagram = std::os::unix::net::UnixDatagram::from_raw_fd(fd); once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
datagram.into()
let raw = StdUnixDatagram::from_raw_fd(fd);
let datagram = Async::<StdUnixDatagram>::new(raw).expect("invalid file descriptor");
UnixDatagram { watcher: datagram }
} }
} }
impl IntoRawFd for UnixDatagram { impl IntoRawFd for UnixDatagram {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.watcher.into_raw_fd()
} }
} }

View file

@ -1,20 +1,20 @@
//! Unix-specific networking extensions. //! Unix-specific networking extensions.
use std::fmt; use std::fmt;
use std::pin::Pin;
use std::future::Future; use std::future::Future;
use std::os::unix::net::UnixListener as StdUnixListener;
use std::pin::Pin;
use mio_uds; use smol::Async;
use super::SocketAddr; use super::SocketAddr;
use super::UnixStream; use super::UnixStream;
use crate::future;
use crate::io; use crate::io;
use crate::net::driver::Watcher;
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::path::Path; use crate::path::Path;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{spawn_blocking, Context, Poll}; use crate::sync::Arc;
use crate::task::{Context, Poll};
/// A Unix domain socket server, listening for connections. /// A Unix domain socket server, listening for connections.
/// ///
@ -50,7 +50,7 @@ use crate::task::{spawn_blocking, Context, Poll};
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub struct UnixListener { pub struct UnixListener {
watcher: Watcher<mio_uds::UnixListener>, watcher: Async<StdUnixListener>,
} }
impl UnixListener { impl UnixListener {
@ -68,12 +68,12 @@ impl UnixListener {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> { pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> {
let path = path.as_ref().to_owned(); once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
let listener = spawn_blocking(move || mio_uds::UnixListener::bind(path)).await?;
Ok(UnixListener { let path = path.as_ref().to_owned();
watcher: Watcher::new(listener), let listener = Async::<StdUnixListener>::bind(path)?;
})
Ok(UnixListener { watcher: listener })
} }
/// Accepts a new incoming connection to this listener. /// Accepts a new incoming connection to this listener.
@ -93,29 +93,14 @@ impl UnixListener {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> {
future::poll_fn(|cx| { let (stream, addr) = self.watcher.accept().await?;
let res = futures_core::ready!(self.watcher.poll_read_with(cx, |inner| {
match inner.accept_std() {
// Converting to `WouldBlock` so that the watcher will
// add the waker of this task to a list of readers.
Ok(None) => Err(io::ErrorKind::WouldBlock.into()),
res => res,
}
}));
match res? { Ok((
Some((io, addr)) => { UnixStream {
let mio_stream = mio_uds::UnixStream::from_stream(io)?; watcher: Arc::new(stream),
let stream = UnixStream { },
watcher: Watcher::new(mio_stream), addr,
}; ))
Poll::Ready(Ok((stream, addr)))
}
// This should never happen since `None` is converted to `WouldBlock`
None => unreachable!(),
}
})
.await
} }
/// Returns a stream of incoming connections. /// Returns a stream of incoming connections.
@ -206,19 +191,20 @@ impl Stream for Incoming<'_> {
} }
} }
impl From<std::os::unix::net::UnixListener> for UnixListener { impl From<StdUnixListener> for UnixListener {
/// Converts a `std::os::unix::net::UnixListener` into its asynchronous equivalent. /// Converts a `std::os::unix::net::UnixListener` into its asynchronous equivalent.
fn from(listener: std::os::unix::net::UnixListener) -> UnixListener { fn from(listener: StdUnixListener) -> UnixListener {
let mio_listener = mio_uds::UnixListener::from_listener(listener).unwrap(); once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
UnixListener { UnixListener {
watcher: Watcher::new(mio_listener), watcher: Async::new(listener).expect("UnixListener is known to be good"),
} }
} }
} }
impl AsRawFd for UnixListener { impl AsRawFd for UnixListener {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.watcher.get_ref().as_raw_fd() self.watcher.as_raw_fd()
} }
} }
@ -231,6 +217,6 @@ impl FromRawFd for UnixListener {
impl IntoRawFd for UnixListener { impl IntoRawFd for UnixListener {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.watcher.into_raw_fd()
} }
} }

View file

@ -1,18 +1,18 @@
//! Unix-specific networking extensions. //! Unix-specific networking extensions.
use std::fmt; use std::fmt;
use std::io::{Read as _, Write as _};
use std::net::Shutdown; use std::net::Shutdown;
use std::os::unix::net::UnixStream as StdUnixStream;
use std::pin::Pin; use std::pin::Pin;
use mio_uds; use smol::Async;
use super::SocketAddr; use super::SocketAddr;
use crate::io::{self, Read, Write}; use crate::io::{self, Read, Write};
use crate::net::driver::Watcher;
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::path::Path; use crate::path::Path;
use crate::task::{spawn_blocking, Context, Poll}; use crate::sync::Arc;
use crate::task::{Context, Poll};
/// A Unix stream socket. /// A Unix stream socket.
/// ///
@ -37,8 +37,9 @@ use crate::task::{spawn_blocking, Context, Poll};
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[derive(Clone)]
pub struct UnixStream { pub struct UnixStream {
pub(super) watcher: Watcher<mio_uds::UnixStream>, pub(super) watcher: Arc<Async<StdUnixStream>>,
} }
impl UnixStream { impl UnixStream {
@ -56,16 +57,12 @@ impl UnixStream {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> { pub async fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> {
let path = path.as_ref().to_owned(); once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
spawn_blocking(move || { let path = path.as_ref().to_owned();
let std_stream = std::os::unix::net::UnixStream::connect(path)?; let stream = Arc::new(Async::<StdUnixStream>::connect(path).await?);
let mio_stream = mio_uds::UnixStream::from_stream(std_stream)?;
Ok(UnixStream { Ok(UnixStream { watcher: stream })
watcher: Watcher::new(mio_stream),
})
})
.await
} }
/// Creates an unnamed pair of connected sockets. /// Creates an unnamed pair of connected sockets.
@ -84,12 +81,14 @@ impl UnixStream {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn pair() -> io::Result<(UnixStream, UnixStream)> { pub fn pair() -> io::Result<(UnixStream, UnixStream)> {
let (a, b) = mio_uds::UnixStream::pair()?; once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
let (a, b) = Async::<StdUnixStream>::pair()?;
let a = UnixStream { let a = UnixStream {
watcher: Watcher::new(a), watcher: Arc::new(a),
}; };
let b = UnixStream { let b = UnixStream {
watcher: Watcher::new(b), watcher: Arc::new(b),
}; };
Ok((a, b)) Ok((a, b))
} }
@ -169,7 +168,7 @@ impl Read for &UnixStream {
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut [u8], buf: &mut [u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
self.watcher.poll_read_with(cx, |mut inner| inner.read(buf)) Pin::new(&mut &*self.watcher).poll_read(cx, buf)
} }
} }
@ -197,16 +196,15 @@ impl Write for &UnixStream {
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &[u8], buf: &[u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
self.watcher Pin::new(&mut &*self.watcher).poll_write(cx, buf)
.poll_write_with(cx, |mut inner| inner.write(buf))
} }
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.watcher.poll_write_with(cx, |mut inner| inner.flush()) Pin::new(&mut &*self.watcher).poll_flush(cx)
} }
fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(())) Pin::new(&mut &*self.watcher).poll_close(cx)
} }
} }
@ -227,19 +225,21 @@ impl fmt::Debug for UnixStream {
} }
} }
impl From<std::os::unix::net::UnixStream> for UnixStream { impl From<StdUnixStream> for UnixStream {
/// Converts a `std::os::unix::net::UnixStream` into its asynchronous equivalent. /// Converts a `std::os::unix::net::UnixStream` into its asynchronous equivalent.
fn from(stream: std::os::unix::net::UnixStream) -> UnixStream { fn from(stream: StdUnixStream) -> UnixStream {
let mio_stream = mio_uds::UnixStream::from_stream(stream).unwrap(); once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
let stream = Async::new(stream).expect("UnixStream is known to be good");
UnixStream { UnixStream {
watcher: Watcher::new(mio_stream), watcher: Arc::new(stream),
} }
} }
} }
impl AsRawFd for UnixStream { impl AsRawFd for UnixStream {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.watcher.get_ref().as_raw_fd() self.watcher.as_raw_fd()
} }
} }
@ -252,6 +252,6 @@ impl FromRawFd for UnixStream {
impl IntoRawFd for UnixStream { impl IntoRawFd for UnixStream {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.as_raw_fd()
} }
} }

55
src/os/windows/fs.rs Normal file
View file

@ -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
}

View file

@ -2,7 +2,8 @@
cfg_not_docs! { cfg_not_docs! {
pub use std::os::windows::io::{ pub use std::os::windows::io::{
AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle,
AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket,
}; };
} }
@ -45,4 +46,33 @@ cfg_docs! {
/// it once it's no longer needed. /// it once it's no longer needed.
fn into_raw_handle(self) -> RawHandle; fn into_raw_handle(self) -> RawHandle;
} }
/// Creates I/O objects from raw sockets.
pub trait FromRawSocket {
/// Creates a new I/O object from the given raw socket.
///
/// This function will consume ownership of the socket provided and it will be closed when the returned object goes out of scope.
///
/// This function is also unsafe as the primitives currently returned have the contract that they are the sole owner of the
/// file descriptor they are wrapping. Usage of this function could accidentally allow violating this contract which can cause
/// memory unsafety in code that relies on it being true.
unsafe fn from_raw_socket(sock: RawSocket) -> Self;
}
/// Extracts raw sockets.
pub trait AsRawSocket {
/// Extracts the underlying raw socket from this object.
fn as_raw_socket(&self) -> RawSocket;
}
/// A trait to express the ability to consume an object and acquire ownership of
/// its raw `SOCKET`.
pub trait IntoRawSocket {
/// Consumes this object, returning the raw underlying socket.
///
/// This function **transfers ownership** of the underlying socket to the
/// caller. Callers are then the unique owners of the socket and must close
/// it once it's no longer needed.
fn into_raw_socket(self) -> RawSocket;
}
} }

View file

@ -3,3 +3,7 @@
cfg_std! { cfg_std! {
pub mod io; pub mod io;
} }
cfg_unstable! {
pub mod fs;
}

View file

@ -4,9 +4,9 @@ use std::ffi::{OsStr, OsString};
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use crate::fs;
use crate::io;
use crate::path::{Ancestors, Components, Display, Iter, PathBuf, StripPrefixError}; use crate::path::{Ancestors, Components, Display, Iter, PathBuf, StripPrefixError};
#[cfg(not(target_os = "unknown"))]
use crate::{fs, io};
/// A slice of a path. /// A slice of a path.
/// ///
@ -584,6 +584,7 @@ impl Path {
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg(not(target_os = "unknown"))]
pub async fn metadata(&self) -> io::Result<fs::Metadata> { pub async fn metadata(&self) -> io::Result<fs::Metadata> {
fs::metadata(self).await fs::metadata(self).await
} }
@ -607,6 +608,7 @@ impl Path {
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg(not(target_os = "unknown"))]
pub async fn symlink_metadata(&self) -> io::Result<fs::Metadata> { pub async fn symlink_metadata(&self) -> io::Result<fs::Metadata> {
fs::symlink_metadata(self).await fs::symlink_metadata(self).await
} }
@ -632,6 +634,7 @@ impl Path {
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg(not(target_os = "unknown"))]
pub async fn canonicalize(&self) -> io::Result<PathBuf> { pub async fn canonicalize(&self) -> io::Result<PathBuf> {
fs::canonicalize(self).await fs::canonicalize(self).await
} }
@ -654,6 +657,7 @@ impl Path {
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg(not(target_os = "unknown"))]
pub async fn read_link(&self) -> io::Result<PathBuf> { pub async fn read_link(&self) -> io::Result<PathBuf> {
fs::read_link(self).await fs::read_link(self).await
} }
@ -688,6 +692,7 @@ impl Path {
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg(not(target_os = "unknown"))]
pub async fn read_dir(&self) -> io::Result<fs::ReadDir> { pub async fn read_dir(&self) -> io::Result<fs::ReadDir> {
fs::read_dir(self).await fs::read_dir(self).await
} }
@ -717,6 +722,7 @@ impl Path {
/// check errors, call [fs::metadata]. /// check errors, call [fs::metadata].
/// ///
/// [fs::metadata]: ../fs/fn.metadata.html /// [fs::metadata]: ../fs/fn.metadata.html
#[cfg(not(target_os = "unknown"))]
pub async fn exists(&self) -> bool { pub async fn exists(&self) -> bool {
fs::metadata(self).await.is_ok() fs::metadata(self).await.is_ok()
} }
@ -749,6 +755,7 @@ impl Path {
/// ///
/// [fs::metadata]: ../fs/fn.metadata.html /// [fs::metadata]: ../fs/fn.metadata.html
/// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file /// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file
#[cfg(not(target_os = "unknown"))]
pub async fn is_file(&self) -> bool { pub async fn is_file(&self) -> bool {
fs::metadata(self) fs::metadata(self)
.await .await
@ -785,6 +792,7 @@ impl Path {
/// ///
/// [fs::metadata]: ../fs/fn.metadata.html /// [fs::metadata]: ../fs/fn.metadata.html
/// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir /// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir
#[cfg(not(target_os = "unknown"))]
pub async fn is_dir(&self) -> bool { pub async fn is_dir(&self) -> bool {
fs::metadata(self) fs::metadata(self)
.await .await

View file

@ -323,7 +323,10 @@ impl<P: AsRef<Path>> stream::Extend<P> for PathBuf {
fn extend<'a, S: IntoStream<Item = P> + 'a>( fn extend<'a, S: IntoStream<Item = P> + '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(async move { Box::pin(async move {
@ -337,15 +340,17 @@ impl<P: AsRef<Path>> stream::Extend<P> for PathBuf {
} }
#[cfg(feature = "unstable")] #[cfg(feature = "unstable")]
impl<'b, P: AsRef<Path> + 'b> FromStream<P> for PathBuf { impl<'b, P: AsRef<Path> + 'b + Send> FromStream<P> for PathBuf {
#[inline] #[inline]
fn from_stream<'a, S: IntoStream<Item = P> + 'a>( fn from_stream<'a, S: IntoStream<Item = P> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = Self> + 'a>> { ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
Box::pin(async move { where
<S as IntoStream>::IntoStream: Send,
{
let stream = stream.into_stream(); let stream = stream.into_stream();
pin_utils::pin_mut!(stream);
Box::pin(async move {
let mut out = Self::new(); let mut out = Self::new();
stream::extend(&mut out, stream).await; stream::extend(&mut out, stream).await;
out out

View file

@ -5,40 +5,68 @@ use crate::stream::{FromStream, IntoStream};
impl<T, E, V> FromStream<Result<T, E>> for Result<V, E> impl<T, E, V> FromStream<Result<T, E>> for Result<V, E>
where where
T: Send,
E: Send,
V: FromStream<T>, V: FromStream<T>,
{ {
/// Takes each element in the stream: if it is an `Err`, no further /// Takes each element in the stream: if it is an `Err`, no further
/// elements are taken, and the `Err` is returned. Should no `Err` /// elements are taken, and the `Err` is returned. Should no `Err`
/// occur, a container with the values of each `Result` is returned. /// occur, a container with the values of each `Result` is returned.
///
/// # Examples
///
/// ```
/// # fn main() { async_std::task::block_on(async {
/// #
/// use async_std::prelude::*;
/// use async_std::stream;
///
/// let v = stream::from_iter(vec![1, 2]);
/// let res: Result<Vec<u32>, &'static str> = v.map(|x: u32|
/// x.checked_add(1).ok_or("Overflow!")
/// ).collect().await;
/// assert_eq!(res, Ok(vec![2, 3]));
/// #
/// # }) }
/// ```
#[inline] #[inline]
fn from_stream<'a, S: IntoStream<Item = Result<T, E>> + 'a>( fn from_stream<'a, S: IntoStream<Item = Result<T, E>> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = Self> + 'a>> { ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
where
<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); // Using `take_while` here because it is able to stop the stream early
// Using `scan` here because it is able to stop the stream early
// if a failure occurs // if a failure occurs
let mut is_error = false;
let mut found_error = None; let mut found_error = None;
let out: V = stream let out: V = stream
.scan((), |_, elem| { .take_while(|elem| {
match elem { // Stop processing the stream on `Err`
Ok(elem) => Some(elem), !is_error
&& (elem.is_ok() || {
is_error = true;
// Capture first `Err`
true
})
})
.filter_map(|elem| match elem {
Ok(value) => Some(value),
Err(err) => { Err(err) => {
found_error = Some(err); found_error = Some(err);
// Stop processing the stream on error
None None
} }
}
}) })
.collect() .collect()
.await; .await;
match found_error { if is_error {
Some(err) => Err(err), Err(found_error.unwrap())
None => Ok(out), } else {
Ok(out)
} }
}) })
} }

View file

@ -1,7 +1,7 @@
use std::pin::Pin; use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Stream, Product}; use crate::stream::{Product, Stream};
impl<T, U, E> Product<Result<U, E>> for Result<T, E> impl<T, U, E> Product<Result<U, E>> for Result<T, E>
where where
@ -36,28 +36,39 @@ where
``` ```
"#] "#]
fn product<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Result<T, E>> + 'a>> fn product<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Result<T, E>> + 'a>>
where S: Stream<Item = Result<U, E>> + 'a where
S: Stream<Item = Result<U, E>> + 'a,
{ {
Box::pin(async move { Box::pin(async move {
pin_utils::pin_mut!(stream); // Using `take_while` here because it is able to stop the stream early
// Using `scan` here because it is able to stop the stream early
// if a failure occurs // if a failure occurs
let mut is_error = false;
let mut found_error = None; let mut found_error = None;
let out = <T as Product<U>>::product(stream let out = <T as Product<U>>::product(
.scan((), |_, elem| { stream
match elem { .take_while(|elem| {
Ok(elem) => Some(elem), // Stop processing the stream on `Err`
!is_error
&& (elem.is_ok() || {
is_error = true;
// Capture first `Err`
true
})
})
.filter_map(|elem| match elem {
Ok(value) => Some(value),
Err(err) => { Err(err) => {
found_error = Some(err); found_error = Some(err);
// Stop processing the stream on error
None None
} }
} }),
})).await; )
match found_error { .await;
Some(err) => Err(err),
None => Ok(out) if is_error {
Err(found_error.unwrap())
} else {
Ok(out)
} }
}) })
} }

View file

@ -36,28 +36,39 @@ where
``` ```
"#] "#]
fn sum<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Result<T, E>> + 'a>> fn sum<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Result<T, E>> + 'a>>
where S: Stream<Item = Result<U, E>> + 'a where
S: Stream<Item = Result<U, E>> + 'a,
{ {
Box::pin(async move { Box::pin(async move {
pin_utils::pin_mut!(stream); // Using `take_while` here because it is able to stop the stream early
// Using `scan` here because it is able to stop the stream early
// if a failure occurs // if a failure occurs
let mut is_error = false;
let mut found_error = None; let mut found_error = None;
let out = <T as Sum<U>>::sum(stream let out = <T as Sum<U>>::sum(
.scan((), |_, elem| { stream
match elem { .take_while(|elem| {
Ok(elem) => Some(elem), // Stop processing the stream on `Err`
!is_error
&& (elem.is_ok() || {
is_error = true;
// Capture first `Err`
true
})
})
.filter_map(|elem| match elem {
Ok(value) => Some(value),
Err(err) => { Err(err) => {
found_error = Some(err); found_error = Some(err);
// Stop processing the stream on error
None None
} }
} }),
})).await; )
match found_error { .await;
Some(err) => Err(err),
None => Ok(out) if is_error {
Err(found_error.unwrap())
} else {
Ok(out)
} }
}) })
} }

34
src/rt/mod.rs Normal file
View file

@ -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 {}
});

Some files were not shown because too many files have changed in this diff Show more