зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1858739 - update tokio to 1.29.1 r=glandium,supply-chain-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D190838
This commit is contained in:
Родитель
a3a60fed58
Коммит
c8d62f7b85
|
@ -351,6 +351,10 @@ dependencies = [
|
|||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.999"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.999"
|
||||
|
@ -5544,18 +5548,19 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.17.0"
|
||||
version = "1.29.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
|
||||
checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio 0.8.8",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"winapi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -132,6 +132,11 @@ base64 = { path = "build/rust/base64" }
|
|||
# Patch wasi 0.10 to 0.11
|
||||
wasi = { path = "build/rust/wasi" }
|
||||
|
||||
# tokio 0.29.0 includes an experimental "tracing" feature which requires
|
||||
# backtrace ^0.3.58 and the `tokio_unstable` flag. We don't use it, and nothing
|
||||
# else we do use requires backtrace, so dummy it out for now.
|
||||
backtrace = { path = "build/rust/backtrace" }
|
||||
|
||||
# Patch bindgen 0.63, 0.64 and 0.66 to 0.68
|
||||
bindgen_0_63 = { package = "bindgen", path = "build/rust/bindgen-0.63" }
|
||||
bindgen_0_64 = { package = "bindgen", path = "build/rust/bindgen-0.64" }
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "backtrace"
|
||||
version = "0.3.999"
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
|
@ -1310,6 +1310,12 @@ criteria = "safe-to-run"
|
|||
version = "0.7.1"
|
||||
aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
|
||||
|
||||
[[audits.google.audits.tokio]]
|
||||
who = "Vovo Yang <vovoy@google.com>"
|
||||
criteria = "safe-to-run"
|
||||
version = "1.29.1"
|
||||
aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
|
||||
|
||||
[[audits.google.audits.tokio-stream]]
|
||||
who = "David Koloski <dkoloski@google.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,3 +1,901 @@
|
|||
# 1.29.1 (June 29, 2023)
|
||||
|
||||
### Fixed
|
||||
|
||||
- rt: fix nesting two `block_in_place` with a `block_on` between ([#5837])
|
||||
|
||||
[#5837]: https://github.com/tokio-rs/tokio/pull/5837
|
||||
|
||||
# 1.29.0 (June 27, 2023)
|
||||
|
||||
Technically a breaking change, the `Send` implementation is removed from
|
||||
`runtime::EnterGuard`. This change fixes a bug and should not impact most users.
|
||||
|
||||
### Breaking
|
||||
|
||||
- rt: `EnterGuard` should not be `Send` ([#5766])
|
||||
|
||||
### Fixed
|
||||
|
||||
- fs: reduce blocking ops in `fs::read_dir` ([#5653])
|
||||
- rt: fix possible starvation ([#5686], [#5712])
|
||||
- rt: fix stacked borrows issue in `JoinSet` ([#5693])
|
||||
- rt: panic if `EnterGuard` dropped incorrect order ([#5772])
|
||||
- time: do not overflow to signal value ([#5710])
|
||||
- fs: wait for in-flight ops before cloning `File` ([#5803])
|
||||
|
||||
### Changed
|
||||
|
||||
- rt: reduce time to poll tasks scheduled from outside the runtime ([#5705], [#5720])
|
||||
|
||||
### Added
|
||||
|
||||
- net: add uds doc alias for unix sockets ([#5659])
|
||||
- rt: add metric for number of tasks ([#5628])
|
||||
- sync: implement more traits for channel errors ([#5666])
|
||||
- net: add nodelay methods on TcpSocket ([#5672])
|
||||
- sync: add `broadcast::Receiver::blocking_recv` ([#5690])
|
||||
- process: add `raw_arg` method to `Command` ([#5704])
|
||||
- io: support PRIORITY epoll events ([#5566])
|
||||
- task: add `JoinSet::poll_join_next` ([#5721])
|
||||
- net: add support for Redox OS ([#5790])
|
||||
|
||||
|
||||
### Unstable
|
||||
|
||||
- rt: add the ability to dump task backtraces ([#5608], [#5676], [#5708], [#5717])
|
||||
- rt: instrument task poll times with a histogram ([#5685])
|
||||
|
||||
[#5766]: https://github.com/tokio-rs/tokio/pull/5766
|
||||
[#5653]: https://github.com/tokio-rs/tokio/pull/5653
|
||||
[#5686]: https://github.com/tokio-rs/tokio/pull/5686
|
||||
[#5712]: https://github.com/tokio-rs/tokio/pull/5712
|
||||
[#5693]: https://github.com/tokio-rs/tokio/pull/5693
|
||||
[#5772]: https://github.com/tokio-rs/tokio/pull/5772
|
||||
[#5710]: https://github.com/tokio-rs/tokio/pull/5710
|
||||
[#5803]: https://github.com/tokio-rs/tokio/pull/5803
|
||||
[#5705]: https://github.com/tokio-rs/tokio/pull/5705
|
||||
[#5720]: https://github.com/tokio-rs/tokio/pull/5720
|
||||
[#5659]: https://github.com/tokio-rs/tokio/pull/5659
|
||||
[#5628]: https://github.com/tokio-rs/tokio/pull/5628
|
||||
[#5666]: https://github.com/tokio-rs/tokio/pull/5666
|
||||
[#5672]: https://github.com/tokio-rs/tokio/pull/5672
|
||||
[#5690]: https://github.com/tokio-rs/tokio/pull/5690
|
||||
[#5704]: https://github.com/tokio-rs/tokio/pull/5704
|
||||
[#5566]: https://github.com/tokio-rs/tokio/pull/5566
|
||||
[#5721]: https://github.com/tokio-rs/tokio/pull/5721
|
||||
[#5790]: https://github.com/tokio-rs/tokio/pull/5790
|
||||
[#5608]: https://github.com/tokio-rs/tokio/pull/5608
|
||||
[#5676]: https://github.com/tokio-rs/tokio/pull/5676
|
||||
[#5708]: https://github.com/tokio-rs/tokio/pull/5708
|
||||
[#5717]: https://github.com/tokio-rs/tokio/pull/5717
|
||||
[#5685]: https://github.com/tokio-rs/tokio/pull/5685
|
||||
|
||||
# 1.28.2 (May 28, 2023)
|
||||
|
||||
Forward ports 1.18.6 changes.
|
||||
|
||||
### Fixed
|
||||
|
||||
- deps: disable default features for mio ([#5728])
|
||||
|
||||
[#5728]: https://github.com/tokio-rs/tokio/pull/5728
|
||||
|
||||
# 1.28.1 (May 10th, 2023)
|
||||
|
||||
This release fixes a mistake in the build script that makes `AsFd`
|
||||
implementations unavailable on Rust 1.63. ([#5677])
|
||||
|
||||
[#5677]: https://github.com/tokio-rs/tokio/pull/5677
|
||||
|
||||
# 1.28.0 (April 25th, 2023)
|
||||
|
||||
### Added
|
||||
|
||||
- io: add `AsyncFd::async_io` ([#5542])
|
||||
- io: impl BufMut for ReadBuf ([#5590])
|
||||
- net: add `recv_buf` for `UdpSocket` and `UnixDatagram` ([#5583])
|
||||
- sync: add `OwnedSemaphorePermit::semaphore` ([#5618])
|
||||
- sync: add `same_channel` to broadcast channel ([#5607])
|
||||
- sync: add `watch::Receiver::wait_for` ([#5611])
|
||||
- task: add `JoinSet::spawn_blocking` and `JoinSet::spawn_blocking_on` ([#5612])
|
||||
|
||||
### Changed
|
||||
|
||||
- deps: update windows-sys to 0.48 ([#5591])
|
||||
- io: make `read_to_end` not grow unnecessarily ([#5610])
|
||||
- macros: make entrypoints more efficient ([#5621])
|
||||
- sync: improve Debug impl for `RwLock` ([#5647])
|
||||
- sync: reduce contention in `Notify` ([#5503])
|
||||
|
||||
### Fixed
|
||||
|
||||
- net: support `get_peer_cred` on AIX ([#5065])
|
||||
- sync: avoid deadlocks in `broadcast` with custom wakers ([#5578])
|
||||
|
||||
### Documented
|
||||
|
||||
- sync: fix typo in `Semaphore::MAX_PERMITS` ([#5645])
|
||||
- sync: fix typo in `tokio::sync::watch::Sender` docs ([#5587])
|
||||
|
||||
[#5065]: https://github.com/tokio-rs/tokio/pull/5065
|
||||
[#5503]: https://github.com/tokio-rs/tokio/pull/5503
|
||||
[#5542]: https://github.com/tokio-rs/tokio/pull/5542
|
||||
[#5578]: https://github.com/tokio-rs/tokio/pull/5578
|
||||
[#5583]: https://github.com/tokio-rs/tokio/pull/5583
|
||||
[#5587]: https://github.com/tokio-rs/tokio/pull/5587
|
||||
[#5590]: https://github.com/tokio-rs/tokio/pull/5590
|
||||
[#5591]: https://github.com/tokio-rs/tokio/pull/5591
|
||||
[#5607]: https://github.com/tokio-rs/tokio/pull/5607
|
||||
[#5610]: https://github.com/tokio-rs/tokio/pull/5610
|
||||
[#5611]: https://github.com/tokio-rs/tokio/pull/5611
|
||||
[#5612]: https://github.com/tokio-rs/tokio/pull/5612
|
||||
[#5618]: https://github.com/tokio-rs/tokio/pull/5618
|
||||
[#5621]: https://github.com/tokio-rs/tokio/pull/5621
|
||||
[#5645]: https://github.com/tokio-rs/tokio/pull/5645
|
||||
[#5647]: https://github.com/tokio-rs/tokio/pull/5647
|
||||
|
||||
# 1.27.0 (March 27th, 2023)
|
||||
|
||||
This release bumps the MSRV of Tokio to 1.56. ([#5559])
|
||||
|
||||
### Added
|
||||
|
||||
- io: add `async_io` helper method to sockets ([#5512])
|
||||
- io: add implementations of `AsFd`/`AsHandle`/`AsSocket` ([#5514], [#5540])
|
||||
- net: add `UdpSocket::peek_sender()` ([#5520])
|
||||
- sync: add `RwLockWriteGuard::{downgrade_map, try_downgrade_map}` ([#5527])
|
||||
- task: add `JoinHandle::abort_handle` ([#5543])
|
||||
|
||||
### Changed
|
||||
|
||||
- io: use `memchr` from `libc` ([#5558])
|
||||
- macros: accept path as crate rename in `#[tokio::main]` ([#5557])
|
||||
- macros: update to syn 2.0.0 ([#5572])
|
||||
- time: don't register for a wakeup when `Interval` returns `Ready` ([#5553])
|
||||
|
||||
### Fixed
|
||||
|
||||
- fs: fuse std iterator in `ReadDir` ([#5555])
|
||||
- tracing: fix `spawn_blocking` location fields ([#5573])
|
||||
- time: clean up redundant check in `Wheel::poll()` ([#5574])
|
||||
|
||||
### Documented
|
||||
|
||||
- macros: define cancellation safety ([#5525])
|
||||
- io: add details to docs of `tokio::io::copy[_buf]` ([#5575])
|
||||
- io: refer to `ReaderStream` and `StreamReader` in module docs ([#5576])
|
||||
|
||||
[#5512]: https://github.com/tokio-rs/tokio/pull/5512
|
||||
[#5514]: https://github.com/tokio-rs/tokio/pull/5514
|
||||
[#5520]: https://github.com/tokio-rs/tokio/pull/5520
|
||||
[#5525]: https://github.com/tokio-rs/tokio/pull/5525
|
||||
[#5527]: https://github.com/tokio-rs/tokio/pull/5527
|
||||
[#5540]: https://github.com/tokio-rs/tokio/pull/5540
|
||||
[#5543]: https://github.com/tokio-rs/tokio/pull/5543
|
||||
[#5553]: https://github.com/tokio-rs/tokio/pull/5553
|
||||
[#5555]: https://github.com/tokio-rs/tokio/pull/5555
|
||||
[#5557]: https://github.com/tokio-rs/tokio/pull/5557
|
||||
[#5558]: https://github.com/tokio-rs/tokio/pull/5558
|
||||
[#5559]: https://github.com/tokio-rs/tokio/pull/5559
|
||||
[#5572]: https://github.com/tokio-rs/tokio/pull/5572
|
||||
[#5573]: https://github.com/tokio-rs/tokio/pull/5573
|
||||
[#5574]: https://github.com/tokio-rs/tokio/pull/5574
|
||||
[#5575]: https://github.com/tokio-rs/tokio/pull/5575
|
||||
[#5576]: https://github.com/tokio-rs/tokio/pull/5576
|
||||
|
||||
# 1.26.0 (March 1st, 2023)
|
||||
|
||||
### Fixed
|
||||
|
||||
- macros: fix empty `join!` and `try_join!` ([#5504])
|
||||
- sync: don't leak tracing spans in mutex guards ([#5469])
|
||||
- sync: drop wakers after unlocking the mutex in Notify ([#5471])
|
||||
- sync: drop wakers outside lock in semaphore ([#5475])
|
||||
|
||||
### Added
|
||||
|
||||
- fs: add `fs::try_exists` ([#4299])
|
||||
- net: add types for named unix pipes ([#5351])
|
||||
- sync: add `MappedOwnedMutexGuard` ([#5474])
|
||||
|
||||
### Changed
|
||||
|
||||
- chore: update windows-sys to 0.45 ([#5386])
|
||||
- net: use Message Read Mode for named pipes ([#5350])
|
||||
- sync: mark lock guards with `#[clippy::has_significant_drop]` ([#5422])
|
||||
- sync: reduce contention in watch channel ([#5464])
|
||||
- time: remove cache padding in timer entries ([#5468])
|
||||
- time: Improve `Instant::now()` perf with test-util ([#5513])
|
||||
|
||||
### Internal Changes
|
||||
|
||||
- io: use `poll_fn` in `copy_bidirectional` ([#5486])
|
||||
- net: refactor named pipe builders to not use bitfields ([#5477])
|
||||
- rt: remove Arc from Clock ([#5434])
|
||||
- sync: make `notify_waiters` calls atomic ([#5458])
|
||||
- time: don't store deadline twice in sleep entries ([#5410])
|
||||
|
||||
### Unstable
|
||||
|
||||
- metrics: add a new metric for budget exhaustion yields ([#5517])
|
||||
|
||||
### Documented
|
||||
|
||||
- io: improve AsyncFd example ([#5481])
|
||||
- runtime: document the nature of the main future ([#5494])
|
||||
- runtime: remove extra period in docs ([#5511])
|
||||
- signal: updated Documentation for Signals ([#5459])
|
||||
- sync: add doc aliases for `blocking_*` methods ([#5448])
|
||||
- sync: fix docs for Send/Sync bounds in broadcast ([#5480])
|
||||
- sync: document drop behavior for channels ([#5497])
|
||||
- task: clarify what happens to spawned work during runtime shutdown ([#5394])
|
||||
- task: clarify `process::Command` docs ([#5413])
|
||||
- task: fix wording with 'unsend' ([#5452])
|
||||
- time: document immediate completion guarantee for timeouts ([#5509])
|
||||
- tokio: document supported platforms ([#5483])
|
||||
|
||||
[#4299]: https://github.com/tokio-rs/tokio/pull/4299
|
||||
[#5350]: https://github.com/tokio-rs/tokio/pull/5350
|
||||
[#5351]: https://github.com/tokio-rs/tokio/pull/5351
|
||||
[#5386]: https://github.com/tokio-rs/tokio/pull/5386
|
||||
[#5394]: https://github.com/tokio-rs/tokio/pull/5394
|
||||
[#5410]: https://github.com/tokio-rs/tokio/pull/5410
|
||||
[#5413]: https://github.com/tokio-rs/tokio/pull/5413
|
||||
[#5422]: https://github.com/tokio-rs/tokio/pull/5422
|
||||
[#5434]: https://github.com/tokio-rs/tokio/pull/5434
|
||||
[#5448]: https://github.com/tokio-rs/tokio/pull/5448
|
||||
[#5452]: https://github.com/tokio-rs/tokio/pull/5452
|
||||
[#5458]: https://github.com/tokio-rs/tokio/pull/5458
|
||||
[#5459]: https://github.com/tokio-rs/tokio/pull/5459
|
||||
[#5464]: https://github.com/tokio-rs/tokio/pull/5464
|
||||
[#5468]: https://github.com/tokio-rs/tokio/pull/5468
|
||||
[#5469]: https://github.com/tokio-rs/tokio/pull/5469
|
||||
[#5471]: https://github.com/tokio-rs/tokio/pull/5471
|
||||
[#5474]: https://github.com/tokio-rs/tokio/pull/5474
|
||||
[#5475]: https://github.com/tokio-rs/tokio/pull/5475
|
||||
[#5477]: https://github.com/tokio-rs/tokio/pull/5477
|
||||
[#5480]: https://github.com/tokio-rs/tokio/pull/5480
|
||||
[#5481]: https://github.com/tokio-rs/tokio/pull/5481
|
||||
[#5483]: https://github.com/tokio-rs/tokio/pull/5483
|
||||
[#5486]: https://github.com/tokio-rs/tokio/pull/5486
|
||||
[#5494]: https://github.com/tokio-rs/tokio/pull/5494
|
||||
[#5497]: https://github.com/tokio-rs/tokio/pull/5497
|
||||
[#5504]: https://github.com/tokio-rs/tokio/pull/5504
|
||||
[#5509]: https://github.com/tokio-rs/tokio/pull/5509
|
||||
[#5511]: https://github.com/tokio-rs/tokio/pull/5511
|
||||
[#5513]: https://github.com/tokio-rs/tokio/pull/5513
|
||||
[#5517]: https://github.com/tokio-rs/tokio/pull/5517
|
||||
|
||||
# 1.25.1 (May 28, 2023)
|
||||
|
||||
Forward ports 1.18.6 changes.
|
||||
|
||||
### Fixed
|
||||
|
||||
- deps: disable default features for mio ([#5728])
|
||||
|
||||
[#5728]: https://github.com/tokio-rs/tokio/pull/5728
|
||||
|
||||
# 1.25.0 (January 28, 2023)
|
||||
|
||||
### Fixed
|
||||
|
||||
- rt: fix runtime metrics reporting ([#5330])
|
||||
|
||||
### Added
|
||||
|
||||
- sync: add `broadcast::Sender::len` ([#5343])
|
||||
|
||||
### Changed
|
||||
|
||||
- fs: increase maximum read buffer size to 2MiB ([#5397])
|
||||
|
||||
[#5330]: https://github.com/tokio-rs/tokio/pull/5330
|
||||
[#5343]: https://github.com/tokio-rs/tokio/pull/5343
|
||||
[#5397]: https://github.com/tokio-rs/tokio/pull/5397
|
||||
|
||||
# 1.24.2 (January 17, 2023)
|
||||
|
||||
Forward ports 1.18.5 changes.
|
||||
|
||||
### Fixed
|
||||
|
||||
- io: fix unsoundness in `ReadHalf::unsplit` ([#5375])
|
||||
|
||||
[#5375]: https://github.com/tokio-rs/tokio/pull/5375
|
||||
|
||||
# 1.24.1 (January 6, 2022)
|
||||
|
||||
This release fixes a compilation failure on targets without `AtomicU64` when using rustc older than 1.63. ([#5356])
|
||||
|
||||
[#5356]: https://github.com/tokio-rs/tokio/pull/5356
|
||||
|
||||
# 1.24.0 (January 5, 2022)
|
||||
|
||||
### Fixed
|
||||
- rt: improve native `AtomicU64` support detection ([#5284])
|
||||
|
||||
### Added
|
||||
- rt: add configuration option for max number of I/O events polled from the OS
|
||||
per tick ([#5186])
|
||||
- rt: add an environment variable for configuring the default number of worker
|
||||
threads per runtime instance ([#4250])
|
||||
|
||||
### Changed
|
||||
- sync: reduce MPSC channel stack usage ([#5294])
|
||||
- io: reduce lock contention in I/O operations ([#5300])
|
||||
- fs: speed up `read_dir()` by chunking operations ([#5309])
|
||||
- rt: use internal `ThreadId` implementation ([#5329])
|
||||
- test: don't auto-advance time when a `spawn_blocking` task is running ([#5115])
|
||||
|
||||
[#5186]: https://github.com/tokio-rs/tokio/pull/5186
|
||||
[#5294]: https://github.com/tokio-rs/tokio/pull/5294
|
||||
[#5284]: https://github.com/tokio-rs/tokio/pull/5284
|
||||
[#4250]: https://github.com/tokio-rs/tokio/pull/4250
|
||||
[#5300]: https://github.com/tokio-rs/tokio/pull/5300
|
||||
[#5329]: https://github.com/tokio-rs/tokio/pull/5329
|
||||
[#5115]: https://github.com/tokio-rs/tokio/pull/5115
|
||||
[#5309]: https://github.com/tokio-rs/tokio/pull/5309
|
||||
|
||||
# 1.23.1 (January 4, 2022)
|
||||
|
||||
This release forward ports changes from 1.18.4.
|
||||
|
||||
### Fixed
|
||||
|
||||
- net: fix Windows named pipe server builder to maintain option when toggling
|
||||
pipe mode ([#5336]).
|
||||
|
||||
[#5336]: https://github.com/tokio-rs/tokio/pull/5336
|
||||
|
||||
# 1.23.0 (December 5, 2022)
|
||||
|
||||
### Fixed
|
||||
|
||||
- net: fix Windows named pipe connect ([#5208])
|
||||
- io: support vectored writes for `ChildStdin` ([#5216])
|
||||
- io: fix `async fn ready()` false positive for OS-specific events ([#5231])
|
||||
|
||||
### Changed
|
||||
- runtime: `yield_now` defers task until after driver poll ([#5223])
|
||||
- runtime: reduce amount of codegen needed per spawned task ([#5213])
|
||||
- windows: replace `winapi` dependency with `windows-sys` ([#5204])
|
||||
|
||||
[#5208]: https://github.com/tokio-rs/tokio/pull/5208
|
||||
[#5216]: https://github.com/tokio-rs/tokio/pull/5216
|
||||
[#5213]: https://github.com/tokio-rs/tokio/pull/5213
|
||||
[#5204]: https://github.com/tokio-rs/tokio/pull/5204
|
||||
[#5223]: https://github.com/tokio-rs/tokio/pull/5223
|
||||
[#5231]: https://github.com/tokio-rs/tokio/pull/5231
|
||||
|
||||
# 1.22.0 (November 17, 2022)
|
||||
|
||||
### Added
|
||||
- runtime: add `Handle::runtime_flavor` ([#5138])
|
||||
- sync: add `Mutex::blocking_lock_owned` ([#5130])
|
||||
- sync: add `Semaphore::MAX_PERMITS` ([#5144])
|
||||
- sync: add `merge()` to semaphore permits ([#4948])
|
||||
- sync: add `mpsc::WeakUnboundedSender` ([#5189])
|
||||
|
||||
### Added (unstable)
|
||||
|
||||
- process: add `Command::process_group` ([#5114])
|
||||
- runtime: export metrics about the blocking thread pool ([#5161])
|
||||
- task: add `task::id()` and `task::try_id()` ([#5171])
|
||||
|
||||
### Fixed
|
||||
- macros: don't take ownership of futures in macros ([#5087])
|
||||
- runtime: fix Stacked Borrows violation in `LocalOwnedTasks` ([#5099])
|
||||
- runtime: mitigate ABA with 32-bit queue indices when possible ([#5042])
|
||||
- task: wake local tasks to the local queue when woken by the same thread ([#5095])
|
||||
- time: panic in release mode when `mark_pending` called illegally ([#5093])
|
||||
- runtime: fix typo in expect message ([#5169])
|
||||
- runtime: fix `unsync_load` on atomic types ([#5175])
|
||||
- task: elaborate safety comments in task deallocation ([#5172])
|
||||
- runtime: fix `LocalSet` drop in thread local ([#5179])
|
||||
- net: remove libc type leakage in a public API ([#5191])
|
||||
- runtime: update the alignment of `CachePadded` ([#5106])
|
||||
|
||||
### Changed
|
||||
- io: make `tokio::io::copy` continue filling the buffer when writer stalls ([#5066])
|
||||
- runtime: remove `coop::budget` from `LocalSet::run_until` ([#5155])
|
||||
- sync: make `Notify` panic safe ([#5154])
|
||||
|
||||
### Documented
|
||||
- io: fix doc for `write_i8` to use signed integers ([#5040])
|
||||
- net: fix doc typos for TCP and UDP `set_tos` methods ([#5073])
|
||||
- net: fix function name in `UdpSocket::recv` documentation ([#5150])
|
||||
- sync: typo in `TryLockError` for `RwLock::try_write` ([#5160])
|
||||
- task: document that spawned tasks execute immediately ([#5117])
|
||||
- time: document return type of `timeout` ([#5118])
|
||||
- time: document that `timeout` checks only before poll ([#5126])
|
||||
- sync: specify return type of `oneshot::Receiver` in docs ([#5198])
|
||||
|
||||
### Internal changes
|
||||
- runtime: use const `Mutex::new` for globals ([#5061])
|
||||
- runtime: remove `Option` around `mio::Events` in io driver ([#5078])
|
||||
- runtime: remove a conditional compilation clause ([#5104])
|
||||
- runtime: remove a reference to internal time handle ([#5107])
|
||||
- runtime: misc time driver cleanup ([#5120])
|
||||
- runtime: move signal driver to runtime module ([#5121])
|
||||
- runtime: signal driver now uses I/O driver directly ([#5125])
|
||||
- runtime: start decoupling I/O driver and I/O handle ([#5127])
|
||||
- runtime: switch `io::handle` refs with scheduler:Handle ([#5128])
|
||||
- runtime: remove Arc from I/O driver ([#5134])
|
||||
- runtime: use signal driver handle via `scheduler::Handle` ([#5135])
|
||||
- runtime: move internal clock fns out of context ([#5139])
|
||||
- runtime: remove `runtime::context` module ([#5140])
|
||||
- runtime: keep driver cfgs in `driver.rs` ([#5141])
|
||||
- runtime: add `runtime::context` to unify thread-locals ([#5143])
|
||||
- runtime: rename some confusing internal variables/fns ([#5151])
|
||||
- runtime: move `coop` mod into `runtime` ([#5152])
|
||||
- runtime: move budget state to context thread-local ([#5157])
|
||||
- runtime: move park logic into runtime module ([#5158])
|
||||
- runtime: move `Runtime` into its own file ([#5159])
|
||||
- runtime: unify entering a runtime with `Handle::enter` ([#5163])
|
||||
- runtime: remove handle reference from each scheduler ([#5166])
|
||||
- runtime: move `enter` into `context` ([#5167])
|
||||
- runtime: combine context and entered thread-locals ([#5168])
|
||||
- runtime: fix accidental unsetting of current handle ([#5178])
|
||||
- runtime: move `CoreStage` methods to `Core` ([#5182])
|
||||
- sync: name mpsc semaphore types ([#5146])
|
||||
|
||||
[#4948]: https://github.com/tokio-rs/tokio/pull/4948
|
||||
[#5040]: https://github.com/tokio-rs/tokio/pull/5040
|
||||
[#5042]: https://github.com/tokio-rs/tokio/pull/5042
|
||||
[#5061]: https://github.com/tokio-rs/tokio/pull/5061
|
||||
[#5066]: https://github.com/tokio-rs/tokio/pull/5066
|
||||
[#5073]: https://github.com/tokio-rs/tokio/pull/5073
|
||||
[#5078]: https://github.com/tokio-rs/tokio/pull/5078
|
||||
[#5087]: https://github.com/tokio-rs/tokio/pull/5087
|
||||
[#5093]: https://github.com/tokio-rs/tokio/pull/5093
|
||||
[#5095]: https://github.com/tokio-rs/tokio/pull/5095
|
||||
[#5099]: https://github.com/tokio-rs/tokio/pull/5099
|
||||
[#5104]: https://github.com/tokio-rs/tokio/pull/5104
|
||||
[#5106]: https://github.com/tokio-rs/tokio/pull/5106
|
||||
[#5107]: https://github.com/tokio-rs/tokio/pull/5107
|
||||
[#5114]: https://github.com/tokio-rs/tokio/pull/5114
|
||||
[#5117]: https://github.com/tokio-rs/tokio/pull/5117
|
||||
[#5118]: https://github.com/tokio-rs/tokio/pull/5118
|
||||
[#5120]: https://github.com/tokio-rs/tokio/pull/5120
|
||||
[#5121]: https://github.com/tokio-rs/tokio/pull/5121
|
||||
[#5125]: https://github.com/tokio-rs/tokio/pull/5125
|
||||
[#5126]: https://github.com/tokio-rs/tokio/pull/5126
|
||||
[#5127]: https://github.com/tokio-rs/tokio/pull/5127
|
||||
[#5128]: https://github.com/tokio-rs/tokio/pull/5128
|
||||
[#5130]: https://github.com/tokio-rs/tokio/pull/5130
|
||||
[#5134]: https://github.com/tokio-rs/tokio/pull/5134
|
||||
[#5135]: https://github.com/tokio-rs/tokio/pull/5135
|
||||
[#5138]: https://github.com/tokio-rs/tokio/pull/5138
|
||||
[#5138]: https://github.com/tokio-rs/tokio/pull/5138
|
||||
[#5139]: https://github.com/tokio-rs/tokio/pull/5139
|
||||
[#5140]: https://github.com/tokio-rs/tokio/pull/5140
|
||||
[#5141]: https://github.com/tokio-rs/tokio/pull/5141
|
||||
[#5143]: https://github.com/tokio-rs/tokio/pull/5143
|
||||
[#5144]: https://github.com/tokio-rs/tokio/pull/5144
|
||||
[#5144]: https://github.com/tokio-rs/tokio/pull/5144
|
||||
[#5146]: https://github.com/tokio-rs/tokio/pull/5146
|
||||
[#5150]: https://github.com/tokio-rs/tokio/pull/5150
|
||||
[#5151]: https://github.com/tokio-rs/tokio/pull/5151
|
||||
[#5152]: https://github.com/tokio-rs/tokio/pull/5152
|
||||
[#5154]: https://github.com/tokio-rs/tokio/pull/5154
|
||||
[#5155]: https://github.com/tokio-rs/tokio/pull/5155
|
||||
[#5157]: https://github.com/tokio-rs/tokio/pull/5157
|
||||
[#5158]: https://github.com/tokio-rs/tokio/pull/5158
|
||||
[#5159]: https://github.com/tokio-rs/tokio/pull/5159
|
||||
[#5160]: https://github.com/tokio-rs/tokio/pull/5160
|
||||
[#5161]: https://github.com/tokio-rs/tokio/pull/5161
|
||||
[#5163]: https://github.com/tokio-rs/tokio/pull/5163
|
||||
[#5166]: https://github.com/tokio-rs/tokio/pull/5166
|
||||
[#5167]: https://github.com/tokio-rs/tokio/pull/5167
|
||||
[#5168]: https://github.com/tokio-rs/tokio/pull/5168
|
||||
[#5169]: https://github.com/tokio-rs/tokio/pull/5169
|
||||
[#5171]: https://github.com/tokio-rs/tokio/pull/5171
|
||||
[#5172]: https://github.com/tokio-rs/tokio/pull/5172
|
||||
[#5175]: https://github.com/tokio-rs/tokio/pull/5175
|
||||
[#5178]: https://github.com/tokio-rs/tokio/pull/5178
|
||||
[#5179]: https://github.com/tokio-rs/tokio/pull/5179
|
||||
[#5182]: https://github.com/tokio-rs/tokio/pull/5182
|
||||
[#5189]: https://github.com/tokio-rs/tokio/pull/5189
|
||||
[#5191]: https://github.com/tokio-rs/tokio/pull/5191
|
||||
[#5198]: https://github.com/tokio-rs/tokio/pull/5198
|
||||
|
||||
# 1.21.2 (September 27, 2022)
|
||||
|
||||
This release removes the dependency on the `once_cell` crate to restore the MSRV
|
||||
of 1.21.x, which is the latest minor version at the time of release. ([#5048])
|
||||
|
||||
[#5048]: https://github.com/tokio-rs/tokio/pull/5048
|
||||
|
||||
# 1.21.1 (September 13, 2022)
|
||||
|
||||
### Fixed
|
||||
|
||||
- net: fix dependency resolution for socket2 ([#5000])
|
||||
- task: ignore failure to set TLS in `LocalSet` Drop ([#4976])
|
||||
|
||||
[#4976]: https://github.com/tokio-rs/tokio/pull/4976
|
||||
[#5000]: https://github.com/tokio-rs/tokio/pull/5000
|
||||
|
||||
# 1.21.0 (September 2, 2022)
|
||||
|
||||
This release is the first release of Tokio to intentionally support WASM. The
|
||||
`sync,macros,io-util,rt,time` features are stabilized on WASM. Additionally the
|
||||
wasm32-wasi target is given unstable support for the `net` feature.
|
||||
|
||||
### Added
|
||||
|
||||
- net: add `device` and `bind_device` methods to TCP/UDP sockets ([#4882])
|
||||
- net: add `tos` and `set_tos` methods to TCP and UDP sockets ([#4877])
|
||||
- net: add security flags to named pipe `ServerOptions` ([#4845])
|
||||
- signal: add more windows signal handlers ([#4924])
|
||||
- sync: add `mpsc::Sender::max_capacity` method ([#4904])
|
||||
- sync: implement Weak version of `mpsc::Sender` ([#4595])
|
||||
- task: add `LocalSet::enter` ([#4765])
|
||||
- task: stabilize `JoinSet` and `AbortHandle` ([#4920])
|
||||
- tokio: add `track_caller` to public APIs ([#4805], [#4848], [#4852])
|
||||
- wasm: initial support for `wasm32-wasi` target ([#4716])
|
||||
|
||||
### Fixed
|
||||
|
||||
- miri: improve miri compatibility by avoiding temporary references in `linked_list::Link` impls ([#4841])
|
||||
- signal: don't register write interest on signal pipe ([#4898])
|
||||
- sync: add `#[must_use]` to lock guards ([#4886])
|
||||
- sync: fix hang when calling `recv` on closed and reopened broadcast channel ([#4867])
|
||||
- task: propagate attributes on task-locals ([#4837])
|
||||
|
||||
### Changed
|
||||
|
||||
- fs: change panic to error in `File::start_seek` ([#4897])
|
||||
- io: reduce syscalls in `poll_read` ([#4840])
|
||||
- process: use blocking threadpool for child stdio I/O ([#4824])
|
||||
- signal: make `SignalKind` methods const ([#4956])
|
||||
|
||||
### Internal changes
|
||||
|
||||
- rt: extract `basic_scheduler::Config` ([#4935])
|
||||
- rt: move I/O driver into `runtime` module ([#4942])
|
||||
- rt: rename internal scheduler types ([#4945])
|
||||
|
||||
### Documented
|
||||
|
||||
- chore: fix typos and grammar ([#4858], [#4894], [#4928])
|
||||
- io: fix typo in `AsyncSeekExt::rewind` docs ([#4893])
|
||||
- net: add documentation to `try_read()` for zero-length buffers ([#4937])
|
||||
- runtime: remove incorrect panic section for `Builder::worker_threads` ([#4849])
|
||||
- sync: doc of `watch::Sender::send` improved ([#4959])
|
||||
- task: add cancel safety docs to `JoinHandle` ([#4901])
|
||||
- task: expand on cancellation of `spawn_blocking` ([#4811])
|
||||
- time: clarify that the first tick of `Interval::tick` happens immediately ([#4951])
|
||||
|
||||
### Unstable
|
||||
|
||||
- rt: add unstable option to disable the LIFO slot ([#4936])
|
||||
- task: fix incorrect signature in `Builder::spawn_on` ([#4953])
|
||||
- task: make `task::Builder::spawn*` methods fallible ([#4823])
|
||||
|
||||
[#4595]: https://github.com/tokio-rs/tokio/pull/4595
|
||||
[#4716]: https://github.com/tokio-rs/tokio/pull/4716
|
||||
[#4765]: https://github.com/tokio-rs/tokio/pull/4765
|
||||
[#4805]: https://github.com/tokio-rs/tokio/pull/4805
|
||||
[#4811]: https://github.com/tokio-rs/tokio/pull/4811
|
||||
[#4823]: https://github.com/tokio-rs/tokio/pull/4823
|
||||
[#4824]: https://github.com/tokio-rs/tokio/pull/4824
|
||||
[#4837]: https://github.com/tokio-rs/tokio/pull/4837
|
||||
[#4840]: https://github.com/tokio-rs/tokio/pull/4840
|
||||
[#4841]: https://github.com/tokio-rs/tokio/pull/4841
|
||||
[#4845]: https://github.com/tokio-rs/tokio/pull/4845
|
||||
[#4848]: https://github.com/tokio-rs/tokio/pull/4848
|
||||
[#4849]: https://github.com/tokio-rs/tokio/pull/4849
|
||||
[#4852]: https://github.com/tokio-rs/tokio/pull/4852
|
||||
[#4858]: https://github.com/tokio-rs/tokio/pull/4858
|
||||
[#4867]: https://github.com/tokio-rs/tokio/pull/4867
|
||||
[#4877]: https://github.com/tokio-rs/tokio/pull/4877
|
||||
[#4882]: https://github.com/tokio-rs/tokio/pull/4882
|
||||
[#4886]: https://github.com/tokio-rs/tokio/pull/4886
|
||||
[#4893]: https://github.com/tokio-rs/tokio/pull/4893
|
||||
[#4894]: https://github.com/tokio-rs/tokio/pull/4894
|
||||
[#4897]: https://github.com/tokio-rs/tokio/pull/4897
|
||||
[#4898]: https://github.com/tokio-rs/tokio/pull/4898
|
||||
[#4901]: https://github.com/tokio-rs/tokio/pull/4901
|
||||
[#4904]: https://github.com/tokio-rs/tokio/pull/4904
|
||||
[#4920]: https://github.com/tokio-rs/tokio/pull/4920
|
||||
[#4924]: https://github.com/tokio-rs/tokio/pull/4924
|
||||
[#4928]: https://github.com/tokio-rs/tokio/pull/4928
|
||||
[#4935]: https://github.com/tokio-rs/tokio/pull/4935
|
||||
[#4936]: https://github.com/tokio-rs/tokio/pull/4936
|
||||
[#4937]: https://github.com/tokio-rs/tokio/pull/4937
|
||||
[#4942]: https://github.com/tokio-rs/tokio/pull/4942
|
||||
[#4945]: https://github.com/tokio-rs/tokio/pull/4945
|
||||
[#4951]: https://github.com/tokio-rs/tokio/pull/4951
|
||||
[#4953]: https://github.com/tokio-rs/tokio/pull/4953
|
||||
[#4956]: https://github.com/tokio-rs/tokio/pull/4956
|
||||
[#4959]: https://github.com/tokio-rs/tokio/pull/4959
|
||||
|
||||
# 1.20.5 (May 28, 2023)
|
||||
|
||||
Forward ports 1.18.6 changes.
|
||||
|
||||
### Fixed
|
||||
|
||||
- deps: disable default features for mio ([#5728])
|
||||
|
||||
[#5728]: https://github.com/tokio-rs/tokio/pull/5728
|
||||
|
||||
# 1.20.4 (January 17, 2023)
|
||||
|
||||
Forward ports 1.18.5 changes.
|
||||
|
||||
### Fixed
|
||||
|
||||
- io: fix unsoundness in `ReadHalf::unsplit` ([#5375])
|
||||
|
||||
[#5375]: https://github.com/tokio-rs/tokio/pull/5375
|
||||
|
||||
# 1.20.3 (January 3, 2022)
|
||||
|
||||
This release forward ports changes from 1.18.4.
|
||||
|
||||
### Fixed
|
||||
|
||||
- net: fix Windows named pipe server builder to maintain option when toggling
|
||||
pipe mode ([#5336]).
|
||||
|
||||
[#5336]: https://github.com/tokio-rs/tokio/pull/5336
|
||||
|
||||
# 1.20.2 (September 27, 2022)
|
||||
|
||||
This release removes the dependency on the `once_cell` crate to restore the MSRV
|
||||
of the 1.20.x LTS release. ([#5048])
|
||||
|
||||
[#5048]: https://github.com/tokio-rs/tokio/pull/5048
|
||||
|
||||
# 1.20.1 (July 25, 2022)
|
||||
|
||||
### Fixed
|
||||
|
||||
- chore: fix version detection in build script ([#4860])
|
||||
|
||||
[#4860]: https://github.com/tokio-rs/tokio/pull/4860
|
||||
|
||||
# 1.20.0 (July 12, 2022)
|
||||
|
||||
### Added
|
||||
- tokio: add `track_caller` to public APIs ([#4772], [#4791], [#4793], [#4806], [#4808])
|
||||
- sync: Add `has_changed` method to `watch::Ref` ([#4758])
|
||||
|
||||
### Changed
|
||||
|
||||
- time: remove `src/time/driver/wheel/stack.rs` ([#4766])
|
||||
- rt: clean up arguments passed to basic scheduler ([#4767])
|
||||
- net: be more specific about winapi features ([#4764])
|
||||
- tokio: use const initialized thread locals where possible ([#4677])
|
||||
- task: various small improvements to LocalKey ([#4795])
|
||||
|
||||
### Documented
|
||||
|
||||
- fs: warn about performance pitfall ([#4762])
|
||||
- chore: fix spelling ([#4769])
|
||||
- sync: document spurious failures in oneshot ([#4777])
|
||||
- sync: add warning for watch in non-Send futures ([#4741])
|
||||
- chore: fix typo ([#4798])
|
||||
|
||||
### Unstable
|
||||
|
||||
- joinset: rename `join_one` to `join_next` ([#4755])
|
||||
- rt: unhandled panic config for current thread rt ([#4770])
|
||||
|
||||
[#4677]: https://github.com/tokio-rs/tokio/pull/4677
|
||||
[#4741]: https://github.com/tokio-rs/tokio/pull/4741
|
||||
[#4755]: https://github.com/tokio-rs/tokio/pull/4755
|
||||
[#4758]: https://github.com/tokio-rs/tokio/pull/4758
|
||||
[#4762]: https://github.com/tokio-rs/tokio/pull/4762
|
||||
[#4764]: https://github.com/tokio-rs/tokio/pull/4764
|
||||
[#4766]: https://github.com/tokio-rs/tokio/pull/4766
|
||||
[#4767]: https://github.com/tokio-rs/tokio/pull/4767
|
||||
[#4769]: https://github.com/tokio-rs/tokio/pull/4769
|
||||
[#4770]: https://github.com/tokio-rs/tokio/pull/4770
|
||||
[#4772]: https://github.com/tokio-rs/tokio/pull/4772
|
||||
[#4777]: https://github.com/tokio-rs/tokio/pull/4777
|
||||
[#4791]: https://github.com/tokio-rs/tokio/pull/4791
|
||||
[#4793]: https://github.com/tokio-rs/tokio/pull/4793
|
||||
[#4795]: https://github.com/tokio-rs/tokio/pull/4795
|
||||
[#4798]: https://github.com/tokio-rs/tokio/pull/4798
|
||||
[#4806]: https://github.com/tokio-rs/tokio/pull/4806
|
||||
[#4808]: https://github.com/tokio-rs/tokio/pull/4808
|
||||
|
||||
# 1.19.2 (June 6, 2022)
|
||||
|
||||
This release fixes another bug in `Notified::enable`. ([#4751])
|
||||
|
||||
[#4751]: https://github.com/tokio-rs/tokio/pull/4751
|
||||
|
||||
# 1.19.1 (June 5, 2022)
|
||||
|
||||
This release fixes a bug in `Notified::enable`. ([#4747])
|
||||
|
||||
[#4747]: https://github.com/tokio-rs/tokio/pull/4747
|
||||
|
||||
# 1.19.0 (June 3, 2022)
|
||||
|
||||
### Added
|
||||
|
||||
- runtime: add `is_finished` method for `JoinHandle` and `AbortHandle` ([#4709])
|
||||
- runtime: make global queue and event polling intervals configurable ([#4671])
|
||||
- sync: add `Notified::enable` ([#4705])
|
||||
- sync: add `watch::Sender::send_if_modified` ([#4591])
|
||||
- sync: add resubscribe method to broadcast::Receiver ([#4607])
|
||||
- net: add `take_error` to `TcpSocket` and `TcpStream` ([#4739])
|
||||
|
||||
### Changed
|
||||
|
||||
- io: refactor out usage of Weak in the io handle ([#4656])
|
||||
|
||||
### Fixed
|
||||
|
||||
- macros: avoid starvation in `join!` and `try_join!` ([#4624])
|
||||
|
||||
### Documented
|
||||
|
||||
- runtime: clarify semantics of tasks outliving `block_on` ([#4729])
|
||||
- time: fix example for `MissedTickBehavior::Burst` ([#4713])
|
||||
|
||||
### Unstable
|
||||
|
||||
- metrics: correctly update atomics in `IoDriverMetrics` ([#4725])
|
||||
- metrics: fix compilation with unstable, process, and rt, but without net ([#4682])
|
||||
- task: add `#[track_caller]` to `JoinSet`/`JoinMap` ([#4697])
|
||||
- task: add `Builder::{spawn_on, spawn_local_on, spawn_blocking_on}` ([#4683])
|
||||
- task: add `consume_budget` for cooperative scheduling ([#4498])
|
||||
- task: add `join_set::Builder` for configuring `JoinSet` tasks ([#4687])
|
||||
- task: update return value of `JoinSet::join_one` ([#4726])
|
||||
|
||||
[#4498]: https://github.com/tokio-rs/tokio/pull/4498
|
||||
[#4591]: https://github.com/tokio-rs/tokio/pull/4591
|
||||
[#4607]: https://github.com/tokio-rs/tokio/pull/4607
|
||||
[#4624]: https://github.com/tokio-rs/tokio/pull/4624
|
||||
[#4656]: https://github.com/tokio-rs/tokio/pull/4656
|
||||
[#4671]: https://github.com/tokio-rs/tokio/pull/4671
|
||||
[#4682]: https://github.com/tokio-rs/tokio/pull/4682
|
||||
[#4683]: https://github.com/tokio-rs/tokio/pull/4683
|
||||
[#4687]: https://github.com/tokio-rs/tokio/pull/4687
|
||||
[#4697]: https://github.com/tokio-rs/tokio/pull/4697
|
||||
[#4705]: https://github.com/tokio-rs/tokio/pull/4705
|
||||
[#4709]: https://github.com/tokio-rs/tokio/pull/4709
|
||||
[#4713]: https://github.com/tokio-rs/tokio/pull/4713
|
||||
[#4725]: https://github.com/tokio-rs/tokio/pull/4725
|
||||
[#4726]: https://github.com/tokio-rs/tokio/pull/4726
|
||||
[#4729]: https://github.com/tokio-rs/tokio/pull/4729
|
||||
[#4739]: https://github.com/tokio-rs/tokio/pull/4739
|
||||
|
||||
# 1.18.6 (May 28, 2023)
|
||||
|
||||
### Fixed
|
||||
|
||||
- deps: disable default features for mio ([#5728])
|
||||
|
||||
[#5728]: https://github.com/tokio-rs/tokio/pull/5728
|
||||
|
||||
# 1.18.5 (January 17, 2023)
|
||||
|
||||
### Fixed
|
||||
|
||||
- io: fix unsoundness in `ReadHalf::unsplit` ([#5375])
|
||||
|
||||
[#5375]: https://github.com/tokio-rs/tokio/pull/5375
|
||||
|
||||
# 1.18.4 (January 3, 2022)
|
||||
|
||||
### Fixed
|
||||
|
||||
- net: fix Windows named pipe server builder to maintain option when toggling
|
||||
pipe mode ([#5336]).
|
||||
|
||||
[#5336]: https://github.com/tokio-rs/tokio/pull/5336
|
||||
|
||||
# 1.18.3 (September 27, 2022)
|
||||
|
||||
This release removes the dependency on the `once_cell` crate to restore the MSRV
|
||||
of the 1.18.x LTS release. ([#5048])
|
||||
|
||||
[#5048]: https://github.com/tokio-rs/tokio/pull/5048
|
||||
|
||||
# 1.18.2 (May 5, 2022)
|
||||
|
||||
Add missing features for the `winapi` dependency. ([#4663])
|
||||
|
||||
[#4663]: https://github.com/tokio-rs/tokio/pull/4663
|
||||
|
||||
# 1.18.1 (May 2, 2022)
|
||||
|
||||
The 1.18.0 release broke the build for targets without 64-bit atomics when
|
||||
building with `tokio_unstable`. This release fixes that. ([#4649])
|
||||
|
||||
[#4649]: https://github.com/tokio-rs/tokio/pull/4649
|
||||
|
||||
# 1.18.0 (April 27, 2022)
|
||||
|
||||
This release adds a number of new APIs in `tokio::net`, `tokio::signal`, and
|
||||
`tokio::sync`. In addition, it adds new unstable APIs to `tokio::task` (`Id`s
|
||||
for uniquely identifying a task, and `AbortHandle` for remotely cancelling a
|
||||
task), as well as a number of bugfixes.
|
||||
|
||||
### Fixed
|
||||
|
||||
- blocking: add missing `#[track_caller]` for `spawn_blocking` ([#4616])
|
||||
- macros: fix `select` macro to process 64 branches ([#4519])
|
||||
- net: fix `try_io` methods not calling Mio's `try_io` internally ([#4582])
|
||||
- runtime: recover when OS fails to spawn a new thread ([#4485])
|
||||
|
||||
### Added
|
||||
|
||||
- net: add `UdpSocket::peer_addr` ([#4611])
|
||||
- net: add `try_read_buf` method for named pipes ([#4626])
|
||||
- signal: add `SignalKind` `Hash`/`Eq` impls and `c_int` conversion ([#4540])
|
||||
- signal: add support for signals up to `SIGRTMAX` ([#4555])
|
||||
- sync: add `watch::Sender::send_modify` method ([#4310])
|
||||
- sync: add `broadcast::Receiver::len` method ([#4542])
|
||||
- sync: add `watch::Receiver::same_channel` method ([#4581])
|
||||
- sync: implement `Clone` for `RecvError` types ([#4560])
|
||||
|
||||
### Changed
|
||||
|
||||
- update `mio` to 0.8.1 ([#4582])
|
||||
- macros: rename `tokio::select!`'s internal `util` module ([#4543])
|
||||
- runtime: use `Vec::with_capacity` when building runtime ([#4553])
|
||||
|
||||
### Documented
|
||||
|
||||
- improve docs for `tokio_unstable` ([#4524])
|
||||
- runtime: include more documentation for thread_pool/worker ([#4511])
|
||||
- runtime: update `Handle::current`'s docs to mention `EnterGuard` ([#4567])
|
||||
- time: clarify platform specific timer resolution ([#4474])
|
||||
- signal: document that `Signal::recv` is cancel-safe ([#4634])
|
||||
- sync: `UnboundedReceiver` close docs ([#4548])
|
||||
|
||||
### Unstable
|
||||
|
||||
The following changes only apply when building with `--cfg tokio_unstable`:
|
||||
|
||||
- task: add `task::Id` type ([#4630])
|
||||
- task: add `AbortHandle` type for cancelling tasks in a `JoinSet` ([#4530],
|
||||
[#4640])
|
||||
- task: fix missing `doc(cfg(...))` attributes for `JoinSet` ([#4531])
|
||||
- task: fix broken link in `AbortHandle` RustDoc ([#4545])
|
||||
- metrics: add initial IO driver metrics ([#4507])
|
||||
|
||||
|
||||
[#4616]: https://github.com/tokio-rs/tokio/pull/4616
|
||||
[#4519]: https://github.com/tokio-rs/tokio/pull/4519
|
||||
[#4582]: https://github.com/tokio-rs/tokio/pull/4582
|
||||
[#4485]: https://github.com/tokio-rs/tokio/pull/4485
|
||||
[#4613]: https://github.com/tokio-rs/tokio/pull/4613
|
||||
[#4611]: https://github.com/tokio-rs/tokio/pull/4611
|
||||
[#4626]: https://github.com/tokio-rs/tokio/pull/4626
|
||||
[#4540]: https://github.com/tokio-rs/tokio/pull/4540
|
||||
[#4555]: https://github.com/tokio-rs/tokio/pull/4555
|
||||
[#4310]: https://github.com/tokio-rs/tokio/pull/4310
|
||||
[#4542]: https://github.com/tokio-rs/tokio/pull/4542
|
||||
[#4581]: https://github.com/tokio-rs/tokio/pull/4581
|
||||
[#4560]: https://github.com/tokio-rs/tokio/pull/4560
|
||||
[#4631]: https://github.com/tokio-rs/tokio/pull/4631
|
||||
[#4582]: https://github.com/tokio-rs/tokio/pull/4582
|
||||
[#4543]: https://github.com/tokio-rs/tokio/pull/4543
|
||||
[#4553]: https://github.com/tokio-rs/tokio/pull/4553
|
||||
[#4524]: https://github.com/tokio-rs/tokio/pull/4524
|
||||
[#4511]: https://github.com/tokio-rs/tokio/pull/4511
|
||||
[#4567]: https://github.com/tokio-rs/tokio/pull/4567
|
||||
[#4474]: https://github.com/tokio-rs/tokio/pull/4474
|
||||
[#4634]: https://github.com/tokio-rs/tokio/pull/4634
|
||||
[#4548]: https://github.com/tokio-rs/tokio/pull/4548
|
||||
[#4630]: https://github.com/tokio-rs/tokio/pull/4630
|
||||
[#4530]: https://github.com/tokio-rs/tokio/pull/4530
|
||||
[#4640]: https://github.com/tokio-rs/tokio/pull/4640
|
||||
[#4531]: https://github.com/tokio-rs/tokio/pull/4531
|
||||
[#4545]: https://github.com/tokio-rs/tokio/pull/4545
|
||||
[#4507]: https://github.com/tokio-rs/tokio/pull/4507
|
||||
|
||||
# 1.17.0 (February 16, 2022)
|
||||
|
||||
This release updates the minimum supported Rust version (MSRV) to 1.49, the
|
||||
|
@ -23,7 +921,7 @@ performance improvements.
|
|||
- time: use bit manipulation instead of modulo to improve performance ([#4480])
|
||||
- net: use `std::future::Ready` instead of our own `Ready` future ([#4271])
|
||||
- replace deprecated `atomic::spin_loop_hint` with `hint::spin_loop` ([#4491])
|
||||
- fix miri failures in intrusive linked lists ([#4397])
|
||||
- fix miri failures in intrusive linked lists ([#4397])
|
||||
|
||||
### Documented
|
||||
|
||||
|
@ -1076,7 +1974,7 @@ Biggest changes are:
|
|||
- Feature flags are simplified
|
||||
- `rt-core` and `rt-util` are combined to `rt`
|
||||
- `rt-threaded` is renamed to `rt-multi-thread` to match builder API
|
||||
- `tcp`, `udp`, `uds`, `dns` are combied to `net`.
|
||||
- `tcp`, `udp`, `uds`, `dns` are combined to `net`.
|
||||
- `parking_lot` is included with `full`
|
||||
|
||||
### Changes
|
||||
|
@ -1574,7 +2472,7 @@ Biggest changes are:
|
|||
- `net::lookup_host` maps a `T: ToSocketAddrs` to a stream of `SocketAddrs` ([#1870]).
|
||||
- `process::Child` fields are made public to match `std` ([#2014]).
|
||||
- impl `Stream` for `sync::broadcast::Receiver` ([#2012]).
|
||||
- `sync::RwLock` provides an asynchonous read-write lock ([#1699]).
|
||||
- `sync::RwLock` provides an asynchronous read-write lock ([#1699]).
|
||||
- `runtime::Handle::current` returns the handle for the current runtime ([#2040]).
|
||||
- `StreamExt::filter` filters stream values according to a predicate ([#2001]).
|
||||
- `StreamExt::filter_map` simultaneously filter and map stream values ([#2001]).
|
||||
|
@ -1683,7 +2581,7 @@ Biggest changes are:
|
|||
### Fixes
|
||||
|
||||
- calling `spawn_blocking` after runtime shutdown ([#1875]).
|
||||
- `LocalSet` drop inifinite loop ([#1892]).
|
||||
- `LocalSet` drop infinite loop ([#1892]).
|
||||
- `LocalSet` hang under load ([#1905]).
|
||||
- improved documentation ([#1865], [#1866], [#1868], [#1874], [#1876], [#1911]).
|
||||
|
||||
|
|
|
@ -10,60 +10,73 @@
|
|||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
rust-version = "1.49"
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
name = "tokio"
|
||||
version = "1.17.0"
|
||||
version = "1.29.1"
|
||||
authors = ["Tokio Contributors <team@tokio.rs>"]
|
||||
description = "An event-driven, non-blocking I/O platform for writing asynchronous I/O\nbacked applications.\n"
|
||||
description = """
|
||||
An event-driven, non-blocking I/O platform for writing asynchronous I/O
|
||||
backed applications.
|
||||
"""
|
||||
homepage = "https://tokio.rs"
|
||||
readme = "README.md"
|
||||
keywords = ["io", "async", "non-blocking", "futures"]
|
||||
categories = ["asynchronous", "network-programming"]
|
||||
keywords = [
|
||||
"io",
|
||||
"async",
|
||||
"non-blocking",
|
||||
"futures",
|
||||
]
|
||||
categories = [
|
||||
"asynchronous",
|
||||
"network-programming",
|
||||
]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustc-args = ["--cfg", "tokio_unstable"]
|
||||
rustdoc-args = ["--cfg", "docsrs", "--cfg", "tokio_unstable"]
|
||||
rustc-args = [
|
||||
"--cfg",
|
||||
"tokio_unstable",
|
||||
]
|
||||
rustdoc-args = [
|
||||
"--cfg",
|
||||
"docsrs",
|
||||
"--cfg",
|
||||
"tokio_unstable",
|
||||
]
|
||||
|
||||
[package.metadata.playground]
|
||||
features = ["full", "test-util"]
|
||||
features = [
|
||||
"full",
|
||||
"test-util",
|
||||
]
|
||||
|
||||
[dependencies.bytes]
|
||||
version = "1.0.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.memchr]
|
||||
version = "2.2"
|
||||
optional = true
|
||||
|
||||
[dependencies.mio]
|
||||
version = "0.8.0"
|
||||
version = "0.8.6"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.num_cpus]
|
||||
version = "1.8.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.once_cell]
|
||||
version = "1.5.2"
|
||||
optional = true
|
||||
|
||||
[dependencies.parking_lot]
|
||||
version = "0.12.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.pin-project-lite]
|
||||
version = "0.2.0"
|
||||
|
||||
[dependencies.socket2]
|
||||
version = "0.4.4"
|
||||
features = ["all"]
|
||||
optional = true
|
||||
version = "0.2.7"
|
||||
|
||||
[dependencies.tokio-macros]
|
||||
version = "1.7.0"
|
||||
version = "~2.1.0"
|
||||
optional = true
|
||||
|
||||
[dev-dependencies.async-stream]
|
||||
version = "0.3"
|
||||
|
||||
|
@ -72,10 +85,7 @@ version = "0.3.0"
|
|||
features = ["async-await"]
|
||||
|
||||
[dev-dependencies.mockall]
|
||||
version = "0.10.2"
|
||||
|
||||
[dev-dependencies.tempfile]
|
||||
version = "3.1.0"
|
||||
version = "0.11.1"
|
||||
|
||||
[dev-dependencies.tokio-stream]
|
||||
version = "0.1"
|
||||
|
@ -83,58 +93,138 @@ version = "0.1"
|
|||
[dev-dependencies.tokio-test]
|
||||
version = "0.4.0"
|
||||
|
||||
[build-dependencies.autocfg]
|
||||
version = "1.1"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
fs = []
|
||||
full = ["fs", "io-util", "io-std", "macros", "net", "parking_lot", "process", "rt", "rt-multi-thread", "signal", "sync", "time"]
|
||||
full = [
|
||||
"fs",
|
||||
"io-util",
|
||||
"io-std",
|
||||
"macros",
|
||||
"net",
|
||||
"parking_lot",
|
||||
"process",
|
||||
"rt",
|
||||
"rt-multi-thread",
|
||||
"signal",
|
||||
"sync",
|
||||
"time",
|
||||
]
|
||||
io-std = []
|
||||
io-util = ["memchr", "bytes"]
|
||||
io-util = ["bytes"]
|
||||
macros = ["tokio-macros"]
|
||||
net = ["libc", "mio/os-poll", "mio/os-ext", "mio/net", "socket2", "winapi/namedpipeapi"]
|
||||
process = ["bytes", "once_cell", "libc", "mio/os-poll", "mio/os-ext", "mio/net", "signal-hook-registry", "winapi/threadpoollegacyapiset"]
|
||||
net = [
|
||||
"libc",
|
||||
"mio/os-poll",
|
||||
"mio/os-ext",
|
||||
"mio/net",
|
||||
"socket2",
|
||||
"windows-sys/Win32_Foundation",
|
||||
"windows-sys/Win32_Security",
|
||||
"windows-sys/Win32_Storage_FileSystem",
|
||||
"windows-sys/Win32_System_Pipes",
|
||||
"windows-sys/Win32_System_SystemServices",
|
||||
]
|
||||
process = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio/os-poll",
|
||||
"mio/os-ext",
|
||||
"mio/net",
|
||||
"signal-hook-registry",
|
||||
"windows-sys/Win32_Foundation",
|
||||
"windows-sys/Win32_System_Threading",
|
||||
"windows-sys/Win32_System_WindowsProgramming",
|
||||
]
|
||||
rt = []
|
||||
rt-multi-thread = ["num_cpus", "rt"]
|
||||
signal = ["once_cell", "libc", "mio/os-poll", "mio/net", "mio/os-ext", "signal-hook-registry", "winapi/consoleapi"]
|
||||
rt-multi-thread = [
|
||||
"num_cpus",
|
||||
"rt",
|
||||
]
|
||||
signal = [
|
||||
"libc",
|
||||
"mio/os-poll",
|
||||
"mio/net",
|
||||
"mio/os-ext",
|
||||
"signal-hook-registry",
|
||||
"windows-sys/Win32_Foundation",
|
||||
"windows-sys/Win32_System_Console",
|
||||
]
|
||||
stats = []
|
||||
sync = []
|
||||
test-util = ["rt", "sync", "time"]
|
||||
test-util = [
|
||||
"rt",
|
||||
"sync",
|
||||
"time",
|
||||
]
|
||||
time = []
|
||||
|
||||
[target."cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), not(target_os = \"wasi\")))".dev-dependencies.wasm-bindgen-test]
|
||||
version = "0.3.0"
|
||||
|
||||
[target."cfg(loom)".dev-dependencies.loom]
|
||||
version = "0.5.2"
|
||||
features = ["futures", "checkpoint"]
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dev-dependencies.proptest]
|
||||
version = "1"
|
||||
features = [
|
||||
"futures",
|
||||
"checkpoint",
|
||||
]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dev-dependencies.rand]
|
||||
[target."cfg(not(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\")))".dev-dependencies.rand]
|
||||
version = "0.8.0"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dev-dependencies.socket2]
|
||||
version = "0.4"
|
||||
[target."cfg(target_arch = \"wasm32\")".dev-dependencies.wasm-bindgen-test]
|
||||
version = "0.3.0"
|
||||
[target."cfg(not(any(target_arch = \"wasm32\", target_arch = \"wasm64\")))".dependencies.socket2]
|
||||
version = "0.4.9"
|
||||
features = ["all"]
|
||||
optional = true
|
||||
|
||||
[target."cfg(not(any(target_arch = \"wasm32\", target_arch = \"wasm64\")))".dev-dependencies.socket2]
|
||||
version = "0.4.9"
|
||||
|
||||
[target."cfg(not(any(target_arch = \"wasm32\", target_arch = \"wasm64\")))".dev-dependencies.tempfile]
|
||||
version = "3.1.0"
|
||||
|
||||
[target."cfg(target_os = \"freebsd\")".dev-dependencies.mio-aio]
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
features = ["tokio"]
|
||||
|
||||
[target."cfg(tokio_taskdump)".dependencies.backtrace]
|
||||
version = "0.3.58"
|
||||
|
||||
[target."cfg(tokio_unstable)".dependencies.tracing]
|
||||
version = "0.1.25"
|
||||
features = ["std"]
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[target."cfg(unix)".dependencies.libc]
|
||||
version = "0.2.42"
|
||||
version = "0.2.145"
|
||||
optional = true
|
||||
|
||||
[target."cfg(unix)".dependencies.signal-hook-registry]
|
||||
version = "1.1.1"
|
||||
optional = true
|
||||
|
||||
[target."cfg(unix)".dev-dependencies.libc]
|
||||
version = "0.2.42"
|
||||
version = "0.2.145"
|
||||
|
||||
[target."cfg(unix)".dev-dependencies.nix]
|
||||
version = "0.23"
|
||||
[target."cfg(windows)".dependencies.winapi]
|
||||
version = "0.3.8"
|
||||
optional = true
|
||||
version = "0.26"
|
||||
features = [
|
||||
"fs",
|
||||
"socket",
|
||||
]
|
||||
default-features = false
|
||||
[target."cfg(windows)".dev-dependencies.ntapi]
|
||||
version = "0.3.6"
|
||||
|
||||
[target."cfg(windows)".dependencies.windows-sys]
|
||||
version = "0.48"
|
||||
optional = true
|
||||
|
||||
[target."cfg(windows)".dev-dependencies.windows-sys]
|
||||
version = "0.48"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Security_Authorization",
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2022 Tokio Contributors
|
||||
Copyright (c) 2023 Tokio Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
|
|
|
@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
|||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.17.0", features = ["full"] }
|
||||
tokio = { version = "1.29.1", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
|
@ -161,11 +161,46 @@ several other libraries, including:
|
|||
[`mio`]: https://github.com/tokio-rs/mio
|
||||
[`bytes`]: https://github.com/tokio-rs/bytes
|
||||
|
||||
## Changelog
|
||||
|
||||
The Tokio repository contains multiple crates. Each crate has its own changelog.
|
||||
|
||||
* `tokio` - [view changelog](https://github.com/tokio-rs/tokio/blob/master/tokio/CHANGELOG.md)
|
||||
* `tokio-util` - [view changelog](https://github.com/tokio-rs/tokio/blob/master/tokio-util/CHANGELOG.md)
|
||||
* `tokio-stream` - [view changelog](https://github.com/tokio-rs/tokio/blob/master/tokio-stream/CHANGELOG.md)
|
||||
* `tokio-macros` - [view changelog](https://github.com/tokio-rs/tokio/blob/master/tokio-macros/CHANGELOG.md)
|
||||
* `tokio-test` - [view changelog](https://github.com/tokio-rs/tokio/blob/master/tokio-test/CHANGELOG.md)
|
||||
|
||||
## Supported Rust Versions
|
||||
|
||||
<!--
|
||||
When updating this, also update:
|
||||
- .github/workflows/ci.yml
|
||||
- CONTRIBUTING.md
|
||||
- README.md
|
||||
- tokio/README.md
|
||||
- tokio/Cargo.toml
|
||||
- tokio-util/Cargo.toml
|
||||
- tokio-test/Cargo.toml
|
||||
- tokio-stream/Cargo.toml
|
||||
-->
|
||||
|
||||
Tokio will keep a rolling MSRV (minimum supported rust version) policy of **at
|
||||
least** 6 months. When increasing the MSRV, the new Rust version must have been
|
||||
released at least six months ago. The current MSRV is 1.49.0.
|
||||
released at least six months ago. The current MSRV is 1.56.0.
|
||||
|
||||
Note that the MSRV is not increased automatically, and only as part of a minor
|
||||
release. The MSRV history for past minor releases can be found below:
|
||||
|
||||
* 1.27 to now - Rust 1.56
|
||||
* 1.17 to 1.26 - Rust 1.49
|
||||
* 1.15 to 1.16 - Rust 1.46
|
||||
* 1.0 to 1.14 - Rust 1.45
|
||||
|
||||
Note that although we try to avoid the situation where a dependency transitively
|
||||
increases the MSRV of Tokio, we do not guarantee that this does not happen.
|
||||
However, every minor release will have some set of versions of dependencies that
|
||||
works with the MSRV of that minor release.
|
||||
|
||||
## Release schedule
|
||||
|
||||
|
@ -180,18 +215,19 @@ warrants a patch release with a fix for the bug, it will be backported and
|
|||
released as a new patch release for each LTS minor version. Our current LTS
|
||||
releases are:
|
||||
|
||||
* `1.8.x` - LTS release until February 2022.
|
||||
* `1.14.x` - LTS release until June 2022.
|
||||
* `1.18.x` - LTS release until June 2023. (MSRV 1.49)
|
||||
* `1.20.x` - LTS release until September 2023. (MSRV 1.49)
|
||||
* `1.25.x` - LTS release until March 2024. (MSRV 1.49)
|
||||
|
||||
Each LTS release will continue to receive backported fixes for at least half a
|
||||
year. If you wish to use a fixed minor release in your project, we recommend
|
||||
that you use an LTS release.
|
||||
Each LTS release will continue to receive backported fixes for at least a year.
|
||||
If you wish to use a fixed minor release in your project, we recommend that you
|
||||
use an LTS release.
|
||||
|
||||
To use a fixed minor version, you can specify the version with a tilde. For
|
||||
example, to specify that you wish to use the newest `1.8.x` patch release, you
|
||||
example, to specify that you wish to use the newest `1.18.x` patch release, you
|
||||
can use the following dependency specification:
|
||||
```text
|
||||
tokio = { version = "~1.8", features = [...] }
|
||||
tokio = { version = "~1.18", features = [...] }
|
||||
```
|
||||
|
||||
## License
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
use autocfg::AutoCfg;
|
||||
|
||||
const CONST_THREAD_LOCAL_PROBE: &str = r#"
|
||||
{
|
||||
thread_local! {
|
||||
static MY_PROBE: usize = const { 10 };
|
||||
}
|
||||
|
||||
MY_PROBE.with(|val| *val)
|
||||
}
|
||||
"#;
|
||||
|
||||
const CONST_MUTEX_NEW_PROBE: &str = r#"
|
||||
{
|
||||
static MY_MUTEX: ::std::sync::Mutex<i32> = ::std::sync::Mutex::new(1);
|
||||
*MY_MUTEX.lock().unwrap()
|
||||
}
|
||||
"#;
|
||||
|
||||
const AS_FD_PROBE: &str = r#"
|
||||
{
|
||||
#[allow(unused_imports)]
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::prelude::AsFd as _;
|
||||
#[allow(unused_imports)]
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::prelude::AsSocket as _;
|
||||
#[allow(unused_imports)]
|
||||
#[cfg(target_os = "wasi")]
|
||||
use std::os::wasi::prelude::AsFd as _;
|
||||
}
|
||||
"#;
|
||||
|
||||
const TARGET_HAS_ATOMIC_PROBE: &str = r#"
|
||||
{
|
||||
#[cfg(target_has_atomic = "ptr")]
|
||||
let _ = ();
|
||||
}
|
||||
"#;
|
||||
|
||||
const TARGET_ATOMIC_U64_PROBE: &str = r#"
|
||||
{
|
||||
#[allow(unused_imports)]
|
||||
use std::sync::atomic::AtomicU64 as _;
|
||||
}
|
||||
"#;
|
||||
|
||||
fn main() {
|
||||
let mut enable_const_thread_local = false;
|
||||
let mut enable_target_has_atomic = false;
|
||||
let mut enable_const_mutex_new = false;
|
||||
let mut enable_as_fd = false;
|
||||
let mut target_needs_atomic_u64_fallback = false;
|
||||
|
||||
match AutoCfg::new() {
|
||||
Ok(ac) => {
|
||||
// These checks prefer to call only `probe_rustc_version` if that is
|
||||
// enough to determine whether the feature is supported. This is
|
||||
// because the `probe_expression` call involves a call to rustc,
|
||||
// which the `probe_rustc_version` call avoids.
|
||||
|
||||
// Const-initialized thread locals were stabilized in 1.59.
|
||||
if ac.probe_rustc_version(1, 60) {
|
||||
enable_const_thread_local = true;
|
||||
} else if ac.probe_rustc_version(1, 59) {
|
||||
// This compiler claims to be 1.59, but there are some nightly
|
||||
// compilers that claim to be 1.59 without supporting the
|
||||
// feature. Explicitly probe to check if code using them
|
||||
// compiles.
|
||||
//
|
||||
// The oldest nightly that supports the feature is 2021-12-06.
|
||||
if ac.probe_expression(CONST_THREAD_LOCAL_PROBE) {
|
||||
enable_const_thread_local = true;
|
||||
}
|
||||
}
|
||||
|
||||
// The `target_has_atomic` cfg was stabilized in 1.60.
|
||||
if ac.probe_rustc_version(1, 61) {
|
||||
enable_target_has_atomic = true;
|
||||
} else if ac.probe_rustc_version(1, 60) {
|
||||
// This compiler claims to be 1.60, but there are some nightly
|
||||
// compilers that claim to be 1.60 without supporting the
|
||||
// feature. Explicitly probe to check if code using them
|
||||
// compiles.
|
||||
//
|
||||
// The oldest nightly that supports the feature is 2022-02-11.
|
||||
if ac.probe_expression(TARGET_HAS_ATOMIC_PROBE) {
|
||||
enable_target_has_atomic = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we can't tell using `target_has_atomic`, tell if the target
|
||||
// has `AtomicU64` by trying to use it.
|
||||
if !enable_target_has_atomic && !ac.probe_expression(TARGET_ATOMIC_U64_PROBE) {
|
||||
target_needs_atomic_u64_fallback = true;
|
||||
}
|
||||
|
||||
// The `Mutex::new` method was made const in 1.63.
|
||||
if ac.probe_rustc_version(1, 64) {
|
||||
enable_const_mutex_new = true;
|
||||
} else if ac.probe_rustc_version(1, 63) {
|
||||
// This compiler claims to be 1.63, but there are some nightly
|
||||
// compilers that claim to be 1.63 without supporting the
|
||||
// feature. Explicitly probe to check if code using them
|
||||
// compiles.
|
||||
//
|
||||
// The oldest nightly that supports the feature is 2022-06-20.
|
||||
if ac.probe_expression(CONST_MUTEX_NEW_PROBE) {
|
||||
enable_const_mutex_new = true;
|
||||
}
|
||||
}
|
||||
|
||||
// The `AsFd` family of traits were made stable in 1.63.
|
||||
if ac.probe_rustc_version(1, 64) {
|
||||
enable_as_fd = true;
|
||||
} else if ac.probe_rustc_version(1, 63) {
|
||||
// This compiler claims to be 1.63, but there are some nightly
|
||||
// compilers that claim to be 1.63 without supporting the
|
||||
// feature. Explicitly probe to check if code using them
|
||||
// compiles.
|
||||
//
|
||||
// The oldest nightly that supports the feature is 2022-06-16.
|
||||
if ac.probe_expression(AS_FD_PROBE) {
|
||||
enable_as_fd = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
// If we couldn't detect the compiler version and features, just
|
||||
// print a warning. This isn't a fatal error: we can still build
|
||||
// Tokio, we just can't enable cfgs automatically.
|
||||
println!(
|
||||
"cargo:warning=tokio: failed to detect compiler features: {}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !enable_const_thread_local {
|
||||
// To disable this feature on compilers that support it, you can
|
||||
// explicitly pass this flag with the following environment variable:
|
||||
//
|
||||
// RUSTFLAGS="--cfg tokio_no_const_thread_local"
|
||||
autocfg::emit("tokio_no_const_thread_local")
|
||||
}
|
||||
|
||||
if !enable_target_has_atomic {
|
||||
// To disable this feature on compilers that support it, you can
|
||||
// explicitly pass this flag with the following environment variable:
|
||||
//
|
||||
// RUSTFLAGS="--cfg tokio_no_target_has_atomic"
|
||||
autocfg::emit("tokio_no_target_has_atomic")
|
||||
}
|
||||
|
||||
if !enable_const_mutex_new {
|
||||
// To disable this feature on compilers that support it, you can
|
||||
// explicitly pass this flag with the following environment variable:
|
||||
//
|
||||
// RUSTFLAGS="--cfg tokio_no_const_mutex_new"
|
||||
autocfg::emit("tokio_no_const_mutex_new")
|
||||
}
|
||||
|
||||
if !enable_as_fd {
|
||||
// To disable this feature on compilers that support it, you can
|
||||
// explicitly pass this flag with the following environment variable:
|
||||
//
|
||||
// RUSTFLAGS="--cfg tokio_no_as_fd"
|
||||
autocfg::emit("tokio_no_as_fd");
|
||||
}
|
||||
|
||||
if target_needs_atomic_u64_fallback {
|
||||
// To disable this feature on compilers that support it, you can
|
||||
// explicitly pass this flag with the following environment variable:
|
||||
//
|
||||
// RUSTFLAGS="--cfg tokio_no_atomic_u64"
|
||||
autocfg::emit("tokio_no_atomic_u64")
|
||||
}
|
||||
|
||||
let target = ::std::env::var("TARGET").unwrap_or_default();
|
||||
|
||||
// We emit cfgs instead of using `target_family = "wasm"` that requires Rust 1.54.
|
||||
// Note that these cfgs are unavailable in `Cargo.toml`.
|
||||
if target.starts_with("wasm") {
|
||||
autocfg::emit("tokio_wasm");
|
||||
if target.contains("wasi") {
|
||||
autocfg::emit("tokio_wasi");
|
||||
} else {
|
||||
autocfg::emit("tokio_wasm_not_wasi");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -188,12 +188,12 @@ readiness, the driver's tick is packed into the atomic `usize`.
|
|||
The `ScheduledIo` readiness `AtomicUsize` is structured as:
|
||||
|
||||
```
|
||||
| reserved | generation | driver tick | readinesss |
|
||||
|----------+------------+--------------+------------|
|
||||
| 1 bit | 7 bits + 8 bits + 16 bits |
|
||||
| shutdown | generation | driver tick | readiness |
|
||||
|----------+------------+--------------+-----------|
|
||||
| 1 bit | 7 bits + 8 bits + 16 bits |
|
||||
```
|
||||
|
||||
The `reserved` and `generation` components exist today.
|
||||
The `shutdown` and `generation` components exist today.
|
||||
|
||||
The `readiness()` function returns a `ReadyEvent` value. This value includes the
|
||||
`tick` component read with the resource's readiness value. When
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# This config file is for the `cargo-check-external-types` tool that is run in CI.
|
||||
|
||||
# The following are types that are allowed to be exposed in Tokio's public API.
|
||||
# The standard library is allowed by default.
|
||||
allowed_external_types = [
|
||||
"bytes::buf::buf_impl::Buf",
|
||||
"bytes::buf::buf_mut::BufMut",
|
||||
|
||||
"tokio_macros::*",
|
||||
]
|
||||
|
|
@ -21,4 +21,3 @@
|
|||
pub enum NotDefinedHere {}
|
||||
|
||||
pub mod os;
|
||||
pub mod winapi;
|
||||
|
|
|
@ -13,7 +13,7 @@ pub mod windows {
|
|||
|
||||
/// See [std::os::windows::io::AsRawHandle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html)
|
||||
pub trait AsRawHandle {
|
||||
/// See [std::os::windows::io::FromRawHandle::from_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html#tymethod.as_raw_handle)
|
||||
/// See [std::os::windows::io::AsRawHandle::as_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html#tymethod.as_raw_handle)
|
||||
fn as_raw_handle(&self) -> RawHandle;
|
||||
}
|
||||
|
||||
|
@ -22,5 +22,44 @@ pub mod windows {
|
|||
/// See [std::os::windows::io::FromRawHandle::from_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html#tymethod.from_raw_handle)
|
||||
unsafe fn from_raw_handle(handle: RawHandle) -> Self;
|
||||
}
|
||||
|
||||
/// See [std::os::windows::io::RawSocket](https://doc.rust-lang.org/std/os/windows/io/type.RawSocket.html)
|
||||
pub type RawSocket = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [std::os::windows::io::AsRawSocket](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html)
|
||||
pub trait AsRawSocket {
|
||||
/// See [std::os::windows::io::AsRawSocket::as_raw_socket](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html#tymethod.as_raw_socket)
|
||||
fn as_raw_socket(&self) -> RawSocket;
|
||||
}
|
||||
|
||||
/// See [std::os::windows::io::FromRawSocket](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawSocket.html)
|
||||
pub trait FromRawSocket {
|
||||
/// See [std::os::windows::io::FromRawSocket::from_raw_socket](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawSocket.html#tymethod.from_raw_socket)
|
||||
unsafe fn from_raw_socket(sock: RawSocket) -> Self;
|
||||
}
|
||||
|
||||
/// See [std::os::windows::io::IntoRawSocket](https://doc.rust-lang.org/std/os/windows/io/trait.IntoRawSocket.html)
|
||||
pub trait IntoRawSocket {
|
||||
/// See [std::os::windows::io::IntoRawSocket::into_raw_socket](https://doc.rust-lang.org/std/os/windows/io/trait.IntoRawSocket.html#tymethod.into_raw_socket)
|
||||
fn into_raw_socket(self) -> RawSocket;
|
||||
}
|
||||
|
||||
/// See [std::os::windows::io::BorrowedHandle](https://doc.rust-lang.org/std/os/windows/io/struct.BorrowedHandle.html)
|
||||
pub type BorrowedHandle<'handle> = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [std::os::windows::io::AsHandle](https://doc.rust-lang.org/std/os/windows/io/trait.AsHandle.html)
|
||||
pub trait AsHandle {
|
||||
/// See [std::os::windows::io::AsHandle::as_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsHandle.html#tymethod.as_handle)
|
||||
fn as_handle(&self) -> BorrowedHandle<'_>;
|
||||
}
|
||||
|
||||
/// See [std::os::windows::io::BorrowedSocket](https://doc.rust-lang.org/std/os/windows/io/struct.BorrowedSocket.html)
|
||||
pub type BorrowedSocket<'socket> = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [std::os::windows::io::AsSocket](https://doc.rust-lang.org/std/os/windows/io/trait.AsSocket.html)
|
||||
pub trait AsSocket {
|
||||
/// See [std::os::windows::io::AsSocket::as_socket](https://doc.rust-lang.org/std/os/windows/io/trait.AsSocket.html#tymethod.as_socket)
|
||||
fn as_socket(&self) -> BorrowedSocket<'_>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
//! See [winapi].
|
||||
//!
|
||||
//! [winapi]: https://docs.rs/winapi
|
||||
|
||||
/// See [winapi::shared](https://docs.rs/winapi/*/winapi/shared/index.html).
|
||||
pub mod shared {
|
||||
/// See [winapi::shared::winerror](https://docs.rs/winapi/*/winapi/shared/winerror/index.html).
|
||||
#[allow(non_camel_case_types)]
|
||||
pub mod winerror {
|
||||
/// See [winapi::shared::winerror::ERROR_ACCESS_DENIED][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_ACCESS_DENIED.html
|
||||
pub type ERROR_ACCESS_DENIED = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [winapi::shared::winerror::ERROR_PIPE_BUSY][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_PIPE_BUSY.html
|
||||
pub type ERROR_PIPE_BUSY = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [winapi::shared::winerror::ERROR_MORE_DATA][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_MORE_DATA.html
|
||||
pub type ERROR_MORE_DATA = crate::doc::NotDefinedHere;
|
||||
}
|
||||
}
|
||||
|
||||
/// See [winapi::um](https://docs.rs/winapi/*/winapi/um/index.html).
|
||||
pub mod um {
|
||||
/// See [winapi::um::winbase](https://docs.rs/winapi/*/winapi/um/winbase/index.html).
|
||||
#[allow(non_camel_case_types)]
|
||||
pub mod winbase {
|
||||
/// See [winapi::um::winbase::PIPE_TYPE_MESSAGE][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_TYPE_MESSAGE.html
|
||||
pub type PIPE_TYPE_MESSAGE = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [winapi::um::winbase::PIPE_TYPE_BYTE][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_TYPE_BYTE.html
|
||||
pub type PIPE_TYPE_BYTE = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [winapi::um::winbase::PIPE_CLIENT_END][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_CLIENT_END.html
|
||||
pub type PIPE_CLIENT_END = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [winapi::um::winbase::PIPE_SERVER_END][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_SERVER_END.html
|
||||
pub type PIPE_SERVER_END = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [winapi::um::winbase::SECURITY_IDENTIFICATION][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.SECURITY_IDENTIFICATION.html
|
||||
pub type SECURITY_IDENTIFICATION = crate::doc::NotDefinedHere;
|
||||
}
|
||||
|
||||
/// See [winapi::um::minwinbase](https://docs.rs/winapi/*/winapi/um/minwinbase/index.html).
|
||||
#[allow(non_camel_case_types)]
|
||||
pub mod minwinbase {
|
||||
/// See [winapi::um::minwinbase::SECURITY_ATTRIBUTES][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/um/minwinbase/constant.SECURITY_ATTRIBUTES.html
|
||||
pub type SECURITY_ATTRIBUTES = crate::doc::NotDefinedHere;
|
||||
}
|
||||
}
|
|
@ -399,6 +399,7 @@ impl File {
|
|||
/// # }
|
||||
/// ```
|
||||
pub async fn try_clone(&self) -> io::Result<File> {
|
||||
self.inner.lock().await.complete_inflight().await;
|
||||
let std = self.std.clone();
|
||||
let std_file = asyncify(move || std.try_clone()).await?;
|
||||
Ok(File::from_std(std_file))
|
||||
|
@ -498,6 +499,7 @@ impl AsyncRead for File {
|
|||
cx: &mut Context<'_>,
|
||||
dst: &mut ReadBuf<'_>,
|
||||
) -> Poll<io::Result<()>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
let me = self.get_mut();
|
||||
let inner = me.inner.get_mut();
|
||||
|
||||
|
@ -565,34 +567,36 @@ impl AsyncSeek for File {
|
|||
let me = self.get_mut();
|
||||
let inner = me.inner.get_mut();
|
||||
|
||||
loop {
|
||||
match inner.state {
|
||||
Busy(_) => panic!("must wait for poll_complete before calling start_seek"),
|
||||
Idle(ref mut buf_cell) => {
|
||||
let mut buf = buf_cell.take().unwrap();
|
||||
match inner.state {
|
||||
Busy(_) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"other file operation is pending, call poll_complete before start_seek",
|
||||
)),
|
||||
Idle(ref mut buf_cell) => {
|
||||
let mut buf = buf_cell.take().unwrap();
|
||||
|
||||
// Factor in any unread data from the buf
|
||||
if !buf.is_empty() {
|
||||
let n = buf.discard_read();
|
||||
// Factor in any unread data from the buf
|
||||
if !buf.is_empty() {
|
||||
let n = buf.discard_read();
|
||||
|
||||
if let SeekFrom::Current(ref mut offset) = pos {
|
||||
*offset += n;
|
||||
}
|
||||
if let SeekFrom::Current(ref mut offset) = pos {
|
||||
*offset += n;
|
||||
}
|
||||
|
||||
let std = me.std.clone();
|
||||
|
||||
inner.state = Busy(spawn_blocking(move || {
|
||||
let res = (&*std).seek(pos);
|
||||
(Operation::Seek(res), buf)
|
||||
}));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let std = me.std.clone();
|
||||
|
||||
inner.state = Busy(spawn_blocking(move || {
|
||||
let res = (&*std).seek(pos);
|
||||
(Operation::Seek(res), buf)
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
let inner = self.inner.get_mut();
|
||||
|
||||
loop {
|
||||
|
@ -628,6 +632,7 @@ impl AsyncWrite for File {
|
|||
cx: &mut Context<'_>,
|
||||
src: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
let me = self.get_mut();
|
||||
let inner = me.inner.get_mut();
|
||||
|
||||
|
@ -694,11 +699,13 @@ impl AsyncWrite for File {
|
|||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
let inner = self.inner.get_mut();
|
||||
inner.poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
self.poll_flush(cx)
|
||||
}
|
||||
}
|
||||
|
@ -724,6 +731,15 @@ impl std::os::unix::io::AsRawFd for File {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(tokio_no_as_fd)))]
|
||||
impl std::os::unix::io::AsFd for File {
|
||||
fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> {
|
||||
unsafe {
|
||||
std::os::unix::io::BorrowedFd::borrow_raw(std::os::unix::io::AsRawFd::as_raw_fd(self))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::FromRawFd for File {
|
||||
unsafe fn from_raw_fd(fd: std::os::unix::io::RawFd) -> Self {
|
||||
|
@ -731,17 +747,32 @@ impl std::os::unix::io::FromRawFd for File {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::AsRawHandle for File {
|
||||
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
|
||||
self.std.as_raw_handle()
|
||||
}
|
||||
}
|
||||
cfg_windows! {
|
||||
use crate::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsHandle, BorrowedHandle};
|
||||
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::FromRawHandle for File {
|
||||
unsafe fn from_raw_handle(handle: std::os::windows::io::RawHandle) -> Self {
|
||||
StdFile::from_raw_handle(handle).into()
|
||||
impl AsRawHandle for File {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
self.std.as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for File {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe {
|
||||
BorrowedHandle::borrow_raw(
|
||||
AsRawHandle::as_raw_handle(self),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawHandle for File {
|
||||
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
|
||||
StdFile::from_raw_handle(handle).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -749,8 +780,18 @@ impl Inner {
|
|||
async fn complete_inflight(&mut self) {
|
||||
use crate::future::poll_fn;
|
||||
|
||||
if let Err(e) = poll_fn(|cx| Pin::new(&mut *self).poll_flush(cx)).await {
|
||||
self.last_write_err = Some(e.kind());
|
||||
poll_fn(|cx| self.poll_complete_inflight(cx)).await
|
||||
}
|
||||
|
||||
fn poll_complete_inflight(&mut self, cx: &mut Context<'_>) -> Poll<()> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
match self.poll_flush(cx) {
|
||||
Poll::Ready(Err(e)) => {
|
||||
self.last_write_err = Some(e.kind());
|
||||
Poll::Ready(())
|
||||
}
|
||||
Poll::Ready(Ok(())) => Poll::Ready(()),
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -231,12 +231,12 @@ fn flush_while_idle() {
|
|||
#[cfg_attr(miri, ignore)] // takes a really long time with miri
|
||||
fn read_with_buffer_larger_than_max() {
|
||||
// Chunks
|
||||
let chunk_a = 16 * 1024;
|
||||
let chunk_a = crate::io::blocking::MAX_BUF;
|
||||
let chunk_b = chunk_a * 2;
|
||||
let chunk_c = chunk_a * 3;
|
||||
let chunk_d = chunk_a * 4;
|
||||
|
||||
assert_eq!(chunk_d / 1024, 64);
|
||||
assert_eq!(chunk_d / 1024 / 1024, 8);
|
||||
|
||||
let mut data = vec![];
|
||||
for i in 0..(chunk_d - 1) {
|
||||
|
@ -303,12 +303,12 @@ fn read_with_buffer_larger_than_max() {
|
|||
#[cfg_attr(miri, ignore)] // takes a really long time with miri
|
||||
fn write_with_buffer_larger_than_max() {
|
||||
// Chunks
|
||||
let chunk_a = 16 * 1024;
|
||||
let chunk_a = crate::io::blocking::MAX_BUF;
|
||||
let chunk_b = chunk_a * 2;
|
||||
let chunk_c = chunk_a * 3;
|
||||
let chunk_d = chunk_a * 4;
|
||||
|
||||
assert_eq!(chunk_d / 1024, 64);
|
||||
assert_eq!(chunk_d / 1024 / 1024, 8);
|
||||
|
||||
let mut data = vec![];
|
||||
for i in 0..(chunk_d - 1) {
|
||||
|
@ -955,3 +955,24 @@ fn partial_read_set_len_ok() {
|
|||
assert_eq!(n, FOO.len());
|
||||
assert_eq!(&buf[..n], FOO);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn busy_file_seek_error() {
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
|
||||
let mut file = crate::io::BufReader::new(File::from_std(file));
|
||||
{
|
||||
let mut t = task::spawn(file.write(HELLO));
|
||||
assert_ready_ok!(t.poll());
|
||||
}
|
||||
|
||||
pool::run_one();
|
||||
|
||||
let mut t = task::spawn(file.seek(SeekFrom::Start(0)));
|
||||
assert_ready_err!(t.poll());
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ impl Write for &'_ MockFile {
|
|||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
tokio_thread_local! {
|
||||
static QUEUE: RefCell<VecDeque<Box<dyn FnOnce() + Send>>> = RefCell::new(VecDeque::new())
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,24 @@
|
|||
//! `std::io::ErrorKind::WouldBlock` if a *worker* thread can not be converted
|
||||
//! to a *backup* thread immediately.
|
||||
//!
|
||||
//! **Warning**: These adapters may create a large number of temporary tasks,
|
||||
//! especially when reading large files. When performing a lot of operations
|
||||
//! in one batch, it may be significantly faster to use [`spawn_blocking`]
|
||||
//! directly:
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio::fs::File;
|
||||
//! use std::io::{BufReader, BufRead};
|
||||
//! async fn count_lines(file: File) -> Result<usize, std::io::Error> {
|
||||
//! let file = file.into_std().await;
|
||||
//! tokio::task::spawn_blocking(move || {
|
||||
//! let line_count = BufReader::new(file).lines().count();
|
||||
//! Ok(line_count)
|
||||
//! }).await?
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [`spawn_blocking`]: fn@crate::task::spawn_blocking
|
||||
//! [`AsyncRead`]: trait@crate::io::AsyncRead
|
||||
|
||||
mod canonicalize;
|
||||
|
@ -84,6 +102,9 @@ pub use self::write::write;
|
|||
mod copy;
|
||||
pub use self::copy::copy;
|
||||
|
||||
mod try_exists;
|
||||
pub use self::try_exists::try_exists;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mocks;
|
||||
|
||||
|
@ -94,9 +115,7 @@ feature! {
|
|||
pub use self::symlink::symlink;
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![windows]
|
||||
|
||||
cfg_windows! {
|
||||
mod symlink_dir;
|
||||
pub use self::symlink_dir::symlink_dir;
|
||||
|
||||
|
|
|
@ -10,6 +10,11 @@ use mock_open_options::MockOpenOptions as StdOpenOptions;
|
|||
#[cfg(not(test))]
|
||||
use std::fs::OpenOptions as StdOpenOptions;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::fs::OpenOptionsExt;
|
||||
|
||||
/// Options and flags which can be used to configure how a file is opened.
|
||||
///
|
||||
/// This builder exposes the ability to configure how a [`File`] is opened and
|
||||
|
@ -399,8 +404,6 @@ impl OpenOptions {
|
|||
feature! {
|
||||
#![unix]
|
||||
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
|
||||
impl OpenOptions {
|
||||
/// Sets the mode bits that a new file will be created with.
|
||||
///
|
||||
|
@ -464,11 +467,7 @@ feature! {
|
|||
}
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![windows]
|
||||
|
||||
use std::os::windows::fs::OpenOptionsExt;
|
||||
|
||||
cfg_windows! {
|
||||
impl OpenOptions {
|
||||
/// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
|
||||
/// with the specified value.
|
||||
|
@ -542,7 +541,7 @@ feature! {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use winapi::um::winbase::FILE_FLAG_DELETE_ON_CLOSE;
|
||||
/// use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_DELETE_ON_CLOSE;
|
||||
/// use tokio::fs::OpenOptions;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
|
@ -581,7 +580,7 @@ feature! {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use winapi::um::winnt::FILE_ATTRIBUTE_HIDDEN;
|
||||
/// use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN;
|
||||
/// use tokio::fs::OpenOptions;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
|
@ -624,7 +623,7 @@ feature! {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use winapi::um::winbase::SECURITY_IDENTIFICATION;
|
||||
/// use windows_sys::Win32::Storage::FileSystem::SECURITY_IDENTIFICATION;
|
||||
/// use tokio::fs::OpenOptions;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(unreachable_pub)]
|
||||
//! Mock version of std::fs::OpenOptions;
|
||||
use mockall::mock;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::fs::asyncify;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{FileType, Metadata};
|
||||
use std::future::Future;
|
||||
|
@ -19,6 +20,8 @@ use crate::blocking::spawn_blocking;
|
|||
#[cfg(not(test))]
|
||||
use crate::blocking::JoinHandle;
|
||||
|
||||
const CHUNK_SIZE: usize = 32;
|
||||
|
||||
/// Returns a stream over the entries within a directory.
|
||||
///
|
||||
/// This is an async version of [`std::fs::read_dir`](std::fs::read_dir)
|
||||
|
@ -29,12 +32,17 @@ use crate::blocking::JoinHandle;
|
|||
/// [`spawn_blocking`]: crate::task::spawn_blocking
|
||||
pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let std = asyncify(|| std::fs::read_dir(path)).await?;
|
||||
asyncify(|| -> io::Result<ReadDir> {
|
||||
let mut std = std::fs::read_dir(path)?;
|
||||
let mut buf = VecDeque::with_capacity(CHUNK_SIZE);
|
||||
let remain = ReadDir::next_chunk(&mut buf, &mut std);
|
||||
|
||||
Ok(ReadDir(State::Idle(Some(std))))
|
||||
Ok(ReadDir(State::Idle(Some((buf, std, remain)))))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Reads the the entries in a directory.
|
||||
/// Reads the entries in a directory.
|
||||
///
|
||||
/// This struct is returned from the [`read_dir`] function of this module and
|
||||
/// will yield instances of [`DirEntry`]. Through a [`DirEntry`] information
|
||||
|
@ -58,8 +66,8 @@ pub struct ReadDir(State);
|
|||
|
||||
#[derive(Debug)]
|
||||
enum State {
|
||||
Idle(Option<std::fs::ReadDir>),
|
||||
Pending(JoinHandle<(Option<io::Result<std::fs::DirEntry>>, std::fs::ReadDir)>),
|
||||
Idle(Option<(VecDeque<io::Result<DirEntry>>, std::fs::ReadDir, bool)>),
|
||||
Pending(JoinHandle<(VecDeque<io::Result<DirEntry>>, std::fs::ReadDir, bool)>),
|
||||
}
|
||||
|
||||
impl ReadDir {
|
||||
|
@ -94,29 +102,58 @@ impl ReadDir {
|
|||
pub fn poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<Option<DirEntry>>> {
|
||||
loop {
|
||||
match self.0 {
|
||||
State::Idle(ref mut std) => {
|
||||
let mut std = std.take().unwrap();
|
||||
State::Idle(ref mut data) => {
|
||||
let (buf, _, ref remain) = data.as_mut().unwrap();
|
||||
|
||||
if let Some(ent) = buf.pop_front() {
|
||||
return Poll::Ready(ent.map(Some));
|
||||
} else if !remain {
|
||||
return Poll::Ready(Ok(None));
|
||||
}
|
||||
|
||||
let (mut buf, mut std, _) = data.take().unwrap();
|
||||
|
||||
self.0 = State::Pending(spawn_blocking(move || {
|
||||
let ret = std.next();
|
||||
(ret, std)
|
||||
let remain = ReadDir::next_chunk(&mut buf, &mut std);
|
||||
(buf, std, remain)
|
||||
}));
|
||||
}
|
||||
State::Pending(ref mut rx) => {
|
||||
let (ret, std) = ready!(Pin::new(rx).poll(cx))?;
|
||||
self.0 = State::Idle(Some(std));
|
||||
|
||||
let ret = match ret {
|
||||
Some(Ok(std)) => Ok(Some(DirEntry(Arc::new(std)))),
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(None),
|
||||
};
|
||||
|
||||
return Poll::Ready(ret);
|
||||
self.0 = State::Idle(Some(ready!(Pin::new(rx).poll(cx))?));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_chunk(buf: &mut VecDeque<io::Result<DirEntry>>, std: &mut std::fs::ReadDir) -> bool {
|
||||
for _ in 0..CHUNK_SIZE {
|
||||
let ret = match std.next() {
|
||||
Some(ret) => ret,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let success = ret.is_ok();
|
||||
|
||||
buf.push_back(ret.map(|std| DirEntry {
|
||||
#[cfg(not(any(
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
target_os = "haiku",
|
||||
target_os = "vxworks",
|
||||
target_os = "nto",
|
||||
target_os = "vita",
|
||||
)))]
|
||||
file_type: std.file_type().ok(),
|
||||
std: Arc::new(std),
|
||||
}));
|
||||
|
||||
if !success {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
feature! {
|
||||
|
@ -160,7 +197,18 @@ feature! {
|
|||
/// filesystem. Each entry can be inspected via methods to learn about the full
|
||||
/// path or possibly other metadata through per-platform extension traits.
|
||||
#[derive(Debug)]
|
||||
pub struct DirEntry(Arc<std::fs::DirEntry>);
|
||||
pub struct DirEntry {
|
||||
#[cfg(not(any(
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
target_os = "haiku",
|
||||
target_os = "vxworks",
|
||||
target_os = "nto",
|
||||
target_os = "vita",
|
||||
)))]
|
||||
file_type: Option<FileType>,
|
||||
std: Arc<std::fs::DirEntry>,
|
||||
}
|
||||
|
||||
impl DirEntry {
|
||||
/// Returns the full path to the file that this entry represents.
|
||||
|
@ -193,7 +241,7 @@ impl DirEntry {
|
|||
///
|
||||
/// The exact text, of course, depends on what files you have in `.`.
|
||||
pub fn path(&self) -> PathBuf {
|
||||
self.0.path()
|
||||
self.std.path()
|
||||
}
|
||||
|
||||
/// Returns the bare file name of this directory entry without any other
|
||||
|
@ -214,7 +262,7 @@ impl DirEntry {
|
|||
/// # }
|
||||
/// ```
|
||||
pub fn file_name(&self) -> OsString {
|
||||
self.0.file_name()
|
||||
self.std.file_name()
|
||||
}
|
||||
|
||||
/// Returns the metadata for the file that this entry points at.
|
||||
|
@ -248,7 +296,7 @@ impl DirEntry {
|
|||
/// # }
|
||||
/// ```
|
||||
pub async fn metadata(&self) -> io::Result<Metadata> {
|
||||
let std = self.0.clone();
|
||||
let std = self.std.clone();
|
||||
asyncify(move || std.metadata()).await
|
||||
}
|
||||
|
||||
|
@ -283,13 +331,25 @@ impl DirEntry {
|
|||
/// # }
|
||||
/// ```
|
||||
pub async fn file_type(&self) -> io::Result<FileType> {
|
||||
let std = self.0.clone();
|
||||
#[cfg(not(any(
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
target_os = "haiku",
|
||||
target_os = "vxworks",
|
||||
target_os = "nto",
|
||||
target_os = "vita",
|
||||
)))]
|
||||
if let Some(file_type) = self.file_type {
|
||||
return Ok(file_type);
|
||||
}
|
||||
|
||||
let std = self.std.clone();
|
||||
asyncify(move || std.file_type()).await
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying `std::fs::DirEntry`.
|
||||
#[cfg(unix)]
|
||||
pub(super) fn as_inner(&self) -> &std::fs::DirEntry {
|
||||
&self.0
|
||||
&self.std
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use std::path::Path;
|
|||
///
|
||||
/// This is an async version of [`std::os::windows::fs::symlink_dir`][std]
|
||||
///
|
||||
/// [std]: std::os::windows::fs::symlink_dir
|
||||
/// [std]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
|
||||
pub async fn symlink_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
let src = src.as_ref().to_owned();
|
||||
let dst = dst.as_ref().to_owned();
|
||||
|
|
|
@ -10,7 +10,7 @@ use std::path::Path;
|
|||
///
|
||||
/// This is an async version of [`std::os::windows::fs::symlink_file`][std]
|
||||
///
|
||||
/// [std]: std::os::windows::fs::symlink_file
|
||||
/// [std]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
|
||||
pub async fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
let src = src.as_ref().to_owned();
|
||||
let dst = dst.as_ref().to_owned();
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
use crate::fs::asyncify;
|
||||
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
/// Returns `Ok(true)` if the path points at an existing entity.
|
||||
///
|
||||
/// This function will traverse symbolic links to query information about the
|
||||
/// destination file. In case of broken symbolic links this will return `Ok(false)`.
|
||||
///
|
||||
/// This is the async equivalent of [`std::path::Path::try_exists`][std].
|
||||
///
|
||||
/// [std]: fn@std::path::Path::try_exists
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs;
|
||||
///
|
||||
/// # async fn dox() -> std::io::Result<()> {
|
||||
/// fs::try_exists("foo.txt").await?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn try_exists(path: impl AsRef<Path>) -> io::Result<bool> {
|
||||
let path = path.as_ref().to_owned();
|
||||
// std's Path::try_exists is not available for current Rust min supported version.
|
||||
// Current implementation is based on its internal implementation instead.
|
||||
match asyncify(move || std::fs::metadata(path)).await {
|
||||
Ok(_) => Ok(true),
|
||||
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
|
@ -1,15 +1,22 @@
|
|||
use std::future::Future;
|
||||
|
||||
cfg_rt! {
|
||||
#[track_caller]
|
||||
pub(crate) fn block_on<F: Future>(f: F) -> F::Output {
|
||||
let mut e = crate::runtime::enter::enter(false);
|
||||
let mut e = crate::runtime::context::try_enter_blocking_region().expect(
|
||||
"Cannot block the current thread from within a runtime. This \
|
||||
happens because a function attempted to block the current \
|
||||
thread while the thread is being used to drive asynchronous \
|
||||
tasks."
|
||||
);
|
||||
e.block_on(f).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
cfg_not_rt! {
|
||||
#[track_caller]
|
||||
pub(crate) fn block_on<F: Future>(f: F) -> F::Output {
|
||||
let mut park = crate::park::thread::CachedParkThread::new();
|
||||
let mut park = crate::runtime::park::CachedParkThread::new();
|
||||
park.block_on(f).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,23 @@ use std::future::Future;
|
|||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
// This struct is intentionally `!Unpin` when `F` is `!Unpin`. This is to
|
||||
// mitigate the issue where rust puts noalias on mutable references to the
|
||||
// `PollFn` type if it is `Unpin`. If the closure has ownership of a future,
|
||||
// then this "leaks" and the future is affected by noalias too, which we don't
|
||||
// want.
|
||||
//
|
||||
// See this thread for more information:
|
||||
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
|
||||
//
|
||||
// The fact that `PollFn` is not `Unpin` when it shouldn't be is tested in
|
||||
// `tests/async_send_sync.rs`.
|
||||
|
||||
/// Future for the [`poll_fn`] function.
|
||||
pub struct PollFn<F> {
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<F> Unpin for PollFn<F> {}
|
||||
|
||||
/// Creates a new future wrapping around a function returning [`Poll`].
|
||||
pub fn poll_fn<T, F>(f: F) -> PollFn<F>
|
||||
where
|
||||
|
@ -34,7 +44,17 @@ where
|
|||
{
|
||||
type Output = T;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
|
||||
(&mut self.f)(cx)
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
|
||||
// Safety: We never construct a `Pin<&mut F>` anywhere, so accessing `f`
|
||||
// mutably in an unpinned way is sound.
|
||||
//
|
||||
// This use of unsafe cannot be replaced with the pin-project macro
|
||||
// because:
|
||||
// * If we put `#[pin]` on the field, then it gives us a `Pin<&mut F>`,
|
||||
// which we can't use to call the closure.
|
||||
// * If we don't put `#[pin]` on the field, then it makes `PollFn` be
|
||||
// unconditionally `Unpin`, which we also don't want.
|
||||
let me = unsafe { Pin::into_inner_unchecked(self) };
|
||||
(me.f)(cx)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
pub use crate::util::linked_list::tests::fuzz_linked_list;
|
|
@ -1,4 +1,6 @@
|
|||
use crate::io::driver::{Handle, Interest, ReadyEvent, Registration};
|
||||
use crate::io::{Interest, Ready};
|
||||
use crate::runtime::io::{ReadyEvent, Registration};
|
||||
use crate::runtime::scheduler;
|
||||
|
||||
use mio::unix::SourceFd;
|
||||
use std::io;
|
||||
|
@ -63,8 +65,8 @@ use std::{task::Context, task::Poll};
|
|||
/// # Examples
|
||||
///
|
||||
/// This example shows how to turn [`std::net::TcpStream`] asynchronous using
|
||||
/// `AsyncFd`. It implements `read` as an async fn, and `AsyncWrite` as a trait
|
||||
/// to show how to implement both approaches.
|
||||
/// `AsyncFd`. It implements the read/write operations both as an `async fn`
|
||||
/// and using the IO traits [`AsyncRead`] and [`AsyncWrite`].
|
||||
///
|
||||
/// ```no_run
|
||||
/// use futures::ready;
|
||||
|
@ -72,7 +74,7 @@ use std::{task::Context, task::Poll};
|
|||
/// use std::net::TcpStream;
|
||||
/// use std::pin::Pin;
|
||||
/// use std::task::{Context, Poll};
|
||||
/// use tokio::io::AsyncWrite;
|
||||
/// use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
/// use tokio::io::unix::AsyncFd;
|
||||
///
|
||||
/// pub struct AsyncTcpStream {
|
||||
|
@ -97,6 +99,39 @@ use std::{task::Context, task::Poll};
|
|||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// pub async fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
/// loop {
|
||||
/// let mut guard = self.inner.writable().await?;
|
||||
///
|
||||
/// match guard.try_io(|inner| inner.get_ref().write(buf)) {
|
||||
/// Ok(result) => return result,
|
||||
/// Err(_would_block) => continue,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl AsyncRead for AsyncTcpStream {
|
||||
/// fn poll_read(
|
||||
/// self: Pin<&mut Self>,
|
||||
/// cx: &mut Context<'_>,
|
||||
/// buf: &mut ReadBuf<'_>
|
||||
/// ) -> Poll<io::Result<()>> {
|
||||
/// loop {
|
||||
/// let mut guard = ready!(self.inner.poll_read_ready(cx))?;
|
||||
///
|
||||
/// let unfilled = buf.initialize_unfilled();
|
||||
/// match guard.try_io(|inner| inner.get_ref().read(unfilled)) {
|
||||
/// Ok(Ok(len)) => {
|
||||
/// buf.advance(len);
|
||||
/// return Poll::Ready(Ok(()));
|
||||
/// },
|
||||
/// Ok(Err(err)) => return Poll::Ready(Err(err)),
|
||||
/// Err(_would_block) => continue,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl AsyncWrite for AsyncTcpStream {
|
||||
|
@ -137,6 +172,8 @@ use std::{task::Context, task::Poll};
|
|||
/// [`writable`]: method@Self::writable
|
||||
/// [`AsyncFdReadyGuard`]: struct@self::AsyncFdReadyGuard
|
||||
/// [`TcpStream::poll_read_ready`]: struct@crate::net::TcpStream
|
||||
/// [`AsyncRead`]: trait@crate::io::AsyncRead
|
||||
/// [`AsyncWrite`]: trait@crate::io::AsyncWrite
|
||||
pub struct AsyncFd<T: AsRawFd> {
|
||||
registration: Registration,
|
||||
inner: Option<T>,
|
||||
|
@ -164,35 +201,50 @@ pub struct AsyncFdReadyMutGuard<'a, T: AsRawFd> {
|
|||
event: Option<ReadyEvent>,
|
||||
}
|
||||
|
||||
const ALL_INTEREST: Interest = Interest::READABLE.add(Interest::WRITABLE);
|
||||
|
||||
impl<T: AsRawFd> AsyncFd<T> {
|
||||
#[inline]
|
||||
/// Creates an AsyncFd backed by (and taking ownership of) an object
|
||||
/// implementing [`AsRawFd`]. The backing file descriptor is cached at the
|
||||
/// time of creation.
|
||||
///
|
||||
/// Only configures the [`Interest::READABLE`] and [`Interest::WRITABLE`] interests. For more
|
||||
/// control, use [`AsyncFd::with_interest`].
|
||||
///
|
||||
/// This method must be called in the context of a tokio runtime.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if there is no current reactor set, or if the `rt`
|
||||
/// feature flag is not enabled.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn new(inner: T) -> io::Result<Self>
|
||||
where
|
||||
T: AsRawFd,
|
||||
{
|
||||
Self::with_interest(inner, ALL_INTEREST)
|
||||
Self::with_interest(inner, Interest::READABLE | Interest::WRITABLE)
|
||||
}
|
||||
|
||||
/// Creates an AsyncFd backed by (and taking ownership of) an object
|
||||
/// implementing [`AsRawFd`], with a specific [`Interest`]. The backing
|
||||
/// file descriptor is cached at the time of creation.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if there is no current reactor set, or if the `rt`
|
||||
/// feature flag is not enabled.
|
||||
#[inline]
|
||||
/// Creates new instance as `new` with additional ability to customize interest,
|
||||
/// allowing to specify whether file descriptor will be polled for read, write or both.
|
||||
#[track_caller]
|
||||
pub fn with_interest(inner: T, interest: Interest) -> io::Result<Self>
|
||||
where
|
||||
T: AsRawFd,
|
||||
{
|
||||
Self::new_with_handle_and_interest(inner, Handle::current(), interest)
|
||||
Self::new_with_handle_and_interest(inner, scheduler::Handle::current(), interest)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn new_with_handle_and_interest(
|
||||
inner: T,
|
||||
handle: Handle,
|
||||
handle: scheduler::Handle,
|
||||
interest: Interest,
|
||||
) -> io::Result<Self> {
|
||||
let fd = inner.as_raw_fd();
|
||||
|
@ -390,7 +442,96 @@ impl<T: AsRawFd> AsyncFd<T> {
|
|||
.into()
|
||||
}
|
||||
|
||||
async fn readiness(&self, interest: Interest) -> io::Result<AsyncFdReadyGuard<'_, T>> {
|
||||
/// Waits for any of the requested ready states, returning a
|
||||
/// [`AsyncFdReadyGuard`] that must be dropped to resume
|
||||
/// polling for the requested ready states.
|
||||
///
|
||||
/// The function may complete without the file descriptor being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// When an IO operation does return `io::ErrorKind::WouldBlock`, the readiness must be cleared.
|
||||
/// When a combined interest is used, it is important to clear only the readiness
|
||||
/// that is actually observed to block. For instance when the combined
|
||||
/// interest `Interest::READABLE | Interest::WRITABLE` is used, and a read blocks, only
|
||||
/// read readiness should be cleared using the [`AsyncFdReadyGuard::clear_ready_matching`] method:
|
||||
/// `guard.clear_ready_matching(Ready::READABLE)`.
|
||||
/// Also clearing the write readiness in this case would be incorrect. The [`AsyncFdReadyGuard::clear_ready`]
|
||||
/// method clears all readiness flags.
|
||||
///
|
||||
/// This method takes `&self`, so it is possible to call this method
|
||||
/// concurrently with other methods on this struct. This method only
|
||||
/// provides shared access to the inner IO resource when handling the
|
||||
/// [`AsyncFdReadyGuard`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Concurrently read and write to a [`std::net::TcpStream`] on the same task without
|
||||
/// splitting.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::error::Error;
|
||||
/// use std::io;
|
||||
/// use std::io::{Read, Write};
|
||||
/// use std::net::TcpStream;
|
||||
/// use tokio::io::unix::AsyncFd;
|
||||
/// use tokio::io::{Interest, Ready};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// let stream = TcpStream::connect("127.0.0.1:8080")?;
|
||||
/// stream.set_nonblocking(true)?;
|
||||
/// let stream = AsyncFd::new(stream)?;
|
||||
///
|
||||
/// loop {
|
||||
/// let mut guard = stream
|
||||
/// .ready(Interest::READABLE | Interest::WRITABLE)
|
||||
/// .await?;
|
||||
///
|
||||
/// if guard.ready().is_readable() {
|
||||
/// let mut data = vec![0; 1024];
|
||||
/// // Try to read data, this may still fail with `WouldBlock`
|
||||
/// // if the readiness event is a false positive.
|
||||
/// match stream.get_ref().read(&mut data) {
|
||||
/// Ok(n) => {
|
||||
/// println!("read {} bytes", n);
|
||||
/// }
|
||||
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
/// // a read has blocked, but a write might still succeed.
|
||||
/// // clear only the read readiness.
|
||||
/// guard.clear_ready_matching(Ready::READABLE);
|
||||
/// continue;
|
||||
/// }
|
||||
/// Err(e) => {
|
||||
/// return Err(e.into());
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// if guard.ready().is_writable() {
|
||||
/// // Try to write data, this may still fail with `WouldBlock`
|
||||
/// // if the readiness event is a false positive.
|
||||
/// match stream.get_ref().write(b"hello world") {
|
||||
/// Ok(n) => {
|
||||
/// println!("write {} bytes", n);
|
||||
/// }
|
||||
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
/// // a write has blocked, but a read might still succeed.
|
||||
/// // clear only the write readiness.
|
||||
/// guard.clear_ready_matching(Ready::WRITABLE);
|
||||
/// continue;
|
||||
/// }
|
||||
/// Err(e) => {
|
||||
/// return Err(e.into());
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<AsyncFdReadyGuard<'_, T>> {
|
||||
let event = self.registration.readiness(interest).await?;
|
||||
|
||||
Ok(AsyncFdReadyGuard {
|
||||
|
@ -399,7 +540,94 @@ impl<T: AsRawFd> AsyncFd<T> {
|
|||
})
|
||||
}
|
||||
|
||||
async fn readiness_mut(
|
||||
/// Waits for any of the requested ready states, returning a
|
||||
/// [`AsyncFdReadyMutGuard`] that must be dropped to resume
|
||||
/// polling for the requested ready states.
|
||||
///
|
||||
/// The function may complete without the file descriptor being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// When an IO operation does return `io::ErrorKind::WouldBlock`, the readiness must be cleared.
|
||||
/// When a combined interest is used, it is important to clear only the readiness
|
||||
/// that is actually observed to block. For instance when the combined
|
||||
/// interest `Interest::READABLE | Interest::WRITABLE` is used, and a read blocks, only
|
||||
/// read readiness should be cleared using the [`AsyncFdReadyMutGuard::clear_ready_matching`] method:
|
||||
/// `guard.clear_ready_matching(Ready::READABLE)`.
|
||||
/// Also clearing the write readiness in this case would be incorrect.
|
||||
/// The [`AsyncFdReadyMutGuard::clear_ready`] method clears all readiness flags.
|
||||
///
|
||||
/// This method takes `&mut self`, so it is possible to access the inner IO
|
||||
/// resource mutably when handling the [`AsyncFdReadyMutGuard`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Concurrently read and write to a [`std::net::TcpStream`] on the same task without
|
||||
/// splitting.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::error::Error;
|
||||
/// use std::io;
|
||||
/// use std::io::{Read, Write};
|
||||
/// use std::net::TcpStream;
|
||||
/// use tokio::io::unix::AsyncFd;
|
||||
/// use tokio::io::{Interest, Ready};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// let stream = TcpStream::connect("127.0.0.1:8080")?;
|
||||
/// stream.set_nonblocking(true)?;
|
||||
/// let mut stream = AsyncFd::new(stream)?;
|
||||
///
|
||||
/// loop {
|
||||
/// let mut guard = stream
|
||||
/// .ready_mut(Interest::READABLE | Interest::WRITABLE)
|
||||
/// .await?;
|
||||
///
|
||||
/// if guard.ready().is_readable() {
|
||||
/// let mut data = vec![0; 1024];
|
||||
/// // Try to read data, this may still fail with `WouldBlock`
|
||||
/// // if the readiness event is a false positive.
|
||||
/// match guard.get_inner_mut().read(&mut data) {
|
||||
/// Ok(n) => {
|
||||
/// println!("read {} bytes", n);
|
||||
/// }
|
||||
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
/// // a read has blocked, but a write might still succeed.
|
||||
/// // clear only the read readiness.
|
||||
/// guard.clear_ready_matching(Ready::READABLE);
|
||||
/// continue;
|
||||
/// }
|
||||
/// Err(e) => {
|
||||
/// return Err(e.into());
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// if guard.ready().is_writable() {
|
||||
/// // Try to write data, this may still fail with `WouldBlock`
|
||||
/// // if the readiness event is a false positive.
|
||||
/// match guard.get_inner_mut().write(b"hello world") {
|
||||
/// Ok(n) => {
|
||||
/// println!("write {} bytes", n);
|
||||
/// }
|
||||
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
/// // a write has blocked, but a read might still succeed.
|
||||
/// // clear only the write readiness.
|
||||
/// guard.clear_ready_matching(Ready::WRITABLE);
|
||||
/// continue;
|
||||
/// }
|
||||
/// Err(e) => {
|
||||
/// return Err(e.into());
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn ready_mut(
|
||||
&mut self,
|
||||
interest: Interest,
|
||||
) -> io::Result<AsyncFdReadyMutGuard<'_, T>> {
|
||||
|
@ -421,7 +649,7 @@ impl<T: AsRawFd> AsyncFd<T> {
|
|||
/// [`AsyncFdReadyGuard`].
|
||||
#[allow(clippy::needless_lifetimes)] // The lifetime improves rustdoc rendering.
|
||||
pub async fn readable<'a>(&'a self) -> io::Result<AsyncFdReadyGuard<'a, T>> {
|
||||
self.readiness(Interest::READABLE).await
|
||||
self.ready(Interest::READABLE).await
|
||||
}
|
||||
|
||||
/// Waits for the file descriptor to become readable, returning a
|
||||
|
@ -432,7 +660,7 @@ impl<T: AsRawFd> AsyncFd<T> {
|
|||
/// resource mutably when handling the [`AsyncFdReadyMutGuard`].
|
||||
#[allow(clippy::needless_lifetimes)] // The lifetime improves rustdoc rendering.
|
||||
pub async fn readable_mut<'a>(&'a mut self) -> io::Result<AsyncFdReadyMutGuard<'a, T>> {
|
||||
self.readiness_mut(Interest::READABLE).await
|
||||
self.ready_mut(Interest::READABLE).await
|
||||
}
|
||||
|
||||
/// Waits for the file descriptor to become writable, returning a
|
||||
|
@ -445,7 +673,7 @@ impl<T: AsRawFd> AsyncFd<T> {
|
|||
/// [`AsyncFdReadyGuard`].
|
||||
#[allow(clippy::needless_lifetimes)] // The lifetime improves rustdoc rendering.
|
||||
pub async fn writable<'a>(&'a self) -> io::Result<AsyncFdReadyGuard<'a, T>> {
|
||||
self.readiness(Interest::WRITABLE).await
|
||||
self.ready(Interest::WRITABLE).await
|
||||
}
|
||||
|
||||
/// Waits for the file descriptor to become writable, returning a
|
||||
|
@ -456,7 +684,110 @@ impl<T: AsRawFd> AsyncFd<T> {
|
|||
/// resource mutably when handling the [`AsyncFdReadyMutGuard`].
|
||||
#[allow(clippy::needless_lifetimes)] // The lifetime improves rustdoc rendering.
|
||||
pub async fn writable_mut<'a>(&'a mut self) -> io::Result<AsyncFdReadyMutGuard<'a, T>> {
|
||||
self.readiness_mut(Interest::WRITABLE).await
|
||||
self.ready_mut(Interest::WRITABLE).await
|
||||
}
|
||||
|
||||
/// Reads or writes from the file descriptor using a user-provided IO operation.
|
||||
///
|
||||
/// The `async_io` method is a convenience utility that waits for the file
|
||||
/// descriptor to become ready, and then executes the provided IO operation.
|
||||
/// Since file descriptors may be marked ready spuriously, the closure will
|
||||
/// be called repeatedly until it returns something other than a
|
||||
/// [`WouldBlock`] error. This is done using the following loop:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use std::io::{self, Result};
|
||||
/// # struct Dox<T> { inner: T }
|
||||
/// # impl<T> Dox<T> {
|
||||
/// # async fn writable(&self) -> Result<&Self> {
|
||||
/// # Ok(self)
|
||||
/// # }
|
||||
/// # fn try_io<R>(&self, _: impl FnMut(&T) -> Result<R>) -> Result<Result<R>> {
|
||||
/// # panic!()
|
||||
/// # }
|
||||
/// async fn async_io<R>(&self, mut f: impl FnMut(&T) -> io::Result<R>) -> io::Result<R> {
|
||||
/// loop {
|
||||
/// // or `readable` if called with the read interest.
|
||||
/// let guard = self.writable().await?;
|
||||
///
|
||||
/// match guard.try_io(&mut f) {
|
||||
/// Ok(result) => return result,
|
||||
/// Err(_would_block) => continue,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// The closure should only return a [`WouldBlock`] error if it has performed
|
||||
/// an IO operation on the file descriptor that failed due to the file descriptor not being
|
||||
/// ready. Returning a [`WouldBlock`] error in any other situation will
|
||||
/// incorrectly clear the readiness flag, which can cause the file descriptor to
|
||||
/// behave incorrectly.
|
||||
///
|
||||
/// The closure should not perform the IO operation using any of the methods
|
||||
/// defined on the Tokio [`AsyncFd`] type, as this will mess with the
|
||||
/// readiness flag and can cause the file descriptor to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// This example sends some bytes on the inner [`std::net::UdpSocket`]. The `async_io`
|
||||
/// method waits for readiness, and retries if the send operation does block. This example
|
||||
/// is equivalent to the one given for [`try_io`].
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::io::{Interest, unix::AsyncFd};
|
||||
///
|
||||
/// use std::io;
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let socket = UdpSocket::bind("0.0.0.0:8080")?;
|
||||
/// socket.set_nonblocking(true)?;
|
||||
/// let async_fd = AsyncFd::new(socket)?;
|
||||
///
|
||||
/// let written = async_fd
|
||||
/// .async_io(Interest::WRITABLE, |inner| inner.send(&[1, 2]))
|
||||
/// .await?;
|
||||
///
|
||||
/// println!("wrote {written} bytes");
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`try_io`]: AsyncFdReadyGuard::try_io
|
||||
/// [`WouldBlock`]: std::io::ErrorKind::WouldBlock
|
||||
pub async fn async_io<R>(
|
||||
&self,
|
||||
interest: Interest,
|
||||
mut f: impl FnMut(&T) -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.registration
|
||||
.async_io(interest, || f(self.get_ref()))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Reads or writes from the file descriptor using a user-provided IO operation.
|
||||
///
|
||||
/// The behavior is the same as [`async_io`], except that the closure can mutate the inner
|
||||
/// value of the [`AsyncFd`].
|
||||
///
|
||||
/// [`async_io`]: AsyncFd::async_io
|
||||
pub async fn async_io_mut<R>(
|
||||
&mut self,
|
||||
interest: Interest,
|
||||
mut f: impl FnMut(&mut T) -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.registration
|
||||
.async_io(interest, || f(self.inner.as_mut().unwrap()))
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,6 +797,13 @@ impl<T: AsRawFd> AsRawFd for AsyncFd<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl<T: AsRawFd> std::os::unix::io::AsFd for AsyncFd<T> {
|
||||
fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> {
|
||||
unsafe { std::os::unix::io::BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Debug + AsRawFd> std::fmt::Debug for AsyncFd<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AsyncFd")
|
||||
|
@ -481,22 +819,117 @@ impl<T: AsRawFd> Drop for AsyncFd<T> {
|
|||
}
|
||||
|
||||
impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
|
||||
/// Indicates to tokio that the file descriptor is no longer ready. The
|
||||
/// internal readiness flag will be cleared, and tokio will wait for the
|
||||
/// Indicates to tokio that the file descriptor is no longer ready. All
|
||||
/// internal readiness flags will be cleared, and tokio will wait for the
|
||||
/// next edge-triggered readiness notification from the OS.
|
||||
///
|
||||
/// This function is commonly used with guards returned by [`AsyncFd::readable`] and
|
||||
/// [`AsyncFd::writable`].
|
||||
///
|
||||
/// It is critical that this function not be called unless your code
|
||||
/// _actually observes_ that the file descriptor is _not_ ready. Do not call
|
||||
/// it simply because, for example, a read succeeded; it should be called
|
||||
/// when a read is observed to block.
|
||||
///
|
||||
/// [`drop`]: method@std::mem::drop
|
||||
pub fn clear_ready(&mut self) {
|
||||
if let Some(event) = self.event.take() {
|
||||
self.async_fd.registration.clear_readiness(event);
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates to tokio that the file descriptor no longer has a specific readiness.
|
||||
/// The internal readiness flag will be cleared, and tokio will wait for the
|
||||
/// next edge-triggered readiness notification from the OS.
|
||||
///
|
||||
/// This function is useful in combination with the [`AsyncFd::ready`] method when a
|
||||
/// combined interest like `Interest::READABLE | Interest::WRITABLE` is used.
|
||||
///
|
||||
/// It is critical that this function not be called unless your code
|
||||
/// _actually observes_ that the file descriptor is _not_ ready for the provided `Ready`.
|
||||
/// Do not call it simply because, for example, a read succeeded; it should be called
|
||||
/// when a read is observed to block. Only clear the specific readiness that is observed to
|
||||
/// block. For example when a read blocks when using a combined interest,
|
||||
/// only clear `Ready::READABLE`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Concurrently read and write to a [`std::net::TcpStream`] on the same task without
|
||||
/// splitting.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::error::Error;
|
||||
/// use std::io;
|
||||
/// use std::io::{Read, Write};
|
||||
/// use std::net::TcpStream;
|
||||
/// use tokio::io::unix::AsyncFd;
|
||||
/// use tokio::io::{Interest, Ready};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// let stream = TcpStream::connect("127.0.0.1:8080")?;
|
||||
/// stream.set_nonblocking(true)?;
|
||||
/// let stream = AsyncFd::new(stream)?;
|
||||
///
|
||||
/// loop {
|
||||
/// let mut guard = stream
|
||||
/// .ready(Interest::READABLE | Interest::WRITABLE)
|
||||
/// .await?;
|
||||
///
|
||||
/// if guard.ready().is_readable() {
|
||||
/// let mut data = vec![0; 1024];
|
||||
/// // Try to read data, this may still fail with `WouldBlock`
|
||||
/// // if the readiness event is a false positive.
|
||||
/// match stream.get_ref().read(&mut data) {
|
||||
/// Ok(n) => {
|
||||
/// println!("read {} bytes", n);
|
||||
/// }
|
||||
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
/// // a read has blocked, but a write might still succeed.
|
||||
/// // clear only the read readiness.
|
||||
/// guard.clear_ready_matching(Ready::READABLE);
|
||||
/// continue;
|
||||
/// }
|
||||
/// Err(e) => {
|
||||
/// return Err(e.into());
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// if guard.ready().is_writable() {
|
||||
/// // Try to write data, this may still fail with `WouldBlock`
|
||||
/// // if the readiness event is a false positive.
|
||||
/// match stream.get_ref().write(b"hello world") {
|
||||
/// Ok(n) => {
|
||||
/// println!("write {} bytes", n);
|
||||
/// }
|
||||
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
/// // a write has blocked, but a read might still succeed.
|
||||
/// // clear only the write readiness.
|
||||
/// guard.clear_ready_matching(Ready::WRITABLE);
|
||||
/// continue;
|
||||
/// }
|
||||
/// Err(e) => {
|
||||
/// return Err(e.into());
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn clear_ready_matching(&mut self, ready: Ready) {
|
||||
if let Some(mut event) = self.event.take() {
|
||||
self.async_fd
|
||||
.registration
|
||||
.clear_readiness(event.with_ready(ready));
|
||||
|
||||
// the event is no longer ready for the readiness that was just cleared
|
||||
event.ready = event.ready - ready;
|
||||
|
||||
if !event.ready.is_empty() {
|
||||
self.event = Some(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This method should be invoked when you intentionally want to keep the
|
||||
/// ready flag asserted.
|
||||
///
|
||||
|
@ -506,6 +939,20 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
|
|||
// no-op
|
||||
}
|
||||
|
||||
/// Get the [`Ready`] value associated with this guard.
|
||||
///
|
||||
/// This method will return the empty readiness state if
|
||||
/// [`AsyncFdReadyGuard::clear_ready`] has been called on
|
||||
/// the guard.
|
||||
///
|
||||
/// [`Ready`]: crate::io::Ready
|
||||
pub fn ready(&self) -> Ready {
|
||||
match &self.event {
|
||||
Some(event) => event.ready,
|
||||
None => Ready::EMPTY,
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs the provided IO operation.
|
||||
///
|
||||
/// If `f` returns a [`WouldBlock`] error, the readiness state associated
|
||||
|
@ -521,6 +968,43 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
|
|||
/// `AsyncFdReadyGuard` no longer expresses the readiness state that was queried to
|
||||
/// create this `AsyncFdReadyGuard`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// This example sends some bytes to the inner [`std::net::UdpSocket`]. Waiting
|
||||
/// for write-readiness and retrying when the send operation does block are explicit.
|
||||
/// This example can be written more succinctly using [`AsyncFd::async_io`].
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::io::unix::AsyncFd;
|
||||
///
|
||||
/// use std::io;
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let socket = UdpSocket::bind("0.0.0.0:8080")?;
|
||||
/// socket.set_nonblocking(true)?;
|
||||
/// let async_fd = AsyncFd::new(socket)?;
|
||||
///
|
||||
/// let written = loop {
|
||||
/// let mut guard = async_fd.writable().await?;
|
||||
/// match guard.try_io(|inner| inner.get_ref().send(&[1, 2])) {
|
||||
/// Ok(result) => {
|
||||
/// break result?;
|
||||
/// }
|
||||
/// Err(_would_block) => {
|
||||
/// // try_io already cleared the file descriptor's readiness state
|
||||
/// continue;
|
||||
/// }
|
||||
/// }
|
||||
/// };
|
||||
///
|
||||
/// println!("wrote {written} bytes");
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`WouldBlock`]: std::io::ErrorKind::WouldBlock
|
||||
// Alias for old name in 0.x
|
||||
#[cfg_attr(docsrs, doc(alias = "with_io"))]
|
||||
|
@ -554,22 +1038,117 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
|
|||
}
|
||||
|
||||
impl<'a, Inner: AsRawFd> AsyncFdReadyMutGuard<'a, Inner> {
|
||||
/// Indicates to tokio that the file descriptor is no longer ready. The
|
||||
/// internal readiness flag will be cleared, and tokio will wait for the
|
||||
/// Indicates to tokio that the file descriptor is no longer ready. All
|
||||
/// internal readiness flags will be cleared, and tokio will wait for the
|
||||
/// next edge-triggered readiness notification from the OS.
|
||||
///
|
||||
/// This function is commonly used with guards returned by [`AsyncFd::readable_mut`] and
|
||||
/// [`AsyncFd::writable_mut`].
|
||||
///
|
||||
/// It is critical that this function not be called unless your code
|
||||
/// _actually observes_ that the file descriptor is _not_ ready. Do not call
|
||||
/// it simply because, for example, a read succeeded; it should be called
|
||||
/// when a read is observed to block.
|
||||
///
|
||||
/// [`drop`]: method@std::mem::drop
|
||||
pub fn clear_ready(&mut self) {
|
||||
if let Some(event) = self.event.take() {
|
||||
self.async_fd.registration.clear_readiness(event);
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates to tokio that the file descriptor no longer has a specific readiness.
|
||||
/// The internal readiness flag will be cleared, and tokio will wait for the
|
||||
/// next edge-triggered readiness notification from the OS.
|
||||
///
|
||||
/// This function is useful in combination with the [`AsyncFd::ready_mut`] method when a
|
||||
/// combined interest like `Interest::READABLE | Interest::WRITABLE` is used.
|
||||
///
|
||||
/// It is critical that this function not be called unless your code
|
||||
/// _actually observes_ that the file descriptor is _not_ ready for the provided `Ready`.
|
||||
/// Do not call it simply because, for example, a read succeeded; it should be called
|
||||
/// when a read is observed to block. Only clear the specific readiness that is observed to
|
||||
/// block. For example when a read blocks when using a combined interest,
|
||||
/// only clear `Ready::READABLE`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Concurrently read and write to a [`std::net::TcpStream`] on the same task without
|
||||
/// splitting.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::error::Error;
|
||||
/// use std::io;
|
||||
/// use std::io::{Read, Write};
|
||||
/// use std::net::TcpStream;
|
||||
/// use tokio::io::unix::AsyncFd;
|
||||
/// use tokio::io::{Interest, Ready};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// let stream = TcpStream::connect("127.0.0.1:8080")?;
|
||||
/// stream.set_nonblocking(true)?;
|
||||
/// let mut stream = AsyncFd::new(stream)?;
|
||||
///
|
||||
/// loop {
|
||||
/// let mut guard = stream
|
||||
/// .ready_mut(Interest::READABLE | Interest::WRITABLE)
|
||||
/// .await?;
|
||||
///
|
||||
/// if guard.ready().is_readable() {
|
||||
/// let mut data = vec![0; 1024];
|
||||
/// // Try to read data, this may still fail with `WouldBlock`
|
||||
/// // if the readiness event is a false positive.
|
||||
/// match guard.get_inner_mut().read(&mut data) {
|
||||
/// Ok(n) => {
|
||||
/// println!("read {} bytes", n);
|
||||
/// }
|
||||
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
/// // a read has blocked, but a write might still succeed.
|
||||
/// // clear only the read readiness.
|
||||
/// guard.clear_ready_matching(Ready::READABLE);
|
||||
/// continue;
|
||||
/// }
|
||||
/// Err(e) => {
|
||||
/// return Err(e.into());
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// if guard.ready().is_writable() {
|
||||
/// // Try to write data, this may still fail with `WouldBlock`
|
||||
/// // if the readiness event is a false positive.
|
||||
/// match guard.get_inner_mut().write(b"hello world") {
|
||||
/// Ok(n) => {
|
||||
/// println!("write {} bytes", n);
|
||||
/// }
|
||||
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
/// // a write has blocked, but a read might still succeed.
|
||||
/// // clear only the write readiness.
|
||||
/// guard.clear_ready_matching(Ready::WRITABLE);
|
||||
/// continue;
|
||||
/// }
|
||||
/// Err(e) => {
|
||||
/// return Err(e.into());
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn clear_ready_matching(&mut self, ready: Ready) {
|
||||
if let Some(mut event) = self.event.take() {
|
||||
self.async_fd
|
||||
.registration
|
||||
.clear_readiness(event.with_ready(ready));
|
||||
|
||||
// the event is no longer ready for the readiness that was just cleared
|
||||
event.ready = event.ready - ready;
|
||||
|
||||
if !event.ready.is_empty() {
|
||||
self.event = Some(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This method should be invoked when you intentionally want to keep the
|
||||
/// ready flag asserted.
|
||||
///
|
||||
|
@ -579,6 +1158,20 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyMutGuard<'a, Inner> {
|
|||
// no-op
|
||||
}
|
||||
|
||||
/// Get the [`Ready`] value associated with this guard.
|
||||
///
|
||||
/// This method will return the empty readiness state if
|
||||
/// [`AsyncFdReadyGuard::clear_ready`] has been called on
|
||||
/// the guard.
|
||||
///
|
||||
/// [`Ready`]: super::Ready
|
||||
pub fn ready(&self) -> Ready {
|
||||
match &self.event {
|
||||
Some(event) => event.ready,
|
||||
None => Ready::EMPTY,
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs the provided IO operation.
|
||||
///
|
||||
/// If `f` returns a [`WouldBlock`] error, the readiness state associated
|
||||
|
|
|
@ -26,7 +26,7 @@ pub(crate) struct Buf {
|
|||
pos: usize,
|
||||
}
|
||||
|
||||
pub(crate) const MAX_BUF: usize = 16 * 1024;
|
||||
pub(crate) const MAX_BUF: usize = 2 * 1024 * 1024;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<T> {
|
||||
|
@ -34,8 +34,9 @@ enum State<T> {
|
|||
Busy(sys::Blocking<(io::Result<usize>, Buf, T)>),
|
||||
}
|
||||
|
||||
cfg_io_std! {
|
||||
cfg_io_blocking! {
|
||||
impl<T> Blocking<T> {
|
||||
#[cfg_attr(feature = "fs", allow(dead_code))]
|
||||
pub(crate) fn new(inner: T) -> Blocking<T> {
|
||||
Blocking {
|
||||
inner: Some(inner),
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//! Use POSIX AIO futures with Tokio.
|
||||
|
||||
use crate::io::driver::{Handle, Interest, ReadyEvent, Registration};
|
||||
use crate::io::interest::Interest;
|
||||
use crate::runtime::io::{ReadyEvent, Registration};
|
||||
use crate::runtime::scheduler;
|
||||
use mio::event::Source;
|
||||
use mio::Registry;
|
||||
use mio::Token;
|
||||
|
@ -117,7 +119,7 @@ impl<E: AioSource> Aio<E> {
|
|||
|
||||
fn new_with_interest(io: E, interest: Interest) -> io::Result<Self> {
|
||||
let mut io = MioSource(io);
|
||||
let handle = Handle::current();
|
||||
let handle = scheduler::Handle::current();
|
||||
let registration = Registration::new_with_interest_and_handle(&mut io, interest, handle)?;
|
||||
Ok(Self { io, registration })
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
pub(crate) use self::sys::*;
|
||||
|
||||
#[cfg(unix)]
|
||||
mod sys {
|
||||
use mio::unix::UnixReady;
|
||||
use mio::Ready;
|
||||
|
||||
pub(crate) fn hup() -> Ready {
|
||||
UnixReady::hup().into()
|
||||
}
|
||||
|
||||
pub(crate) fn is_hup(ready: Ready) -> bool {
|
||||
UnixReady::from(ready).is_hup()
|
||||
}
|
||||
|
||||
pub(crate) fn error() -> Ready {
|
||||
UnixReady::error().into()
|
||||
}
|
||||
|
||||
pub(crate) fn is_error(ready: Ready) -> bool {
|
||||
UnixReady::from(ready).is_error()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod sys {
|
||||
use mio::Ready;
|
||||
|
||||
pub(crate) fn hup() -> Ready {
|
||||
Ready::empty()
|
||||
}
|
||||
|
||||
pub(crate) fn is_hup(_: Ready) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn error() -> Ready {
|
||||
Ready::empty()
|
||||
}
|
||||
|
||||
pub(crate) fn is_error(_: Ready) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
#![cfg_attr(not(feature = "net"), allow(dead_code, unreachable_pub))]
|
||||
|
||||
use crate::io::driver::Ready;
|
||||
use crate::io::ready::Ready;
|
||||
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
|
@ -44,6 +44,11 @@ impl Interest {
|
|||
/// Writable interest includes write-closed events.
|
||||
pub const WRITABLE: Interest = Interest(mio::Interest::WRITABLE);
|
||||
|
||||
/// Returns a `Interest` set representing priority completion interests.
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
|
||||
pub const PRIORITY: Interest = Interest(mio::Interest::PRIORITY);
|
||||
|
||||
/// Returns true if the value includes readable interest.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -78,6 +83,25 @@ impl Interest {
|
|||
self.0.is_writable()
|
||||
}
|
||||
|
||||
/// Returns true if the value includes priority interest.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::io::Interest;
|
||||
///
|
||||
/// assert!(!Interest::READABLE.is_priority());
|
||||
/// assert!(Interest::PRIORITY.is_priority());
|
||||
///
|
||||
/// let both = Interest::READABLE | Interest::PRIORITY;
|
||||
/// assert!(both.is_priority());
|
||||
/// ```
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
|
||||
pub const fn is_priority(self) -> bool {
|
||||
self.0.is_priority()
|
||||
}
|
||||
|
||||
/// Add together two `Interest` values.
|
||||
///
|
||||
/// This function works from a `const` context.
|
||||
|
@ -100,10 +124,12 @@ impl Interest {
|
|||
self.0
|
||||
}
|
||||
|
||||
pub(super) fn mask(self) -> Ready {
|
||||
pub(crate) fn mask(self) -> Ready {
|
||||
match self {
|
||||
Interest::READABLE => Ready::READABLE | Ready::READ_CLOSED,
|
||||
Interest::WRITABLE => Ready::WRITABLE | Ready::WRITE_CLOSED,
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
Interest::PRIORITY => Ready::PRIORITY | Ready::READ_CLOSED,
|
||||
_ => Ready::EMPTY,
|
||||
}
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
#![cfg_attr(loom, allow(dead_code, unreachable_pub))]
|
||||
|
||||
//! Traits, helpers, and type definitions for asynchronous I/O functionality.
|
||||
//!
|
||||
//! This module is the asynchronous version of `std::io`. Primarily, it
|
||||
|
@ -132,19 +130,23 @@
|
|||
//! other words, these types must never block the thread, and instead the
|
||||
//! current task is notified when the I/O resource is ready.
|
||||
//!
|
||||
//! ## Conversion to and from Sink/Stream
|
||||
//! ## Conversion to and from Stream/Sink
|
||||
//!
|
||||
//! It is often convenient to encapsulate the reading and writing of
|
||||
//! bytes and instead work with a [`Sink`] or [`Stream`] of some data
|
||||
//! type that is encoded as bytes and/or decoded from bytes. Tokio
|
||||
//! provides some utility traits in the [tokio-util] crate that
|
||||
//! abstract the asynchronous buffering that is required and allows
|
||||
//! you to write [`Encoder`] and [`Decoder`] functions working with a
|
||||
//! buffer of bytes, and then use that ["codec"] to transform anything
|
||||
//! that implements [`AsyncRead`] and [`AsyncWrite`] into a `Sink`/`Stream` of
|
||||
//! your structured data.
|
||||
//! It is often convenient to encapsulate the reading and writing of bytes in a
|
||||
//! [`Stream`] or [`Sink`] of data.
|
||||
//!
|
||||
//! [tokio-util]: https://docs.rs/tokio-util/0.6/tokio_util/codec/index.html
|
||||
//! Tokio provides simple wrappers for converting [`AsyncRead`] to [`Stream`]
|
||||
//! and vice-versa in the [tokio-util] crate, see [`ReaderStream`] and
|
||||
//! [`StreamReader`].
|
||||
//!
|
||||
//! There are also utility traits that abstract the asynchronous buffering
|
||||
//! necessary to write your own adaptors for encoding and decoding bytes to/from
|
||||
//! your structured data, allowing to transform something that implements
|
||||
//! [`AsyncRead`]/[`AsyncWrite`] into a [`Stream`]/[`Sink`], see [`Decoder`] and
|
||||
//! [`Encoder`] in the [tokio-util::codec] module.
|
||||
//!
|
||||
//! [tokio-util]: https://docs.rs/tokio-util
|
||||
//! [tokio-util::codec]: https://docs.rs/tokio-util/latest/tokio_util/codec/index.html
|
||||
//!
|
||||
//! # Standard input and output
|
||||
//!
|
||||
|
@ -169,9 +171,11 @@
|
|||
//! [`AsyncWrite`]: trait@AsyncWrite
|
||||
//! [`AsyncReadExt`]: trait@AsyncReadExt
|
||||
//! [`AsyncWriteExt`]: trait@AsyncWriteExt
|
||||
//! ["codec"]: https://docs.rs/tokio-util/0.6/tokio_util/codec/index.html
|
||||
//! [`Encoder`]: https://docs.rs/tokio-util/0.6/tokio_util/codec/trait.Encoder.html
|
||||
//! [`Decoder`]: https://docs.rs/tokio-util/0.6/tokio_util/codec/trait.Decoder.html
|
||||
//! ["codec"]: https://docs.rs/tokio-util/latest/tokio_util/codec/index.html
|
||||
//! [`Encoder`]: https://docs.rs/tokio-util/latest/tokio_util/codec/trait.Encoder.html
|
||||
//! [`Decoder`]: https://docs.rs/tokio-util/latest/tokio_util/codec/trait.Decoder.html
|
||||
//! [`ReaderStream`]: https://docs.rs/tokio-util/latest/tokio_util/io/struct.ReaderStream.html
|
||||
//! [`StreamReader`]: https://docs.rs/tokio-util/latest/tokio_util/io/struct.StreamReader.html
|
||||
//! [`Error`]: struct@Error
|
||||
//! [`ErrorKind`]: enum@ErrorKind
|
||||
//! [`Result`]: type@Result
|
||||
|
@ -180,6 +184,12 @@
|
|||
//! [`Sink`]: https://docs.rs/futures/0.3/futures/sink/trait.Sink.html
|
||||
//! [`Stream`]: https://docs.rs/futures/0.3/futures/stream/trait.Stream.html
|
||||
//! [`Write`]: std::io::Write
|
||||
|
||||
#![cfg_attr(
|
||||
not(all(feature = "rt", feature = "net")),
|
||||
allow(dead_code, unused_imports)
|
||||
)]
|
||||
|
||||
cfg_io_blocking! {
|
||||
pub(crate) mod blocking;
|
||||
}
|
||||
|
@ -205,15 +215,19 @@ pub use self::read_buf::ReadBuf;
|
|||
pub use std::io::{Error, ErrorKind, Result, SeekFrom};
|
||||
|
||||
cfg_io_driver_impl! {
|
||||
pub(crate) mod driver;
|
||||
pub(crate) mod interest;
|
||||
pub(crate) mod ready;
|
||||
|
||||
cfg_net! {
|
||||
pub use driver::{Interest, Ready};
|
||||
pub use interest::Interest;
|
||||
pub use ready::Ready;
|
||||
}
|
||||
|
||||
#[cfg_attr(tokio_wasi, allow(unused_imports))]
|
||||
mod poll_evented;
|
||||
|
||||
#[cfg(not(loom))]
|
||||
#[cfg_attr(tokio_wasi, allow(unused_imports))]
|
||||
pub(crate) use poll_evented::PollEvented;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::io::driver::{Handle, Interest, Registration};
|
||||
use crate::io::interest::Interest;
|
||||
use crate::runtime::io::Registration;
|
||||
use crate::runtime::scheduler;
|
||||
|
||||
use mio::event::Source;
|
||||
use std::fmt;
|
||||
|
@ -11,7 +13,7 @@ cfg_io_driver! {
|
|||
/// [`std::io::Write`] traits with the reactor that drives it.
|
||||
///
|
||||
/// `PollEvented` uses [`Registration`] internally to take a type that
|
||||
/// implements [`mio::event::Source`] as well as [`std::io::Read`] and or
|
||||
/// implements [`mio::event::Source`] as well as [`std::io::Read`] and/or
|
||||
/// [`std::io::Write`] and associate it with a reactor that will drive it.
|
||||
///
|
||||
/// Once the [`mio::event::Source`] type is wrapped by `PollEvented`, it can be
|
||||
|
@ -41,12 +43,12 @@ cfg_io_driver! {
|
|||
/// [`poll_read_ready`] again will also indicate read readiness.
|
||||
///
|
||||
/// When the operation is attempted and is unable to succeed due to the I/O
|
||||
/// resource not being ready, the caller must call `clear_readiness`.
|
||||
/// resource not being ready, the caller must call [`clear_readiness`].
|
||||
/// This clears the readiness state until a new readiness event is received.
|
||||
///
|
||||
/// This allows the caller to implement additional functions. For example,
|
||||
/// [`TcpListener`] implements poll_accept by using [`poll_read_ready`] and
|
||||
/// `clear_read_ready`.
|
||||
/// [`clear_readiness`].
|
||||
///
|
||||
/// ## Platform-specific events
|
||||
///
|
||||
|
@ -57,6 +59,7 @@ cfg_io_driver! {
|
|||
/// [`AsyncRead`]: crate::io::AsyncRead
|
||||
/// [`AsyncWrite`]: crate::io::AsyncWrite
|
||||
/// [`TcpListener`]: crate::net::TcpListener
|
||||
/// [`clear_readiness`]: Registration::clear_readiness
|
||||
/// [`poll_read_ready`]: Registration::poll_read_ready
|
||||
/// [`poll_write_ready`]: Registration::poll_write_ready
|
||||
pub(crate) struct PollEvented<E: Source> {
|
||||
|
@ -70,6 +73,9 @@ cfg_io_driver! {
|
|||
impl<E: Source> PollEvented<E> {
|
||||
/// Creates a new `PollEvented` associated with the default reactor.
|
||||
///
|
||||
/// The returned `PollEvented` has readable and writable interests. For more control, use
|
||||
/// [`Self::new_with_interest`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
|
@ -77,6 +83,7 @@ impl<E: Source> PollEvented<E> {
|
|||
/// The runtime is usually set implicitly when this function is called
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||
#[track_caller]
|
||||
#[cfg_attr(feature = "signal", allow(unused))]
|
||||
pub(crate) fn new(io: E) -> io::Result<Self> {
|
||||
PollEvented::new_with_interest(io, Interest::READABLE | Interest::WRITABLE)
|
||||
|
@ -97,15 +104,17 @@ impl<E: Source> PollEvented<E> {
|
|||
/// a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter)
|
||||
/// function.
|
||||
#[track_caller]
|
||||
#[cfg_attr(feature = "signal", allow(unused))]
|
||||
pub(crate) fn new_with_interest(io: E, interest: Interest) -> io::Result<Self> {
|
||||
Self::new_with_interest_and_handle(io, interest, Handle::current())
|
||||
Self::new_with_interest_and_handle(io, interest, scheduler::Handle::current())
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn new_with_interest_and_handle(
|
||||
mut io: E,
|
||||
interest: Interest,
|
||||
handle: Handle,
|
||||
handle: scheduler::Handle,
|
||||
) -> io::Result<Self> {
|
||||
let registration = Registration::new_with_interest_and_handle(&mut io, interest, handle)?;
|
||||
Ok(Self {
|
||||
|
@ -115,11 +124,7 @@ impl<E: Source> PollEvented<E> {
|
|||
}
|
||||
|
||||
/// Returns a reference to the registration.
|
||||
#[cfg(any(
|
||||
feature = "net",
|
||||
all(unix, feature = "process"),
|
||||
all(unix, feature = "signal"),
|
||||
))]
|
||||
#[cfg(any(feature = "net"))]
|
||||
pub(crate) fn registration(&self) -> &Registration {
|
||||
&self.registration
|
||||
}
|
||||
|
@ -134,7 +139,7 @@ impl<E: Source> PollEvented<E> {
|
|||
}
|
||||
|
||||
feature! {
|
||||
#![any(feature = "net", feature = "process")]
|
||||
#![any(feature = "net", all(unix, feature = "process"))]
|
||||
|
||||
use crate::io::ReadBuf;
|
||||
use std::task::{Context, Poll};
|
||||
|
@ -151,16 +156,32 @@ feature! {
|
|||
{
|
||||
use std::io::Read;
|
||||
|
||||
let n = ready!(self.registration.poll_read_io(cx, || {
|
||||
let b = &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]);
|
||||
self.io.as_ref().unwrap().read(b)
|
||||
}))?;
|
||||
loop {
|
||||
let evt = ready!(self.registration.poll_read_ready(cx))?;
|
||||
|
||||
// Safety: We trust `TcpStream::read` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
buf.assume_init(n);
|
||||
buf.advance(n);
|
||||
Poll::Ready(Ok(()))
|
||||
let b = &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]);
|
||||
let len = b.len();
|
||||
|
||||
match self.io.as_ref().unwrap().read(b) {
|
||||
Ok(n) => {
|
||||
// if we read a partially full buffer, this is sufficient on unix to show
|
||||
// that the socket buffer has been drained
|
||||
if n > 0 && (!cfg!(windows) && n < len) {
|
||||
self.registration.clear_readiness(evt);
|
||||
}
|
||||
|
||||
// Safety: We trust `TcpStream::read` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
buf.assume_init(n);
|
||||
buf.advance(n);
|
||||
return Poll::Ready(Ok(()));
|
||||
},
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
self.registration.clear_readiness(evt);
|
||||
}
|
||||
Err(e) => return Poll::Ready(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn poll_write<'a>(&'a self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>>
|
||||
|
@ -168,10 +189,29 @@ feature! {
|
|||
&'a E: io::Write + 'a,
|
||||
{
|
||||
use std::io::Write;
|
||||
self.registration.poll_write_io(cx, || self.io.as_ref().unwrap().write(buf))
|
||||
|
||||
loop {
|
||||
let evt = ready!(self.registration.poll_write_ready(cx))?;
|
||||
|
||||
match self.io.as_ref().unwrap().write(buf) {
|
||||
Ok(n) => {
|
||||
// if we write only part of our buffer, this is sufficient on unix to show
|
||||
// that the socket buffer is full
|
||||
if n > 0 && (!cfg!(windows) && n < buf.len()) {
|
||||
self.registration.clear_readiness(evt);
|
||||
}
|
||||
|
||||
return Poll::Ready(Ok(n));
|
||||
},
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
self.registration.clear_readiness(evt);
|
||||
}
|
||||
Err(e) => return Poll::Ready(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "net")]
|
||||
#[cfg(any(feature = "net", feature = "process"))]
|
||||
pub(crate) fn poll_write_vectored<'a>(
|
||||
&'a self,
|
||||
cx: &mut Context<'_>,
|
||||
|
|
|
@ -152,6 +152,7 @@ impl<'a> ReadBuf<'a> {
|
|||
///
|
||||
/// Panics if `self.remaining()` is less than `n`.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn initialize_unfilled_to(&mut self, n: usize) -> &mut [u8] {
|
||||
assert!(self.remaining() >= n, "n overflows remaining");
|
||||
|
||||
|
@ -195,6 +196,7 @@ impl<'a> ReadBuf<'a> {
|
|||
///
|
||||
/// Panics if the filled region of the buffer would become larger than the initialized region.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
let new = self.filled.checked_add(n).expect("filled overflow");
|
||||
self.set_filled(new);
|
||||
|
@ -211,6 +213,7 @@ impl<'a> ReadBuf<'a> {
|
|||
///
|
||||
/// Panics if the filled region of the buffer would become larger than the initialized region.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn set_filled(&mut self, n: usize) {
|
||||
assert!(
|
||||
n <= self.initialized,
|
||||
|
@ -241,6 +244,7 @@ impl<'a> ReadBuf<'a> {
|
|||
///
|
||||
/// Panics if `self.remaining()` is less than `buf.len()`.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn put_slice(&mut self, buf: &[u8]) {
|
||||
assert!(
|
||||
self.remaining() >= buf.len(),
|
||||
|
@ -266,6 +270,33 @@ impl<'a> ReadBuf<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "io-util")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
|
||||
unsafe impl<'a> bytes::BufMut for ReadBuf<'a> {
|
||||
fn remaining_mut(&self) -> usize {
|
||||
self.remaining()
|
||||
}
|
||||
|
||||
// SAFETY: The caller guarantees that at least `cnt` unfilled bytes have been initialized.
|
||||
unsafe fn advance_mut(&mut self, cnt: usize) {
|
||||
self.assume_init(cnt);
|
||||
self.advance(cnt);
|
||||
}
|
||||
|
||||
fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
|
||||
// SAFETY: No region of `unfilled` will be deinitialized because it is
|
||||
// exposed as an `UninitSlice`, whose API guarantees that the memory is
|
||||
// never deinitialized.
|
||||
let unfilled = unsafe { self.unfilled_mut() };
|
||||
let len = unfilled.len();
|
||||
let ptr = unfilled.as_mut_ptr() as *mut u8;
|
||||
|
||||
// SAFETY: The pointer is valid for `len` bytes because it comes from a
|
||||
// slice of that length.
|
||||
unsafe { bytes::buf::UninitSlice::from_raw_parts_mut(ptr, len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ReadBuf<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ReadBuf")
|
||||
|
|
|
@ -7,12 +7,14 @@ const READABLE: usize = 0b0_01;
|
|||
const WRITABLE: usize = 0b0_10;
|
||||
const READ_CLOSED: usize = 0b0_0100;
|
||||
const WRITE_CLOSED: usize = 0b0_1000;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
const PRIORITY: usize = 0b1_0000;
|
||||
|
||||
/// Describes the readiness state of an I/O resources.
|
||||
///
|
||||
/// `Ready` tracks which operation an I/O resource is ready to perform.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Ready(usize);
|
||||
|
||||
impl Ready {
|
||||
|
@ -31,7 +33,17 @@ impl Ready {
|
|||
/// Returns a `Ready` representing write closed readiness.
|
||||
pub const WRITE_CLOSED: Ready = Ready(WRITE_CLOSED);
|
||||
|
||||
/// Returns a `Ready` representing priority readiness.
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
|
||||
pub const PRIORITY: Ready = Ready(PRIORITY);
|
||||
|
||||
/// Returns a `Ready` representing readiness for all operations.
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub const ALL: Ready = Ready(READABLE | WRITABLE | READ_CLOSED | WRITE_CLOSED | PRIORITY);
|
||||
|
||||
/// Returns a `Ready` representing readiness for all operations.
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub const ALL: Ready = Ready(READABLE | WRITABLE | READ_CLOSED | WRITE_CLOSED);
|
||||
|
||||
// Must remain crate-private to avoid adding a public dependency on Mio.
|
||||
|
@ -65,6 +77,13 @@ impl Ready {
|
|||
ready |= Ready::WRITE_CLOSED;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
if event.is_priority() {
|
||||
ready |= Ready::PRIORITY;
|
||||
}
|
||||
}
|
||||
|
||||
ready
|
||||
}
|
||||
|
||||
|
@ -144,6 +163,23 @@ impl Ready {
|
|||
self.contains(Ready::WRITE_CLOSED)
|
||||
}
|
||||
|
||||
/// Returns `true` if the value includes priority `readiness`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::io::Ready;
|
||||
///
|
||||
/// assert!(!Ready::EMPTY.is_priority());
|
||||
/// assert!(!Ready::WRITABLE.is_priority());
|
||||
/// assert!(Ready::PRIORITY.is_priority());
|
||||
/// ```
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
|
||||
pub fn is_priority(self) -> bool {
|
||||
self.contains(Ready::PRIORITY)
|
||||
}
|
||||
|
||||
/// Returns true if `self` is a superset of `other`.
|
||||
///
|
||||
/// `other` may represent more than one readiness operations, in which case
|
||||
|
@ -191,6 +227,12 @@ cfg_io_readiness! {
|
|||
ready |= Ready::WRITE_CLOSED;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if interest.is_priority() {
|
||||
ready |= Ready::PRIORITY;
|
||||
ready |= Ready::READ_CLOSED;
|
||||
}
|
||||
|
||||
ready
|
||||
}
|
||||
|
||||
|
@ -240,11 +282,16 @@ impl ops::Sub<Ready> for Ready {
|
|||
|
||||
impl fmt::Debug for Ready {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("Ready")
|
||||
.field("is_readable", &self.is_readable())
|
||||
let mut fmt = fmt.debug_struct("Ready");
|
||||
|
||||
fmt.field("is_readable", &self.is_readable())
|
||||
.field("is_writable", &self.is_writable())
|
||||
.field("is_read_closed", &self.is_read_closed())
|
||||
.field("is_write_closed", &self.is_write_closed())
|
||||
.finish()
|
||||
.field("is_write_closed", &self.is_write_closed());
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fmt.field("is_priority", &self.is_priority());
|
||||
|
||||
fmt.finish()
|
||||
}
|
||||
}
|
|
@ -74,7 +74,11 @@ impl<T> ReadHalf<T> {
|
|||
/// same `split` operation this method will panic.
|
||||
/// This can be checked ahead of time by comparing the stream ID
|
||||
/// of the two halves.
|
||||
pub fn unsplit(self, wr: WriteHalf<T>) -> T {
|
||||
#[track_caller]
|
||||
pub fn unsplit(self, wr: WriteHalf<T>) -> T
|
||||
where
|
||||
T: Unpin,
|
||||
{
|
||||
if self.is_pair_of(&wr) {
|
||||
drop(wr);
|
||||
|
||||
|
|
|
@ -74,16 +74,43 @@ cfg_io_std! {
|
|||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::AsRawFd for Stderr {
|
||||
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
|
||||
std::io::stderr().as_raw_fd()
|
||||
mod sys {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use super::Stderr;
|
||||
|
||||
impl AsRawFd for Stderr {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
std::io::stderr().as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for Stderr {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::AsRawHandle for Stderr {
|
||||
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
|
||||
std::io::stderr().as_raw_handle()
|
||||
cfg_windows! {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsHandle, BorrowedHandle};
|
||||
use crate::os::windows::io::{AsRawHandle, RawHandle};
|
||||
|
||||
impl AsRawHandle for Stderr {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
std::io::stderr().as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for Stderr {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,16 +49,43 @@ cfg_io_std! {
|
|||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::AsRawFd for Stdin {
|
||||
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
|
||||
std::io::stdin().as_raw_fd()
|
||||
mod sys {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use super::Stdin;
|
||||
|
||||
impl AsRawFd for Stdin {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
std::io::stdin().as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for Stdin {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::AsRawHandle for Stdin {
|
||||
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
|
||||
std::io::stdin().as_raw_handle()
|
||||
cfg_windows! {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsHandle, BorrowedHandle};
|
||||
use crate::os::windows::io::{AsRawHandle, RawHandle};
|
||||
|
||||
impl AsRawHandle for Stdin {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
std::io::stdin().as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for Stdin {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ where
|
|||
// for further code. Since `AsyncWrite` can always shrink
|
||||
// buffer at its discretion, excessive (i.e. in tests) shrinking
|
||||
// does not break correctness.
|
||||
// 2. If buffer is small, it will not be shrinked.
|
||||
// 2. If buffer is small, it will not be shrunk.
|
||||
// That's why, it's "textness" will not change, so we don't have
|
||||
// to fixup it.
|
||||
if cfg!(not(any(target_os = "windows", test))) || buf.len() <= crate::io::blocking::MAX_BUF
|
||||
|
@ -108,14 +108,13 @@ where
|
|||
#[cfg(test)]
|
||||
#[cfg(not(loom))]
|
||||
mod tests {
|
||||
use crate::io::blocking::MAX_BUF;
|
||||
use crate::io::AsyncWriteExt;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::Context;
|
||||
use std::task::Poll;
|
||||
|
||||
const MAX_BUF: usize = 16 * 1024;
|
||||
|
||||
struct TextMockWriter;
|
||||
|
||||
impl crate::io::AsyncWrite for TextMockWriter {
|
||||
|
@ -177,6 +176,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn test_splitter() {
|
||||
let data = str::repeat("█", MAX_BUF);
|
||||
let mut wr = super::SplitByUtf8BoundaryIfWindows::new(TextMockWriter);
|
||||
|
@ -190,10 +190,11 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn test_pseudo_text() {
|
||||
// In this test we write a piece of binary data, whose beginning is
|
||||
// text though. We then validate that even in this corner case buffer
|
||||
// was not shrinked too much.
|
||||
// was not shrunk too much.
|
||||
let checked_count = super::MAGIC_CONST * super::MAX_BYTES_PER_CHAR;
|
||||
let mut data: Vec<u8> = str::repeat("a", checked_count).into();
|
||||
data.extend(std::iter::repeat(0b1010_1010).take(MAX_BUF - checked_count + 1));
|
||||
|
@ -212,7 +213,7 @@ mod tests {
|
|||
writer.write_history.iter().copied().sum::<usize>(),
|
||||
data.len()
|
||||
);
|
||||
// Check that at most MAX_BYTES_PER_CHAR + 1 (i.e. 5) bytes were shrinked
|
||||
// Check that at most MAX_BYTES_PER_CHAR + 1 (i.e. 5) bytes were shrunk
|
||||
// from the buffer: one because it was outside of MAX_BUF boundary, and
|
||||
// up to one "utf8 code point".
|
||||
assert!(data.len() - writer.write_history[0] <= super::MAX_BYTES_PER_CHAR + 1);
|
||||
|
|
|
@ -73,16 +73,43 @@ cfg_io_std! {
|
|||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::AsRawFd for Stdout {
|
||||
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
|
||||
std::io::stdout().as_raw_fd()
|
||||
mod sys {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use super::Stdout;
|
||||
|
||||
impl AsRawFd for Stdout {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
std::io::stdout().as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for Stdout {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::AsRawHandle for Stdout {
|
||||
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
|
||||
std::io::stdout().as_raw_handle()
|
||||
cfg_windows! {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsHandle, BorrowedHandle};
|
||||
use crate::os::windows::io::{AsRawHandle, RawHandle};
|
||||
|
||||
impl AsRawHandle for Stdout {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
std::io::stdout().as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for Stdout {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ cfg_io_util! {
|
|||
/// [`next_line`] method.
|
||||
/// * Use [`tokio_util::codec::LinesCodec`][LinesCodec].
|
||||
///
|
||||
/// [LinesCodec]: https://docs.rs/tokio-util/0.6/tokio_util/codec/struct.LinesCodec.html
|
||||
/// [LinesCodec]: https://docs.rs/tokio-util/latest/tokio_util/codec/struct.LinesCodec.html
|
||||
/// [`read_until`]: Self::read_until
|
||||
/// [`lines`]: Self::lines
|
||||
/// [`next_line`]: crate::io::Lines::next_line
|
||||
|
|
|
@ -69,7 +69,7 @@ cfg_io_util! {
|
|||
|
||||
/// Creates a future which will rewind to the beginning of the stream.
|
||||
///
|
||||
/// This is convenience method, equivalent to to `self.seek(SeekFrom::Start(0))`.
|
||||
/// This is convenience method, equivalent to `self.seek(SeekFrom::Start(0))`.
|
||||
fn rewind(&mut self) -> Seek<'_, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
|
|
|
@ -406,7 +406,7 @@ cfg_io_util! {
|
|||
/// ```
|
||||
fn write_u8(&mut self, n: u8) -> WriteU8;
|
||||
|
||||
/// Writes an unsigned 8-bit integer to the underlying writer.
|
||||
/// Writes a signed 8-bit integer to the underlying writer.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
|
@ -425,7 +425,7 @@ cfg_io_util! {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Write unsigned 8 bit integers to a `AsyncWrite`:
|
||||
/// Write signed 8 bit integers to a `AsyncWrite`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
|
@ -434,10 +434,10 @@ cfg_io_util! {
|
|||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_u8(2).await?;
|
||||
/// writer.write_u8(5).await?;
|
||||
/// writer.write_i8(-2).await?;
|
||||
/// writer.write_i8(126).await?;
|
||||
///
|
||||
/// assert_eq!(writer, b"\x02\x05");
|
||||
/// assert_eq!(writer, b"\xFE\x7E");
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
|
|
|
@ -27,6 +27,51 @@ impl CopyBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
fn poll_fill_buf<R>(
|
||||
&mut self,
|
||||
cx: &mut Context<'_>,
|
||||
reader: Pin<&mut R>,
|
||||
) -> Poll<io::Result<()>>
|
||||
where
|
||||
R: AsyncRead + ?Sized,
|
||||
{
|
||||
let me = &mut *self;
|
||||
let mut buf = ReadBuf::new(&mut me.buf);
|
||||
buf.set_filled(me.cap);
|
||||
|
||||
let res = reader.poll_read(cx, &mut buf);
|
||||
if let Poll::Ready(Ok(_)) = res {
|
||||
let filled_len = buf.filled().len();
|
||||
me.read_done = me.cap == filled_len;
|
||||
me.cap = filled_len;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn poll_write_buf<R, W>(
|
||||
&mut self,
|
||||
cx: &mut Context<'_>,
|
||||
mut reader: Pin<&mut R>,
|
||||
mut writer: Pin<&mut W>,
|
||||
) -> Poll<io::Result<usize>>
|
||||
where
|
||||
R: AsyncRead + ?Sized,
|
||||
W: AsyncWrite + ?Sized,
|
||||
{
|
||||
let me = &mut *self;
|
||||
match writer.as_mut().poll_write(cx, &me.buf[me.pos..me.cap]) {
|
||||
Poll::Pending => {
|
||||
// Top up the buffer towards full if we can read a bit more
|
||||
// data - this should improve the chances of a large write
|
||||
if !me.read_done && me.cap < me.buf.len() {
|
||||
ready!(me.poll_fill_buf(cx, reader.as_mut()))?;
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
res => res,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn poll_copy<R, W>(
|
||||
&mut self,
|
||||
cx: &mut Context<'_>,
|
||||
|
@ -41,10 +86,10 @@ impl CopyBuffer {
|
|||
// If our buffer is empty, then we need to read some data to
|
||||
// continue.
|
||||
if self.pos == self.cap && !self.read_done {
|
||||
let me = &mut *self;
|
||||
let mut buf = ReadBuf::new(&mut me.buf);
|
||||
self.pos = 0;
|
||||
self.cap = 0;
|
||||
|
||||
match reader.as_mut().poll_read(cx, &mut buf) {
|
||||
match self.poll_fill_buf(cx, reader.as_mut()) {
|
||||
Poll::Ready(Ok(_)) => (),
|
||||
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
|
||||
Poll::Pending => {
|
||||
|
@ -58,20 +103,11 @@ impl CopyBuffer {
|
|||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
|
||||
let n = buf.filled().len();
|
||||
if n == 0 {
|
||||
self.read_done = true;
|
||||
} else {
|
||||
self.pos = 0;
|
||||
self.cap = n;
|
||||
}
|
||||
}
|
||||
|
||||
// If our buffer has some data, let's write it out!
|
||||
while self.pos < self.cap {
|
||||
let me = &mut *self;
|
||||
let i = ready!(writer.as_mut().poll_write(cx, &me.buf[me.pos..me.cap]))?;
|
||||
let i = ready!(self.poll_write_buf(cx, reader.as_mut(), writer.as_mut()))?;
|
||||
if i == 0 {
|
||||
return Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::WriteZero,
|
||||
|
@ -117,14 +153,22 @@ cfg_io_util! {
|
|||
///
|
||||
/// This function returns a future that will continuously read data from
|
||||
/// `reader` and then write it into `writer` in a streaming fashion until
|
||||
/// `reader` returns EOF.
|
||||
/// `reader` returns EOF or fails.
|
||||
///
|
||||
/// On success, the total number of bytes that were copied from `reader` to
|
||||
/// `writer` is returned.
|
||||
///
|
||||
/// This is an asynchronous version of [`std::io::copy`][std].
|
||||
///
|
||||
/// A heap-allocated copy buffer with 8 KB is created to take data from the
|
||||
/// reader to the writer, check [`copy_buf`] if you want an alternative for
|
||||
/// [`AsyncBufRead`]. You can use `copy_buf` with [`BufReader`] to change the
|
||||
/// buffer capacity.
|
||||
///
|
||||
/// [std]: std::io::copy
|
||||
/// [`copy_buf`]: crate::io::copy_buf
|
||||
/// [`AsyncBufRead`]: crate::io::AsyncBufRead
|
||||
/// [`BufReader`]: crate::io::BufReader
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use super::copy::CopyBuffer;
|
||||
|
||||
use crate::future::poll_fn;
|
||||
use crate::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
@ -13,13 +13,6 @@ enum TransferState {
|
|||
Done(u64),
|
||||
}
|
||||
|
||||
struct CopyBidirectional<'a, A: ?Sized, B: ?Sized> {
|
||||
a: &'a mut A,
|
||||
b: &'a mut B,
|
||||
a_to_b: TransferState,
|
||||
b_to_a: TransferState,
|
||||
}
|
||||
|
||||
fn transfer_one_direction<A, B>(
|
||||
cx: &mut Context<'_>,
|
||||
state: &mut TransferState,
|
||||
|
@ -48,35 +41,6 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A, B> Future for CopyBidirectional<'a, A, B>
|
||||
where
|
||||
A: AsyncRead + AsyncWrite + Unpin + ?Sized,
|
||||
B: AsyncRead + AsyncWrite + Unpin + ?Sized,
|
||||
{
|
||||
type Output = io::Result<(u64, u64)>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// Unpack self into mut refs to each field to avoid borrow check issues.
|
||||
let CopyBidirectional {
|
||||
a,
|
||||
b,
|
||||
a_to_b,
|
||||
b_to_a,
|
||||
} = &mut *self;
|
||||
|
||||
let a_to_b = transfer_one_direction(cx, a_to_b, &mut *a, &mut *b)?;
|
||||
let b_to_a = transfer_one_direction(cx, b_to_a, &mut *b, &mut *a)?;
|
||||
|
||||
// It is not a problem if ready! returns early because transfer_one_direction for the
|
||||
// other direction will keep returning TransferState::Done(count) in future calls to poll
|
||||
let a_to_b = ready!(a_to_b);
|
||||
let b_to_a = ready!(b_to_a);
|
||||
|
||||
Poll::Ready(Ok((a_to_b, b_to_a)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies data in both directions between `a` and `b`.
|
||||
///
|
||||
/// This function returns a future that will read from both streams,
|
||||
|
@ -110,11 +74,18 @@ where
|
|||
A: AsyncRead + AsyncWrite + Unpin + ?Sized,
|
||||
B: AsyncRead + AsyncWrite + Unpin + ?Sized,
|
||||
{
|
||||
CopyBidirectional {
|
||||
a,
|
||||
b,
|
||||
a_to_b: TransferState::Running(CopyBuffer::new()),
|
||||
b_to_a: TransferState::Running(CopyBuffer::new()),
|
||||
}
|
||||
let mut a_to_b = TransferState::Running(CopyBuffer::new());
|
||||
let mut b_to_a = TransferState::Running(CopyBuffer::new());
|
||||
poll_fn(|cx| {
|
||||
let a_to_b = transfer_one_direction(cx, &mut a_to_b, a, b)?;
|
||||
let b_to_a = transfer_one_direction(cx, &mut b_to_a, b, a)?;
|
||||
|
||||
// It is not a problem if ready! returns early because transfer_one_direction for the
|
||||
// other direction will keep returning TransferState::Done(count) in future calls to poll
|
||||
let a_to_b = ready!(a_to_b);
|
||||
let b_to_a = ready!(b_to_a);
|
||||
|
||||
Poll::Ready(Ok((a_to_b, b_to_a)))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -24,11 +24,17 @@ cfg_io_util! {
|
|||
///
|
||||
/// This function returns a future that will continuously read data from
|
||||
/// `reader` and then write it into `writer` in a streaming fashion until
|
||||
/// `reader` returns EOF.
|
||||
/// `reader` returns EOF or fails.
|
||||
///
|
||||
/// On success, the total number of bytes that were copied from `reader` to
|
||||
/// `writer` is returned.
|
||||
///
|
||||
/// This is a [`tokio::io::copy`] alternative for [`AsyncBufRead`] readers
|
||||
/// with no extra buffer allocation, since [`AsyncBufRead`] allow access
|
||||
/// to the reader's inner buffer.
|
||||
///
|
||||
/// [`tokio::io::copy`]: crate::io::copy
|
||||
/// [`AsyncBufRead`]: crate::io::AsyncBufRead
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
|
|
@ -53,6 +53,7 @@ impl AsyncRead for Empty {
|
|||
cx: &mut Context<'_>,
|
||||
_: &mut ReadBuf<'_>,
|
||||
) -> Poll<io::Result<()>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
@ -61,6 +62,7 @@ impl AsyncRead for Empty {
|
|||
impl AsyncBufRead for Empty {
|
||||
#[inline]
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
Poll::Ready(Ok(&[]))
|
||||
}
|
||||
|
@ -77,7 +79,7 @@ impl fmt::Debug for Empty {
|
|||
|
||||
cfg_coop! {
|
||||
fn poll_proceed_and_make_progress(cx: &mut Context<'_>) -> Poll<()> {
|
||||
let coop = ready!(crate::coop::poll_proceed(cx));
|
||||
let coop = ready!(crate::runtime::coop::poll_proceed(cx));
|
||||
coop.made_progress();
|
||||
Poll::Ready(())
|
||||
}
|
||||
|
|
|
@ -233,7 +233,8 @@ impl AsyncRead for Pipe {
|
|||
cx: &mut task::Context<'_>,
|
||||
buf: &mut ReadBuf<'_>,
|
||||
) -> Poll<std::io::Result<()>> {
|
||||
let coop = ready!(crate::coop::poll_proceed(cx));
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
let coop = ready!(crate::runtime::coop::poll_proceed(cx));
|
||||
|
||||
let ret = self.poll_read_internal(cx, buf);
|
||||
if ret.is_ready() {
|
||||
|
@ -249,6 +250,7 @@ impl AsyncRead for Pipe {
|
|||
cx: &mut task::Context<'_>,
|
||||
buf: &mut ReadBuf<'_>,
|
||||
) -> Poll<std::io::Result<()>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
self.poll_read_internal(cx, buf)
|
||||
}
|
||||
}
|
||||
|
@ -261,7 +263,8 @@ impl AsyncWrite for Pipe {
|
|||
cx: &mut task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<std::io::Result<usize>> {
|
||||
let coop = ready!(crate::coop::poll_proceed(cx));
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
let coop = ready!(crate::runtime::coop::poll_proceed(cx));
|
||||
|
||||
let ret = self.poll_write_internal(cx, buf);
|
||||
if ret.is_ready() {
|
||||
|
@ -277,6 +280,7 @@ impl AsyncWrite for Pipe {
|
|||
cx: &mut task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<std::io::Result<usize>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
self.poll_write_internal(cx, buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ where
|
|||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
|
||||
let me = self.project();
|
||||
let mut buf = ReadBuf::new(*me.buf);
|
||||
let mut buf = ReadBuf::new(me.buf);
|
||||
ready!(Pin::new(me.reader).poll_read(cx, &mut buf))?;
|
||||
Poll::Ready(Ok(buf.filled().len()))
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::io::util::vec_with_initialized::{into_read_buf_parts, VecU8, VecWithInitialized};
|
||||
use crate::io::AsyncRead;
|
||||
use crate::io::{AsyncRead, ReadBuf};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::marker::PhantomPinned;
|
||||
use std::mem;
|
||||
use std::mem::{self, MaybeUninit};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
|
@ -67,16 +67,47 @@ fn poll_read_to_end<V: VecU8, R: AsyncRead + ?Sized>(
|
|||
// has 4 bytes while still making large reads if the reader does have a ton
|
||||
// of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every
|
||||
// time is 4,500 times (!) slower than this if the reader has a very small
|
||||
// amount of data to return.
|
||||
buf.reserve(32);
|
||||
// amount of data to return. When the vector is full with its starting
|
||||
// capacity, we first try to read into a small buffer to see if we reached
|
||||
// an EOF. This only happens when the starting capacity is >= NUM_BYTES, since
|
||||
// we allocate at least NUM_BYTES each time. This avoids the unnecessary
|
||||
// allocation that we attempt before reading into the vector.
|
||||
|
||||
const NUM_BYTES: usize = 32;
|
||||
let try_small_read = buf.try_small_read_first(NUM_BYTES);
|
||||
|
||||
// Get a ReadBuf into the vector.
|
||||
let mut read_buf = buf.get_read_buf();
|
||||
let mut read_buf;
|
||||
let poll_result;
|
||||
|
||||
let filled_before = read_buf.filled().len();
|
||||
let poll_result = read.poll_read(cx, &mut read_buf);
|
||||
let filled_after = read_buf.filled().len();
|
||||
let n = filled_after - filled_before;
|
||||
let n = if try_small_read {
|
||||
// Read some bytes using a small read.
|
||||
let mut small_buf: [MaybeUninit<u8>; NUM_BYTES] = [MaybeUninit::uninit(); NUM_BYTES];
|
||||
let mut small_read_buf = ReadBuf::uninit(&mut small_buf);
|
||||
poll_result = read.poll_read(cx, &mut small_read_buf);
|
||||
let to_write = small_read_buf.filled();
|
||||
|
||||
// Ensure we have enough space to fill our vector with what we read.
|
||||
read_buf = buf.get_read_buf();
|
||||
if to_write.len() > read_buf.remaining() {
|
||||
buf.reserve(NUM_BYTES);
|
||||
read_buf = buf.get_read_buf();
|
||||
}
|
||||
read_buf.put_slice(to_write);
|
||||
|
||||
to_write.len()
|
||||
} else {
|
||||
// Ensure we have enough space for reading.
|
||||
buf.reserve(NUM_BYTES);
|
||||
read_buf = buf.get_read_buf();
|
||||
|
||||
// Read data directly into vector.
|
||||
let filled_before = read_buf.filled().len();
|
||||
poll_result = read.poll_read(cx, &mut read_buf);
|
||||
|
||||
// Compute the number of bytes read.
|
||||
read_buf.filled().len() - filled_before
|
||||
};
|
||||
|
||||
// Update the length of the vector using the result of poll_read.
|
||||
let read_buf_parts = into_read_buf_parts(read_buf);
|
||||
|
@ -87,11 +118,11 @@ fn poll_read_to_end<V: VecU8, R: AsyncRead + ?Sized>(
|
|||
// In this case, nothing should have been read. However we still
|
||||
// update the vector in case the poll_read call initialized parts of
|
||||
// the vector's unused capacity.
|
||||
debug_assert_eq!(filled_before, filled_after);
|
||||
debug_assert_eq!(n, 0);
|
||||
Poll::Pending
|
||||
}
|
||||
Poll::Ready(Err(err)) => {
|
||||
debug_assert_eq!(filled_before, filled_after);
|
||||
debug_assert_eq!(n, 0);
|
||||
Poll::Ready(Err(err))
|
||||
}
|
||||
Poll::Ready(Ok(())) => Poll::Ready(Ok(n)),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::io::AsyncBufRead;
|
||||
use crate::util::memchr;
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::future::Future;
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
use crate::io::ReadBuf;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
/// Something that looks like a `Vec<u8>`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The implementor must guarantee that the vector returned by the
|
||||
/// `as_mut` and `as_mut` methods do not change from one call to
|
||||
/// another.
|
||||
pub(crate) unsafe trait VecU8: AsRef<Vec<u8>> + AsMut<Vec<u8>> {}
|
||||
|
||||
impl Sealed for Vec<u8> {}
|
||||
impl Sealed for &mut Vec<u8> {}
|
||||
}
|
||||
unsafe impl VecU8 for Vec<u8> {}
|
||||
unsafe impl VecU8 for &mut Vec<u8> {}
|
||||
|
||||
/// A sealed trait that constrains the generic type parameter in `VecWithInitialized<V>`. That struct's safety relies
|
||||
/// on certain invariants upheld by `Vec<u8>`.
|
||||
pub(crate) trait VecU8: AsMut<Vec<u8>> + private::Sealed {}
|
||||
|
||||
impl VecU8 for Vec<u8> {}
|
||||
impl VecU8 for &mut Vec<u8> {}
|
||||
/// This struct wraps a `Vec<u8>` or `&mut Vec<u8>`, combining it with a
|
||||
/// `num_initialized`, which keeps track of the number of initialized bytes
|
||||
/// in the unused capacity.
|
||||
|
@ -29,6 +28,7 @@ pub(crate) struct VecWithInitialized<V> {
|
|||
// The number of initialized bytes in the vector.
|
||||
// Always between `vec.len()` and `vec.capacity()`.
|
||||
num_initialized: usize,
|
||||
starting_capacity: usize,
|
||||
}
|
||||
|
||||
impl VecWithInitialized<Vec<u8>> {
|
||||
|
@ -48,6 +48,7 @@ where
|
|||
// to its length are initialized.
|
||||
Self {
|
||||
num_initialized: vec.as_mut().len(),
|
||||
starting_capacity: vec.as_ref().capacity(),
|
||||
vec,
|
||||
}
|
||||
}
|
||||
|
@ -64,8 +65,8 @@ where
|
|||
}
|
||||
|
||||
#[cfg(feature = "io-util")]
|
||||
pub(crate) fn is_empty(&mut self) -> bool {
|
||||
self.vec.as_mut().is_empty()
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.vec.as_ref().is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn get_read_buf<'a>(&'a mut self) -> ReadBuf<'a> {
|
||||
|
@ -112,6 +113,15 @@ where
|
|||
vec.set_len(parts.len);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a boolean telling the caller to try reading into a small local buffer first if true.
|
||||
// Doing so would avoid overallocating when vec is filled to capacity and we reached EOF.
|
||||
pub(crate) fn try_small_read_first(&self, num_bytes: usize) -> bool {
|
||||
let vec = self.vec.as_ref();
|
||||
vec.capacity() - vec.len() < num_bytes
|
||||
&& self.starting_capacity == vec.capacity()
|
||||
&& self.starting_capacity >= num_bytes
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ReadBufParts {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#![allow(
|
||||
clippy::cognitive_complexity,
|
||||
clippy::large_enum_variant,
|
||||
clippy::needless_doctest_main
|
||||
clippy::module_inception,
|
||||
clippy::needless_doctest_main,
|
||||
clippy::declare_interior_mutable_const
|
||||
)]
|
||||
#![warn(
|
||||
missing_debug_implementations,
|
||||
|
@ -16,6 +18,7 @@
|
|||
))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, allow(unused_attributes))]
|
||||
#![cfg_attr(loom, allow(dead_code, unreachable_pub))]
|
||||
|
||||
//! A runtime for writing reliable network applications without compromising speed.
|
||||
//!
|
||||
|
@ -114,7 +117,7 @@
|
|||
//! The [`tokio::sync`] module contains synchronization primitives to use when
|
||||
//! needing to communicate or share data. These include:
|
||||
//!
|
||||
//! * channels ([`oneshot`], [`mpsc`], and [`watch`]), for sending values
|
||||
//! * channels ([`oneshot`], [`mpsc`], [`watch`], and [`broadcast`]), for sending values
|
||||
//! between tasks,
|
||||
//! * a non-blocking [`Mutex`], for controlling access to a shared, mutable
|
||||
//! value,
|
||||
|
@ -130,6 +133,7 @@
|
|||
//! [`oneshot`]: crate::sync::oneshot
|
||||
//! [`mpsc`]: crate::sync::mpsc
|
||||
//! [`watch`]: crate::sync::watch
|
||||
//! [`broadcast`]: crate::sync::broadcast
|
||||
//!
|
||||
//! The [`tokio::time`] module provides utilities for tracking time and
|
||||
//! scheduling work. This includes functions for setting [timeouts][timeout] for
|
||||
|
@ -151,7 +155,7 @@
|
|||
//! provide the functionality you need.
|
||||
//!
|
||||
//! Using the runtime requires the "rt" or "rt-multi-thread" feature flags, to
|
||||
//! enable the basic [single-threaded scheduler][rt] and the [thread-pool
|
||||
//! enable the current-thread [single-threaded scheduler][rt] and the [multi-thread
|
||||
//! scheduler][rt-multi-thread], respectively. See the [`runtime` module
|
||||
//! documentation][rt-features] for details. In addition, the "macros" feature
|
||||
//! flag enables the `#[tokio::main]` and `#[tokio::test]` attributes.
|
||||
|
@ -170,12 +174,15 @@
|
|||
//! swapping the currently running task on each thread. However, this kind of
|
||||
//! swapping can only happen at `.await` points, so code that spends a long time
|
||||
//! without reaching an `.await` will prevent other tasks from running. To
|
||||
//! combat this, Tokio provides two kinds of threads: Core threads and blocking
|
||||
//! threads. The core threads are where all asynchronous code runs, and Tokio
|
||||
//! will by default spawn one for each CPU core. The blocking threads are
|
||||
//! spawned on demand, can be used to run blocking code that would otherwise
|
||||
//! block other tasks from running and are kept alive when not used for a certain
|
||||
//! amount of time which can be configured with [`thread_keep_alive`].
|
||||
//! combat this, Tokio provides two kinds of threads: Core threads and blocking threads.
|
||||
//!
|
||||
//! The core threads are where all asynchronous code runs, and Tokio will by default
|
||||
//! spawn one for each CPU core. You can use the environment variable `TOKIO_WORKER_THREADS`
|
||||
//! to override the default value.
|
||||
//!
|
||||
//! The blocking threads are spawned on demand, can be used to run blocking code
|
||||
//! that would otherwise block other tasks from running and are kept alive when
|
||||
//! not used for a certain amount of time which can be configured with [`thread_keep_alive`].
|
||||
//! Since it is not possible for Tokio to swap out blocking tasks, like it
|
||||
//! can do with asynchronous code, the upper limit on the number of blocking
|
||||
//! threads is very large. These limits can be configured on the [`Builder`].
|
||||
|
@ -308,7 +315,7 @@
|
|||
//! need.
|
||||
//!
|
||||
//! - `full`: Enables all features listed below except `test-util` and `tracing`.
|
||||
//! - `rt`: Enables `tokio::spawn`, the basic (current thread) scheduler,
|
||||
//! - `rt`: Enables `tokio::spawn`, the current-thread scheduler,
|
||||
//! and non-scheduler utilities.
|
||||
//! - `rt-multi-thread`: Enables the heavier, multi-threaded, work-stealing scheduler.
|
||||
//! - `io-util`: Enables the IO based `Ext` traits.
|
||||
|
@ -324,30 +331,117 @@
|
|||
//! - `signal`: Enables all `tokio::signal` types.
|
||||
//! - `fs`: Enables `tokio::fs` types.
|
||||
//! - `test-util`: Enables testing based infrastructure for the Tokio runtime.
|
||||
//! - `parking_lot`: As a potential optimization, use the _parking_lot_ crate's
|
||||
//! synchronization primitives internally. Also, this
|
||||
//! dependency is necessary to construct some of our primitives
|
||||
//! in a const context. MSRV may increase according to the
|
||||
//! _parking_lot_ release in use.
|
||||
//!
|
||||
//! _Note: `AsyncRead` and `AsyncWrite` traits do not require any features and are
|
||||
//! always available._
|
||||
//!
|
||||
//! ### Internal features
|
||||
//!
|
||||
//! These features do not expose any new API, but influence internal
|
||||
//! implementation aspects of Tokio, and can pull in additional
|
||||
//! dependencies.
|
||||
//!
|
||||
//! - `parking_lot`: As a potential optimization, use the _parking_lot_ crate's
|
||||
//! synchronization primitives internally. MSRV may increase according to the
|
||||
//! _parking_lot_ release in use.
|
||||
//!
|
||||
//! ### Unstable features
|
||||
//!
|
||||
//! These feature flags enable **unstable** features. The public API may break in 1.x
|
||||
//! releases. To enable these features, the `--cfg tokio_unstable` must be passed to
|
||||
//! `rustc` when compiling. This is easiest done using the `RUSTFLAGS` env variable:
|
||||
//! `RUSTFLAGS="--cfg tokio_unstable"`.
|
||||
//! Some feature flags are only available when specifying the `tokio_unstable` flag:
|
||||
//!
|
||||
//! - `tracing`: Enables tracing events.
|
||||
//!
|
||||
//! Likewise, some parts of the API are only available with the same flag:
|
||||
//!
|
||||
//! - [`task::Builder`]
|
||||
//! - Some methods on [`task::JoinSet`]
|
||||
//! - [`runtime::RuntimeMetrics`]
|
||||
//! - [`runtime::Builder::unhandled_panic`]
|
||||
//! - [`task::Id`]
|
||||
//!
|
||||
//! This flag enables **unstable** features. The public API of these features
|
||||
//! may break in 1.x releases. To enable these features, the `--cfg
|
||||
//! tokio_unstable` argument must be passed to `rustc` when compiling. This
|
||||
//! serves to explicitly opt-in to features which may break semver conventions,
|
||||
//! since Cargo [does not yet directly support such opt-ins][unstable features].
|
||||
//!
|
||||
//! You can specify it in your project's `.cargo/config.toml` file:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [build]
|
||||
//! rustflags = ["--cfg", "tokio_unstable"]
|
||||
//! ```
|
||||
//!
|
||||
//! Alternatively, you can specify it with an environment variable:
|
||||
//!
|
||||
//! ```sh
|
||||
//! ## Many *nix shells:
|
||||
//! export RUSTFLAGS="--cfg tokio_unstable"
|
||||
//! cargo build
|
||||
//! ```
|
||||
//!
|
||||
//! ```powershell
|
||||
//! ## Windows PowerShell:
|
||||
//! $Env:RUSTFLAGS="--cfg tokio_unstable"
|
||||
//! cargo build
|
||||
//! ```
|
||||
//!
|
||||
//! [unstable features]: https://internals.rust-lang.org/t/feature-request-unstable-opt-in-non-transitive-crate-features/16193#why-not-a-crate-feature-2
|
||||
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
|
||||
//!
|
||||
//! ## Supported platforms
|
||||
//!
|
||||
//! Tokio currently guarantees support for the following platforms:
|
||||
//!
|
||||
//! * Linux
|
||||
//! * Windows
|
||||
//! * Android (API level 21)
|
||||
//! * macOS
|
||||
//! * iOS
|
||||
//! * FreeBSD
|
||||
//!
|
||||
//! Tokio will continue to support these platforms in the future. However,
|
||||
//! future releases may change requirements such as the minimum required libc
|
||||
//! version on Linux, the API level on Android, or the supported FreeBSD
|
||||
//! release.
|
||||
//!
|
||||
//! Beyond the above platforms, Tokio is intended to work on all platforms
|
||||
//! supported by the mio crate. You can find a longer list [in mio's
|
||||
//! documentation][mio-supported]. However, these additional platforms may
|
||||
//! become unsupported in the future.
|
||||
//!
|
||||
//! Note that Wine is considered to be a different platform from Windows. See
|
||||
//! mio's documentation for more information on Wine support.
|
||||
//!
|
||||
//! [mio-supported]: https://crates.io/crates/mio#platforms
|
||||
//!
|
||||
//! ### WASM support
|
||||
//!
|
||||
//! Tokio has some limited support for the WASM platform. Without the
|
||||
//! `tokio_unstable` flag, the following features are supported:
|
||||
//!
|
||||
//! * `sync`
|
||||
//! * `macros`
|
||||
//! * `io-util`
|
||||
//! * `rt`
|
||||
//! * `time`
|
||||
//!
|
||||
//! Enabling any other feature (including `full`) will cause a compilation
|
||||
//! failure.
|
||||
//!
|
||||
//! The `time` module will only work on WASM platforms that have support for
|
||||
//! timers (e.g. wasm32-wasi). The timing functions will panic if used on a WASM
|
||||
//! platform that does not support timers.
|
||||
//!
|
||||
//! Note also that if the runtime becomes indefinitely idle, it will panic
|
||||
//! immediately instead of blocking forever. On platforms that don't support
|
||||
//! time, this means that the runtime can never be idle in any way.
|
||||
//!
|
||||
//! ### Unstable WASM support
|
||||
//!
|
||||
//! Tokio also has unstable support for some additional WASM features. This
|
||||
//! requires the use of the `tokio_unstable` flag.
|
||||
//!
|
||||
//! Using this flag enables the use of `tokio::net` on the wasm32-wasi target.
|
||||
//! However, not all methods are available on the networking types as WASI
|
||||
//! currently does not support the creation of new sockets from within WASM.
|
||||
//! Because of this, sockets must currently be created via the `FromRawFd`
|
||||
//! trait.
|
||||
|
||||
// Test that pointer width is compatible. This asserts that e.g. usize is at
|
||||
// least 32 bits, which a lot of components in Tokio currently assumes.
|
||||
|
@ -362,6 +456,52 @@ compile_error! {
|
|||
"Tokio requires the platform pointer width to be 32, 64, or 128 bits"
|
||||
}
|
||||
|
||||
// Ensure that our build script has correctly set cfg flags for wasm.
|
||||
//
|
||||
// Each condition is written all(a, not(b)). This should be read as
|
||||
// "if a, then we must also have b".
|
||||
#[cfg(any(
|
||||
all(target_arch = "wasm32", not(tokio_wasm)),
|
||||
all(target_arch = "wasm64", not(tokio_wasm)),
|
||||
all(target_family = "wasm", not(tokio_wasm)),
|
||||
all(target_os = "wasi", not(tokio_wasm)),
|
||||
all(target_os = "wasi", not(tokio_wasi)),
|
||||
all(target_os = "wasi", tokio_wasm_not_wasi),
|
||||
all(tokio_wasm, not(any(target_arch = "wasm32", target_arch = "wasm64"))),
|
||||
all(tokio_wasm_not_wasi, not(tokio_wasm)),
|
||||
all(tokio_wasi, not(tokio_wasm))
|
||||
))]
|
||||
compile_error!("Tokio's build script has incorrectly detected wasm.");
|
||||
|
||||
#[cfg(all(
|
||||
not(tokio_unstable),
|
||||
tokio_wasm,
|
||||
any(
|
||||
feature = "fs",
|
||||
feature = "io-std",
|
||||
feature = "net",
|
||||
feature = "process",
|
||||
feature = "rt-multi-thread",
|
||||
feature = "signal"
|
||||
)
|
||||
))]
|
||||
compile_error!("Only features sync,macros,io-util,rt,time are supported on wasm.");
|
||||
|
||||
#[cfg(all(not(tokio_unstable), tokio_taskdump))]
|
||||
compile_error!("The `tokio_taskdump` feature requires `--cfg tokio_unstable`.");
|
||||
|
||||
#[cfg(all(
|
||||
tokio_taskdump,
|
||||
not(all(
|
||||
target_os = "linux",
|
||||
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
|
||||
))
|
||||
))]
|
||||
compile_error!(
|
||||
"The `tokio_taskdump` feature is only currently supported on \
|
||||
linux, on `aarch64`, `x86` and `x86_64`."
|
||||
);
|
||||
|
||||
// Includes re-exports used by macros.
|
||||
//
|
||||
// This module is not intended to be part of the public API. In general, any
|
||||
|
@ -380,20 +520,25 @@ pub mod io;
|
|||
pub mod net;
|
||||
|
||||
mod loom;
|
||||
mod park;
|
||||
|
||||
cfg_process! {
|
||||
pub mod process;
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "net", feature = "fs", feature = "io-std"))]
|
||||
#[cfg(any(
|
||||
feature = "fs",
|
||||
feature = "io-std",
|
||||
feature = "net",
|
||||
all(windows, feature = "process"),
|
||||
))]
|
||||
mod blocking;
|
||||
|
||||
cfg_rt! {
|
||||
pub mod runtime;
|
||||
}
|
||||
|
||||
pub(crate) mod coop;
|
||||
cfg_not_rt! {
|
||||
pub(crate) mod runtime;
|
||||
}
|
||||
|
||||
cfg_signal! {
|
||||
pub mod signal;
|
||||
|
@ -422,6 +567,40 @@ cfg_time! {
|
|||
pub mod time;
|
||||
}
|
||||
|
||||
mod trace {
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
cfg_taskdump! {
|
||||
pub(crate) use crate::runtime::task::trace::trace_leaf;
|
||||
}
|
||||
|
||||
cfg_not_taskdump! {
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn trace_leaf(_: &mut std::task::Context<'_>) -> std::task::Poll<()> {
|
||||
std::task::Poll::Ready(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "sync"), allow(dead_code))]
|
||||
pub(crate) fn async_trace_leaf() -> impl Future<Output = ()> {
|
||||
struct Trace;
|
||||
|
||||
impl Future for Trace {
|
||||
type Output = ();
|
||||
|
||||
#[inline(always)]
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||
trace_leaf(cx)
|
||||
}
|
||||
}
|
||||
|
||||
Trace
|
||||
}
|
||||
}
|
||||
|
||||
mod util;
|
||||
|
||||
/// Due to the `Stream` trait's inclusion in `std` landing later than Tokio's 1.0
|
||||
|
@ -477,14 +656,6 @@ pub(crate) use self::doc::os;
|
|||
#[allow(unused)]
|
||||
pub(crate) use std::os;
|
||||
|
||||
#[cfg(docsrs)]
|
||||
#[allow(unused)]
|
||||
pub(crate) use self::doc::winapi;
|
||||
|
||||
#[cfg(all(not(docsrs), windows, feature = "net"))]
|
||||
#[allow(unused)]
|
||||
pub(crate) use ::winapi;
|
||||
|
||||
cfg_macros! {
|
||||
/// Implementation detail of the `select!` macro. This macro is **not**
|
||||
/// intended to be used as part of the public API and is permitted to
|
||||
|
@ -535,3 +706,7 @@ cfg_macros! {
|
|||
#[cfg(feature = "io-util")]
|
||||
#[cfg(test)]
|
||||
fn is_unpin<T: Unpin>() {}
|
||||
|
||||
/// fuzz test (fuzz_linked_list)
|
||||
#[cfg(fuzzing)]
|
||||
pub mod fuzz;
|
||||
|
|
|
@ -25,6 +25,13 @@ pub(crate) mod sync {
|
|||
}
|
||||
}
|
||||
pub(crate) use loom::sync::*;
|
||||
|
||||
pub(crate) mod atomic {
|
||||
pub(crate) use loom::sync::atomic::*;
|
||||
|
||||
// TODO: implement a loom version
|
||||
pub(crate) type StaticAtomicU64 = std::sync::atomic::AtomicU64;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod rand {
|
||||
|
@ -38,3 +45,8 @@ pub(crate) mod sys {
|
|||
2
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod thread {
|
||||
pub use loom::lazy_static::AccessError;
|
||||
pub use loom::thread::*;
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// `AtomicPtr` providing an additional `load_unsync` function.
|
||||
pub(crate) struct AtomicPtr<T> {
|
||||
inner: std::sync::atomic::AtomicPtr<T>,
|
||||
}
|
||||
|
||||
impl<T> AtomicPtr<T> {
|
||||
pub(crate) fn new(ptr: *mut T) -> AtomicPtr<T> {
|
||||
let inner = std::sync::atomic::AtomicPtr::new(ptr);
|
||||
AtomicPtr { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for AtomicPtr<T> {
|
||||
type Target = std::sync::atomic::AtomicPtr<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for AtomicPtr<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for AtomicPtr<T> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.deref().fmt(fmt)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ use std::cell::UnsafeCell;
|
|||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// `AtomicU16` providing an additional `load_unsync` function.
|
||||
/// `AtomicU16` providing an additional `unsync_load` function.
|
||||
pub(crate) struct AtomicU16 {
|
||||
inner: UnsafeCell<std::sync::atomic::AtomicU16>,
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ impl AtomicU16 {
|
|||
/// All mutations must have happened before the unsynchronized load.
|
||||
/// Additionally, there must be no concurrent mutations.
|
||||
pub(crate) unsafe fn unsync_load(&self) -> u16 {
|
||||
*(*self.inner.get()).get_mut()
|
||||
core::ptr::read(self.inner.get() as *const u16)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::cell::UnsafeCell;
|
|||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// `AtomicU32` providing an additional `load_unsync` function.
|
||||
/// `AtomicU32` providing an additional `unsync_load` function.
|
||||
pub(crate) struct AtomicU32 {
|
||||
inner: UnsafeCell<std::sync::atomic::AtomicU32>,
|
||||
}
|
||||
|
@ -15,6 +15,16 @@ impl AtomicU32 {
|
|||
let inner = UnsafeCell::new(std::sync::atomic::AtomicU32::new(val));
|
||||
AtomicU32 { inner }
|
||||
}
|
||||
|
||||
/// Performs an unsynchronized load.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// All mutations must have happened before the unsynchronized load.
|
||||
/// Additionally, there must be no concurrent mutations.
|
||||
pub(crate) unsafe fn unsync_load(&self) -> u32 {
|
||||
core::ptr::read(self.inner.get() as *const u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for AtomicU32 {
|
||||
|
|
|
@ -7,72 +7,13 @@
|
|||
// `#[cfg(target_has_atomic = "64")]`.
|
||||
// Refs: https://github.com/rust-lang/rust/tree/master/src/librustc_target
|
||||
cfg_has_atomic_u64! {
|
||||
pub(crate) use std::sync::atomic::AtomicU64;
|
||||
#[path = "atomic_u64_native.rs"]
|
||||
mod imp;
|
||||
}
|
||||
|
||||
cfg_not_has_atomic_u64! {
|
||||
use crate::loom::sync::Mutex;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AtomicU64 {
|
||||
inner: Mutex<u64>,
|
||||
}
|
||||
|
||||
impl AtomicU64 {
|
||||
pub(crate) fn new(val: u64) -> Self {
|
||||
Self {
|
||||
inner: Mutex::new(val),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn load(&self, _: Ordering) -> u64 {
|
||||
*self.inner.lock()
|
||||
}
|
||||
|
||||
pub(crate) fn store(&self, val: u64, _: Ordering) {
|
||||
*self.inner.lock() = val;
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_add(&self, val: u64, _: Ordering) -> u64 {
|
||||
let mut lock = self.inner.lock();
|
||||
let prev = *lock;
|
||||
*lock = prev + val;
|
||||
prev
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_or(&self, val: u64, _: Ordering) -> u64 {
|
||||
let mut lock = self.inner.lock();
|
||||
let prev = *lock;
|
||||
*lock = prev | val;
|
||||
prev
|
||||
}
|
||||
|
||||
pub(crate) fn compare_exchange(
|
||||
&self,
|
||||
current: u64,
|
||||
new: u64,
|
||||
_success: Ordering,
|
||||
_failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
let mut lock = self.inner.lock();
|
||||
|
||||
if *lock == current {
|
||||
*lock = new;
|
||||
Ok(current)
|
||||
} else {
|
||||
Err(*lock)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compare_exchange_weak(
|
||||
&self,
|
||||
current: u64,
|
||||
new: u64,
|
||||
success: Ordering,
|
||||
failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
self.compare_exchange(current, new, success, failure)
|
||||
}
|
||||
}
|
||||
#[path = "atomic_u64_as_mutex.rs"]
|
||||
mod imp;
|
||||
}
|
||||
|
||||
pub(crate) use imp::{AtomicU64, StaticAtomicU64};
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
use crate::loom::sync::Mutex;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
cfg_has_const_mutex_new! {
|
||||
#[path = "atomic_u64_static_const_new.rs"]
|
||||
mod static_macro;
|
||||
}
|
||||
|
||||
cfg_not_has_const_mutex_new! {
|
||||
#[path = "atomic_u64_static_once_cell.rs"]
|
||||
mod static_macro;
|
||||
}
|
||||
|
||||
pub(crate) use static_macro::StaticAtomicU64;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AtomicU64 {
|
||||
inner: Mutex<u64>,
|
||||
}
|
||||
|
||||
impl AtomicU64 {
|
||||
pub(crate) fn load(&self, _: Ordering) -> u64 {
|
||||
*self.inner.lock()
|
||||
}
|
||||
|
||||
pub(crate) fn store(&self, val: u64, _: Ordering) {
|
||||
*self.inner.lock() = val;
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_add(&self, val: u64, _: Ordering) -> u64 {
|
||||
let mut lock = self.inner.lock();
|
||||
let prev = *lock;
|
||||
*lock = prev + val;
|
||||
prev
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_or(&self, val: u64, _: Ordering) -> u64 {
|
||||
let mut lock = self.inner.lock();
|
||||
let prev = *lock;
|
||||
*lock = prev | val;
|
||||
prev
|
||||
}
|
||||
|
||||
pub(crate) fn compare_exchange(
|
||||
&self,
|
||||
current: u64,
|
||||
new: u64,
|
||||
_success: Ordering,
|
||||
_failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
let mut lock = self.inner.lock();
|
||||
|
||||
if *lock == current {
|
||||
*lock = new;
|
||||
Ok(current)
|
||||
} else {
|
||||
Err(*lock)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compare_exchange_weak(
|
||||
&self,
|
||||
current: u64,
|
||||
new: u64,
|
||||
success: Ordering,
|
||||
failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
self.compare_exchange(current, new, success, failure)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AtomicU64 {
|
||||
fn default() -> AtomicU64 {
|
||||
AtomicU64::new(u64::default())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
pub(crate) use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
/// Alias `AtomicU64` to `StaticAtomicU64`
|
||||
pub(crate) type StaticAtomicU64 = AtomicU64;
|
|
@ -0,0 +1,12 @@
|
|||
use super::AtomicU64;
|
||||
use crate::loom::sync::Mutex;
|
||||
|
||||
pub(crate) type StaticAtomicU64 = AtomicU64;
|
||||
|
||||
impl AtomicU64 {
|
||||
pub(crate) const fn new(val: u64) -> Self {
|
||||
Self {
|
||||
inner: Mutex::const_new(val),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
use super::AtomicU64;
|
||||
use crate::loom::sync::{atomic::Ordering, Mutex};
|
||||
use crate::util::once_cell::OnceCell;
|
||||
|
||||
pub(crate) struct StaticAtomicU64 {
|
||||
init: u64,
|
||||
cell: OnceCell<Mutex<u64>>,
|
||||
}
|
||||
|
||||
impl AtomicU64 {
|
||||
pub(crate) fn new(val: u64) -> Self {
|
||||
Self {
|
||||
inner: Mutex::new(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticAtomicU64 {
|
||||
pub(crate) const fn new(val: u64) -> StaticAtomicU64 {
|
||||
StaticAtomicU64 {
|
||||
init: val,
|
||||
cell: OnceCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn load(&self, order: Ordering) -> u64 {
|
||||
*self.inner().lock()
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_add(&self, val: u64, order: Ordering) -> u64 {
|
||||
let mut lock = self.inner().lock();
|
||||
let prev = *lock;
|
||||
*lock = prev + val;
|
||||
prev
|
||||
}
|
||||
|
||||
pub(crate) fn compare_exchange_weak(
|
||||
&self,
|
||||
current: u64,
|
||||
new: u64,
|
||||
_success: Ordering,
|
||||
_failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
let mut lock = self.inner().lock();
|
||||
|
||||
if *lock == current {
|
||||
*lock = new;
|
||||
Ok(current)
|
||||
} else {
|
||||
Err(*lock)
|
||||
}
|
||||
}
|
||||
|
||||
fn inner(&self) -> &Mutex<u64> {
|
||||
self.cell.get(|| Mutex::new(self.init))
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// `AtomicU8` providing an additional `load_unsync` function.
|
||||
pub(crate) struct AtomicU8 {
|
||||
inner: UnsafeCell<std::sync::atomic::AtomicU8>,
|
||||
}
|
||||
|
||||
unsafe impl Send for AtomicU8 {}
|
||||
unsafe impl Sync for AtomicU8 {}
|
||||
|
||||
impl AtomicU8 {
|
||||
pub(crate) const fn new(val: u8) -> AtomicU8 {
|
||||
let inner = UnsafeCell::new(std::sync::atomic::AtomicU8::new(val));
|
||||
AtomicU8 { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for AtomicU8 {
|
||||
type Target = std::sync::atomic::AtomicU8;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// safety: it is always safe to access `&self` fns on the inner value as
|
||||
// we never perform unsafe mutations.
|
||||
unsafe { &*self.inner.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AtomicU8 {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.deref().fmt(fmt)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ use std::cell::UnsafeCell;
|
|||
use std::fmt;
|
||||
use std::ops;
|
||||
|
||||
/// `AtomicUsize` providing an additional `load_unsync` function.
|
||||
/// `AtomicUsize` providing an additional `unsync_load` function.
|
||||
pub(crate) struct AtomicUsize {
|
||||
inner: UnsafeCell<std::sync::atomic::AtomicUsize>,
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ impl AtomicUsize {
|
|||
/// All mutations must have happened before the unsynchronized load.
|
||||
/// Additionally, there must be no concurrent mutations.
|
||||
pub(crate) unsafe fn unsync_load(&self) -> usize {
|
||||
*(*self.inner.get()).get_mut()
|
||||
core::ptr::read(self.inner.get() as *const usize)
|
||||
}
|
||||
|
||||
pub(crate) fn with_mut<R>(&mut self, f: impl FnOnce(&mut usize) -> R) -> R {
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
//! A `Barrier` that provides `wait_timeout`.
|
||||
//!
|
||||
//! This implementation mirrors that of the Rust standard library.
|
||||
|
||||
use crate::loom::sync::{Condvar, Mutex};
|
||||
use std::fmt;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// A barrier enables multiple threads to synchronize the beginning
|
||||
/// of some computation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Barrier};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mut handles = Vec::with_capacity(10);
|
||||
/// let barrier = Arc::new(Barrier::new(10));
|
||||
/// for _ in 0..10 {
|
||||
/// let c = Arc::clone(&barrier);
|
||||
/// // The same messages will be printed together.
|
||||
/// // You will NOT see any interleaving.
|
||||
/// handles.push(thread::spawn(move|| {
|
||||
/// println!("before wait");
|
||||
/// c.wait();
|
||||
/// println!("after wait");
|
||||
/// }));
|
||||
/// }
|
||||
/// // Wait for other threads to finish.
|
||||
/// for handle in handles {
|
||||
/// handle.join().unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
pub(crate) struct Barrier {
|
||||
lock: Mutex<BarrierState>,
|
||||
cvar: Condvar,
|
||||
num_threads: usize,
|
||||
}
|
||||
|
||||
// The inner state of a double barrier
|
||||
struct BarrierState {
|
||||
count: usize,
|
||||
generation_id: usize,
|
||||
}
|
||||
|
||||
/// A `BarrierWaitResult` is returned by [`Barrier::wait()`] when all threads
|
||||
/// in the [`Barrier`] have rendezvoused.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
///
|
||||
/// let barrier = Barrier::new(1);
|
||||
/// let barrier_wait_result = barrier.wait();
|
||||
/// ```
|
||||
pub(crate) struct BarrierWaitResult(bool);
|
||||
|
||||
impl fmt::Debug for Barrier {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Barrier").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl Barrier {
|
||||
/// Creates a new barrier that can block a given number of threads.
|
||||
///
|
||||
/// A barrier will block `n`-1 threads which call [`wait()`] and then wake
|
||||
/// up all threads at once when the `n`th thread calls [`wait()`].
|
||||
///
|
||||
/// [`wait()`]: Barrier::wait
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
///
|
||||
/// let barrier = Barrier::new(10);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub(crate) fn new(n: usize) -> Barrier {
|
||||
Barrier {
|
||||
lock: Mutex::new(BarrierState {
|
||||
count: 0,
|
||||
generation_id: 0,
|
||||
}),
|
||||
cvar: Condvar::new(),
|
||||
num_threads: n,
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks the current thread until all threads have rendezvoused here.
|
||||
///
|
||||
/// Barriers are re-usable after all threads have rendezvoused once, and can
|
||||
/// be used continuously.
|
||||
///
|
||||
/// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that
|
||||
/// returns `true` from [`BarrierWaitResult::is_leader()`] when returning
|
||||
/// from this function, and all other threads will receive a result that
|
||||
/// will return `false` from [`BarrierWaitResult::is_leader()`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Barrier};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mut handles = Vec::with_capacity(10);
|
||||
/// let barrier = Arc::new(Barrier::new(10));
|
||||
/// for _ in 0..10 {
|
||||
/// let c = Arc::clone(&barrier);
|
||||
/// // The same messages will be printed together.
|
||||
/// // You will NOT see any interleaving.
|
||||
/// handles.push(thread::spawn(move|| {
|
||||
/// println!("before wait");
|
||||
/// c.wait();
|
||||
/// println!("after wait");
|
||||
/// }));
|
||||
/// }
|
||||
/// // Wait for other threads to finish.
|
||||
/// for handle in handles {
|
||||
/// handle.join().unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
pub(crate) fn wait(&self) -> BarrierWaitResult {
|
||||
let mut lock = self.lock.lock();
|
||||
let local_gen = lock.generation_id;
|
||||
lock.count += 1;
|
||||
if lock.count < self.num_threads {
|
||||
// We need a while loop to guard against spurious wakeups.
|
||||
// https://en.wikipedia.org/wiki/Spurious_wakeup
|
||||
while local_gen == lock.generation_id {
|
||||
lock = self.cvar.wait(lock).unwrap();
|
||||
}
|
||||
BarrierWaitResult(false)
|
||||
} else {
|
||||
lock.count = 0;
|
||||
lock.generation_id = lock.generation_id.wrapping_add(1);
|
||||
self.cvar.notify_all();
|
||||
BarrierWaitResult(true)
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks the current thread until all threads have rendezvoused here for
|
||||
/// at most `timeout` duration.
|
||||
pub(crate) fn wait_timeout(&self, timeout: Duration) -> Option<BarrierWaitResult> {
|
||||
// This implementation mirrors `wait`, but with each blocking operation
|
||||
// replaced by a timeout-amenable alternative.
|
||||
|
||||
let deadline = Instant::now() + timeout;
|
||||
|
||||
// Acquire `self.lock` with at most `timeout` duration.
|
||||
let mut lock = loop {
|
||||
if let Some(guard) = self.lock.try_lock() {
|
||||
break guard;
|
||||
} else if Instant::now() > deadline {
|
||||
return None;
|
||||
} else {
|
||||
std::thread::yield_now();
|
||||
}
|
||||
};
|
||||
|
||||
// Shrink the `timeout` to account for the time taken to acquire `lock`.
|
||||
let timeout = deadline.saturating_duration_since(Instant::now());
|
||||
|
||||
let local_gen = lock.generation_id;
|
||||
lock.count += 1;
|
||||
if lock.count < self.num_threads {
|
||||
// We need a while loop to guard against spurious wakeups.
|
||||
// https://en.wikipedia.org/wiki/Spurious_wakeup
|
||||
while local_gen == lock.generation_id {
|
||||
let (guard, timeout_result) = self.cvar.wait_timeout(lock, timeout).unwrap();
|
||||
lock = guard;
|
||||
if timeout_result.timed_out() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(BarrierWaitResult(false))
|
||||
} else {
|
||||
lock.count = 0;
|
||||
lock.generation_id = lock.generation_id.wrapping_add(1);
|
||||
self.cvar.notify_all();
|
||||
Some(BarrierWaitResult(true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BarrierWaitResult {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("BarrierWaitResult")
|
||||
.field("is_leader", &self.is_leader())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl BarrierWaitResult {
|
||||
/// Returns `true` if this thread is the "leader thread" for the call to
|
||||
/// [`Barrier::wait()`].
|
||||
///
|
||||
/// Only one thread will have `true` returned from their result, all other
|
||||
/// threads will have `false` returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
///
|
||||
/// let barrier = Barrier::new(1);
|
||||
/// let barrier_wait_result = barrier.wait();
|
||||
/// println!("{:?}", barrier_wait_result.is_leader());
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub(crate) fn is_leader(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
#![cfg_attr(any(not(feature = "full"), loom), allow(unused_imports, dead_code))]
|
||||
|
||||
mod atomic_ptr;
|
||||
mod atomic_u16;
|
||||
mod atomic_u32;
|
||||
mod atomic_u64;
|
||||
mod atomic_u8;
|
||||
mod atomic_usize;
|
||||
mod barrier;
|
||||
mod mutex;
|
||||
#[cfg(feature = "parking_lot")]
|
||||
mod parking_lot;
|
||||
|
@ -71,21 +70,41 @@ pub(crate) mod sync {
|
|||
pub(crate) use crate::loom::std::mutex::Mutex;
|
||||
|
||||
pub(crate) mod atomic {
|
||||
pub(crate) use crate::loom::std::atomic_ptr::AtomicPtr;
|
||||
pub(crate) use crate::loom::std::atomic_u16::AtomicU16;
|
||||
pub(crate) use crate::loom::std::atomic_u32::AtomicU32;
|
||||
pub(crate) use crate::loom::std::atomic_u64::AtomicU64;
|
||||
pub(crate) use crate::loom::std::atomic_u8::AtomicU8;
|
||||
pub(crate) use crate::loom::std::atomic_u64::{AtomicU64, StaticAtomicU64};
|
||||
pub(crate) use crate::loom::std::atomic_usize::AtomicUsize;
|
||||
|
||||
pub(crate) use std::sync::atomic::{fence, AtomicBool, Ordering};
|
||||
pub(crate) use std::sync::atomic::{fence, AtomicBool, AtomicPtr, AtomicU8, Ordering};
|
||||
}
|
||||
|
||||
pub(crate) use super::barrier::Barrier;
|
||||
}
|
||||
|
||||
pub(crate) mod sys {
|
||||
#[cfg(feature = "rt-multi-thread")]
|
||||
pub(crate) fn num_cpus() -> usize {
|
||||
usize::max(1, num_cpus::get())
|
||||
const ENV_WORKER_THREADS: &str = "TOKIO_WORKER_THREADS";
|
||||
|
||||
match std::env::var(ENV_WORKER_THREADS) {
|
||||
Ok(s) => {
|
||||
let n = s.parse().unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"\"{}\" must be usize, error: {}, value: {}",
|
||||
ENV_WORKER_THREADS, e, s
|
||||
)
|
||||
});
|
||||
assert!(n > 0, "\"{}\" cannot be set to 0", ENV_WORKER_THREADS);
|
||||
n
|
||||
}
|
||||
Err(std::env::VarError::NotPresent) => usize::max(1, num_cpus::get()),
|
||||
Err(std::env::VarError::NotUnicode(e)) => {
|
||||
panic!(
|
||||
"\"{}\" must be valid unicode, error: {:?}",
|
||||
ENV_WORKER_THREADS, e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "rt-multi-thread"))]
|
||||
|
@ -102,7 +121,7 @@ pub(crate) mod thread {
|
|||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use std::thread::{
|
||||
current, panicking, park, park_timeout, sleep, spawn, Builder, JoinHandle, LocalKey,
|
||||
Result, Thread, ThreadId,
|
||||
current, panicking, park, park_timeout, sleep, spawn, AccessError, Builder, JoinHandle,
|
||||
LocalKey, Result, Thread, ThreadId,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,6 +12,12 @@ impl<T> Mutex<T> {
|
|||
Mutex(sync::Mutex::new(t))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(tokio_no_const_mutex_new))]
|
||||
pub(crate) const fn const_new(t: T) -> Mutex<T> {
|
||||
Mutex(sync::Mutex::new(t))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn lock(&self) -> MutexGuard<'_, T> {
|
||||
match self.0.lock() {
|
||||
|
|
|
@ -52,7 +52,7 @@ impl<T> Mutex<T> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(all(feature = "parking_lot", not(all(loom, test)),))]
|
||||
#[cfg(all(feature = "parking_lot", not(all(loom, test))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "parking_lot",))))]
|
||||
pub(crate) const fn const_new(t: T) -> Mutex<T> {
|
||||
Mutex(PhantomData, parking_lot::const_mutex(t))
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
//! This module defines a macro that lets you go from a raw pointer to a struct
|
||||
//! to a raw pointer to a field of the struct.
|
||||
|
||||
macro_rules! generate_addr_of_methods {
|
||||
(
|
||||
impl<$($gen:ident)*> $struct_name:ty {$(
|
||||
$(#[$attrs:meta])*
|
||||
$vis:vis unsafe fn $fn_name:ident(self: NonNull<Self>) -> NonNull<$field_type:ty> {
|
||||
&self$(.$field_name:tt)+
|
||||
}
|
||||
)*}
|
||||
) => {
|
||||
impl<$($gen)*> $struct_name {$(
|
||||
$(#[$attrs])*
|
||||
$vis unsafe fn $fn_name(me: ::core::ptr::NonNull<Self>) -> ::core::ptr::NonNull<$field_type> {
|
||||
let me = me.as_ptr();
|
||||
let field = ::std::ptr::addr_of_mut!((*me) $(.$field_name)+ );
|
||||
::core::ptr::NonNull::new_unchecked(field)
|
||||
}
|
||||
)*}
|
||||
};
|
||||
}
|
|
@ -13,6 +13,18 @@ macro_rules! feature {
|
|||
}
|
||||
}
|
||||
|
||||
/// Enables Windows-specific code.
|
||||
/// Use this macro instead of `cfg(windows)` to generate docs properly.
|
||||
macro_rules! cfg_windows {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(any(all(doc, docsrs), windows))]
|
||||
#[cfg_attr(docsrs, doc(cfg(windows)))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables enter::block_on.
|
||||
macro_rules! cfg_block_on {
|
||||
($($item:item)*) => {
|
||||
|
@ -61,6 +73,7 @@ macro_rules! cfg_fs {
|
|||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(feature = "fs")]
|
||||
#[cfg(not(tokio_wasi))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
|
||||
$item
|
||||
)*
|
||||
|
@ -69,7 +82,11 @@ macro_rules! cfg_fs {
|
|||
|
||||
macro_rules! cfg_io_blocking {
|
||||
($($item:item)*) => {
|
||||
$( #[cfg(any(feature = "io-std", feature = "fs"))] $item )*
|
||||
$( #[cfg(any(
|
||||
feature = "io-std",
|
||||
feature = "fs",
|
||||
all(windows, feature = "process"),
|
||||
))] $item )*
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,12 +95,12 @@ macro_rules! cfg_io_driver {
|
|||
$(
|
||||
#[cfg(any(
|
||||
feature = "net",
|
||||
feature = "process",
|
||||
all(unix, feature = "process"),
|
||||
all(unix, feature = "signal"),
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(
|
||||
feature = "net",
|
||||
feature = "process",
|
||||
all(unix, feature = "process"),
|
||||
all(unix, feature = "signal"),
|
||||
))))]
|
||||
$item
|
||||
|
@ -96,7 +113,7 @@ macro_rules! cfg_io_driver_impl {
|
|||
$(
|
||||
#[cfg(any(
|
||||
feature = "net",
|
||||
feature = "process",
|
||||
all(unix, feature = "process"),
|
||||
all(unix, feature = "signal"),
|
||||
))]
|
||||
$item
|
||||
|
@ -109,7 +126,7 @@ macro_rules! cfg_not_io_driver {
|
|||
$(
|
||||
#[cfg(not(any(
|
||||
feature = "net",
|
||||
feature = "process",
|
||||
all(unix, feature = "process"),
|
||||
all(unix, feature = "signal"),
|
||||
)))]
|
||||
$item
|
||||
|
@ -195,6 +212,22 @@ macro_rules! cfg_not_metrics {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_not_rt_and_metrics_and_net {
|
||||
($($item:item)*) => {
|
||||
$( #[cfg(not(all(feature = "net", feature = "rt", all(tokio_unstable, not(loom)))))]$item )*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_net_or_process {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(any(feature = "net", feature = "process"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "net", feature = "process"))))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_net {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
|
@ -231,6 +264,7 @@ macro_rules! cfg_process {
|
|||
#[cfg(feature = "process")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
|
||||
#[cfg(not(loom))]
|
||||
#[cfg(not(tokio_wasi))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
|
@ -259,6 +293,7 @@ macro_rules! cfg_signal {
|
|||
#[cfg(feature = "signal")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
|
||||
#[cfg(not(loom))]
|
||||
#[cfg(not(tokio_wasi))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
|
@ -274,6 +309,13 @@ macro_rules! cfg_signal_internal {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_signal_internal_and_unix {
|
||||
($($item:item)*) => {
|
||||
#[cfg(unix)]
|
||||
cfg_signal_internal! { $($item)* }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_not_signal_internal {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
|
@ -318,7 +360,7 @@ macro_rules! cfg_not_rt {
|
|||
macro_rules! cfg_rt_multi_thread {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(feature = "rt-multi-thread")]
|
||||
#[cfg(all(feature = "rt-multi-thread", not(tokio_wasi)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))]
|
||||
$item
|
||||
)*
|
||||
|
@ -331,6 +373,44 @@ macro_rules! cfg_not_rt_multi_thread {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_taskdump {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(all(
|
||||
tokio_unstable,
|
||||
tokio_taskdump,
|
||||
feature = "rt",
|
||||
target_os = "linux",
|
||||
any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64"
|
||||
)
|
||||
))]
|
||||
$item
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! cfg_not_taskdump {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(not(all(
|
||||
tokio_unstable,
|
||||
tokio_taskdump,
|
||||
feature = "rt",
|
||||
target_os = "linux",
|
||||
any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64"
|
||||
)
|
||||
)))]
|
||||
$item
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! cfg_test_util {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
|
@ -431,12 +511,14 @@ macro_rules! cfg_not_coop {
|
|||
macro_rules! cfg_has_atomic_u64 {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(not(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "riscv32"
|
||||
)))]
|
||||
#[cfg_attr(
|
||||
not(tokio_no_target_has_atomic),
|
||||
cfg(all(target_has_atomic = "64", not(tokio_no_atomic_u64))
|
||||
))]
|
||||
#[cfg_attr(
|
||||
tokio_no_target_has_atomic,
|
||||
cfg(not(tokio_no_atomic_u64))
|
||||
)]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
|
@ -445,12 +527,62 @@ macro_rules! cfg_has_atomic_u64 {
|
|||
macro_rules! cfg_not_has_atomic_u64 {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "riscv32"
|
||||
))]
|
||||
#[cfg_attr(
|
||||
not(tokio_no_target_has_atomic),
|
||||
cfg(any(not(target_has_atomic = "64"), tokio_no_atomic_u64)
|
||||
))]
|
||||
#[cfg_attr(
|
||||
tokio_no_target_has_atomic,
|
||||
cfg(tokio_no_atomic_u64)
|
||||
)]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_has_const_mutex_new {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(all(
|
||||
not(all(loom, test)),
|
||||
any(
|
||||
feature = "parking_lot",
|
||||
not(tokio_no_const_mutex_new)
|
||||
)
|
||||
))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_not_has_const_mutex_new {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(not(all(
|
||||
not(all(loom, test)),
|
||||
any(
|
||||
feature = "parking_lot",
|
||||
not(tokio_no_const_mutex_new)
|
||||
)
|
||||
)))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_not_wasi {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(not(tokio_wasi))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_is_wasm_not_wasi {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(tokio_wasm_not_wasi)]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
/// for **all** branches complete regardless if any complete with `Err`. Use
|
||||
/// [`try_join!`] to return early when `Err` is encountered.
|
||||
///
|
||||
/// [`try_join!`]: macro@try_join
|
||||
/// [`try_join!`]: crate::try_join
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
|
@ -60,6 +60,9 @@ macro_rules! join {
|
|||
// normalization is complete.
|
||||
( $($count:tt)* )
|
||||
|
||||
// The expression `0+1+1+ ... +1` equal to the number of branches.
|
||||
( $($total:tt)* )
|
||||
|
||||
// Normalized join! branches
|
||||
$( ( $($skip:tt)* ) $e:expr, )*
|
||||
|
||||
|
@ -69,24 +72,66 @@ macro_rules! join {
|
|||
|
||||
// Safety: nothing must be moved out of `futures`. This is to satisfy
|
||||
// the requirement of `Pin::new_unchecked` called below.
|
||||
//
|
||||
// We can't use the `pin!` macro for this because `futures` is a tuple
|
||||
// and the standard library provides no way to pin-project to the fields
|
||||
// of a tuple.
|
||||
let mut futures = ( $( maybe_done($e), )* );
|
||||
|
||||
// This assignment makes sure that the `poll_fn` closure only has a
|
||||
// reference to the futures, instead of taking ownership of them. This
|
||||
// mitigates the issue described in
|
||||
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
|
||||
let mut futures = &mut futures;
|
||||
|
||||
// Each time the future created by poll_fn is polled, a different future will be polled first
|
||||
// to ensure every future passed to join! gets a chance to make progress even if
|
||||
// one of the futures consumes the whole budget.
|
||||
//
|
||||
// This is number of futures that will be skipped in the first loop
|
||||
// iteration the next time.
|
||||
let mut skip_next_time: u32 = 0;
|
||||
|
||||
poll_fn(move |cx| {
|
||||
const COUNT: u32 = $($total)*;
|
||||
|
||||
let mut is_pending = false;
|
||||
|
||||
let mut to_run = COUNT;
|
||||
|
||||
// The number of futures that will be skipped in the first loop iteration.
|
||||
let mut skip = skip_next_time;
|
||||
|
||||
skip_next_time = if skip + 1 == COUNT { 0 } else { skip + 1 };
|
||||
|
||||
// This loop runs twice and the first `skip` futures
|
||||
// are not polled in the first iteration.
|
||||
loop {
|
||||
$(
|
||||
// Extract the future for this branch from the tuple.
|
||||
let ( $($skip,)* fut, .. ) = &mut futures;
|
||||
if skip == 0 {
|
||||
if to_run == 0 {
|
||||
// Every future has been polled
|
||||
break;
|
||||
}
|
||||
to_run -= 1;
|
||||
|
||||
// Safety: future is stored on the stack above
|
||||
// and never moved.
|
||||
let mut fut = unsafe { Pin::new_unchecked(fut) };
|
||||
// Extract the future for this branch from the tuple.
|
||||
let ( $($skip,)* fut, .. ) = &mut *futures;
|
||||
|
||||
// Try polling
|
||||
if fut.poll(cx).is_pending() {
|
||||
is_pending = true;
|
||||
// Safety: future is stored on the stack above
|
||||
// and never moved.
|
||||
let mut fut = unsafe { Pin::new_unchecked(fut) };
|
||||
|
||||
// Try polling
|
||||
if fut.poll(cx).is_pending() {
|
||||
is_pending = true;
|
||||
}
|
||||
} else {
|
||||
// Future skipped, one less future to skip in the next iteration
|
||||
skip -= 1;
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
if is_pending {
|
||||
Pending
|
||||
|
@ -107,13 +152,15 @@ macro_rules! join {
|
|||
|
||||
// ===== Normalize =====
|
||||
|
||||
(@ { ( $($s:tt)* ) $($t:tt)* } $e:expr, $($r:tt)* ) => {
|
||||
$crate::join!(@{ ($($s)* _) $($t)* ($($s)*) $e, } $($r)*)
|
||||
(@ { ( $($s:tt)* ) ( $($n:tt)* ) $($t:tt)* } $e:expr, $($r:tt)* ) => {
|
||||
$crate::join!(@{ ($($s)* _) ($($n)* + 1) $($t)* ($($s)*) $e, } $($r)*)
|
||||
};
|
||||
|
||||
// ===== Entry point =====
|
||||
|
||||
( $($e:expr),* $(,)?) => {
|
||||
$crate::join!(@{ () } $($e,)*)
|
||||
( $($e:expr),+ $(,)?) => {
|
||||
$crate::join!(@{ () (0) } $($e,)*)
|
||||
};
|
||||
|
||||
() => { async {}.await }
|
||||
}
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
macro_rules! if_loom {
|
||||
($($t:tt)*) => {{
|
||||
#[cfg(loom)]
|
||||
const LOOM: bool = true;
|
||||
#[cfg(not(loom))]
|
||||
const LOOM: bool = false;
|
||||
|
||||
if LOOM {
|
||||
{
|
||||
$($t)*
|
||||
}
|
||||
}}
|
||||
|
|
|
@ -15,15 +15,14 @@ mod ready;
|
|||
#[macro_use]
|
||||
mod thread_local;
|
||||
|
||||
#[macro_use]
|
||||
mod addr_of;
|
||||
|
||||
cfg_trace! {
|
||||
#[macro_use]
|
||||
mod trace;
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
#[cfg(feature = "rt")]
|
||||
pub(crate) mod scoped_tls;
|
||||
|
||||
cfg_macros! {
|
||||
#[macro_use]
|
||||
mod select;
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
use crate::loom::thread::LocalKey;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::marker;
|
||||
|
||||
/// Sets a reference as a thread-local.
|
||||
macro_rules! scoped_thread_local {
|
||||
($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty) => (
|
||||
$(#[$attrs])*
|
||||
$vis static $name: $crate::macros::scoped_tls::ScopedKey<$ty>
|
||||
= $crate::macros::scoped_tls::ScopedKey {
|
||||
inner: {
|
||||
thread_local!(static FOO: ::std::cell::Cell<*const ()> = {
|
||||
std::cell::Cell::new(::std::ptr::null())
|
||||
});
|
||||
&FOO
|
||||
},
|
||||
_marker: ::std::marker::PhantomData,
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
/// Type representing a thread local storage key corresponding to a reference
|
||||
/// to the type parameter `T`.
|
||||
pub(crate) struct ScopedKey<T> {
|
||||
pub(crate) inner: &'static LocalKey<Cell<*const ()>>,
|
||||
pub(crate) _marker: marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for ScopedKey<T> {}
|
||||
|
||||
impl<T> ScopedKey<T> {
|
||||
/// Inserts a value into this scoped thread local storage slot for a
|
||||
/// duration of a closure.
|
||||
pub(crate) fn set<F, R>(&'static self, t: &T, f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
struct Reset {
|
||||
key: &'static LocalKey<Cell<*const ()>>,
|
||||
val: *const (),
|
||||
}
|
||||
|
||||
impl Drop for Reset {
|
||||
fn drop(&mut self) {
|
||||
self.key.with(|c| c.set(self.val));
|
||||
}
|
||||
}
|
||||
|
||||
let prev = self.inner.with(|c| {
|
||||
let prev = c.get();
|
||||
c.set(t as *const _ as *const ());
|
||||
prev
|
||||
});
|
||||
|
||||
let _reset = Reset {
|
||||
key: self.inner,
|
||||
val: prev,
|
||||
};
|
||||
|
||||
f()
|
||||
}
|
||||
|
||||
/// Gets a value out of this scoped variable.
|
||||
pub(crate) fn with<F, R>(&'static self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(Option<&T>) -> R,
|
||||
{
|
||||
let val = self.inner.with(|c| c.get());
|
||||
|
||||
if val.is_null() {
|
||||
f(None)
|
||||
} else {
|
||||
unsafe { f(Some(&*(val as *const T))) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -101,6 +101,7 @@
|
|||
/// * [`tokio::sync::watch::Receiver::changed`](crate::sync::watch::Receiver::changed)
|
||||
/// * [`tokio::net::TcpListener::accept`](crate::net::TcpListener::accept)
|
||||
/// * [`tokio::net::UnixListener::accept`](crate::net::UnixListener::accept)
|
||||
/// * [`tokio::signal::unix::Signal::recv`](crate::signal::unix::Signal::recv)
|
||||
/// * [`tokio::io::AsyncReadExt::read`](crate::io::AsyncReadExt::read) on any `AsyncRead`
|
||||
/// * [`tokio::io::AsyncReadExt::read_buf`](crate::io::AsyncReadExt::read_buf) on any `AsyncRead`
|
||||
/// * [`tokio::io::AsyncWriteExt::write`](crate::io::AsyncWriteExt::write) on any `AsyncWrite`
|
||||
|
@ -130,6 +131,13 @@
|
|||
/// correctly even if it is restarted while waiting at an `.await`, then it is
|
||||
/// cancellation safe.
|
||||
///
|
||||
/// Cancellation safety can be defined in the following way: If you have a
|
||||
/// future that has not yet completed, then it must be a no-op to drop that
|
||||
/// future and recreate it. This definition is motivated by the situation where
|
||||
/// a `select!` is used in a loop. Without this guarantee, you would lose your
|
||||
/// progress when another branch completes and you restart the `select!` by
|
||||
/// going around the loop.
|
||||
///
|
||||
/// Be aware that cancelling something that is not cancellation safe is not
|
||||
/// necessarily wrong. For example, if you are cancelling a task because the
|
||||
/// application is shutting down, then you probably don't care that partially
|
||||
|
@ -429,7 +437,8 @@ macro_rules! select {
|
|||
//
|
||||
// This module is defined within a scope and should not leak out of this
|
||||
// macro.
|
||||
mod util {
|
||||
#[doc(hidden)]
|
||||
mod __tokio_select_util {
|
||||
// Generate an enum with one variant per select branch
|
||||
$crate::select_priv_declare_output_enum!( ( $($count)* ) );
|
||||
}
|
||||
|
@ -442,13 +451,13 @@ macro_rules! select {
|
|||
|
||||
const BRANCHES: u32 = $crate::count!( $($count)* );
|
||||
|
||||
let mut disabled: util::Mask = Default::default();
|
||||
let mut disabled: __tokio_select_util::Mask = Default::default();
|
||||
|
||||
// First, invoke all the pre-conditions. For any that return true,
|
||||
// set the appropriate bit in `disabled`.
|
||||
$(
|
||||
if !$c {
|
||||
let mask: util::Mask = 1 << $crate::count!( $($skip)* );
|
||||
let mask: __tokio_select_util::Mask = 1 << $crate::count!( $($skip)* );
|
||||
disabled |= mask;
|
||||
}
|
||||
)*
|
||||
|
@ -458,8 +467,18 @@ macro_rules! select {
|
|||
let mut output = {
|
||||
// Safety: Nothing must be moved out of `futures`. This is to
|
||||
// satisfy the requirement of `Pin::new_unchecked` called below.
|
||||
//
|
||||
// We can't use the `pin!` macro for this because `futures` is a
|
||||
// tuple and the standard library provides no way to pin-project to
|
||||
// the fields of a tuple.
|
||||
let mut futures = ( $( $fut , )+ );
|
||||
|
||||
// This assignment makes sure that the `poll_fn` closure only has a
|
||||
// reference to the futures, instead of taking ownership of them.
|
||||
// This mitigates the issue described in
|
||||
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
|
||||
let mut futures = &mut futures;
|
||||
|
||||
$crate::macros::support::poll_fn(|cx| {
|
||||
// Track if any branch returns pending. If no branch completes
|
||||
// **or** returns pending, this implies that all branches are
|
||||
|
@ -495,7 +514,7 @@ macro_rules! select {
|
|||
|
||||
// Extract the future for this branch from the
|
||||
// tuple
|
||||
let ( $($skip,)* fut, .. ) = &mut futures;
|
||||
let ( $($skip,)* fut, .. ) = &mut *futures;
|
||||
|
||||
// Safety: future is stored on the stack above
|
||||
// and never moved.
|
||||
|
@ -525,7 +544,7 @@ macro_rules! select {
|
|||
}
|
||||
|
||||
// The select is complete, return the value
|
||||
return Ready($crate::select_variant!(util::Out, ($($skip)*))(out));
|
||||
return Ready($crate::select_variant!(__tokio_select_util::Out, ($($skip)*))(out));
|
||||
}
|
||||
)*
|
||||
_ => unreachable!("reaching this means there probably is an off by one bug"),
|
||||
|
@ -536,16 +555,16 @@ macro_rules! select {
|
|||
Pending
|
||||
} else {
|
||||
// All branches have been disabled.
|
||||
Ready(util::Out::Disabled)
|
||||
Ready(__tokio_select_util::Out::Disabled)
|
||||
}
|
||||
}).await
|
||||
};
|
||||
|
||||
match output {
|
||||
$(
|
||||
$crate::select_variant!(util::Out, ($($skip)*) ($bind)) => $handle,
|
||||
$crate::select_variant!(__tokio_select_util::Out, ($($skip)*) ($bind)) => $handle,
|
||||
)*
|
||||
util::Out::Disabled => $else,
|
||||
__tokio_select_util::Out::Disabled => $else,
|
||||
_ => unreachable!("failed to match bind"),
|
||||
}
|
||||
}};
|
||||
|
@ -801,6 +820,9 @@ macro_rules! count {
|
|||
(_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => {
|
||||
63
|
||||
};
|
||||
(_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => {
|
||||
64
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
cfg_macros! {
|
||||
pub use crate::future::poll_fn;
|
||||
pub use crate::future::maybe_done::maybe_done;
|
||||
pub use crate::util::thread_rng_n;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn thread_rng_n(n: u32) -> u32 {
|
||||
crate::runtime::context::thread_rng_n(n)
|
||||
}
|
||||
}
|
||||
|
||||
pub use std::future::Future;
|
||||
|
|
|
@ -1,4 +1,32 @@
|
|||
#[cfg(all(loom, test))]
|
||||
macro_rules! thread_local {
|
||||
macro_rules! tokio_thread_local {
|
||||
($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty = const { $expr:expr } $(;)?) => {
|
||||
loom::thread_local! {
|
||||
$(#[$attrs])*
|
||||
$vis static $name: $ty = $expr;
|
||||
}
|
||||
};
|
||||
|
||||
($($tts:tt)+) => { loom::thread_local!{ $($tts)+ } }
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_const_thread_local))]
|
||||
#[cfg(not(all(loom, test)))]
|
||||
macro_rules! tokio_thread_local {
|
||||
($($tts:tt)+) => {
|
||||
::std::thread_local!{ $($tts)+ }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(tokio_no_const_thread_local)]
|
||||
#[cfg(not(all(loom, test)))]
|
||||
macro_rules! tokio_thread_local {
|
||||
($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty = const { $expr:expr } $(;)?) => {
|
||||
::std::thread_local! {
|
||||
$(#[$attrs])*
|
||||
$vis static $name: $ty = $expr;
|
||||
}
|
||||
};
|
||||
|
||||
($($tts:tt)+) => { ::std::thread_local!{ $($tts)+ } }
|
||||
}
|
||||
|
|
|
@ -106,6 +106,9 @@ macro_rules! try_join {
|
|||
// normalization is complete.
|
||||
( $($count:tt)* )
|
||||
|
||||
// The expression `0+1+1+ ... +1` equal to the number of branches.
|
||||
( $($total:tt)* )
|
||||
|
||||
// Normalized try_join! branches
|
||||
$( ( $($skip:tt)* ) $e:expr, )*
|
||||
|
||||
|
@ -115,26 +118,68 @@ macro_rules! try_join {
|
|||
|
||||
// Safety: nothing must be moved out of `futures`. This is to satisfy
|
||||
// the requirement of `Pin::new_unchecked` called below.
|
||||
//
|
||||
// We can't use the `pin!` macro for this because `futures` is a tuple
|
||||
// and the standard library provides no way to pin-project to the fields
|
||||
// of a tuple.
|
||||
let mut futures = ( $( maybe_done($e), )* );
|
||||
|
||||
// This assignment makes sure that the `poll_fn` closure only has a
|
||||
// reference to the futures, instead of taking ownership of them. This
|
||||
// mitigates the issue described in
|
||||
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
|
||||
let mut futures = &mut futures;
|
||||
|
||||
// Each time the future created by poll_fn is polled, a different future will be polled first
|
||||
// to ensure every future passed to join! gets a chance to make progress even if
|
||||
// one of the futures consumes the whole budget.
|
||||
//
|
||||
// This is number of futures that will be skipped in the first loop
|
||||
// iteration the next time.
|
||||
let mut skip_next_time: u32 = 0;
|
||||
|
||||
poll_fn(move |cx| {
|
||||
const COUNT: u32 = $($total)*;
|
||||
|
||||
let mut is_pending = false;
|
||||
|
||||
let mut to_run = COUNT;
|
||||
|
||||
// The number of futures that will be skipped in the first loop iteration
|
||||
let mut skip = skip_next_time;
|
||||
|
||||
skip_next_time = if skip + 1 == COUNT { 0 } else { skip + 1 };
|
||||
|
||||
// This loop runs twice and the first `skip` futures
|
||||
// are not polled in the first iteration.
|
||||
loop {
|
||||
$(
|
||||
// Extract the future for this branch from the tuple.
|
||||
let ( $($skip,)* fut, .. ) = &mut futures;
|
||||
if skip == 0 {
|
||||
if to_run == 0 {
|
||||
// Every future has been polled
|
||||
break;
|
||||
}
|
||||
to_run -= 1;
|
||||
|
||||
// Safety: future is stored on the stack above
|
||||
// and never moved.
|
||||
let mut fut = unsafe { Pin::new_unchecked(fut) };
|
||||
// Extract the future for this branch from the tuple.
|
||||
let ( $($skip,)* fut, .. ) = &mut *futures;
|
||||
|
||||
// Try polling
|
||||
if fut.as_mut().poll(cx).is_pending() {
|
||||
is_pending = true;
|
||||
} else if fut.as_mut().output_mut().expect("expected completed future").is_err() {
|
||||
return Ready(Err(fut.take_output().expect("expected completed future").err().unwrap()))
|
||||
// Safety: future is stored on the stack above
|
||||
// and never moved.
|
||||
let mut fut = unsafe { Pin::new_unchecked(fut) };
|
||||
|
||||
// Try polling
|
||||
if fut.as_mut().poll(cx).is_pending() {
|
||||
is_pending = true;
|
||||
} else if fut.as_mut().output_mut().expect("expected completed future").is_err() {
|
||||
return Ready(Err(fut.take_output().expect("expected completed future").err().unwrap()))
|
||||
}
|
||||
} else {
|
||||
// Future skipped, one less future to skip in the next iteration
|
||||
skip -= 1;
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
if is_pending {
|
||||
Pending
|
||||
|
@ -159,13 +204,15 @@ macro_rules! try_join {
|
|||
|
||||
// ===== Normalize =====
|
||||
|
||||
(@ { ( $($s:tt)* ) $($t:tt)* } $e:expr, $($r:tt)* ) => {
|
||||
$crate::try_join!(@{ ($($s)* _) $($t)* ($($s)*) $e, } $($r)*)
|
||||
(@ { ( $($s:tt)* ) ( $($n:tt)* ) $($t:tt)* } $e:expr, $($r:tt)* ) => {
|
||||
$crate::try_join!(@{ ($($s)* _) ($($n)* + 1) $($t)* ($($s)*) $e, } $($r)*)
|
||||
};
|
||||
|
||||
// ===== Entry point =====
|
||||
|
||||
( $($e:expr),* $(,)?) => {
|
||||
$crate::try_join!(@{ () } $($e,)*)
|
||||
( $($e:expr),+ $(,)?) => {
|
||||
$crate::try_join!(@{ () (0) } $($e,)*)
|
||||
};
|
||||
|
||||
() => { async { Ok(()) }.await }
|
||||
}
|
||||
|
|
|
@ -136,7 +136,22 @@ impl sealed::ToSocketAddrsPriv for &[SocketAddr] {
|
|||
type Future = ReadyFuture<Self::Iter>;
|
||||
|
||||
fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
|
||||
let iter = self.to_vec().into_iter();
|
||||
#[inline]
|
||||
fn slice_to_vec(addrs: &[SocketAddr]) -> Vec<SocketAddr> {
|
||||
addrs.to_vec()
|
||||
}
|
||||
|
||||
// This uses a helper method because clippy doesn't like the `to_vec()`
|
||||
// call here (it will allocate, whereas `self.iter().copied()` would
|
||||
// not), but it's actually necessary in order to ensure that the
|
||||
// returned iterator is valid for the `'static` lifetime, which the
|
||||
// borrowed `slice::Iter` iterator would not be.
|
||||
//
|
||||
// Note that we can't actually add an `allow` attribute for
|
||||
// `clippy::unnecessary_to_owned` here, as Tokio's CI runs clippy lints
|
||||
// on Rust 1.52 to avoid breaking LTS releases of Tokio. Users of newer
|
||||
// Rust versions who see this lint should just ignore it.
|
||||
let iter = slice_to_vec(self).into_iter();
|
||||
future::ready(Ok(iter))
|
||||
}
|
||||
}
|
||||
|
@ -229,7 +244,7 @@ cfg_net! {
|
|||
type Future = <str as sealed::ToSocketAddrsPriv>::Future;
|
||||
|
||||
fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
|
||||
(&self[..]).to_socket_addrs(sealed::Internal)
|
||||
self[..].to_socket_addrs(sealed::Internal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,10 @@
|
|||
//! [`UnixDatagram`]: UnixDatagram
|
||||
|
||||
mod addr;
|
||||
#[cfg(feature = "net")]
|
||||
pub(crate) use addr::to_socket_addrs;
|
||||
cfg_not_wasi! {
|
||||
#[cfg(feature = "net")]
|
||||
pub(crate) use addr::to_socket_addrs;
|
||||
}
|
||||
pub use addr::ToSocketAddrs;
|
||||
|
||||
cfg_net! {
|
||||
|
@ -33,11 +35,13 @@ cfg_net! {
|
|||
|
||||
pub mod tcp;
|
||||
pub use tcp::listener::TcpListener;
|
||||
pub use tcp::socket::TcpSocket;
|
||||
pub use tcp::stream::TcpStream;
|
||||
cfg_not_wasi! {
|
||||
pub use tcp::socket::TcpSocket;
|
||||
|
||||
mod udp;
|
||||
pub use udp::UdpSocket;
|
||||
mod udp;
|
||||
pub use udp::UdpSocket;
|
||||
}
|
||||
}
|
||||
|
||||
cfg_net_unix! {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use crate::io::{Interest, PollEvented};
|
||||
use crate::net::tcp::TcpStream;
|
||||
use crate::net::{to_socket_addrs, ToSocketAddrs};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
cfg_not_wasi! {
|
||||
use crate::net::{to_socket_addrs, ToSocketAddrs};
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::net::{self, SocketAddr};
|
||||
|
@ -55,68 +57,70 @@ cfg_net! {
|
|||
}
|
||||
|
||||
impl TcpListener {
|
||||
/// Creates a new TcpListener, which will be bound to the specified address.
|
||||
///
|
||||
/// The returned listener is ready for accepting connections.
|
||||
///
|
||||
/// Binding with a port number of 0 will request that the OS assigns a port
|
||||
/// to this listener. The port allocated can be queried via the `local_addr`
|
||||
/// method.
|
||||
///
|
||||
/// The address type can be any implementor of the [`ToSocketAddrs`] trait.
|
||||
/// If `addr` yields multiple addresses, bind will be attempted with each of
|
||||
/// the addresses until one succeeds and returns the listener. If none of
|
||||
/// the addresses succeed in creating a listener, the error returned from
|
||||
/// the last attempt (the last address) is returned.
|
||||
///
|
||||
/// This function sets the `SO_REUSEADDR` option on the socket.
|
||||
///
|
||||
/// To configure the socket before binding, you can use the [`TcpSocket`]
|
||||
/// type.
|
||||
///
|
||||
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
|
||||
/// [`TcpSocket`]: struct@crate::net::TcpSocket
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::TcpListener;
|
||||
///
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let listener = TcpListener::bind("127.0.0.1:2345").await?;
|
||||
///
|
||||
/// // use the listener
|
||||
///
|
||||
/// # let _ = listener;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
|
||||
let addrs = to_socket_addrs(addr).await?;
|
||||
cfg_not_wasi! {
|
||||
/// Creates a new TcpListener, which will be bound to the specified address.
|
||||
///
|
||||
/// The returned listener is ready for accepting connections.
|
||||
///
|
||||
/// Binding with a port number of 0 will request that the OS assigns a port
|
||||
/// to this listener. The port allocated can be queried via the `local_addr`
|
||||
/// method.
|
||||
///
|
||||
/// The address type can be any implementor of the [`ToSocketAddrs`] trait.
|
||||
/// If `addr` yields multiple addresses, bind will be attempted with each of
|
||||
/// the addresses until one succeeds and returns the listener. If none of
|
||||
/// the addresses succeed in creating a listener, the error returned from
|
||||
/// the last attempt (the last address) is returned.
|
||||
///
|
||||
/// This function sets the `SO_REUSEADDR` option on the socket.
|
||||
///
|
||||
/// To configure the socket before binding, you can use the [`TcpSocket`]
|
||||
/// type.
|
||||
///
|
||||
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
|
||||
/// [`TcpSocket`]: struct@crate::net::TcpSocket
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::TcpListener;
|
||||
///
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let listener = TcpListener::bind("127.0.0.1:2345").await?;
|
||||
///
|
||||
/// // use the listener
|
||||
///
|
||||
/// # let _ = listener;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
|
||||
let addrs = to_socket_addrs(addr).await?;
|
||||
|
||||
let mut last_err = None;
|
||||
let mut last_err = None;
|
||||
|
||||
for addr in addrs {
|
||||
match TcpListener::bind_addr(addr) {
|
||||
Ok(listener) => return Ok(listener),
|
||||
Err(e) => last_err = Some(e),
|
||||
for addr in addrs {
|
||||
match TcpListener::bind_addr(addr) {
|
||||
Ok(listener) => return Ok(listener),
|
||||
Err(e) => last_err = Some(e),
|
||||
}
|
||||
}
|
||||
|
||||
Err(last_err.unwrap_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"could not resolve to any address",
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
Err(last_err.unwrap_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"could not resolve to any address",
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
fn bind_addr(addr: SocketAddr) -> io::Result<TcpListener> {
|
||||
let listener = mio::net::TcpListener::bind(addr)?;
|
||||
TcpListener::new(listener)
|
||||
fn bind_addr(addr: SocketAddr) -> io::Result<TcpListener> {
|
||||
let listener = mio::net::TcpListener::bind(addr)?;
|
||||
TcpListener::new(listener)
|
||||
}
|
||||
}
|
||||
|
||||
/// Accepts a new incoming connection from this listener.
|
||||
|
@ -190,15 +194,22 @@ impl TcpListener {
|
|||
/// Creates new `TcpListener` from a `std::net::TcpListener`.
|
||||
///
|
||||
/// This function is intended to be used to wrap a TCP listener from the
|
||||
/// standard library in the Tokio equivalent. The conversion assumes nothing
|
||||
/// about the underlying listener; it is left up to the user to set it in
|
||||
/// non-blocking mode.
|
||||
/// standard library in the Tokio equivalent.
|
||||
///
|
||||
/// This API is typically paired with the `socket2` crate and the `Socket`
|
||||
/// type to build up and customize a listener before it's shipped off to the
|
||||
/// backing event loop. This allows configuration of options like
|
||||
/// `SO_REUSEPORT`, binding to multiple addresses, etc.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The caller is responsible for ensuring that the listener is in
|
||||
/// non-blocking mode. Otherwise all I/O operations on the listener
|
||||
/// will block the thread, which will cause unexpected behavior.
|
||||
/// Non-blocking mode can be set using [`set_nonblocking`].
|
||||
///
|
||||
/// [`set_nonblocking`]: std::net::TcpListener::set_nonblocking
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
|
@ -216,11 +227,13 @@ impl TcpListener {
|
|||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
/// This function panics if it is not called from within a runtime with
|
||||
/// IO enabled.
|
||||
///
|
||||
/// The runtime is usually set implicitly when this function is called
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||
#[track_caller]
|
||||
pub fn from_std(listener: net::TcpListener) -> io::Result<TcpListener> {
|
||||
let io = mio::net::TcpListener::from_std(listener);
|
||||
let io = PollEvented::new(io)?;
|
||||
|
@ -267,11 +280,22 @@ impl TcpListener {
|
|||
.map(|io| io.into_raw_socket())
|
||||
.map(|raw_socket| unsafe { std::net::TcpListener::from_raw_socket(raw_socket) })
|
||||
}
|
||||
|
||||
#[cfg(tokio_wasi)]
|
||||
{
|
||||
use std::os::wasi::io::{FromRawFd, IntoRawFd};
|
||||
self.io
|
||||
.into_inner()
|
||||
.map(|io| io.into_raw_fd())
|
||||
.map(|raw_fd| unsafe { std::net::TcpListener::from_raw_fd(raw_fd) })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(listener: mio::net::TcpListener) -> io::Result<TcpListener> {
|
||||
let io = PollEvented::new(listener)?;
|
||||
Ok(TcpListener { io })
|
||||
cfg_not_wasi! {
|
||||
pub(crate) fn new(listener: mio::net::TcpListener) -> io::Result<TcpListener> {
|
||||
let io = PollEvented::new(listener)?;
|
||||
Ok(TcpListener { io })
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the local address that this listener is bound to.
|
||||
|
@ -382,16 +406,51 @@ mod sys {
|
|||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for TcpListener {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod sys {
|
||||
use super::TcpListener;
|
||||
use std::os::windows::prelude::*;
|
||||
cfg_unstable! {
|
||||
#[cfg(tokio_wasi)]
|
||||
mod sys {
|
||||
use super::TcpListener;
|
||||
use std::os::wasi::prelude::*;
|
||||
|
||||
impl AsRawFd for TcpListener {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for TcpListener {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg_windows! {
|
||||
use crate::os::windows::io::{AsRawSocket, RawSocket};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsSocket, BorrowedSocket};
|
||||
|
||||
impl AsRawSocket for TcpListener {
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.io.as_raw_socket()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsSocket for TcpListener {
|
||||
fn as_socket(&self) -> BorrowedSocket<'_> {
|
||||
unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
pub(crate) mod listener;
|
||||
|
||||
pub(crate) mod socket;
|
||||
cfg_not_wasi! {
|
||||
pub(crate) mod socket;
|
||||
}
|
||||
|
||||
mod split;
|
||||
pub use split::{ReadHalf, WriteHalf};
|
||||
|
|
|
@ -4,12 +4,18 @@ use std::fmt;
|
|||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[cfg(all(unix, not(tokio_no_as_fd)))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
|
||||
use std::time::Duration;
|
||||
|
||||
cfg_windows! {
|
||||
use crate::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsSocket, BorrowedSocket};
|
||||
}
|
||||
|
||||
cfg_net! {
|
||||
/// A TCP socket that has not yet been converted to a `TcpStream` or
|
||||
/// `TcpListener`.
|
||||
|
@ -398,6 +404,134 @@ impl TcpSocket {
|
|||
self.inner.linger()
|
||||
}
|
||||
|
||||
/// Sets the value of the `TCP_NODELAY` option on this socket.
|
||||
///
|
||||
/// If set, this option disables the Nagle algorithm. This means that segments are always
|
||||
/// sent as soon as possible, even if there is only a small amount of data. When not set,
|
||||
/// data is buffered until there is a sufficient amount to send out, thereby avoiding
|
||||
/// the frequent sending of small packets.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::TcpSocket;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let socket = TcpSocket::new_v4()?;
|
||||
///
|
||||
/// println!("{:?}", socket.nodelay()?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
|
||||
self.inner.set_nodelay(nodelay)
|
||||
}
|
||||
|
||||
/// Gets the value of the `TCP_NODELAY` option on this socket.
|
||||
///
|
||||
/// For more information about this option, see [`set_nodelay`].
|
||||
///
|
||||
/// [`set_nodelay`]: TcpSocket::set_nodelay
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::TcpSocket;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let stream = TcpSocket::new_v4()?;
|
||||
///
|
||||
/// stream.set_nodelay(true)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn nodelay(&self) -> io::Result<bool> {
|
||||
self.inner.nodelay()
|
||||
}
|
||||
|
||||
/// Gets the value of the `IP_TOS` option for this socket.
|
||||
///
|
||||
/// For more information about this option, see [`set_tos`].
|
||||
///
|
||||
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
|
||||
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
|
||||
///
|
||||
/// [`set_tos`]: Self::set_tos
|
||||
// https://docs.rs/socket2/0.4.2/src/socket2/socket.rs.html#1178
|
||||
#[cfg(not(any(
|
||||
target_os = "fuchsia",
|
||||
target_os = "redox",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
)))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(not(any(
|
||||
target_os = "fuchsia",
|
||||
target_os = "redox",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
))))
|
||||
)]
|
||||
pub fn tos(&self) -> io::Result<u32> {
|
||||
self.inner.tos()
|
||||
}
|
||||
|
||||
/// Sets the value for the `IP_TOS` option on this socket.
|
||||
///
|
||||
/// This value sets the type-of-service field that is used in every packet
|
||||
/// sent from this socket.
|
||||
///
|
||||
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
|
||||
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
|
||||
// https://docs.rs/socket2/0.4.2/src/socket2/socket.rs.html#1178
|
||||
#[cfg(not(any(
|
||||
target_os = "fuchsia",
|
||||
target_os = "redox",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
)))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(not(any(
|
||||
target_os = "fuchsia",
|
||||
target_os = "redox",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
))))
|
||||
)]
|
||||
pub fn set_tos(&self, tos: u32) -> io::Result<()> {
|
||||
self.inner.set_tos(tos)
|
||||
}
|
||||
|
||||
/// Gets the value for the `SO_BINDTODEVICE` option on this socket
|
||||
///
|
||||
/// This value gets the socket binded device's interface name.
|
||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux",))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux",)))
|
||||
)]
|
||||
pub fn device(&self) -> io::Result<Option<Vec<u8>>> {
|
||||
self.inner.device()
|
||||
}
|
||||
|
||||
/// Sets the value for the `SO_BINDTODEVICE` option on this socket
|
||||
///
|
||||
/// If a socket is bound to an interface, only packets received from that
|
||||
/// particular interface are processed by the socket. Note that this only
|
||||
/// works for some socket types, particularly `AF_INET` sockets.
|
||||
///
|
||||
/// If `interface` is `None` or an empty string it removes the binding.
|
||||
#[cfg(all(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(all(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))))
|
||||
)]
|
||||
pub fn bind_device(&self, interface: Option<&[u8]>) -> io::Result<()> {
|
||||
self.inner.bind_device(interface)
|
||||
}
|
||||
|
||||
/// Gets the local address of this socket.
|
||||
///
|
||||
/// Will fail on windows if called before `bind`.
|
||||
|
@ -424,6 +558,11 @@ impl TcpSocket {
|
|||
self.inner.local_addr().and_then(convert_address)
|
||||
}
|
||||
|
||||
/// Returns the value of the `SO_ERROR` option.
|
||||
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
|
||||
self.inner.take_error()
|
||||
}
|
||||
|
||||
/// Binds the socket to the given address.
|
||||
///
|
||||
/// This calls the `bind(2)` operating-system function. Behavior is
|
||||
|
@ -582,6 +721,15 @@ impl TcpSocket {
|
|||
/// [`std::net::TcpStream`]: struct@std::net::TcpStream
|
||||
/// [`socket2`]: https://docs.rs/socket2/
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The caller is responsible for ensuring that the socket is in
|
||||
/// non-blocking mode. Otherwise all I/O operations on the socket
|
||||
/// will block the thread, which will cause unexpected behavior.
|
||||
/// Non-blocking mode can be set using [`set_nonblocking`].
|
||||
///
|
||||
/// [`set_nonblocking`]: std::net::TcpStream::set_nonblocking
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
|
@ -590,8 +738,8 @@ impl TcpSocket {
|
|||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> std::io::Result<()> {
|
||||
///
|
||||
/// let socket2_socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;
|
||||
/// socket2_socket.set_nonblocking(true)?;
|
||||
///
|
||||
/// let socket = TcpSocket::from_std_stream(socket2_socket.into());
|
||||
///
|
||||
|
@ -640,6 +788,13 @@ impl AsRawFd for TcpSocket {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(tokio_no_as_fd)))]
|
||||
impl AsFd for TcpSocket {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl FromRawFd for TcpSocket {
|
||||
/// Converts a `RawFd` to a `TcpSocket`.
|
||||
|
@ -661,30 +816,36 @@ impl IntoRawFd for TcpSocket {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl IntoRawSocket for TcpSocket {
|
||||
fn into_raw_socket(self) -> RawSocket {
|
||||
self.inner.into_raw_socket()
|
||||
cfg_windows! {
|
||||
impl IntoRawSocket for TcpSocket {
|
||||
fn into_raw_socket(self) -> RawSocket {
|
||||
self.inner.into_raw_socket()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl AsRawSocket for TcpSocket {
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.inner.as_raw_socket()
|
||||
impl AsRawSocket for TcpSocket {
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.inner.as_raw_socket()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl FromRawSocket for TcpSocket {
|
||||
/// Converts a `RawSocket` to a `TcpStream`.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The caller is responsible for ensuring that the socket is in
|
||||
/// non-blocking mode.
|
||||
unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket {
|
||||
let inner = socket2::Socket::from_raw_socket(socket);
|
||||
TcpSocket { inner }
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsSocket for TcpSocket {
|
||||
fn as_socket(&self) -> BorrowedSocket<'_> {
|
||||
unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawSocket for TcpSocket {
|
||||
/// Converts a `RawSocket` to a `TcpStream`.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The caller is responsible for ensuring that the socket is in
|
||||
/// non-blocking mode.
|
||||
unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket {
|
||||
let inner = socket2::Socket::from_raw_socket(socket);
|
||||
TcpSocket { inner }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,12 +141,21 @@ impl ReadHalf<'_> {
|
|||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_read()`]. It can be used instead
|
||||
/// of [`readable()`] to check the returned ready set for [`Ready::READABLE`]
|
||||
/// and [`Ready::READ_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read
|
||||
/// [`readable()`]: Self::readable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
|
@ -190,8 +199,12 @@ impl ReadHalf<'_> {
|
|||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
|
||||
///
|
||||
/// 1. The stream's read half is closed and will no longer yield data.
|
||||
/// 2. The specified buffer was 0 bytes in length.
|
||||
///
|
||||
/// If the stream is not ready to read data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.try_read(buf)
|
||||
|
@ -265,12 +278,21 @@ impl ReadHalf<'_> {
|
|||
impl WriteHalf<'_> {
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_write()`]. It can be used instead
|
||||
/// of [`writable()`] to check the returned ready set for [`Ready::WRITABLE`]
|
||||
/// and [`Ready::WRITE_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write
|
||||
/// [`writable()`]: Self::writable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
|
|
|
@ -196,12 +196,21 @@ impl OwnedReadHalf {
|
|||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_read()`]. It can be used instead
|
||||
/// of [`readable()`] to check the returned ready set for [`Ready::READABLE`]
|
||||
/// and [`Ready::READ_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read
|
||||
/// [`readable()`]: Self::readable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
|
@ -245,8 +254,12 @@ impl OwnedReadHalf {
|
|||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
|
||||
///
|
||||
/// 1. The stream's read half is closed and will no longer yield data.
|
||||
/// 2. The specified buffer was 0 bytes in length.
|
||||
///
|
||||
/// If the stream is not ready to read data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.try_read(buf)
|
||||
|
@ -347,12 +360,21 @@ impl OwnedWriteHalf {
|
|||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_write()`]. It can be used instead
|
||||
/// of [`writable()`] to check the returned ready set for [`Ready::WRITABLE`]
|
||||
/// and [`Ready::WRITE_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write
|
||||
/// [`writable()`]: Self::writable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
|
@ -474,12 +496,12 @@ impl AsyncWrite for OwnedWriteHalf {
|
|||
|
||||
impl AsRef<TcpStream> for OwnedReadHalf {
|
||||
fn as_ref(&self) -> &TcpStream {
|
||||
&*self.inner
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<TcpStream> for OwnedWriteHalf {
|
||||
fn as_ref(&self) -> &TcpStream {
|
||||
&*self.inner
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
use crate::future::poll_fn;
|
||||
cfg_not_wasi! {
|
||||
use crate::future::poll_fn;
|
||||
use crate::net::{to_socket_addrs, ToSocketAddrs};
|
||||
use std::time::Duration;
|
||||
}
|
||||
|
||||
use crate::io::{AsyncRead, AsyncWrite, Interest, PollEvented, ReadBuf, Ready};
|
||||
use crate::net::tcp::split::{split, ReadHalf, WriteHalf};
|
||||
use crate::net::tcp::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf};
|
||||
use crate::net::{to_socket_addrs, ToSocketAddrs};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::net::{Shutdown, SocketAddr};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
|
||||
cfg_io_util! {
|
||||
use bytes::BufMut;
|
||||
|
@ -70,86 +72,88 @@ cfg_net! {
|
|||
}
|
||||
|
||||
impl TcpStream {
|
||||
/// Opens a TCP connection to a remote host.
|
||||
///
|
||||
/// `addr` is an address of the remote host. Anything which implements the
|
||||
/// [`ToSocketAddrs`] trait can be supplied as the address. If `addr`
|
||||
/// yields multiple addresses, connect will be attempted with each of the
|
||||
/// addresses until a connection is successful. If none of the addresses
|
||||
/// result in a successful connection, the error returned from the last
|
||||
/// connection attempt (the last address) is returned.
|
||||
///
|
||||
/// To configure the socket before connecting, you can use the [`TcpSocket`]
|
||||
/// type.
|
||||
///
|
||||
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
|
||||
/// [`TcpSocket`]: struct@crate::net::TcpSocket
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::TcpStream;
|
||||
/// use tokio::io::AsyncWriteExt;
|
||||
/// use std::error::Error;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // Connect to a peer
|
||||
/// let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
|
||||
///
|
||||
/// // Write some data.
|
||||
/// stream.write_all(b"hello world!").await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
|
||||
///
|
||||
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
|
||||
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
|
||||
pub async fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
|
||||
let addrs = to_socket_addrs(addr).await?;
|
||||
cfg_not_wasi! {
|
||||
/// Opens a TCP connection to a remote host.
|
||||
///
|
||||
/// `addr` is an address of the remote host. Anything which implements the
|
||||
/// [`ToSocketAddrs`] trait can be supplied as the address. If `addr`
|
||||
/// yields multiple addresses, connect will be attempted with each of the
|
||||
/// addresses until a connection is successful. If none of the addresses
|
||||
/// result in a successful connection, the error returned from the last
|
||||
/// connection attempt (the last address) is returned.
|
||||
///
|
||||
/// To configure the socket before connecting, you can use the [`TcpSocket`]
|
||||
/// type.
|
||||
///
|
||||
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
|
||||
/// [`TcpSocket`]: struct@crate::net::TcpSocket
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::TcpStream;
|
||||
/// use tokio::io::AsyncWriteExt;
|
||||
/// use std::error::Error;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // Connect to a peer
|
||||
/// let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
|
||||
///
|
||||
/// // Write some data.
|
||||
/// stream.write_all(b"hello world!").await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
|
||||
///
|
||||
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
|
||||
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
|
||||
pub async fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
|
||||
let addrs = to_socket_addrs(addr).await?;
|
||||
|
||||
let mut last_err = None;
|
||||
let mut last_err = None;
|
||||
|
||||
for addr in addrs {
|
||||
match TcpStream::connect_addr(addr).await {
|
||||
Ok(stream) => return Ok(stream),
|
||||
Err(e) => last_err = Some(e),
|
||||
for addr in addrs {
|
||||
match TcpStream::connect_addr(addr).await {
|
||||
Ok(stream) => return Ok(stream),
|
||||
Err(e) => last_err = Some(e),
|
||||
}
|
||||
}
|
||||
|
||||
Err(last_err.unwrap_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"could not resolve to any address",
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
Err(last_err.unwrap_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"could not resolve to any address",
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Establishes a connection to the specified `addr`.
|
||||
async fn connect_addr(addr: SocketAddr) -> io::Result<TcpStream> {
|
||||
let sys = mio::net::TcpStream::connect(addr)?;
|
||||
TcpStream::connect_mio(sys).await
|
||||
}
|
||||
|
||||
pub(crate) async fn connect_mio(sys: mio::net::TcpStream) -> io::Result<TcpStream> {
|
||||
let stream = TcpStream::new(sys)?;
|
||||
|
||||
// Once we've connected, wait for the stream to be writable as
|
||||
// that's when the actual connection has been initiated. Once we're
|
||||
// writable we check for `take_socket_error` to see if the connect
|
||||
// actually hit an error or not.
|
||||
//
|
||||
// If all that succeeded then we ship everything on up.
|
||||
poll_fn(|cx| stream.io.registration().poll_write_ready(cx)).await?;
|
||||
|
||||
if let Some(e) = stream.io.take_error()? {
|
||||
return Err(e);
|
||||
/// Establishes a connection to the specified `addr`.
|
||||
async fn connect_addr(addr: SocketAddr) -> io::Result<TcpStream> {
|
||||
let sys = mio::net::TcpStream::connect(addr)?;
|
||||
TcpStream::connect_mio(sys).await
|
||||
}
|
||||
|
||||
Ok(stream)
|
||||
pub(crate) async fn connect_mio(sys: mio::net::TcpStream) -> io::Result<TcpStream> {
|
||||
let stream = TcpStream::new(sys)?;
|
||||
|
||||
// Once we've connected, wait for the stream to be writable as
|
||||
// that's when the actual connection has been initiated. Once we're
|
||||
// writable we check for `take_socket_error` to see if the connect
|
||||
// actually hit an error or not.
|
||||
//
|
||||
// If all that succeeded then we ship everything on up.
|
||||
poll_fn(|cx| stream.io.registration().poll_write_ready(cx)).await?;
|
||||
|
||||
if let Some(e) = stream.io.take_error()? {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
Ok(stream)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(connected: mio::net::TcpStream) -> io::Result<TcpStream> {
|
||||
|
@ -160,9 +164,16 @@ impl TcpStream {
|
|||
/// Creates new `TcpStream` from a `std::net::TcpStream`.
|
||||
///
|
||||
/// This function is intended to be used to wrap a TCP stream from the
|
||||
/// standard library in the Tokio equivalent. The conversion assumes nothing
|
||||
/// about the underlying stream; it is left up to the user to set it in
|
||||
/// non-blocking mode.
|
||||
/// standard library in the Tokio equivalent.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The caller is responsible for ensuring that the stream is in
|
||||
/// non-blocking mode. Otherwise all I/O operations on the stream
|
||||
/// will block the thread, which will cause unexpected behavior.
|
||||
/// Non-blocking mode can be set using [`set_nonblocking`].
|
||||
///
|
||||
/// [`set_nonblocking`]: std::net::TcpStream::set_nonblocking
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -181,11 +192,13 @@ impl TcpStream {
|
|||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
/// This function panics if it is not called from within a runtime with
|
||||
/// IO enabled.
|
||||
///
|
||||
/// The runtime is usually set implicitly when this function is called
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||
#[track_caller]
|
||||
pub fn from_std(stream: std::net::TcpStream) -> io::Result<TcpStream> {
|
||||
let io = mio::net::TcpStream::from_std(stream);
|
||||
let io = PollEvented::new(io)?;
|
||||
|
@ -244,6 +257,15 @@ impl TcpStream {
|
|||
.map(|io| io.into_raw_socket())
|
||||
.map(|raw_socket| unsafe { std::net::TcpStream::from_raw_socket(raw_socket) })
|
||||
}
|
||||
|
||||
#[cfg(tokio_wasi)]
|
||||
{
|
||||
use std::os::wasi::io::{FromRawFd, IntoRawFd};
|
||||
self.io
|
||||
.into_inner()
|
||||
.map(|io| io.into_raw_fd())
|
||||
.map(|raw_fd| unsafe { std::net::TcpStream::from_raw_fd(raw_fd) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the local address that this stream is bound to.
|
||||
|
@ -264,6 +286,11 @@ impl TcpStream {
|
|||
self.io.local_addr()
|
||||
}
|
||||
|
||||
/// Returns the value of the `SO_ERROR` option.
|
||||
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
|
||||
self.io.take_error()
|
||||
}
|
||||
|
||||
/// Returns the remote address that this stream is connected to.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -356,6 +383,12 @@ impl TcpStream {
|
|||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
|
@ -526,8 +559,12 @@ impl TcpStream {
|
|||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
|
||||
///
|
||||
/// 1. The stream's read half is closed and will no longer yield data.
|
||||
/// 2. The specified buffer was 0 bytes in length.
|
||||
///
|
||||
/// If the stream is not ready to read data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -939,7 +976,7 @@ impl TcpStream {
|
|||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
/// should attempt to perform IO operation on the socket by manually
|
||||
/// calling the appropriate syscall. If the operation fails because the
|
||||
/// socket is not actually ready, then the closure should return a
|
||||
/// `WouldBlock` error and the readiness flag is cleared. The return value
|
||||
|
@ -958,6 +995,11 @@ impl TcpStream {
|
|||
/// defined on the Tokio `TcpStream` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
///
|
||||
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: TcpStream::readable()
|
||||
|
@ -968,7 +1010,45 @@ impl TcpStream {
|
|||
interest: Interest,
|
||||
f: impl FnOnce() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io.registration().try_io(interest, f)
|
||||
self.io
|
||||
.registration()
|
||||
.try_io(interest, || self.io.try_io(f))
|
||||
}
|
||||
|
||||
/// Reads or writes from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// The readiness of the socket is awaited and when the socket is ready,
|
||||
/// the provided closure is called. The closure should attempt to perform
|
||||
/// IO operation on the socket by manually calling the appropriate syscall.
|
||||
/// If the operation fails because the socket is not actually ready,
|
||||
/// then the closure should return a `WouldBlock` error. In such case the
|
||||
/// readiness flag is cleared and the socket readiness is awaited again.
|
||||
/// This loop is repeated until the closure returns an `Ok` or an error
|
||||
/// other than `WouldBlock`.
|
||||
///
|
||||
/// The closure should only return a `WouldBlock` error if it has performed
|
||||
/// an IO operation on the socket that failed due to the socket not being
|
||||
/// ready. Returning a `WouldBlock` error in any other situation will
|
||||
/// incorrectly clear the readiness flag, which can cause the socket to
|
||||
/// behave incorrectly.
|
||||
///
|
||||
/// The closure should not perform the IO operation using any of the methods
|
||||
/// defined on the Tokio `TcpStream` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
pub async fn async_io<R>(
|
||||
&self,
|
||||
interest: Interest,
|
||||
mut f: impl FnMut() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io
|
||||
.registration()
|
||||
.async_io(interest, || self.io.try_io(&mut f))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Receives data on the socket from the remote address to which it is
|
||||
|
@ -1070,52 +1150,54 @@ impl TcpStream {
|
|||
self.io.set_nodelay(nodelay)
|
||||
}
|
||||
|
||||
/// Reads the linger duration for this socket by getting the `SO_LINGER`
|
||||
/// option.
|
||||
///
|
||||
/// For more information about this option, see [`set_linger`].
|
||||
///
|
||||
/// [`set_linger`]: TcpStream::set_linger
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::TcpStream;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
|
||||
///
|
||||
/// println!("{:?}", stream.linger()?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn linger(&self) -> io::Result<Option<Duration>> {
|
||||
socket2::SockRef::from(self).linger()
|
||||
}
|
||||
cfg_not_wasi! {
|
||||
/// Reads the linger duration for this socket by getting the `SO_LINGER`
|
||||
/// option.
|
||||
///
|
||||
/// For more information about this option, see [`set_linger`].
|
||||
///
|
||||
/// [`set_linger`]: TcpStream::set_linger
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::TcpStream;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
|
||||
///
|
||||
/// println!("{:?}", stream.linger()?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn linger(&self) -> io::Result<Option<Duration>> {
|
||||
socket2::SockRef::from(self).linger()
|
||||
}
|
||||
|
||||
/// Sets the linger duration of this socket by setting the SO_LINGER option.
|
||||
///
|
||||
/// This option controls the action taken when a stream has unsent messages and the stream is
|
||||
/// closed. If SO_LINGER is set, the system shall block the process until it can transmit the
|
||||
/// data or until the time expires.
|
||||
///
|
||||
/// If SO_LINGER is not specified, and the stream is closed, the system handles the call in a
|
||||
/// way that allows the process to continue as quickly as possible.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::TcpStream;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
|
||||
///
|
||||
/// stream.set_linger(None)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
|
||||
socket2::SockRef::from(self).set_linger(dur)
|
||||
/// Sets the linger duration of this socket by setting the SO_LINGER option.
|
||||
///
|
||||
/// This option controls the action taken when a stream has unsent messages and the stream is
|
||||
/// closed. If SO_LINGER is set, the system shall block the process until it can transmit the
|
||||
/// data or until the time expires.
|
||||
///
|
||||
/// If SO_LINGER is not specified, and the stream is closed, the system handles the call in a
|
||||
/// way that allows the process to continue as quickly as possible.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::TcpStream;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
|
||||
///
|
||||
/// stream.set_linger(None)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
|
||||
socket2::SockRef::from(self).set_linger(dur)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the value of the `IP_TTL` option for this socket.
|
||||
|
@ -1295,16 +1377,49 @@ mod sys {
|
|||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for TcpStream {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod sys {
|
||||
use super::TcpStream;
|
||||
use std::os::windows::prelude::*;
|
||||
cfg_windows! {
|
||||
use crate::os::windows::io::{AsRawSocket, RawSocket};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsSocket, BorrowedSocket};
|
||||
|
||||
impl AsRawSocket for TcpStream {
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.io.as_raw_socket()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsSocket for TcpStream {
|
||||
fn as_socket(&self) -> BorrowedSocket<'_> {
|
||||
unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(tokio_unstable, tokio_wasi))]
|
||||
mod sys {
|
||||
use super::TcpStream;
|
||||
use std::os::wasi::prelude::*;
|
||||
|
||||
impl AsRawFd for TcpStream {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for TcpStream {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::io::{Interest, PollEvented, ReadBuf, Ready};
|
||||
use crate::net::{to_socket_addrs, ToSocketAddrs};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::net::{self, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
|
@ -170,6 +169,7 @@ impl UdpSocket {
|
|||
UdpSocket::new(sys)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn new(socket: mio::net::UdpSocket) -> io::Result<UdpSocket> {
|
||||
let io = PollEvented::new(socket)?;
|
||||
Ok(UdpSocket { io })
|
||||
|
@ -178,14 +178,21 @@ impl UdpSocket {
|
|||
/// Creates new `UdpSocket` from a previously bound `std::net::UdpSocket`.
|
||||
///
|
||||
/// This function is intended to be used to wrap a UDP socket from the
|
||||
/// standard library in the Tokio equivalent. The conversion assumes nothing
|
||||
/// about the underlying socket; it is left up to the user to set it in
|
||||
/// non-blocking mode.
|
||||
/// standard library in the Tokio equivalent.
|
||||
///
|
||||
/// This can be used in conjunction with socket2's `Socket` interface to
|
||||
/// configure a socket before it's handed off, such as setting options like
|
||||
/// `reuse_address` or binding to multiple addresses.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The caller is responsible for ensuring that the socket is in
|
||||
/// non-blocking mode. Otherwise all I/O operations on the socket
|
||||
/// will block the thread, which will cause unexpected behavior.
|
||||
/// Non-blocking mode can be set using [`set_nonblocking`].
|
||||
///
|
||||
/// [`set_nonblocking`]: std::net::UdpSocket::set_nonblocking
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
|
@ -210,6 +217,7 @@ impl UdpSocket {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn from_std(socket: net::UdpSocket) -> io::Result<UdpSocket> {
|
||||
let io = mio::net::UdpSocket::from_std(socket);
|
||||
UdpSocket::new(io)
|
||||
|
@ -257,6 +265,10 @@ impl UdpSocket {
|
|||
}
|
||||
}
|
||||
|
||||
fn as_socket(&self) -> socket2::SockRef<'_> {
|
||||
socket2::SockRef::from(self)
|
||||
}
|
||||
|
||||
/// Returns the local address that this socket is bound to.
|
||||
///
|
||||
/// # Example
|
||||
|
@ -278,6 +290,28 @@ impl UdpSocket {
|
|||
self.io.local_addr()
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote peer this socket was connected to.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::net::UdpSocket;
|
||||
///
|
||||
/// # use std::{io, net::SocketAddr};
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> io::Result<()> {
|
||||
/// let addr = "0.0.0.0:8080".parse::<SocketAddr>().unwrap();
|
||||
/// let peer = "127.0.0.1:11100".parse::<SocketAddr>().unwrap();
|
||||
/// let sock = UdpSocket::bind(addr).await?;
|
||||
/// sock.connect(peer).await?;
|
||||
/// assert_eq!(peer, sock.peer_addr()?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.io.peer_addr()
|
||||
}
|
||||
|
||||
/// Connects the UDP socket setting the default destination for send() and
|
||||
/// limiting packets that are read via recv from the address specified in
|
||||
/// `addr`.
|
||||
|
@ -329,7 +363,9 @@ impl UdpSocket {
|
|||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`.
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
|
@ -712,7 +748,7 @@ impl UdpSocket {
|
|||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If `recv_from` is used as the event in a
|
||||
/// This method is cancel safe. If `recv` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// completes first, it is guaranteed that no messages were received on this
|
||||
/// socket.
|
||||
|
@ -789,7 +825,7 @@ impl UdpSocket {
|
|||
/// address to which it is connected. On success, returns the number of
|
||||
/// bytes read.
|
||||
///
|
||||
/// The function must be called with valid byte array buf of sufficient size
|
||||
/// This method must be called with valid byte array buf of sufficient size
|
||||
/// to hold the message bytes. If a message is too long to fit in the
|
||||
/// supplied buffer, excess bytes may be discarded.
|
||||
///
|
||||
|
@ -845,10 +881,12 @@ impl UdpSocket {
|
|||
/// Tries to receive data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// The function must be called with valid byte array buf of sufficient size
|
||||
/// This method must be called with valid byte array buf of sufficient size
|
||||
/// to hold the message bytes. If a message is too long to fit in the
|
||||
/// supplied buffer, excess bytes may be discarded.
|
||||
///
|
||||
/// This method can be used even if `buf` is uninitialized.
|
||||
///
|
||||
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
||||
/// returned. This function is usually paired with `readable()`.
|
||||
///
|
||||
|
@ -895,10 +933,10 @@ impl UdpSocket {
|
|||
let dst =
|
||||
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
||||
|
||||
let n = (*self.io).recv(dst)?;
|
||||
|
||||
// Safety: We trust `UdpSocket::recv` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
let n = (&*self.io).recv(dst)?;
|
||||
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
}
|
||||
|
@ -907,16 +945,75 @@ impl UdpSocket {
|
|||
})
|
||||
}
|
||||
|
||||
/// Tries to receive a single datagram message on the socket. On success,
|
||||
/// returns the number of bytes read and the origin.
|
||||
/// Receives a single datagram message on the socket from the remote address
|
||||
/// to which it is connected, advancing the buffer's internal cursor,
|
||||
/// returning how many bytes were read.
|
||||
///
|
||||
/// The function must be called with valid byte array buf of sufficient size
|
||||
/// This method must be called with valid byte array buf of sufficient size
|
||||
/// to hold the message bytes. If a message is too long to fit in the
|
||||
/// supplied buffer, excess bytes may be discarded.
|
||||
///
|
||||
/// This method can be used even if `buf` is uninitialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::UdpSocket;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// // Connect to a peer
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:8080").await?;
|
||||
/// socket.connect("127.0.0.1:8081").await?;
|
||||
///
|
||||
/// let mut buf = Vec::with_capacity(512);
|
||||
/// let len = socket.recv_buf(&mut buf).await?;
|
||||
///
|
||||
/// println!("received {} bytes {:?}", len, &buf[..len]);
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn recv_buf<B: BufMut>(&self, buf: &mut B) -> io::Result<usize> {
|
||||
self.io.registration().async_io(Interest::READABLE, || {
|
||||
let dst = buf.chunk_mut();
|
||||
let dst =
|
||||
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
||||
|
||||
let n = (*self.io).recv(dst)?;
|
||||
|
||||
// Safety: We trust `UdpSocket::recv` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
}
|
||||
|
||||
Ok(n)
|
||||
}).await
|
||||
}
|
||||
|
||||
/// Tries to receive a single datagram message on the socket. On success,
|
||||
/// returns the number of bytes read and the origin.
|
||||
///
|
||||
/// This method must be called with valid byte array buf of sufficient size
|
||||
/// to hold the message bytes. If a message is too long to fit in the
|
||||
/// supplied buffer, excess bytes may be discarded.
|
||||
///
|
||||
/// This method can be used even if `buf` is uninitialized.
|
||||
///
|
||||
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
||||
/// returned. This function is usually paired with `readable()`.
|
||||
///
|
||||
/// # Notes
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
|
@ -959,10 +1056,10 @@ impl UdpSocket {
|
|||
let dst =
|
||||
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
||||
|
||||
let (n, addr) = (*self.io).recv_from(dst)?;
|
||||
|
||||
// Safety: We trust `UdpSocket::recv_from` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
let (n, addr) = (&*self.io).recv_from(dst)?;
|
||||
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
}
|
||||
|
@ -970,6 +1067,62 @@ impl UdpSocket {
|
|||
Ok((n, addr))
|
||||
})
|
||||
}
|
||||
|
||||
/// Receives a single datagram message on the socket, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read and the origin.
|
||||
///
|
||||
/// This method must be called with valid byte array buf of sufficient size
|
||||
/// to hold the message bytes. If a message is too long to fit in the
|
||||
/// supplied buffer, excess bytes may be discarded.
|
||||
///
|
||||
/// This method can be used even if `buf` is uninitialized.
|
||||
///
|
||||
/// # Notes
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::UdpSocket;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// // Connect to a peer
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:8080").await?;
|
||||
/// socket.connect("127.0.0.1:8081").await?;
|
||||
///
|
||||
/// let mut buf = Vec::with_capacity(512);
|
||||
/// let (len, addr) = socket.recv_buf_from(&mut buf).await?;
|
||||
///
|
||||
/// println!("received {:?} bytes from {:?}", len, addr);
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn recv_buf_from<B: BufMut>(&self, buf: &mut B) -> io::Result<(usize, SocketAddr)> {
|
||||
self.io.registration().async_io(Interest::READABLE, || {
|
||||
let dst = buf.chunk_mut();
|
||||
let dst =
|
||||
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
||||
|
||||
let (n, addr) = (*self.io).recv_from(dst)?;
|
||||
|
||||
// Safety: We trust `UdpSocket::recv_from` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
}
|
||||
|
||||
Ok((n,addr))
|
||||
}).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends data on the socket to the given address. On success, returns the
|
||||
|
@ -1140,6 +1293,15 @@ impl UdpSocket {
|
|||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Notes
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.io
|
||||
.registration()
|
||||
|
@ -1164,6 +1326,15 @@ impl UdpSocket {
|
|||
/// # Errors
|
||||
///
|
||||
/// This function may encounter any standard I/O error except `WouldBlock`.
|
||||
///
|
||||
/// # Notes
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub fn poll_recv_from(
|
||||
&self,
|
||||
cx: &mut Context<'_>,
|
||||
|
@ -1189,13 +1360,23 @@ impl UdpSocket {
|
|||
/// Tries to receive a single datagram message on the socket. On success,
|
||||
/// returns the number of bytes read and the origin.
|
||||
///
|
||||
/// The function must be called with valid byte array buf of sufficient size
|
||||
/// This method must be called with valid byte array buf of sufficient size
|
||||
/// to hold the message bytes. If a message is too long to fit in the
|
||||
/// supplied buffer, excess bytes may be discarded.
|
||||
///
|
||||
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
||||
/// returned. This function is usually paired with `readable()`.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
|
@ -1243,7 +1424,7 @@ impl UdpSocket {
|
|||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
/// should attempt to perform IO operation on the socket by manually
|
||||
/// calling the appropriate syscall. If the operation fails because the
|
||||
/// socket is not actually ready, then the closure should return a
|
||||
/// `WouldBlock` error and the readiness flag is cleared. The return value
|
||||
|
@ -1262,6 +1443,11 @@ impl UdpSocket {
|
|||
/// defined on the Tokio `UdpSocket` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
///
|
||||
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: UdpSocket::readable()
|
||||
|
@ -1272,7 +1458,45 @@ impl UdpSocket {
|
|||
interest: Interest,
|
||||
f: impl FnOnce() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io.registration().try_io(interest, f)
|
||||
self.io
|
||||
.registration()
|
||||
.try_io(interest, || self.io.try_io(f))
|
||||
}
|
||||
|
||||
/// Reads or writes from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// The readiness of the socket is awaited and when the socket is ready,
|
||||
/// the provided closure is called. The closure should attempt to perform
|
||||
/// IO operation on the socket by manually calling the appropriate syscall.
|
||||
/// If the operation fails because the socket is not actually ready,
|
||||
/// then the closure should return a `WouldBlock` error. In such case the
|
||||
/// readiness flag is cleared and the socket readiness is awaited again.
|
||||
/// This loop is repeated until the closure returns an `Ok` or an error
|
||||
/// other than `WouldBlock`.
|
||||
///
|
||||
/// The closure should only return a `WouldBlock` error if it has performed
|
||||
/// an IO operation on the socket that failed due to the socket not being
|
||||
/// ready. Returning a `WouldBlock` error in any other situation will
|
||||
/// incorrectly clear the readiness flag, which can cause the socket to
|
||||
/// behave incorrectly.
|
||||
///
|
||||
/// The closure should not perform the IO operation using any of the methods
|
||||
/// defined on the Tokio `UdpSocket` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
pub async fn async_io<R>(
|
||||
&self,
|
||||
interest: Interest,
|
||||
mut f: impl FnMut() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io
|
||||
.registration()
|
||||
.async_io(interest, || self.io.try_io(&mut f))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Receives data from the socket, without removing it from the input queue.
|
||||
|
@ -1287,6 +1511,17 @@ impl UdpSocket {
|
|||
/// Make sure to always use a sufficiently large buffer to hold the
|
||||
/// maximum UDP packet size, which can be up to 65536 bytes in size.
|
||||
///
|
||||
/// MacOS will return an error if you pass a zero-sized buffer.
|
||||
///
|
||||
/// If you're merely interested in learning the sender of the data at the head of the queue,
|
||||
/// try [`peek_sender`].
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
|
@ -1305,6 +1540,9 @@ impl UdpSocket {
|
|||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`peek_sender`]: method@Self::peek_sender
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub async fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.io
|
||||
.registration()
|
||||
|
@ -1313,7 +1551,7 @@ impl UdpSocket {
|
|||
}
|
||||
|
||||
/// Receives data from the socket, without removing it from the input queue.
|
||||
/// On success, returns the number of bytes read.
|
||||
/// On success, returns the sending address of the datagram.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
|
@ -1327,6 +1565,17 @@ impl UdpSocket {
|
|||
/// Make sure to always use a sufficiently large buffer to hold the
|
||||
/// maximum UDP packet size, which can be up to 65536 bytes in size.
|
||||
///
|
||||
/// MacOS will return an error if you pass a zero-sized buffer.
|
||||
///
|
||||
/// If you're merely interested in learning the sender of the data at the head of the queue,
|
||||
/// try [`poll_peek_sender`].
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// The function returns:
|
||||
|
@ -1338,6 +1587,9 @@ impl UdpSocket {
|
|||
/// # Errors
|
||||
///
|
||||
/// This function may encounter any standard I/O error except `WouldBlock`.
|
||||
///
|
||||
/// [`poll_peek_sender`]: method@Self::poll_peek_sender
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub fn poll_peek_from(
|
||||
&self,
|
||||
cx: &mut Context<'_>,
|
||||
|
@ -1360,6 +1612,117 @@ impl UdpSocket {
|
|||
Poll::Ready(Ok(addr))
|
||||
}
|
||||
|
||||
/// Tries to receive data on the socket without removing it from the input queue.
|
||||
/// On success, returns the number of bytes read and the sending address of the
|
||||
/// datagram.
|
||||
///
|
||||
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
||||
/// returned. This function is usually paired with `readable()`.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// On Windows, if the data is larger than the buffer specified, the buffer
|
||||
/// is filled with the first part of the data, and peek returns the error
|
||||
/// WSAEMSGSIZE(10040). The excess data is lost.
|
||||
/// Make sure to always use a sufficiently large buffer to hold the
|
||||
/// maximum UDP packet size, which can be up to 65536 bytes in size.
|
||||
///
|
||||
/// MacOS will return an error if you pass a zero-sized buffer.
|
||||
///
|
||||
/// If you're merely interested in learning the sender of the data at the head of the queue,
|
||||
/// try [`try_peek_sender`].
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [`try_peek_sender`]: method@Self::try_peek_sender
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub fn try_peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.io
|
||||
.registration()
|
||||
.try_io(Interest::READABLE, || self.io.peek_from(buf))
|
||||
}
|
||||
|
||||
/// Retrieve the sender of the data at the head of the input queue, waiting if empty.
|
||||
///
|
||||
/// This is equivalent to calling [`peek_from`] with a zero-sized buffer,
|
||||
/// but suppresses the `WSAEMSGSIZE` error on Windows and the "invalid argument" error on macOS.
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [`peek_from`]: method@Self::peek_from
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub async fn peek_sender(&self) -> io::Result<SocketAddr> {
|
||||
self.io
|
||||
.registration()
|
||||
.async_io(Interest::READABLE, || self.peek_sender_inner())
|
||||
.await
|
||||
}
|
||||
|
||||
/// Retrieve the sender of the data at the head of the input queue,
|
||||
/// scheduling a wakeup if empty.
|
||||
///
|
||||
/// This is equivalent to calling [`poll_peek_from`] with a zero-sized buffer,
|
||||
/// but suppresses the `WSAEMSGSIZE` error on Windows and the "invalid argument" error on macOS.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Note that on multiple calls to a `poll_*` method in the recv direction, only the
|
||||
/// `Waker` from the `Context` passed to the most recent call will be scheduled to
|
||||
/// receive a wakeup.
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [`poll_peek_from`]: method@Self::poll_peek_from
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub fn poll_peek_sender(&self, cx: &mut Context<'_>) -> Poll<io::Result<SocketAddr>> {
|
||||
self.io
|
||||
.registration()
|
||||
.poll_read_io(cx, || self.peek_sender_inner())
|
||||
}
|
||||
|
||||
/// Try to retrieve the sender of the data at the head of the input queue.
|
||||
///
|
||||
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
||||
/// returned. This function is usually paired with `readable()`.
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub fn try_peek_sender(&self) -> io::Result<SocketAddr> {
|
||||
self.io
|
||||
.registration()
|
||||
.try_io(Interest::READABLE, || self.peek_sender_inner())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn peek_sender_inner(&self) -> io::Result<SocketAddr> {
|
||||
self.io.try_io(|| {
|
||||
self.as_socket()
|
||||
.peek_sender()?
|
||||
// May be `None` if the platform doesn't populate the sender for some reason.
|
||||
// In testing, that only occurred on macOS if you pass a zero-sized buffer,
|
||||
// but the implementation of `Socket::peek_sender()` covers that.
|
||||
.as_socket()
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "sender not available"))
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets the value of the `SO_BROADCAST` option for this socket.
|
||||
///
|
||||
/// For more information about this option, see [`set_broadcast`].
|
||||
|
@ -1484,6 +1847,89 @@ impl UdpSocket {
|
|||
self.io.set_ttl(ttl)
|
||||
}
|
||||
|
||||
/// Gets the value of the `IP_TOS` option for this socket.
|
||||
///
|
||||
/// For more information about this option, see [`set_tos`].
|
||||
///
|
||||
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
|
||||
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
|
||||
///
|
||||
/// [`set_tos`]: Self::set_tos
|
||||
// https://docs.rs/socket2/0.4.2/src/socket2/socket.rs.html#1178
|
||||
#[cfg(not(any(
|
||||
target_os = "fuchsia",
|
||||
target_os = "redox",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
)))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(not(any(
|
||||
target_os = "fuchsia",
|
||||
target_os = "redox",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
))))
|
||||
)]
|
||||
pub fn tos(&self) -> io::Result<u32> {
|
||||
self.as_socket().tos()
|
||||
}
|
||||
|
||||
/// Sets the value for the `IP_TOS` option on this socket.
|
||||
///
|
||||
/// This value sets the type-of-service field that is used in every packet
|
||||
/// sent from this socket.
|
||||
///
|
||||
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
|
||||
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
|
||||
// https://docs.rs/socket2/0.4.2/src/socket2/socket.rs.html#1178
|
||||
#[cfg(not(any(
|
||||
target_os = "fuchsia",
|
||||
target_os = "redox",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
)))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(not(any(
|
||||
target_os = "fuchsia",
|
||||
target_os = "redox",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
))))
|
||||
)]
|
||||
pub fn set_tos(&self, tos: u32) -> io::Result<()> {
|
||||
self.as_socket().set_tos(tos)
|
||||
}
|
||||
|
||||
/// Gets the value for the `SO_BINDTODEVICE` option on this socket
|
||||
///
|
||||
/// This value gets the socket-bound device's interface name.
|
||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux",))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux",)))
|
||||
)]
|
||||
pub fn device(&self) -> io::Result<Option<Vec<u8>>> {
|
||||
self.as_socket().device()
|
||||
}
|
||||
|
||||
/// Sets the value for the `SO_BINDTODEVICE` option on this socket
|
||||
///
|
||||
/// If a socket is bound to an interface, only packets received from that
|
||||
/// particular interface are processed by the socket. Note that this only
|
||||
/// works for some socket types, particularly `AF_INET` sockets.
|
||||
///
|
||||
/// If `interface` is `None` or an empty string it removes the binding.
|
||||
#[cfg(all(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(all(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))))
|
||||
)]
|
||||
pub fn bind_device(&self, interface: Option<&[u8]>) -> io::Result<()> {
|
||||
self.as_socket().bind_device(interface)
|
||||
}
|
||||
|
||||
/// Executes an operation of the `IP_ADD_MEMBERSHIP` type.
|
||||
///
|
||||
/// This function specifies a new multicast group for this socket to join.
|
||||
|
@ -1564,7 +2010,7 @@ impl fmt::Debug for UdpSocket {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix))]
|
||||
#[cfg(unix)]
|
||||
mod sys {
|
||||
use super::UdpSocket;
|
||||
use std::os::unix::prelude::*;
|
||||
|
@ -1574,16 +2020,30 @@ mod sys {
|
|||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for UdpSocket {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod sys {
|
||||
use super::UdpSocket;
|
||||
use std::os::windows::prelude::*;
|
||||
cfg_windows! {
|
||||
use crate::os::windows::io::{AsRawSocket, RawSocket};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsSocket, BorrowedSocket};
|
||||
|
||||
impl AsRawSocket for UdpSocket {
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.io.as_raw_socket()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsSocket for UdpSocket {
|
||||
fn as_socket(&self) -> BorrowedSocket<'_> {
|
||||
unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::io::{Interest, PollEvented, ReadBuf, Ready};
|
||||
use crate::net::unix::SocketAddr;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::net::Shutdown;
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use std::os::unix::net;
|
||||
use std::path::Path;
|
||||
|
@ -90,6 +91,7 @@ cfg_net_unix! {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg_attr(docsrs, doc(alias = "uds"))]
|
||||
pub struct UnixDatagram {
|
||||
io: PollEvented<mio::net::UnixDatagram>,
|
||||
}
|
||||
|
@ -104,7 +106,9 @@ impl UnixDatagram {
|
|||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`.
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
|
@ -424,13 +428,21 @@ impl UnixDatagram {
|
|||
/// Creates new `UnixDatagram` from a `std::os::unix::net::UnixDatagram`.
|
||||
///
|
||||
/// This function is intended to be used to wrap a UnixDatagram from the
|
||||
/// standard library in the Tokio equivalent. The conversion assumes
|
||||
/// nothing about the underlying datagram; it is left up to the user to set
|
||||
/// it in non-blocking mode.
|
||||
/// standard library in the Tokio equivalent.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The caller is responsible for ensuring that the socker is in
|
||||
/// non-blocking mode. Otherwise all I/O operations on the socket
|
||||
/// will block the thread, which will cause unexpected behavior.
|
||||
/// Non-blocking mode can be set using [`set_nonblocking`].
|
||||
///
|
||||
/// [`set_nonblocking`]: std::os::unix::net::UnixDatagram::set_nonblocking
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
/// This function panics if it is not called from within a runtime with
|
||||
/// IO enabled.
|
||||
///
|
||||
/// The runtime is usually set implicitly when this function is called
|
||||
/// from a future driven by a Tokio runtime, otherwise runtime can be set
|
||||
|
@ -457,6 +469,7 @@ impl UnixDatagram {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn from_std(datagram: net::UnixDatagram) -> io::Result<UnixDatagram> {
|
||||
let socket = mio::net::UnixDatagram::from_std(datagram);
|
||||
let io = PollEvented::new(socket)?;
|
||||
|
@ -466,21 +479,19 @@ impl UnixDatagram {
|
|||
/// Turns a [`tokio::net::UnixDatagram`] into a [`std::os::unix::net::UnixDatagram`].
|
||||
///
|
||||
/// The returned [`std::os::unix::net::UnixDatagram`] will have nonblocking
|
||||
/// mode set as `true`. Use [`set_nonblocking`] to change the blocking mode
|
||||
/// mode set as `true`. Use [`set_nonblocking`] to change the blocking mode
|
||||
/// if needed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use std::error::Error;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// let tokio_socket = tokio::net::UnixDatagram::bind("127.0.0.1:0")?;
|
||||
/// let std_socket = tokio_socket.into_std()?;
|
||||
/// std_socket.set_nonblocking(false)?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// # use std::error::Error;
|
||||
/// # async fn dox() -> Result<(), Box<dyn Error>> {
|
||||
/// let tokio_socket = tokio::net::UnixDatagram::bind("/path/to/the/socket")?;
|
||||
/// let std_socket = tokio_socket.into_std()?;
|
||||
/// std_socket.set_nonblocking(false)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`tokio::net::UnixDatagram`]: UnixDatagram
|
||||
|
@ -797,6 +808,8 @@ impl UnixDatagram {
|
|||
cfg_io_util! {
|
||||
/// Tries to receive data from the socket without waiting.
|
||||
///
|
||||
/// This method can be used even if `buf` is uninitialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
|
@ -844,7 +857,7 @@ impl UnixDatagram {
|
|||
|
||||
// Safety: We trust `UnixDatagram::recv_from` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
let (n, addr) = (&*self.io).recv_from(dst)?;
|
||||
let (n, addr) = (*self.io).recv_from(dst)?;
|
||||
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
|
@ -856,9 +869,64 @@ impl UnixDatagram {
|
|||
Ok((n, SocketAddr(addr)))
|
||||
}
|
||||
|
||||
/// Receives from the socket, advances the
|
||||
/// buffer's internal cursor and returns how many bytes were read and the origin.
|
||||
///
|
||||
/// This method can be used even if `buf` is uninitialized.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use tokio::net::UnixDatagram;
|
||||
/// use tempfile::tempdir;
|
||||
///
|
||||
/// // We use a temporary directory so that the socket
|
||||
/// // files left by the bound sockets will get cleaned up.
|
||||
/// let tmp = tempdir()?;
|
||||
///
|
||||
/// // Bind each socket to a filesystem path
|
||||
/// let tx_path = tmp.path().join("tx");
|
||||
/// let tx = UnixDatagram::bind(&tx_path)?;
|
||||
/// let rx_path = tmp.path().join("rx");
|
||||
/// let rx = UnixDatagram::bind(&rx_path)?;
|
||||
///
|
||||
/// let bytes = b"hello world";
|
||||
/// tx.send_to(bytes, &rx_path).await?;
|
||||
///
|
||||
/// let mut buf = Vec::with_capacity(24);
|
||||
/// let (size, addr) = rx.recv_buf_from(&mut buf).await?;
|
||||
///
|
||||
/// let dgram = &buf[..size];
|
||||
/// assert_eq!(dgram, bytes);
|
||||
/// assert_eq!(addr.as_pathname().unwrap(), &tx_path);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn recv_buf_from<B: BufMut>(&self, buf: &mut B) -> io::Result<(usize, SocketAddr)> {
|
||||
self.io.registration().async_io(Interest::READABLE, || {
|
||||
let dst = buf.chunk_mut();
|
||||
let dst =
|
||||
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
||||
|
||||
// Safety: We trust `UnixDatagram::recv_from` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
let (n, addr) = (*self.io).recv_from(dst)?;
|
||||
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
}
|
||||
Ok((n,SocketAddr(addr)))
|
||||
}).await
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// This method can be used even if `buf` is uninitialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
|
@ -907,7 +975,7 @@ impl UnixDatagram {
|
|||
|
||||
// Safety: We trust `UnixDatagram::recv` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
let n = (&*self.io).recv(dst)?;
|
||||
let n = (*self.io).recv(dst)?;
|
||||
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
|
@ -916,6 +984,52 @@ impl UnixDatagram {
|
|||
Ok(n)
|
||||
})
|
||||
}
|
||||
|
||||
/// Receives data from the socket from the address to which it is connected,
|
||||
/// advancing the buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// This method can be used even if `buf` is uninitialized.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use tokio::net::UnixDatagram;
|
||||
///
|
||||
/// // Create the pair of sockets
|
||||
/// let (sock1, sock2) = UnixDatagram::pair()?;
|
||||
///
|
||||
/// // Since the sockets are paired, the paired send/recv
|
||||
/// // functions can be used
|
||||
/// let bytes = b"hello world";
|
||||
/// sock1.send(bytes).await?;
|
||||
///
|
||||
/// let mut buff = Vec::with_capacity(24);
|
||||
/// let size = sock2.recv_buf(&mut buff).await?;
|
||||
///
|
||||
/// let dgram = &buff[..size];
|
||||
/// assert_eq!(dgram, bytes);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn recv_buf<B: BufMut>(&self, buf: &mut B) -> io::Result<usize> {
|
||||
self.io.registration().async_io(Interest::READABLE, || {
|
||||
let dst = buf.chunk_mut();
|
||||
let dst =
|
||||
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
||||
|
||||
// Safety: We trust `UnixDatagram::recv_from` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
let n = (*self.io).recv(dst)?;
|
||||
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
}
|
||||
Ok(n)
|
||||
}).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends data on the socket to the specified address.
|
||||
|
@ -1212,7 +1326,7 @@ impl UnixDatagram {
|
|||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
/// should attempt to perform IO operation on the socket by manually
|
||||
/// calling the appropriate syscall. If the operation fails because the
|
||||
/// socket is not actually ready, then the closure should return a
|
||||
/// `WouldBlock` error and the readiness flag is cleared. The return value
|
||||
|
@ -1231,6 +1345,11 @@ impl UnixDatagram {
|
|||
/// defined on the Tokio `UnixDatagram` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
///
|
||||
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: UnixDatagram::readable()
|
||||
|
@ -1241,7 +1360,45 @@ impl UnixDatagram {
|
|||
interest: Interest,
|
||||
f: impl FnOnce() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io.registration().try_io(interest, f)
|
||||
self.io
|
||||
.registration()
|
||||
.try_io(interest, || self.io.try_io(f))
|
||||
}
|
||||
|
||||
/// Reads or writes from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// The readiness of the socket is awaited and when the socket is ready,
|
||||
/// the provided closure is called. The closure should attempt to perform
|
||||
/// IO operation on the socket by manually calling the appropriate syscall.
|
||||
/// If the operation fails because the socket is not actually ready,
|
||||
/// then the closure should return a `WouldBlock` error. In such case the
|
||||
/// readiness flag is cleared and the socket readiness is awaited again.
|
||||
/// This loop is repeated until the closure returns an `Ok` or an error
|
||||
/// other than `WouldBlock`.
|
||||
///
|
||||
/// The closure should only return a `WouldBlock` error if it has performed
|
||||
/// an IO operation on the socket that failed due to the socket not being
|
||||
/// ready. Returning a `WouldBlock` error in any other situation will
|
||||
/// incorrectly clear the readiness flag, which can cause the socket to
|
||||
/// behave incorrectly.
|
||||
///
|
||||
/// The closure should not perform the IO operation using any of the methods
|
||||
/// defined on the Tokio `UnixDatagram` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
pub async fn async_io<R>(
|
||||
&self,
|
||||
interest: Interest,
|
||||
mut f: impl FnMut() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io
|
||||
.registration()
|
||||
.async_io(interest, || self.io.try_io(&mut f))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns the local address that this socket is bound to.
|
||||
|
@ -1420,3 +1577,10 @@ impl AsRawFd for UnixDatagram {
|
|||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for UnixDatagram {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use crate::io::{Interest, PollEvented};
|
||||
use crate::net::unix::{SocketAddr, UnixStream};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use std::os::unix::net;
|
||||
use std::path::Path;
|
||||
|
@ -44,6 +45,7 @@ cfg_net_unix! {
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg_attr(docsrs, doc(alias = "uds"))]
|
||||
pub struct UnixListener {
|
||||
io: PollEvented<mio::net::UnixListener>,
|
||||
}
|
||||
|
@ -54,11 +56,13 @@ impl UnixListener {
|
|||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
/// This function panics if it is not called from within a runtime with
|
||||
/// IO enabled.
|
||||
///
|
||||
/// The runtime is usually set implicitly when this function is called
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||
#[track_caller]
|
||||
pub fn bind<P>(path: P) -> io::Result<UnixListener>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
|
@ -71,17 +75,41 @@ impl UnixListener {
|
|||
/// Creates new `UnixListener` from a `std::os::unix::net::UnixListener `.
|
||||
///
|
||||
/// This function is intended to be used to wrap a UnixListener from the
|
||||
/// standard library in the Tokio equivalent. The conversion assumes
|
||||
/// nothing about the underlying listener; it is left up to the user to set
|
||||
/// it in non-blocking mode.
|
||||
/// standard library in the Tokio equivalent.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The caller is responsible for ensuring that the listener is in
|
||||
/// non-blocking mode. Otherwise all I/O operations on the listener
|
||||
/// will block the thread, which will cause unexpected behavior.
|
||||
/// Non-blocking mode can be set using [`set_nonblocking`].
|
||||
///
|
||||
/// [`set_nonblocking`]: std::os::unix::net::UnixListener::set_nonblocking
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::UnixListener;
|
||||
/// use std::os::unix::net::UnixListener as StdUnixListener;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn Error>> {
|
||||
/// let std_listener = StdUnixListener::bind("/path/to/the/socket")?;
|
||||
/// std_listener.set_nonblocking(true)?;
|
||||
/// let listener = UnixListener::from_std(std_listener)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
/// This function panics if it is not called from within a runtime with
|
||||
/// IO enabled.
|
||||
///
|
||||
/// The runtime is usually set implicitly when this function is called
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||
#[track_caller]
|
||||
pub fn from_std(listener: net::UnixListener) -> io::Result<UnixListener> {
|
||||
let listener = mio::net::UnixListener::from_std(listener);
|
||||
let io = PollEvented::new(listener)?;
|
||||
|
@ -91,20 +119,18 @@ impl UnixListener {
|
|||
/// Turns a [`tokio::net::UnixListener`] into a [`std::os::unix::net::UnixListener`].
|
||||
///
|
||||
/// The returned [`std::os::unix::net::UnixListener`] will have nonblocking mode
|
||||
/// set as `true`. Use [`set_nonblocking`] to change the blocking mode if needed.
|
||||
/// set as `true`. Use [`set_nonblocking`] to change the blocking mode if needed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use std::error::Error;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// let tokio_listener = tokio::net::UnixListener::bind("127.0.0.1:0")?;
|
||||
/// let std_listener = tokio_listener.into_std()?;
|
||||
/// std_listener.set_nonblocking(false)?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// # use std::error::Error;
|
||||
/// # async fn dox() -> Result<(), Box<dyn Error>> {
|
||||
/// let tokio_listener = tokio::net::UnixListener::bind("/path/to/the/socket")?;
|
||||
/// let std_listener = tokio_listener.into_std()?;
|
||||
/// std_listener.set_nonblocking(false)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`tokio::net::UnixListener`]: UnixListener
|
||||
|
@ -184,3 +210,10 @@ impl AsRawFd for UnixListener {
|
|||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for UnixListener {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//! Unix domain socket utility types.
|
||||
|
||||
//! Unix specific network types.
|
||||
// This module does not currently provide any public API, but it was
|
||||
// unintentionally defined as a public module. Hide it from the documentation
|
||||
// instead of changing it to a private module to avoid breakage.
|
||||
|
@ -22,3 +21,17 @@ pub(crate) use stream::UnixStream;
|
|||
|
||||
mod ucred;
|
||||
pub use ucred::UCred;
|
||||
|
||||
pub mod pipe;
|
||||
|
||||
/// A type representing process and process group IDs.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type uid_t = u32;
|
||||
|
||||
/// A type representing user ID.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type gid_t = u32;
|
||||
|
||||
/// A type representing group ID.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type pid_t = i32;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -55,9 +55,20 @@ pub(crate) fn split(stream: &mut UnixStream) -> (ReadHalf<'_>, WriteHalf<'_>) {
|
|||
impl ReadHalf<'_> {
|
||||
/// Wait for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_read()`]. It can be used instead
|
||||
/// of [`readable()`] to check the returned ready set for [`Ready::READABLE`]
|
||||
/// and [`Ready::READ_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`UnixStream::ready`].
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read
|
||||
/// [`readable()`]: Self::readable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
|
@ -100,8 +111,12 @@ impl ReadHalf<'_> {
|
|||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
|
||||
///
|
||||
/// 1. The stream's read half is closed and will no longer yield data.
|
||||
/// 2. The specified buffer was 0 bytes in length.
|
||||
///
|
||||
/// If the stream is not ready to read data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.try_read(buf)
|
||||
|
@ -174,9 +189,20 @@ impl ReadHalf<'_> {
|
|||
impl WriteHalf<'_> {
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_write()`]. It can be used instead
|
||||
/// of [`writable()`] to check the returned ready set for [`Ready::WRITABLE`]
|
||||
/// and [`Ready::WRITE_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`UnixStream::ready`].
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write
|
||||
/// [`writable()`]: Self::writable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
|
|
|
@ -110,9 +110,20 @@ impl OwnedReadHalf {
|
|||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_read()`]. It can be used instead
|
||||
/// of [`readable()`] to check the returned ready set for [`Ready::READABLE`]
|
||||
/// and [`Ready::READ_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`UnixStream::ready`].
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read
|
||||
/// [`readable()`]: Self::readable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
|
@ -155,8 +166,12 @@ impl OwnedReadHalf {
|
|||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
|
||||
///
|
||||
/// 1. The stream's read half is closed and will no longer yield data.
|
||||
/// 2. The specified buffer was 0 bytes in length.
|
||||
///
|
||||
/// If the stream is not ready to read data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.try_read(buf)
|
||||
|
@ -257,9 +272,20 @@ impl OwnedWriteHalf {
|
|||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_write()`]. It can be used instead
|
||||
/// of [`writable()`] to check the returned ready set for [`Ready::WRITABLE`]
|
||||
/// and [`Ready::WRITE_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`UnixStream::ready`].
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write
|
||||
/// [`writable()`]: Self::writable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
|
@ -382,12 +408,12 @@ impl AsyncWrite for OwnedWriteHalf {
|
|||
|
||||
impl AsRef<UnixStream> for OwnedReadHalf {
|
||||
fn as_ref(&self) -> &UnixStream {
|
||||
&*self.inner
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<UnixStream> for OwnedWriteHalf {
|
||||
fn as_ref(&self) -> &UnixStream {
|
||||
&*self.inner
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,11 @@ use crate::net::unix::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf};
|
|||
use crate::net::unix::ucred::{self, UCred};
|
||||
use crate::net::unix::SocketAddr;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::Shutdown;
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use std::os::unix::net;
|
||||
use std::path::Path;
|
||||
|
@ -22,8 +23,8 @@ cfg_io_util! {
|
|||
cfg_net_unix! {
|
||||
/// A structure representing a connected Unix socket.
|
||||
///
|
||||
/// This socket can be connected directly with `UnixStream::connect` or accepted
|
||||
/// from a listener with `UnixListener::incoming`. Additionally, a pair of
|
||||
/// This socket can be connected directly with [`UnixStream::connect`] or accepted
|
||||
/// from a listener with [`UnixListener::accept`]. Additionally, a pair of
|
||||
/// anonymous Unix sockets can be created with `UnixStream::pair`.
|
||||
///
|
||||
/// To shut down the stream in the write direction, you can call the
|
||||
|
@ -32,6 +33,8 @@ cfg_net_unix! {
|
|||
/// the stream in one direction.
|
||||
///
|
||||
/// [`shutdown()`]: fn@crate::io::AsyncWriteExt::shutdown
|
||||
/// [`UnixListener::accept`]: crate::net::UnixListener::accept
|
||||
#[cfg_attr(docsrs, doc(alias = "uds"))]
|
||||
pub struct UnixStream {
|
||||
io: PollEvented<mio::net::UnixStream>,
|
||||
}
|
||||
|
@ -65,6 +68,12 @@ impl UnixStream {
|
|||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
|
@ -239,8 +248,12 @@ impl UnixStream {
|
|||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
|
||||
///
|
||||
/// 1. The stream's read half is closed and will no longer yield data.
|
||||
/// 2. The specified buffer was 0 bytes in length.
|
||||
///
|
||||
/// If the stream is not ready to read data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -656,7 +669,7 @@ impl UnixStream {
|
|||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
/// should attempt to perform IO operation on the socket by manually
|
||||
/// calling the appropriate syscall. If the operation fails because the
|
||||
/// socket is not actually ready, then the closure should return a
|
||||
/// `WouldBlock` error and the readiness flag is cleared. The return value
|
||||
|
@ -675,6 +688,11 @@ impl UnixStream {
|
|||
/// defined on the Tokio `UnixStream` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
///
|
||||
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: UnixStream::readable()
|
||||
|
@ -685,23 +703,85 @@ impl UnixStream {
|
|||
interest: Interest,
|
||||
f: impl FnOnce() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io.registration().try_io(interest, f)
|
||||
self.io
|
||||
.registration()
|
||||
.try_io(interest, || self.io.try_io(f))
|
||||
}
|
||||
|
||||
/// Reads or writes from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// The readiness of the socket is awaited and when the socket is ready,
|
||||
/// the provided closure is called. The closure should attempt to perform
|
||||
/// IO operation on the socket by manually calling the appropriate syscall.
|
||||
/// If the operation fails because the socket is not actually ready,
|
||||
/// then the closure should return a `WouldBlock` error. In such case the
|
||||
/// readiness flag is cleared and the socket readiness is awaited again.
|
||||
/// This loop is repeated until the closure returns an `Ok` or an error
|
||||
/// other than `WouldBlock`.
|
||||
///
|
||||
/// The closure should only return a `WouldBlock` error if it has performed
|
||||
/// an IO operation on the socket that failed due to the socket not being
|
||||
/// ready. Returning a `WouldBlock` error in any other situation will
|
||||
/// incorrectly clear the readiness flag, which can cause the socket to
|
||||
/// behave incorrectly.
|
||||
///
|
||||
/// The closure should not perform the IO operation using any of the methods
|
||||
/// defined on the Tokio `UnixStream` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
pub async fn async_io<R>(
|
||||
&self,
|
||||
interest: Interest,
|
||||
mut f: impl FnMut() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io
|
||||
.registration()
|
||||
.async_io(interest, || self.io.try_io(&mut f))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Creates new `UnixStream` from a `std::os::unix::net::UnixStream`.
|
||||
///
|
||||
/// This function is intended to be used to wrap a UnixStream from the
|
||||
/// standard library in the Tokio equivalent. The conversion assumes
|
||||
/// nothing about the underlying stream; it is left up to the user to set
|
||||
/// it in non-blocking mode.
|
||||
/// standard library in the Tokio equivalent.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The caller is responsible for ensuring that the stream is in
|
||||
/// non-blocking mode. Otherwise all I/O operations on the stream
|
||||
/// will block the thread, which will cause unexpected behavior.
|
||||
/// Non-blocking mode can be set using [`set_nonblocking`].
|
||||
///
|
||||
/// [`set_nonblocking`]: std::os::unix::net::UnixStream::set_nonblocking
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::UnixStream;
|
||||
/// use std::os::unix::net::UnixStream as StdUnixStream;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn Error>> {
|
||||
/// let std_stream = StdUnixStream::connect("/path/to/the/socket")?;
|
||||
/// std_stream.set_nonblocking(true)?;
|
||||
/// let stream = UnixStream::from_std(std_stream)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
/// This function panics if it is not called from within a runtime with
|
||||
/// IO enabled.
|
||||
///
|
||||
/// The runtime is usually set implicitly when this function is called
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||
#[track_caller]
|
||||
pub fn from_std(stream: net::UnixStream) -> io::Result<UnixStream> {
|
||||
let stream = mio::net::UnixStream::from_std(stream);
|
||||
let io = PollEvented::new(stream)?;
|
||||
|
@ -958,3 +1038,10 @@ impl AsRawFd for UnixStream {
|
|||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for UnixStream {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
use libc::{gid_t, pid_t, uid_t};
|
||||
use crate::net::unix;
|
||||
|
||||
/// Credentials of a process.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct UCred {
|
||||
/// PID (process ID) of the process.
|
||||
pid: Option<pid_t>,
|
||||
pid: Option<unix::pid_t>,
|
||||
/// UID (user ID) of the process.
|
||||
uid: uid_t,
|
||||
uid: unix::uid_t,
|
||||
/// GID (group ID) of the process.
|
||||
gid: gid_t,
|
||||
gid: unix::gid_t,
|
||||
}
|
||||
|
||||
impl UCred {
|
||||
/// Gets UID (user ID) of the process.
|
||||
pub fn uid(&self) -> uid_t {
|
||||
pub fn uid(&self) -> unix::uid_t {
|
||||
self.uid
|
||||
}
|
||||
|
||||
/// Gets GID (group ID) of the process.
|
||||
pub fn gid(&self) -> gid_t {
|
||||
pub fn gid(&self) -> unix::gid_t {
|
||||
self.gid
|
||||
}
|
||||
|
||||
|
@ -26,12 +26,17 @@ impl UCred {
|
|||
///
|
||||
/// This is only implemented under Linux, Android, iOS, macOS, Solaris and
|
||||
/// Illumos. On other platforms this will always return `None`.
|
||||
pub fn pid(&self) -> Option<pid_t> {
|
||||
pub fn pid(&self) -> Option<unix::pid_t> {
|
||||
self.pid
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android", target_os = "openbsd"))]
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "redox",
|
||||
target_os = "android",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
pub(crate) use self::impl_linux::get_peer_cred;
|
||||
|
||||
#[cfg(any(target_os = "netbsd"))]
|
||||
|
@ -46,16 +51,24 @@ pub(crate) use self::impl_macos::get_peer_cred;
|
|||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
pub(crate) use self::impl_solaris::get_peer_cred;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android", target_os = "openbsd"))]
|
||||
#[cfg(target_os = "aix")]
|
||||
pub(crate) use self::impl_aix::get_peer_cred;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "redox",
|
||||
target_os = "android",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
pub(crate) mod impl_linux {
|
||||
use crate::net::unix::UnixStream;
|
||||
use crate::net::unix::{self, UnixStream};
|
||||
|
||||
use libc::{c_void, getsockopt, socklen_t, SOL_SOCKET, SO_PEERCRED};
|
||||
use std::{io, mem};
|
||||
|
||||
#[cfg(target_os = "openbsd")]
|
||||
use libc::sockpeercred as ucred;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[cfg(any(target_os = "linux", target_os = "redox", target_os = "android"))]
|
||||
use libc::ucred;
|
||||
|
||||
pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
|
||||
|
@ -87,9 +100,9 @@ pub(crate) mod impl_linux {
|
|||
);
|
||||
if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
|
||||
Ok(super::UCred {
|
||||
uid: ucred.uid,
|
||||
gid: ucred.gid,
|
||||
pid: Some(ucred.pid),
|
||||
uid: ucred.uid as unix::uid_t,
|
||||
gid: ucred.gid as unix::gid_t,
|
||||
pid: Some(ucred.pid as unix::pid_t),
|
||||
})
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
|
@ -100,7 +113,7 @@ pub(crate) mod impl_linux {
|
|||
|
||||
#[cfg(any(target_os = "netbsd"))]
|
||||
pub(crate) mod impl_netbsd {
|
||||
use crate::net::unix::UnixStream;
|
||||
use crate::net::unix::{self, UnixStream};
|
||||
|
||||
use libc::{c_void, getsockopt, socklen_t, unpcbid, LOCAL_PEEREID, SOL_SOCKET};
|
||||
use std::io;
|
||||
|
@ -129,9 +142,9 @@ pub(crate) mod impl_netbsd {
|
|||
);
|
||||
if ret == 0 && unpcbid_size as usize == size_of::<unpcbid>() {
|
||||
Ok(super::UCred {
|
||||
uid: unpcbid.unp_euid,
|
||||
gid: unpcbid.unp_egid,
|
||||
pid: Some(unpcbid.unp_pid),
|
||||
uid: unpcbid.unp_euid as unix::uid_t,
|
||||
gid: unpcbid.unp_egid as unix::gid_t,
|
||||
pid: Some(unpcbid.unp_pid as unix::pid_t),
|
||||
})
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
|
@ -142,7 +155,7 @@ pub(crate) mod impl_netbsd {
|
|||
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
pub(crate) mod impl_bsd {
|
||||
use crate::net::unix::UnixStream;
|
||||
use crate::net::unix::{self, UnixStream};
|
||||
|
||||
use libc::getpeereid;
|
||||
use std::io;
|
||||
|
@ -160,8 +173,8 @@ pub(crate) mod impl_bsd {
|
|||
|
||||
if ret == 0 {
|
||||
Ok(super::UCred {
|
||||
uid: uid.assume_init(),
|
||||
gid: gid.assume_init(),
|
||||
uid: uid.assume_init() as unix::uid_t,
|
||||
gid: gid.assume_init() as unix::gid_t,
|
||||
pid: None,
|
||||
})
|
||||
} else {
|
||||
|
@ -173,7 +186,7 @@ pub(crate) mod impl_bsd {
|
|||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
pub(crate) mod impl_macos {
|
||||
use crate::net::unix::UnixStream;
|
||||
use crate::net::unix::{self, UnixStream};
|
||||
|
||||
use libc::{c_void, getpeereid, getsockopt, pid_t, LOCAL_PEEREPID, SOL_LOCAL};
|
||||
use std::io;
|
||||
|
@ -207,9 +220,9 @@ pub(crate) mod impl_macos {
|
|||
|
||||
if ret == 0 {
|
||||
Ok(super::UCred {
|
||||
uid: uid.assume_init(),
|
||||
gid: gid.assume_init(),
|
||||
pid: Some(pid.assume_init()),
|
||||
uid: uid.assume_init() as unix::uid_t,
|
||||
gid: gid.assume_init() as unix::gid_t,
|
||||
pid: Some(pid.assume_init() as unix::pid_t),
|
||||
})
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
|
@ -220,7 +233,7 @@ pub(crate) mod impl_macos {
|
|||
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
pub(crate) mod impl_solaris {
|
||||
use crate::net::unix::UnixStream;
|
||||
use crate::net::unix::{self, UnixStream};
|
||||
use std::io;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::ptr;
|
||||
|
@ -240,9 +253,37 @@ pub(crate) mod impl_solaris {
|
|||
libc::ucred_free(cred);
|
||||
|
||||
Ok(super::UCred {
|
||||
uid,
|
||||
gid,
|
||||
pid: Some(pid),
|
||||
uid: uid as unix::uid_t,
|
||||
gid: gid as unix::gid_t,
|
||||
pid: Some(pid as unix::pid_t),
|
||||
})
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "aix")]
|
||||
pub(crate) mod impl_aix {
|
||||
use crate::net::unix::UnixStream;
|
||||
use std::io;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
|
||||
unsafe {
|
||||
let raw_fd = sock.as_raw_fd();
|
||||
|
||||
let mut uid = std::mem::MaybeUninit::uninit();
|
||||
let mut gid = std::mem::MaybeUninit::uninit();
|
||||
|
||||
let ret = libc::getpeereid(raw_fd, uid.as_mut_ptr(), gid.as_mut_ptr());
|
||||
|
||||
if ret == 0 {
|
||||
Ok(super::UCred {
|
||||
uid: uid.assume_init(),
|
||||
gid: gid.assume_init(),
|
||||
pid: None,
|
||||
})
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,74 +0,0 @@
|
|||
#![cfg_attr(not(feature = "full"), allow(dead_code))]
|
||||
|
||||
use crate::park::{Park, Unpark};
|
||||
|
||||
use std::fmt;
|
||||
use std::time::Duration;
|
||||
|
||||
pub(crate) enum Either<A, B> {
|
||||
A(A),
|
||||
B(B),
|
||||
}
|
||||
|
||||
impl<A, B> Park for Either<A, B>
|
||||
where
|
||||
A: Park,
|
||||
B: Park,
|
||||
{
|
||||
type Unpark = Either<A::Unpark, B::Unpark>;
|
||||
type Error = Either<A::Error, B::Error>;
|
||||
|
||||
fn unpark(&self) -> Self::Unpark {
|
||||
match self {
|
||||
Either::A(a) => Either::A(a.unpark()),
|
||||
Either::B(b) => Either::B(b.unpark()),
|
||||
}
|
||||
}
|
||||
|
||||
fn park(&mut self) -> Result<(), Self::Error> {
|
||||
match self {
|
||||
Either::A(a) => a.park().map_err(Either::A),
|
||||
Either::B(b) => b.park().map_err(Either::B),
|
||||
}
|
||||
}
|
||||
|
||||
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> {
|
||||
match self {
|
||||
Either::A(a) => a.park_timeout(duration).map_err(Either::A),
|
||||
Either::B(b) => b.park_timeout(duration).map_err(Either::B),
|
||||
}
|
||||
}
|
||||
|
||||
fn shutdown(&mut self) {
|
||||
match self {
|
||||
Either::A(a) => a.shutdown(),
|
||||
Either::B(b) => b.shutdown(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Unpark for Either<A, B>
|
||||
where
|
||||
A: Unpark,
|
||||
B: Unpark,
|
||||
{
|
||||
fn unpark(&self) {
|
||||
match self {
|
||||
Either::A(a) => a.unpark(),
|
||||
Either::B(b) => b.unpark(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> fmt::Debug for Either<A, B>
|
||||
where
|
||||
A: fmt::Debug,
|
||||
B: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Either::A(a) => a.fmt(fmt),
|
||||
Either::B(b) => b.fmt(fmt),
|
||||
}
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче