calloop-0.13.0/.cargo_vcs_info.json0000644000000001360000000000100125460ustar { "git": { "sha1": "72ab703e65681001aa69df798cf9fcd729b78a74" }, "path_in_vcs": "" }calloop-0.13.0/.cirrus.yml000064400000000000000000000021411046102023000134440ustar 00000000000000task: only_if: $CIRRUS_BRANCH == 'master' || $CIRRUS_PR != '' environment: CODECOV_TOKEN: ENCRYPTED[cfd48b16731c254067ef7b39a7083384127a594c6ad52e51cc32982da2eb8cc67c0a54f69736c29559a28664c615ab99] matrix: - name: FreeBSD 13.2 freebsd_instance: image_family: freebsd-13-2 - name: FreeBSD 14.0 freebsd_instance: image_family: freebsd-14-0 # Install Rust setup_script: - fetch https://sh.rustup.rs -o rustup.sh - sh rustup.sh -y --profile=minimal --default-toolchain nightly - pkg install -y grcov bash - . $HOME/.cargo/env - rustup component add llvm-tools-preview test_script: - . $HOME/.cargo/env - env LLVM_PROFILE_FILE="calloop-%p-%m.profraw" RUSTFLAGS="-Cinstrument-coverage --cfg coverage" cargo test --all-features - grcov . --binary-path ./target/debug -s . -t lcov --branch --llvm --ignore-not-existing --keep-only "src/*" --excl-br-start "mod tests \{" --excl-start "mod tests \{" --excl-br-line "#\[derive\(" --excl-line "#\[derive\(" -o lcov.info - bash -c 'bash <(curl -s https://codecov.io/bash) -f lcov.info' -F freebsd calloop-0.13.0/.github/dependabot.yml000064400000000000000000000003031046102023000155220ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: / schedule: interval: weekly - package-ecosystem: github-actions directory: / schedule: interval: weekly calloop-0.13.0/.github/workflows/ci.yml000064400000000000000000000210301046102023000160450ustar 00000000000000name: Continuous Integration on: push: branches: - master pull_request: jobs: lint: runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v4 - name: Get date for registry cache id: date run: echo "::set-output name=date::$(date +'%Y-%m-%d')" - name: Cargo registry cache uses: actions/cache@v4 with: path: | ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git key: ${{ runner.os }}-cargo-registry-${{ steps.date.outputs.date }} restore-keys: ${{ runner.os }}-cargo-registry- - name: Rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: stable override: true components: rustfmt, clippy - name: Get cargo-cache latest version id: cargocacheversion run: echo "::set-output name=version::$(cargo search 'cargo-cache' --limit 1 | head -n 1 | cut -d ' ' -f 3 | cut -d '"' -f 2)" - name: Cargo binaries cache uses: actions/cache@v4 with: path: | ~/.cargo/bin/cargo-cache ~/.cargo/.crates.toml ~/.cargo/.crates2.json key: ${{ runner.os }}-cargo-binaries-${{ steps.cargocacheversion.outputs.version }} - name: Install cargo-cache uses: actions-rs/cargo@v1 with: command: install args: cargo-cache --version ${{ steps.cargocacheversion.outputs.version }} - name: Clean cargo cache of old items uses: actions-rs/cargo@v1 with: command: cache args: clean-unref - name: Get rustc version for caching id: rustcversion run: echo "::set-output name=version::$(rustc --version | cut -d ' ' -f 2)" - name: Build cache uses: actions/cache@v4 with: path: target key: ${{ runner.os }}-lint-${{ steps.rustcversion.outputs.version }}-${{ hashFiles('**/Cargo.toml') }} - name: Cargo fmt run: cargo fmt --all -- --check - name: Clippy run: cargo clippy --workspace --all-targets --features "block_on executor" -- -D warnings ci-linux: name: CI needs: - lint strategy: fail-fast: false matrix: rust: ['1.63.0', '1.69.0', 'stable', 'beta'] runs-on: 'ubuntu-latest' steps: - name: Checkout sources uses: actions/checkout@v4 - name: Get date for registry cache id: date run: echo "::set-output name=date::$(date +'%Y-%m-%d')" - name: Cargo registry cache uses: actions/cache@v4 with: path: | ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git key: ${{ runner.os }}-cargo-registry-${{ steps.date.outputs.date }} restore-keys: ${{ runner.os }}-cargo-registry- - name: Rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} override: true - name: Get rustc version for caching id: rustcversion run: echo "::set-output name=version::$(rustc --version | cut -d ' ' -f 2)" - name: Build cache uses: actions/cache@v4 with: path: target key: ${{ runner.os }}-test-${{ steps.rustcversion.outputs.version }}-${{ hashFiles('**/Cargo.toml') }} - name: Downgrade log uses: actions-rs/cargo@v1 if: ${{ matrix.rust == '1.63.0' }} with: command: update args: --package log --precise 0.4.16 - name: Run tests uses: actions-rs/cargo@v1 with: command: test args: --features "block_on executor" - name: Run tests with signals uses: actions-rs/cargo@v1 with: command: test args: --features "block_on executor signals" if: ${{ matrix.rust != '1.63.0' }} - name: Run book tests uses: actions-rs/cargo@v1 if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }} with: command: test args: --all-features --manifest-path ./doc/Cargo.toml ci-windows: name: CI (Windows) needs: - lint strategy: fail-fast: false matrix: rust: ['1.63.0', 'stable'] runs-on: 'windows-latest' steps: - name: Checkout sources uses: actions/checkout@v4 - name: Rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} override: true - name: Run tests uses: actions-rs/cargo@v1 with: command: test args: --features "block_on executor" coverage: name: Coverage needs: - ci-linux strategy: fail-fast: false matrix: os: ['ubuntu-latest', 'macos-latest'] runs-on: ${{ matrix.os }} env: GRCOV_VERSION: "0.8.10" steps: - name: Checkout sources uses: actions/checkout@v4 - name: Get date for registry cache id: date run: echo "::set-output name=date::$(date +'%Y-%m-%d')" - name: Cargo registry cache uses: actions/cache@v4 with: path: | ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git key: ${{ runner.os }}-cargo-registry-${{ steps.date.outputs.date }} restore-keys: ${{ runner.os }}-cargo-registry- - name: Rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: nightly # required for feature(no_coverage) override: true components: llvm-tools-preview # Required for grcov - name: Get grcov latest version id: grcovversion run: echo "::set-output name=version::$(cargo search 'grcov' --limit 1 | head -n 1 | cut -d ' ' -f 3 | cut -d '"' -f 2)" - name: grcov cache uses: actions/cache@v4 with: path: | ~/.cargo/bin/grcov ~/.cargo/.crates.toml ~/.cargo/.crates2.json key: ${{ runner.os }}-grcov-${{ steps.grcovversion.outputs.version }} - name: Install grcov uses: actions-rs/cargo@v1 with: command: install args: grcov --version ${{ steps.grcovversion.outputs.version }} - name: Run tests uses: actions-rs/cargo@v1 with: command: test args: --all-features env: LLVM_PROFILE_FILE: "calloop-%p-%m.profraw" RUSTFLAGS: "-Cinstrument-coverage --cfg coverage" - name: Coverage run: grcov . --binary-path ./target/debug -s . -t lcov --branch --llvm --ignore-not-existing --ignore 'examples/*' --ignore 'tests/*' --ignore '*/.cargo/registry/*' --excl-br-start "mod tests \{" --excl-start "mod tests \{" --excl-br-line "#\[derive\(" --excl-line "#\[derive\(" -o lcov.info - name: Upload to codecov.io uses: codecov/codecov-action@v4 with: files: ./lcov.info flags: ${{ matrix.os }} doc: name: Documentation on Github Pages runs-on: ubuntu-latest needs: - ci-linux steps: - name: Checkout sources uses: actions/checkout@v4 - name: Get date for registry cache id: date run: echo "::set-output name=date::$(date +'%Y-%m-%d')" - name: Cargo registry cache uses: actions/cache@v4 with: path: | ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git key: ${{ runner.os }}-cargo-registry-${{ steps.date.outputs.date }} restore-keys: ${{ runner.os }}-cargo-registry- - name: Rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: beta override: true # Combining these docs is order-dependent. The API docs must be copied # into mdBook's source directory for it to copy it to the output directory # as static content. - name: Build Documentation uses: actions-rs/cargo@v1 with: command: doc args: --no-deps --features "block_on executor" - run: rsync -r target/doc/ doc/src/api - name: Build mdBook uses: peaceiris/actions-mdbook@v1 with: mdbook-version: '0.4.8' - run: mdbook build doc - name: Deploy uses: peaceiris/actions-gh-pages@v3 if: ${{ github.event_name == 'push' }} with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./doc/book force_orphan: true calloop-0.13.0/.gitignore000064400000000000000000000000521046102023000133230ustar 00000000000000/target /doc/target **/*.rs.bk Cargo.lock calloop-0.13.0/CHANGELOG.md000064400000000000000000000267221046102023000131600ustar 00000000000000# Change Log ## Unreleased ## 0.13.0 -- 2024-02-25 #### Breaking changes - Bump `nix` to `v0.28`. As `nix` is exposed in the public API, this is a breaking change. (#176) #### Bugfixes - Fix a panic that would occur when a task is scheduled in an event callback. (#172) ## 0.12.4 -- 2024-01-15 #### Additions - `calloop` is now supported for Windows. (#168) - Add the `signals` feature to `docs.rs`. (#166) #### Bugfixes - Fix a borrow error that can occur while using the executor. (#165) ## 0.12.3 -- 2023-10-10 #### Additions - `Token` and `RegistrationToken` are now invalidated when the event source they represent is removed from the event loop. - Implement `AsRawFd` and `AsFd` for `EventLoop<'l, Data>` #### Bugfixes - Fix an issue, where id-reuse could execute a PostAction on a newly registered event source ## 0.12.2 -- 2023-09-25 #### Bugfixes - Fix an issue where the `Generic` event source would try to unregister its contents from the event loop after a failed registration. - Fix an issue where the `EventLoop` would panic when processing a `PostAction::Remove` from an event source with subsources. ## 0.12.1 -- 2023-09-19 #### Bugfixes - Fix `EventSource::before_handle_events()` being erroneously give an iterator over synthetic events instead of real events ## 0.12.0 -- 2023-09-11 #### Breaking changes - Bump MSRV to 1.63 - Make signals an optional feature under the `signals` features. - Replace the `nix` crate with standard library I/O errors and the `rustix` crate. - `pre_run` and `post_run` on `EventSource` have been replaced with `before_sleep` and `before_handle_events`, respectively. These are now opt-in through the `NEEDS_EXTRA_LIFECYCLE_EVENTS` associated constant, and occur at slightly different times to the methods they are replacing. This allows greater compatibility with Wayland based event sources. ## 0.11.0 -- 2023-06-05 #### Bugfixes - Fixed a crash due to double borrow when handling pre/post run hooks - Fixes a panic that can occur when large `Duration`s are passed to `Timer::from_duration`. - Replace the `sys` module with the `polling` crate. #### Additions - With the `block_on` feature enabled, the `EventLoop` method now has a `block_on` method that runs a future to completion on the event loop. #### Breaking changes - Bump MSRV to 1.56 - **Breaking:** The `TransientSource` is now an opaque type. It provides API methods for removing or replacing the wrapped source. This mitigates a potential leak of registration data if the TransientSource is replaced by direct assignment in a parent source. - **Breaking:** `Timer::current_deadline` returns `Option`, so that it can return `None` in the event of an overflow. - **Breaking:** Use `AsFd` instead of `AsRawFd`/`RawFd`. ## 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`. - **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` 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>`, 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` 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` 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 calloop-0.13.0/Cargo.lock0000644000000241100000000000100105170ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "async-task" version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "calloop" version = "0.13.0" dependencies = [ "async-task", "bitflags", "futures", "futures-io", "log", "nix", "pin-utils", "polling", "rustix", "slab", "thiserror", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "concurrent-queue" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "errno" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys", ] [[package]] name = "futures" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 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.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "linux-raw-sys" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "nix" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags", "cfg-if", "cfg_aliases", "libc", ] [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "polling" version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", "rustix", "tracing", "windows-sys", ] [[package]] name = "proc-macro2" version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "rustix" version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "syn" version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" calloop-0.13.0/Cargo.toml0000644000000036710000000000100105530ustar # 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" rust-version = "1.63.0" name = "calloop" version = "0.13.0" authors = ["Elinor Berger "] 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] features = [ "block_on", "executor", "signals", ] rustdoc-args = [ "--cfg", "docsrs", ] [[test]] name = "signals" harness = false [dependencies.async-task] version = "4.4.0" optional = true [dependencies.bitflags] version = "2.4" [dependencies.futures-io] version = "0.3.5" optional = true [dependencies.log] version = "0.4" [dependencies.pin-utils] version = "0.1.0" optional = true [dependencies.polling] version = "3.0.0" [dependencies.rustix] version = "0.38" features = [ "event", "fs", "pipe", "std", ] default-features = false [dependencies.slab] version = "0.4.8" [dependencies.thiserror] version = "1.0.7" [dev-dependencies.futures] version = "0.3.5" [dev-dependencies.rustix] version = "0.38" features = ["net"] default-features = false [features] block_on = ["pin-utils"] executor = ["async-task"] nightly_coverage = [] signals = ["nix"] [target."cfg(unix)".dependencies.nix] version = "0.28" features = ["signal"] optional = true default-features = false [badges.codecov] repository = "Smithay/calloop" calloop-0.13.0/Cargo.toml.orig000064400000000000000000000024651046102023000142340ustar 00000000000000[package] name = "calloop" version = "0.13.0" authors = ["Elinor Berger "] documentation = "https://docs.rs/calloop/" repository = "https://github.com/Smithay/calloop" license = "MIT" description = "A callback-based event loop" keywords = [ "events", "loop", "callback", "eventloop", "unix" ] autotests = false edition = "2018" readme = "README.md" rust-version = "1.63.0" [workspace] members = [ "doc" ] [badges] codecov = { repository = "Smithay/calloop" } [dependencies] async-task = { version = "4.4.0", optional = true } bitflags = "2.4" futures-io = { version = "0.3.5", optional = true } log = "0.4" pin-utils = { version = "0.1.0", optional = true } polling = "3.0.0" slab = "0.4.8" rustix = { version = "0.38", default-features = false, features = ["event", "fs", "pipe", "std"] } thiserror = "1.0.7" [target.'cfg(unix)'.dependencies] nix = { version = "0.28", default-features = false, features = ["signal"], optional = true } [dev-dependencies] futures = "0.3.5" rustix = { version = "0.38", default-features = false, features = ["net"] } [features] block_on = ["pin-utils"] executor = ["async-task"] nightly_coverage = [] signals = ["nix"] [package.metadata.docs.rs] features = ["block_on", "executor", "signals"] rustdoc-args = ["--cfg", "docsrs"] [[test]] name = "signals" harness = false calloop-0.13.0/LICENSE.txt000064400000000000000000000020411046102023000131560ustar 00000000000000Copyright (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. calloop-0.13.0/README.md000064400000000000000000000152431046102023000126220ustar 00000000000000[![crates.io](https://img.shields.io/crates/v/calloop.svg)](https://crates.io/crates/calloop) [![docs.rs](https://docs.rs/calloop/badge.svg)](https://docs.rs/calloop) [![Coverage Status](https://codecov.io/gh/Smithay/calloop/branch/master/graph/badge.svg)](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 = 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. ### Minimum Safe Rust Version The current MSRV (Minimum Safe Rust Version) for this crate is Rust **1.63**. When the `signals` feature is enabled, however, it will be bumped to `nix`'s MSRV. At the time of writing this is Rust **1.69**. License: MIT calloop-0.13.0/README.tpl000064400000000000000000000003401046102023000130110ustar 00000000000000[![crates.io](https://img.shields.io/crates/v/calloop.svg)](https://crates.io/crates/calloop) [![docs.rs](https://docs.rs/calloop/badge.svg)](https://docs.rs/calloop) {{badges}} # {{crate}} {{readme}} License: {{license}}calloop-0.13.0/examples/high_precision.rs000064400000000000000000000013231046102023000165130ustar 00000000000000use std::time::{Duration, Instant}; use calloop::{ timer::{TimeoutAction, Timer}, EventLoop, }; fn main() { // As of calloop v0.11 there is no difference between low and high precision event loops. let mut event_loop = EventLoop::try_new().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() ); } calloop-0.13.0/examples/timer.rs000064400000000000000000000067401046102023000146510ustar 00000000000000use 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 = 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!"); } calloop-0.13.0/src/error.rs000064400000000000000000000057611046102023000136350ustar 00000000000000//! 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), } impl From 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 = core::result::Result; /// An error generated when trying to insert an event source #[derive(thiserror::Error)] #[error("error inserting event source")] pub struct InsertError { /// The source that could not be inserted pub inserted: T, /// The generated error #[source] pub error: Error, } impl Debug for InsertError { #[cfg_attr(feature = "nightly_coverage", coverage(off))] fn fmt(&self, formatter: &mut Formatter) -> core::result::Result<(), fmt::Error> { write!(formatter, "{:?}", self.error) } } impl From> for crate::Error { /// Converts the [`InsertError`] into Calloop's error type, throwing away /// the contained source. #[cfg_attr(feature = "nightly_coverage", coverage(off))] fn from(e: InsertError) -> crate::Error { e.error } } calloop-0.13.0/src/io.rs000064400000000000000000000462401046102023000131100ustar 00000000000000//! 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::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll as TaskPoll, Waker}; #[cfg(unix)] use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{ AsRawSocket as AsRawFd, AsSocket as AsFd, BorrowedSocket as BorrowedFd, RawSocket as RawFd, }; #[cfg(feature = "futures-io")] use futures_io::{AsyncRead, AsyncWrite, IoSlice, IoSliceMut}; use crate::loop_logic::EventIterator; use crate::{ loop_logic::LoopInner, sources::EventDispatcher, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory, }; use crate::{AdditionalLifecycleEventsSet, RegistrationToken}; /// 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. /// /// ## Platform-Specific /// /// - **Windows:** Usually, on drop, the file descriptor is set back to its previous status. /// For example, if the file was previously nonblocking it will be set to nonblocking, and /// if the file was blocking it will be set to blocking. However, on Windows, it is impossible /// to tell what its status was before. Therefore it will always be set to blocking. pub struct Async<'l, F: AsFd> { fd: Option, dispatcher: Rc>, inner: Rc, was_nonblocking: bool, } impl<'l, F: AsFd + std::fmt::Debug> std::fmt::Debug for Async<'l, F> { #[cfg_attr(feature = "nightly_coverage", coverage(off))] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Async").field("fd", &self.fd).finish() } } impl<'l, F: AsFd> Async<'l, F> { pub(crate) fn new(inner: Rc>, fd: F) -> crate::Result> { // set non-blocking let was_nonblocking = set_nonblocking( #[cfg(unix)] fd.as_fd(), #[cfg(windows)] fd.as_socket(), true, )?; // register in the loop let dispatcher = Rc::new(RefCell::new(IoDispatcher { #[cfg(unix)] fd: fd.as_fd().as_raw_fd(), #[cfg(windows)] fd: fd.as_socket().as_raw_socket(), token: None, waker: None, is_registered: false, interest: Interest::EMPTY, last_readiness: Readiness::EMPTY, })); { let mut sources = inner.sources.borrow_mut(); let slot = sources.vacant_entry(); slot.source = Some(dispatcher.clone()); dispatcher.borrow_mut().token = Some(Token { inner: slot.token }); } // SAFETY: We are sure to deregister on drop. unsafe { 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 = unsafe { std::mem::transmute(inner as Rc) }; Ok(Async { fd: Some(fd), dispatcher, inner, was_nonblocking, }) } /// 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: AsFd> { io: &'s mut Async<'l, F>, } impl<'s, 'l, F: AsFd> 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: AsFd> { io: &'s mut Async<'l, F>, } impl<'s, 'l, F: AsFd> 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: AsFd> Drop for Async<'l, F> { fn drop(&mut self) { self.inner.kill(&self.dispatcher); // restore flags let _ = set_nonblocking( unsafe { BorrowedFd::borrow_raw(self.dispatcher.borrow().fd) }, self.was_nonblocking, ); } } impl<'l, F: AsFd> Unpin for Async<'l, F> {} trait IoLoopInner { unsafe fn register(&self, dispatcher: &RefCell) -> crate::Result<()>; fn reregister(&self, dispatcher: &RefCell) -> crate::Result<()>; fn kill(&self, dispatcher: &RefCell); } impl<'l, Data> IoLoopInner for LoopInner<'l, Data> { unsafe fn register(&self, dispatcher: &RefCell) -> crate::Result<()> { let disp = dispatcher.borrow(); self.poll.borrow_mut().register( unsafe { BorrowedFd::borrow_raw(disp.fd) }, Interest::EMPTY, Mode::OneShot, disp.token.expect("No token for IO dispatcher"), ) } fn reregister(&self, dispatcher: &RefCell) -> crate::Result<()> { let disp = dispatcher.borrow(); self.poll.borrow_mut().reregister( unsafe { BorrowedFd::borrow_raw(disp.fd) }, disp.interest, Mode::OneShot, disp.token.expect("No token for IO dispatcher"), ) } fn kill(&self, dispatcher: &RefCell) { let token = dispatcher .borrow() .token .expect("No token for IO dispatcher"); if let Ok(slot) = self.sources.borrow_mut().get_mut(token.inner) { slot.source = None; } } } struct IoDispatcher { fd: RawFd, // FIXME: `BorrowedFd`? How to statically verify it doesn't outlive file? token: Option, waker: Option, 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 EventDispatcher for RefCell { fn process_events( &self, readiness: Readiness, _token: Token, _data: &mut Data, ) -> crate::Result { 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 AdditionalLifecycleEventsSet, _: &mut TokenFactory, ) -> crate::Result<()> { // registration is handled by IoLoopInner unreachable!() } fn reregister( &self, _: &mut Poll, _: &mut AdditionalLifecycleEventsSet, _: &mut TokenFactory, ) -> crate::Result { // registration is handled by IoLoopInner unreachable!() } fn unregister( &self, poll: &mut Poll, _: &mut AdditionalLifecycleEventsSet, _: RegistrationToken, ) -> crate::Result { let disp = self.borrow(); if disp.is_registered { poll.unregister(unsafe { BorrowedFd::borrow_raw(disp.fd) })?; } Ok(true) } fn before_sleep(&self) -> crate::Result> { Ok(None) } fn before_handle_events(&self, _: EventIterator<'_>) {} } /* * Async IO trait implementations */ #[cfg(feature = "futures-io")] #[cfg_attr(docsrs, doc(cfg(feature = "futures-io")))] impl<'l, F: AsFd + std::io::Read> AsyncRead for Async<'l, F> { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> TaskPoll> { 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> { 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: AsFd + std::io::Write> AsyncWrite for Async<'l, F> { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> TaskPoll> { 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> { 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> { 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> { self.poll_flush(cx) } } // https://github.com/smol-rs/async-io/blob/6499077421495f2200d5b86918399f3a84bbe8e4/src/lib.rs#L2171-L2195 /// Set the nonblocking status of an FD and return whether it was nonblocking before. #[allow(clippy::needless_return)] #[inline] fn set_nonblocking(fd: BorrowedFd<'_>, is_nonblocking: bool) -> std::io::Result { #[cfg(windows)] { rustix::io::ioctl_fionbio(fd, is_nonblocking)?; // Unfortunately it is impossible to tell if a socket was nonblocking on Windows. // Just say it wasn't for now. return Ok(false); } #[cfg(not(windows))] { let previous = rustix::fs::fcntl_getfl(fd)?; let new = if is_nonblocking { previous | rustix::fs::OFlags::NONBLOCK } else { previous & !(rustix::fs::OFlags::NONBLOCK) }; if new != previous { rustix::fs::fcntl_setfl(fd, new)?; } return Ok(previous.contains(rustix::fs::OFlags::NONBLOCK)); } } #[cfg(all(test, unix, 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::>(); 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::>(); 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(); } } } calloop-0.13.0/src/lib.rs000064400000000000000000000164031046102023000132450ustar 00000000000000//! 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 = //! 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(feature = "nightly_coverage", feature(coverage_attribute))] 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 list; mod loop_logic; mod macros; mod sources; mod token; calloop-0.13.0/src/list.rs000064400000000000000000000041031046102023000134440ustar 00000000000000use std::rc::Rc; use crate::sources::EventDispatcher; use crate::token::TokenInner; pub(crate) struct SourceEntry<'l, Data> { pub(crate) token: TokenInner, pub(crate) source: Option + 'l>>, } pub(crate) struct SourceList<'l, Data> { sources: Vec>, } impl<'l, Data> SourceList<'l, Data> { pub(crate) fn new() -> Self { SourceList { sources: Vec::new(), } } pub(crate) fn vacant_entry(&mut self) -> &mut SourceEntry<'l, Data> { let opt_id = self.sources.iter().position(|slot| slot.source.is_none()); match opt_id { Some(id) => { // we are reusing a slot let slot = &mut self.sources[id]; // increment the slot version slot.token = slot.token.increment_version(); slot } None => { // we are inserting a new slot let next_id = self.sources.len(); self.sources.push(SourceEntry { token: TokenInner::new(self.sources.len()) .expect("Trying to insert too many sources in an event loop."), source: None, }); &mut self.sources[next_id] } } } pub(crate) fn get(&self, token: TokenInner) -> crate::Result<&SourceEntry<'l, Data>> { let entry = self .sources .get(token.get_id()) .ok_or(crate::Error::InvalidToken)?; if entry.token.same_source_as(token) { Ok(entry) } else { Err(crate::Error::InvalidToken) } } pub(crate) fn get_mut( &mut self, token: TokenInner, ) -> crate::Result<&mut SourceEntry<'l, Data>> { let entry = self .sources .get_mut(token.get_id()) .ok_or(crate::Error::InvalidToken)?; if entry.token.same_source_as(token) { Ok(entry) } else { Err(crate::Error::InvalidToken) } } } calloop-0.13.0/src/loop_logic.rs000064400000000000000000001527431046102023000146350ustar 00000000000000use std::cell::{Cell, RefCell}; use std::fmt::Debug; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::{Duration, Instant}; use std::{io, slice}; #[cfg(feature = "block_on")] use std::future::Future; #[cfg(unix)] use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{AsHandle, AsRawHandle, AsSocket as AsFd, BorrowedHandle, RawHandle}; use log::trace; use polling::Poller; use crate::list::{SourceEntry, SourceList}; use crate::sources::{Dispatcher, EventSource, Idle, IdleDispatcher}; use crate::sys::{Notifier, PollEvent}; use crate::token::TokenInner; use crate::{ AdditionalLifecycleEventsSet, InsertError, Poll, PostAction, Readiness, Token, TokenFactory, }; type IdleCallback<'i, Data> = Rc + 'i>>; /// 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(Clone, Copy, Debug, PartialEq, Eq)] pub struct RegistrationToken { inner: TokenInner, } impl RegistrationToken { /// Create the RegistrationToken corresponding to the given raw key /// This is needed because some methods use `RegistrationToken`s as /// raw usizes within this crate pub(crate) fn new(inner: TokenInner) -> Self { Self { inner } } } pub(crate) struct LoopInner<'l, Data> { pub(crate) poll: RefCell, // The `Option` is used to keep slots of the slab occipied, to prevent id reuse // while in-flight events might still referr to a recently destroyed event source. pub(crate) sources: RefCell>, pub(crate) sources_with_additional_lifecycle_events: RefCell, idles: RefCell>>, pending_action: Cell, } /// 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>, } impl<'l, Data> std::fmt::Debug for LoopHandle<'l, Data> { #[cfg_attr(feature = "nightly_coverage", coverage(off))] 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(feature = "nightly_coverage", coverage(off))] 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( &self, source: S, callback: F, ) -> Result> 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`. #[cfg_attr(feature = "nightly_coverage", coverage(off))] // Contains a branch we can't hit w/o OOM pub fn register_dispatcher( &self, dispatcher: Dispatcher<'l, S, Data>, ) -> crate::Result where S: EventSource + 'l, { let mut sources = self.inner.sources.borrow_mut(); let mut poll = self.inner.poll.borrow_mut(); // Find an empty slot if any let slot = sources.vacant_entry(); slot.source = Some(dispatcher.clone_as_event_dispatcher()); trace!("[calloop] Inserting new source #{}", slot.token.get_id()); let ret = slot.source.as_ref().unwrap().register( &mut poll, &mut self .inner .sources_with_additional_lifecycle_events .borrow_mut(), &mut TokenFactory::new(slot.token), ); if let Err(error) = ret { slot.source = None; return Err(error); } Ok(RegistrationToken { inner: slot.token }) } /// 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 &SourceEntry { token: entry_token, source: Some(ref source), } = self.inner.sources.borrow().get(token.inner)? { trace!("[calloop] Registering source #{}", entry_token.get_id()); source.register( &mut self.inner.poll.borrow_mut(), &mut self .inner .sources_with_additional_lifecycle_events .borrow_mut(), &mut TokenFactory::new(entry_token), ) } else { Err(crate::Error::InvalidToken) } } /// 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 &SourceEntry { token: entry_token, source: Some(ref source), } = self.inner.sources.borrow().get(token.inner)? { trace!( "[calloop] Updating registration of source #{}", entry_token.get_id() ); if !source.reregister( &mut self.inner.poll.borrow_mut(), &mut self .inner .sources_with_additional_lifecycle_events .borrow_mut(), &mut TokenFactory::new(entry_token), )? { trace!("[calloop] Cannot do it now, storing for later."); // we are in a callback, store for later processing self.inner.pending_action.set(PostAction::Reregister); } Ok(()) } else { Err(crate::Error::InvalidToken) } } /// 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 &SourceEntry { token: entry_token, source: Some(ref source), } = self.inner.sources.borrow().get(token.inner)? { if !token.inner.same_source_as(entry_token) { // The token provided by the user is no longer valid return Err(crate::Error::InvalidToken); } trace!("[calloop] Unregistering source #{}", entry_token.get_id()); if !source.unregister( &mut self.inner.poll.borrow_mut(), &mut self .inner .sources_with_additional_lifecycle_events .borrow_mut(), *token, )? { trace!("[calloop] Cannot do it now, storing for later."); // we are in a callback, store for later processing self.inner.pending_action.set(PostAction::Disable); } Ok(()) } else { Err(crate::Error::InvalidToken) } } /// Removes this source from the event loop. pub fn remove(&self, token: RegistrationToken) { if let Ok(&mut SourceEntry { token: entry_token, ref mut source, }) = self.inner.sources.borrow_mut().get_mut(token.inner) { if let Some(source) = source.take() { trace!("[calloop] Removing source #{}", entry_token.get_id()); if let Err(e) = source.unregister( &mut self.inner.poll.borrow_mut(), &mut self .inner .sources_with_additional_lifecycle_events .borrow_mut(), token, ) { 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(&self, fd: F) -> crate::Result> { 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> { #[allow(dead_code)] poller: Arc, handle: LoopHandle<'l, Data>, signals: Arc, // A caching vector for synthetic poll events synthetic_events: Vec, } impl<'l, Data> std::fmt::Debug for EventLoop<'l, Data> { #[cfg_attr(feature = "nightly_coverage", coverage(off))] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("EventLoop { ... }") } } /// Signals related to the event loop. struct Signals { /// Signal to stop the event loop. stop: AtomicBool, /// Signal that the future is ready. #[cfg(feature = "block_on")] future_ready: AtomicBool, } 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 { let poll = Poll::new()?; let poller = poll.poller.clone(); let handle = LoopHandle { inner: Rc::new(LoopInner { poll: RefCell::new(poll), sources: RefCell::new(SourceList::new()), idles: RefCell::new(Vec::new()), pending_action: Cell::new(PostAction::Continue), sources_with_additional_lifecycle_events: Default::default(), }), }; Ok(EventLoop { handle, signals: Arc::new(Signals { stop: AtomicBool::new(false), #[cfg(feature = "block_on")] future_ready: AtomicBool::new(false), }), poller, synthetic_events: vec![], }) } /// Retrieve a loop handle pub fn handle(&self) -> LoopHandle<'l, Data> { self.handle.clone() } fn dispatch_events( &mut self, mut timeout: Option, data: &mut Data, ) -> crate::Result<()> { let now = Instant::now(); { let mut extra_lifecycle_sources = self .handle .inner .sources_with_additional_lifecycle_events .borrow_mut(); let sources = &self.handle.inner.sources.borrow(); for source in &mut *extra_lifecycle_sources.values { if let Ok(SourceEntry { source: Some(disp), .. }) = sources.get(source.inner) { if let Some((readiness, token)) = disp.before_sleep()? { // Wake up instantly after polling if we recieved an event timeout = Some(Duration::ZERO); self.synthetic_events.push(PollEvent { readiness, token }); } } else { unreachable!() } } } let events = { let poll = self.handle.inner.poll.borrow(); 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), }; } }; { let mut extra_lifecycle_sources = self .handle .inner .sources_with_additional_lifecycle_events .borrow_mut(); if !extra_lifecycle_sources.values.is_empty() { for source in &mut *extra_lifecycle_sources.values { if let Ok(SourceEntry { source: Some(disp), .. }) = self.handle.inner.sources.borrow().get(source.inner) { let iter = EventIterator { inner: events.iter(), registration_token: *source, }; disp.before_handle_events(iter); } else { unreachable!() } } } } for event in self.synthetic_events.drain(..).chain(events) { // Get the registration token associated with the event. let reg_token = event.token.inner.forget_sub_id(); let opt_disp = self .handle .inner .sources .borrow() .get(reg_token) .ok() .and_then(|entry| entry.source.clone()); if let Some(disp) = opt_disp { trace!( "[calloop] Dispatching events for source #{}", reg_token.get_id() ); 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 => { trace!( "[calloop] Postaction reregister for source #{}", reg_token.get_id() ); disp.reregister( &mut self.handle.inner.poll.borrow_mut(), &mut self .handle .inner .sources_with_additional_lifecycle_events .borrow_mut(), &mut TokenFactory::new(reg_token), )?; } PostAction::Disable => { trace!( "[calloop] Postaction unregister for source #{}", reg_token.get_id() ); disp.unregister( &mut self.handle.inner.poll.borrow_mut(), &mut self .handle .inner .sources_with_additional_lifecycle_events .borrow_mut(), RegistrationToken::new(reg_token), )?; } PostAction::Remove => { trace!( "[calloop] Postaction remove for source #{}", reg_token.get_id() ); if let Ok(entry) = self.handle.inner.sources.borrow_mut().get_mut(reg_token) { entry.source = None; } } PostAction::Continue => {} } if self .handle .inner .sources .borrow() .get(reg_token) .ok() .map(|entry| entry.source.is_none()) .unwrap_or(true) { // 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, &mut self .handle .inner .sources_with_additional_lifecycle_events .borrow_mut(), RegistrationToken::new(reg_token), ) { log::warn!( "[calloop] Failed to unregister source from the polling system: {:?}", e ); } } } else { log::warn!( "[calloop] Received an event for non-existence source: {:?}", reg_token ); } } 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); } } /// 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>>( &mut self, timeout: D, data: &mut Data, ) -> crate::Result<()> { self.dispatch_events(timeout.into(), data)?; self.dispatch_idles(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.signals.clone(), notifier: self.handle.inner.poll.borrow().notifier(), } } /// 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>>( &mut self, timeout: D, data: &mut Data, mut cb: F, ) -> crate::Result<()> where F: FnMut(&mut Data), { let timeout = timeout.into(); self.signals.stop.store(false, Ordering::Release); while !self.signals.stop.load(Ordering::Acquire) { self.dispatch(timeout, data)?; cb(data); } Ok(()) } /// Block a future on this event loop. /// /// This will run the provided future on this event loop, blocking until it is /// resolved. /// /// If [`LoopSignal::stop()`] is called before the future is resolved, this function returns /// `None`. #[cfg(feature = "block_on")] pub fn block_on( &mut self, future: impl Future, data: &mut Data, mut cb: impl FnMut(&mut Data), ) -> crate::Result> { use std::task::{Context, Poll, Wake, Waker}; /// A waker that will wake up the event loop when it is ready to make progress. struct EventLoopWaker(LoopSignal); impl Wake for EventLoopWaker { fn wake(self: Arc) { // Set the waker. self.0.signal.future_ready.store(true, Ordering::Release); self.0.notifier.notify().ok(); } fn wake_by_ref(self: &Arc) { // Set the waker. self.0.signal.future_ready.store(true, Ordering::Release); self.0.notifier.notify().ok(); } } // Pin the future to the stack. pin_utils::pin_mut!(future); // Create a waker that will wake up the event loop when it is ready to make progress. let waker = { let handle = EventLoopWaker(self.get_signal()); Waker::from(Arc::new(handle)) }; let mut context = Context::from_waker(&waker); // Begin running the loop. let mut output = None; self.signals.stop.store(false, Ordering::Release); self.signals.future_ready.store(true, Ordering::Release); while !self.signals.stop.load(Ordering::Acquire) { // If the future is ready to be polled, poll it. if self.signals.future_ready.swap(false, Ordering::AcqRel) { // Poll the future and break the loop if it's ready. if let Poll::Ready(result) = future.as_mut().poll(&mut context) { output = Some(result); break; } } // Otherwise, block on the event loop. self.dispatch_events(None, data)?; self.dispatch_idles(data); cb(data); } Ok(output) } } #[cfg(unix)] impl<'l, Data> AsRawFd for EventLoop<'l, Data> { /// Get the underlying raw-fd of the poller. /// /// This could be used to create [`Generic`] source out of the current loop /// and inserting into some other [`EventLoop`]. It's recommended to clone `fd` /// before doing so. /// /// [`Generic`]: crate::generic::Generic fn as_raw_fd(&self) -> RawFd { self.poller.as_raw_fd() } } #[cfg(unix)] impl<'l, Data> AsFd for EventLoop<'l, Data> { /// Get the underlying fd of the poller. /// /// This could be used to create [`Generic`] source out of the current loop /// and inserting into some other [`EventLoop`]. /// /// [`Generic`]: crate::generic::Generic fn as_fd(&self) -> BorrowedFd<'_> { self.poller.as_fd() } } #[cfg(windows)] impl AsRawHandle for EventLoop<'_, Data> { fn as_raw_handle(&self) -> RawHandle { self.poller.as_raw_handle() } } #[cfg(windows)] impl AsHandle for EventLoop<'_, Data> { fn as_handle(&self) -> BorrowedHandle<'_> { self.poller.as_handle() } } #[derive(Clone, Debug)] /// The EventIterator is an `Iterator` over the events relevant to a particular source /// This type is used in the [`EventSource::before_handle_events`] methods for /// two main reasons: /// - To avoid dynamic dispatch overhead /// - Secondly, it is to allow this type to be `Clone`, which is not /// possible with dynamic dispatch pub struct EventIterator<'a> { inner: slice::Iter<'a, PollEvent>, registration_token: RegistrationToken, } impl<'a> Iterator for EventIterator<'a> { type Item = (Readiness, Token); fn next(&mut self) -> Option { for next in self.inner.by_ref() { if next .token .inner .same_source_as(self.registration_token.inner) { return Some((next.readiness, next.token)); } } None } } /// A signal that can be shared between thread to stop or wakeup a running /// event loop #[derive(Clone)] pub struct LoopSignal { signal: Arc, notifier: Notifier, } impl std::fmt::Debug for LoopSignal { #[cfg_attr(feature = "nightly_coverage", coverage(off))] 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 useful if you are using the `EventLoop::run()` method. pub fn stop(&self) { self.signal.stop.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.notifier.notify().ok(); } } #[cfg(test)] mod tests { use std::{cell::Cell, rc::Rc, time::Duration}; use crate::{ channel::{channel, Channel}, ping::*, EventIterator, EventSource, Poll, PostAction, Readiness, RegistrationToken, Token, TokenFactory, }; #[cfg(unix)] use crate::{generic::Generic, Dispatcher, Interest, Mode}; 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 additional_events() { let mut event_loop: EventLoop<'_, Lock> = EventLoop::try_new().unwrap(); let mut lock = Lock { lock: Rc::new(( // Whether the lock is locked Cell::new(false), // The total number of events processed in process_events Cell::new(0), // The total number of events processed in before_handle_events // This is used to ensure that the count seen in before_handle_events is expected Cell::new(0), )), }; let (sender, channel) = channel(); let token = event_loop .handle() .insert_source( LockingSource { channel, lock: lock.clone(), }, |_, _, lock| { lock.lock(); lock.unlock(); }, ) .unwrap(); sender.send(()).unwrap(); event_loop.dispatch(None, &mut lock).unwrap(); // We should have been locked twice so far assert_eq!(lock.lock.1.get(), 2); // And we should have received one event assert_eq!(lock.lock.2.get(), 1); event_loop.handle().disable(&token).unwrap(); event_loop .dispatch(Some(Duration::ZERO), &mut lock) .unwrap(); assert_eq!(lock.lock.1.get(), 2); event_loop.handle().enable(&token).unwrap(); event_loop .dispatch(Some(Duration::ZERO), &mut lock) .unwrap(); assert_eq!(lock.lock.1.get(), 3); event_loop.handle().remove(token); event_loop .dispatch(Some(Duration::ZERO), &mut lock) .unwrap(); assert_eq!(lock.lock.1.get(), 3); assert_eq!(lock.lock.2.get(), 1); #[derive(Clone)] struct Lock { lock: Rc<(Cell, Cell, Cell)>, } impl Lock { fn lock(&self) { if self.lock.0.get() { panic!(); } // Increase the count self.lock.1.set(self.lock.1.get() + 1); self.lock.0.set(true) } fn unlock(&self) { if !self.lock.0.get() { panic!(); } self.lock.0.set(false); } } struct LockingSource { channel: Channel<()>, lock: Lock, } impl EventSource for LockingSource { type Event = as EventSource>::Event; type Metadata = as EventSource>::Metadata; type Ret = as EventSource>::Ret; type Error = as EventSource>::Error; fn process_events( &mut self, readiness: Readiness, token: Token, callback: F, ) -> Result where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { self.channel.process_events(readiness, token, callback) } fn register( &mut self, poll: &mut Poll, token_factory: &mut TokenFactory, ) -> crate::Result<()> { self.channel.register(poll, token_factory) } fn reregister( &mut self, poll: &mut Poll, token_factory: &mut TokenFactory, ) -> crate::Result<()> { self.channel.reregister(poll, token_factory) } fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> { self.channel.unregister(poll) } const NEEDS_EXTRA_LIFECYCLE_EVENTS: bool = true; fn before_sleep(&mut self) -> crate::Result> { self.lock.lock(); Ok(None) } fn before_handle_events(&mut self, events: EventIterator) { let events_count = events.count(); let lock = &self.lock.lock; lock.2.set(lock.2.get() + events_count as u32); self.lock.unlock(); } } } #[test] fn default_additional_events() { let (sender, channel) = channel(); let mut test_source = NoopWithDefaultHandlers { channel }; let mut event_loop = EventLoop::try_new().unwrap(); event_loop .handle() .insert_source(Box::new(&mut test_source), |_, _, _| {}) .unwrap(); sender.send(()).unwrap(); event_loop.dispatch(None, &mut ()).unwrap(); struct NoopWithDefaultHandlers { channel: Channel<()>, } impl EventSource for NoopWithDefaultHandlers { type Event = as EventSource>::Event; type Metadata = as EventSource>::Metadata; type Ret = as EventSource>::Ret; type Error = as EventSource>::Error; fn process_events( &mut self, readiness: Readiness, token: Token, callback: F, ) -> Result where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { self.channel.process_events(readiness, token, callback) } fn register( &mut self, poll: &mut Poll, token_factory: &mut TokenFactory, ) -> crate::Result<()> { self.channel.register(poll, token_factory) } fn reregister( &mut self, poll: &mut Poll, token_factory: &mut TokenFactory, ) -> crate::Result<()> { self.channel.reregister(poll, token_factory) } fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> { self.channel.unregister(poll) } const NEEDS_EXTRA_LIFECYCLE_EVENTS: bool = true; } } #[test] fn additional_events_synthetic() { let mut event_loop: EventLoop<'_, Lock> = EventLoop::try_new().unwrap(); let mut lock = Lock { lock: Rc::new(Cell::new(false)), }; event_loop .handle() .insert_source( InstantWakeupLockingSource { lock: lock.clone(), token: None, }, |_, _, lock| { lock.lock(); lock.unlock(); }, ) .unwrap(); // Loop should finish, as event_loop.dispatch(None, &mut lock).unwrap(); #[derive(Clone)] struct Lock { lock: Rc>, } impl Lock { fn lock(&self) { if self.lock.get() { panic!(); } self.lock.set(true) } fn unlock(&self) { if !self.lock.get() { panic!(); } self.lock.set(false); } } struct InstantWakeupLockingSource { lock: Lock, token: Option, } impl EventSource for InstantWakeupLockingSource { type Event = (); type Metadata = (); type Ret = (); type Error = as EventSource>::Error; fn process_events( &mut self, _: Readiness, token: Token, mut callback: F, ) -> Result where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { assert_eq!(token, self.token.unwrap()); callback((), &mut ()); Ok(PostAction::Continue) } fn register( &mut self, _: &mut Poll, token_factory: &mut TokenFactory, ) -> crate::Result<()> { self.token = Some(token_factory.token()); Ok(()) } fn reregister(&mut self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result<()> { unreachable!() } fn unregister(&mut self, _: &mut Poll) -> crate::Result<()> { unreachable!() } const NEEDS_EXTRA_LIFECYCLE_EVENTS: bool = true; fn before_sleep(&mut self) -> crate::Result> { self.lock.lock(); Ok(Some((Readiness::EMPTY, self.token.unwrap()))) } fn before_handle_events(&mut self, _: EventIterator) { self.lock.unlock(); } } } #[cfg(unix)] #[test] fn insert_bad_source() { use std::os::unix::io::FromRawFd; let event_loop = EventLoop::<()>::try_new().unwrap(); let fd = unsafe { std::os::unix::io::OwnedFd::from_raw_fd(420) }; let ret = event_loop.handle().insert_source( crate::sources::generic::Generic::new(fd, Interest::READ, Mode::Level), |_, _, _| Ok(PostAction::Continue), ); assert!(ret.is_err()); } #[test] fn invalid_token() { let (_ping, source) = crate::sources::ping::make_ping().unwrap(); let event_loop = EventLoop::<()>::try_new().unwrap(); let handle = event_loop.handle(); let reg_token = handle.insert_source(source, |_, _, _| {}).unwrap(); handle.remove(reg_token); let ret = handle.enable(®_token); assert!(ret.is_err()); } #[cfg(unix)] #[test] fn insert_source_no_interest() { use rustix::pipe::pipe; // Create a pipe to get an arbitrary fd. let (read, _write) = pipe().unwrap(); let source = crate::sources::generic::Generic::new(read, Interest::EMPTY, Mode::Level); let dispatcher = Dispatcher::new(source, |_, _, _| Ok(PostAction::Continue)); let event_loop = EventLoop::<()>::try_new().unwrap(); let handle = event_loop.handle(); let ret = handle.register_dispatcher(dispatcher.clone()); if let Ok(token) = ret { // Unwrap the dispatcher+source and close the read end. handle.remove(token); } else { // Fail the test. panic!(); } } #[test] fn disarm_rearm() { let mut event_loop = EventLoop::::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); // 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( &mut self, readiness: Readiness, token: Token, mut callback: F, ) -> Result 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::::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); } #[cfg(unix)] #[test] fn change_interests() { use rustix::io::write; use rustix::net::{recv, socketpair, AddressFamily, RecvFlags, SocketFlags, SocketType}; let mut event_loop = EventLoop::::try_new().unwrap(); let (sock1, sock2) = socketpair( AddressFamily::UNIX, SocketType::STREAM, SocketFlags::empty(), None, // 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, |_, fd, dispatched| { *dispatched = true; // read all contents available to drain the socket let mut buf = [0u8; 32]; loop { match recv(&*fd, &mut buf, RecvFlags::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::>::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::>::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); } #[cfg(feature = "block_on")] #[test] fn block_on_test() { use crate::sources::timer::TimeoutFuture; use std::time::Duration; let mut evl = EventLoop::<()>::try_new().unwrap(); let mut data = 22; let timeout = { let data = &mut data; let evl_handle = evl.handle(); async move { TimeoutFuture::from_duration(&evl_handle, Duration::from_secs(2)).await; *data = 32; 11 } }; let result = evl.block_on(timeout, &mut (), |&mut ()| {}).unwrap(); assert_eq!(result, Some(11)); assert_eq!(data, 32); } #[cfg(feature = "block_on")] #[test] fn block_on_early_cancel() { use crate::sources::timer; use std::time::Duration; let mut evl = EventLoop::<()>::try_new().unwrap(); let mut data = 22; let timeout = { let data = &mut data; let evl_handle = evl.handle(); async move { timer::TimeoutFuture::from_duration(&evl_handle, Duration::from_secs(2)).await; *data = 32; 11 } }; let timer_source = timer::Timer::from_duration(Duration::from_secs(1)); let handle = evl.get_signal(); let _timer_token = evl .handle() .insert_source(timer_source, move |_, _, _| { handle.stop(); timer::TimeoutAction::Drop }) .unwrap(); let result = evl.block_on(timeout, &mut (), |&mut ()| {}).unwrap(); assert_eq!(result, None); assert_eq!(data, 22); } #[test] fn reuse() { use crate::sources::timer; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; let mut evl = EventLoop::::try_new().unwrap(); let handle = evl.handle(); let data = Arc::new(Mutex::new(1)); let data_cloned = data.clone(); let timer_source = timer::Timer::from_duration(Duration::from_secs(1)); let mut first_timer_token = evl .handle() .insert_source(timer_source, move |_, _, own_token| { handle.remove(*own_token); let data_cloned = data_cloned.clone(); let _ = handle.insert_source(timer::Timer::immediate(), move |_, _, _| { *data_cloned.lock().unwrap() = 2; timer::TimeoutAction::Drop }); timer::TimeoutAction::Drop }) .unwrap(); let now = Instant::now(); loop { evl.dispatch(Some(Duration::from_secs(3)), &mut first_timer_token) .unwrap(); if Instant::now().duration_since(now) > Duration::from_secs(3) { break; } } assert_eq!(*data.lock().unwrap(), 2); } #[test] fn drop_of_subsource() { struct WithSubSource { token: Option, } impl crate::EventSource for WithSubSource { type Event = (); type Metadata = (); type Ret = (); type Error = crate::Error; const NEEDS_EXTRA_LIFECYCLE_EVENTS: bool = true; fn process_events( &mut self, _: Readiness, _: Token, mut callback: F, ) -> Result where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { callback((), &mut ()); // Drop the source Ok(PostAction::Remove) } fn register(&mut self, _: &mut Poll, fact: &mut TokenFactory) -> crate::Result<()> { // produce a few tokens to emulate a subsource fact.token(); fact.token(); self.token = Some(fact.token()); Ok(()) } fn reregister(&mut self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result<()> { Ok(()) } fn unregister(&mut self, _: &mut Poll) -> crate::Result<()> { Ok(()) } // emulate a readiness fn before_sleep(&mut self) -> crate::Result> { Ok(self.token.map(|token| { ( Readiness { readable: true, writable: false, error: false, }, token, ) })) } } // Now the actual test let mut evl = EventLoop::::try_new().unwrap(); evl.handle() .insert_source(WithSubSource { token: None }, |_, _, ran| { *ran = true; }) .unwrap(); let mut ran = false; evl.dispatch(Some(Duration::ZERO), &mut ran).unwrap(); assert!(ran); } // 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( &mut self, _: Readiness, _: Token, mut callback: F, ) -> Result 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(()) } } } calloop-0.13.0/src/macros.rs000064400000000000000000000143341046102023000137640ustar 00000000000000//! 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; fn process_events( &mut self, readiness: crate::Readiness, token: crate::Token, mut callback: F, ) -> Result 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]); } } calloop-0.13.0/src/sources/channel.rs000064400000000000000000000234111046102023000155670ustar 00000000000000//! 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 { /// 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 { sender: mpsc::Sender, ping: Ping, } impl Clone for Sender { #[cfg_attr(feature = "nightly_coverage", coverage(off))] fn clone(&self) -> Sender { Sender { sender: self.sender.clone(), ping: self.ping.clone(), } } } impl Sender { /// 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> { self.sender.send(t).map(|()| self.ping.ping()) } } impl Drop for Sender { 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 { sender: mpsc::SyncSender, ping: Ping, } impl Clone for SyncSender { #[cfg_attr(feature = "nightly_coverage", coverage(off))] fn clone(&self) -> SyncSender { SyncSender { sender: self.sender.clone(), ping: self.ping.clone(), } } } impl SyncSender { /// 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> { 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> { 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 { receiver: mpsc::Receiver, 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 has a count of 1, and it is obviously // safe to Send between threads. unsafe impl Send for Channel {} impl Channel { /// 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 { 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 { self.receiver.try_recv() } } /// Create a new asynchronous channel pub fn channel() -> (Sender, Channel) { 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(bound: usize) -> (SyncSender, Channel) { 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 EventSource for Channel { type Event = Event; type Metadata = (); type Ret = (); type Error = ChannelError; fn process_events( &mut self, readiness: Readiness, token: Token, mut callback: C, ) -> Result 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); } } calloop-0.13.0/src/sources/futures.rs000064400000000000000000000327351046102023000156650ustar 00000000000000//! 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, Scheduler)` 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 async_task::{Builder, Runnable}; use slab::Slab; use std::{ cell::RefCell, future::Future, rc::Rc, sync::{ atomic::{AtomicBool, Ordering}, mpsc, Arc, Mutex, }, task::Waker, }; use crate::{ sources::{ channel::ChannelError, ping::{make_ping, Ping, PingError, PingSource}, EventSource, }, Poll, PostAction, Readiness, Token, TokenFactory, }; /// A future executor as an event source #[derive(Debug)] pub struct Executor { /// Shared state between the executor and the scheduler. state: Rc>, /// Notifies us when the executor is woken up. ping: PingSource, } /// A scheduler to send futures to an executor #[derive(Clone, Debug)] pub struct Scheduler { /// Shared state between the executor and the scheduler. state: Rc>, } /// The inner state of the executor. #[derive(Debug)] struct State { /// The incoming queue of runnables to be executed. incoming: mpsc::Receiver>, /// The sender corresponding to `incoming`. sender: Arc, /// The list of currently active tasks. /// /// This is set to `None` when the executor is destroyed. active_tasks: RefCell>>>, } /// Send a future to an executor. /// /// This needs to be thread-safe, as it is called from a `Waker` that may be on a different thread. #[derive(Debug)] struct Sender { /// The sender used to send runnables to the executor. /// /// `mpsc::Sender` is `!Sync`, wrapping it in a `Mutex` makes it `Sync`. sender: Mutex>>, /// The ping source used to wake up the executor. wake_up: Ping, /// Whether the executor has already been woken. notified: AtomicBool, } /// An active future or its result. #[derive(Debug)] enum Active { /// The future is currently being polled. /// /// Waking this waker will insert the runnable into `incoming`. Future(Waker), /// The future has finished polling, and its result is stored here. Finished(T), } impl Active { fn is_finished(&self) -> bool { matches!(self, Active::Finished(_)) } } impl Scheduler { /// Sends the given future to the executor associated to this scheduler /// /// Returns an error if the the executor not longer exists. pub fn schedule(&self, future: Fut) -> Result<(), ExecutorDestroyed> where Fut: Future, T: 'static, { /// Store this future's result in the executor. struct StoreOnDrop<'a, T> { index: usize, value: Option, state: &'a State, } impl Drop for StoreOnDrop<'_, T> { fn drop(&mut self) { let mut active_tasks = self.state.active_tasks.borrow_mut(); if let Some(active_tasks) = active_tasks.as_mut() { if let Some(value) = self.value.take() { active_tasks[self.index] = Active::Finished(value); } else { // The future was dropped before it finished. // Remove it from the active list. active_tasks.remove(self.index); } } } } fn assert_send_and_sync(_: &T) {} let mut active_guard = self.state.active_tasks.borrow_mut(); let active_tasks = active_guard.as_mut().ok_or(ExecutorDestroyed)?; // Wrap the future in another future that polls it and stores the result. let index = active_tasks.vacant_key(); let future = { let state = self.state.clone(); async move { let mut guard = StoreOnDrop { index, value: None, state: &state, }; // Get the value of the future. let value = future.await; // Store it in the executor. guard.value = Some(value); } }; // A schedule function that inserts the runnable into the incoming queue. let schedule = { let sender = self.state.sender.clone(); move |runnable| sender.send(runnable) }; assert_send_and_sync(&schedule); // Spawn the future. let (runnable, task) = Builder::new() .metadata(index) .spawn_local(move |_| future, schedule); // Insert the runnable into the set of active tasks. active_tasks.insert(Active::Future(runnable.waker())); drop(active_guard); // Schedule the runnable and detach the task so it isn't cancellable. runnable.schedule(); task.detach(); Ok(()) } } impl Sender { /// Send a runnable to the executor. fn send(&self, runnable: Runnable) { // Send on the channel. // // All we do with the lock is call `send`, so there's no chance of any state being corrupted on // panic. Therefore it's safe to ignore the mutex poison. if let Err(e) = self .sender .lock() .unwrap_or_else(|e| e.into_inner()) .send(runnable) { // The runnable must be dropped on its origin thread, since the original future might be // !Send. This channel immediately sends it back to the Executor, which is pinned to the // origin thread. The executor's Drop implementation will force all of the runnables to be // dropped, therefore the channel should always be available. If we can't send the runnable, // it indicates that the above behavior is broken and that unsoundness has occurred. The // only option at this stage is to forget the runnable and leak the future. std::mem::forget(e); unreachable!("Attempted to send runnable to a stopped executor"); } // If the executor is already awake, don't bother waking it up again. if self.notified.swap(true, Ordering::SeqCst) { return; } // Wake the executor. self.wake_up.ping(); } } impl Drop for Executor { fn drop(&mut self) { let active_tasks = self.state.active_tasks.borrow_mut().take().unwrap(); // Wake all of the active tasks in order to destroy their runnables. for (_, task) in active_tasks { if let Active::Future(waker) = task { // Don't let a panicking waker blow everything up. // // There is a chance that a future will panic and, during the unwinding process, // drop this executor. However, since the future panicked, there is a possibility // that the internal state of the waker will be invalid in such a way that the waker // panics as well. Since this would be a panic during a panic, Rust will upgrade it // into an abort. // // In the interest of not aborting without a good reason, we just drop the panic here. std::panic::catch_unwind(|| waker.wake()).ok(); } } // Drain the queue in order to drop all of the runnables. while self.state.incoming.try_recv().is_ok() {} } } /// 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; /// 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() -> crate::Result<(Executor, Scheduler)> { let (sender, incoming) = mpsc::channel(); let (wake_up, ping) = make_ping()?; let state = Rc::new(State { incoming, active_tasks: RefCell::new(Some(Slab::new())), sender: Arc::new(Sender { sender: Mutex::new(sender), wake_up, notified: AtomicBool::new(false), }), }); Ok(( Executor { state: state.clone(), ping, }, Scheduler { state }, )) } impl EventSource for Executor { type Event = T; type Metadata = (); type Ret = (); type Error = ExecutorError; fn process_events( &mut self, readiness: Readiness, token: Token, mut callback: F, ) -> Result where F: FnMut(T, &mut ()), { let state = &self.state; let clear_readiness = { let mut clear_readiness = false; // Process runnables, but not too many at a time; better to move onto the next event quickly! for _ in 0..1024 { let runnable = match state.incoming.try_recv() { Ok(runnable) => runnable, Err(_) => { // Make sure to clear the readiness if there are no more runnables. clear_readiness = true; break; } }; // Run the runnable. let index = *runnable.metadata(); runnable.run(); // If the runnable finished with a result, call the callback. let mut active_guard = state.active_tasks.borrow_mut(); let active_tasks = active_guard.as_mut().unwrap(); if let Some(state) = active_tasks.get(index) { if state.is_finished() { // Take out the state and provide it to the caller. let result = match active_tasks.remove(index) { Active::Finished(result) => result, _ => unreachable!(), }; // Drop the guard since the callback may register another future to the scheduler. drop(active_guard); callback(result, &mut ()); } } } clear_readiness }; // Clear the readiness of the ping source if there are no more runnables. if clear_readiness { self.ping .process_events(readiness, token, |(), &mut ()| {}) .map_err(ExecutorError::WakeError)?; } // Set to the unnotified state. state.sender.notified.store(false, Ordering::SeqCst); Ok(PostAction::Continue) } fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> { self.ping.register(poll, token_factory)?; Ok(()) } fn reregister( &mut self, poll: &mut Poll, token_factory: &mut TokenFactory, ) -> crate::Result<()> { self.ping.reregister(poll, token_factory)?; Ok(()) } fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> { self.ping.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::::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 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); } } calloop-0.13.0/src/sources/generic.rs000064400000000000000000000356311046102023000156020ustar 00000000000000//! 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. //! #![cfg_attr(unix, doc = "```")] #![cfg_attr(not(unix), doc = "```no_run")] //! # 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(); //! # #[cfg(unix)] //! # let io_object = std::io::stdin(); //! # #[cfg(windows)] //! # let io_object: std::net::TcpStream = panic!(); //! 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 //! // 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. use polling::Poller; use std::{borrow, marker::PhantomData, ops, sync::Arc}; #[cfg(unix)] use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd}; #[cfg(windows)] use std::os::windows::io::{ AsRawSocket as AsRawFd, AsSocket as AsFd, BorrowedSocket as BorrowedFd, }; use crate::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory}; /// Wrapper to use a type implementing `AsRawFd` but not `AsFd` with `Generic` #[derive(Debug)] pub struct FdWrapper(T); impl FdWrapper { /// Wrap `inner` with an `AsFd` implementation. /// /// # Safety /// This is safe if the `AsRawFd` implementation of `inner` always returns /// a valid fd. This should usually be true for types implementing /// `AsRawFd`. But this isn't guaranteed with `FdWrapper`. pub unsafe fn new(inner: T) -> Self { Self(inner) } } impl ops::Deref for FdWrapper { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } } impl ops::DerefMut for FdWrapper { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl AsFd for FdWrapper { #[cfg(unix)] fn as_fd(&self) -> BorrowedFd { unsafe { BorrowedFd::borrow_raw(self.0.as_raw_fd()) } } #[cfg(windows)] fn as_socket(&self) -> BorrowedFd { unsafe { BorrowedFd::borrow_raw(self.0.as_raw_socket()) } } } /// A wrapper around a type that doesn't expose it mutably safely. /// /// The [`EventSource`] trait's `Metadata` type demands mutable access to the inner I/O source. /// However, the inner polling source used by `calloop` keeps the handle-based equivalent of an /// immutable pointer to the underlying object's I/O handle. Therefore, if the inner source is /// dropped, this leaves behind a dangling pointer which immediately invokes undefined behavior /// on the next poll of the event loop. /// /// In order to prevent this from happening, the [`Generic`] I/O source must not directly expose /// a mutable reference to the underlying handle. This type wraps around the underlying handle and /// easily allows users to take immutable (`&`) references to the type, but makes mutable (`&mut`) /// references unsafe to get. Therefore, it prevents the source from being moved out and dropped /// while it is still registered in the event loop. /// /// [`EventSource`]: crate::EventSource #[derive(Debug)] pub struct NoIoDrop(T); impl NoIoDrop { /// Get a mutable reference. /// /// # Safety /// /// The inner type's I/O source must not be dropped. pub unsafe fn get_mut(&mut self) -> &mut T { &mut self.0 } } impl AsRef for NoIoDrop { fn as_ref(&self) -> &T { &self.0 } } impl borrow::Borrow for NoIoDrop { fn borrow(&self) -> &T { &self.0 } } impl ops::Deref for NoIoDrop { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } } impl AsFd for NoIoDrop { #[cfg(unix)] fn as_fd(&self) -> BorrowedFd<'_> { // SAFETY: The innter type is not mutated. self.0.as_fd() } #[cfg(windows)] fn as_socket(&self) -> BorrowedFd<'_> { // SAFETY: The innter type is not mutated. self.0.as_socket() } } /// A generic event source wrapping a FD-backed type #[derive(Debug)] pub struct Generic { /// The wrapped FD-backed type. /// /// This must be deregistered before it is dropped. file: Option>, /// The programmed interest pub interest: Interest, /// The programmed mode pub mode: Mode, /// Back-reference to the poller. /// /// This is needed to drop the original file. poller: Option>, // This token is used by the event loop logic to look up this source when an // event occurs. token: Option, // This allows us to make the associated error and return types generic. _error_type: PhantomData, } impl Generic { /// 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 { Generic { file: Some(NoIoDrop(file)), interest, mode, token: None, poller: None, _error_type: PhantomData, } } /// Wrap a FD-backed type into a `Generic` event source using an arbitrary error type. pub fn new_with_error(file: F, interest: Interest, mode: Mode) -> Generic { Generic { file: Some(NoIoDrop(file)), interest, mode, token: None, poller: None, _error_type: PhantomData, } } } impl Generic { /// Unwrap the `Generic` source to retrieve the underlying type pub fn unwrap(mut self) -> F { let NoIoDrop(file) = self.file.take().unwrap(); // Remove it from the poller. if let Some(poller) = self.poller.take() { poller .delete( #[cfg(unix)] file.as_fd(), #[cfg(windows)] file.as_socket(), ) .ok(); } file } /// Get a reference to the underlying type. pub fn get_ref(&self) -> &F { &self.file.as_ref().unwrap().0 } /// Get a mutable reference to the underlying type. /// /// # Safety /// /// This is unsafe because it allows you to modify the underlying type, which /// allows you to drop the underlying event source. Dropping the underlying source /// leads to a dangling reference. pub unsafe fn get_mut(&mut self) -> &mut F { self.file.as_mut().unwrap().get_mut() } } impl Drop for Generic { fn drop(&mut self) { // Remove it from the poller. if let (Some(file), Some(poller)) = (self.file.take(), self.poller.take()) { poller .delete( #[cfg(unix)] file.as_fd(), #[cfg(windows)] file.as_socket(), ) .ok(); } } } impl EventSource for Generic where F: AsFd, E: Into>, { type Event = Readiness; type Metadata = NoIoDrop; type Ret = Result; type Error = E; fn process_events( &mut self, readiness: Readiness, token: Token, mut callback: C, ) -> Result 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, self.file.as_mut().unwrap()) } fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> { let token = token_factory.token(); // SAFETY: We ensure that we have a poller to deregister with (see below). unsafe { poll.register( &self.file.as_ref().unwrap().0, self.interest, self.mode, token, )?; } // Make sure we can use the poller to deregister if need be. // But only if registration actually succeeded // So that we don't try to unregister the FD on drop if it wasn't registered // in the first place (for example if registration failed because of a duplicate insertion) self.poller = Some(poll.poller().clone()); 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_ref().unwrap().0, self.interest, self.mode, token, )?; self.token = Some(token); Ok(()) } fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> { poll.unregister(&self.file.as_ref().unwrap().0)?; self.poller = None; self.token = None; Ok(()) } } #[cfg(all(unix, 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); } // Duplicate insertion does not fail on all platforms, but does on Linux #[cfg(target_os = "linux")] #[test] fn duplicate_insert() { use std::os::unix::{ io::{AsFd, BorrowedFd}, net::UnixStream, }; let event_loop = crate::EventLoop::<()>::try_new().unwrap(); let handle = event_loop.handle(); let (_, rx) = UnixStream::pair().unwrap(); // Rc only implements AsFd since 1.69... struct RcFd { rc: std::rc::Rc, } impl AsFd for RcFd { fn as_fd(&self) -> BorrowedFd<'_> { self.rc.as_fd() } } let rx = std::rc::Rc::new(rx); let token = handle .insert_source( Generic::new(RcFd { rc: rx.clone() }, Interest::READ, Mode::Level), |_, _, _| Ok(PostAction::Continue), ) .unwrap(); // inserting the same FD a second time should fail let ret = handle.insert_source( Generic::new(RcFd { rc: rx.clone() }, Interest::READ, Mode::Level), |_, _, _| Ok(PostAction::Continue), ); assert!(ret.is_err()); std::mem::drop(ret); // but the original token is still registered handle.update(&token).unwrap(); } } calloop-0.13.0/src/sources/mod.rs000064400000000000000000000606341046102023000147460ustar 00000000000000use std::{ cell::{Ref, RefCell, RefMut}, ops::{BitOr, BitOrAssign}, rc::Rc, }; use log::trace; pub use crate::loop_logic::EventIterator; use crate::{sys::TokenFactory, Poll, Readiness, RegistrationToken, Token}; pub mod channel; #[cfg(feature = "executor")] #[cfg_attr(docsrs, doc(cfg(feature = "executor")))] pub mod futures; pub mod generic; pub mod ping; #[cfg(all(target_os = "linux", feature = "signals"))] #[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(Copy, Clone, Debug, PartialEq, Eq)] 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 [`NEEDS_EXTRA_LIFECYCLE_EVENTS`] to `true`. /// For all sources for which that constant is `true`, the methods [`before_sleep`] and /// [`before_handle_events`] will be called. /// [`before_sleep`] is called before the polling system performs a poll operation. /// [`before_handle_events`] is called before any process_events methods have been called. /// This means that during `process_events` you can assume that all cleanup has occured on /// all sources. /// /// [`NEEDS_EXTRA_LIFECYCLE_EVENTS`]: EventSource::NEEDS_EXTRA_LIFECYCLE_EVENTS /// [`before_sleep`]: EventSource::before_sleep /// [`before_handle_events`]: EventSource::before_handle_events 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>; /// 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( &mut self, readiness: Readiness, token: Token, callback: F, ) -> Result 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<()>; /// Whether this source needs to be sent the [`EventSource::before_sleep`] /// and [`EventSource::before_handle_events`] notifications. These are opt-in because /// they require more expensive checks, and almost all sources will not need these notifications const NEEDS_EXTRA_LIFECYCLE_EVENTS: bool = false; /// Notification that a single `poll` is about to begin /// /// Use this to perform operations which must be done before polling, /// but which may conflict with other event handlers. For example, /// if polling requires a lock to be taken /// /// If this returns Ok(Some), this will be treated as an event arriving in polling, and /// your event handler will be called with the returned `Token` and `Readiness`. /// Polling will however still occur, but with a timeout of 0, so additional events /// from this or other sources may also be handled in the same iterations. /// The returned `Token` must belong to this source // If you need to return multiple synthetic events from this notification, please // open an issue fn before_sleep(&mut self) -> crate::Result> { Ok(None) } /// Notification that polling is complete, and [`EventSource::process_events`] will /// be called with the given events for this source. The iterator may be empty, /// which indicates that no events were generated for this source /// /// Please note, the iterator excludes any synthetic events returned from /// [`EventSource::before_sleep`] /// /// Use this to perform a cleanup before event handlers with arbitrary /// code may run. This could be used to drop a lock obtained in /// [`EventSource::before_sleep`] #[allow(unused_variables)] fn before_handle_events(&mut self, events: EventIterator<'_>) {} } /// Blanket implementation for boxed event sources. [`EventSource`] is not an /// object safe trait, so this does not include trait objects. impl EventSource for Box { type Event = T::Event; type Metadata = T::Metadata; type Ret = T::Ret; type Error = T::Error; fn process_events( &mut self, readiness: Readiness, token: Token, callback: F, ) -> Result 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) } const NEEDS_EXTRA_LIFECYCLE_EVENTS: bool = T::NEEDS_EXTRA_LIFECYCLE_EVENTS; fn before_sleep(&mut self) -> crate::Result> { T::before_sleep(&mut **self) } fn before_handle_events(&mut self, events: EventIterator) { T::before_handle_events(&mut **self, events) } } /// Blanket implementation for exclusive references to event sources. /// [`EventSource`] is not an object safe trait, so this does not include trait /// objects. impl EventSource for &mut T { type Event = T::Event; type Metadata = T::Metadata; type Ret = T::Ret; type Error = T::Error; fn process_events( &mut self, readiness: Readiness, token: Token, callback: F, ) -> Result 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) } const NEEDS_EXTRA_LIFECYCLE_EVENTS: bool = T::NEEDS_EXTRA_LIFECYCLE_EVENTS; fn before_sleep(&mut self) -> crate::Result> { T::before_sleep(&mut **self) } fn before_handle_events(&mut self, events: EventIterator) { T::before_handle_events(&mut **self, events) } } pub(crate) struct DispatcherInner { source: S, callback: F, needs_additional_lifecycle_events: bool, } impl EventDispatcher for RefCell> 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 { let mut disp = self.borrow_mut(); let DispatcherInner { ref mut source, ref mut callback, .. } = *disp; trace!( "[calloop] Processing events for source type {}", std::any::type_name::() ); 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, additional_lifecycle_register: &mut AdditionalLifecycleEventsSet, token_factory: &mut TokenFactory, ) -> crate::Result<()> { let mut this = self.borrow_mut(); if this.needs_additional_lifecycle_events { additional_lifecycle_register.register(token_factory.registration_token()); } this.source.register(poll, token_factory) } fn reregister( &self, poll: &mut Poll, additional_lifecycle_register: &mut AdditionalLifecycleEventsSet, token_factory: &mut TokenFactory, ) -> crate::Result { if let Ok(mut me) = self.try_borrow_mut() { me.source.reregister(poll, token_factory)?; if me.needs_additional_lifecycle_events { additional_lifecycle_register.register(token_factory.registration_token()); } Ok(true) } else { Ok(false) } } fn unregister( &self, poll: &mut Poll, additional_lifecycle_register: &mut AdditionalLifecycleEventsSet, registration_token: RegistrationToken, ) -> crate::Result { if let Ok(mut me) = self.try_borrow_mut() { me.source.unregister(poll)?; if me.needs_additional_lifecycle_events { additional_lifecycle_register.unregister(registration_token); } Ok(true) } else { Ok(false) } } fn before_sleep(&self) -> crate::Result> { let mut disp = self.borrow_mut(); let DispatcherInner { ref mut source, .. } = *disp; source.before_sleep() } fn before_handle_events(&self, events: EventIterator<'_>) { let mut disp = self.borrow_mut(); let DispatcherInner { ref mut source, .. } = *disp; source.before_handle_events(events); } } pub(crate) trait EventDispatcher { fn process_events( &self, readiness: Readiness, token: Token, data: &mut Data, ) -> crate::Result; fn register( &self, poll: &mut Poll, additional_lifecycle_register: &mut AdditionalLifecycleEventsSet, token_factory: &mut TokenFactory, ) -> crate::Result<()>; fn reregister( &self, poll: &mut Poll, additional_lifecycle_register: &mut AdditionalLifecycleEventsSet, token_factory: &mut TokenFactory, ) -> crate::Result; fn unregister( &self, poll: &mut Poll, additional_lifecycle_register: &mut AdditionalLifecycleEventsSet, registration_token: RegistrationToken, ) -> crate::Result; fn before_sleep(&self) -> crate::Result>; fn before_handle_events(&self, events: EventIterator<'_>); } #[derive(Default)] /// The list of events pub(crate) struct AdditionalLifecycleEventsSet { /// The list of sources pub(crate) values: Vec, } impl AdditionalLifecycleEventsSet { fn register(&mut self, token: RegistrationToken) { self.values.push(token) } fn unregister(&mut self, token: RegistrationToken) { self.values.retain(|it| it != &token) } } // An internal trait to erase the `F` type parameter of `DispatcherInner` trait ErasedDispatcher<'a, S, Data> { fn as_source_ref(&self) -> Ref; fn as_source_mut(&self) -> RefMut; fn into_source_inner(self: Rc) -> S; fn into_event_dispatcher(self: Rc) -> Rc + 'a>; } impl<'a, S, Data, F> ErasedDispatcher<'a, S, Data> for RefCell> where S: EventSource + 'a, F: FnMut(S::Event, &mut S::Metadata, &mut Data) -> S::Ret + 'a, { fn as_source_ref(&self) -> Ref { Ref::map(self.borrow(), |inner| &inner.source) } fn as_source_mut(&self) -> RefMut { RefMut::map(self.borrow_mut(), |inner| &mut inner.source) } fn into_source_inner(self: Rc) -> 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) -> Rc + 'a> where S: 'a, { self as Rc + '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 + 'a>); impl<'a, S, Data> std::fmt::Debug for Dispatcher<'a, S, Data> { #[cfg_attr(feature = "nightly_coverage", coverage(off))] 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(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, needs_additional_lifecycle_events: S::NEEDS_EXTRA_LIFECYCLE_EVENTS, }))) } /// 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 { 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 { 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 + '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>, } impl<'i> std::fmt::Debug for Idle<'i> { #[cfg_attr(feature = "nightly_coverage", coverage(off))] 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 CancellableIdle for Option { fn cancel(&mut self) { self.take(); } } pub(crate) trait IdleDispatcher { fn dispatch(&mut self, data: &mut Data); } impl IdleDispatcher for Option 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); } } calloop-0.13.0/src/sources/ping/eventfd.rs000064400000000000000000000136201046102023000165500ustar 00000000000000//! 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::{AsFd, BorrowedFd, OwnedFd}; use std::sync::Arc; use rustix::event::{eventfd, EventfdFlags}; use rustix::io::{read, write, Errno}; use super::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, EventfdFlags::CLOEXEC | EventfdFlags::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::new(read); let ping = Ping { event: Arc::new(FlagOnDrop(Arc::clone(&fd))), }; let source = PingSource { event: Generic::new(ArcAsFd(fd), Interest::READ, Mode::Level), }; Ok((ping, source)) } // Helper functions for the event source IO. #[inline] fn send_ping(fd: BorrowedFd<'_>, 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(Errno::AGAIN) => Ok(()), // Anything else is a real error. Err(e) => Err(e.into()), } } #[inline] fn drain_ping(fd: BorrowedFd<'_>) -> std::io::Result { // 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()), } } // Rust 1.64.0 adds an `AsFd` implementation for `Arc`, so this won't be needed #[derive(Debug)] struct ArcAsFd(Arc); impl AsFd for ArcAsFd { fn as_fd(&self) -> BorrowedFd { self.0.as_fd() } } // The event source is simply a generic source with one of the eventfds. #[derive(Debug)] pub struct PingSource { event: Generic, } impl EventSource for PingSource { type Event = (); type Metadata = (); type Ret = (); type Error = PingError; fn process_events( &mut self, readiness: Readiness, token: Token, mut callback: C, ) -> Result where C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { self.event .process_events(readiness, token, |_, fd| { let counter = drain_ping(fd.as_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, } impl Ping { /// Send a ping to the `PingSource`. pub fn ping(&self) { if let Err(e) = send_ping(self.event.0.as_fd(), 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); impl Drop for FlagOnDrop { fn drop(&mut self) { if let Err(e) = send_ping(self.0.as_fd(), INCREMENT_CLOSE) { log::warn!("[calloop] Failed to send close ping: {:?}", e); } } } calloop-0.13.0/src/sources/ping/iocp.rs000064400000000000000000000222711046102023000160510ustar 00000000000000//! IOCP-based implementation of the ping event source. //! //! The underlying `Poller` can be woken up at any time, using the `post` method //! to send an arbitrary packet to the I/O completion port. The complication is //! emulating a pipe. //! //! Since `Poller` is already wrapped in an `Arc`, we can clone it into some //! synchronized inner state to send a pre-determined packet into it. Thankfully //! calloop's use of the pipe is constrained enough that we can implement it using //! a simple bool to keep track of whether or not it is notified. use crate::sources::EventSource; use polling::os::iocp::{CompletionPacket, PollerIocpExt}; use polling::Poller; use std::fmt; use std::io; use std::sync::{Arc, Mutex, TryLockError}; #[inline] pub fn make_ping() -> io::Result<(Ping, PingSource)> { let state = Arc::new(State { counter: Mutex::new(Counter { notified: false, poll_state: None, }), }); Ok(( Ping { state: state.clone(), }, PingSource { state }, )) } /// The event to trigger. #[derive(Clone)] pub struct Ping { state: Arc, } impl fmt::Debug for Ping { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { debug_ping(&self.state, "Ping", f) } } /// The event source. pub struct PingSource { state: Arc, } impl fmt::Debug for PingSource { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { debug_ping(&self.state, "PingSource", f) } } impl Ping { /// Send a ping to the `PingSource`. pub fn ping(&self) { let mut counter = self.state.counter.lock().unwrap_or_else(|e| e.into_inner()); // Indicate that we are now notified. counter.notified = true; let poll_state = match &mut counter.poll_state { Some(ps) => ps, None => { log::warn!("[calloop] ping was not registered with the event loop"); return; } }; // If we aren't currently inserted in the loop, send our packet. if let Err(e) = poll_state.notify() { log::warn!("[calloop] failed to post packet to IOCP: {}", e); } } } impl Drop for Ping { fn drop(&mut self) { // If this is the last ping, wake up the source so it removes itself. if Arc::strong_count(&self.state) <= 2 { let mut counter = self.state.counter.lock().unwrap_or_else(|e| e.into_inner()); if let Some(poll_state) = &mut counter.poll_state { if let Err(e) = poll_state.notify() { log::warn!("[calloop] failed to post packet to IOCP during drop: {}", e); } } } } } impl EventSource for PingSource { type Error = super::PingError; type Event = (); type Metadata = (); type Ret = (); fn process_events( &mut self, _readiness: crate::Readiness, token: crate::Token, mut callback: F, ) -> Result where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { let mut counter = self.state.counter.lock().unwrap_or_else(|e| e.into_inner()); // If we aren't registered, break out. let poll_state = match &mut counter.poll_state { Some(ps) => ps, None => { // We were deregistered; indicate to the higher level loop. return Ok(crate::PostAction::Disable); } }; // We are no longer inserted into the poller. poll_state.inserted = false; // Make sure this is our token. let token: usize = token.inner.into(); if poll_state.packet.event().key != token { log::warn!( "[calloop] token does not match; expected {:x}, got {:x}", poll_state.packet.event().key, token ); return Ok(crate::PostAction::Continue); } // Tell if we are registered. if counter.notified { counter.notified = false; // Call the callback. callback((), &mut ()); } // Stop looping if all of the Ping's have been dropped. let action = if Arc::strong_count(&self.state) <= 1 { crate::PostAction::Remove } else { crate::PostAction::Continue }; Ok(action) } fn register( &mut self, poll: &mut crate::Poll, token_factory: &mut crate::TokenFactory, ) -> crate::Result<()> { let token = token_factory.token(); let mut counter = self.state.counter.lock().unwrap_or_else(|e| e.into_inner()); // Make sure we haven't already been registered. if counter.poll_state.is_some() { return Err(io::Error::from(io::ErrorKind::AlreadyExists).into()); } // Create the event to send. let packet = { let token = token.inner.into(); let event = polling::Event::readable(token); CompletionPacket::new(event) }; // Create the poll state. let poll_state = PollState::new(poll.poller(), packet, counter.notified)?; // Substitute it into our poll state. counter.poll_state = Some(poll_state); Ok(()) } fn reregister( &mut self, poll: &mut crate::Poll, token_factory: &mut crate::TokenFactory, ) -> crate::Result<()> { let token = token_factory.token(); let mut counter = self.state.counter.lock().unwrap_or_else(|e| e.into_inner()); // Make sure that the poller has been registered. let poll_state = match &mut counter.poll_state { Some(ps) => ps, None => return Err(io::Error::from(io::ErrorKind::NotFound).into()), }; // If it's a different poller, throw an error. if !Arc::ptr_eq(&poll_state.poller, poll.poller()) { return Err(io::Error::new( io::ErrorKind::NotFound, "attempted to reregister() a PingSource with a different poller", ) .into()); } // Change the token if needed. let token = token.inner.into(); let event = polling::Event::readable(token); if event.key != poll_state.packet.event().key { poll_state.packet = CompletionPacket::new(event); if poll_state.inserted { poll_state.inserted = false; poll_state.notify()?; } } Ok(()) } fn unregister(&mut self, _poll: &mut crate::Poll) -> crate::Result<()> { let mut counter = self.state.counter.lock().unwrap_or_else(|e| e.into_inner()); // Remove our current registration. if counter.poll_state.take().is_none() { log::trace!("[calloop] unregistered a source that wasn't registered"); } Ok(()) } } /// Inner state of the pipe. struct State { /// The counter used to keep track of our state. counter: Mutex, } /// Inner counter of the pipe. struct Counter { /// Are we notified? notified: bool, /// The `Poller`-related state. /// /// This is `None` if we aren't inserted into the `Poller` yet. poll_state: Option, } /// The `Poller` combined with some associated state. struct PollState { /// The `Poller` that we are registered in. poller: Arc, /// Are we inserted into the poller? inserted: bool, /// The completion packet to send. packet: CompletionPacket, } impl PollState { /// Create a new `PollState` based on the `Poller` and the `packet`. /// /// If `notified` is `true`, a packet is inserted into the poller. fn new(poller: &Arc, packet: CompletionPacket, notified: bool) -> io::Result { let mut poll_state = Self { poller: poller.clone(), packet, inserted: false, }; if notified { poll_state.notify()?; } Ok(poll_state) } /// Notify the poller. fn notify(&mut self) -> io::Result<()> { if !self.inserted { self.poller.post(self.packet.clone())?; self.inserted = true; } Ok(()) } } #[inline] fn debug_ping(state: &State, name: &str, f: &mut fmt::Formatter) -> fmt::Result { let counter = match state.counter.try_lock() { Ok(counter) => counter, Err(TryLockError::WouldBlock) => { return f .debug_tuple("Ping") .field(&format_args!("")) .finish() } Err(TryLockError::Poisoned(_)) => { return f .debug_tuple("Ping") .field(&format_args!("")) .finish() } }; let mut s = f.debug_struct(name); s.field("notified", &counter.notified); // Tell if we are registered. match &counter.poll_state { Some(poll_state) => { s.field("packet", poll_state.packet.event()); s.field("inserted", &poll_state.inserted); } None => { s.field("packet", &format_args!("")); } } s.finish() } calloop-0.13.0/src/sources/ping/pipe.rs000064400000000000000000000077331046102023000160620ustar 00000000000000//! 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::{AsFd, BorrowedFd, OwnedFd}; use std::sync::Arc; use rustix::io::{read, write, Errno}; use super::PingError; use crate::{ generic::Generic, EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory, }; #[cfg(target_os = "macos")] #[inline] fn make_ends() -> std::io::Result<(OwnedFd, OwnedFd)> { use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags}; use rustix::pipe::pipe; let (read, write) = pipe()?; let set_flags = |fd| fcntl_setfl(fd, fcntl_getfl(fd)? | OFlags::CLOEXEC | OFlags::NONBLOCK); set_flags(&read)?; set_flags(&write)?; Ok((read, write)) } #[cfg(not(target_os = "macos"))] #[inline] fn make_ends() -> std::io::Result<(OwnedFd, OwnedFd)> { use rustix::pipe::{pipe_with, PipeFlags}; Ok(pipe_with(PipeFlags::CLOEXEC | PipeFlags::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(write), }; Ok((ping, source)) } // Helper functions for the event source IO. #[inline] fn send_ping(fd: BorrowedFd<'_>) -> 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, } impl EventSource for PingSource { type Event = (); type Metadata = (); type Ret = (); type Error = PingError; fn process_events( &mut self, readiness: Readiness, token: Token, mut callback: C, ) -> Result where C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { self.pipe .process_events(readiness, token, |_, 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(Errno::AGAIN) => 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) } } // The sending end of the ping writes zeroes to the write end of the pipe. #[derive(Clone, Debug)] pub struct Ping { pipe: Arc, } // 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.as_fd()) { log::warn!("[calloop] Failed to write a ping: {:?}", e); } } } calloop-0.13.0/src/sources/ping.rs000064400000000000000000000210161046102023000151130ustar 00000000000000//! 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). // 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(windows)] mod iocp; #[cfg(windows)] use iocp as platform; #[cfg(not(any(target_os = "linux", windows)))] mod pipe; #[cfg(not(any(target_os = "linux", windows)))] 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); #[cfg(test)] mod tests { use crate::transient::TransientSource; use std::time::Duration; use super::*; #[test] fn ping() { let mut event_loop = crate::EventLoop::::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::::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::::try_new().unwrap(); let (sender, source) = make_ping().unwrap(); let wrapper = TransientSource::from(source); // Check that the source starts off in the wrapper. assert!(!wrapper.is_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!(wrapper.is_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::::try_new().unwrap(); let (sender, source) = make_ping().unwrap(); let wrapper = TransientSource::from(source); // Check that the source starts off in the wrapper. assert!(!wrapper.is_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!(wrapper.is_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::::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!(!wrapper.is_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!(wrapper.is_none()); } } calloop-0.13.0/src/sources/signals.rs000064400000000000000000000137221046102023000156230ustar 00000000000000//! 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::io::Error as IoError; 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::{FdWrapper, 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>, mask: SigSet, } impl Signals { /// Create a new signal event source listening on the specified list of signals pub fn new(signals: &[Signal]) -> crate::Result { let mut mask = SigSet::empty(); for &s in signals { mask.add(s); } // Mask the signals for this thread mask.thread_block().map_err(IoError::from)?; // Create the SignalFd let sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK | SfdFlags::SFD_CLOEXEC) .map_err(IoError::from)?; Ok(Signals { sfd: Generic::new(unsafe { FdWrapper::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().map_err(IoError::from)?; // SAFETY: We don't drop the underlying mask. unsafe { self.sfd .get_mut() .set_mask(&self.mask) .map_err(IoError::from)?; } 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().map_err(IoError::from)?; // SAFETY: We don't drop the underlying mask. unsafe { self.sfd .get_mut() .set_mask(&self.mask) .map_err(IoError::from)?; } 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().map_err(IoError::from)?; new_mask.thread_block().map_err(IoError::from)?; // SAFETY: We don't drop the underlying mask. unsafe { self.sfd .get_mut() .set_mask(&new_mask) .map_err(IoError::from)?; } 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( &mut self, readiness: Readiness, token: Token, mut callback: C, ) -> Result where C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { self.sfd .process_events(readiness, token, |_, sfd| { loop { match unsafe { sfd.get_mut().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); calloop-0.13.0/src/sources/timer.rs000064400000000000000000000465621046102023000153130ustar 00000000000000//! 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. //! //! As of calloop v0.11.0, the event loop always uses high-precision timers. However, the timer //! precision varies between operating systems; for instance, the scheduler granularity on Windows //! is about 16 milliseconds. 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>, 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, deadline: Option, } 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_inner(Instant::now().checked_add(duration)) } /// Create a timer that will fire at a given instant pub fn from_deadline(deadline: Instant) -> Timer { Self::from_deadline_inner(Some(deadline)) } fn from_deadline_inner(deadline: Option) -> 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 = Some(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.deadline = Instant::now().checked_add(duration); } /// Get the current deadline of this `Timer` /// /// Returns `None` if the timer has overflowed. pub fn current_deadline(&self) -> Option { self.deadline } } impl EventSource for Timer { type Event = Instant; type Metadata = (); type Ret = TimeoutAction; type Error = std::io::Error; fn process_events( &mut self, _: Readiness, token: Token, mut callback: F, ) -> Result where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { if let (Some(ref registration), Some(ref deadline)) = (&self.registration, &self.deadline) { if registration.token != token { return Ok(PostAction::Continue); } let new_deadline = match callback(*deadline, &mut ()) { TimeoutAction::Drop => return Ok(PostAction::Remove), TimeoutAction::ToInstant(instant) => instant, TimeoutAction::ToDuration(duration) => match Instant::now().checked_add(duration) { Some(new_deadline) => new_deadline, None => { // The timer has overflowed, meaning we have no choice but to drop it. self.deadline = None; return Ok(PostAction::Remove); } }, }; // 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 = Some(new_deadline); } Ok(PostAction::Continue) } fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> { // Only register a deadline if we haven't overflowed. if let Some(deadline) = self.deadline { let wheel = poll.timers.clone(); let token = token_factory.token(); let counter = wheel.borrow_mut().insert(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>, counter: u32, } // A data structure for tracking registered timeouts #[derive(Debug)] pub(crate) struct TimerWheel { heap: BinaryHeap, 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 { 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 { 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(feature = "nightly_coverage", coverage(off))] 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: Option, waker: Rc>>, } impl std::fmt::Debug for TimeoutFuture { #[cfg_attr(feature = "nightly_coverage", coverage(off))] 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(handle: &LoopHandle<'_, Data>, duration: Duration) -> TimeoutFuture { Self::from_deadline_inner(handle, Instant::now().checked_add(duration)) } /// Create a future that resolves at a given instant pub fn from_deadline(handle: &LoopHandle<'_, Data>, deadline: Instant) -> TimeoutFuture { Self::from_deadline_inner(handle, Some(deadline)) } /// Create a future that resolves at a given instant fn from_deadline_inner( handle: &LoopHandle<'_, Data>, deadline: Option, ) -> TimeoutFuture { let timer = Timer::from_deadline_inner(deadline); let waker = Rc::new(RefCell::new(None::)); handle .insert_source(timer, { let waker = waker.clone(); move |_, &mut (), _| { if let Some(waker) = waker.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 { match self.deadline { None => return std::task::Poll::Pending, Some(deadline) => { if Instant::now() >= 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().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)); // This one should never go off. let timeout_3 = TimeoutFuture::from_duration(&event_loop.handle(), Duration::MAX); 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(); sched.schedule(timeout_3).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); } #[test] fn no_overflow() { 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::Drop }, ) .unwrap(); event_loop .handle() .insert_source(Timer::from_duration(Duration::MAX), |_, &mut (), _| { panic!("This timer should never go off") }) .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, 1); } } calloop-0.13.0/src/sources/transient.rs000064400000000000000000001164351046102023000161770ustar 00000000000000//! 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>, /// /// // 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 = source.into(); /// ``` /// /// (If you want to start off with an empty `TransientSource`, you can just use /// `Default::default()` instead.) /// /// `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( /// &mut self, /// readiness: calloop::Readiness, /// token: calloop::Token, /// callback: F, /// ) -> Result /// 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` 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. /// /// ## Replacing or removing `TransientSource`s /// /// Not properly removing or replacing `TransientSource`s can cause spurious /// wakeups of the event loop, and in some cases can leak file descriptors or /// fail to free entries in Calloop's internal data structures. No unsoundness /// or undefined behaviour will result, but leaking file descriptors can result /// in errors or panics. /// /// If you want to remove a source before it returns `PostAction::Remove`, use /// the [`TransientSource::remove()`] method. If you want to replace a source /// with another one, use the [`TransientSource::replace()`] method. Either of /// these may be called at any time during processing or from outside the event /// loop. Both require either returning `PostAction::Reregister` from the /// `process_event()` call that does this, or reregistering the event source /// some other way eg. via the top-level loop handle. /// /// If, instead, you directly assign a new source to the variable holding the /// `TransientSource`, the inner source will be dropped before it can be /// unregistered. For example: /// /// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193 /// self.mpsc_receiver = Default::default(); /// self.mpsc_receiver = new_channel.into(); /// ``` #[derive(Debug, Default)] pub struct TransientSource { state: TransientSourceState, } /// This is the internal state of the [`TransientSource`], as a separate type so /// it's not exposed. #[derive(Debug)] enum TransientSourceState { /// 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 is being replaced by another. For most API purposes (eg. /// `map()`), this will be treated as the `Register` state enclosing the new /// source. Replace { /// The new source, which will be registered and used from now on. new: T, /// The old source, which will be unregistered and dropped. old: 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 Default for TransientSourceState { fn default() -> Self { Self::None } } impl TransientSourceState { /// 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. If the variant is `Replace` then `replacer` will be /// called **on the new source**, which may cause the old source to leak /// registration in the event loop if it has not yet been unregistered. /// /// The `replacer` function here is expected to be one of the enum variant /// constructors eg. `replace(TransientSource::Remove)`. fn replace_state(&mut self, replacer: F) where F: FnOnce(T) -> Self, { *self = match std::mem::take(self) { Self::Keep(source) | Self::Register(source) | Self::Remove(source) | Self::Disable(source) | Self::Replace { new: source, .. } => replacer(source), Self::None => return, }; } } impl TransientSource { /// Apply a function to the enclosed source, if it exists and is not about /// to be removed. pub fn map(&mut self, f: F) -> Option where F: FnOnce(&mut T) -> U, { match &mut self.state { TransientSourceState::Keep(source) | TransientSourceState::Register(source) | TransientSourceState::Disable(source) | TransientSourceState::Replace { new: source, .. } => Some(f(source)), TransientSourceState::Remove(_) | TransientSourceState::None => None, } } /// Returns `true` if there is no wrapped event source. pub fn is_none(&self) -> bool { matches!(self.state, TransientSourceState::None) } /// Removes the wrapped event source from the event loop and this wrapper. /// /// If this is called from outside of the event loop, you will need to wake /// up the event loop for any changes to take place. If it is called from /// within the event loop, you must return `PostAction::Reregister` from /// your own event source's `process_events()`, and the source will be /// unregistered as needed after it exits. pub fn remove(&mut self) { self.state.replace_state(TransientSourceState::Remove); } /// Replace the currently wrapped source with the given one. No more events /// will be generated from the old source after this point. The old source /// will not be dropped immediately, it will be kept so that it can be /// deregistered. /// /// If this is called from outside of the event loop, you will need to wake /// up the event loop for any changes to take place. If it is called from /// within the event loop, you must return `PostAction::Reregister` from /// your own event source's `process_events()`, and the sources will be /// registered and unregistered as needed after it exits. pub fn replace(&mut self, new: T) { self.state .replace_state(|old| TransientSourceState::Replace { new, old }); } } impl From for TransientSource { fn from(source: T) -> Self { Self { state: TransientSourceState::Register(source), } } } impl crate::EventSource for TransientSource { type Event = T::Event; type Metadata = T::Metadata; type Ret = T::Ret; type Error = T::Error; fn process_events( &mut self, readiness: crate::Readiness, token: crate::Token, callback: F, ) -> Result where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { let reregister = if let TransientSourceState::Keep(source) = &mut self.state { 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.state.replace_state(TransientSourceState::Disable); true } crate::PostAction::Remove => { self.state.replace_state(TransientSourceState::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 &mut self.state { TransientSourceState::Keep(source) => { source.register(poll, token_factory)?; } TransientSourceState::Register(source) | TransientSourceState::Disable(source) | TransientSourceState::Replace { new: source, .. } => { source.register(poll, token_factory)?; self.state.replace_state(TransientSourceState::Keep); // Drops the disposed source in the Replace case. } TransientSourceState::Remove(_source) => { self.state.replace_state(|_| TransientSourceState::None); } TransientSourceState::None => (), } Ok(()) } fn reregister( &mut self, poll: &mut crate::Poll, token_factory: &mut crate::TokenFactory, ) -> crate::Result<()> { match &mut self.state { TransientSourceState::Keep(source) => source.reregister(poll, token_factory)?, TransientSourceState::Register(source) => { source.register(poll, token_factory)?; self.state.replace_state(TransientSourceState::Keep); } TransientSourceState::Disable(source) => { source.unregister(poll)?; } TransientSourceState::Remove(source) => { source.unregister(poll)?; self.state.replace_state(|_| TransientSourceState::None); } TransientSourceState::Replace { new, old } => { old.unregister(poll)?; new.register(poll, token_factory)?; self.state.replace_state(TransientSourceState::Keep); // Drops 'dispose'. } TransientSourceState::None => (), } Ok(()) } fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> { match &mut self.state { TransientSourceState::Keep(source) | TransientSourceState::Register(source) | TransientSourceState::Disable(source) => source.unregister(poll)?, TransientSourceState::Remove(source) => { source.unregister(poll)?; self.state.replace_state(|_| TransientSourceState::None); } TransientSourceState::Replace { new, old } => { old.unregister(poll)?; new.unregister(poll)?; self.state.replace_state(TransientSourceState::Register); } TransientSourceState::None => (), } Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::{ channel::{channel, Channel, Event}, ping::{make_ping, PingSource}, Dispatcher, EventSource, PostAction, }; use std::{ rc::Rc, 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; fn process_events( &mut self, readiness: crate::Readiness, token: crate::Token, callback: F, ) -> Result 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; fn process_events( &mut self, readiness: crate::Readiness, token: crate::Token, mut callback: F, ) -> Result 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); impl EventSource for WrapperSource { type Event = ::Event; type Metadata = ::Metadata; type Ret = ::Ret; type Error = ::Error; fn process_events( &mut self, readiness: crate::Readiness, token: crate::Token, callback: F, ) -> Result 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; fn process_events( &mut self, readiness: crate::Readiness, token: crate::Token, callback: F, ) -> Result 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); } #[test] fn test_transient_replace_unregister() { // This is a bit of a complex test, but it essentially boils down to: // how can a "parent" event source containing a TransientSource replace // the "child" source without leaking the source's registration? // First, a source that finishes immediately. This is so we cover the // edge case of replacing a source as soon as it wants to be removed. struct FinishImmediatelySource { source: PingSource, data: Option, registered: bool, dropped: Rc, } impl FinishImmediatelySource { // The constructor passes out the drop flag so we can check that // this source was or wasn't dropped. fn new(source: PingSource, data: i32) -> (Self, Rc) { let dropped = Rc::new(false.into()); ( Self { source, data: Some(data), registered: false, dropped: Rc::clone(&dropped), }, dropped, ) } } impl EventSource for FinishImmediatelySource { type Event = i32; type Metadata = (); type Ret = (); type Error = Box; fn process_events( &mut self, readiness: crate::Readiness, token: crate::Token, mut callback: F, ) -> Result where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { let mut data = self.data.take(); self.source.process_events(readiness, token, |_, _| { if let Some(data) = data.take() { callback(data, &mut ()) } })?; self.data = data; Ok(if self.data.is_none() { PostAction::Remove } else { PostAction::Continue }) } fn register( &mut self, poll: &mut crate::Poll, token_factory: &mut crate::TokenFactory, ) -> crate::Result<()> { self.registered = true; self.source.register(poll, token_factory) } fn reregister( &mut self, poll: &mut crate::Poll, token_factory: &mut crate::TokenFactory, ) -> crate::Result<()> { self.source.reregister(poll, token_factory) } fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> { self.registered = false; self.source.unregister(poll) } } // The drop handler sets a flag we can check for debugging (we want to // know that the source itself was dropped), and also checks that the // source was unregistered. Ultimately neither the source nor its // registration should be leaked. impl Drop for FinishImmediatelySource { fn drop(&mut self) { assert!(!self.registered, "source dropped while still registered"); self.dropped.store(true, Ordering::Relaxed); } } // Our wrapper source handles detecting when the child source finishes, // and replacing that child source with another one that will generate // more events. This is one intended use case of the TransientSource. struct WrapperSource { current: TransientSource, replacement: Option, dropped: Rc, } impl WrapperSource { // The constructor passes out the drop flag so we can check that // this source was or wasn't dropped. fn new( first: FinishImmediatelySource, second: FinishImmediatelySource, ) -> (Self, Rc) { let dropped = Rc::new(false.into()); ( Self { current: first.into(), replacement: second.into(), dropped: Rc::clone(&dropped), }, dropped, ) } } impl EventSource for WrapperSource { type Event = i32; type Metadata = (); type Ret = (); type Error = Box; fn process_events( &mut self, readiness: crate::Readiness, token: crate::Token, mut callback: F, ) -> Result where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { // Did our event source generate an event? let mut fired = false; let post_action = self.current.process_events(readiness, token, |data, _| { callback(data, &mut ()); fired = true; })?; if fired { // The event source will be unregistered after the current // process_events() iteration is finished. The replace() // method will handle doing that even while we've added a // new source. if let Some(replacement) = self.replacement.take() { self.current.replace(replacement); } // Parent source is responsible for flagging this, but it's // already set. assert_eq!(post_action, PostAction::Reregister); } Ok(post_action) } fn register( &mut self, poll: &mut crate::Poll, token_factory: &mut crate::TokenFactory, ) -> crate::Result<()> { self.current.register(poll, token_factory) } fn reregister( &mut self, poll: &mut crate::Poll, token_factory: &mut crate::TokenFactory, ) -> crate::Result<()> { self.current.reregister(poll, token_factory) } fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> { self.current.unregister(poll) } } impl Drop for WrapperSource { fn drop(&mut self) { self.dropped.store(true, Ordering::Relaxed); } } // Construct the various nested sources - FinishImmediatelySource inside // TransientSource inside WrapperSource. The numbers let us verify which // event source fires first. let (ping0_tx, ping0_rx) = crate::ping::make_ping().unwrap(); let (ping1_tx, ping1_rx) = crate::ping::make_ping().unwrap(); let (inner0, inner0_dropped) = FinishImmediatelySource::new(ping0_rx, 0); let (inner1, inner1_dropped) = FinishImmediatelySource::new(ping1_rx, 1); let (outer, outer_dropped) = WrapperSource::new(inner0, inner1); // Now the actual test starts. let mut event_loop: crate::EventLoop<(Option, crate::LoopSignal)> = crate::EventLoop::try_new().unwrap(); let handle = event_loop.handle(); let signal = event_loop.get_signal(); // This is how we communicate with the event sources. let mut context = (None, signal); let _token = handle .insert_source(outer, |data, _, (evt, sig)| { *evt = Some(data); sig.stop(); }) .unwrap(); // Ensure our sources fire. ping0_tx.ping(); ping1_tx.ping(); // Use run() rather than dispatch() because it's not strictly part of // any API contract as to how many runs of the event loop it takes to // replace the nested source. event_loop.run(None, &mut context, |_| {}).unwrap(); // First, make sure the inner source actually did fire. assert_eq!(context.0.take(), Some(0), "first inner source did not fire"); // Make sure that the outer source is still alive. assert!( !outer_dropped.load(Ordering::Relaxed), "outer source already dropped" ); // Make sure that the inner child source IS dropped now. assert!( inner0_dropped.load(Ordering::Relaxed), "first inner source not dropped" ); // Make sure that, in between the first event and second event, the // replacement child source still exists. assert!( !inner1_dropped.load(Ordering::Relaxed), "replacement inner source dropped" ); // Run the event loop until we get a second event. event_loop.run(None, &mut context, |_| {}).unwrap(); // Ensure the replacement source fired (which checks that it was // registered and is being processed by the TransientSource). assert_eq!(context.0.take(), Some(1), "replacement source did not fire"); } #[test] fn test_transient_remove() { // This tests that calling remove(), even before an event source has // requested its own removal, results in the event source being removed. const STOP_AT: i32 = 2; // A wrapper source to automate the removal of the inner source. struct WrapperSource { inner: TransientSource>, } impl EventSource for WrapperSource { type Event = i32; type Metadata = (); type Ret = (); type Error = Box; fn process_events( &mut self, readiness: crate::Readiness, token: crate::Token, mut callback: F, ) -> Result where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { let mut remove = false; let mut post_action = self.inner.process_events(readiness, token, |evt, _| { if let Event::Msg(num) = evt { callback(num, &mut ()); remove = num >= STOP_AT; } })?; if remove { self.inner.remove(); post_action |= PostAction::Reregister; } Ok(post_action) } fn register( &mut self, poll: &mut crate::Poll, token_factory: &mut crate::TokenFactory, ) -> crate::Result<()> { self.inner.register(poll, token_factory) } fn reregister( &mut self, poll: &mut crate::Poll, token_factory: &mut crate::TokenFactory, ) -> crate::Result<()> { self.inner.reregister(poll, token_factory) } fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> { self.inner.unregister(poll) } } // Create our sources and loop. let (sender, receiver) = channel(); let wrapper = WrapperSource { inner: receiver.into(), }; let mut event_loop = crate::EventLoop::try_new().unwrap(); let handle = event_loop.handle(); handle .insert_source(wrapper, |num, _, out: &mut Option<_>| { *out = Some(num); }) .unwrap(); // Storage for callback data. let mut out = None; // Send some data we expect to get callbacks for. for num in 0..=STOP_AT { sender.send(num).unwrap(); event_loop.dispatch(Duration::ZERO, &mut out).unwrap(); assert_eq!(out.take(), Some(num)); } // Now we expect the receiver to be gone. assert!(matches!( sender.send(STOP_AT + 1), Err(std::sync::mpsc::SendError { .. }) )); } } calloop-0.13.0/src/sys.rs000064400000000000000000000332401046102023000133130ustar 00000000000000use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc, time::Duration}; #[cfg(unix)] use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd as Borrowed, RawFd as Raw}; #[cfg(windows)] use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket as Borrowed, RawSocket as Raw}; use polling::{Event, Events, PollMode, Poller}; use crate::sources::timer::TimerWheel; use crate::token::TokenInner; use crate::RegistrationToken; /// 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. 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. /// /// This mode is not supported on certain platforms, and an error will be returned /// if it is used. /// /// ## Supported Platforms /// /// As of the time of writing, the platforms that support edge triggered polling are /// as follows: /// /// - Linux/Android /// - macOS/iOS/tvOS/watchOS /// - FreeBSD/OpenBSD/NetBSD/DragonflyBSD 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 { next_token: TokenInner, } impl TokenFactory { pub(crate) fn new(token: TokenInner) -> TokenFactory { TokenFactory { next_token: token.forget_sub_id(), } } /// Get the "raw" registration token of this TokenFactory pub(crate) fn registration_token(&self) -> RegistrationToken { RegistrationToken::new(self.next_token.forget_sub_id()) } /// Produce a new unique token pub fn token(&mut self) -> Token { let token = self.next_token; self.next_token = token.increment_sub_id(); Token { inner: 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, Eq)] pub struct Token { pub(crate) inner: TokenInner, } /// 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 { /// The handle to wepoll/epoll/kqueue/... used to poll for events. pub(crate) poller: Arc, /// The buffer of events returned by the poller. events: RefCell, /// The sources registered as level triggered. /// /// Some platforms that `polling` supports do not support level-triggered events. As of the time /// of writing, this only includes Solaris and illumos. To work around this, we emulate level /// triggered events by keeping this map of file descriptors. /// /// One can emulate level triggered events on top of oneshot events by just re-registering the /// file descriptor every time it is polled. However, this is not ideal, as it requires a /// system call every time. It's better to use the intergrated system, if available. level_triggered: Option>>, pub(crate) timers: Rc>, } impl std::fmt::Debug for Poll { #[cfg_attr(feature = "nightly_coverage", coverage(off))] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("Poll { ... }") } } impl Poll { pub(crate) fn new() -> crate::Result { Self::new_inner(false) } fn new_inner(force_fallback_lt: bool) -> crate::Result { let poller = Poller::new()?; let level_triggered = if poller.supports_level() && !force_fallback_lt { None } else { Some(RefCell::new(HashMap::new())) }; Ok(Poll { poller: Arc::new(poller), events: RefCell::new(Events::new()), timers: Rc::new(RefCell::new(TimerWheel::new())), level_triggered, }) } pub(crate) fn poll(&self, mut timeout: Option) -> crate::Result> { 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.events.borrow_mut(); events.clear(); self.poller.wait(&mut events, timeout)?; // Convert `polling` events to `calloop` events. let level_triggered = self.level_triggered.as_ref().map(RefCell::borrow); let mut poll_events = events .iter() .map(|ev| { // If we need to emulate level-triggered events... if let Some(level_triggered) = level_triggered.as_ref() { // ...and this event is from a level-triggered source... if let Some((source, interest)) = level_triggered.get(&ev.key) { // ...then we need to re-register the source. // SAFETY: The source is valid. self.poller .modify(unsafe { Borrowed::borrow_raw(*source) }, *interest)?; } } Ok(PollEvent { readiness: Readiness { readable: ev.readable, writable: ev.writable, error: false, }, token: Token { inner: TokenInner::from(ev.key), }, }) }) .collect::>>()?; drop(events); // 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) { poll_events.push(PollEvent { readiness: Readiness { readable: true, writable: false, error: false, }, token, }); } Ok(poll_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. /// /// # Safety /// /// The registered source must not be dropped before it is unregistered. /// /// # 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 unsafe fn register( &self, #[cfg(unix)] fd: impl AsFd, #[cfg(windows)] fd: impl AsSocket, interest: Interest, mode: Mode, token: Token, ) -> crate::Result<()> { let raw = { #[cfg(unix)] { fd.as_fd().as_raw_fd() } #[cfg(windows)] { fd.as_socket().as_raw_socket() } }; let ev = cvt_interest(interest, token); // SAFETY: See invariant on function. unsafe { self.poller .add_with_mode(raw, ev, cvt_mode(mode, self.poller.supports_level()))?; } // If this is level triggered and we're emulating level triggered mode... if let (Mode::Level, Some(level_triggered)) = (mode, self.level_triggered.as_ref()) { // ...then we need to keep track of the source. let mut level_triggered = level_triggered.borrow_mut(); level_triggered.insert(ev.key, (raw, ev)); } Ok(()) } /// 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( &self, #[cfg(unix)] fd: impl AsFd, #[cfg(windows)] fd: impl AsSocket, interest: Interest, mode: Mode, token: Token, ) -> crate::Result<()> { let (borrowed, raw) = { #[cfg(unix)] { (fd.as_fd(), fd.as_fd().as_raw_fd()) } #[cfg(windows)] { (fd.as_socket(), fd.as_socket().as_raw_socket()) } }; let ev = cvt_interest(interest, token); self.poller .modify_with_mode(borrowed, ev, cvt_mode(mode, self.poller.supports_level()))?; // If this is level triggered and we're emulating level triggered mode... if let (Mode::Level, Some(level_triggered)) = (mode, self.level_triggered.as_ref()) { // ...then we need to keep track of the source. let mut level_triggered = level_triggered.borrow_mut(); level_triggered.insert(ev.key, (raw, ev)); } Ok(()) } /// 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( &self, #[cfg(unix)] fd: impl AsFd, #[cfg(windows)] fd: impl AsSocket, ) -> crate::Result<()> { let (borrowed, raw) = { #[cfg(unix)] { (fd.as_fd(), fd.as_fd().as_raw_fd()) } #[cfg(windows)] { (fd.as_socket(), fd.as_socket().as_raw_socket()) } }; self.poller.delete(borrowed)?; if let Some(level_triggered) = self.level_triggered.as_ref() { let mut level_triggered = level_triggered.borrow_mut(); level_triggered.retain(|_, (source, _)| *source != raw); } Ok(()) } /// Get a thread-safe handle which can be used to wake up the `Poll`. pub(crate) fn notifier(&self) -> Notifier { Notifier(self.poller.clone()) } /// Get a reference to the poller. pub(crate) fn poller(&self) -> &Arc { &self.poller } } /// Thread-safe handle which can be used to wake up the `Poll`. #[derive(Clone)] pub(crate) struct Notifier(Arc); impl Notifier { pub(crate) fn notify(&self) -> crate::Result<()> { self.0.notify()?; Ok(()) } } fn cvt_interest(interest: Interest, tok: Token) -> Event { let mut event = Event::none(tok.inner.into()); event.readable = interest.readable; event.writable = interest.writable; event } fn cvt_mode(mode: Mode, supports_other_modes: bool) -> PollMode { if !supports_other_modes { return PollMode::Oneshot; } match mode { Mode::Edge => PollMode::Edge, Mode::Level => PollMode::Level, Mode::OneShot => PollMode::Oneshot, } } calloop-0.13.0/src/token.rs000064400000000000000000000054011046102023000136130ustar 00000000000000// Several implementations of the internals of `Token` depending on the size of `usize` use std::convert::TryInto; #[cfg(target_pointer_width = "64")] const BITS_VERSION: usize = 16; #[cfg(target_pointer_width = "64")] const BITS_SUBID: usize = 16; #[cfg(target_pointer_width = "32")] const BITS_VERSION: usize = 8; #[cfg(target_pointer_width = "32")] const BITS_SUBID: usize = 8; #[cfg(target_pointer_width = "16")] const BITS_VERSION: usize = 4; #[cfg(target_pointer_width = "16")] const BITS_SUBID: usize = 4; const MASK_VERSION: usize = (1 << BITS_VERSION) - 1; const MASK_SUBID: usize = (1 << BITS_SUBID) - 1; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub(crate) struct TokenInner { id: u32, version: u16, sub_id: u16, } impl TokenInner { pub(crate) fn new(id: usize) -> Result { Ok(TokenInner { id: id.try_into().map_err(|_| ())?, version: 0, sub_id: 0, }) } pub(crate) fn get_id(self) -> usize { self.id as usize } pub(crate) fn same_source_as(self, other: TokenInner) -> bool { self.id == other.id && self.version == other.version } pub(crate) fn increment_version(self) -> TokenInner { TokenInner { id: self.id, version: self.version.wrapping_add(1) & (MASK_VERSION as u16), sub_id: 0, } } pub(crate) fn increment_sub_id(self) -> TokenInner { let sub_id = match self.sub_id.checked_add(1) { Some(sid) if sid <= (MASK_SUBID as u16) => sid, _ => panic!("Maximum number of sub-ids reached for source #{}", self.id), }; TokenInner { id: self.id, version: self.version, sub_id, } } pub(crate) fn forget_sub_id(self) -> TokenInner { TokenInner { id: self.id, version: self.version, sub_id: 0, } } } impl From for TokenInner { fn from(value: usize) -> Self { let sub_id = (value & MASK_SUBID) as u16; let version = ((value >> BITS_SUBID) & MASK_VERSION) as u16; let id = (value >> (BITS_SUBID + BITS_VERSION)) as u32; TokenInner { id, version, sub_id, } } } impl From for usize { fn from(token: TokenInner) -> Self { ((token.id as usize) << (BITS_SUBID + BITS_VERSION)) + ((token.version as usize) << BITS_SUBID) + (token.sub_id as usize) } } #[cfg(test)] mod tests { use super::*; #[should_panic] #[test] fn overflow_subid() { let token = TokenInner { id: 0, version: 0, sub_id: MASK_SUBID as u16, }; token.increment_sub_id(); } } calloop-0.13.0/tests/signals.rs000064400000000000000000000077561046102023000145250ustar 00000000000000// 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(all(target_os = "linux", feature = "signals"))] fn main() { for test in self::test::TESTS { test(); // reset the signal mask between tests self::test::reset_mask(); } } #[cfg(not(all(target_os = "linux", feature = "signals")))] fn main() {} #[cfg(all(target_os = "linux", feature = "signals"))] 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)); } }