Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
1
third-party/vendor/calloop/.cargo-checksum.json
vendored
Normal file
1
third-party/vendor/calloop/.cargo-checksum.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"files":{"CHANGELOG.md":"e28177e187b037be4d6c52f8ad9191c79c08539ed86e789803875ca3c95ea616","Cargo.lock":"181167c591dc8f0bd91e998d203e6699ebc6e655b7d288411cd3dc9baf9dd34a","Cargo.toml":"455a5a6828f665a4a4bc7fc4e9005fdba2bdf89d803e5da5c59c520f82bcb522","LICENSE.txt":"4b71658d3410d813b6fc6ceb654ce2aa8c2d3094c944dc59c2041da6227810e3","README.md":"1e609225164b6f7b88b3ae124e9b73b165a452aba4c97b0fe20a543294b2af9d","README.tpl":"521393c5b72ea749489781d3f752a1797044f0c0b0707ec7a208299c3f3ee9db","clippy.toml":"4c18c3dc5b155cc0d556ec0d881e4a3e82ea40a1bee7c793b10d03657b32af79","examples/high_precision.rs":"6857463f5bc529af5654110a74d4e71a3da51b6a095b5227ba18cf89618013d3","examples/timer.rs":"667e4d1cefb54a34c227f4f9df0d4a8da923ef7856be2be88db440449411eeee","src/error.rs":"840345177d05e5e965fa059086da0bad527527223535e48e84fe1b0c2cea9f16","src/io.rs":"f5f69a52a41d1df6e2dff6b8e14e253aed29a1c6c80fe934f86d4d380a1e06d7","src/lib.rs":"c48344287ddf8bb02f233d9be376036d41fff72da0dc9e87373a1e9c5f9b8367","src/loop_logic.rs":"5fb141b6fa6953b32bf0ebf5441e5cb2c05e3ceca2c27262f1ca7ca11eede4fe","src/macros.rs":"4b698b68db3bbe8b236872c52030f8b16fb1b27a9230de1a4a15fad980e687fc","src/sources/channel.rs":"8ae05f242ba74c1320f19361e6ffd230745f7f3c7dc138d37a82ebd363c5fd25","src/sources/futures.rs":"2ec69fc57e62c01c81e7017ec2ff4b28f7b4a81331e80f50a7ecaf8b16f01549","src/sources/generic.rs":"1ae7ae0f023ae19d95bf9900cb9a6e5597b9e0a64c272185e97e635052f95162","src/sources/mod.rs":"a88608ea7958f538e7fbd6a1a5a331a602e2f7f2db81b796f77f456211b68339","src/sources/ping.rs":"acc18e8b9d51fcde7210a98aa0a33f4a7b011d24ed14beda9d3c17cb8966fec5","src/sources/ping/eventfd.rs":"2673638dfce8432b474670f388607097bb4b6fe16a54d9fe4bc4f11716f3dae9","src/sources/ping/pipe.rs":"41e12f028f9b0fb597a56aef718020d574fb0faa4309b1cb812fb49abed6d182","src/sources/signals.rs":"8f99ddd0f4e29fb1b16ea8b474350ee94ee6d3dd2ca16929c5d191232798d268","src/sources/timer.rs":"623efcace05796c85e797c1ef7c63ac90edfc27069203292f3951e3eaf03a667","src/sources/transient.rs":"e726863c539c51eda5c15fd408aa72d2a04ed521bc93ba0781f0c627861f782f","src/sys/epoll.rs":"8a6dfa579d89e87e09529f8ed9557811662672f0306d4a272f06f7a6f2316c38","src/sys/kqueue.rs":"b8cc842efb8515ab08148843bcbb688aea29f84c96110ed684772e073366c733","src/sys/mod.rs":"87e73f0dfc752e181e16587512219480667a3b7c79fcdb23f9fff7f3559da1f8","tests/signals.rs":"80c63efb8b7f7382a8f624a30248034ecedfc3f5c817226a49733aa608a199f3"},"package":"52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8"}
|
||||
260
third-party/vendor/calloop/CHANGELOG.md
vendored
Normal file
260
third-party/vendor/calloop/CHANGELOG.md
vendored
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
# Change Log
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 0.10.4 -- 2022-12-05
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- Fixed a crash due to double borrow when handling pre/post run hooks
|
||||
|
||||
## 0.10.2 -- 2022-11-08
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- The return value of `LoopHandle::insert_idle` no longer borrows the `LoopHandle`.
|
||||
|
||||
## 0.10.1 -- 2022-06-20
|
||||
|
||||
#### Additions
|
||||
|
||||
- The `Channel` now has proxy methods for `Receiver::recv` and `Receiver::try_recv`
|
||||
- Enable support for `target_os = "android"`
|
||||
|
||||
## 0.10.0 -- 2022-05-06
|
||||
|
||||
- **Breaking:** Calloop's internal storage is now backed by a `slotmap`. As a result the
|
||||
`RegistrationToken` is now `Copy+Clone`, and the low-level registration API of `Poll` is
|
||||
altered in a breaking way. MSRV is bumped to 1.49.
|
||||
- **Breaking:** `generic::Fd` adapter is removed, as since that rust version `RawFd` implements
|
||||
`AsRawFd`, allowing it to be used directly in `Generic`.
|
||||
- **Breaking:** The `EventSource` trait has a new associated type `Error`. This determines the type
|
||||
of the error variant returned by `EventSource::process_events()`. It must be convertible into
|
||||
`Box<dyn std::error::Error + Sync + Send>`.
|
||||
- **Breaking:** All library-provided event sources now have their own error types for the
|
||||
associated `Error` type on the `EventSource` trait.
|
||||
- **Breaking:** Many API functions now use Calloop's own error type (`calloop::Error`) instead of
|
||||
`std::io::Error` as the error variants of their returned results.
|
||||
- **Breaking:** The `Timer` event source has been completely reworked and is now directly driven by
|
||||
calloop polling mechanism instead of a background thread. Timer multiplexing is now handled by
|
||||
creating multiple `Timer`s, and self-repeating timers is handled by the return value of the
|
||||
associated event callback.
|
||||
- **Breaking:** The minimum supported Rust version is now 1.53.0
|
||||
- Introduce `EventLoop::try_new_high_precision()` for sub-millisecond accuracy in the event loop
|
||||
- The `PingSource` event source now uses an `eventfd` instead of a pipe on Linux.
|
||||
|
||||
## 0.9.2 -- 2021-12-27
|
||||
|
||||
#### Additions
|
||||
|
||||
- Introduce the methods `pre_run()` and `post_run()` to `EventSource`, allowing event sources
|
||||
to do preparations before entering a run/dispatch session, and cleanup afterwards. They have default
|
||||
implementations doing nothing.
|
||||
|
||||
## 0.9.1 -- 2021-08-10
|
||||
|
||||
- Update `nix` dependency to 0.22
|
||||
|
||||
## 0.9.0 -- 2021-06-29
|
||||
|
||||
#### Breaking changes
|
||||
|
||||
- MSRV is now 1.41
|
||||
- The `futures` module now has a proper error type for `Scheduler::schedule()`
|
||||
- The return type of `EventSource::process_events()` is now `io::Result<PostAction>` allowing
|
||||
the sources to directly request the event loop to reregister/disable/destroy them.
|
||||
- The `Token` creation mechanism is now driven by a `TokenFactory`, that
|
||||
dynamically generates new unique token for sub-sources. Following for this
|
||||
if you create a new event source that is not built by composing the ones
|
||||
provided by calloop, you need to check if the `Token` provided to
|
||||
`process_events` is the same as the one you created when (re)registering
|
||||
your source. If you delegate `process_events` to a sub-source, you no longer
|
||||
need to check the `sub_id` before, instead the source you are delegating to
|
||||
is responsible to to this check.
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- Cancelling a timeout no longer prevents later timeouts from firing.
|
||||
|
||||
## 0.8.0 -- 2021-05-30
|
||||
|
||||
#### Breaking changes
|
||||
|
||||
- The `Dispatcher` type no longer has the closure type within its type parameters,
|
||||
but instead now has an explicit lifetime parameter, as well as the source type `S`
|
||||
and the event loop `Data` type. This allows the type to be explicitly named and
|
||||
stored into an other struct.
|
||||
|
||||
#### Additions
|
||||
|
||||
- `Token` now has a method `with_sub_id()` that returns a copy of the token
|
||||
but with the given `sub_id`.
|
||||
|
||||
## 0.7.2 -- 2021-02-09
|
||||
|
||||
#### Changes
|
||||
|
||||
- `EventLoop::run()` now accepts `Into<Option<Duration>>`, like `EventLoop::dispatch()`
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- The `Ping` event source now automatically disables itself when its sending end is
|
||||
dropped, preventing to always be considered readable (which caused a busy-loop).
|
||||
This also fixes a similar behavior of `Executor` and `Channel`, which use `Ping`
|
||||
internally.
|
||||
|
||||
## 0.7.0 -- 2020-10-13
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
- The return type for `LoopHandle::insert_source` was renamed as
|
||||
`RegistrationToken` and can be used in `{enable,disable,update,remove,kill}`
|
||||
just like before.
|
||||
- Allow non-`'static` event sources and callbacks, so they can hold references
|
||||
to other values.
|
||||
- `LoopHandle::with_source` was removed. To achieve the same behaviour, use a
|
||||
`Dispatcher` and register it via the `LoopHandle::register_dispatcher`. The
|
||||
`EventSource` will be available using `Dispatcher::as_source_{ref,mut}`.
|
||||
- `LoopHandle::remove` doesn't return the event source any more. To achieve
|
||||
the same behaviour, use a `Dispatcher` and register it via the
|
||||
`LoopHandle::register_dispatcher`. After removing the `EventSource` with
|
||||
`LoopHandle::remove`, you will be able to call
|
||||
`Dispatcher::into_source_inner` to get ownership of the `EventSource`.
|
||||
- `LoopHandle::register_dispatcher` can be used in place of
|
||||
`LoopHandle::insert_source` when the source needs to be accessed after
|
||||
its insertion in the loop.
|
||||
- `Interest` is changed into a struct to allow empty interest queries
|
||||
|
||||
#### Additions
|
||||
|
||||
- Introduce a futures executor as a new event source, behind the `executor` cargo
|
||||
feature.
|
||||
- Introduce the `LoopHandle::adapt_io` method for creating `Async<F>` adapters to
|
||||
adapt IO objects for async use, powered by the event loop.
|
||||
|
||||
## 0.6.5 -- 2020-10-07
|
||||
|
||||
#### Fixes
|
||||
|
||||
- Channel now signals readinnes after the event has actually been sent, fixing a race
|
||||
condition where the event loop would try to read the message before it has been
|
||||
written.
|
||||
|
||||
## 0.6.4 -- 2020-08-30
|
||||
|
||||
#### Fixes
|
||||
|
||||
- Fix double borrow during dispatch when some event source is getting removed
|
||||
|
||||
## 0.6.3 -- 2020-08-27
|
||||
|
||||
#### Aditions
|
||||
|
||||
- Add support for `openbsd`, `netbsd`, and `dragonfly`.
|
||||
- `InsertError<E>` now implements `std::error::Error`.
|
||||
|
||||
#### Changes
|
||||
|
||||
- Allow non-`'static` dispatch `Data`. `Data` is passed as an argument to the
|
||||
`callback`s while dispatching. This change allows defining `Data` types which
|
||||
can hold references to other values.
|
||||
- `dispatch` now will retry on `EINTR`.
|
||||
|
||||
## 0.6.2 -- 2020-04-23
|
||||
|
||||
- Update the README and keywords for crates.io
|
||||
|
||||
## 0.6.1 -- 2020-04-22
|
||||
|
||||
- Introduce `LoopHandle::kill` to allow dropping a source from within its callback
|
||||
|
||||
## 0.6.0 -- 2020-04-22
|
||||
|
||||
- Drop the `mio` dependency
|
||||
- **Breaking Change**: Significantly rework the `calloop` API, notably:
|
||||
- Event sources are now owned by the `EventLoop`
|
||||
- Users can now again set the polling mode (Level/Edge/OneShot)
|
||||
- Introduce the `Ping` event source
|
||||
|
||||
## 0.5.2 -- 2020-04-14
|
||||
|
||||
- `channel::Channel` is now `Send`, allowing you to create a channel in one thread and sending
|
||||
its receiving end to an other thread for event loop insertion.
|
||||
|
||||
## 0.5.1 -- 2020-03-14
|
||||
|
||||
- Update `mio` to `0.7`
|
||||
|
||||
## 0.5.0 -- 2020-02-07
|
||||
|
||||
- Update to 2018 edition
|
||||
- Update `nix` dependency to `0.17`
|
||||
- **Breaking** Update `mio` dependency to `0.7.0-alpha.1`. The API of `calloop` for custom
|
||||
event sources significantly changed, and the channel and timer event sources are now
|
||||
implemented in `calloop` rather than pulled from `mio-extras`.
|
||||
|
||||
## 0.4.4 -- 2019-06-13
|
||||
|
||||
- Update `nix` dependency to `0.14`
|
||||
|
||||
## 0.4.3 -- 2019-02-17
|
||||
|
||||
- Update `mio` dependency
|
||||
- Update `nix` dependency
|
||||
|
||||
## 0.4.2 -- 2018-11-15
|
||||
|
||||
- Implement `Debug` for `InsertError`.
|
||||
|
||||
## 0.4.1 -- 2018-11-14
|
||||
|
||||
- Disable the `sources::signal` module on FreeBSD so that the library can be built on this
|
||||
platform.
|
||||
|
||||
## 0.4.0 -- 2018-11-04
|
||||
|
||||
- **Breaking** Use `mio-extras` instead of `mio-more` which is not maintained.
|
||||
- **Breaking** Reexport `mio` rather than selectively re-exporting some of its types.
|
||||
- Add methods to `Generic` to retrive the inner `Rc` and construct it from an `Rc`
|
||||
- **Breaking** `LoopHandle::insert_source` now allows to retrieve the source on error.
|
||||
|
||||
## 0.3.2 -- 2018-09-25
|
||||
|
||||
- Fix the contents of `EventedRawFd` which was erroneously not public.
|
||||
|
||||
## 0.3.1 -- 2018-09-25
|
||||
|
||||
- introduce `EventedRawFd` as a special case for the `Generic` event source, for when
|
||||
you really have to manipulate raw fds
|
||||
- Don't panic when the removal of an event source trigger the removal of an other one
|
||||
|
||||
## 0.3.0 -- 2018-09-10
|
||||
|
||||
- Fixed a bug where inserting an event source from within a callback caused a panic.
|
||||
- **[breaking]** Erase the `Data` type parameter from `Source` and `Idle`, for
|
||||
improved ergonomics.
|
||||
|
||||
## 0.2.2 -- 2018-09-10
|
||||
|
||||
- Introduce an `EventLoop::run` method, as well as the `LoopSignal` handle allowing to
|
||||
wakeup or stop the event loop from anywhere.
|
||||
|
||||
## 0.2.1 -- 2018-09-01
|
||||
|
||||
- Use `FnOnce` for insertion in idle callbacks.
|
||||
|
||||
## 0.2.0 -- 2018-08-30
|
||||
|
||||
- **[breaking]** Add a `&mut shared_data` argument to `EventLoop::dispatch(..)` to share data
|
||||
between callbacks.
|
||||
|
||||
## 0.1.1 -- 2018-08-29
|
||||
|
||||
- `Generic` event source for wrapping arbitrary `Evented` types
|
||||
- timer event sources
|
||||
- UNIX signal event sources
|
||||
- channel event sources
|
||||
|
||||
## 0.1.0 -- 2018-08-24
|
||||
|
||||
Initial release
|
||||
265
third-party/vendor/calloop/Cargo.lock
generated
vendored
Normal file
265
third-party/vendor/calloop/Cargo.lock
generated
vendored
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
version = "0.10.6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"futures",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"log",
|
||||
"nix",
|
||||
"slotmap",
|
||||
"thiserror",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slotmap"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
85
third-party/vendor/calloop/Cargo.toml
vendored
Normal file
85
third-party/vendor/calloop/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "calloop"
|
||||
version = "0.10.6"
|
||||
authors = ["Elinor Berger <elinor@safaradeg.net>"]
|
||||
autotests = false
|
||||
description = "A callback-based event loop"
|
||||
documentation = "https://docs.rs/calloop/"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"events",
|
||||
"loop",
|
||||
"callback",
|
||||
"eventloop",
|
||||
"unix",
|
||||
]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/Smithay/calloop"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = [
|
||||
"--cfg",
|
||||
"docsrs",
|
||||
]
|
||||
|
||||
[[test]]
|
||||
name = "signals"
|
||||
harness = false
|
||||
|
||||
[dependencies.bitflags]
|
||||
version = "1.2"
|
||||
|
||||
[dependencies.futures-io]
|
||||
version = "0.3.5"
|
||||
optional = true
|
||||
|
||||
[dependencies.futures-util]
|
||||
version = "0.3.5"
|
||||
features = ["std"]
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4"
|
||||
|
||||
[dependencies.nix]
|
||||
version = "0.25"
|
||||
features = [
|
||||
"event",
|
||||
"fs",
|
||||
"signal",
|
||||
"socket",
|
||||
"time",
|
||||
]
|
||||
default-features = false
|
||||
|
||||
[dependencies.slotmap]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.thiserror]
|
||||
version = "1.0.7"
|
||||
|
||||
[dependencies.vec_map]
|
||||
version = "0.8.2"
|
||||
|
||||
[dev-dependencies.futures]
|
||||
version = "0.3.5"
|
||||
|
||||
[features]
|
||||
executor = ["futures-util"]
|
||||
|
||||
[badges.codecov]
|
||||
repository = "Smithay/calloop"
|
||||
19
third-party/vendor/calloop/LICENSE.txt
vendored
Normal file
19
third-party/vendor/calloop/LICENSE.txt
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2018 Victor Berger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
148
third-party/vendor/calloop/README.md
vendored
Normal file
148
third-party/vendor/calloop/README.md
vendored
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
[](https://crates.io/crates/calloop)
|
||||
[](https://docs.rs/calloop)
|
||||
[](https://codecov.io/gh/Smithay/calloop)
|
||||
|
||||
# calloop
|
||||
|
||||
Calloop, a Callback-based Event Loop
|
||||
|
||||
This crate provides an `EventLoop` type, which is a small abstraction
|
||||
over a polling system. The main difference between this crate
|
||||
and other traditional rust event loops is that it is based on callbacks:
|
||||
you can register several event sources, each being associated with a callback
|
||||
closure that will be invoked whenever the associated event source generates
|
||||
events.
|
||||
|
||||
The main target use of this event loop is thus for apps that expect to spend
|
||||
most of their time waiting for events and wishes to do so in a cheap and convenient
|
||||
way. It is not meant for large scale high performance IO.
|
||||
|
||||
### How to use it
|
||||
|
||||
Below is a quick usage example of calloop. For a more in-depth tutorial, see
|
||||
the [calloop book](https://smithay.github.io/calloop).
|
||||
|
||||
For simple uses, you can just add event sources with callbacks to the event
|
||||
loop. For example, here's a runnable program that exits after five seconds:
|
||||
|
||||
```rust
|
||||
use calloop::{timer::{Timer, TimeoutAction}, EventLoop, LoopSignal};
|
||||
|
||||
fn main() {
|
||||
// Create the event loop. The loop is parameterised by the kind of shared
|
||||
// data you want the callbacks to use. In this case, we want to be able to
|
||||
// stop the loop when the timer fires, so we provide the loop with a
|
||||
// LoopSignal, which has the ability to stop the loop from within events. We
|
||||
// just annotate the type here; the actual data is provided later in the
|
||||
// run() call.
|
||||
let mut event_loop: EventLoop<LoopSignal> =
|
||||
EventLoop::try_new().expect("Failed to initialize the event loop!");
|
||||
|
||||
// Retrieve a handle. It is used to insert new sources into the event loop
|
||||
// It can be cloned, allowing you to insert sources from within source
|
||||
// callbacks.
|
||||
let handle = event_loop.handle();
|
||||
|
||||
// Create our event source, a timer, that will expire in 2 seconds
|
||||
let source = Timer::from_duration(std::time::Duration::from_secs(2));
|
||||
|
||||
// Inserting an event source takes this general form. It can also be done
|
||||
// from within the callback of another event source.
|
||||
handle
|
||||
.insert_source(
|
||||
// a type which implements the EventSource trait
|
||||
source,
|
||||
// a callback that is invoked whenever this source generates an event
|
||||
|event, _metadata, shared_data| {
|
||||
// This callback is given 3 values:
|
||||
// - the event generated by the source (in our case, timer events are the Instant
|
||||
// representing the deadline for which it has fired)
|
||||
// - &mut access to some metadata, specific to the event source (in our case, a
|
||||
// timer handle)
|
||||
// - &mut access to the global shared data that was passed to EventLoop::run or
|
||||
// EventLoop::dispatch (in our case, a LoopSignal object to stop the loop)
|
||||
//
|
||||
// The return type is just () because nothing uses it. Some
|
||||
// sources will expect a Result of some kind instead.
|
||||
println!("Timeout for {:?} expired!", event);
|
||||
// notify the event loop to stop running using the signal in the shared data
|
||||
// (see below)
|
||||
shared_data.stop();
|
||||
// The timer event source requires us to return a TimeoutAction to
|
||||
// specify if the timer should be rescheduled. In our case we just drop it.
|
||||
TimeoutAction::Drop
|
||||
},
|
||||
)
|
||||
.expect("Failed to insert event source!");
|
||||
|
||||
// Create the shared data for our loop.
|
||||
let mut shared_data = event_loop.get_signal();
|
||||
|
||||
// Actually run the event loop. This will dispatch received events to their
|
||||
// callbacks, waiting at most 20ms for new events between each invocation of
|
||||
// the provided callback (pass None for the timeout argument if you want to
|
||||
// wait indefinitely between events).
|
||||
//
|
||||
// This is where we pass the *value* of the shared data, as a mutable
|
||||
// reference that will be forwarded to all your callbacks, allowing them to
|
||||
// share some state
|
||||
event_loop
|
||||
.run(
|
||||
std::time::Duration::from_millis(20),
|
||||
&mut shared_data,
|
||||
|_shared_data| {
|
||||
// Finally, this is where you can insert the processing you need
|
||||
// to do do between each waiting event eg. drawing logic if
|
||||
// you're doing a GUI app.
|
||||
},
|
||||
)
|
||||
.expect("Error during event loop!");
|
||||
}
|
||||
```
|
||||
|
||||
### Event source types
|
||||
|
||||
The event loop is backed by an OS provided polling selector (epoll on Linux).
|
||||
|
||||
This crate also provide some adapters for common event sources such as:
|
||||
|
||||
- MPSC channels
|
||||
- Timers
|
||||
- unix signals on Linux
|
||||
|
||||
As well as generic objects backed by file descriptors.
|
||||
|
||||
It is also possible to insert "idle" callbacks. These callbacks represent computations that
|
||||
need to be done at some point, but are not as urgent as processing the events. These callbacks
|
||||
are stored and then executed during `EventLoop::dispatch`, once all events from the sources
|
||||
have been processed.
|
||||
|
||||
### Async/Await compatibility
|
||||
|
||||
`calloop` can be used with futures, both as an executor and for monitoring Async IO.
|
||||
|
||||
Activating the `executor` cargo feature will add the `futures` module, which provides
|
||||
a future executor that can be inserted into an `EventLoop` as yet another `EventSource`.
|
||||
|
||||
IO objects can be made Async-aware via the `LoopHandle::adapt_io` method. Waking up the
|
||||
futures using these objects is handled by the associated `EventLoop` directly.
|
||||
|
||||
### Custom event sources
|
||||
|
||||
You can create custom event sources can will be inserted in the event loop by
|
||||
implementing the `EventSource` trait. This can be done either directly from the file
|
||||
descriptors of your source of interest, or by wrapping an other event source and further
|
||||
processing its events. An `EventSource` can register more than one file descriptor and
|
||||
aggregate them.
|
||||
|
||||
### Platforms support
|
||||
|
||||
Currently, calloop is tested on Linux, FreeBSD and macOS.
|
||||
|
||||
The following platforms are also enabled at compile time but not tested: Android, NetBSD,
|
||||
OpenBSD, DragonFlyBSD.
|
||||
|
||||
Those platforms *should* work based on the fact that they have the same polling mechanism as
|
||||
tested platforms, but some subtle bugs might still occur.
|
||||
|
||||
License: MIT
|
||||
9
third-party/vendor/calloop/README.tpl
vendored
Normal file
9
third-party/vendor/calloop/README.tpl
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[](https://crates.io/crates/calloop)
|
||||
[](https://docs.rs/calloop)
|
||||
{{badges}}
|
||||
|
||||
# {{crate}}
|
||||
|
||||
{{readme}}
|
||||
|
||||
License: {{license}}
|
||||
1
third-party/vendor/calloop/clippy.toml
vendored
Normal file
1
third-party/vendor/calloop/clippy.toml
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
msrv = "1.41"
|
||||
30
third-party/vendor/calloop/examples/high_precision.rs
vendored
Normal file
30
third-party/vendor/calloop/examples/high_precision.rs
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
use std::time::{Duration, Instant};
|
||||
|
||||
use calloop::{
|
||||
timer::{TimeoutAction, Timer},
|
||||
EventLoop,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut event_loop =
|
||||
EventLoop::try_new_high_precision().expect("Failed to initialize the event loop!");
|
||||
|
||||
let before = Instant::now();
|
||||
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(
|
||||
Timer::from_duration(Duration::from_micros(20)),
|
||||
|_, _, _| TimeoutAction::Drop,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
event_loop.dispatch(None, &mut ()).unwrap();
|
||||
|
||||
let elapsed = before.elapsed();
|
||||
|
||||
println!(
|
||||
"The event loop slept for {} microseconds.",
|
||||
elapsed.as_micros()
|
||||
);
|
||||
}
|
||||
75
third-party/vendor/calloop/examples/timer.rs
vendored
Normal file
75
third-party/vendor/calloop/examples/timer.rs
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
use calloop::{
|
||||
timer::{TimeoutAction, Timer},
|
||||
EventLoop, LoopSignal,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
// Create the event loop. The loop is parameterised by the kind of shared
|
||||
// data you want the callbacks to use. In this case, we want to be able to
|
||||
// stop the loop when the timer fires, so we provide the loop with a
|
||||
// LoopSignal, which has the ability to stop the loop from within events. We
|
||||
// just annotate the type here; the actual data is provided later in the
|
||||
// run() call.
|
||||
let mut event_loop: EventLoop<LoopSignal> =
|
||||
EventLoop::try_new().expect("Failed to initialize the event loop!");
|
||||
|
||||
// Retrieve a handle. It is used to insert new sources into the event loop
|
||||
// It can be cloned, allowing you to insert sources from within source
|
||||
// callbacks.
|
||||
let handle = event_loop.handle();
|
||||
|
||||
// Create our event source, a timer, that will expire in 2 seconds
|
||||
let source = Timer::from_duration(std::time::Duration::from_secs(2));
|
||||
|
||||
// Inserting an event source takes this general form. It can also be done
|
||||
// from within the callback of another event source.
|
||||
handle
|
||||
.insert_source(
|
||||
// a type which implements the EventSource trait
|
||||
source,
|
||||
// a callback that is invoked whenever this source generates an event
|
||||
|event, _metadata, shared_data| {
|
||||
// This callback is given 3 values:
|
||||
// - the event generated by the source (in our case, timer events are the Instant
|
||||
// representing the deadline for which it has fired)
|
||||
// - &mut access to some metadata, specific to the event source (in our case, a
|
||||
// timer handle)
|
||||
// - &mut access to the global shared data that was passed to EventLoop::run or
|
||||
// EventLoop::dispatch (in our case, a LoopSignal object to stop the loop)
|
||||
//
|
||||
// The return type is just () because nothing uses it. Some
|
||||
// sources will expect a Result of some kind instead.
|
||||
println!("Timeout for {:?} expired!", event);
|
||||
// notify the event loop to stop running using the signal in the shared data
|
||||
// (see below)
|
||||
shared_data.stop();
|
||||
// The timer event source requires us to return a TimeoutAction to
|
||||
// specify if the timer should be rescheduled. In our case we just drop it.
|
||||
TimeoutAction::Drop
|
||||
},
|
||||
)
|
||||
.expect("Failed to insert event source!");
|
||||
|
||||
// Create the shared data for our loop.
|
||||
let mut shared_data = event_loop.get_signal();
|
||||
|
||||
// Actually run the event loop. This will dispatch received events to their
|
||||
// callbacks, waiting at most 20ms for new events between each invocation of
|
||||
// the provided callback (pass None for the timeout argument if you want to
|
||||
// wait indefinitely between events).
|
||||
//
|
||||
// This is where we pass the *value* of the shared data, as a mutable
|
||||
// reference that will be forwarded to all your callbacks, allowing them to
|
||||
// share some state
|
||||
event_loop
|
||||
.run(
|
||||
std::time::Duration::from_millis(20),
|
||||
&mut shared_data,
|
||||
|_shared_data| {
|
||||
// Finally, this is where you can insert the processing you need
|
||||
// to do do between each waiting event eg. drawing logic if
|
||||
// you're doing a GUI app.
|
||||
},
|
||||
)
|
||||
.expect("Error during event loop!");
|
||||
}
|
||||
91
third-party/vendor/calloop/src/error.rs
vendored
Normal file
91
third-party/vendor/calloop/src/error.rs
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//! Error types used and generated by Calloop.
|
||||
//!
|
||||
//! This module contains error types for Calloop's operations. They are designed
|
||||
//! to make it easy to deal with errors arising from Calloop's internal I/O and
|
||||
//! other operations.
|
||||
//!
|
||||
//! There are two top-level error types:
|
||||
//!
|
||||
//! - [`Error`]: used by callback functions, internal operations, and some event
|
||||
//! loop API calls
|
||||
//!
|
||||
//! - [`InsertError`]: used primarily by the [`insert_source()`] method when an
|
||||
//! event source cannot be added to the loop and needs to be given back to the
|
||||
//! caller
|
||||
//!
|
||||
//! [`insert_source()`]: crate::LoopHandle::insert_source()
|
||||
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
/// The primary error type used by Calloop covering internal errors and I/O
|
||||
/// errors that arise during loop operations such as source registration or
|
||||
/// event dispatching.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
/// When an event source is registered (or re- or un-registered) with the
|
||||
/// event loop, this error variant will occur if the token Calloop uses to
|
||||
/// keep track of the event source is not valid.
|
||||
#[error("invalid token provided to internal function")]
|
||||
InvalidToken,
|
||||
|
||||
/// This variant wraps a [`std::io::Error`], which might arise from
|
||||
/// Calloop's internal operations.
|
||||
#[error("underlying IO error")]
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
/// Any other unexpected error kind (most likely from a user implementation of
|
||||
/// [`EventSource::process_events()`]) will be wrapped in this.
|
||||
///
|
||||
/// [`EventSource::process_events()`]: crate::EventSource::process_events()
|
||||
#[error("other error during loop operation")]
|
||||
OtherError(#[from] Box<dyn std::error::Error + Sync + Send>),
|
||||
}
|
||||
|
||||
impl From<nix::errno::Errno> for Error {
|
||||
/// Converts a [`nix::Error`] into a wrapped version of the equivalent
|
||||
/// [`std::io::Error`].
|
||||
fn from(err: nix::errno::Errno) -> Self {
|
||||
Into::<std::io::Error>::into(err).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for std::io::Error {
|
||||
/// Converts Calloop's error type into a [`std::io::Error`].
|
||||
fn from(err: Error) -> Self {
|
||||
match err {
|
||||
Error::IoError(source) => source,
|
||||
Error::InvalidToken => Self::new(std::io::ErrorKind::InvalidInput, err.to_string()),
|
||||
Error::OtherError(source) => Self::new(std::io::ErrorKind::Other, source),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Result`] alias using Calloop's error type.
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
/// An error generated when trying to insert an event source
|
||||
#[derive(thiserror::Error)]
|
||||
#[error("error inserting event source")]
|
||||
pub struct InsertError<T> {
|
||||
/// The source that could not be inserted
|
||||
pub inserted: T,
|
||||
/// The generated error
|
||||
#[source]
|
||||
pub error: Error,
|
||||
}
|
||||
|
||||
impl<T> Debug for InsertError<T> {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn fmt(&self, formatter: &mut Formatter) -> core::result::Result<(), fmt::Error> {
|
||||
write!(formatter, "{:?}", self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<InsertError<T>> for crate::Error {
|
||||
/// Converts the [`InsertError`] into Calloop's error type, throwing away
|
||||
/// the contained source.
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn from(e: InsertError<T>) -> crate::Error {
|
||||
e.error
|
||||
}
|
||||
}
|
||||
545
third-party/vendor/calloop/src/io.rs
vendored
Normal file
545
third-party/vendor/calloop/src/io.rs
vendored
Normal file
|
|
@ -0,0 +1,545 @@
|
|||
//! Adapters for async IO objects
|
||||
//!
|
||||
//! This module mainly hosts the [`Async`] adapter for making IO objects async with readiness
|
||||
//! monitoring backed by an [`EventLoop`](crate::EventLoop). See [`LoopHandle::adapt_io`] for
|
||||
//! how to create them.
|
||||
//!
|
||||
//! [`LoopHandle::adapt_io`]: crate::LoopHandle#method.adapt_io
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll as TaskPoll, Waker};
|
||||
|
||||
use nix::fcntl::{fcntl, FcntlArg, OFlag};
|
||||
|
||||
#[cfg(feature = "futures-io")]
|
||||
use futures_io::{AsyncRead, AsyncWrite, IoSlice, IoSliceMut};
|
||||
|
||||
use crate::{
|
||||
loop_logic::LoopInner, sources::EventDispatcher, Interest, Mode, Poll, PostAction, Readiness,
|
||||
Token, TokenFactory,
|
||||
};
|
||||
|
||||
/// Adapter for async IO manipulations
|
||||
///
|
||||
/// This type wraps an IO object, providing methods to create futures waiting for its
|
||||
/// readiness.
|
||||
///
|
||||
/// If the `futures-io` cargo feature is enabled, it also implements `AsyncRead` and/or
|
||||
/// `AsyncWrite` if the underlying type implements `Read` and/or `Write`.
|
||||
///
|
||||
/// Note that this adapter and the futures procuded from it and *not* threadsafe.
|
||||
pub struct Async<'l, F: AsRawFd> {
|
||||
fd: Option<F>,
|
||||
dispatcher: Rc<RefCell<IoDispatcher>>,
|
||||
inner: Rc<dyn IoLoopInner + 'l>,
|
||||
old_flags: OFlag,
|
||||
}
|
||||
|
||||
impl<'l, F: AsRawFd + std::fmt::Debug> std::fmt::Debug for Async<'l, F> {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Async").field("fd", &self.fd).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l, F: AsRawFd> Async<'l, F> {
|
||||
pub(crate) fn new<Data>(inner: Rc<LoopInner<'l, Data>>, fd: F) -> crate::Result<Async<'l, F>> {
|
||||
let rawfd = fd.as_raw_fd();
|
||||
// set non-blocking
|
||||
let old_flags = fcntl(rawfd, FcntlArg::F_GETFL)?;
|
||||
let old_flags = unsafe { OFlag::from_bits_unchecked(old_flags) };
|
||||
fcntl(rawfd, FcntlArg::F_SETFL(old_flags | OFlag::O_NONBLOCK))?;
|
||||
// register in the loop
|
||||
let dispatcher = Rc::new(RefCell::new(IoDispatcher {
|
||||
fd: rawfd,
|
||||
token: None,
|
||||
waker: None,
|
||||
is_registered: false,
|
||||
interest: Interest::EMPTY,
|
||||
last_readiness: Readiness::EMPTY,
|
||||
}));
|
||||
let key = inner.sources.borrow_mut().insert(dispatcher.clone());
|
||||
dispatcher.borrow_mut().token = Some(Token { key, sub_id: 0 });
|
||||
inner.register(&dispatcher)?;
|
||||
|
||||
// Straightforward casting would require us to add the bound `Data: 'l` but we don't actually need it
|
||||
// as this module never accesses the dispatch data, so we use transmute to erase it
|
||||
let inner: Rc<dyn IoLoopInner + 'l> =
|
||||
unsafe { std::mem::transmute(inner as Rc<dyn IoLoopInner>) };
|
||||
|
||||
Ok(Async {
|
||||
fd: Some(fd),
|
||||
dispatcher,
|
||||
inner,
|
||||
old_flags,
|
||||
})
|
||||
}
|
||||
|
||||
/// Mutably access the underlying IO object
|
||||
pub fn get_mut(&mut self) -> &mut F {
|
||||
self.fd.as_mut().unwrap()
|
||||
}
|
||||
|
||||
/// A future that resolves once the object becomes ready for reading
|
||||
pub fn readable<'s>(&'s mut self) -> Readable<'s, 'l, F> {
|
||||
Readable { io: self }
|
||||
}
|
||||
|
||||
/// A future that resolves once the object becomes ready for writing
|
||||
pub fn writable<'s>(&'s mut self) -> Writable<'s, 'l, F> {
|
||||
Writable { io: self }
|
||||
}
|
||||
|
||||
/// Remove the async adapter and retrieve the underlying object
|
||||
pub fn into_inner(mut self) -> F {
|
||||
self.fd.take().unwrap()
|
||||
}
|
||||
|
||||
fn readiness(&self) -> Readiness {
|
||||
self.dispatcher.borrow_mut().readiness()
|
||||
}
|
||||
|
||||
fn register_waker(&self, interest: Interest, waker: Waker) -> crate::Result<()> {
|
||||
{
|
||||
let mut disp = self.dispatcher.borrow_mut();
|
||||
disp.interest = interest;
|
||||
disp.waker = Some(waker);
|
||||
}
|
||||
self.inner.reregister(&self.dispatcher)
|
||||
}
|
||||
}
|
||||
|
||||
/// A future that resolves once the associated object becomes ready for reading
|
||||
#[derive(Debug)]
|
||||
pub struct Readable<'s, 'l, F: AsRawFd> {
|
||||
io: &'s mut Async<'l, F>,
|
||||
}
|
||||
|
||||
impl<'s, 'l, F: AsRawFd> std::future::Future for Readable<'s, 'l, F> {
|
||||
type Output = ();
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> TaskPoll<()> {
|
||||
let io = &mut self.as_mut().io;
|
||||
let readiness = io.readiness();
|
||||
if readiness.readable || readiness.error {
|
||||
TaskPoll::Ready(())
|
||||
} else {
|
||||
let _ = io.register_waker(Interest::READ, cx.waker().clone());
|
||||
TaskPoll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A future that resolves once the associated object becomes ready for writing
|
||||
#[derive(Debug)]
|
||||
pub struct Writable<'s, 'l, F: AsRawFd> {
|
||||
io: &'s mut Async<'l, F>,
|
||||
}
|
||||
|
||||
impl<'s, 'l, F: AsRawFd> std::future::Future for Writable<'s, 'l, F> {
|
||||
type Output = ();
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> TaskPoll<()> {
|
||||
let io = &mut self.as_mut().io;
|
||||
let readiness = io.readiness();
|
||||
if readiness.writable || readiness.error {
|
||||
TaskPoll::Ready(())
|
||||
} else {
|
||||
let _ = io.register_waker(Interest::WRITE, cx.waker().clone());
|
||||
TaskPoll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l, F: AsRawFd> Drop for Async<'l, F> {
|
||||
fn drop(&mut self) {
|
||||
self.inner.kill(&self.dispatcher);
|
||||
// restore flags
|
||||
let _ = fcntl(
|
||||
self.dispatcher.borrow().fd,
|
||||
FcntlArg::F_SETFL(self.old_flags),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l, F: AsRawFd> Unpin for Async<'l, F> {}
|
||||
|
||||
trait IoLoopInner {
|
||||
fn register(&self, dispatcher: &RefCell<IoDispatcher>) -> crate::Result<()>;
|
||||
fn reregister(&self, dispatcher: &RefCell<IoDispatcher>) -> crate::Result<()>;
|
||||
fn kill(&self, dispatcher: &RefCell<IoDispatcher>);
|
||||
}
|
||||
|
||||
impl<'l, Data> IoLoopInner for LoopInner<'l, Data> {
|
||||
fn register(&self, dispatcher: &RefCell<IoDispatcher>) -> crate::Result<()> {
|
||||
let disp = dispatcher.borrow();
|
||||
self.poll.borrow_mut().register(
|
||||
disp.fd,
|
||||
Interest::EMPTY,
|
||||
Mode::OneShot,
|
||||
disp.token.expect("No token for IO dispatcher"),
|
||||
)
|
||||
}
|
||||
|
||||
fn reregister(&self, dispatcher: &RefCell<IoDispatcher>) -> crate::Result<()> {
|
||||
let disp = dispatcher.borrow();
|
||||
self.poll.borrow_mut().reregister(
|
||||
disp.fd,
|
||||
disp.interest,
|
||||
Mode::OneShot,
|
||||
disp.token.expect("No token for IO dispatcher"),
|
||||
)
|
||||
}
|
||||
|
||||
fn kill(&self, dispatcher: &RefCell<IoDispatcher>) {
|
||||
let key = dispatcher
|
||||
.borrow()
|
||||
.token
|
||||
.expect("No token for IO dispatcher")
|
||||
.key;
|
||||
let _source = self
|
||||
.sources
|
||||
.borrow_mut()
|
||||
.remove(key)
|
||||
.expect("Attempting to remove a non-existent source?!");
|
||||
}
|
||||
}
|
||||
|
||||
struct IoDispatcher {
|
||||
fd: RawFd,
|
||||
token: Option<Token>,
|
||||
waker: Option<Waker>,
|
||||
is_registered: bool,
|
||||
interest: Interest,
|
||||
last_readiness: Readiness,
|
||||
}
|
||||
|
||||
impl IoDispatcher {
|
||||
fn readiness(&mut self) -> Readiness {
|
||||
std::mem::replace(&mut self.last_readiness, Readiness::EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Data> EventDispatcher<Data> for RefCell<IoDispatcher> {
|
||||
fn process_events(
|
||||
&self,
|
||||
readiness: Readiness,
|
||||
_token: Token,
|
||||
_data: &mut Data,
|
||||
) -> crate::Result<PostAction> {
|
||||
let mut disp = self.borrow_mut();
|
||||
disp.last_readiness = readiness;
|
||||
if let Some(waker) = disp.waker.take() {
|
||||
waker.wake();
|
||||
}
|
||||
Ok(PostAction::Continue)
|
||||
}
|
||||
|
||||
fn register(&self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result<()> {
|
||||
// registration is handled by IoLoopInner
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn reregister(&self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result<bool> {
|
||||
// registration is handled by IoLoopInner
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn unregister(&self, poll: &mut Poll) -> crate::Result<bool> {
|
||||
let disp = self.borrow();
|
||||
if disp.is_registered {
|
||||
poll.unregister(disp.fd)?;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn pre_run(&self, _data: &mut Data) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn post_run(&self, _data: &mut Data) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Async IO trait implementations
|
||||
*/
|
||||
|
||||
#[cfg(feature = "futures-io")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "futures-io")))]
|
||||
impl<'l, F: AsRawFd + std::io::Read> AsyncRead for Async<'l, F> {
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> TaskPoll<std::io::Result<usize>> {
|
||||
match (*self).get_mut().read(buf) {
|
||||
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {}
|
||||
res => return TaskPoll::Ready(res),
|
||||
}
|
||||
self.register_waker(Interest::READ, cx.waker().clone())?;
|
||||
TaskPoll::Pending
|
||||
}
|
||||
|
||||
fn poll_read_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &mut [IoSliceMut<'_>],
|
||||
) -> TaskPoll<std::io::Result<usize>> {
|
||||
match (*self).get_mut().read_vectored(bufs) {
|
||||
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {}
|
||||
res => return TaskPoll::Ready(res),
|
||||
}
|
||||
self.register_waker(Interest::READ, cx.waker().clone())?;
|
||||
TaskPoll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "futures-io")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "futures-io")))]
|
||||
impl<'l, F: AsRawFd + std::io::Write> AsyncWrite for Async<'l, F> {
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> TaskPoll<std::io::Result<usize>> {
|
||||
match (*self).get_mut().write(buf) {
|
||||
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {}
|
||||
res => return TaskPoll::Ready(res),
|
||||
}
|
||||
self.register_waker(Interest::WRITE, cx.waker().clone())?;
|
||||
TaskPoll::Pending
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> TaskPoll<std::io::Result<usize>> {
|
||||
match (*self).get_mut().write_vectored(bufs) {
|
||||
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {}
|
||||
res => return TaskPoll::Ready(res),
|
||||
}
|
||||
self.register_waker(Interest::WRITE, cx.waker().clone())?;
|
||||
TaskPoll::Pending
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> TaskPoll<std::io::Result<()>> {
|
||||
match (*self).get_mut().flush() {
|
||||
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {}
|
||||
res => return TaskPoll::Ready(res),
|
||||
}
|
||||
self.register_waker(Interest::WRITE, cx.waker().clone())?;
|
||||
TaskPoll::Pending
|
||||
}
|
||||
|
||||
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> TaskPoll<std::io::Result<()>> {
|
||||
self.poll_flush(cx)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "executor", feature = "futures-io"))]
|
||||
mod tests {
|
||||
use futures::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
use crate::sources::futures::executor;
|
||||
|
||||
#[test]
|
||||
fn read_write() {
|
||||
let mut event_loop = crate::EventLoop::try_new().unwrap();
|
||||
let handle = event_loop.handle();
|
||||
let (exec, sched) = executor().unwrap();
|
||||
handle
|
||||
.insert_source(exec, move |ret, &mut (), got| {
|
||||
*got = ret;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap();
|
||||
let mut tx = handle.adapt_io(tx).unwrap();
|
||||
let mut rx = handle.adapt_io(rx).unwrap();
|
||||
let received = std::rc::Rc::new(std::cell::Cell::new(false));
|
||||
let fut_received = received.clone();
|
||||
|
||||
sched
|
||||
.schedule(async move {
|
||||
let mut buf = [0; 12];
|
||||
rx.read_exact(&mut buf).await.unwrap();
|
||||
assert_eq!(&buf, b"Hello World!");
|
||||
fut_received.set(true);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// The receiving future alone cannot advance
|
||||
event_loop
|
||||
.dispatch(Some(std::time::Duration::from_millis(10)), &mut ())
|
||||
.unwrap();
|
||||
assert!(!received.get());
|
||||
|
||||
// schedule the writing future as well and wait until finish
|
||||
sched
|
||||
.schedule(async move {
|
||||
tx.write_all(b"Hello World!").await.unwrap();
|
||||
tx.flush().await.unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
while !received.get() {
|
||||
event_loop.dispatch(None, &mut ()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_write_vectored() {
|
||||
let mut event_loop = crate::EventLoop::try_new().unwrap();
|
||||
let handle = event_loop.handle();
|
||||
let (exec, sched) = executor().unwrap();
|
||||
handle
|
||||
.insert_source(exec, move |ret, &mut (), got| {
|
||||
*got = ret;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap();
|
||||
let mut tx = handle.adapt_io(tx).unwrap();
|
||||
let mut rx = handle.adapt_io(rx).unwrap();
|
||||
let received = std::rc::Rc::new(std::cell::Cell::new(false));
|
||||
let fut_received = received.clone();
|
||||
|
||||
sched
|
||||
.schedule(async move {
|
||||
let mut buf = [0; 12];
|
||||
let mut ioslices = buf
|
||||
.chunks_mut(2)
|
||||
.map(std::io::IoSliceMut::new)
|
||||
.collect::<Vec<_>>();
|
||||
let count = rx.read_vectored(&mut ioslices).await.unwrap();
|
||||
assert_eq!(count, 12);
|
||||
assert_eq!(&buf, b"Hello World!");
|
||||
fut_received.set(true);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// The receiving future alone cannot advance
|
||||
event_loop
|
||||
.dispatch(Some(std::time::Duration::from_millis(10)), &mut ())
|
||||
.unwrap();
|
||||
assert!(!received.get());
|
||||
|
||||
// schedule the writing future as well and wait until finish
|
||||
sched
|
||||
.schedule(async move {
|
||||
let buf = b"Hello World!";
|
||||
let ioslices = buf.chunks(2).map(std::io::IoSlice::new).collect::<Vec<_>>();
|
||||
let count = tx.write_vectored(&ioslices).await.unwrap();
|
||||
assert_eq!(count, 12);
|
||||
tx.flush().await.unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
while !received.get() {
|
||||
event_loop.dispatch(None, &mut ()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn readable() {
|
||||
use std::io::Write;
|
||||
|
||||
let mut event_loop = crate::EventLoop::try_new().unwrap();
|
||||
let handle = event_loop.handle();
|
||||
let (exec, sched) = executor().unwrap();
|
||||
handle
|
||||
.insert_source(exec, move |(), &mut (), got| {
|
||||
*got = true;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let (mut tx, rx) = std::os::unix::net::UnixStream::pair().unwrap();
|
||||
|
||||
let mut rx = handle.adapt_io(rx).unwrap();
|
||||
sched
|
||||
.schedule(async move {
|
||||
rx.readable().await;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut dispatched = false;
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(std::time::Duration::from_millis(100)), &mut dispatched)
|
||||
.unwrap();
|
||||
// The socket is not yet readable, so the readable() future has not completed
|
||||
assert!(!dispatched);
|
||||
|
||||
tx.write_all(&[42]).unwrap();
|
||||
tx.flush().unwrap();
|
||||
|
||||
// Now we should become readable
|
||||
while !dispatched {
|
||||
event_loop.dispatch(None, &mut dispatched).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn writable() {
|
||||
use std::io::{BufReader, BufWriter, Read, Write};
|
||||
|
||||
let mut event_loop = crate::EventLoop::try_new().unwrap();
|
||||
let handle = event_loop.handle();
|
||||
let (exec, sched) = executor().unwrap();
|
||||
handle
|
||||
.insert_source(exec, move |(), &mut (), got| {
|
||||
*got = true;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let (mut tx, mut rx) = std::os::unix::net::UnixStream::pair().unwrap();
|
||||
tx.set_nonblocking(true).unwrap();
|
||||
rx.set_nonblocking(true).unwrap();
|
||||
|
||||
// First, fill the socket buffers
|
||||
{
|
||||
let mut writer = BufWriter::new(&mut tx);
|
||||
let data = vec![42u8; 1024];
|
||||
loop {
|
||||
if writer.write(&data).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, wait for it to be readable
|
||||
let mut tx = handle.adapt_io(tx).unwrap();
|
||||
sched
|
||||
.schedule(async move {
|
||||
tx.writable().await;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut dispatched = false;
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(std::time::Duration::from_millis(100)), &mut dispatched)
|
||||
.unwrap();
|
||||
// The socket is not yet writable, so the readable() future has not completed
|
||||
assert!(!dispatched);
|
||||
|
||||
// now read everything
|
||||
{
|
||||
let mut reader = BufReader::new(&mut rx);
|
||||
let mut buffer = vec![0u8; 1024];
|
||||
loop {
|
||||
if reader.read(&mut buffer).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we should become writable
|
||||
while !dispatched {
|
||||
event_loop.dispatch(None, &mut dispatched).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
161
third-party/vendor/calloop/src/lib.rs
vendored
Normal file
161
third-party/vendor/calloop/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
//! Calloop, a Callback-based Event Loop
|
||||
//!
|
||||
//! This crate provides an [`EventLoop`] type, which is a small abstraction
|
||||
//! over a polling system. The main difference between this crate
|
||||
//! and other traditional rust event loops is that it is based on callbacks:
|
||||
//! you can register several event sources, each being associated with a callback
|
||||
//! closure that will be invoked whenever the associated event source generates
|
||||
//! events.
|
||||
//!
|
||||
//! The main target use of this event loop is thus for apps that expect to spend
|
||||
//! most of their time waiting for events and wishes to do so in a cheap and convenient
|
||||
//! way. It is not meant for large scale high performance IO.
|
||||
//!
|
||||
//! ## How to use it
|
||||
//!
|
||||
//! Below is a quick usage example of calloop. For a more in-depth tutorial, see
|
||||
//! the [calloop book](https://smithay.github.io/calloop).
|
||||
//!
|
||||
//! For simple uses, you can just add event sources with callbacks to the event
|
||||
//! loop. For example, here's a runnable program that exits after five seconds:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use calloop::{timer::{Timer, TimeoutAction}, EventLoop, LoopSignal};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! // Create the event loop. The loop is parameterised by the kind of shared
|
||||
//! // data you want the callbacks to use. In this case, we want to be able to
|
||||
//! // stop the loop when the timer fires, so we provide the loop with a
|
||||
//! // LoopSignal, which has the ability to stop the loop from within events. We
|
||||
//! // just annotate the type here; the actual data is provided later in the
|
||||
//! // run() call.
|
||||
//! let mut event_loop: EventLoop<LoopSignal> =
|
||||
//! EventLoop::try_new().expect("Failed to initialize the event loop!");
|
||||
//!
|
||||
//! // Retrieve a handle. It is used to insert new sources into the event loop
|
||||
//! // It can be cloned, allowing you to insert sources from within source
|
||||
//! // callbacks.
|
||||
//! let handle = event_loop.handle();
|
||||
//!
|
||||
//! // Create our event source, a timer, that will expire in 2 seconds
|
||||
//! let source = Timer::from_duration(std::time::Duration::from_secs(2));
|
||||
//!
|
||||
//! // Inserting an event source takes this general form. It can also be done
|
||||
//! // from within the callback of another event source.
|
||||
//! handle
|
||||
//! .insert_source(
|
||||
//! // a type which implements the EventSource trait
|
||||
//! source,
|
||||
//! // a callback that is invoked whenever this source generates an event
|
||||
//! |event, _metadata, shared_data| {
|
||||
//! // This callback is given 3 values:
|
||||
//! // - the event generated by the source (in our case, timer events are the Instant
|
||||
//! // representing the deadline for which it has fired)
|
||||
//! // - &mut access to some metadata, specific to the event source (in our case, a
|
||||
//! // timer handle)
|
||||
//! // - &mut access to the global shared data that was passed to EventLoop::run or
|
||||
//! // EventLoop::dispatch (in our case, a LoopSignal object to stop the loop)
|
||||
//! //
|
||||
//! // The return type is just () because nothing uses it. Some
|
||||
//! // sources will expect a Result of some kind instead.
|
||||
//! println!("Timeout for {:?} expired!", event);
|
||||
//! // notify the event loop to stop running using the signal in the shared data
|
||||
//! // (see below)
|
||||
//! shared_data.stop();
|
||||
//! // The timer event source requires us to return a TimeoutAction to
|
||||
//! // specify if the timer should be rescheduled. In our case we just drop it.
|
||||
//! TimeoutAction::Drop
|
||||
//! },
|
||||
//! )
|
||||
//! .expect("Failed to insert event source!");
|
||||
//!
|
||||
//! // Create the shared data for our loop.
|
||||
//! let mut shared_data = event_loop.get_signal();
|
||||
//!
|
||||
//! // Actually run the event loop. This will dispatch received events to their
|
||||
//! // callbacks, waiting at most 20ms for new events between each invocation of
|
||||
//! // the provided callback (pass None for the timeout argument if you want to
|
||||
//! // wait indefinitely between events).
|
||||
//! //
|
||||
//! // This is where we pass the *value* of the shared data, as a mutable
|
||||
//! // reference that will be forwarded to all your callbacks, allowing them to
|
||||
//! // share some state
|
||||
//! event_loop
|
||||
//! .run(
|
||||
//! std::time::Duration::from_millis(20),
|
||||
//! &mut shared_data,
|
||||
//! |_shared_data| {
|
||||
//! // Finally, this is where you can insert the processing you need
|
||||
//! // to do do between each waiting event eg. drawing logic if
|
||||
//! // you're doing a GUI app.
|
||||
//! },
|
||||
//! )
|
||||
//! .expect("Error during event loop!");
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Event source types
|
||||
//!
|
||||
//! The event loop is backed by an OS provided polling selector (epoll on Linux).
|
||||
//!
|
||||
//! This crate also provide some adapters for common event sources such as:
|
||||
//!
|
||||
//! - [MPSC channels](channel)
|
||||
//! - [Timers](timer)
|
||||
//! - [unix signals](signals) on Linux
|
||||
//!
|
||||
//! As well as generic objects backed by file descriptors.
|
||||
//!
|
||||
//! It is also possible to insert "idle" callbacks. These callbacks represent computations that
|
||||
//! need to be done at some point, but are not as urgent as processing the events. These callbacks
|
||||
//! are stored and then executed during [`EventLoop::dispatch`](EventLoop#method.dispatch), once all
|
||||
//! events from the sources have been processed.
|
||||
//!
|
||||
//! ## Async/Await compatibility
|
||||
//!
|
||||
//! `calloop` can be used with futures, both as an executor and for monitoring Async IO.
|
||||
//!
|
||||
//! Activating the `executor` cargo feature will add the [`futures`] module, which provides
|
||||
//! a future executor that can be inserted into an [`EventLoop`] as yet another [`EventSource`].
|
||||
//!
|
||||
//! IO objects can be made Async-aware via the [`LoopHandle::adapt_io`](LoopHandle#method.adapt_io)
|
||||
//! method. Waking up the futures using these objects is handled by the associated [`EventLoop`]
|
||||
//! directly.
|
||||
//!
|
||||
//! ## Custom event sources
|
||||
//!
|
||||
//! You can create custom event sources can will be inserted in the event loop by
|
||||
//! implementing the [`EventSource`] trait. This can be done either directly from the file
|
||||
//! descriptors of your source of interest, or by wrapping an other event source and further
|
||||
//! processing its events. An [`EventSource`] can register more than one file descriptor and
|
||||
//! aggregate them.
|
||||
//!
|
||||
//! ## Platforms support
|
||||
//!
|
||||
//! Currently, calloop is tested on Linux, FreeBSD and macOS.
|
||||
//!
|
||||
//! The following platforms are also enabled at compile time but not tested: Android, NetBSD,
|
||||
//! OpenBSD, DragonFlyBSD.
|
||||
//!
|
||||
//! Those platforms *should* work based on the fact that they have the same polling mechanism as
|
||||
//! tested platforms, but some subtle bugs might still occur.
|
||||
|
||||
#![warn(missing_docs, missing_debug_implementations)]
|
||||
#![allow(clippy::needless_doctest_main)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(coverage, feature(no_coverage))]
|
||||
|
||||
mod sys;
|
||||
|
||||
pub use sys::{Interest, Mode, Poll, Readiness, Token, TokenFactory};
|
||||
|
||||
pub use self::loop_logic::{EventLoop, LoopHandle, LoopSignal, RegistrationToken};
|
||||
pub use self::sources::*;
|
||||
|
||||
pub mod error;
|
||||
pub use error::{Error, InsertError, Result};
|
||||
|
||||
pub mod io;
|
||||
mod loop_logic;
|
||||
mod macros;
|
||||
mod sources;
|
||||
913
third-party/vendor/calloop/src/loop_logic.rs
vendored
Normal file
913
third-party/vendor/calloop/src/loop_logic.rs
vendored
Normal file
|
|
@ -0,0 +1,913 @@
|
|||
use std::cell::{Cell, RefCell};
|
||||
use std::fmt::Debug;
|
||||
use std::io;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use slotmap::SlotMap;
|
||||
|
||||
use crate::sources::{Dispatcher, EventSource, Idle, IdleDispatcher};
|
||||
use crate::{EventDispatcher, InsertError, Poll, PostAction, TokenFactory};
|
||||
|
||||
type IdleCallback<'i, Data> = Rc<RefCell<dyn IdleDispatcher<Data> + 'i>>;
|
||||
|
||||
slotmap::new_key_type! {
|
||||
pub(crate) struct CalloopKey;
|
||||
}
|
||||
|
||||
/// A token representing a registration in the [`EventLoop`].
|
||||
///
|
||||
/// This token is given to you by the [`EventLoop`] when an [`EventSource`] is inserted or
|
||||
/// a [`Dispatcher`] is registered. You can use it to [disable](LoopHandle#method.disable),
|
||||
/// [enable](LoopHandle#method.enable), [update`](LoopHandle#method.update),
|
||||
/// [remove](LoopHandle#method.remove) or [kill](LoopHandle#method.kill) it.
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub struct RegistrationToken {
|
||||
key: CalloopKey,
|
||||
}
|
||||
|
||||
pub(crate) struct LoopInner<'l, Data> {
|
||||
pub(crate) poll: RefCell<Poll>,
|
||||
pub(crate) sources: RefCell<SlotMap<CalloopKey, Rc<dyn EventDispatcher<Data> + 'l>>>,
|
||||
idles: RefCell<Vec<IdleCallback<'l, Data>>>,
|
||||
pending_action: Cell<PostAction>,
|
||||
}
|
||||
|
||||
/// An handle to an event loop
|
||||
///
|
||||
/// This handle allows you to insert new sources and idles in this event loop,
|
||||
/// it can be cloned, and it is possible to insert new sources from within a source
|
||||
/// callback.
|
||||
pub struct LoopHandle<'l, Data> {
|
||||
inner: Rc<LoopInner<'l, Data>>,
|
||||
}
|
||||
|
||||
impl<'l, Data> std::fmt::Debug for LoopHandle<'l, Data> {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("LoopHandle { ... }")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l, Data> Clone for LoopHandle<'l, Data> {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn clone(&self) -> Self {
|
||||
LoopHandle {
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l, Data> LoopHandle<'l, Data> {
|
||||
/// Inserts a new event source in the loop.
|
||||
///
|
||||
/// The provided callback will be called during the dispatching cycles whenever the
|
||||
/// associated source generates events, see `EventLoop::dispatch(..)` for details.
|
||||
///
|
||||
/// This function takes ownership of the event source. Use `register_dispatcher`
|
||||
/// if you need access to the event source after this call.
|
||||
pub fn insert_source<S, F>(
|
||||
&self,
|
||||
source: S,
|
||||
callback: F,
|
||||
) -> Result<RegistrationToken, InsertError<S>>
|
||||
where
|
||||
S: EventSource + 'l,
|
||||
F: FnMut(S::Event, &mut S::Metadata, &mut Data) -> S::Ret + 'l,
|
||||
{
|
||||
let dispatcher = Dispatcher::new(source, callback);
|
||||
self.register_dispatcher(dispatcher.clone())
|
||||
.map_err(|error| InsertError {
|
||||
error,
|
||||
inserted: dispatcher.into_source_inner(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Registers a `Dispatcher` in the loop.
|
||||
///
|
||||
/// Use this function if you need access to the event source after its insertion in the loop.
|
||||
///
|
||||
/// See also `insert_source`.
|
||||
pub fn register_dispatcher<S>(
|
||||
&self,
|
||||
dispatcher: Dispatcher<'l, S, Data>,
|
||||
) -> crate::Result<RegistrationToken>
|
||||
where
|
||||
S: EventSource + 'l,
|
||||
{
|
||||
let mut sources = self.inner.sources.borrow_mut();
|
||||
let mut poll = self.inner.poll.borrow_mut();
|
||||
|
||||
let key = sources.insert(dispatcher.clone_as_event_dispatcher());
|
||||
let ret = sources
|
||||
.get(key)
|
||||
.unwrap()
|
||||
.register(&mut *poll, &mut TokenFactory::new(key));
|
||||
|
||||
if let Err(error) = ret {
|
||||
sources.remove(key).expect("Source was just inserted?!");
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
Ok(RegistrationToken { key })
|
||||
}
|
||||
|
||||
/// Inserts an idle callback.
|
||||
///
|
||||
/// This callback will be called during a dispatching cycle when the event loop has
|
||||
/// finished processing all pending events from the sources and becomes idle.
|
||||
pub fn insert_idle<'i, F: FnOnce(&mut Data) + 'l + 'i>(&self, callback: F) -> Idle<'i> {
|
||||
let mut opt_cb = Some(callback);
|
||||
let callback = Rc::new(RefCell::new(Some(move |data: &mut Data| {
|
||||
if let Some(cb) = opt_cb.take() {
|
||||
cb(data);
|
||||
}
|
||||
})));
|
||||
self.inner.idles.borrow_mut().push(callback.clone());
|
||||
Idle { callback }
|
||||
}
|
||||
|
||||
/// Enables this previously disabled event source.
|
||||
///
|
||||
/// This previously disabled source will start generating events again.
|
||||
///
|
||||
/// **Note:** this cannot be done from within the source callback.
|
||||
pub fn enable(&self, token: &RegistrationToken) -> crate::Result<()> {
|
||||
if let Some(source) = self.inner.sources.borrow().get(token.key) {
|
||||
source.register(
|
||||
&mut *self.inner.poll.borrow_mut(),
|
||||
&mut TokenFactory::new(token.key),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Makes this source update its registration.
|
||||
///
|
||||
/// If after accessing the source you changed its parameters in a way that requires
|
||||
/// updating its registration.
|
||||
pub fn update(&self, token: &RegistrationToken) -> crate::Result<()> {
|
||||
if let Some(source) = self.inner.sources.borrow().get(token.key) {
|
||||
if !source.reregister(
|
||||
&mut *self.inner.poll.borrow_mut(),
|
||||
&mut TokenFactory::new(token.key),
|
||||
)? {
|
||||
// we are in a callback, store for later processing
|
||||
self.inner.pending_action.set(PostAction::Reregister);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Disables this event source.
|
||||
///
|
||||
/// The source remains in the event loop, but it'll no longer generate events
|
||||
pub fn disable(&self, token: &RegistrationToken) -> crate::Result<()> {
|
||||
if let Some(source) = self.inner.sources.borrow().get(token.key) {
|
||||
if !source.unregister(&mut *self.inner.poll.borrow_mut())? {
|
||||
// we are in a callback, store for later processing
|
||||
self.inner.pending_action.set(PostAction::Disable);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes this source from the event loop.
|
||||
pub fn remove(&self, token: RegistrationToken) {
|
||||
if let Some(source) = self.inner.sources.borrow_mut().remove(token.key) {
|
||||
if let Err(e) = source.unregister(&mut self.inner.poll.borrow_mut()) {
|
||||
log::warn!(
|
||||
"[calloop] Failed to unregister source from the polling system: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap an IO object into an async adapter
|
||||
///
|
||||
/// This adapter turns the IO object into an async-aware one that can be used in futures.
|
||||
/// The readiness of these futures will be driven by the event loop.
|
||||
///
|
||||
/// The produced futures can be polled in any executor, and notably the one provided by
|
||||
/// calloop.
|
||||
pub fn adapt_io<F: AsRawFd>(&self, fd: F) -> crate::Result<crate::io::Async<'l, F>> {
|
||||
crate::io::Async::new(self.inner.clone(), fd)
|
||||
}
|
||||
}
|
||||
|
||||
/// An event loop
|
||||
///
|
||||
/// This loop can host several event sources, that can be dynamically added or removed.
|
||||
pub struct EventLoop<'l, Data> {
|
||||
handle: LoopHandle<'l, Data>,
|
||||
stop_signal: Arc<AtomicBool>,
|
||||
ping: crate::sources::ping::Ping,
|
||||
}
|
||||
|
||||
impl<'l, Data> std::fmt::Debug for EventLoop<'l, Data> {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("EventLoop { ... }")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l, Data> EventLoop<'l, Data> {
|
||||
/// Create a new event loop
|
||||
///
|
||||
/// Fails if the initialization of the polling system failed.
|
||||
pub fn try_new() -> crate::Result<Self> {
|
||||
Self::inner_new(false)
|
||||
}
|
||||
|
||||
/// Create a new event loop in high precision mode
|
||||
///
|
||||
/// On some platforms it requires to setup more resources to enable high-precision
|
||||
/// (sub millisecond) capabilities, so you should use this constructor if you need
|
||||
/// this kind of precision.
|
||||
///
|
||||
/// Fails if the initialization of the polling system failed.
|
||||
pub fn try_new_high_precision() -> crate::Result<Self> {
|
||||
Self::inner_new(true)
|
||||
}
|
||||
|
||||
fn inner_new(high_precision: bool) -> crate::Result<Self> {
|
||||
let poll = Poll::new(high_precision)?;
|
||||
let handle = LoopHandle {
|
||||
inner: Rc::new(LoopInner {
|
||||
poll: RefCell::new(poll),
|
||||
sources: RefCell::new(SlotMap::with_key()),
|
||||
idles: RefCell::new(Vec::new()),
|
||||
pending_action: Cell::new(PostAction::Continue),
|
||||
}),
|
||||
};
|
||||
let (ping, ping_source) = crate::sources::ping::make_ping()?;
|
||||
handle.insert_source(ping_source, |_, _, _| {})?;
|
||||
Ok(EventLoop {
|
||||
handle,
|
||||
stop_signal: Arc::new(AtomicBool::new(false)),
|
||||
ping,
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve a loop handle
|
||||
pub fn handle(&self) -> LoopHandle<'l, Data> {
|
||||
self.handle.clone()
|
||||
}
|
||||
|
||||
fn dispatch_events(
|
||||
&mut self,
|
||||
mut timeout: Option<Duration>,
|
||||
data: &mut Data,
|
||||
) -> crate::Result<()> {
|
||||
let now = Instant::now();
|
||||
let events = {
|
||||
let mut poll = self.handle.inner.poll.borrow_mut();
|
||||
loop {
|
||||
let result = poll.poll(timeout);
|
||||
|
||||
match result {
|
||||
Ok(events) => break events,
|
||||
Err(crate::Error::IoError(err)) if err.kind() == io::ErrorKind::Interrupted => {
|
||||
// Interrupted by a signal. Update timeout and retry.
|
||||
if let Some(to) = timeout {
|
||||
let elapsed = now.elapsed();
|
||||
if elapsed >= to {
|
||||
return Ok(());
|
||||
} else {
|
||||
timeout = Some(to - elapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
for event in events {
|
||||
let opt_disp = self
|
||||
.handle
|
||||
.inner
|
||||
.sources
|
||||
.borrow()
|
||||
.get(event.token.key)
|
||||
.cloned();
|
||||
|
||||
if let Some(disp) = opt_disp {
|
||||
let mut ret = disp.process_events(event.readiness, event.token, data)?;
|
||||
|
||||
// if the returned PostAction is Continue, it may be overwritten by an user-specified pending action
|
||||
let pending_action = self
|
||||
.handle
|
||||
.inner
|
||||
.pending_action
|
||||
.replace(PostAction::Continue);
|
||||
if let PostAction::Continue = ret {
|
||||
ret = pending_action;
|
||||
}
|
||||
|
||||
match ret {
|
||||
PostAction::Reregister => {
|
||||
disp.reregister(
|
||||
&mut self.handle.inner.poll.borrow_mut(),
|
||||
&mut TokenFactory::new(event.token.key),
|
||||
)?;
|
||||
}
|
||||
PostAction::Disable => {
|
||||
disp.unregister(&mut self.handle.inner.poll.borrow_mut())?;
|
||||
}
|
||||
PostAction::Remove => {
|
||||
// delete the source from the list, it'll be cleaned up with the if just below
|
||||
self.handle
|
||||
.inner
|
||||
.sources
|
||||
.borrow_mut()
|
||||
.remove(event.token.key);
|
||||
}
|
||||
PostAction::Continue => {}
|
||||
}
|
||||
|
||||
if !self
|
||||
.handle
|
||||
.inner
|
||||
.sources
|
||||
.borrow()
|
||||
.contains_key(event.token.key)
|
||||
{
|
||||
// the source has been removed from within its callback, unregister it
|
||||
let mut poll = self.handle.inner.poll.borrow_mut();
|
||||
if let Err(e) = disp.unregister(&mut *poll) {
|
||||
log::warn!(
|
||||
"[calloop] Failed to unregister source from the polling system: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::warn!(
|
||||
"[calloop] Received an event for non-existence source: {:?}",
|
||||
event.token.key
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dispatch_idles(&mut self, data: &mut Data) {
|
||||
let idles = std::mem::take(&mut *self.handle.inner.idles.borrow_mut());
|
||||
for idle in idles {
|
||||
idle.borrow_mut().dispatch(data);
|
||||
}
|
||||
}
|
||||
|
||||
fn invoke_pre_run(&self, data: &mut Data) -> crate::Result<()> {
|
||||
let sources = self
|
||||
.handle
|
||||
.inner
|
||||
.sources
|
||||
.borrow()
|
||||
.values()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for source in sources {
|
||||
source.pre_run(data)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn invoke_post_run(&self, data: &mut Data) -> crate::Result<()> {
|
||||
let sources = self
|
||||
.handle
|
||||
.inner
|
||||
.sources
|
||||
.borrow()
|
||||
.values()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for source in sources {
|
||||
source.post_run(data)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Dispatch pending events to their callbacks
|
||||
///
|
||||
/// If some sources have events available, their callbacks will be immediatly called.
|
||||
/// Otherwise this will wait until an event is receive or the provided `timeout`
|
||||
/// is reached. If `timeout` is `None`, it will wait without a duration limit.
|
||||
///
|
||||
/// Once pending events have been processed or the timeout is reached, all pending
|
||||
/// idle callbacks will be fired before this method returns.
|
||||
pub fn dispatch<D: Into<Option<Duration>>>(
|
||||
&mut self,
|
||||
timeout: D,
|
||||
data: &mut Data,
|
||||
) -> crate::Result<()> {
|
||||
self.invoke_pre_run(data)?;
|
||||
self.dispatch_events(timeout.into(), data)?;
|
||||
self.dispatch_idles(data);
|
||||
self.invoke_post_run(data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get a signal to stop this event loop from running
|
||||
///
|
||||
/// To be used in conjunction with the `run()` method.
|
||||
pub fn get_signal(&self) -> LoopSignal {
|
||||
LoopSignal {
|
||||
signal: self.stop_signal.clone(),
|
||||
ping: self.ping.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Run this event loop
|
||||
///
|
||||
/// This will repeatedly try to dispatch events (see the `dispatch()` method) on
|
||||
/// this event loop, waiting at most `timeout` every time.
|
||||
///
|
||||
/// Between each dispatch wait, your provided callback will be called.
|
||||
///
|
||||
/// You can use the `get_signal()` method to retrieve a way to stop or wakeup
|
||||
/// the event loop from anywhere.
|
||||
pub fn run<F, D: Into<Option<Duration>>>(
|
||||
&mut self,
|
||||
timeout: D,
|
||||
data: &mut Data,
|
||||
mut cb: F,
|
||||
) -> crate::Result<()>
|
||||
where
|
||||
F: FnMut(&mut Data),
|
||||
{
|
||||
let timeout = timeout.into();
|
||||
self.stop_signal.store(false, Ordering::Release);
|
||||
self.invoke_pre_run(data)?;
|
||||
while !self.stop_signal.load(Ordering::Acquire) {
|
||||
self.dispatch_events(timeout, data)?;
|
||||
self.dispatch_idles(data);
|
||||
cb(data);
|
||||
}
|
||||
self.invoke_post_run(data)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A signal that can be shared between thread to stop or wakeup a running
|
||||
/// event loop
|
||||
#[derive(Clone)]
|
||||
pub struct LoopSignal {
|
||||
signal: Arc<AtomicBool>,
|
||||
ping: crate::sources::ping::Ping,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for LoopSignal {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("LoopSignal { ... }")
|
||||
}
|
||||
}
|
||||
|
||||
impl LoopSignal {
|
||||
/// Stop the event loop
|
||||
///
|
||||
/// Once this method is called, the next time the event loop has finished
|
||||
/// waiting for events, it will return rather than starting to wait again.
|
||||
///
|
||||
/// This is only usefull if you are using the `EventLoop::run()` method.
|
||||
pub fn stop(&self) {
|
||||
self.signal.store(true, Ordering::Release);
|
||||
}
|
||||
|
||||
/// Wake up the event loop
|
||||
///
|
||||
/// This sends a dummy event to the event loop to simulate the reception
|
||||
/// of an event, making the wait return early. Called after `stop()`, this
|
||||
/// ensures the event loop will terminate quickly if you specified a long
|
||||
/// timeout (or no timeout at all) to the `dispatch` or `run` method.
|
||||
pub fn wakeup(&self) {
|
||||
self.ping.ping();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{
|
||||
generic::Generic, ping::*, Dispatcher, Interest, Mode, Poll, PostAction, Readiness,
|
||||
RegistrationToken, Token, TokenFactory,
|
||||
};
|
||||
|
||||
use super::EventLoop;
|
||||
|
||||
#[test]
|
||||
fn dispatch_idle() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let mut dispatched = false;
|
||||
|
||||
event_loop.handle().insert_idle(|d| {
|
||||
*d = true;
|
||||
});
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::ZERO), &mut dispatched)
|
||||
.unwrap();
|
||||
|
||||
assert!(dispatched);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_idle() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let mut dispatched = false;
|
||||
|
||||
let handle = event_loop.handle();
|
||||
let idle = handle.insert_idle(move |d| {
|
||||
*d = true;
|
||||
});
|
||||
|
||||
idle.cancel();
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
|
||||
assert!(!dispatched);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wakeup() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let signal = event_loop.get_signal();
|
||||
|
||||
::std::thread::spawn(move || {
|
||||
::std::thread::sleep(Duration::from_millis(500));
|
||||
signal.wakeup();
|
||||
});
|
||||
|
||||
// the test should return
|
||||
event_loop.dispatch(None, &mut ()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wakeup_stop() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let signal = event_loop.get_signal();
|
||||
|
||||
::std::thread::spawn(move || {
|
||||
::std::thread::sleep(Duration::from_millis(500));
|
||||
signal.stop();
|
||||
signal.wakeup();
|
||||
});
|
||||
|
||||
// the test should return
|
||||
event_loop.run(None, &mut (), |_| {}).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_bad_source() {
|
||||
let event_loop = EventLoop::<()>::try_new().unwrap();
|
||||
let ret = event_loop.handle().insert_source(
|
||||
crate::sources::generic::Generic::new(420, Interest::READ, Mode::Level),
|
||||
|_, _, _| Ok(PostAction::Continue),
|
||||
);
|
||||
assert!(ret.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_source_no_interest() {
|
||||
let event_loop = EventLoop::<()>::try_new().unwrap();
|
||||
let ret = event_loop.handle().insert_source(
|
||||
crate::sources::generic::Generic::new(0, Interest::EMPTY, Mode::Level),
|
||||
|_, _, _| Ok(PostAction::Continue),
|
||||
);
|
||||
assert!(ret.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disarm_rearm() {
|
||||
let mut event_loop = EventLoop::<bool>::try_new().unwrap();
|
||||
let (ping, ping_source) = make_ping().unwrap();
|
||||
|
||||
let ping_token = event_loop
|
||||
.handle()
|
||||
.insert_source(ping_source, |(), &mut (), dispatched| {
|
||||
*dispatched = true;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
ping.ping();
|
||||
let mut dispatched = false;
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(dispatched);
|
||||
|
||||
// disable the source
|
||||
ping.ping();
|
||||
event_loop.handle().disable(&ping_token).unwrap();
|
||||
let mut dispatched = false;
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(!dispatched);
|
||||
|
||||
// disabling it again is an error
|
||||
event_loop.handle().disable(&ping_token).unwrap_err();
|
||||
|
||||
// reenable it, the previous ping now gets dispatched
|
||||
event_loop.handle().enable(&ping_token).unwrap();
|
||||
let mut dispatched = false;
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(dispatched);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_tokens() {
|
||||
struct DoubleSource {
|
||||
ping1: PingSource,
|
||||
ping2: PingSource,
|
||||
}
|
||||
|
||||
impl crate::EventSource for DoubleSource {
|
||||
type Event = u32;
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
type Error = PingError;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: Readiness,
|
||||
token: Token,
|
||||
mut callback: F,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
self.ping1
|
||||
.process_events(readiness, token, |(), &mut ()| callback(1, &mut ()))?;
|
||||
self.ping2
|
||||
.process_events(readiness, token, |(), &mut ()| callback(2, &mut ()))?;
|
||||
Ok(PostAction::Continue)
|
||||
}
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
poll: &mut Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.ping1.register(poll, token_factory)?;
|
||||
self.ping2.register(poll, token_factory)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.ping1.reregister(poll, token_factory)?;
|
||||
self.ping2.reregister(poll, token_factory)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
|
||||
self.ping1.unregister(poll)?;
|
||||
self.ping2.unregister(poll)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut event_loop = EventLoop::<u32>::try_new().unwrap();
|
||||
|
||||
let (ping1, source1) = make_ping().unwrap();
|
||||
let (ping2, source2) = make_ping().unwrap();
|
||||
|
||||
let source = DoubleSource {
|
||||
ping1: source1,
|
||||
ping2: source2,
|
||||
};
|
||||
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(source, |i, _, d| {
|
||||
eprintln!("Dispatching {}", i);
|
||||
*d += i
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut dispatched = 0;
|
||||
ping1.ping();
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert_eq!(dispatched, 1);
|
||||
|
||||
dispatched = 0;
|
||||
ping2.ping();
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert_eq!(dispatched, 2);
|
||||
|
||||
dispatched = 0;
|
||||
ping1.ping();
|
||||
ping2.ping();
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert_eq!(dispatched, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_interests() {
|
||||
use nix::sys::socket::{recv, socketpair, AddressFamily, MsgFlags, SockFlag, SockType};
|
||||
use nix::unistd::write;
|
||||
let mut event_loop = EventLoop::<bool>::try_new().unwrap();
|
||||
|
||||
let (sock1, sock2) = socketpair(
|
||||
AddressFamily::Unix,
|
||||
SockType::Stream,
|
||||
None,
|
||||
SockFlag::empty(), // recv with DONTWAIT will suffice for platforms without SockFlag::SOCK_NONBLOCKING such as macOS
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let source = Generic::new(sock1, Interest::READ, Mode::Level);
|
||||
let dispatcher = Dispatcher::new(source, |_, &mut fd, dispatched| {
|
||||
*dispatched = true;
|
||||
// read all contents available to drain the socket
|
||||
let mut buf = [0u8; 32];
|
||||
loop {
|
||||
match recv(fd, &mut buf, MsgFlags::MSG_DONTWAIT) {
|
||||
Ok(0) => break, // closed pipe, we are now inert
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
let e: std::io::Error = e.into();
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
// nothing more to read
|
||||
} else {
|
||||
// propagate error
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(PostAction::Continue)
|
||||
});
|
||||
|
||||
let sock_token_1 = event_loop
|
||||
.handle()
|
||||
.register_dispatcher(dispatcher.clone())
|
||||
.unwrap();
|
||||
|
||||
// first dispatch, nothing is readable
|
||||
let mut dispatched = false;
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(!dispatched);
|
||||
|
||||
// write something, the socket becomes readable
|
||||
write(sock2, &[1, 2, 3]).unwrap();
|
||||
dispatched = false;
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(dispatched);
|
||||
|
||||
// All has been read, no longer readable
|
||||
dispatched = false;
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(!dispatched);
|
||||
|
||||
// change the interests for writability instead
|
||||
dispatcher.as_source_mut().interest = Interest::WRITE;
|
||||
event_loop.handle().update(&sock_token_1).unwrap();
|
||||
|
||||
// the socket is writable
|
||||
dispatched = false;
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(dispatched);
|
||||
|
||||
// change back to readable
|
||||
dispatcher.as_source_mut().interest = Interest::READ;
|
||||
event_loop.handle().update(&sock_token_1).unwrap();
|
||||
|
||||
// the socket is not readable
|
||||
dispatched = false;
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(!dispatched);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kill_source() {
|
||||
let mut event_loop = EventLoop::<Option<RegistrationToken>>::try_new().unwrap();
|
||||
|
||||
let handle = event_loop.handle();
|
||||
let (ping, ping_source) = make_ping().unwrap();
|
||||
let ping_token = event_loop
|
||||
.handle()
|
||||
.insert_source(ping_source, move |(), &mut (), opt_src| {
|
||||
if let Some(src) = opt_src.take() {
|
||||
handle.remove(src);
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
ping.ping();
|
||||
|
||||
let mut opt_src = Some(ping_token);
|
||||
|
||||
event_loop.dispatch(Duration::ZERO, &mut opt_src).unwrap();
|
||||
|
||||
assert!(opt_src.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_static_data() {
|
||||
use std::sync::mpsc;
|
||||
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
|
||||
{
|
||||
struct RefSender<'a>(&'a mpsc::Sender<()>);
|
||||
let mut ref_sender = RefSender(&sender);
|
||||
|
||||
let mut event_loop = EventLoop::<RefSender<'_>>::try_new().unwrap();
|
||||
let (ping, ping_source) = make_ping().unwrap();
|
||||
let _ping_token = event_loop
|
||||
.handle()
|
||||
.insert_source(ping_source, |_, _, ref_sender| {
|
||||
ref_sender.0.send(()).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
ping.ping();
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut ref_sender)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
receiver.recv().unwrap();
|
||||
// sender still usable (e.g. for another EventLoop)
|
||||
drop(sender);
|
||||
}
|
||||
|
||||
// A dummy EventSource to test insertion and removal of sources
|
||||
struct DummySource;
|
||||
|
||||
impl crate::EventSource for DummySource {
|
||||
type Event = ();
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
type Error = crate::Error;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
_: Readiness,
|
||||
_: Token,
|
||||
mut callback: F,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
callback((), &mut ());
|
||||
Ok(PostAction::Continue)
|
||||
}
|
||||
|
||||
fn register(&mut self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reregister(&mut self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister(&mut self, _: &mut Poll) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
223
third-party/vendor/calloop/src/macros.rs
vendored
Normal file
223
third-party/vendor/calloop/src/macros.rs
vendored
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
//! Macros for helping with common operations in Calloop.
|
||||
|
||||
/// Register a set of event sources. Effectively calls
|
||||
/// [`EventSource::register()`] for all the sources provided.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
|
||||
/// calloop::batch_register!(
|
||||
/// poll, token_factory,
|
||||
/// self.source_one,
|
||||
/// self.source_two,
|
||||
/// self.source_three,
|
||||
/// self.source_four,
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// Note that there is no scope for customisation; if you need to do special
|
||||
/// things with a particular source, you'll need to leave it off the list. Also
|
||||
/// note that this only does try-or-early-return error handling in the order
|
||||
/// that you list the sources; if you need anything else, don't use this macro.
|
||||
///
|
||||
/// [`EventSource::register()`]: crate::EventSource::register()
|
||||
#[macro_export]
|
||||
macro_rules! batch_register {
|
||||
($poll:ident, $token_fac:ident, $( $source:expr ),* $(,)?) => {
|
||||
{
|
||||
$(
|
||||
$source.register($poll, $token_fac)?;
|
||||
)*
|
||||
$crate::Result::<_>::Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Reregister a set of event sources. Effectively calls
|
||||
/// [`EventSource::reregister()`] for all the sources provided.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
|
||||
/// calloop::batch_reregister!(
|
||||
/// poll, token_factory,
|
||||
/// self.source_one,
|
||||
/// self.source_two,
|
||||
/// self.source_three,
|
||||
/// self.source_four,
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// Note that there is no scope for customisation; if you need to do special
|
||||
/// things with a particular source, you'll need to leave it off the list. Also
|
||||
/// note that this only does try-or-early-return error handling in the order
|
||||
/// that you list the sources; if you need anything else, don't use this macro.
|
||||
///
|
||||
/// [`EventSource::reregister()`]: crate::EventSource::reregister()
|
||||
#[macro_export]
|
||||
macro_rules! batch_reregister {
|
||||
($poll:ident, $token_fac:ident, $( $source:expr ),* $(,)?) => {
|
||||
{
|
||||
$(
|
||||
$source.reregister($poll, $token_fac)?;
|
||||
)*
|
||||
$crate::Result::<_>::Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Unregister a set of event sources. Effectively calls
|
||||
/// [`EventSource::unregister()`] for all the sources provided.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
|
||||
/// calloop::batch_unregister!(
|
||||
/// poll,
|
||||
/// self.source_one,
|
||||
/// self.source_two,
|
||||
/// self.source_three,
|
||||
/// self.source_four,
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// Note that there is no scope for customisation; if you need to do special
|
||||
/// things with a particular source, you'll need to leave it off the list. Also
|
||||
/// note that this only does try-or-early-return error handling in the order
|
||||
/// that you list the sources; if you need anything else, don't use this macro.
|
||||
///
|
||||
/// [`EventSource::unregister()`]: crate::EventSource::unregister()
|
||||
#[macro_export]
|
||||
macro_rules! batch_unregister {
|
||||
($poll:ident, $( $source:expr ),* $(,)?) => {
|
||||
{
|
||||
$(
|
||||
$source.unregister($poll)?;
|
||||
)*
|
||||
$crate::Result::<_>::Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{
|
||||
ping::{make_ping, PingSource},
|
||||
EventSource, PostAction,
|
||||
};
|
||||
|
||||
struct BatchSource {
|
||||
ping0: PingSource,
|
||||
ping1: PingSource,
|
||||
ping2: PingSource,
|
||||
}
|
||||
|
||||
impl EventSource for BatchSource {
|
||||
type Event = usize;
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
type Error = Box<dyn std::error::Error + Sync + Send>;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: crate::Readiness,
|
||||
token: crate::Token,
|
||||
mut callback: F,
|
||||
) -> Result<crate::PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
self.ping0
|
||||
.process_events(readiness, token, |_, m| callback(0, m))?;
|
||||
self.ping1
|
||||
.process_events(readiness, token, |_, m| callback(1, m))?;
|
||||
self.ping2
|
||||
.process_events(readiness, token, |_, m| callback(2, m))?;
|
||||
Ok(PostAction::Continue)
|
||||
}
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
poll: &mut crate::Poll,
|
||||
token_factory: &mut crate::TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
crate::batch_register!(poll, token_factory, self.ping0, self.ping1, self.ping2)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut crate::Poll,
|
||||
token_factory: &mut crate::TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
crate::batch_reregister!(poll, token_factory, self.ping0, self.ping1, self.ping2)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
|
||||
crate::batch_unregister!(poll, self.ping0, self.ping1, self.ping2)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_batch_operations() {
|
||||
let mut fired = [false; 3];
|
||||
|
||||
let (send0, ping0) = make_ping().unwrap();
|
||||
let (send1, ping1) = make_ping().unwrap();
|
||||
let (send2, ping2) = make_ping().unwrap();
|
||||
|
||||
let top = BatchSource {
|
||||
ping0,
|
||||
ping1,
|
||||
ping2,
|
||||
};
|
||||
|
||||
let mut event_loop = crate::EventLoop::<[bool; 3]>::try_new().unwrap();
|
||||
let handle = event_loop.handle();
|
||||
|
||||
let token = handle
|
||||
.insert_source(top, |idx, _, fired| {
|
||||
fired[idx] = true;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
send0.ping();
|
||||
send1.ping();
|
||||
send2.ping();
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::new(0, 0), &mut fired)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(fired, [true; 3]);
|
||||
|
||||
fired = [false; 3];
|
||||
|
||||
handle.update(&token).unwrap();
|
||||
|
||||
send0.ping();
|
||||
send1.ping();
|
||||
send2.ping();
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::new(0, 0), &mut fired)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(fired, [true; 3]);
|
||||
|
||||
fired = [false; 3];
|
||||
|
||||
handle.remove(token);
|
||||
|
||||
send0.ping();
|
||||
send1.ping();
|
||||
send2.ping();
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::new(0, 0), &mut fired)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(fired, [false; 3]);
|
||||
}
|
||||
}
|
||||
327
third-party/vendor/calloop/src/sources/channel.rs
vendored
Normal file
327
third-party/vendor/calloop/src/sources/channel.rs
vendored
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
//! An MPSC channel whose receiving end is an event source
|
||||
//!
|
||||
//! Create a channel using [`channel()`](channel), which returns a
|
||||
//! [`Sender`] that can be cloned and sent accross threads if `T: Send`,
|
||||
//! and a [`Channel`] that can be inserted into an [`EventLoop`](crate::EventLoop).
|
||||
//! It will generate one event per message.
|
||||
//!
|
||||
//! A synchronous version of the channel is provided by [`sync_channel`], in which
|
||||
//! the [`SyncSender`] will block when the channel is full.
|
||||
|
||||
use std::sync::mpsc;
|
||||
|
||||
use crate::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory};
|
||||
|
||||
use super::ping::{make_ping, Ping, PingError, PingSource};
|
||||
|
||||
/// The events generated by the channel event source
|
||||
#[derive(Debug)]
|
||||
pub enum Event<T> {
|
||||
/// A message was received and is bundled here
|
||||
Msg(T),
|
||||
/// The channel was closed
|
||||
///
|
||||
/// This means all the `Sender`s associated with this channel
|
||||
/// have been dropped, no more messages will ever be received.
|
||||
Closed,
|
||||
}
|
||||
|
||||
/// The sender end of a channel
|
||||
///
|
||||
/// It can be cloned and sent accross threads (if `T` is).
|
||||
#[derive(Debug)]
|
||||
pub struct Sender<T> {
|
||||
sender: mpsc::Sender<T>,
|
||||
ping: Ping,
|
||||
}
|
||||
|
||||
impl<T> Clone for Sender<T> {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn clone(&self) -> Sender<T> {
|
||||
Sender {
|
||||
sender: self.sender.clone(),
|
||||
ping: self.ping.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sender<T> {
|
||||
/// Send a message to the channel
|
||||
///
|
||||
/// This will wake the event loop and deliver an `Event::Msg` to
|
||||
/// it containing the provided value.
|
||||
pub fn send(&self, t: T) -> Result<(), mpsc::SendError<T>> {
|
||||
self.sender.send(t).map(|()| self.ping.ping())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Sender<T> {
|
||||
fn drop(&mut self) {
|
||||
// ping on drop, to notify about channel closure
|
||||
self.ping.ping();
|
||||
}
|
||||
}
|
||||
|
||||
/// The sender end of a synchronous channel
|
||||
///
|
||||
/// It can be cloned and sent accross threads (if `T` is).
|
||||
#[derive(Debug)]
|
||||
pub struct SyncSender<T> {
|
||||
sender: mpsc::SyncSender<T>,
|
||||
ping: Ping,
|
||||
}
|
||||
|
||||
impl<T> Clone for SyncSender<T> {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn clone(&self) -> SyncSender<T> {
|
||||
SyncSender {
|
||||
sender: self.sender.clone(),
|
||||
ping: self.ping.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SyncSender<T> {
|
||||
/// Send a message to the synchronous channel
|
||||
///
|
||||
/// This will wake the event loop and deliver an `Event::Msg` to
|
||||
/// it containing the provided value. If the channel is full, this
|
||||
/// function will block until the event loop empties it and it can
|
||||
/// deliver the message.
|
||||
///
|
||||
/// Due to the blocking behavior, this method should not be used on the
|
||||
/// same thread as the one running the event loop, as it could cause deadlocks.
|
||||
pub fn send(&self, t: T) -> Result<(), mpsc::SendError<T>> {
|
||||
let ret = self.try_send(t);
|
||||
match ret {
|
||||
Ok(()) => Ok(()),
|
||||
Err(mpsc::TrySendError::Full(t)) => self.sender.send(t).map(|()| self.ping.ping()),
|
||||
Err(mpsc::TrySendError::Disconnected(t)) => Err(mpsc::SendError(t)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a message to the synchronous channel
|
||||
///
|
||||
/// This will wake the event loop and deliver an `Event::Msg` to
|
||||
/// it containing the provided value. If the channel is full, this
|
||||
/// function will return an error, but the event loop will still be
|
||||
/// signaled for readiness.
|
||||
pub fn try_send(&self, t: T) -> Result<(), mpsc::TrySendError<T>> {
|
||||
let ret = self.sender.try_send(t);
|
||||
if let Ok(()) | Err(mpsc::TrySendError::Full(_)) = ret {
|
||||
self.ping.ping();
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
/// The receiving end of the channel
|
||||
///
|
||||
/// This is the event source to be inserted into your `EventLoop`.
|
||||
#[derive(Debug)]
|
||||
pub struct Channel<T> {
|
||||
receiver: mpsc::Receiver<T>,
|
||||
source: PingSource,
|
||||
}
|
||||
|
||||
// This impl is safe because the Channel is only able to move around threads
|
||||
// when it is not inserted into an event loop. (Otherwise it is stuck into
|
||||
// a Source<_> and the internals of calloop, which are not Send).
|
||||
// At this point, the Arc<Receiver> has a count of 1, and it is obviously
|
||||
// safe to Send between threads.
|
||||
unsafe impl<T: Send> Send for Channel<T> {}
|
||||
|
||||
impl<T> Channel<T> {
|
||||
/// Proxy for [`mpsc::Receiver::recv`] to manually poll events.
|
||||
///
|
||||
/// *Note*: Normally you would want to use the `Channel` by inserting
|
||||
/// it into an event loop instead. Use this for example to immediately
|
||||
/// dispatch pending events after creation.
|
||||
pub fn recv(&self) -> Result<T, mpsc::RecvError> {
|
||||
self.receiver.recv()
|
||||
}
|
||||
|
||||
/// Proxy for [`mpsc::Receiver::try_recv`] to manually poll events.
|
||||
///
|
||||
/// *Note*: Normally you would want to use the `Channel` by inserting
|
||||
/// it into an event loop instead. Use this for example to immediately
|
||||
/// dispatch pending events after creation.
|
||||
pub fn try_recv(&self) -> Result<T, mpsc::TryRecvError> {
|
||||
self.receiver.try_recv()
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new asynchronous channel
|
||||
pub fn channel<T>() -> (Sender<T>, Channel<T>) {
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let (ping, source) = make_ping().expect("Failed to create a Ping.");
|
||||
(Sender { sender, ping }, Channel { receiver, source })
|
||||
}
|
||||
|
||||
/// Create a new synchronous, bounded channel
|
||||
pub fn sync_channel<T>(bound: usize) -> (SyncSender<T>, Channel<T>) {
|
||||
let (sender, receiver) = mpsc::sync_channel(bound);
|
||||
let (ping, source) = make_ping().expect("Failed to create a Ping.");
|
||||
(SyncSender { sender, ping }, Channel { receiver, source })
|
||||
}
|
||||
|
||||
impl<T> EventSource for Channel<T> {
|
||||
type Event = Event<T>;
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
type Error = ChannelError;
|
||||
|
||||
fn process_events<C>(
|
||||
&mut self,
|
||||
readiness: Readiness,
|
||||
token: Token,
|
||||
mut callback: C,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
let receiver = &self.receiver;
|
||||
self.source
|
||||
.process_events(readiness, token, |(), &mut ()| loop {
|
||||
match receiver.try_recv() {
|
||||
Ok(val) => callback(Event::Msg(val), &mut ()),
|
||||
Err(mpsc::TryRecvError::Empty) => break,
|
||||
Err(mpsc::TryRecvError::Disconnected) => {
|
||||
callback(Event::Closed, &mut ());
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
.map_err(ChannelError)
|
||||
}
|
||||
|
||||
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
|
||||
self.source.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.source.reregister(poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
|
||||
self.source.unregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
/// An error arising from processing events for a channel.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[error(transparent)]
|
||||
pub struct ChannelError(PingError);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn basic_channel() {
|
||||
let mut event_loop = crate::EventLoop::try_new().unwrap();
|
||||
|
||||
let handle = event_loop.handle();
|
||||
|
||||
let (tx, rx) = channel::<()>();
|
||||
|
||||
// (got_msg, got_closed)
|
||||
let mut got = (false, false);
|
||||
|
||||
let _channel_token = handle
|
||||
.insert_source(rx, move |evt, &mut (), got: &mut (bool, bool)| match evt {
|
||||
Event::Msg(()) => {
|
||||
got.0 = true;
|
||||
}
|
||||
Event::Closed => {
|
||||
got.1 = true;
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// nothing is sent, nothing is received
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut got)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(got, (false, false));
|
||||
|
||||
// a message is send
|
||||
tx.send(()).unwrap();
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut got)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(got, (true, false));
|
||||
|
||||
// the sender is dropped
|
||||
::std::mem::drop(tx);
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut got)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(got, (true, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_sync_channel() {
|
||||
let mut event_loop = crate::EventLoop::try_new().unwrap();
|
||||
|
||||
let handle = event_loop.handle();
|
||||
|
||||
let (tx, rx) = sync_channel::<()>(2);
|
||||
|
||||
let mut received = (0, false);
|
||||
|
||||
let _channel_token = handle
|
||||
.insert_source(
|
||||
rx,
|
||||
move |evt, &mut (), received: &mut (u32, bool)| match evt {
|
||||
Event::Msg(()) => {
|
||||
received.0 += 1;
|
||||
}
|
||||
Event::Closed => {
|
||||
received.1 = true;
|
||||
}
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// nothing is sent, nothing is received
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut received)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(received.0, 0);
|
||||
assert!(!received.1);
|
||||
|
||||
// fill the channel
|
||||
tx.send(()).unwrap();
|
||||
tx.send(()).unwrap();
|
||||
assert!(tx.try_send(()).is_err());
|
||||
|
||||
// empty it
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut received)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(received.0, 2);
|
||||
assert!(!received.1);
|
||||
|
||||
// send a final message and drop the sender
|
||||
tx.send(()).unwrap();
|
||||
std::mem::drop(tx);
|
||||
|
||||
// final read of the channel
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut received)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(received.0, 3);
|
||||
assert!(received.1);
|
||||
}
|
||||
}
|
||||
212
third-party/vendor/calloop/src/sources/futures.rs
vendored
Normal file
212
third-party/vendor/calloop/src/sources/futures.rs
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
//! A futures executor as an event source
|
||||
//!
|
||||
//! Only available with the `executor` cargo feature of `calloop`.
|
||||
//!
|
||||
//! This executor is intended for light futures, which will be polled as part of your
|
||||
//! event loop. Such futures may be waiting for IO, or for some external computation on an
|
||||
//! other thread for example.
|
||||
//!
|
||||
//! You can create a new executor using the `executor` function, which creates a pair
|
||||
//! `(Executor<T>, Scheduler<T>)` to handle futures that all evaluate to type `T`. The
|
||||
//! executor should be inserted into your event loop, and will yield the return values of
|
||||
//! the futures as they finish into your callback. The scheduler can be cloned and used
|
||||
//! to send futures to be executed into the executor. A generic executor can be obtained
|
||||
//! by choosing `T = ()` and letting futures handle the forwarding of their return values
|
||||
//! (if any) by their own means.
|
||||
//!
|
||||
//! **Note:** The futures must have their own means of being woken up, as this executor is,
|
||||
//! by itself, not I/O aware. See [`LoopHandle::adapt_io`](crate::LoopHandle#method.adapt_io)
|
||||
//! for that, or you can use some other mechanism if you prefer.
|
||||
use std::{future::Future, pin::Pin, sync::Arc};
|
||||
|
||||
use futures_util::{
|
||||
stream::{FuturesUnordered, Stream},
|
||||
task::{waker_ref, ArcWake, Context, LocalFutureObj, Poll as FutPoll},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
sources::{
|
||||
channel::{channel, Channel, ChannelError, Event, Sender},
|
||||
ping::{make_ping, Ping, PingError, PingSource},
|
||||
EventSource,
|
||||
},
|
||||
Poll, PostAction, Readiness, Token, TokenFactory,
|
||||
};
|
||||
|
||||
/// A future executor as an event source
|
||||
#[derive(Debug)]
|
||||
pub struct Executor<T> {
|
||||
futures: FuturesUnordered<LocalFutureObj<'static, T>>,
|
||||
new_futures: Channel<LocalFutureObj<'static, T>>,
|
||||
ready_futures: PingSource,
|
||||
waker: Arc<ExecWaker>,
|
||||
}
|
||||
|
||||
/// A scheduler to send futures to an executor
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Scheduler<T> {
|
||||
sender: Sender<LocalFutureObj<'static, T>>,
|
||||
}
|
||||
|
||||
impl<T> Scheduler<T> {
|
||||
/// Sends the given future to the executor associated to this scheduler
|
||||
///
|
||||
/// Returns an error if the the executor not longer exists.
|
||||
pub fn schedule<Fut: 'static>(&self, future: Fut) -> Result<(), ExecutorDestroyed>
|
||||
where
|
||||
Fut: Future<Output = T>,
|
||||
{
|
||||
let obj = LocalFutureObj::new(Box::new(future));
|
||||
self.sender.send(obj).map_err(|_| ExecutorDestroyed)
|
||||
}
|
||||
}
|
||||
|
||||
/// Error generated when trying to schedule a future after the
|
||||
/// executor was destroyed.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[error("the executor was destroyed")]
|
||||
pub struct ExecutorDestroyed;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ExecWaker {
|
||||
ping: Ping,
|
||||
}
|
||||
|
||||
impl ArcWake for ExecWaker {
|
||||
fn wake_by_ref(arc_self: &Arc<ExecWaker>) {
|
||||
arc_self.ping.ping();
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new executor, and its associated scheduler
|
||||
///
|
||||
/// May fail due to OS errors preventing calloop to setup its internal pipes (if your
|
||||
/// process has reatched its file descriptor limit for example).
|
||||
pub fn executor<T>() -> crate::Result<(Executor<T>, Scheduler<T>)> {
|
||||
let (ping, ready_futures) = make_ping()?;
|
||||
let (sender, new_futures) = channel();
|
||||
Ok((
|
||||
Executor {
|
||||
futures: FuturesUnordered::new(),
|
||||
new_futures,
|
||||
ready_futures,
|
||||
waker: Arc::new(ExecWaker { ping }),
|
||||
},
|
||||
Scheduler { sender },
|
||||
))
|
||||
}
|
||||
|
||||
impl<T> EventSource for Executor<T> {
|
||||
type Event = T;
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
type Error = ExecutorError;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: Readiness,
|
||||
token: Token,
|
||||
mut callback: F,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(T, &mut ()),
|
||||
{
|
||||
// fetch all newly inserted futures and push them to the container
|
||||
let futures = &mut self.futures;
|
||||
self.new_futures
|
||||
.process_events(readiness, token, |evt, _| {
|
||||
if let Event::Msg(fut) = evt {
|
||||
futures.push(fut);
|
||||
}
|
||||
})
|
||||
.map_err(ExecutorError::NewFutureError)?;
|
||||
|
||||
// process ping events to make it non-ready again
|
||||
self.ready_futures
|
||||
.process_events(readiness, token, |(), _| {})
|
||||
.map_err(ExecutorError::WakeError)?;
|
||||
|
||||
// advance all available futures as much as possible
|
||||
let waker = waker_ref(&self.waker);
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
while let FutPoll::Ready(Some(ret)) = Pin::new(&mut self.futures).poll_next(&mut cx) {
|
||||
callback(ret, &mut ());
|
||||
}
|
||||
Ok(PostAction::Continue)
|
||||
}
|
||||
|
||||
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
|
||||
self.new_futures.register(poll, token_factory)?;
|
||||
self.ready_futures.register(poll, token_factory)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.new_futures.reregister(poll, token_factory)?;
|
||||
self.ready_futures.reregister(poll, token_factory)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
|
||||
self.new_futures.unregister(poll)?;
|
||||
self.ready_futures.unregister(poll)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// An error arising from processing events in an async executor event source.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ExecutorError {
|
||||
/// Error while reading new futures added via [`Scheduler::schedule()`].
|
||||
#[error("error adding new futures")]
|
||||
NewFutureError(ChannelError),
|
||||
|
||||
/// Error while processing wake events from existing futures.
|
||||
#[error("error processing wake events")]
|
||||
WakeError(PingError),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn ready() {
|
||||
let mut event_loop = crate::EventLoop::<u32>::try_new().unwrap();
|
||||
|
||||
let handle = event_loop.handle();
|
||||
|
||||
let (exec, sched) = executor::<u32>().unwrap();
|
||||
|
||||
handle
|
||||
.insert_source(exec, move |ret, &mut (), got| {
|
||||
*got = ret;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut got = 0;
|
||||
|
||||
let fut = async { 42 };
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut got)
|
||||
.unwrap();
|
||||
|
||||
// the future is not yet inserted, and thus has not yet run
|
||||
assert_eq!(got, 0);
|
||||
|
||||
sched.schedule(fut).unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut got)
|
||||
.unwrap();
|
||||
|
||||
// the future has run
|
||||
assert_eq!(got, 42);
|
||||
}
|
||||
}
|
||||
271
third-party/vendor/calloop/src/sources/generic.rs
vendored
Normal file
271
third-party/vendor/calloop/src/sources/generic.rs
vendored
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
//! A generic event source wrapping an IO objects or file descriptor
|
||||
//!
|
||||
//! You can use this general purpose adapter around file-descriptor backed objects to
|
||||
//! insert into an [`EventLoop`](crate::EventLoop).
|
||||
//!
|
||||
//! The event generated by this [`Generic`] event source are the [`Readiness`](crate::Readiness)
|
||||
//! notification itself, and the monitored object is provided to your callback as the second
|
||||
//! argument.
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate calloop;
|
||||
//! use calloop::{generic::Generic, Interest, Mode, PostAction};
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! # let mut event_loop = calloop::EventLoop::<()>::try_new()
|
||||
//! # .expect("Failed to initialize the event loop!");
|
||||
//! # let handle = event_loop.handle();
|
||||
//! # let io_object = 0;
|
||||
//! handle.insert_source(
|
||||
//! // wrap your IO object in a Generic, here we register for read readiness
|
||||
//! // in level-triggering mode
|
||||
//! Generic::new(io_object, Interest::READ, Mode::Level),
|
||||
//! |readiness, io_object, shared_data| {
|
||||
//! // The first argument of the callback is a Readiness
|
||||
//! // The second is a &mut reference to your object
|
||||
//!
|
||||
//! // your callback needs to return a Result<PostAction, std::io::Error>
|
||||
//! // if it returns an error, the event loop will consider this event
|
||||
//! // event source as erroring and report it to the user.
|
||||
//! Ok(PostAction::Continue)
|
||||
//! }
|
||||
//! );
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! It can also help you implementing your own event sources: just have
|
||||
//! these `Generic<_>` as fields of your event source, and delegate the
|
||||
//! [`EventSource`](crate::EventSource) implementation to them.
|
||||
//!
|
||||
//! If you need to directly work with a [`RawFd`](std::os::unix::io::RawFd), rather than an
|
||||
//! FD-backed object, see [`Generic::from_fd`](Generic#method.from_fd).
|
||||
|
||||
use std::{marker::PhantomData, os::unix::io::AsRawFd};
|
||||
|
||||
use crate::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory};
|
||||
|
||||
/// A generic event source wrapping a FD-backed type
|
||||
#[derive(Debug)]
|
||||
pub struct Generic<F: AsRawFd, E = std::io::Error> {
|
||||
/// The wrapped FD-backed type
|
||||
pub file: F,
|
||||
/// The programmed interest
|
||||
pub interest: Interest,
|
||||
/// The programmed mode
|
||||
pub mode: Mode,
|
||||
|
||||
// This token is used by the event loop logic to look up this source when an
|
||||
// event occurs.
|
||||
token: Option<Token>,
|
||||
|
||||
// This allows us to make the associated error and return types generic.
|
||||
_error_type: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<F: AsRawFd> Generic<F, std::io::Error> {
|
||||
/// Wrap a FD-backed type into a `Generic` event source that uses
|
||||
/// [`std::io::Error`] as its error type.
|
||||
pub fn new(file: F, interest: Interest, mode: Mode) -> Generic<F, std::io::Error> {
|
||||
Generic {
|
||||
file,
|
||||
interest,
|
||||
mode,
|
||||
token: None,
|
||||
_error_type: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap a FD-backed type into a `Generic` event source using an arbitrary error type.
|
||||
pub fn new_with_error<E>(file: F, interest: Interest, mode: Mode) -> Generic<F, E> {
|
||||
Generic {
|
||||
file,
|
||||
interest,
|
||||
mode,
|
||||
token: None,
|
||||
_error_type: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: AsRawFd, E> Generic<F, E> {
|
||||
/// Unwrap the `Generic` source to retrieve the underlying type
|
||||
pub fn unwrap(self) -> F {
|
||||
self.file
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, E> EventSource for Generic<F, E>
|
||||
where
|
||||
F: AsRawFd,
|
||||
E: Into<Box<dyn std::error::Error + Send + Sync>>,
|
||||
{
|
||||
type Event = Readiness;
|
||||
type Metadata = F;
|
||||
type Ret = Result<PostAction, E>;
|
||||
type Error = E;
|
||||
|
||||
fn process_events<C>(
|
||||
&mut self,
|
||||
readiness: Readiness,
|
||||
token: Token,
|
||||
mut callback: C,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
// If the token is invalid or not ours, skip processing.
|
||||
if self.token != Some(token) {
|
||||
return Ok(PostAction::Continue);
|
||||
}
|
||||
|
||||
callback(readiness, &mut self.file)
|
||||
}
|
||||
|
||||
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
|
||||
let token = token_factory.token();
|
||||
|
||||
poll.register(self.file.as_raw_fd(), self.interest, self.mode, token)?;
|
||||
|
||||
self.token = Some(token);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
let token = token_factory.token();
|
||||
|
||||
poll.reregister(self.file.as_raw_fd(), self.interest, self.mode, token)?;
|
||||
|
||||
self.token = Some(token);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
|
||||
poll.unregister(self.file.as_raw_fd())?;
|
||||
self.token = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use super::Generic;
|
||||
use crate::{Dispatcher, Interest, Mode, PostAction};
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn dispatch_unix() {
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
let mut event_loop = crate::EventLoop::try_new().unwrap();
|
||||
|
||||
let handle = event_loop.handle();
|
||||
|
||||
let (mut tx, rx) = UnixStream::pair().unwrap();
|
||||
|
||||
let generic = Generic::new(rx, Interest::READ, Mode::Level);
|
||||
|
||||
let mut dispached = false;
|
||||
|
||||
let _generic_token = handle
|
||||
.insert_source(generic, move |readiness, file, d| {
|
||||
assert!(readiness.readable);
|
||||
// we have not registered for writability
|
||||
assert!(!readiness.writable);
|
||||
let mut buffer = vec![0; 10];
|
||||
let ret = file.read(&mut buffer).unwrap();
|
||||
assert_eq!(ret, 6);
|
||||
assert_eq!(&buffer[..6], &[1, 2, 3, 4, 5, 6]);
|
||||
|
||||
*d = true;
|
||||
Ok(PostAction::Continue)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut dispached)
|
||||
.unwrap();
|
||||
|
||||
assert!(!dispached);
|
||||
|
||||
let ret = tx.write(&[1, 2, 3, 4, 5, 6]).unwrap();
|
||||
assert_eq!(ret, 6);
|
||||
tx.flush().unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut dispached)
|
||||
.unwrap();
|
||||
|
||||
assert!(dispached);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn register_deregister_unix() {
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
let mut event_loop = crate::EventLoop::try_new().unwrap();
|
||||
|
||||
let handle = event_loop.handle();
|
||||
|
||||
let (mut tx, rx) = UnixStream::pair().unwrap();
|
||||
|
||||
let generic = Generic::new(rx, Interest::READ, Mode::Level);
|
||||
let dispatcher = Dispatcher::new(generic, move |_, _, d| {
|
||||
*d = true;
|
||||
Ok(PostAction::Continue)
|
||||
});
|
||||
|
||||
let mut dispached = false;
|
||||
|
||||
let generic_token = handle.register_dispatcher(dispatcher.clone()).unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut dispached)
|
||||
.unwrap();
|
||||
|
||||
assert!(!dispached);
|
||||
|
||||
// remove the source, and then write something
|
||||
|
||||
event_loop.handle().remove(generic_token);
|
||||
|
||||
let ret = tx.write(&[1, 2, 3, 4, 5, 6]).unwrap();
|
||||
assert_eq!(ret, 6);
|
||||
tx.flush().unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut dispached)
|
||||
.unwrap();
|
||||
|
||||
// the source has not been dispatched, as the source is no longer here
|
||||
assert!(!dispached);
|
||||
|
||||
// insert it again
|
||||
let generic = dispatcher.into_source_inner();
|
||||
let _generic_token = handle
|
||||
.insert_source(generic, move |readiness, file, d| {
|
||||
assert!(readiness.readable);
|
||||
// we have not registered for writability
|
||||
assert!(!readiness.writable);
|
||||
let mut buffer = vec![0; 10];
|
||||
let ret = file.read(&mut buffer).unwrap();
|
||||
assert_eq!(ret, 6);
|
||||
assert_eq!(&buffer[..6], &[1, 2, 3, 4, 5, 6]);
|
||||
|
||||
*d = true;
|
||||
Ok(PostAction::Continue)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::ZERO), &mut dispached)
|
||||
.unwrap();
|
||||
|
||||
// the has now been properly dispatched
|
||||
assert!(dispached);
|
||||
}
|
||||
}
|
||||
714
third-party/vendor/calloop/src/sources/mod.rs
vendored
Normal file
714
third-party/vendor/calloop/src/sources/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,714 @@
|
|||
use std::{
|
||||
cell::{Ref, RefCell, RefMut},
|
||||
ops::{BitOr, BitOrAssign},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use crate::{sys::TokenFactory, Poll, Readiness, Token};
|
||||
|
||||
pub mod channel;
|
||||
#[cfg(feature = "executor")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "executor")))]
|
||||
pub mod futures;
|
||||
pub mod generic;
|
||||
pub mod ping;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
|
||||
pub mod signals;
|
||||
pub mod timer;
|
||||
pub mod transient;
|
||||
|
||||
/// Possible actions that can be requested to the event loop by an
|
||||
/// event source once its events have been processed.
|
||||
///
|
||||
/// `PostAction` values can be combined with the `|` (bit-or) operator (or with
|
||||
/// `|=`) with the result that:
|
||||
/// - if both values are identical, the result is that value
|
||||
/// - if they are different, the result is [`Reregister`](PostAction::Reregister)
|
||||
///
|
||||
/// Bit-or-ing these results is useful for composed sources to combine the
|
||||
/// results of their child sources, but note that it only applies to the child
|
||||
/// sources. For example, if every child source returns `Continue`, the result
|
||||
/// will be `Continue`, but the parent source might still need to return
|
||||
/// `Reregister` or something else depending on any additional logic it uses.
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
pub enum PostAction {
|
||||
/// Continue listening for events on this source as before
|
||||
Continue,
|
||||
/// Trigger a re-registration of this source
|
||||
Reregister,
|
||||
/// Disable this source
|
||||
///
|
||||
/// Has the same effect as [`LoopHandle::disable`](crate::LoopHandle#method.disable)
|
||||
Disable,
|
||||
/// Remove this source from the eventloop
|
||||
///
|
||||
/// Has the same effect as [`LoopHandle::kill`](crate::LoopHandle#method.kill)
|
||||
Remove,
|
||||
}
|
||||
|
||||
/// Combines `PostAction` values returned from nested event sources.
|
||||
impl BitOr for PostAction {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
if matches!(self, x if x == rhs) {
|
||||
self
|
||||
} else {
|
||||
Self::Reregister
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Combines `PostAction` values returned from nested event sources.
|
||||
impl BitOrAssign for PostAction {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
if *self != rhs {
|
||||
*self = Self::Reregister;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait representing an event source
|
||||
///
|
||||
/// This is the trait you need to implement if you wish to create your own
|
||||
/// calloop-compatible event sources.
|
||||
///
|
||||
/// The 3 associated types define the type of closure the user will need to
|
||||
/// provide to process events for your event source.
|
||||
///
|
||||
/// The `process_events` method will be called when one of the FD you registered
|
||||
/// is ready, with the associated readiness and token.
|
||||
///
|
||||
/// The `register`, `reregister` and `unregister` methods are plumbing to let your
|
||||
/// source register itself with the polling system. See their documentation for details.
|
||||
///
|
||||
/// In case your event source needs to do some special processing before or after a
|
||||
/// polling session occurs (to prepare the underlying source for polling, and cleanup
|
||||
/// after that), you can override the `pre_run` and `post_run`, that do nothing by
|
||||
/// default. Depending on the underlying events, `process_events` may be invoked once,
|
||||
/// several times, or none at all between `pre_run` and `post_run` are called, but when
|
||||
/// it is invoked, it'll always be between those two.
|
||||
pub trait EventSource {
|
||||
/// The type of events generated by your source.
|
||||
type Event;
|
||||
/// Some metadata of your event source
|
||||
///
|
||||
/// This is typically useful if your source contains some internal state that
|
||||
/// the user may need to interact with when processing events. The user callback
|
||||
/// will receive a `&mut Metadata` reference.
|
||||
///
|
||||
/// Set to `()` if not needed.
|
||||
type Metadata;
|
||||
/// The return type of the user callback
|
||||
///
|
||||
/// If the user needs to return some value back to your event source once its
|
||||
/// processing is finshed (to indicate success or failure for example), you can
|
||||
/// specify it using this type.
|
||||
///
|
||||
/// Set to `()` if not needed.
|
||||
type Ret;
|
||||
/// The error type returned from
|
||||
/// [`process_events()`](Self::process_events()) (not the user callback!).
|
||||
type Error: Into<Box<dyn std::error::Error + Sync + Send>>;
|
||||
|
||||
/// Process any relevant events
|
||||
///
|
||||
/// This method will be called every time one of the FD you registered becomes
|
||||
/// ready, including the readiness details and the associated token.
|
||||
///
|
||||
/// Your event source will then do some processing of the file descriptor(s) to generate
|
||||
/// events, and call the provided `callback` for each one of them.
|
||||
///
|
||||
/// You should ensure you drained the file descriptors of their events, especially if using
|
||||
/// edge-triggered mode.
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: Readiness,
|
||||
token: Token,
|
||||
callback: F,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret;
|
||||
|
||||
/// Register yourself to this poll instance
|
||||
///
|
||||
/// You should register all your relevant file descriptors to the provided [`Poll`](crate::Poll)
|
||||
/// using its [`Poll::register`](crate::Poll#method.register) method.
|
||||
///
|
||||
/// If you need to register more than one file descriptor, you can change the
|
||||
/// `sub_id` field of the [`Token`](crate::Token) to differentiate between them.
|
||||
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()>;
|
||||
|
||||
/// Re-register your file descriptors
|
||||
///
|
||||
/// Your should update the registration of all your relevant file descriptor to
|
||||
/// the provided [`Poll`](crate::Poll) using its [`Poll::reregister`](crate::Poll#method.reregister),
|
||||
/// if necessary.
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> crate::Result<()>;
|
||||
|
||||
/// Unregister your file descriptors
|
||||
///
|
||||
/// You should unregister all your file descriptors from this [`Poll`](crate::Poll) using its
|
||||
/// [`Poll::unregister`](crate::Poll#method.unregister) method.
|
||||
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()>;
|
||||
|
||||
/// Notification that a polling session is going to start
|
||||
///
|
||||
/// You can generate events from this method as you would from `process_events`.
|
||||
fn pre_run<F>(&mut self, _callback: F) -> crate::Result<()>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Notification that the current polling session ended
|
||||
///
|
||||
/// You can generate events from this method as you would from `process_events`.
|
||||
fn post_run<F>(&mut self, _callback: F) -> crate::Result<()>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Blanket implementation for boxed event sources. [`EventSource`] is not an
|
||||
/// object safe trait, so this does not include trait objects.
|
||||
impl<T: EventSource> EventSource for Box<T> {
|
||||
type Event = T::Event;
|
||||
type Metadata = T::Metadata;
|
||||
type Ret = T::Ret;
|
||||
type Error = T::Error;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: Readiness,
|
||||
token: Token,
|
||||
callback: F,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
T::process_events(&mut **self, readiness, token, callback)
|
||||
}
|
||||
|
||||
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
|
||||
T::register(&mut **self, poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
T::reregister(&mut **self, poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
|
||||
T::unregister(&mut **self, poll)
|
||||
}
|
||||
|
||||
fn pre_run<F>(&mut self, callback: F) -> crate::Result<()>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
T::pre_run(&mut **self, callback)
|
||||
}
|
||||
|
||||
fn post_run<F>(&mut self, callback: F) -> crate::Result<()>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
T::post_run(&mut **self, callback)
|
||||
}
|
||||
}
|
||||
|
||||
/// Blanket implementation for exclusive references to event sources.
|
||||
/// [`EventSource`] is not an object safe trait, so this does not include trait
|
||||
/// objects.
|
||||
impl<T: EventSource> EventSource for &mut T {
|
||||
type Event = T::Event;
|
||||
type Metadata = T::Metadata;
|
||||
type Ret = T::Ret;
|
||||
type Error = T::Error;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: Readiness,
|
||||
token: Token,
|
||||
callback: F,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
T::process_events(&mut **self, readiness, token, callback)
|
||||
}
|
||||
|
||||
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
|
||||
T::register(&mut **self, poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
T::reregister(&mut **self, poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
|
||||
T::unregister(&mut **self, poll)
|
||||
}
|
||||
|
||||
fn pre_run<F>(&mut self, callback: F) -> crate::Result<()>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
T::pre_run(&mut **self, callback)
|
||||
}
|
||||
|
||||
fn post_run<F>(&mut self, callback: F) -> crate::Result<()>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
T::post_run(&mut **self, callback)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DispatcherInner<S, F> {
|
||||
source: S,
|
||||
callback: F,
|
||||
}
|
||||
|
||||
impl<Data, S, F> EventDispatcher<Data> for RefCell<DispatcherInner<S, F>>
|
||||
where
|
||||
S: EventSource,
|
||||
F: FnMut(S::Event, &mut S::Metadata, &mut Data) -> S::Ret,
|
||||
{
|
||||
fn process_events(
|
||||
&self,
|
||||
readiness: Readiness,
|
||||
token: Token,
|
||||
data: &mut Data,
|
||||
) -> crate::Result<PostAction> {
|
||||
let mut disp = self.borrow_mut();
|
||||
let DispatcherInner {
|
||||
ref mut source,
|
||||
ref mut callback,
|
||||
} = *disp;
|
||||
source
|
||||
.process_events(readiness, token, |event, meta| callback(event, meta, data))
|
||||
.map_err(|e| crate::Error::OtherError(e.into()))
|
||||
}
|
||||
|
||||
fn register(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
|
||||
self.borrow_mut().source.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<bool> {
|
||||
if let Ok(mut me) = self.try_borrow_mut() {
|
||||
me.source.reregister(poll, token_factory)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn unregister(&self, poll: &mut Poll) -> crate::Result<bool> {
|
||||
if let Ok(mut me) = self.try_borrow_mut() {
|
||||
me.source.unregister(poll)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_run(&self, data: &mut Data) -> crate::Result<()> {
|
||||
let mut disp = self.borrow_mut();
|
||||
let DispatcherInner {
|
||||
ref mut source,
|
||||
ref mut callback,
|
||||
} = *disp;
|
||||
source.pre_run(|event, meta| callback(event, meta, data))
|
||||
}
|
||||
|
||||
fn post_run(&self, data: &mut Data) -> crate::Result<()> {
|
||||
let mut disp = self.borrow_mut();
|
||||
let DispatcherInner {
|
||||
ref mut source,
|
||||
ref mut callback,
|
||||
} = *disp;
|
||||
source.post_run(|event, meta| callback(event, meta, data))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait EventDispatcher<Data> {
|
||||
fn process_events(
|
||||
&self,
|
||||
readiness: Readiness,
|
||||
token: Token,
|
||||
data: &mut Data,
|
||||
) -> crate::Result<PostAction>;
|
||||
|
||||
fn register(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()>;
|
||||
|
||||
fn reregister(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<bool>;
|
||||
|
||||
fn unregister(&self, poll: &mut Poll) -> crate::Result<bool>;
|
||||
|
||||
fn pre_run(&self, data: &mut Data) -> crate::Result<()>;
|
||||
|
||||
fn post_run(&self, data: &mut Data) -> crate::Result<()>;
|
||||
}
|
||||
|
||||
// An internal trait to erase the `F` type parameter of `DispatcherInner`
|
||||
trait ErasedDispatcher<'a, S, Data> {
|
||||
fn as_source_ref(&self) -> Ref<S>;
|
||||
fn as_source_mut(&self) -> RefMut<S>;
|
||||
fn into_source_inner(self: Rc<Self>) -> S;
|
||||
fn into_event_dispatcher(self: Rc<Self>) -> Rc<dyn EventDispatcher<Data> + 'a>;
|
||||
}
|
||||
|
||||
impl<'a, S, Data, F> ErasedDispatcher<'a, S, Data> for RefCell<DispatcherInner<S, F>>
|
||||
where
|
||||
S: EventSource + 'a,
|
||||
F: FnMut(S::Event, &mut S::Metadata, &mut Data) -> S::Ret + 'a,
|
||||
{
|
||||
fn as_source_ref(&self) -> Ref<S> {
|
||||
Ref::map(self.borrow(), |inner| &inner.source)
|
||||
}
|
||||
|
||||
fn as_source_mut(&self) -> RefMut<S> {
|
||||
RefMut::map(self.borrow_mut(), |inner| &mut inner.source)
|
||||
}
|
||||
|
||||
fn into_source_inner(self: Rc<Self>) -> S {
|
||||
if let Ok(ref_cell) = Rc::try_unwrap(self) {
|
||||
ref_cell.into_inner().source
|
||||
} else {
|
||||
panic!("Dispatcher is still registered");
|
||||
}
|
||||
}
|
||||
|
||||
fn into_event_dispatcher(self: Rc<Self>) -> Rc<dyn EventDispatcher<Data> + 'a>
|
||||
where
|
||||
S: 'a,
|
||||
{
|
||||
self as Rc<dyn EventDispatcher<Data> + 'a>
|
||||
}
|
||||
}
|
||||
|
||||
/// An event source with its callback.
|
||||
///
|
||||
/// The `Dispatcher` can be registered in an event loop.
|
||||
/// Use the `as_source_{ref,mut}` functions to interact with the event source.
|
||||
/// Use `into_source_inner` to get the event source back.
|
||||
pub struct Dispatcher<'a, S, Data>(Rc<dyn ErasedDispatcher<'a, S, Data> + 'a>);
|
||||
|
||||
impl<'a, S, Data> std::fmt::Debug for Dispatcher<'a, S, Data> {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("Dispatcher { ... }")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S, Data> Dispatcher<'a, S, Data>
|
||||
where
|
||||
S: EventSource + 'a,
|
||||
{
|
||||
/// Builds a dispatcher.
|
||||
///
|
||||
/// The resulting `Dispatcher`
|
||||
pub fn new<F>(source: S, callback: F) -> Self
|
||||
where
|
||||
F: FnMut(S::Event, &mut S::Metadata, &mut Data) -> S::Ret + 'a,
|
||||
{
|
||||
Dispatcher(Rc::new(RefCell::new(DispatcherInner { source, callback })))
|
||||
}
|
||||
|
||||
/// Returns an immutable reference to the event source.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Has the same semantics as `RefCell::borrow()`.
|
||||
///
|
||||
/// The dispatcher being mutably borrowed while its events are dispatched,
|
||||
/// this method will panic if invoked from within the associated dispatching closure.
|
||||
pub fn as_source_ref(&self) -> Ref<S> {
|
||||
self.0.as_source_ref()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the event source.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Has the same semantics as `RefCell::borrow_mut()`.
|
||||
///
|
||||
/// The dispatcher being mutably borrowed while its events are dispatched,
|
||||
/// this method will panic if invoked from within the associated dispatching closure.
|
||||
pub fn as_source_mut(&self) -> RefMut<S> {
|
||||
self.0.as_source_mut()
|
||||
}
|
||||
|
||||
/// Consumes the Dispatcher and returns the inner event source.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the `Dispatcher` is still registered.
|
||||
pub fn into_source_inner(self) -> S {
|
||||
self.0.into_source_inner()
|
||||
}
|
||||
|
||||
pub(crate) fn clone_as_event_dispatcher(&self) -> Rc<dyn EventDispatcher<Data> + 'a> {
|
||||
Rc::clone(&self.0).into_event_dispatcher()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S, Data> Clone for Dispatcher<'a, S, Data> {
|
||||
fn clone(&self) -> Dispatcher<'a, S, Data> {
|
||||
Dispatcher(Rc::clone(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// An idle callback that was inserted in this loop
|
||||
///
|
||||
/// This handle allows you to cancel the callback. Dropping
|
||||
/// it will *not* cancel it.
|
||||
pub struct Idle<'i> {
|
||||
pub(crate) callback: Rc<RefCell<dyn CancellableIdle + 'i>>,
|
||||
}
|
||||
|
||||
impl<'i> std::fmt::Debug for Idle<'i> {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("Idle { ... }")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Idle<'i> {
|
||||
/// Cancel the idle callback if it was not already run
|
||||
pub fn cancel(self) {
|
||||
self.callback.borrow_mut().cancel();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait CancellableIdle {
|
||||
fn cancel(&mut self);
|
||||
}
|
||||
|
||||
impl<F> CancellableIdle for Option<F> {
|
||||
fn cancel(&mut self) {
|
||||
self.take();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait IdleDispatcher<Data> {
|
||||
fn dispatch(&mut self, data: &mut Data);
|
||||
}
|
||||
|
||||
impl<Data, F> IdleDispatcher<Data> for Option<F>
|
||||
where
|
||||
F: FnMut(&mut Data),
|
||||
{
|
||||
fn dispatch(&mut self, data: &mut Data) {
|
||||
if let Some(callabck) = self.as_mut() {
|
||||
callabck(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{ping::make_ping, EventLoop};
|
||||
|
||||
// Test event source boxing.
|
||||
#[test]
|
||||
fn test_boxed_source() {
|
||||
let mut fired = false;
|
||||
|
||||
let (pinger, source) = make_ping().unwrap();
|
||||
let boxed = Box::new(source);
|
||||
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
let handle = event_loop.handle();
|
||||
|
||||
let token = handle
|
||||
.insert_source(boxed, |_, _, fired| *fired = true)
|
||||
.unwrap();
|
||||
|
||||
pinger.ping();
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::new(0, 0), &mut fired)
|
||||
.unwrap();
|
||||
|
||||
assert!(fired);
|
||||
fired = false;
|
||||
|
||||
handle.update(&token).unwrap();
|
||||
|
||||
pinger.ping();
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::new(0, 0), &mut fired)
|
||||
.unwrap();
|
||||
|
||||
assert!(fired);
|
||||
fired = false;
|
||||
|
||||
handle.remove(token);
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::new(0, 0), &mut fired)
|
||||
.unwrap();
|
||||
|
||||
assert!(!fired);
|
||||
}
|
||||
|
||||
// Test event source trait methods via mut ref.
|
||||
#[test]
|
||||
fn test_mut_ref_source() {
|
||||
let mut fired = false;
|
||||
|
||||
let (pinger, mut source) = make_ping().unwrap();
|
||||
let source_ref = &mut source;
|
||||
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
let handle = event_loop.handle();
|
||||
|
||||
let token = handle
|
||||
.insert_source(source_ref, |_, _, fired| *fired = true)
|
||||
.unwrap();
|
||||
|
||||
pinger.ping();
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::new(0, 0), &mut fired)
|
||||
.unwrap();
|
||||
|
||||
assert!(fired);
|
||||
fired = false;
|
||||
|
||||
handle.update(&token).unwrap();
|
||||
|
||||
pinger.ping();
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::new(0, 0), &mut fired)
|
||||
.unwrap();
|
||||
|
||||
assert!(fired);
|
||||
fired = false;
|
||||
|
||||
handle.remove(token);
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::new(0, 0), &mut fired)
|
||||
.unwrap();
|
||||
|
||||
assert!(!fired);
|
||||
}
|
||||
|
||||
// Test PostAction combinations.
|
||||
#[test]
|
||||
fn post_action_combine() {
|
||||
use super::PostAction::*;
|
||||
assert_eq!(Continue | Continue, Continue);
|
||||
assert_eq!(Continue | Reregister, Reregister);
|
||||
assert_eq!(Continue | Disable, Reregister);
|
||||
assert_eq!(Continue | Remove, Reregister);
|
||||
|
||||
assert_eq!(Reregister | Continue, Reregister);
|
||||
assert_eq!(Reregister | Reregister, Reregister);
|
||||
assert_eq!(Reregister | Disable, Reregister);
|
||||
assert_eq!(Reregister | Remove, Reregister);
|
||||
|
||||
assert_eq!(Disable | Continue, Reregister);
|
||||
assert_eq!(Disable | Reregister, Reregister);
|
||||
assert_eq!(Disable | Disable, Disable);
|
||||
assert_eq!(Disable | Remove, Reregister);
|
||||
|
||||
assert_eq!(Remove | Continue, Reregister);
|
||||
assert_eq!(Remove | Reregister, Reregister);
|
||||
assert_eq!(Remove | Disable, Reregister);
|
||||
assert_eq!(Remove | Remove, Remove);
|
||||
}
|
||||
|
||||
// Test PostAction self-assignment.
|
||||
#[test]
|
||||
fn post_action_combine_assign() {
|
||||
use super::PostAction::*;
|
||||
|
||||
let mut action = Continue;
|
||||
action |= Continue;
|
||||
assert_eq!(action, Continue);
|
||||
|
||||
let mut action = Continue;
|
||||
action |= Reregister;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Continue;
|
||||
action |= Disable;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Continue;
|
||||
action |= Remove;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Reregister;
|
||||
action |= Continue;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Reregister;
|
||||
action |= Reregister;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Reregister;
|
||||
action |= Disable;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Reregister;
|
||||
action |= Remove;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Disable;
|
||||
action |= Continue;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Disable;
|
||||
action |= Reregister;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Disable;
|
||||
action |= Disable;
|
||||
assert_eq!(action, Disable);
|
||||
|
||||
let mut action = Disable;
|
||||
action |= Remove;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Remove;
|
||||
action |= Continue;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Remove;
|
||||
action |= Reregister;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Remove;
|
||||
action |= Disable;
|
||||
assert_eq!(action, Reregister);
|
||||
|
||||
let mut action = Remove;
|
||||
action |= Remove;
|
||||
assert_eq!(action, Remove);
|
||||
}
|
||||
}
|
||||
290
third-party/vendor/calloop/src/sources/ping.rs
vendored
Normal file
290
third-party/vendor/calloop/src/sources/ping.rs
vendored
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
//! Ping to the event loop
|
||||
//!
|
||||
//! This is an event source that just produces `()` events whevener the associated
|
||||
//! [`Ping::ping`](Ping#method.ping) method is called. If the event source is pinged multiple times
|
||||
//! between a single dispatching, it'll only generate one event.
|
||||
//!
|
||||
//! This event source is a simple way of waking up the event loop from an other part of your program
|
||||
//! (and is what backs the [`LoopSignal`](crate::LoopSignal)). It can also be used as a building
|
||||
//! block to construct event sources whose source of event is not file descriptor, but rather an
|
||||
//! userspace source (like an other thread).
|
||||
|
||||
use nix::unistd::close;
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
// The ping source has platform-dependent implementations provided by modules
|
||||
// under this one. These modules should expose:
|
||||
// - a make_ping() function
|
||||
// - a Ping type
|
||||
// - a PingSource type
|
||||
//
|
||||
// See eg. the pipe implementation for these items' specific requirements.
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod eventfd;
|
||||
#[cfg(target_os = "linux")]
|
||||
use eventfd as platform;
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
mod pipe;
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
use pipe as platform;
|
||||
|
||||
/// Create a new ping event source
|
||||
///
|
||||
/// you are given a [`Ping`] instance, which can be cloned and used to ping the
|
||||
/// event loop, and a [`PingSource`], which you can insert in your event loop to
|
||||
/// receive the pings.
|
||||
pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
|
||||
platform::make_ping()
|
||||
}
|
||||
|
||||
/// The ping event source
|
||||
///
|
||||
/// You can insert it in your event loop to receive pings.
|
||||
///
|
||||
/// If you use it directly, it will automatically remove itself from the event loop
|
||||
/// once all [`Ping`] instances are dropped.
|
||||
pub type Ping = platform::Ping;
|
||||
|
||||
/// The Ping handle
|
||||
///
|
||||
/// This handle can be cloned and sent accross threads. It can be used to
|
||||
/// send pings to the `PingSource`.
|
||||
pub type PingSource = platform::PingSource;
|
||||
|
||||
/// An error arising from processing events for a ping.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[error(transparent)]
|
||||
pub struct PingError(Box<dyn std::error::Error + Sync + Send>);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CloseOnDrop(RawFd);
|
||||
|
||||
impl Drop for CloseOnDrop {
|
||||
fn drop(&mut self) {
|
||||
if let Err(e) = close(self.0) {
|
||||
log::warn!("[calloop] Failed to close ping fd: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::transient::TransientSource;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn ping() {
|
||||
let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
|
||||
|
||||
let (ping, source) = make_ping().unwrap();
|
||||
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(source, |(), &mut (), dispatched| *dispatched = true)
|
||||
.unwrap();
|
||||
|
||||
ping.ping();
|
||||
|
||||
let mut dispatched = false;
|
||||
event_loop
|
||||
.dispatch(std::time::Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(dispatched);
|
||||
|
||||
// Ping has been drained an no longer generates events
|
||||
let mut dispatched = false;
|
||||
event_loop
|
||||
.dispatch(std::time::Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(!dispatched);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ping_closed() {
|
||||
let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
|
||||
|
||||
let (_, source) = make_ping().unwrap();
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(source, |(), &mut (), dispatched| *dispatched = true)
|
||||
.unwrap();
|
||||
|
||||
let mut dispatched = false;
|
||||
|
||||
// If the sender is closed from the start, the ping should first trigger
|
||||
// once, disabling itself but not invoking the callback
|
||||
event_loop
|
||||
.dispatch(std::time::Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(!dispatched);
|
||||
|
||||
// Then it should not trigger any more, so this dispatch should wait the whole 100ms
|
||||
let now = std::time::Instant::now();
|
||||
event_loop
|
||||
.dispatch(std::time::Duration::from_millis(100), &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(now.elapsed() >= std::time::Duration::from_millis(100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ping_removed() {
|
||||
// This keeps track of whether the event fired.
|
||||
let mut dispatched = false;
|
||||
|
||||
let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
|
||||
|
||||
let (sender, source) = make_ping().unwrap();
|
||||
let wrapper = TransientSource::from(source);
|
||||
|
||||
// Check that the source starts off in the wrapper.
|
||||
assert!(!matches!(wrapper, TransientSource::None));
|
||||
|
||||
// Put the source in the loop.
|
||||
|
||||
let dispatcher =
|
||||
crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
|
||||
|
||||
let token = event_loop
|
||||
.handle()
|
||||
.register_dispatcher(dispatcher.clone())
|
||||
.unwrap();
|
||||
|
||||
// Drop the sender and check that it's actually removed.
|
||||
drop(sender);
|
||||
|
||||
// There should be no event, but the loop still needs to wake up to
|
||||
// process the close event (just like in the ping_closed() test).
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(!dispatched);
|
||||
|
||||
// Pull the source wrapper out.
|
||||
|
||||
event_loop.handle().remove(token);
|
||||
let wrapper = dispatcher.into_source_inner();
|
||||
|
||||
// Check that the inner source is now gone.
|
||||
assert!(matches!(wrapper, TransientSource::None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ping_fired_and_removed() {
|
||||
// This is like ping_removed() with the single difference that we fire a
|
||||
// ping and drop it between two successive dispatches of the loop.
|
||||
|
||||
// This keeps track of whether the event fired.
|
||||
let mut dispatched = false;
|
||||
|
||||
let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
|
||||
|
||||
let (sender, source) = make_ping().unwrap();
|
||||
let wrapper = TransientSource::from(source);
|
||||
|
||||
// Check that the source starts off in the wrapper.
|
||||
assert!(!matches!(wrapper, TransientSource::None));
|
||||
|
||||
// Put the source in the loop.
|
||||
|
||||
let dispatcher =
|
||||
crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
|
||||
|
||||
let token = event_loop
|
||||
.handle()
|
||||
.register_dispatcher(dispatcher.clone())
|
||||
.unwrap();
|
||||
|
||||
// Send a ping AND drop the sender and check that it's actually removed.
|
||||
sender.ping();
|
||||
drop(sender);
|
||||
|
||||
// There should be an event, but the source should be removed from the
|
||||
// loop immediately after.
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(dispatched);
|
||||
|
||||
// Pull the source wrapper out.
|
||||
|
||||
event_loop.handle().remove(token);
|
||||
let wrapper = dispatcher.into_source_inner();
|
||||
|
||||
// Check that the inner source is now gone.
|
||||
assert!(matches!(wrapper, TransientSource::None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ping_multiple_senders() {
|
||||
// This is like ping_removed() but for testing the behaviour of multiple
|
||||
// senders.
|
||||
|
||||
// This keeps track of whether the event fired.
|
||||
let mut dispatched = false;
|
||||
|
||||
let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
|
||||
|
||||
let (sender0, source) = make_ping().unwrap();
|
||||
let wrapper = TransientSource::from(source);
|
||||
let sender1 = sender0.clone();
|
||||
let sender2 = sender1.clone();
|
||||
|
||||
// Check that the source starts off in the wrapper.
|
||||
assert!(!matches!(wrapper, TransientSource::None));
|
||||
|
||||
// Put the source in the loop.
|
||||
|
||||
let dispatcher =
|
||||
crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
|
||||
|
||||
let token = event_loop
|
||||
.handle()
|
||||
.register_dispatcher(dispatcher.clone())
|
||||
.unwrap();
|
||||
|
||||
// Send a ping AND drop the sender and check that it's actually removed.
|
||||
sender0.ping();
|
||||
drop(sender0);
|
||||
|
||||
// There should be an event, and the source should remain in the loop.
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(dispatched);
|
||||
|
||||
// Now test that the clones still work. Drop after the dispatch loop
|
||||
// instead of before, this time.
|
||||
dispatched = false;
|
||||
|
||||
sender1.ping();
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(dispatched);
|
||||
|
||||
// Finally, drop all of them without sending anything.
|
||||
|
||||
dispatched = false;
|
||||
|
||||
drop(sender1);
|
||||
drop(sender2);
|
||||
|
||||
event_loop
|
||||
.dispatch(Duration::ZERO, &mut dispatched)
|
||||
.unwrap();
|
||||
assert!(!dispatched);
|
||||
|
||||
// Pull the source wrapper out.
|
||||
|
||||
event_loop.handle().remove(token);
|
||||
let wrapper = dispatcher.into_source_inner();
|
||||
|
||||
// Check that the inner source is now gone.
|
||||
assert!(matches!(wrapper, TransientSource::None));
|
||||
}
|
||||
}
|
||||
189
third-party/vendor/calloop/src/sources/ping/eventfd.rs
vendored
Normal file
189
third-party/vendor/calloop/src/sources/ping/eventfd.rs
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
//! Eventfd based implementation of the ping event source.
|
||||
//!
|
||||
//! # Implementation notes
|
||||
//!
|
||||
//! The eventfd is a much lighter signalling mechanism provided by the Linux
|
||||
//! kernel. Rather than write an arbitrary sequence of bytes, it only has a
|
||||
//! 64-bit counter.
|
||||
//!
|
||||
//! To avoid closing the eventfd early, we wrap it in a RAII-style closer
|
||||
//! `CloseOnDrop` in `make_ping()`. When all the senders are dropped, another
|
||||
//! wrapper `FlagOnDrop` handles signalling this to the event source, which is
|
||||
//! the sole owner of the eventfd itself. The senders have weak references to
|
||||
//! the eventfd, and if the source is dropped before the senders, they will
|
||||
//! simply not do anything (except log a message).
|
||||
//!
|
||||
//! To differentiate between regular ping events and close ping events, we add 2
|
||||
//! to the counter for regular events and 1 for close events. In the source we
|
||||
//! can then check the LSB and if it's set, we know it was a close event. This
|
||||
//! only works if a close event never fires more than once.
|
||||
|
||||
use std::{os::unix::io::RawFd, sync::Arc};
|
||||
|
||||
use nix::sys::eventfd::{eventfd, EfdFlags};
|
||||
use nix::unistd::{read, write};
|
||||
|
||||
use super::{CloseOnDrop, PingError};
|
||||
use crate::{
|
||||
generic::Generic, EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory,
|
||||
};
|
||||
|
||||
// These are not bitfields! They are increments to add to the eventfd counter.
|
||||
// Since the fd can only be closed once, we can effectively use the
|
||||
// INCREMENT_CLOSE value as a bitmask when checking.
|
||||
const INCREMENT_PING: u64 = 0x2;
|
||||
const INCREMENT_CLOSE: u64 = 0x1;
|
||||
|
||||
#[inline]
|
||||
pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
|
||||
let read = eventfd(0, EfdFlags::EFD_CLOEXEC | EfdFlags::EFD_NONBLOCK)?;
|
||||
|
||||
// We only have one fd for the eventfd. If the sending end closes it when
|
||||
// all copies are dropped, the receiving end will be closed as well. We need
|
||||
// to make sure the fd is not closed until all holders of it have dropped
|
||||
// it.
|
||||
|
||||
let fd_arc = Arc::new(CloseOnDrop(read));
|
||||
|
||||
let ping = Ping {
|
||||
event: Arc::new(FlagOnDrop(Arc::clone(&fd_arc))),
|
||||
};
|
||||
|
||||
let source = PingSource {
|
||||
event: Generic::new(read, Interest::READ, Mode::Level),
|
||||
_fd: fd_arc,
|
||||
};
|
||||
|
||||
Ok((ping, source))
|
||||
}
|
||||
|
||||
// Helper functions for the event source IO.
|
||||
|
||||
#[inline]
|
||||
fn send_ping(fd: RawFd, count: u64) -> std::io::Result<()> {
|
||||
assert!(count > 0);
|
||||
match write(fd, &count.to_ne_bytes()) {
|
||||
// The write succeeded, the ping will wake up the loop.
|
||||
Ok(_) => Ok(()),
|
||||
|
||||
// The counter hit its cap, which means previous calls to write() will
|
||||
// wake up the loop.
|
||||
Err(nix::errno::Errno::EAGAIN) => Ok(()),
|
||||
|
||||
// Anything else is a real error.
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn drain_ping(fd: RawFd) -> std::io::Result<u64> {
|
||||
// The eventfd counter is effectively a u64.
|
||||
const NBYTES: usize = 8;
|
||||
let mut buf = [0u8; NBYTES];
|
||||
|
||||
match read(fd, &mut buf) {
|
||||
// Reading from an eventfd should only ever produce 8 bytes. No looping
|
||||
// is required.
|
||||
Ok(NBYTES) => Ok(u64::from_ne_bytes(buf)),
|
||||
|
||||
Ok(_) => unreachable!(),
|
||||
|
||||
// Any other error can be propagated.
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
// The event source is simply a generic source with one of the eventfds.
|
||||
#[derive(Debug)]
|
||||
pub struct PingSource {
|
||||
event: Generic<RawFd>,
|
||||
|
||||
// This is held only to ensure that there is an owner of the fd that lives
|
||||
// as long as the Generic source, so that the fd is not closed unexpectedly
|
||||
// when all the senders are dropped.
|
||||
_fd: Arc<CloseOnDrop>,
|
||||
}
|
||||
|
||||
impl EventSource for PingSource {
|
||||
type Event = ();
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
type Error = PingError;
|
||||
|
||||
fn process_events<C>(
|
||||
&mut self,
|
||||
readiness: Readiness,
|
||||
token: Token,
|
||||
mut callback: C,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
self.event
|
||||
.process_events(readiness, token, |_, &mut fd| {
|
||||
let counter = drain_ping(fd)?;
|
||||
|
||||
// If the LSB is set, it means we were closed. If anything else
|
||||
// is also set, it means we were pinged. The two are not
|
||||
// mutually exclusive.
|
||||
let close = (counter & INCREMENT_CLOSE) != 0;
|
||||
let ping = (counter & (u64::MAX - 1)) != 0;
|
||||
|
||||
if ping {
|
||||
callback((), &mut ());
|
||||
}
|
||||
|
||||
if close {
|
||||
Ok(PostAction::Remove)
|
||||
} else {
|
||||
Ok(PostAction::Continue)
|
||||
}
|
||||
})
|
||||
.map_err(|e| PingError(e.into()))
|
||||
}
|
||||
|
||||
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
|
||||
self.event.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.event.reregister(poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
|
||||
self.event.unregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Ping {
|
||||
// This is an Arc because it's potentially shared with clones. The last one
|
||||
// dropped needs to signal to the event source via the eventfd.
|
||||
event: Arc<FlagOnDrop>,
|
||||
}
|
||||
|
||||
impl Ping {
|
||||
/// Send a ping to the `PingSource`.
|
||||
pub fn ping(&self) {
|
||||
if let Err(e) = send_ping(self.event.0 .0, INCREMENT_PING) {
|
||||
log::warn!("[calloop] Failed to write a ping: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This manages signalling to the PingSource when it's dropped. There should
|
||||
/// only ever be one of these per PingSource.
|
||||
#[derive(Debug)]
|
||||
struct FlagOnDrop(Arc<CloseOnDrop>);
|
||||
|
||||
impl Drop for FlagOnDrop {
|
||||
fn drop(&mut self) {
|
||||
if let Err(e) = send_ping(self.0 .0, INCREMENT_CLOSE) {
|
||||
log::warn!("[calloop] Failed to send close ping: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
162
third-party/vendor/calloop/src/sources/ping/pipe.rs
vendored
Normal file
162
third-party/vendor/calloop/src/sources/ping/pipe.rs
vendored
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
//! Pipe based implementation of the ping event source, using the pipe or pipe2
|
||||
//! syscall. Sending a ping involves writing to one end of a pipe, and the other
|
||||
//! end becoming readable is what wakes up the event loop.
|
||||
|
||||
use std::{os::unix::io::RawFd, sync::Arc};
|
||||
|
||||
use nix::fcntl::OFlag;
|
||||
use nix::unistd::{close, read, write};
|
||||
|
||||
use super::{CloseOnDrop, PingError};
|
||||
use crate::{
|
||||
generic::Generic, EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[inline]
|
||||
fn make_ends() -> std::io::Result<(RawFd, RawFd)> {
|
||||
// macOS does not have pipe2, but we can emulate the behavior of pipe2 by
|
||||
// setting the flags after calling pipe.
|
||||
use nix::{
|
||||
fcntl::{fcntl, FcntlArg},
|
||||
unistd::pipe,
|
||||
};
|
||||
|
||||
let (read, write) = pipe()?;
|
||||
|
||||
let read_flags = OFlag::from_bits_truncate(fcntl(read, FcntlArg::F_GETFD)?)
|
||||
| OFlag::O_CLOEXEC
|
||||
| OFlag::O_NONBLOCK;
|
||||
let write_flags = OFlag::from_bits_truncate(fcntl(write, FcntlArg::F_GETFD)?)
|
||||
| OFlag::O_CLOEXEC
|
||||
| OFlag::O_NONBLOCK;
|
||||
|
||||
fcntl(read, FcntlArg::F_SETFL(read_flags))?;
|
||||
fcntl(write, FcntlArg::F_SETFL(write_flags))?;
|
||||
|
||||
Ok((read, write))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[inline]
|
||||
fn make_ends() -> std::io::Result<(RawFd, RawFd)> {
|
||||
Ok(nix::unistd::pipe2(OFlag::O_CLOEXEC | OFlag::O_NONBLOCK)?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
|
||||
let (read, write) = make_ends()?;
|
||||
|
||||
let source = PingSource {
|
||||
pipe: Generic::new(read, Interest::READ, Mode::Level),
|
||||
};
|
||||
let ping = Ping {
|
||||
pipe: Arc::new(CloseOnDrop(write)),
|
||||
};
|
||||
Ok((ping, source))
|
||||
}
|
||||
|
||||
// Helper functions for the event source IO.
|
||||
|
||||
#[inline]
|
||||
fn send_ping(fd: RawFd) -> std::io::Result<()> {
|
||||
write(fd, &[0u8])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// The event source is simply a generic source with the FD of the read end of
|
||||
// the pipe.
|
||||
#[derive(Debug)]
|
||||
pub struct PingSource {
|
||||
pipe: Generic<RawFd>,
|
||||
}
|
||||
|
||||
impl EventSource for PingSource {
|
||||
type Event = ();
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
type Error = PingError;
|
||||
|
||||
fn process_events<C>(
|
||||
&mut self,
|
||||
readiness: Readiness,
|
||||
token: Token,
|
||||
mut callback: C,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
self.pipe
|
||||
.process_events(readiness, token, |_, &mut fd| {
|
||||
let mut buf = [0u8; 32];
|
||||
let mut read_something = false;
|
||||
let mut action = PostAction::Continue;
|
||||
|
||||
loop {
|
||||
match read(fd, &mut buf) {
|
||||
Ok(0) => {
|
||||
// The other end of the pipe was closed, mark ourselves
|
||||
// for removal.
|
||||
action = PostAction::Remove;
|
||||
break;
|
||||
}
|
||||
|
||||
// Got one or more pings.
|
||||
Ok(_) => read_something = true,
|
||||
|
||||
// Nothing more to read.
|
||||
Err(nix::errno::Errno::EAGAIN) => break,
|
||||
|
||||
// Propagate error.
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
if read_something {
|
||||
callback((), &mut ());
|
||||
}
|
||||
Ok(action)
|
||||
})
|
||||
.map_err(|e| PingError(e.into()))
|
||||
}
|
||||
|
||||
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
|
||||
self.pipe.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.pipe.reregister(poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
|
||||
self.pipe.unregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PingSource {
|
||||
fn drop(&mut self) {
|
||||
if let Err(e) = close(self.pipe.file) {
|
||||
log::warn!("[calloop] Failed to close read ping: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The sending end of the ping writes zeroes to the write end of the pipe.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Ping {
|
||||
pipe: Arc<CloseOnDrop>,
|
||||
}
|
||||
|
||||
// The sending end of the ping writes zeroes to the write end of the pipe.
|
||||
impl Ping {
|
||||
/// Send a ping to the `PingSource`
|
||||
pub fn ping(&self) {
|
||||
if let Err(e) = send_ping(self.pipe.0) {
|
||||
log::warn!("[calloop] Failed to write a ping: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
176
third-party/vendor/calloop/src/sources/signals.rs
vendored
Normal file
176
third-party/vendor/calloop/src/sources/signals.rs
vendored
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
//! Event source for tracking Unix signals
|
||||
//!
|
||||
//! Only available on Linux.
|
||||
//!
|
||||
//! This allows you to track and receive Unix signals through the event loop
|
||||
//! rather than by registering signal handlers. It uses `signalfd` under the hood.
|
||||
//!
|
||||
//! The source will take care of masking and unmasking signals for the thread it runs on,
|
||||
//! but you are responsible for masking them on other threads if you run them. The simplest
|
||||
//! way to ensure that is to setup the signal event source before spawning any thread, as
|
||||
//! they'll inherit their parent signal mask.
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use nix::sys::signal::SigSet;
|
||||
pub use nix::sys::signal::Signal;
|
||||
pub use nix::sys::signalfd::siginfo;
|
||||
use nix::sys::signalfd::{SfdFlags, SignalFd};
|
||||
|
||||
use super::generic::Generic;
|
||||
use crate::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory};
|
||||
|
||||
/// An event generated by the signal event source
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Event {
|
||||
info: siginfo,
|
||||
}
|
||||
|
||||
impl Event {
|
||||
/// Retrieve the signal number that was receive
|
||||
pub fn signal(&self) -> Signal {
|
||||
Signal::try_from(self.info.ssi_signo as c_int).unwrap()
|
||||
}
|
||||
|
||||
/// Access the full `siginfo_t` associated with this signal event
|
||||
pub fn full_info(&self) -> siginfo {
|
||||
self.info
|
||||
}
|
||||
}
|
||||
|
||||
/// An event source for receiving Unix signals
|
||||
#[derive(Debug)]
|
||||
pub struct Signals {
|
||||
sfd: Generic<SignalFd>,
|
||||
mask: SigSet,
|
||||
}
|
||||
|
||||
impl Signals {
|
||||
/// Create a new signal event source listening on the specified list of signals
|
||||
pub fn new(signals: &[Signal]) -> crate::Result<Signals> {
|
||||
let mut mask = SigSet::empty();
|
||||
for &s in signals {
|
||||
mask.add(s);
|
||||
}
|
||||
|
||||
// Mask the signals for this thread
|
||||
mask.thread_block()?;
|
||||
// Create the SignalFd
|
||||
let sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK | SfdFlags::SFD_CLOEXEC)?;
|
||||
|
||||
Ok(Signals {
|
||||
sfd: Generic::new(sfd, Interest::READ, Mode::Level),
|
||||
mask,
|
||||
})
|
||||
}
|
||||
|
||||
/// Add a list of signals to the signals source
|
||||
///
|
||||
/// If this function returns an error, the signal mask of the thread may
|
||||
/// have still been changed.
|
||||
pub fn add_signals(&mut self, signals: &[Signal]) -> crate::Result<()> {
|
||||
for &s in signals {
|
||||
self.mask.add(s);
|
||||
}
|
||||
self.mask.thread_block()?;
|
||||
self.sfd.file.set_mask(&self.mask)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a list of signals from the signals source
|
||||
///
|
||||
/// If this function returns an error, the signal mask of the thread may
|
||||
/// have still been changed.
|
||||
pub fn remove_signals(&mut self, signals: &[Signal]) -> crate::Result<()> {
|
||||
let mut removed = SigSet::empty();
|
||||
for &s in signals {
|
||||
self.mask.remove(s);
|
||||
removed.add(s);
|
||||
}
|
||||
removed.thread_unblock()?;
|
||||
self.sfd.file.set_mask(&self.mask)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Replace the list of signals of the source
|
||||
///
|
||||
/// If this function returns an error, the signal mask of the thread may
|
||||
/// have still been changed.
|
||||
pub fn set_signals(&mut self, signals: &[Signal]) -> crate::Result<()> {
|
||||
let mut new_mask = SigSet::empty();
|
||||
for &s in signals {
|
||||
new_mask.add(s);
|
||||
}
|
||||
|
||||
self.mask.thread_unblock()?;
|
||||
new_mask.thread_block()?;
|
||||
self.sfd.file.set_mask(&new_mask)?;
|
||||
self.mask = new_mask;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Signals {
|
||||
fn drop(&mut self) {
|
||||
// we cannot handle error here
|
||||
if let Err(e) = self.mask.thread_unblock() {
|
||||
log::warn!("[calloop] Failed to unmask signals: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventSource for Signals {
|
||||
type Event = Event;
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
type Error = SignalError;
|
||||
|
||||
fn process_events<C>(
|
||||
&mut self,
|
||||
readiness: Readiness,
|
||||
token: Token,
|
||||
mut callback: C,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
self.sfd
|
||||
.process_events(readiness, token, |_, sfd| {
|
||||
loop {
|
||||
match sfd.read_signal() {
|
||||
Ok(Some(info)) => callback(Event { info }, &mut ()),
|
||||
Ok(None) => break,
|
||||
Err(e) => {
|
||||
log::warn!("[callop] Error reading from signalfd: {}", e);
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(PostAction::Continue)
|
||||
})
|
||||
.map_err(|e| SignalError(e.into()))
|
||||
}
|
||||
|
||||
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
|
||||
self.sfd.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.sfd.reregister(poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
|
||||
self.sfd.unregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
/// An error arising from processing events for a process signal.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[error(transparent)]
|
||||
pub struct SignalError(Box<dyn std::error::Error + Sync + Send>);
|
||||
571
third-party/vendor/calloop/src/sources/timer.rs
vendored
Normal file
571
third-party/vendor/calloop/src/sources/timer.rs
vendored
Normal file
|
|
@ -0,0 +1,571 @@
|
|||
//! Timer event source
|
||||
//!
|
||||
//! The [`Timer`] is an event source that will fire its event after a certain amount of time
|
||||
//! specified at creation. Its timing is tracked directly by the event loop core logic, and it does
|
||||
//! not consume any system resource.
|
||||
//!
|
||||
//! The timer precision depends on whether the loop was initialized in high-precision mode. If not,
|
||||
//! you can expect precision of order of 1 millisecond, if you need sub-millisecond precision,
|
||||
//! make sure you initialize the [`EventLoop`](crate::EventLoop) using
|
||||
//! [`EventLoop::try_new_high_precision()`](crate::EventLoop::try_new_high_precision). Note also
|
||||
//! that if you need to rely on good precision timers in general, you may need to enable realtime
|
||||
//! features of your OS to ensure your thread is quickly woken up by the system scheduler.
|
||||
//!
|
||||
//! The provided event is an [`Instant`] representing the deadline for which this timer has fired
|
||||
//! (which can be earlier than the current time depending on the event loop congestion).
|
||||
//!
|
||||
//! The callback associated with this event source is expected to return a [`TimeoutAction`], which
|
||||
//! can be used to implement self-repeating timers by telling calloop to reprogram the same timer
|
||||
//! for a later timeout after it has fired.
|
||||
|
||||
/*
|
||||
* This module provides two main types:
|
||||
*
|
||||
* - `Timer` is the user-facing type that represents a timer event source
|
||||
* - `TimerWheel` is an internal data structure for tracking registered timeouts, it is used by
|
||||
* the polling logic in sys/mod.rs
|
||||
*/
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::BinaryHeap,
|
||||
rc::Rc,
|
||||
task::Waker,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::{EventSource, LoopHandle, Poll, PostAction, Readiness, Token, TokenFactory};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Registration {
|
||||
token: Token,
|
||||
wheel: Rc<RefCell<TimerWheel>>,
|
||||
counter: u32,
|
||||
}
|
||||
|
||||
/// A timer event source
|
||||
///
|
||||
/// When registered to the event loop, it will trigger an event once its deadline is reached.
|
||||
/// If the deadline is in the past relative to the moment of its insertion in the event loop,
|
||||
/// the `TImer` will trigger an event as soon as the event loop is dispatched.
|
||||
#[derive(Debug)]
|
||||
pub struct Timer {
|
||||
registration: Option<Registration>,
|
||||
deadline: Instant,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
/// Create a timer that will fire immediately when inserted in the event loop
|
||||
pub fn immediate() -> Timer {
|
||||
Self::from_deadline(Instant::now())
|
||||
}
|
||||
|
||||
/// Create a timer that will fire after a given duration from now
|
||||
pub fn from_duration(duration: Duration) -> Timer {
|
||||
Self::from_deadline(Instant::now() + duration)
|
||||
}
|
||||
|
||||
/// Create a timer that will fire at a given instant
|
||||
pub fn from_deadline(deadline: Instant) -> Timer {
|
||||
Timer {
|
||||
registration: None,
|
||||
deadline,
|
||||
}
|
||||
}
|
||||
|
||||
/// Changes the deadline of this timer to an [`Instant`]
|
||||
///
|
||||
/// If the `Timer` is currently registered in the event loop, it needs to be
|
||||
/// re-registered for this change to take effect.
|
||||
pub fn set_deadline(&mut self, deadline: Instant) {
|
||||
self.deadline = deadline;
|
||||
}
|
||||
|
||||
/// Changes the deadline of this timer to a [`Duration`] from now
|
||||
///
|
||||
/// If the `Timer` is currently registered in the event loop, it needs to be
|
||||
/// re-registered for this change to take effect.
|
||||
pub fn set_duration(&mut self, duration: Duration) {
|
||||
self.set_deadline(Instant::now() + duration)
|
||||
}
|
||||
|
||||
/// Get the current deadline of this `Timer`
|
||||
pub fn current_deadline(&self) -> Instant {
|
||||
self.deadline
|
||||
}
|
||||
}
|
||||
|
||||
impl EventSource for Timer {
|
||||
type Event = Instant;
|
||||
type Metadata = ();
|
||||
type Ret = TimeoutAction;
|
||||
type Error = std::io::Error;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
_: Readiness,
|
||||
token: Token,
|
||||
mut callback: F,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
if let Some(ref registration) = self.registration {
|
||||
if registration.token != token {
|
||||
return Ok(PostAction::Continue);
|
||||
}
|
||||
let new_deadline = match callback(self.deadline, &mut ()) {
|
||||
TimeoutAction::Drop => return Ok(PostAction::Remove),
|
||||
TimeoutAction::ToInstant(instant) => instant,
|
||||
TimeoutAction::ToDuration(duration) => Instant::now() + duration,
|
||||
};
|
||||
// If we received an event, we MUST have a valid counter value
|
||||
registration.wheel.borrow_mut().insert_reuse(
|
||||
registration.counter,
|
||||
new_deadline,
|
||||
registration.token,
|
||||
);
|
||||
self.deadline = new_deadline;
|
||||
}
|
||||
Ok(PostAction::Continue)
|
||||
}
|
||||
|
||||
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
|
||||
let wheel = poll.timers.clone();
|
||||
let token = token_factory.token();
|
||||
let counter = wheel.borrow_mut().insert(self.deadline, token);
|
||||
self.registration = Some(Registration {
|
||||
token,
|
||||
wheel,
|
||||
counter,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.unregister(poll)?;
|
||||
self.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
|
||||
if let Some(registration) = self.registration.take() {
|
||||
poll.timers.borrow_mut().cancel(registration.counter);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Action to reschedule a timeout if necessary
|
||||
#[derive(Debug)]
|
||||
pub enum TimeoutAction {
|
||||
/// Don't reschedule this timer
|
||||
Drop,
|
||||
/// Reschedule this timer to a given [`Instant`]
|
||||
ToInstant(Instant),
|
||||
/// Reschedule this timer to a given [`Duration`] in the future
|
||||
ToDuration(Duration),
|
||||
}
|
||||
|
||||
// Internal representation of a timeout registered in the TimerWheel
|
||||
#[derive(Debug)]
|
||||
struct TimeoutData {
|
||||
deadline: Instant,
|
||||
token: RefCell<Option<Token>>,
|
||||
counter: u32,
|
||||
}
|
||||
|
||||
// A data structure for tracking registered timeouts
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct TimerWheel {
|
||||
heap: BinaryHeap<TimeoutData>,
|
||||
counter: u32,
|
||||
}
|
||||
|
||||
impl TimerWheel {
|
||||
pub(crate) fn new() -> TimerWheel {
|
||||
TimerWheel {
|
||||
heap: BinaryHeap::new(),
|
||||
counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn insert(&mut self, deadline: Instant, token: Token) -> u32 {
|
||||
self.heap.push(TimeoutData {
|
||||
deadline,
|
||||
token: RefCell::new(Some(token)),
|
||||
counter: self.counter,
|
||||
});
|
||||
let ret = self.counter;
|
||||
self.counter += 1;
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) fn insert_reuse(&mut self, counter: u32, deadline: Instant, token: Token) {
|
||||
self.heap.push(TimeoutData {
|
||||
deadline,
|
||||
token: RefCell::new(Some(token)),
|
||||
counter,
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn cancel(&mut self, counter: u32) {
|
||||
self.heap
|
||||
.iter()
|
||||
.find(|data| data.counter == counter)
|
||||
.map(|data| data.token.take());
|
||||
}
|
||||
|
||||
pub(crate) fn next_expired(&mut self, now: Instant) -> Option<(u32, Token)> {
|
||||
loop {
|
||||
// check if there is an expired item
|
||||
if let Some(data) = self.heap.peek() {
|
||||
if data.deadline > now {
|
||||
return None;
|
||||
}
|
||||
// there is an expired timeout, continue the
|
||||
// loop body
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
|
||||
// There is an item in the heap, this unwrap cannot blow
|
||||
let data = self.heap.pop().unwrap();
|
||||
if let Some(token) = data.token.into_inner() {
|
||||
return Some((data.counter, token));
|
||||
}
|
||||
// otherwise this timeout was cancelled, continue looping
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn next_deadline(&self) -> Option<std::time::Instant> {
|
||||
self.heap.peek().map(|data| data.deadline)
|
||||
}
|
||||
}
|
||||
|
||||
// trait implementations for TimeoutData
|
||||
|
||||
impl std::cmp::Ord for TimeoutData {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
// earlier values have priority
|
||||
self.deadline.cmp(&other.deadline).reverse()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::cmp::PartialOrd for TimeoutData {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
// This impl is required for PartialOrd but actually never used
|
||||
// and the type is private, so ignore its coverage
|
||||
impl std::cmp::PartialEq for TimeoutData {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.deadline == other.deadline
|
||||
}
|
||||
}
|
||||
|
||||
impl std::cmp::Eq for TimeoutData {}
|
||||
|
||||
// Logic for timer futures
|
||||
|
||||
/// A future that resolves once a certain timeout is expired
|
||||
pub struct TimeoutFuture {
|
||||
deadline: Instant,
|
||||
waker: Rc<RefCell<Option<Waker>>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for TimeoutFuture {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("TimeoutFuture")
|
||||
.field("deadline", &self.deadline)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeoutFuture {
|
||||
/// Create a future that resolves after a given duration
|
||||
pub fn from_duration<Data>(handle: &LoopHandle<'_, Data>, duration: Duration) -> TimeoutFuture {
|
||||
Self::from_deadline(handle, Instant::now() + duration)
|
||||
}
|
||||
|
||||
/// Create a future that resolves at a given instant
|
||||
pub fn from_deadline<Data>(handle: &LoopHandle<'_, Data>, deadline: Instant) -> TimeoutFuture {
|
||||
let timer = Timer::from_deadline(deadline);
|
||||
let waker = Rc::new(RefCell::new(None::<Waker>));
|
||||
let waker2 = waker.clone();
|
||||
handle
|
||||
.insert_source(timer, move |_, &mut (), _| {
|
||||
if let Some(waker) = waker2.borrow_mut().clone() {
|
||||
waker.wake()
|
||||
}
|
||||
TimeoutAction::Drop
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
TimeoutFuture { deadline, waker }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::future::Future for TimeoutFuture {
|
||||
type Output = ();
|
||||
|
||||
fn poll(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Self::Output> {
|
||||
if std::time::Instant::now() >= self.deadline {
|
||||
return std::task::Poll::Ready(());
|
||||
}
|
||||
*self.waker.borrow_mut() = Some(cx.waker().clone());
|
||||
std::task::Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::*;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn simple_timer() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let mut dispatched = false;
|
||||
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(
|
||||
Timer::from_duration(Duration::from_millis(100)),
|
||||
|_, &mut (), dispatched| {
|
||||
*dispatched = true;
|
||||
TimeoutAction::Drop
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::ZERO), &mut dispatched)
|
||||
.unwrap();
|
||||
// not yet dispatched
|
||||
assert!(!dispatched);
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(150)), &mut dispatched)
|
||||
.unwrap();
|
||||
// now dispatched
|
||||
assert!(dispatched);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_timer_instant() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let mut dispatched = false;
|
||||
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(
|
||||
Timer::from_duration(Duration::from_millis(100)),
|
||||
|_, &mut (), dispatched| {
|
||||
*dispatched = true;
|
||||
TimeoutAction::Drop
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(150)), &mut dispatched)
|
||||
.unwrap();
|
||||
// now dispatched
|
||||
assert!(dispatched);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn immediate_timer() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let mut dispatched = false;
|
||||
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(Timer::immediate(), |_, &mut (), dispatched| {
|
||||
*dispatched = true;
|
||||
TimeoutAction::Drop
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::ZERO), &mut dispatched)
|
||||
.unwrap();
|
||||
// now dispatched
|
||||
assert!(dispatched);
|
||||
}
|
||||
|
||||
// We cannot actually test high precision timers, as they are only high precision in release mode
|
||||
// This test is here to ensure that the high-precision codepath are executed and work as intended
|
||||
// even if we cannot test if they are actually high precision
|
||||
#[test]
|
||||
fn high_precision_timer() {
|
||||
let mut event_loop = EventLoop::try_new_high_precision().unwrap();
|
||||
|
||||
let mut dispatched = false;
|
||||
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(
|
||||
Timer::from_duration(Duration::from_millis(100)),
|
||||
|_, &mut (), dispatched| {
|
||||
*dispatched = true;
|
||||
TimeoutAction::Drop
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::ZERO), &mut dispatched)
|
||||
.unwrap();
|
||||
// not yet dispatched
|
||||
assert!(!dispatched);
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_micros(10200)), &mut dispatched)
|
||||
.unwrap();
|
||||
// yet not dispatched
|
||||
assert!(!dispatched);
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(100)), &mut dispatched)
|
||||
.unwrap();
|
||||
// now dispatched
|
||||
assert!(dispatched);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_timer() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let mut dispatched = false;
|
||||
|
||||
let token = event_loop
|
||||
.handle()
|
||||
.insert_source(
|
||||
Timer::from_duration(Duration::from_millis(100)),
|
||||
|_, &mut (), dispatched| {
|
||||
*dispatched = true;
|
||||
TimeoutAction::Drop
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::ZERO), &mut dispatched)
|
||||
.unwrap();
|
||||
// not yet dispatched
|
||||
assert!(!dispatched);
|
||||
|
||||
event_loop.handle().remove(token);
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(150)), &mut dispatched)
|
||||
.unwrap();
|
||||
// still not dispatched
|
||||
assert!(!dispatched);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeating_timer() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let mut dispatched = 0;
|
||||
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(
|
||||
Timer::from_duration(Duration::from_millis(500)),
|
||||
|_, &mut (), dispatched| {
|
||||
*dispatched += 1;
|
||||
TimeoutAction::ToDuration(Duration::from_millis(500))
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(250)), &mut dispatched)
|
||||
.unwrap();
|
||||
assert_eq!(dispatched, 0);
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(510)), &mut dispatched)
|
||||
.unwrap();
|
||||
assert_eq!(dispatched, 1);
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(510)), &mut dispatched)
|
||||
.unwrap();
|
||||
assert_eq!(dispatched, 2);
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(510)), &mut dispatched)
|
||||
.unwrap();
|
||||
assert_eq!(dispatched, 3);
|
||||
}
|
||||
|
||||
#[cfg(feature = "executor")]
|
||||
#[test]
|
||||
fn timeout_future() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let mut dispatched = 0;
|
||||
|
||||
let timeout_1 =
|
||||
TimeoutFuture::from_duration(&event_loop.handle(), Duration::from_millis(500));
|
||||
let timeout_2 =
|
||||
TimeoutFuture::from_duration(&event_loop.handle(), Duration::from_millis(1500));
|
||||
|
||||
let (exec, sched) = crate::sources::futures::executor().unwrap();
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(exec, move |(), &mut (), got| {
|
||||
*got += 1;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
sched.schedule(timeout_1).unwrap();
|
||||
sched.schedule(timeout_2).unwrap();
|
||||
|
||||
// We do a 0-timeout dispatch after every regular dispatch to let the timeout triggers
|
||||
// flow back to the executor
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::ZERO), &mut dispatched)
|
||||
.unwrap();
|
||||
event_loop
|
||||
.dispatch(Some(Duration::ZERO), &mut dispatched)
|
||||
.unwrap();
|
||||
assert_eq!(dispatched, 0);
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(1000)), &mut dispatched)
|
||||
.unwrap();
|
||||
event_loop
|
||||
.dispatch(Some(Duration::ZERO), &mut dispatched)
|
||||
.unwrap();
|
||||
assert_eq!(dispatched, 1);
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(1100)), &mut dispatched)
|
||||
.unwrap();
|
||||
event_loop
|
||||
.dispatch(Some(Duration::ZERO), &mut dispatched)
|
||||
.unwrap();
|
||||
assert_eq!(dispatched, 2);
|
||||
}
|
||||
}
|
||||
669
third-party/vendor/calloop/src/sources/transient.rs
vendored
Normal file
669
third-party/vendor/calloop/src/sources/transient.rs
vendored
Normal file
|
|
@ -0,0 +1,669 @@
|
|||
//! Wrapper for a transient Calloop event source.
|
||||
//!
|
||||
//! If you have high level event source that you expect to remain in the event
|
||||
//! loop indefinitely, and another event source nested inside that one that you
|
||||
//! expect to require removal or disabling from time to time, this module can
|
||||
//! handle it for you.
|
||||
|
||||
/// A [`TransientSource`] wraps a Calloop event source and manages its
|
||||
/// registration. A user of this type only needs to perform the usual Calloop
|
||||
/// calls (`process_events()` and `*register()`) and the return value of
|
||||
/// [`process_events()`](crate::EventSource::process_events).
|
||||
///
|
||||
/// Rather than needing to check for the full set of
|
||||
/// [`PostAction`](crate::PostAction) values returned from `process_events()`,
|
||||
/// you can just check for `Continue` or `Reregister` and pass that back out
|
||||
/// through your own `process_events()` implementation. In your registration
|
||||
/// functions, you then only need to call the same function on this type ie.
|
||||
/// `register()` inside `register()` etc.
|
||||
///
|
||||
/// For example, say you have a source that contains a channel along with some
|
||||
/// other logic. If the channel's sending end has been dropped, it needs to be
|
||||
/// removed from the loop. So to manage this, you use this in your struct:
|
||||
///
|
||||
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
|
||||
/// struct CompositeSource {
|
||||
/// // Event source for channel.
|
||||
/// mpsc_receiver: TransientSource<calloop::channel::Channel<T>>,
|
||||
///
|
||||
/// // Any other fields go here...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// To create the transient source, you can simply use the `Into`
|
||||
/// implementation:
|
||||
///
|
||||
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
|
||||
/// let (sender, source) = channel();
|
||||
/// let mpsc_receiver: TransientSource<Channel> = source.into();
|
||||
/// ```
|
||||
///
|
||||
/// `TransientSource` implements [`EventSource`](crate::EventSource) and passes
|
||||
/// through `process_events()` calls, so in the parent's `process_events()`
|
||||
/// implementation you can just do this:
|
||||
///
|
||||
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
|
||||
/// fn process_events<F>(
|
||||
/// &mut self,
|
||||
/// readiness: calloop::Readiness,
|
||||
/// token: calloop::Token,
|
||||
/// callback: F,
|
||||
/// ) -> Result<calloop::PostAction, Self::Error>
|
||||
/// where
|
||||
/// F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
/// {
|
||||
/// let channel_return = self.mpsc_receiver.process_events(readiness, token, callback)?;
|
||||
///
|
||||
/// // Perform other logic here...
|
||||
///
|
||||
/// Ok(channel_return)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Note that:
|
||||
///
|
||||
/// - You can call `process_events()` on the `TransientSource<Channel>` even
|
||||
/// if the channel has been unregistered and dropped. All that will happen
|
||||
/// is that you won't get any events from it.
|
||||
///
|
||||
/// - The [`PostAction`](crate::PostAction) returned from `process_events()`
|
||||
/// will only ever be `PostAction::Continue` or `PostAction::Reregister`.
|
||||
/// You will still need to combine this with the result of any other sources
|
||||
/// (transient or not).
|
||||
///
|
||||
/// Once you return `channel_return` from your `process_events()` method (and
|
||||
/// assuming it propagates all the way up to the event loop itself through any
|
||||
/// other event sources), the event loop might call `reregister()` on your
|
||||
/// source. All your source has to do is:
|
||||
///
|
||||
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
|
||||
/// fn reregister(
|
||||
/// &mut self,
|
||||
/// poll: &mut calloop::Poll,
|
||||
/// token_factory: &mut calloop::TokenFactory,
|
||||
/// ) -> crate::Result<()> {
|
||||
/// self.mpsc_receiver.reregister(poll, token_factory)?;
|
||||
///
|
||||
/// // Other registration actions...
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The `TransientSource` will take care of updating the registration of the
|
||||
/// inner source, even if it actually needs to be unregistered or initially
|
||||
/// registered.
|
||||
#[derive(Debug)]
|
||||
pub enum TransientSource<T> {
|
||||
/// The source should be kept in the loop.
|
||||
Keep(T),
|
||||
/// The source needs to be registered with the loop.
|
||||
Register(T),
|
||||
/// The source needs to be disabled but kept.
|
||||
Disable(T),
|
||||
/// The source needs to be removed from the loop.
|
||||
Remove(T),
|
||||
/// The source has been removed from the loop and dropped (this might also
|
||||
/// be observed if there is a panic while changing states).
|
||||
None,
|
||||
}
|
||||
|
||||
impl<T> TransientSource<T> {
|
||||
/// Apply a function to the enclosed source, if it exists. It will be
|
||||
/// appplied even if the source is ready to be removed or is disabled.
|
||||
pub fn map<F, U>(&mut self, f: F) -> Option<U>
|
||||
where
|
||||
F: FnOnce(&mut T) -> U,
|
||||
{
|
||||
match self {
|
||||
TransientSource::Keep(source)
|
||||
| TransientSource::Register(source)
|
||||
| TransientSource::Disable(source)
|
||||
| TransientSource::Remove(source) => Some(f(source)),
|
||||
TransientSource::None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// If a caller needs to flag the contained source for removal or
|
||||
/// registration, we need to replace the enum variant safely. This requires
|
||||
/// having a `None` value in there temporarily while we do the swap.
|
||||
///
|
||||
/// If the variant is `None` the value will not change and `replacer` will
|
||||
/// not be called.
|
||||
///
|
||||
/// The `replacer` function here is expected to be one of the enum variant
|
||||
/// constructors eg. `replace(TransientSource::Remove)`.
|
||||
fn replace<F>(&mut self, replacer: F)
|
||||
where
|
||||
F: FnOnce(T) -> Self,
|
||||
{
|
||||
*self = match std::mem::replace(self, TransientSource::None) {
|
||||
TransientSource::Keep(source)
|
||||
| TransientSource::Register(source)
|
||||
| TransientSource::Remove(source)
|
||||
| TransientSource::Disable(source) => replacer(source),
|
||||
TransientSource::None => return,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: crate::EventSource> From<T> for TransientSource<T> {
|
||||
fn from(source: T) -> Self {
|
||||
Self::Register(source)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: crate::EventSource> crate::EventSource for TransientSource<T> {
|
||||
type Event = T::Event;
|
||||
type Metadata = T::Metadata;
|
||||
type Ret = T::Ret;
|
||||
type Error = T::Error;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: crate::Readiness,
|
||||
token: crate::Token,
|
||||
callback: F,
|
||||
) -> Result<crate::PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
let reregister = if let TransientSource::Keep(ref mut source) = self {
|
||||
let child_post_action = source.process_events(readiness, token, callback)?;
|
||||
|
||||
match child_post_action {
|
||||
// Nothing needs to change.
|
||||
crate::PostAction::Continue => false,
|
||||
|
||||
// Our child source needs re-registration, therefore this
|
||||
// wrapper needs re-registration.
|
||||
crate::PostAction::Reregister => true,
|
||||
|
||||
// If our nested source needs to be removed or disabled, we need
|
||||
// to swap it out for the "Remove" or "Disable" variant.
|
||||
crate::PostAction::Disable => {
|
||||
self.replace(TransientSource::Disable);
|
||||
true
|
||||
}
|
||||
|
||||
crate::PostAction::Remove => {
|
||||
self.replace(TransientSource::Remove);
|
||||
true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let post_action = if reregister {
|
||||
crate::PostAction::Reregister
|
||||
} else {
|
||||
crate::PostAction::Continue
|
||||
};
|
||||
|
||||
Ok(post_action)
|
||||
}
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
poll: &mut crate::Poll,
|
||||
token_factory: &mut crate::TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
match self {
|
||||
TransientSource::Keep(source) => {
|
||||
source.register(poll, token_factory)?;
|
||||
}
|
||||
TransientSource::Register(source) | TransientSource::Disable(source) => {
|
||||
source.register(poll, token_factory)?;
|
||||
self.replace(TransientSource::Keep);
|
||||
}
|
||||
TransientSource::Remove(_source) => {
|
||||
*self = TransientSource::None;
|
||||
}
|
||||
TransientSource::None => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut crate::Poll,
|
||||
token_factory: &mut crate::TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
match self {
|
||||
TransientSource::Keep(source) => source.reregister(poll, token_factory)?,
|
||||
TransientSource::Register(source) => {
|
||||
source.register(poll, token_factory)?;
|
||||
self.replace(TransientSource::Keep);
|
||||
}
|
||||
TransientSource::Disable(source) => {
|
||||
source.unregister(poll)?;
|
||||
}
|
||||
TransientSource::Remove(source) => {
|
||||
source.unregister(poll)?;
|
||||
*self = TransientSource::None;
|
||||
}
|
||||
TransientSource::None => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
|
||||
match self {
|
||||
TransientSource::Keep(source)
|
||||
| TransientSource::Register(source)
|
||||
| TransientSource::Disable(source) => source.unregister(poll)?,
|
||||
TransientSource::Remove(source) => {
|
||||
source.unregister(poll)?;
|
||||
*self = TransientSource::None;
|
||||
}
|
||||
TransientSource::None => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
channel::{channel, Event},
|
||||
ping::{make_ping, PingSource},
|
||||
Dispatcher, EventSource, PostAction,
|
||||
};
|
||||
use std::{
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_transient_drop() {
|
||||
// A test source that sets a flag when it's dropped.
|
||||
struct TestSource<'a> {
|
||||
dropped: &'a AtomicBool,
|
||||
ping: PingSource,
|
||||
}
|
||||
|
||||
impl<'a> Drop for TestSource<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.dropped.store(true, Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::EventSource for TestSource<'a> {
|
||||
type Event = ();
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
type Error = Box<dyn std::error::Error + Sync + Send>;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: crate::Readiness,
|
||||
token: crate::Token,
|
||||
callback: F,
|
||||
) -> Result<crate::PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
self.ping.process_events(readiness, token, callback)?;
|
||||
Ok(PostAction::Remove)
|
||||
}
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
poll: &mut crate::Poll,
|
||||
token_factory: &mut crate::TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.ping.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut crate::Poll,
|
||||
token_factory: &mut crate::TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.ping.reregister(poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
|
||||
self.ping.unregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the inner source is actually dropped when it asks to be
|
||||
// removed from the loop, while the TransientSource remains. We use two
|
||||
// flags for this:
|
||||
// - fired: should be set only when the inner event source has an event
|
||||
// - dropped: set by the drop handler for the inner source (it's an
|
||||
// AtomicBool becaues it requires a longer lifetime than the fired
|
||||
// flag)
|
||||
let mut fired = false;
|
||||
let dropped = false.into();
|
||||
|
||||
// The inner source that should be dropped after the first loop run.
|
||||
let (pinger, ping) = make_ping().unwrap();
|
||||
let inner = TestSource {
|
||||
dropped: &dropped,
|
||||
ping,
|
||||
};
|
||||
|
||||
// The TransientSource wrapper.
|
||||
let outer: TransientSource<_> = inner.into();
|
||||
|
||||
let mut event_loop = crate::EventLoop::try_new().unwrap();
|
||||
let handle = event_loop.handle();
|
||||
|
||||
let _token = handle
|
||||
.insert_source(outer, |_, _, fired| {
|
||||
*fired = true;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// First loop run: the ping generates an event for the inner source.
|
||||
pinger.ping();
|
||||
|
||||
event_loop.dispatch(Duration::ZERO, &mut fired).unwrap();
|
||||
|
||||
assert!(fired);
|
||||
assert!(dropped.load(Ordering::Relaxed));
|
||||
|
||||
// Second loop run: the ping does nothing because the receiver has been
|
||||
// dropped.
|
||||
fired = false;
|
||||
|
||||
pinger.ping();
|
||||
|
||||
event_loop.dispatch(Duration::ZERO, &mut fired).unwrap();
|
||||
assert!(!fired);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transient_passthrough() {
|
||||
// Test that event processing works when a source is nested inside a
|
||||
// TransientSource. In particular, we want to ensure that the final
|
||||
// event is received even if it corresponds to that same event source
|
||||
// returning `PostAction::Remove`.
|
||||
let (sender, receiver) = channel();
|
||||
let outer: TransientSource<_> = receiver.into();
|
||||
|
||||
let mut event_loop = crate::EventLoop::try_new().unwrap();
|
||||
let handle = event_loop.handle();
|
||||
|
||||
// Our callback puts the receied events in here for us to check later.
|
||||
let mut msg_queue = vec![];
|
||||
|
||||
let _token = handle
|
||||
.insert_source(outer, |msg, _, queue: &mut Vec<_>| {
|
||||
queue.push(msg);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Send some data and drop the sender. We specifically want to test that
|
||||
// we get the "closed" message.
|
||||
sender.send(0u32).unwrap();
|
||||
sender.send(1u32).unwrap();
|
||||
sender.send(2u32).unwrap();
|
||||
sender.send(3u32).unwrap();
|
||||
drop(sender);
|
||||
|
||||
// Run loop once to process events.
|
||||
event_loop.dispatch(Duration::ZERO, &mut msg_queue).unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
msg_queue.as_slice(),
|
||||
&[
|
||||
Event::Msg(0u32),
|
||||
Event::Msg(1u32),
|
||||
Event::Msg(2u32),
|
||||
Event::Msg(3u32),
|
||||
Event::Closed
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transient_map() {
|
||||
struct IdSource {
|
||||
id: u32,
|
||||
ping: PingSource,
|
||||
}
|
||||
|
||||
impl EventSource for IdSource {
|
||||
type Event = u32;
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
type Error = Box<dyn std::error::Error + Sync + Send>;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: crate::Readiness,
|
||||
token: crate::Token,
|
||||
mut callback: F,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
let id = self.id;
|
||||
self.ping
|
||||
.process_events(readiness, token, |_, md| callback(id, md))?;
|
||||
|
||||
let action = if self.id > 2 {
|
||||
PostAction::Remove
|
||||
} else {
|
||||
PostAction::Continue
|
||||
};
|
||||
|
||||
Ok(action)
|
||||
}
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
poll: &mut crate::Poll,
|
||||
token_factory: &mut crate::TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.ping.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut crate::Poll,
|
||||
token_factory: &mut crate::TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.ping.reregister(poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
|
||||
self.ping.unregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
struct WrapperSource(TransientSource<IdSource>);
|
||||
|
||||
impl EventSource for WrapperSource {
|
||||
type Event = <IdSource as EventSource>::Event;
|
||||
type Metadata = <IdSource as EventSource>::Metadata;
|
||||
type Ret = <IdSource as EventSource>::Ret;
|
||||
type Error = <IdSource as EventSource>::Error;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: crate::Readiness,
|
||||
token: crate::Token,
|
||||
callback: F,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
let action = self.0.process_events(readiness, token, callback);
|
||||
self.0.map(|inner| inner.id += 1);
|
||||
action
|
||||
}
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
poll: &mut crate::Poll,
|
||||
token_factory: &mut crate::TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.0.map(|inner| inner.id += 1);
|
||||
self.0.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut crate::Poll,
|
||||
token_factory: &mut crate::TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.0.map(|inner| inner.id += 1);
|
||||
self.0.reregister(poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
|
||||
self.0.map(|inner| inner.id += 1);
|
||||
self.0.unregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
// To test the id later.
|
||||
let mut id = 0;
|
||||
|
||||
// Create our source.
|
||||
let (pinger, ping) = make_ping().unwrap();
|
||||
let inner = IdSource { id, ping };
|
||||
|
||||
// The TransientSource wrapper.
|
||||
let outer: TransientSource<_> = inner.into();
|
||||
|
||||
// The top level source.
|
||||
let top = WrapperSource(outer);
|
||||
|
||||
// Create a dispatcher so we can check the source afterwards.
|
||||
let dispatcher = Dispatcher::new(top, |got_id, _, test_id| {
|
||||
*test_id = got_id;
|
||||
});
|
||||
|
||||
let mut event_loop = crate::EventLoop::try_new().unwrap();
|
||||
let handle = event_loop.handle();
|
||||
|
||||
let token = handle.register_dispatcher(dispatcher.clone()).unwrap();
|
||||
|
||||
// First loop run: the ping generates an event for the inner source.
|
||||
// The ID should be 1 after the increment in register().
|
||||
pinger.ping();
|
||||
event_loop.dispatch(Duration::ZERO, &mut id).unwrap();
|
||||
assert_eq!(id, 1);
|
||||
|
||||
// Second loop run: the ID should be 2 after the previous
|
||||
// process_events().
|
||||
pinger.ping();
|
||||
event_loop.dispatch(Duration::ZERO, &mut id).unwrap();
|
||||
assert_eq!(id, 2);
|
||||
|
||||
// Third loop run: the ID should be 3 after another process_events().
|
||||
pinger.ping();
|
||||
event_loop.dispatch(Duration::ZERO, &mut id).unwrap();
|
||||
assert_eq!(id, 3);
|
||||
|
||||
// Fourth loop run: the callback is no longer called by the inner
|
||||
// source, so our local ID is not incremented.
|
||||
pinger.ping();
|
||||
event_loop.dispatch(Duration::ZERO, &mut id).unwrap();
|
||||
assert_eq!(id, 3);
|
||||
|
||||
// Remove the dispatcher so we can inspect the sources.
|
||||
handle.remove(token);
|
||||
|
||||
let mut top_after = dispatcher.into_source_inner();
|
||||
|
||||
// I expect the inner source to be dropped, so the TransientSource
|
||||
// variant is None (its version of None, not Option::None), so its map()
|
||||
// won't call the passed-in function (hence the unreachable!()) and its
|
||||
// return value should be Option::None.
|
||||
assert!(top_after.0.map(|_| unreachable!()).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transient_disable() {
|
||||
// Test that disabling and enabling is handled properly.
|
||||
struct DisablingSource(PingSource);
|
||||
|
||||
impl EventSource for DisablingSource {
|
||||
type Event = ();
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
type Error = Box<dyn std::error::Error + Sync + Send>;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: crate::Readiness,
|
||||
token: crate::Token,
|
||||
callback: F,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
|
||||
{
|
||||
self.0.process_events(readiness, token, callback)?;
|
||||
Ok(PostAction::Disable)
|
||||
}
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
poll: &mut crate::Poll,
|
||||
token_factory: &mut crate::TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.0.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut crate::Poll,
|
||||
token_factory: &mut crate::TokenFactory,
|
||||
) -> crate::Result<()> {
|
||||
self.0.reregister(poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
|
||||
self.0.unregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
// Flag for checking when the source fires.
|
||||
let mut fired = false;
|
||||
|
||||
// Create our source.
|
||||
let (pinger, ping) = make_ping().unwrap();
|
||||
|
||||
let inner = DisablingSource(ping);
|
||||
|
||||
// The TransientSource wrapper.
|
||||
let outer: TransientSource<_> = inner.into();
|
||||
|
||||
let mut event_loop = crate::EventLoop::try_new().unwrap();
|
||||
let handle = event_loop.handle();
|
||||
let token = handle
|
||||
.insert_source(outer, |_, _, fired| {
|
||||
*fired = true;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Ping here and not later, to check that disabling after an event is
|
||||
// triggered but not processed does not discard the event.
|
||||
pinger.ping();
|
||||
event_loop.dispatch(Duration::ZERO, &mut fired).unwrap();
|
||||
assert!(fired);
|
||||
|
||||
// Source should now be disabled.
|
||||
pinger.ping();
|
||||
fired = false;
|
||||
event_loop.dispatch(Duration::ZERO, &mut fired).unwrap();
|
||||
assert!(!fired);
|
||||
|
||||
// Re-enable the source.
|
||||
handle.enable(&token).unwrap();
|
||||
|
||||
// Trigger another event.
|
||||
pinger.ping();
|
||||
fired = false;
|
||||
event_loop.dispatch(Duration::ZERO, &mut fired).unwrap();
|
||||
assert!(fired);
|
||||
}
|
||||
}
|
||||
159
third-party/vendor/calloop/src/sys/epoll.rs
vendored
Normal file
159
third-party/vendor/calloop/src/sys/epoll.rs
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use super::{Interest, Mode, PollEvent, Readiness, Token};
|
||||
|
||||
use nix::sys::{
|
||||
epoll::{
|
||||
epoll_create1, epoll_ctl, epoll_wait, EpollCreateFlags, EpollEvent, EpollFlags, EpollOp,
|
||||
},
|
||||
time::TimeSpec,
|
||||
timerfd::{ClockId, Expiration, TimerFd, TimerFlags, TimerSetTimeFlags},
|
||||
};
|
||||
|
||||
pub struct Epoll {
|
||||
epoll_fd: RawFd,
|
||||
timer_fd: Option<TimerFd>,
|
||||
}
|
||||
|
||||
const TIMER_DATA: u64 = u64::MAX;
|
||||
|
||||
fn make_flags(interest: Interest, mode: Mode) -> EpollFlags {
|
||||
let mut flags = EpollFlags::empty();
|
||||
if interest.readable {
|
||||
flags |= EpollFlags::EPOLLIN;
|
||||
}
|
||||
if interest.writable {
|
||||
flags |= EpollFlags::EPOLLOUT;
|
||||
}
|
||||
match mode {
|
||||
Mode::Level => { /* This is the default */ }
|
||||
Mode::Edge => flags |= EpollFlags::EPOLLET,
|
||||
Mode::OneShot => flags |= EpollFlags::EPOLLONESHOT,
|
||||
}
|
||||
flags
|
||||
}
|
||||
|
||||
fn flags_to_readiness(flags: EpollFlags) -> Readiness {
|
||||
Readiness {
|
||||
readable: flags.contains(EpollFlags::EPOLLIN),
|
||||
writable: flags.contains(EpollFlags::EPOLLOUT),
|
||||
error: flags.contains(EpollFlags::EPOLLERR),
|
||||
}
|
||||
}
|
||||
|
||||
impl Epoll {
|
||||
pub(crate) fn new(high_precision: bool) -> crate::Result<Epoll> {
|
||||
let epoll_fd = epoll_create1(EpollCreateFlags::EPOLL_CLOEXEC)?;
|
||||
let mut timer_fd = None;
|
||||
if high_precision {
|
||||
// Prepare a timerfd for precise time tracking and register it to the event queue
|
||||
// This timerfd allows for nanosecond precision in setting the timout up (though in practice
|
||||
// we rather get ~10 microsecond precision), while epoll_wait() API only allows millisecond
|
||||
// granularity
|
||||
let timer = TimerFd::new(
|
||||
ClockId::CLOCK_MONOTONIC,
|
||||
TimerFlags::TFD_CLOEXEC | TimerFlags::TFD_NONBLOCK,
|
||||
)?;
|
||||
let mut timer_event = EpollEvent::new(EpollFlags::EPOLLIN, TIMER_DATA);
|
||||
epoll_ctl(
|
||||
epoll_fd,
|
||||
EpollOp::EpollCtlAdd,
|
||||
timer.as_raw_fd(),
|
||||
&mut timer_event,
|
||||
)?;
|
||||
timer_fd = Some(timer);
|
||||
}
|
||||
Ok(Epoll { epoll_fd, timer_fd })
|
||||
}
|
||||
|
||||
pub(crate) fn poll(
|
||||
&mut self,
|
||||
timeout: Option<std::time::Duration>,
|
||||
) -> crate::Result<Vec<PollEvent>> {
|
||||
let mut buffer = [EpollEvent::empty(); 32];
|
||||
if let Some(ref timer) = self.timer_fd {
|
||||
if let Some(timeout) = timeout {
|
||||
// Set up the precise timer
|
||||
timer.set(
|
||||
Expiration::OneShot(TimeSpec::from_duration(timeout)),
|
||||
TimerSetTimeFlags::empty(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
// add 1 to the millisecond wait, to round up for timer tracking. If the high precision timer is set up
|
||||
// it'll fire before that timeout
|
||||
let timeout = timeout.map(|d| (d.as_millis() + 1) as isize).unwrap_or(-1);
|
||||
let n_ready = epoll_wait(self.epoll_fd, &mut buffer, timeout)?;
|
||||
let events = buffer
|
||||
.iter()
|
||||
.take(n_ready)
|
||||
.flat_map(|event| {
|
||||
if event.data() == TIMER_DATA {
|
||||
// We woke up because the high-precision timer fired, we need to disarm it by reading its
|
||||
// contents to ensure it will be ready for next time
|
||||
// Timer is created in non-blocking mode, and should have already fired anyway, this
|
||||
// cannot possibly block
|
||||
let _ = self
|
||||
.timer_fd
|
||||
.as_ref()
|
||||
.expect("Got an event from high-precision timer while it is not set up?!")
|
||||
.wait();
|
||||
// don't forward this event to downstream
|
||||
None
|
||||
} else {
|
||||
// In C, the underlying data type is a union including a void
|
||||
// pointer; in Rust's FFI bindings, it only exposes the u64. The
|
||||
// round-trip conversion is valid however.
|
||||
let token_ptr = event.data() as usize as *const Token;
|
||||
Some(PollEvent {
|
||||
readiness: flags_to_readiness(event.events()),
|
||||
// Why this is safe: it points to memory boxed and owned by
|
||||
// the parent Poller type.
|
||||
token: unsafe { *token_ptr },
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if let Some(ref timer) = self.timer_fd {
|
||||
// in all cases, disarm the timer
|
||||
timer.unset()?;
|
||||
// clear the timer in case it fired between epoll_wait and now, as timer is in
|
||||
// non-blocking mode, this will return Err(WouldBlock) if it had not fired, so
|
||||
// we ignore the error
|
||||
let _ = timer.wait();
|
||||
}
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
pub fn register(
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
interest: Interest,
|
||||
mode: Mode,
|
||||
token: *const Token,
|
||||
) -> crate::Result<()> {
|
||||
let mut event = EpollEvent::new(make_flags(interest, mode), token as usize as u64);
|
||||
epoll_ctl(self.epoll_fd, EpollOp::EpollCtlAdd, fd, &mut event).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn reregister(
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
interest: Interest,
|
||||
mode: Mode,
|
||||
token: *const Token,
|
||||
) -> crate::Result<()> {
|
||||
let mut event = EpollEvent::new(make_flags(interest, mode), token as usize as u64);
|
||||
epoll_ctl(self.epoll_fd, EpollOp::EpollCtlMod, fd, &mut event).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn unregister(&mut self, fd: RawFd) -> crate::Result<()> {
|
||||
epoll_ctl(self.epoll_fd, EpollOp::EpollCtlDel, fd, None).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Epoll {
|
||||
fn drop(&mut self) {
|
||||
let _ = nix::unistd::close(self.epoll_fd);
|
||||
}
|
||||
}
|
||||
223
third-party/vendor/calloop/src/sys/kqueue.rs
vendored
Normal file
223
third-party/vendor/calloop/src/sys/kqueue.rs
vendored
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
use std::{io, os::unix::io::RawFd};
|
||||
|
||||
use nix::{
|
||||
libc::{c_long, time_t, timespec},
|
||||
sys::event::{kevent_ts, kqueue, EventFilter, EventFlag, FilterFlag, KEvent},
|
||||
};
|
||||
|
||||
use super::{Interest, Mode, PollEvent, Readiness, Token};
|
||||
|
||||
pub struct Kqueue {
|
||||
kq: RawFd,
|
||||
}
|
||||
|
||||
fn mode_to_flag(mode: Mode) -> EventFlag {
|
||||
match mode {
|
||||
Mode::Level => EventFlag::empty(),
|
||||
Mode::OneShot => EventFlag::EV_DISPATCH,
|
||||
Mode::Edge => EventFlag::EV_CLEAR,
|
||||
}
|
||||
}
|
||||
|
||||
impl Kqueue {
|
||||
// Kqueue is always high precision
|
||||
pub(crate) fn new(_high_precision: bool) -> crate::Result<Kqueue> {
|
||||
let kq = kqueue()?;
|
||||
Ok(Kqueue { kq })
|
||||
}
|
||||
|
||||
pub(crate) fn poll(
|
||||
&mut self,
|
||||
timeout: Option<std::time::Duration>,
|
||||
) -> crate::Result<Vec<PollEvent>> {
|
||||
let mut buffer = [KEvent::new(
|
||||
0,
|
||||
EventFilter::EVFILT_READ,
|
||||
EventFlag::empty(),
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
); 32];
|
||||
|
||||
let nevents = kevent_ts(
|
||||
self.kq,
|
||||
&[],
|
||||
&mut buffer,
|
||||
timeout.map(|d| timespec {
|
||||
tv_sec: d.as_secs() as time_t,
|
||||
tv_nsec: d.subsec_nanos() as c_long,
|
||||
}),
|
||||
)?;
|
||||
|
||||
let ret = buffer
|
||||
.iter()
|
||||
.take(nevents)
|
||||
.map(|event| {
|
||||
// The kevent data field in Rust's libc FFI bindings is an
|
||||
// intptr_t, which is specified to allow this kind of
|
||||
// conversion.
|
||||
let token_ptr = event.udata() as usize as *const Token;
|
||||
PollEvent {
|
||||
readiness: Readiness {
|
||||
readable: event.filter() == Ok(EventFilter::EVFILT_READ),
|
||||
writable: event.filter() == Ok(EventFilter::EVFILT_WRITE),
|
||||
error: event.flags().contains(EventFlag::EV_ERROR) && event.data() != 0,
|
||||
},
|
||||
// Why this is safe: it points to memory boxed and owned by
|
||||
// the parent Poller type.
|
||||
token: unsafe { *token_ptr },
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn register(
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
interest: Interest,
|
||||
mode: Mode,
|
||||
token: *const Token,
|
||||
) -> crate::Result<()> {
|
||||
self.reregister(fd, interest, mode, token)
|
||||
}
|
||||
|
||||
pub fn reregister(
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
interest: Interest,
|
||||
mode: Mode,
|
||||
token: *const Token,
|
||||
) -> crate::Result<()> {
|
||||
let write_flags = if interest.writable {
|
||||
EventFlag::EV_ADD | EventFlag::EV_RECEIPT | mode_to_flag(mode)
|
||||
} else {
|
||||
EventFlag::EV_DELETE
|
||||
};
|
||||
let read_flags = if interest.readable {
|
||||
EventFlag::EV_ADD | EventFlag::EV_RECEIPT | mode_to_flag(mode)
|
||||
} else {
|
||||
EventFlag::EV_DELETE
|
||||
};
|
||||
|
||||
let changes = [
|
||||
KEvent::new(
|
||||
fd as usize,
|
||||
EventFilter::EVFILT_WRITE,
|
||||
write_flags,
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
token as usize as isize,
|
||||
),
|
||||
KEvent::new(
|
||||
fd as usize,
|
||||
EventFilter::EVFILT_READ,
|
||||
read_flags,
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
token as usize as isize,
|
||||
),
|
||||
];
|
||||
|
||||
let mut out = [
|
||||
KEvent::new(
|
||||
0,
|
||||
EventFilter::EVFILT_WRITE,
|
||||
EventFlag::empty(),
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
KEvent::new(
|
||||
0,
|
||||
EventFilter::EVFILT_READ,
|
||||
EventFlag::empty(),
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
];
|
||||
|
||||
kevent_ts(self.kq, &changes, &mut out, None)?;
|
||||
|
||||
for o in &out {
|
||||
if o.flags().contains(EventFlag::EV_ERROR) && o.data() != 0 {
|
||||
let e = io::Error::from_raw_os_error(o.data() as i32);
|
||||
// ignore NotFound error which is raised if we tried to remove a non-existent filter
|
||||
if e.kind() != io::ErrorKind::NotFound {
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unregister(&mut self, fd: RawFd) -> crate::Result<()> {
|
||||
let changes = [
|
||||
KEvent::new(
|
||||
fd as usize,
|
||||
EventFilter::EVFILT_WRITE,
|
||||
EventFlag::EV_DELETE,
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
KEvent::new(
|
||||
fd as usize,
|
||||
EventFilter::EVFILT_READ,
|
||||
EventFlag::EV_DELETE,
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
];
|
||||
|
||||
let mut out = [
|
||||
KEvent::new(
|
||||
0,
|
||||
EventFilter::EVFILT_WRITE,
|
||||
EventFlag::empty(),
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
KEvent::new(
|
||||
0,
|
||||
EventFilter::EVFILT_READ,
|
||||
EventFlag::empty(),
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
];
|
||||
|
||||
kevent_ts(self.kq, &changes, &mut out, None)?;
|
||||
|
||||
// Report an error if *both* fd were missing, meaning we were not registered at all
|
||||
let mut notfound = 0;
|
||||
|
||||
for o in &out {
|
||||
if o.flags().contains(EventFlag::EV_ERROR) && o.data() != 0 {
|
||||
let e = io::Error::from_raw_os_error(o.data() as i32);
|
||||
// ignore NotFound error which is raised if we tried to remove a non-existent filter
|
||||
if e.kind() != io::ErrorKind::NotFound {
|
||||
return Err(e.into());
|
||||
} else {
|
||||
notfound += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if notfound == 2 {
|
||||
return Err(std::io::Error::from(io::ErrorKind::NotFound).into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Kqueue {
|
||||
fn drop(&mut self) {
|
||||
let _ = nix::unistd::close(self.kq);
|
||||
}
|
||||
}
|
||||
363
third-party/vendor/calloop/src/sys/mod.rs
vendored
Normal file
363
third-party/vendor/calloop/src/sys/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
use std::{cell::RefCell, convert::TryInto, os::unix::io::RawFd, rc::Rc, time::Duration};
|
||||
use vec_map::VecMap;
|
||||
|
||||
use crate::{loop_logic::CalloopKey, sources::timer::TimerWheel};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
mod epoll;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use epoll::Epoll as Poller;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "macos"
|
||||
))]
|
||||
mod kqueue;
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "macos"
|
||||
))]
|
||||
use kqueue::Kqueue as Poller;
|
||||
|
||||
/// Possible modes for registering a file descriptor
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Mode {
|
||||
/// Single event generation
|
||||
///
|
||||
/// This FD will be disabled as soon as it has generated one event.
|
||||
///
|
||||
/// The user will need to use `LoopHandle::update()` to re-enable it if
|
||||
/// desired.
|
||||
OneShot,
|
||||
/// Level-triggering
|
||||
///
|
||||
/// This FD will report events on every poll as long as the requested interests
|
||||
/// are available. If the same FD is inserted in multiple event loops, all of
|
||||
/// them are notified of readiness.
|
||||
Level,
|
||||
/// Edge-triggering
|
||||
///
|
||||
/// This FD will report events only when it *gains* one of the requested interests.
|
||||
/// it must thus be fully processed before it'll generate events again. If the same
|
||||
/// FD is inserted on multiple event loops, it may be that not all of them are notified
|
||||
/// of readiness, and not necessarily always the same(s) (at least one is notified).
|
||||
Edge,
|
||||
}
|
||||
|
||||
/// Interest to register regarding the file descriptor
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Interest {
|
||||
/// Wait for the FD to be readable
|
||||
pub readable: bool,
|
||||
/// Wait for the FD to be writable
|
||||
pub writable: bool,
|
||||
}
|
||||
|
||||
impl Interest {
|
||||
/// Shorthand for empty interest
|
||||
pub const EMPTY: Interest = Interest {
|
||||
readable: false,
|
||||
writable: false,
|
||||
};
|
||||
/// Shorthand for read interest
|
||||
pub const READ: Interest = Interest {
|
||||
readable: true,
|
||||
writable: false,
|
||||
};
|
||||
/// Shorthand for write interest
|
||||
pub const WRITE: Interest = Interest {
|
||||
readable: false,
|
||||
writable: true,
|
||||
};
|
||||
/// Shorthand for read and write interest
|
||||
pub const BOTH: Interest = Interest {
|
||||
readable: true,
|
||||
writable: true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Readiness for a file descriptor notification
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Readiness {
|
||||
/// Is the FD readable
|
||||
pub readable: bool,
|
||||
/// Is the FD writable
|
||||
pub writable: bool,
|
||||
/// Is the FD in an error state
|
||||
pub error: bool,
|
||||
}
|
||||
|
||||
impl Readiness {
|
||||
/// Shorthand for empty readiness
|
||||
pub const EMPTY: Readiness = Readiness {
|
||||
readable: false,
|
||||
writable: false,
|
||||
error: false,
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PollEvent {
|
||||
pub(crate) readiness: Readiness,
|
||||
pub(crate) token: Token,
|
||||
}
|
||||
|
||||
/// Factory for creating tokens in your registrations
|
||||
///
|
||||
/// When composing event sources, each sub-source needs to
|
||||
/// have its own token to identify itself. This factory is
|
||||
/// provided to produce such unique tokens.
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TokenFactory {
|
||||
key: CalloopKey,
|
||||
sub_id: u32,
|
||||
}
|
||||
|
||||
impl TokenFactory {
|
||||
pub(crate) fn new(key: CalloopKey) -> TokenFactory {
|
||||
TokenFactory { key, sub_id: 0 }
|
||||
}
|
||||
|
||||
/// Produce a new unique token
|
||||
pub fn token(&mut self) -> Token {
|
||||
let token = Token {
|
||||
key: self.key,
|
||||
sub_id: self.sub_id,
|
||||
};
|
||||
self.sub_id += 1;
|
||||
token
|
||||
}
|
||||
}
|
||||
|
||||
/// A token (for implementation of the [`EventSource`](crate::EventSource) trait)
|
||||
///
|
||||
/// This token is produced by the [`TokenFactory`] and is used when calling the
|
||||
/// [`EventSource`](crate::EventSource) implementations to process event, in order
|
||||
/// to identify which sub-source produced them.
|
||||
///
|
||||
/// You should forward it to the [`Poll`] when registering your file descriptors.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Token {
|
||||
pub(crate) key: CalloopKey,
|
||||
pub(crate) sub_id: u32,
|
||||
}
|
||||
|
||||
/// The polling system
|
||||
///
|
||||
/// This type represents the polling system of calloop, on which you
|
||||
/// can register your file descriptors. This interface is only accessible in
|
||||
/// implementations of the [`EventSource`](crate::EventSource) trait.
|
||||
///
|
||||
/// You only need to interact with this type if you are implementing your
|
||||
/// own event sources, while implementing the [`EventSource`](crate::EventSource) trait.
|
||||
/// And even in this case, you can often just use the [`Generic`](crate::generic::Generic) event
|
||||
/// source and delegate the implementations to it.
|
||||
pub struct Poll {
|
||||
poller: Poller,
|
||||
|
||||
// It is essential for safe use of this type that the pointers passed in to
|
||||
// the underlying poller API are properly managed. Each time an event source
|
||||
// is registered, the token it passes in is Boxed and converted to a raw
|
||||
// pointer to be passed to the polling system by FFI. This pointer is what's
|
||||
// stored in the map. When the event source is re- or unregistered, the same
|
||||
// raw pointer can then be converted back into the Box and dropped, safely
|
||||
// deallocating it. To put it another way, we effectively "own" the Token
|
||||
// memory on behalf of the underlying polling mechanism.
|
||||
//
|
||||
// All the platforms we currently support follow the rule that file
|
||||
// descriptors must be "small", positive integers. This means we can use a
|
||||
// VecMap which has that exact constraint for its keys. If that ever
|
||||
// changes, this will need to be changed to a different structure.
|
||||
tokens: VecMap<*mut Token>,
|
||||
pub(crate) timers: Rc<RefCell<TimerWheel>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Poll {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("Poll { ... }")
|
||||
}
|
||||
}
|
||||
|
||||
impl Poll {
|
||||
pub(crate) fn new(high_precision: bool) -> crate::Result<Poll> {
|
||||
Ok(Poll {
|
||||
poller: Poller::new(high_precision)?,
|
||||
tokens: VecMap::new(),
|
||||
timers: Rc::new(RefCell::new(TimerWheel::new())),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn poll(
|
||||
&mut self,
|
||||
mut timeout: Option<std::time::Duration>,
|
||||
) -> crate::Result<Vec<PollEvent>> {
|
||||
let now = std::time::Instant::now();
|
||||
// adjust the timeout for the timers
|
||||
if let Some(next_timeout) = self.timers.borrow().next_deadline() {
|
||||
if next_timeout <= now {
|
||||
timeout = Some(Duration::ZERO);
|
||||
} else if let Some(deadline) = timeout {
|
||||
timeout = Some(std::cmp::min(deadline, next_timeout - now));
|
||||
} else {
|
||||
timeout = Some(next_timeout - now);
|
||||
}
|
||||
};
|
||||
|
||||
let mut events = self.poller.poll(timeout)?;
|
||||
|
||||
// Update 'now' as some time may have elapsed in poll()
|
||||
let now = std::time::Instant::now();
|
||||
let mut timers = self.timers.borrow_mut();
|
||||
while let Some((_, token)) = timers.next_expired(now) {
|
||||
events.push(PollEvent {
|
||||
readiness: Readiness {
|
||||
readable: true,
|
||||
writable: false,
|
||||
error: false,
|
||||
},
|
||||
token,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
/// Register a new file descriptor for polling
|
||||
///
|
||||
/// The file descriptor will be registered with given interest,
|
||||
/// mode and token. This function will fail if given a
|
||||
/// bad file descriptor or if the provided file descriptor is already
|
||||
/// registered.
|
||||
///
|
||||
/// # Leaking tokens
|
||||
///
|
||||
/// If your event source is dropped without being unregistered, the token
|
||||
/// passed in here will remain on the heap and continue to be used by the
|
||||
/// polling system even though no event source will match it.
|
||||
pub fn register(
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
interest: Interest,
|
||||
mode: Mode,
|
||||
token: Token,
|
||||
) -> crate::Result<()> {
|
||||
let token_box = Box::new(token);
|
||||
let token_ptr = Box::into_raw(token_box);
|
||||
|
||||
let registration_result = self.poller.register(fd, interest, mode, token_ptr);
|
||||
|
||||
if registration_result.is_err() {
|
||||
// If registration did not work, do not add the file descriptor to
|
||||
// the token map. Instead, reconstruct the Box and drop it. This is
|
||||
// safe because it's from Box::into_raw() above.
|
||||
let token_box = unsafe { Box::from_raw(token_ptr) };
|
||||
std::mem::drop(token_box);
|
||||
} else {
|
||||
// Registration worked, keep the token pointer until it's replaced
|
||||
// or removed.
|
||||
let index = index_from_fd(fd);
|
||||
if self.tokens.insert(index, token_ptr).is_some() {
|
||||
// If there is already a file descriptor associated with a
|
||||
// token, then replacing that entry will leak the token, but
|
||||
// converting it back into a Box might leave a dangling pointer
|
||||
// somewhere. We can theoretically continue safely by choosing
|
||||
// to leak, but one of our assumptions is no longer valid, so
|
||||
// panic.
|
||||
panic!("File descriptor ({}) already registered", fd);
|
||||
}
|
||||
}
|
||||
|
||||
registration_result
|
||||
}
|
||||
|
||||
/// Update the registration for a file descriptor
|
||||
///
|
||||
/// This allows you to change the interest, mode or token of a file
|
||||
/// descriptor. Fails if the provided fd is not currently registered.
|
||||
///
|
||||
/// See note on [`register()`](Self::register()) regarding leaking.
|
||||
pub fn reregister(
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
interest: Interest,
|
||||
mode: Mode,
|
||||
token: Token,
|
||||
) -> crate::Result<()> {
|
||||
let token_box = Box::new(token);
|
||||
let token_ptr = Box::into_raw(token_box);
|
||||
|
||||
let reregistration_result = self.poller.reregister(fd, interest, mode, token_ptr);
|
||||
|
||||
if reregistration_result.is_err() {
|
||||
// If registration did not work, do not add the file descriptor to
|
||||
// the token map. Instead, reconstruct the Box and drop it. This is
|
||||
// safe because it's from Box::into_raw() above.
|
||||
let token_box = unsafe { Box::from_raw(token_ptr) };
|
||||
std::mem::drop(token_box);
|
||||
} else {
|
||||
// Registration worked, drop the old token memory and keep the new
|
||||
// token pointer until it's replaced or removed.
|
||||
let index = index_from_fd(fd);
|
||||
if let Some(previous) = self.tokens.insert(index, token_ptr) {
|
||||
// This is safe because it's from Box::into_raw() from a
|
||||
// previous (re-)register() call.
|
||||
let token_box = unsafe { Box::from_raw(previous) };
|
||||
std::mem::drop(token_box);
|
||||
} else {
|
||||
// If there is no previous token registered for this file
|
||||
// descriptor, either the event source has wrongly called
|
||||
// reregister() without first being registered, or the
|
||||
// underlying poller has a dangling pointer. In the first case,
|
||||
// the reregistration should have failed; in the second case, we
|
||||
// cannot safely proceed.
|
||||
panic!("File descriptor ({}) had no previous registration", fd);
|
||||
}
|
||||
}
|
||||
|
||||
reregistration_result
|
||||
}
|
||||
|
||||
/// Unregister a file descriptor
|
||||
///
|
||||
/// This file descriptor will no longer generate events. Fails if the
|
||||
/// provided file descriptor is not currently registered.
|
||||
pub fn unregister(&mut self, fd: RawFd) -> crate::Result<()> {
|
||||
let unregistration_result = self.poller.unregister(fd);
|
||||
|
||||
if unregistration_result.is_ok() {
|
||||
// The source was unregistered, we can remove the old token data.
|
||||
let index = index_from_fd(fd);
|
||||
if let Some(previous) = self.tokens.remove(index) {
|
||||
// This is safe because it's from Box::into_raw() from a
|
||||
// previous (re-)register() call.
|
||||
let token_box = unsafe { Box::from_raw(previous) };
|
||||
std::mem::drop(token_box);
|
||||
} else {
|
||||
// If there is no previous token registered for this file
|
||||
// descriptor, either the event source has wrongly called
|
||||
// unregister() without first being registered, or the
|
||||
// underlying poller has a dangling pointer. In the first case,
|
||||
// the reregistration should have failed; in the second case, we
|
||||
// cannot safely proceed.
|
||||
panic!("File descriptor ({}) had no previous registration", fd);
|
||||
}
|
||||
}
|
||||
|
||||
unregistration_result
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a file descriptor into an index for the token map. Panics if the
|
||||
/// file descriptor is negative.
|
||||
fn index_from_fd(fd: RawFd) -> usize {
|
||||
fd.try_into()
|
||||
.unwrap_or_else(|_| panic!("File descriptor ({}) is invalid", fd))
|
||||
}
|
||||
139
third-party/vendor/calloop/tests/signals.rs
vendored
Normal file
139
third-party/vendor/calloop/tests/signals.rs
vendored
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// These tests cannot run as a regular test because cargo would spawn a thread to run it,
|
||||
// failing the signal masking. So we make our own, non-threaded harnessing
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn main() {
|
||||
for test in self::test::TESTS {
|
||||
test();
|
||||
// reset the signal mask between tests
|
||||
self::test::reset_mask();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn main() {}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod test {
|
||||
extern crate calloop;
|
||||
extern crate nix;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use self::calloop::signals::{Signal, Signals};
|
||||
use self::calloop::{Dispatcher, EventLoop};
|
||||
|
||||
use self::nix::sys::signal::{kill, SigSet};
|
||||
use self::nix::unistd::Pid;
|
||||
|
||||
pub const TESTS: &[fn()] = &[single_usr1, usr2_added_afterwards, usr2_signal_removed];
|
||||
|
||||
pub fn reset_mask() {
|
||||
SigSet::empty().thread_set_mask().unwrap();
|
||||
}
|
||||
|
||||
fn single_usr1() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let mut signal_received = false;
|
||||
|
||||
let _signal_source = event_loop
|
||||
.handle()
|
||||
.insert_source(
|
||||
Signals::new(&[Signal::SIGUSR1]).unwrap(),
|
||||
move |evt, &mut (), rcv| {
|
||||
assert!(evt.signal() == Signal::SIGUSR1);
|
||||
*rcv = true;
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// send ourselves a SIGUSR1
|
||||
kill(Pid::this(), Signal::SIGUSR1).unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(10)), &mut signal_received)
|
||||
.unwrap();
|
||||
|
||||
assert!(signal_received);
|
||||
}
|
||||
|
||||
fn usr2_added_afterwards() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let mut signal_received = None;
|
||||
let dispatcher = Dispatcher::new(
|
||||
Signals::new(&[Signal::SIGUSR1]).unwrap(),
|
||||
move |evt, &mut (), rcv| {
|
||||
*rcv = Some(evt.signal());
|
||||
},
|
||||
);
|
||||
|
||||
let _signal_token = event_loop
|
||||
.handle()
|
||||
.register_dispatcher(dispatcher.clone())
|
||||
.unwrap();
|
||||
dispatcher
|
||||
.as_source_mut()
|
||||
.add_signals(&[Signal::SIGUSR2])
|
||||
.unwrap();
|
||||
|
||||
// send ourselves a SIGUSR2
|
||||
kill(Pid::this(), Signal::SIGUSR2).unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(10)), &mut signal_received)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(signal_received, Some(Signal::SIGUSR2));
|
||||
}
|
||||
|
||||
fn usr2_signal_removed() {
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
|
||||
let mut signal_received = None;
|
||||
let dispatcher = Dispatcher::new(
|
||||
Signals::new(&[Signal::SIGUSR1, Signal::SIGUSR2]).unwrap(),
|
||||
move |evt, &mut (), rcv| {
|
||||
*rcv = Some(evt.signal());
|
||||
},
|
||||
);
|
||||
|
||||
let _signal_token = event_loop
|
||||
.handle()
|
||||
.register_dispatcher(dispatcher.clone())
|
||||
.unwrap();
|
||||
dispatcher
|
||||
.as_source_mut()
|
||||
.remove_signals(&[Signal::SIGUSR2])
|
||||
.unwrap();
|
||||
|
||||
// block sigusr2 anyway, to not be killed by it
|
||||
let mut set = SigSet::empty();
|
||||
set.add(Signal::SIGUSR2);
|
||||
set.thread_block().unwrap();
|
||||
|
||||
// send ourselves a SIGUSR2
|
||||
kill(Pid::this(), Signal::SIGUSR2).unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(10)), &mut signal_received)
|
||||
.unwrap();
|
||||
|
||||
// we should not have received anything, as we don't listen to SIGUSR2 any more
|
||||
assert!(signal_received.is_none());
|
||||
|
||||
// swap the signals from [SIGUSR1] to [SIGUSR2]
|
||||
dispatcher
|
||||
.as_source_mut()
|
||||
.set_signals(&[Signal::SIGUSR2])
|
||||
.unwrap();
|
||||
|
||||
event_loop
|
||||
.dispatch(Some(Duration::from_millis(10)), &mut signal_received)
|
||||
.unwrap();
|
||||
|
||||
// we should get back the pending SIGUSR2 now
|
||||
assert_eq!(signal_received, Some(Signal::SIGUSR2));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue