calloop-0.10.5/.cargo_vcs_info.json0000644000000001360000000000100125500ustar { "git": { "sha1": "8e433d22df734f89dfec1a0aee98b567af04b3b8" }, "path_in_vcs": "" }calloop-0.10.5/.cirrus.yml000064400000000000000000000022301046102023000134450ustar 00000000000000task: only_if: $CIRRUS_BRANCH == 'master' || $CIRRUS_PR != '' matrix: #- name: FreeBSD 11.3 # freebsd_instance: # image_family: freebsd-11-3-snap - name: FreeBSD 12.1 freebsd_instance: image_family: freebsd-12-1-snap - name: FreeBSD 13.0 freebsd_instance: image_family: freebsd-13-0-snap # 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 - cargo test --all-features # coverage is disabled for now # see https://github.com/rust-lang/rust/issues/94616 # - env LLVM_PROFILE_FILE="calloop-%p-%m.profraw" RUSTFLAGS="-Zinstrument-coverage" cargo test --all-features # - grcov . --binary-path ./target/debug -s . -t lcov --branch --llvm --ignore-not-existing --keep-only "src/sys/*" --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' calloop-0.10.5/.github/workflows/ci.yml000064400000000000000000000164551046102023000160660ustar 00000000000000name: Continuous Integration on: push: branches: - master pull_request: jobs: lint: runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v2 - 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@v2 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@v2 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@v2 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-features --all-targets -- -D warnings ci-linux: name: CI needs: - lint strategy: fail-fast: false matrix: rust: ['1.53.0', 'stable', 'beta'] runs-on: 'ubuntu-latest' steps: - name: Checkout sources uses: actions/checkout@v2 - 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@v2 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@v2 with: path: target key: ${{ runner.os }}-test-${{ steps.rustcversion.outputs.version }}-${{ hashFiles('**/Cargo.toml') }} - name: Run tests uses: actions-rs/cargo@v1 with: command: test args: --workspace --all-features 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@v2 - 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@v2 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@v2 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@v2 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@v2 - 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@v2 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 --all-features - 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 calloop-0.10.5/.gitignore000064400000000000000000000000521046102023000133250ustar 00000000000000/target /doc/target **/*.rs.bk Cargo.lock calloop-0.10.5/CHANGELOG.md000064400000000000000000000215661046102023000131630ustar 00000000000000# Change Log ## Unreleased ## 0.10.4 -- 2022-12-05 #### Bugfixes - Fixed a crash due to double borrow when handling pre/post run hooks ## 0.10.2 -- 2022-11-08 #### Bugfixes - The return value of `LoopHandle::insert_idle` no longer borrows the `LoopHandle`. ## 0.10.1 -- 2022-06-20 #### Additions - The `Channel` now has proxy methods for `Receiver::recv` and `Receiver::try_recv` - Enable support for `target_os = "android"` ## 0.10.0 -- 2022-05-06 - **Breaking:** Calloop's internal storage is now backed by a `slotmap`. As a result the `RegistrationToken` is now `Copy+Clone`, and the low-level registration API of `Poll` is altered in a breaking way. MSRV is bumped to 1.49. - **Breaking:** `generic::Fd` adapter is removed, as since that rust version `RawFd` implements `AsRawFd`, allowing it to be used directly in `Generic`. - **Breaking:** The `EventSource` trait has a new associated type `Error`. This determines the type of the error variant returned by `EventSource::process_events()`. It must be convertible into `Box`. - **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.10.5/Cargo.lock0000644000000152420000000000100105270ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "calloop" version = "0.10.5" dependencies = [ "futures", "futures-io", "futures-util", "log", "nix", "slotmap", "thiserror", "vec_map", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "futures" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" [[package]] name = "futures-executor" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] name = "futures-macro" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-sink" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" [[package]] name = "futures-task" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" [[package]] name = "futures-util" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "libc" version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] [[package]] name = "nix" version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" dependencies = [ "autocfg", "bitflags", "cfg-if", "libc", "memoffset", ] [[package]] name = "pin-project-lite" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] [[package]] name = "slab" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" dependencies = [ "autocfg", ] [[package]] name = "slotmap" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" dependencies = [ "version_check", ] [[package]] name = "syn" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6e37992ba0f05f3aa079be7f453c91b55d15a01a7aad202baa9c6fa48c3e6b5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f3308bffe57639095bdb057e73850afecf2730c3b5539af96e6ae096644f4db" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" calloop-0.10.5/Cargo.toml0000644000000031010000000000100105410ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "calloop" version = "0.10.5" 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] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [[test]] name = "signals" harness = false [dependencies.futures-io] version = "0.3.5" optional = true [dependencies.futures-util] version = "0.3.5" features = ["std"] optional = true default-features = false [dependencies.log] version = "0.4" [dependencies.nix] version = "0.25" features = [ "event", "fs", "signal", "socket", "time", ] default-features = false [dependencies.slotmap] version = "1.0" [dependencies.thiserror] version = "1.0.7" [dependencies.vec_map] version = "0.8.2" [dev-dependencies.futures] version = "0.3.5" [features] executor = ["futures-util"] [badges.codecov] repository = "Smithay/calloop" calloop-0.10.5/Cargo.toml.orig000064400000000000000000000017551046102023000142370ustar 00000000000000[package] name = "calloop" version = "0.10.5" 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" [workspace] members = [ "doc" ] [badges] codecov = { repository = "Smithay/calloop" } [dependencies] log = "0.4" nix = { version = "0.25", default-features = false, features = ["event", "fs", "signal", "socket", "time"] } futures-util = { version = "0.3.5", optional = true, default-features = false, features = ["std"]} futures-io = { version = "0.3.5", optional = true } slotmap = "1.0" thiserror = "1.0.7" vec_map = "0.8.2" [dev-dependencies] futures = "0.3.5" [features] executor = ["futures-util"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] [[test]] name = "signals" harness = false calloop-0.10.5/LICENSE.txt000064400000000000000000000020411046102023000131600ustar 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.10.5/README.md000064400000000000000000000146641046102023000126320ustar 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. License: MIT calloop-0.10.5/README.tpl000064400000000000000000000003401046102023000130130ustar 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.10.5/clippy.toml000064400000000000000000000000161046102023000135320ustar 00000000000000msrv = "1.41" calloop-0.10.5/examples/high_precision.rs000064400000000000000000000012141046102023000165140ustar 00000000000000use std::time::{Duration, Instant}; use calloop::{ timer::{TimeoutAction, Timer}, EventLoop, }; fn main() { let mut event_loop = EventLoop::try_new_high_precision().expect("Failed to initialize the event loop!"); let before = Instant::now(); event_loop .handle() .insert_source( Timer::from_duration(Duration::from_micros(20)), |_, _, _| TimeoutAction::Drop, ) .unwrap(); event_loop.dispatch(None, &mut ()).unwrap(); let elapsed = before.elapsed(); println!( "The event loop slept for {} microseconds.", elapsed.as_micros() ); } calloop-0.10.5/examples/timer.rs000064400000000000000000000067401046102023000146530ustar 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.10.5/src/error.rs000064400000000000000000000062751046102023000136400ustar 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 Error { /// Converts a [`nix::Error`] into a wrapped version of the equivalent /// [`std::io::Error`]. fn from(err: nix::errno::Errno) -> Self { Into::::into(err).into() } } 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(coverage, no_coverage)] 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(coverage, no_coverage)] fn from(e: InsertError) -> crate::Error { e.error } } calloop-0.10.5/src/io.rs000064400000000000000000000416561046102023000131200ustar 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::os::unix::io::{AsRawFd, RawFd}; use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll as TaskPoll, Waker}; use nix::fcntl::{fcntl, FcntlArg, OFlag}; #[cfg(feature = "futures-io")] use futures_io::{AsyncRead, AsyncWrite, IoSlice, IoSliceMut}; use crate::{ loop_logic::LoopInner, sources::EventDispatcher, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory, }; /// Adapter for async IO manipulations /// /// This type wraps an IO object, providing methods to create futures waiting for its /// readiness. /// /// If the `futures-io` cargo feature is enabled, it also implements `AsyncRead` and/or /// `AsyncWrite` if the underlying type implements `Read` and/or `Write`. /// /// Note that this adapter and the futures procuded from it and *not* threadsafe. pub struct Async<'l, F: AsRawFd> { fd: Option, dispatcher: Rc>, inner: Rc, old_flags: OFlag, } impl<'l, F: AsRawFd + std::fmt::Debug> std::fmt::Debug for Async<'l, F> { #[cfg_attr(coverage, no_coverage)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Async").field("fd", &self.fd).finish() } } impl<'l, F: AsRawFd> Async<'l, F> { pub(crate) fn new(inner: Rc>, fd: F) -> crate::Result> { let rawfd = fd.as_raw_fd(); // set non-blocking let old_flags = fcntl(rawfd, FcntlArg::F_GETFL)?; let old_flags = unsafe { OFlag::from_bits_unchecked(old_flags) }; fcntl(rawfd, FcntlArg::F_SETFL(old_flags | OFlag::O_NONBLOCK))?; // register in the loop let dispatcher = Rc::new(RefCell::new(IoDispatcher { fd: rawfd, token: None, waker: None, is_registered: false, interest: Interest::EMPTY, last_readiness: Readiness::EMPTY, })); let key = inner.sources.borrow_mut().insert(dispatcher.clone()); dispatcher.borrow_mut().token = Some(Token { key, sub_id: 0 }); inner.register(&dispatcher)?; // Straightforward casting would require us to add the bound `Data: 'l` but we don't actually need it // as this module never accesses the dispatch data, so we use transmute to erase it let inner: Rc = unsafe { std::mem::transmute(inner as Rc) }; Ok(Async { fd: Some(fd), dispatcher, inner, old_flags, }) } /// Mutably access the underlying IO object pub fn get_mut(&mut self) -> &mut F { self.fd.as_mut().unwrap() } /// A future that resolves once the object becomes ready for reading pub fn readable<'s>(&'s mut self) -> Readable<'s, 'l, F> { Readable { io: self } } /// A future that resolves once the object becomes ready for writing pub fn writable<'s>(&'s mut self) -> Writable<'s, 'l, F> { Writable { io: self } } /// Remove the async adapter and retrieve the underlying object pub fn into_inner(mut self) -> F { self.fd.take().unwrap() } fn readiness(&self) -> Readiness { self.dispatcher.borrow_mut().readiness() } fn register_waker(&self, interest: Interest, waker: Waker) -> crate::Result<()> { { let mut disp = self.dispatcher.borrow_mut(); disp.interest = interest; disp.waker = Some(waker); } self.inner.reregister(&self.dispatcher) } } /// A future that resolves once the associated object becomes ready for reading #[derive(Debug)] pub struct Readable<'s, 'l, F: AsRawFd> { io: &'s mut Async<'l, F>, } impl<'s, 'l, F: AsRawFd> std::future::Future for Readable<'s, 'l, F> { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> TaskPoll<()> { let io = &mut self.as_mut().io; let readiness = io.readiness(); if readiness.readable || readiness.error { TaskPoll::Ready(()) } else { let _ = io.register_waker(Interest::READ, cx.waker().clone()); TaskPoll::Pending } } } /// A future that resolves once the associated object becomes ready for writing #[derive(Debug)] pub struct Writable<'s, 'l, F: AsRawFd> { io: &'s mut Async<'l, F>, } impl<'s, 'l, F: AsRawFd> std::future::Future for Writable<'s, 'l, F> { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> TaskPoll<()> { let io = &mut self.as_mut().io; let readiness = io.readiness(); if readiness.writable || readiness.error { TaskPoll::Ready(()) } else { let _ = io.register_waker(Interest::WRITE, cx.waker().clone()); TaskPoll::Pending } } } impl<'l, F: AsRawFd> Drop for Async<'l, F> { fn drop(&mut self) { self.inner.kill(&self.dispatcher); // restore flags let _ = fcntl( self.dispatcher.borrow().fd, FcntlArg::F_SETFL(self.old_flags), ); } } impl<'l, F: AsRawFd> Unpin for Async<'l, F> {} trait IoLoopInner { fn register(&self, dispatcher: &RefCell) -> crate::Result<()>; fn reregister(&self, dispatcher: &RefCell) -> crate::Result<()>; fn kill(&self, dispatcher: &RefCell); } impl<'l, Data> IoLoopInner for LoopInner<'l, Data> { fn register(&self, dispatcher: &RefCell) -> crate::Result<()> { let disp = dispatcher.borrow(); self.poll.borrow_mut().register( disp.fd, Interest::EMPTY, Mode::OneShot, disp.token.expect("No token for IO dispatcher"), ) } fn reregister(&self, dispatcher: &RefCell) -> crate::Result<()> { let disp = dispatcher.borrow(); self.poll.borrow_mut().reregister( disp.fd, disp.interest, Mode::OneShot, disp.token.expect("No token for IO dispatcher"), ) } fn kill(&self, dispatcher: &RefCell) { let key = dispatcher .borrow() .token .expect("No token for IO dispatcher") .key; let _source = self .sources .borrow_mut() .remove(key) .expect("Attempting to remove a non-existent source?!"); } } struct IoDispatcher { fd: RawFd, token: Option, 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 TokenFactory) -> crate::Result<()> { // registration is handled by IoLoopInner unreachable!() } fn reregister(&self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result { // registration is handled by IoLoopInner unreachable!() } fn unregister(&self, poll: &mut Poll) -> crate::Result { let disp = self.borrow(); if disp.is_registered { poll.unregister(disp.fd)?; } Ok(true) } fn pre_run(&self, _data: &mut Data) -> crate::Result<()> { Ok(()) } fn post_run(&self, _data: &mut Data) -> crate::Result<()> { Ok(()) } } /* * Async IO trait implementations */ #[cfg(feature = "futures-io")] #[cfg_attr(docsrs, doc(cfg(feature = "futures-io")))] impl<'l, F: AsRawFd + std::io::Read> AsyncRead for Async<'l, F> { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> TaskPoll> { 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: AsRawFd + 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) } } #[cfg(all(test, feature = "executor", feature = "futures-io"))] mod tests { use futures::io::{AsyncReadExt, AsyncWriteExt}; use crate::sources::futures::executor; #[test] fn read_write() { let mut event_loop = crate::EventLoop::try_new().unwrap(); let handle = event_loop.handle(); let (exec, sched) = executor().unwrap(); handle .insert_source(exec, move |ret, &mut (), got| { *got = ret; }) .unwrap(); let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut tx = handle.adapt_io(tx).unwrap(); let mut rx = handle.adapt_io(rx).unwrap(); let received = std::rc::Rc::new(std::cell::Cell::new(false)); let fut_received = received.clone(); sched .schedule(async move { let mut buf = [0; 12]; rx.read_exact(&mut buf).await.unwrap(); assert_eq!(&buf, b"Hello World!"); fut_received.set(true); }) .unwrap(); // The receiving future alone cannot advance event_loop .dispatch(Some(std::time::Duration::from_millis(10)), &mut ()) .unwrap(); assert!(!received.get()); // schedule the writing future as well and wait until finish sched .schedule(async move { tx.write_all(b"Hello World!").await.unwrap(); tx.flush().await.unwrap(); }) .unwrap(); while !received.get() { event_loop.dispatch(None, &mut ()).unwrap(); } } #[test] fn read_write_vectored() { let mut event_loop = crate::EventLoop::try_new().unwrap(); let handle = event_loop.handle(); let (exec, sched) = executor().unwrap(); handle .insert_source(exec, move |ret, &mut (), got| { *got = ret; }) .unwrap(); let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut tx = handle.adapt_io(tx).unwrap(); let mut rx = handle.adapt_io(rx).unwrap(); let received = std::rc::Rc::new(std::cell::Cell::new(false)); let fut_received = received.clone(); sched .schedule(async move { let mut buf = [0; 12]; let mut ioslices = buf .chunks_mut(2) .map(std::io::IoSliceMut::new) .collect::>(); 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.10.5/src/lib.rs000064400000000000000000000163231046102023000132500ustar 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(coverage, feature(no_coverage))] mod sys; pub use sys::{Interest, Mode, Poll, Readiness, Token, TokenFactory}; pub use self::loop_logic::{EventLoop, LoopHandle, LoopSignal, RegistrationToken}; pub use self::sources::*; pub mod error; pub use error::{Error, InsertError, Result}; pub mod io; mod loop_logic; mod macros; mod sources; calloop-0.10.5/src/loop_logic.rs000064400000000000000000000704731046102023000146360ustar 00000000000000use std::cell::{Cell, RefCell}; use std::fmt::Debug; use std::io; use std::os::unix::io::AsRawFd; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::{Duration, Instant}; use slotmap::SlotMap; use crate::sources::{Dispatcher, EventSource, Idle, IdleDispatcher}; use crate::{EventDispatcher, InsertError, Poll, PostAction, TokenFactory}; type IdleCallback<'i, Data> = Rc + 'i>>; slotmap::new_key_type! { pub(crate) struct CalloopKey; } /// A token representing a registration in the [`EventLoop`]. /// /// This token is given to you by the [`EventLoop`] when an [`EventSource`] is inserted or /// a [`Dispatcher`] is registered. You can use it to [disable](LoopHandle#method.disable), /// [enable](LoopHandle#method.enable), [update`](LoopHandle#method.update), /// [remove](LoopHandle#method.remove) or [kill](LoopHandle#method.kill) it. #[derive(Debug, PartialEq, Clone, Copy)] pub struct RegistrationToken { key: CalloopKey, } pub(crate) struct LoopInner<'l, Data> { pub(crate) poll: RefCell, pub(crate) sources: RefCell + 'l>>>, 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(coverage, no_coverage)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("LoopHandle { ... }") } } impl<'l, Data> Clone for LoopHandle<'l, Data> { #[cfg_attr(coverage, no_coverage)] fn clone(&self) -> Self { LoopHandle { inner: self.inner.clone(), } } } impl<'l, Data> LoopHandle<'l, Data> { /// Inserts a new event source in the loop. /// /// The provided callback will be called during the dispatching cycles whenever the /// associated source generates events, see `EventLoop::dispatch(..)` for details. /// /// This function takes ownership of the event source. Use `register_dispatcher` /// if you need access to the event source after this call. pub fn insert_source( &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`. 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(); let key = sources.insert(dispatcher.clone_as_event_dispatcher()); let ret = sources .get(key) .unwrap() .register(&mut *poll, &mut TokenFactory::new(key)); if let Err(error) = ret { sources.remove(key).expect("Source was just inserted?!"); return Err(error); } Ok(RegistrationToken { key }) } /// Inserts an idle callback. /// /// This callback will be called during a dispatching cycle when the event loop has /// finished processing all pending events from the sources and becomes idle. pub fn insert_idle<'i, F: FnOnce(&mut Data) + 'l + 'i>(&self, callback: F) -> Idle<'i> { let mut opt_cb = Some(callback); let callback = Rc::new(RefCell::new(Some(move |data: &mut Data| { if let Some(cb) = opt_cb.take() { cb(data); } }))); self.inner.idles.borrow_mut().push(callback.clone()); Idle { callback } } /// Enables this previously disabled event source. /// /// This previously disabled source will start generating events again. /// /// **Note:** this cannot be done from within the source callback. pub fn enable(&self, token: &RegistrationToken) -> crate::Result<()> { if let Some(source) = self.inner.sources.borrow().get(token.key) { source.register( &mut *self.inner.poll.borrow_mut(), &mut TokenFactory::new(token.key), )?; } Ok(()) } /// Makes this source update its registration. /// /// If after accessing the source you changed its parameters in a way that requires /// updating its registration. pub fn update(&self, token: &RegistrationToken) -> crate::Result<()> { if let Some(source) = self.inner.sources.borrow().get(token.key) { if !source.reregister( &mut *self.inner.poll.borrow_mut(), &mut TokenFactory::new(token.key), )? { // we are in a callback, store for later processing self.inner.pending_action.set(PostAction::Reregister); } } Ok(()) } /// Disables this event source. /// /// The source remains in the event loop, but it'll no longer generate events pub fn disable(&self, token: &RegistrationToken) -> crate::Result<()> { if let Some(source) = self.inner.sources.borrow().get(token.key) { if !source.unregister(&mut *self.inner.poll.borrow_mut())? { // we are in a callback, store for later processing self.inner.pending_action.set(PostAction::Disable); } } Ok(()) } /// Removes this source from the event loop. pub fn remove(&self, token: RegistrationToken) { if let Some(source) = self.inner.sources.borrow_mut().remove(token.key) { if let Err(e) = source.unregister(&mut self.inner.poll.borrow_mut()) { log::warn!( "[calloop] Failed to unregister source from the polling system: {:?}", e ); } } } /// Wrap an IO object into an async adapter /// /// This adapter turns the IO object into an async-aware one that can be used in futures. /// The readiness of these futures will be driven by the event loop. /// /// The produced futures can be polled in any executor, and notably the one provided by /// calloop. pub fn adapt_io(&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> { handle: LoopHandle<'l, Data>, stop_signal: Arc, ping: crate::sources::ping::Ping, } impl<'l, Data> std::fmt::Debug for EventLoop<'l, Data> { #[cfg_attr(coverage, no_coverage)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("EventLoop { ... }") } } impl<'l, Data> EventLoop<'l, Data> { /// Create a new event loop /// /// Fails if the initialization of the polling system failed. pub fn try_new() -> crate::Result { Self::inner_new(false) } /// Create a new event loop in high precision mode /// /// On some platforms it requires to setup more resources to enable high-precision /// (sub millisecond) capabilities, so you should use this constructor if you need /// this kind of precision. /// /// Fails if the initialization of the polling system failed. pub fn try_new_high_precision() -> crate::Result { Self::inner_new(true) } fn inner_new(high_precision: bool) -> crate::Result { let poll = Poll::new(high_precision)?; let handle = LoopHandle { inner: Rc::new(LoopInner { poll: RefCell::new(poll), sources: RefCell::new(SlotMap::with_key()), idles: RefCell::new(Vec::new()), pending_action: Cell::new(PostAction::Continue), }), }; let (ping, ping_source) = crate::sources::ping::make_ping()?; handle.insert_source(ping_source, |_, _, _| {})?; Ok(EventLoop { handle, stop_signal: Arc::new(AtomicBool::new(false)), ping, }) } /// Retrieve a loop handle pub fn handle(&self) -> LoopHandle<'l, Data> { self.handle.clone() } fn dispatch_events( &mut self, mut timeout: Option, data: &mut Data, ) -> crate::Result<()> { let now = Instant::now(); let events = { let mut poll = self.handle.inner.poll.borrow_mut(); loop { let result = poll.poll(timeout); match result { Ok(events) => break events, Err(crate::Error::IoError(err)) if err.kind() == io::ErrorKind::Interrupted => { // Interrupted by a signal. Update timeout and retry. if let Some(to) = timeout { let elapsed = now.elapsed(); if elapsed >= to { return Ok(()); } else { timeout = Some(to - elapsed); } } } Err(err) => return Err(err), }; } }; for event in events { let opt_disp = self .handle .inner .sources .borrow() .get(event.token.key) .cloned(); if let Some(disp) = opt_disp { let mut ret = disp.process_events(event.readiness, event.token, data)?; // if the returned PostAction is Continue, it may be overwritten by an user-specified pending action let pending_action = self .handle .inner .pending_action .replace(PostAction::Continue); if let PostAction::Continue = ret { ret = pending_action; } match ret { PostAction::Reregister => { disp.reregister( &mut self.handle.inner.poll.borrow_mut(), &mut TokenFactory::new(event.token.key), )?; } PostAction::Disable => { disp.unregister(&mut self.handle.inner.poll.borrow_mut())?; } PostAction::Remove => { // delete the source from the list, it'll be cleaned up with the if just below self.handle .inner .sources .borrow_mut() .remove(event.token.key); } PostAction::Continue => {} } if !self .handle .inner .sources .borrow() .contains_key(event.token.key) { // the source has been removed from within its callback, unregister it let mut poll = self.handle.inner.poll.borrow_mut(); if let Err(e) = disp.unregister(&mut *poll) { log::warn!( "[calloop] Failed to unregister source from the polling system: {:?}", e ); } } } else { log::warn!( "[calloop] Received an event for non-existence source: {:?}", event.token.key ); } } Ok(()) } fn dispatch_idles(&mut self, data: &mut Data) { let idles = std::mem::take(&mut *self.handle.inner.idles.borrow_mut()); for idle in idles { idle.borrow_mut().dispatch(data); } } fn invoke_pre_run(&self, data: &mut Data) -> crate::Result<()> { let sources = self .handle .inner .sources .borrow() .values() .cloned() .collect::>(); for source in sources { source.pre_run(data)?; } Ok(()) } fn invoke_post_run(&self, data: &mut Data) -> crate::Result<()> { let sources = self .handle .inner .sources .borrow() .values() .cloned() .collect::>(); for source in sources { source.post_run(data)?; } Ok(()) } /// Dispatch pending events to their callbacks /// /// If some sources have events available, their callbacks will be immediatly called. /// Otherwise this will wait until an event is receive or the provided `timeout` /// is reached. If `timeout` is `None`, it will wait without a duration limit. /// /// Once pending events have been processed or the timeout is reached, all pending /// idle callbacks will be fired before this method returns. pub fn dispatch>>( &mut self, timeout: D, data: &mut Data, ) -> crate::Result<()> { self.invoke_pre_run(data)?; self.dispatch_events(timeout.into(), data)?; self.dispatch_idles(data); self.invoke_post_run(data)?; Ok(()) } /// Get a signal to stop this event loop from running /// /// To be used in conjunction with the `run()` method. pub fn get_signal(&self) -> LoopSignal { LoopSignal { signal: self.stop_signal.clone(), ping: self.ping.clone(), } } /// Run this event loop /// /// This will repeatedly try to dispatch events (see the `dispatch()` method) on /// this event loop, waiting at most `timeout` every time. /// /// Between each dispatch wait, your provided callback will be called. /// /// You can use the `get_signal()` method to retrieve a way to stop or wakeup /// the event loop from anywhere. pub fn run>>( &mut self, timeout: D, data: &mut Data, mut cb: F, ) -> crate::Result<()> where F: FnMut(&mut Data), { let timeout = timeout.into(); self.stop_signal.store(false, Ordering::Release); self.invoke_pre_run(data)?; while !self.stop_signal.load(Ordering::Acquire) { self.dispatch_events(timeout, data)?; self.dispatch_idles(data); cb(data); } self.invoke_post_run(data)?; Ok(()) } } /// A signal that can be shared between thread to stop or wakeup a running /// event loop #[derive(Clone)] pub struct LoopSignal { signal: Arc, ping: crate::sources::ping::Ping, } impl std::fmt::Debug for LoopSignal { #[cfg_attr(coverage, no_coverage)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("LoopSignal { ... }") } } impl LoopSignal { /// Stop the event loop /// /// Once this method is called, the next time the event loop has finished /// waiting for events, it will return rather than starting to wait again. /// /// This is only usefull if you are using the `EventLoop::run()` method. pub fn stop(&self) { self.signal.store(true, Ordering::Release); } /// Wake up the event loop /// /// This sends a dummy event to the event loop to simulate the reception /// of an event, making the wait return early. Called after `stop()`, this /// ensures the event loop will terminate quickly if you specified a long /// timeout (or no timeout at all) to the `dispatch` or `run` method. pub fn wakeup(&self) { self.ping.ping(); } } #[cfg(test)] mod tests { use std::time::Duration; use crate::{ generic::Generic, ping::*, Dispatcher, Interest, Mode, Poll, PostAction, Readiness, RegistrationToken, Token, TokenFactory, }; use super::EventLoop; #[test] fn dispatch_idle() { let mut event_loop = EventLoop::try_new().unwrap(); let mut dispatched = false; event_loop.handle().insert_idle(|d| { *d = true; }); event_loop .dispatch(Some(Duration::ZERO), &mut dispatched) .unwrap(); assert!(dispatched); } #[test] fn cancel_idle() { let mut event_loop = EventLoop::try_new().unwrap(); let mut dispatched = false; let handle = event_loop.handle(); let idle = handle.insert_idle(move |d| { *d = true; }); idle.cancel(); event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(!dispatched); } #[test] fn wakeup() { let mut event_loop = EventLoop::try_new().unwrap(); let signal = event_loop.get_signal(); ::std::thread::spawn(move || { ::std::thread::sleep(Duration::from_millis(500)); signal.wakeup(); }); // the test should return event_loop.dispatch(None, &mut ()).unwrap(); } #[test] fn wakeup_stop() { let mut event_loop = EventLoop::try_new().unwrap(); let signal = event_loop.get_signal(); ::std::thread::spawn(move || { ::std::thread::sleep(Duration::from_millis(500)); signal.stop(); signal.wakeup(); }); // the test should return event_loop.run(None, &mut (), |_| {}).unwrap(); } #[test] fn insert_bad_source() { let event_loop = EventLoop::<()>::try_new().unwrap(); let ret = event_loop.handle().insert_source( crate::sources::generic::Generic::new(420, Interest::READ, Mode::Level), |_, _, _| Ok(PostAction::Continue), ); assert!(ret.is_err()); } #[test] fn insert_source_no_interest() { let event_loop = EventLoop::<()>::try_new().unwrap(); let ret = event_loop.handle().insert_source( crate::sources::generic::Generic::new(0, Interest::EMPTY, Mode::Level), |_, _, _| Ok(PostAction::Continue), ); assert!(ret.is_ok()); } #[test] fn disarm_rearm() { let mut event_loop = EventLoop::::try_new().unwrap(); let (ping, ping_source) = make_ping().unwrap(); let ping_token = event_loop .handle() .insert_source(ping_source, |(), &mut (), dispatched| { *dispatched = true; }) .unwrap(); ping.ping(); let mut dispatched = false; event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(dispatched); // disable the source ping.ping(); event_loop.handle().disable(&ping_token).unwrap(); let mut dispatched = false; event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(!dispatched); // disabling it again is an error event_loop.handle().disable(&ping_token).unwrap_err(); // reenable it, the previous ping now gets dispatched event_loop.handle().enable(&ping_token).unwrap(); let mut dispatched = false; event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(dispatched); } #[test] fn multiple_tokens() { struct DoubleSource { ping1: PingSource, ping2: PingSource, } impl crate::EventSource for DoubleSource { type Event = u32; type Metadata = (); type Ret = (); type Error = PingError; fn process_events( &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); } #[test] fn change_interests() { use nix::sys::socket::{recv, socketpair, AddressFamily, MsgFlags, SockFlag, SockType}; use nix::unistd::write; let mut event_loop = EventLoop::::try_new().unwrap(); let (sock1, sock2) = socketpair( AddressFamily::Unix, SockType::Stream, None, SockFlag::empty(), // recv with DONTWAIT will suffice for platforms without SockFlag::SOCK_NONBLOCKING such as macOS ) .unwrap(); let source = Generic::new(sock1, Interest::READ, Mode::Level); let dispatcher = Dispatcher::new(source, |_, &mut fd, dispatched| { *dispatched = true; // read all contents available to drain the socket let mut buf = [0u8; 32]; loop { match recv(fd, &mut buf, MsgFlags::MSG_DONTWAIT) { Ok(0) => break, // closed pipe, we are now inert Ok(_) => {} Err(e) => { let e: std::io::Error = e.into(); if e.kind() == std::io::ErrorKind::WouldBlock { break; // nothing more to read } else { // propagate error return Err(e); } } } } Ok(PostAction::Continue) }); let sock_token_1 = event_loop .handle() .register_dispatcher(dispatcher.clone()) .unwrap(); // first dispatch, nothing is readable let mut dispatched = false; event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(!dispatched); // write something, the socket becomes readable write(sock2, &[1, 2, 3]).unwrap(); dispatched = false; event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(dispatched); // All has been read, no longer readable dispatched = false; event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(!dispatched); // change the interests for writability instead dispatcher.as_source_mut().interest = Interest::WRITE; event_loop.handle().update(&sock_token_1).unwrap(); // the socket is writable dispatched = false; event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(dispatched); // change back to readable dispatcher.as_source_mut().interest = Interest::READ; event_loop.handle().update(&sock_token_1).unwrap(); // the socket is not readable dispatched = false; event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(!dispatched); } #[test] fn kill_source() { let mut event_loop = EventLoop::>::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); } // 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.10.5/src/macros.rs000064400000000000000000000143341046102023000137660ustar 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.10.5/src/sources/channel.rs000064400000000000000000000233351046102023000155760ustar 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(coverage, no_coverage)] 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(coverage, no_coverage)] 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.10.5/src/sources/futures.rs000064400000000000000000000147311046102023000156630ustar 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 std::{future::Future, pin::Pin, sync::Arc}; use futures_util::{ stream::{FuturesUnordered, Stream}, task::{waker_ref, ArcWake, Context, LocalFutureObj, Poll as FutPoll}, }; use crate::{ sources::{ channel::{channel, Channel, ChannelError, Event, Sender}, ping::{make_ping, Ping, PingError, PingSource}, EventSource, }, Poll, PostAction, Readiness, Token, TokenFactory, }; /// A future executor as an event source #[derive(Debug)] pub struct Executor { futures: FuturesUnordered>, new_futures: Channel>, ready_futures: PingSource, waker: Arc, } /// A scheduler to send futures to an executor #[derive(Clone, Debug)] pub struct Scheduler { sender: Sender>, } 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, { let obj = LocalFutureObj::new(Box::new(future)); self.sender.send(obj).map_err(|_| ExecutorDestroyed) } } /// Error generated when trying to schedule a future after the /// executor was destroyed. #[derive(thiserror::Error, Debug)] #[error("the executor was destroyed")] pub struct ExecutorDestroyed; #[derive(Debug)] struct ExecWaker { ping: Ping, } impl ArcWake for ExecWaker { fn wake_by_ref(arc_self: &Arc) { arc_self.ping.ping(); } } /// Create a new executor, and its associated scheduler /// /// May fail due to OS errors preventing calloop to setup its internal pipes (if your /// process has reatched its file descriptor limit for example). pub fn executor() -> crate::Result<(Executor, Scheduler)> { let (ping, ready_futures) = make_ping()?; let (sender, new_futures) = channel(); Ok(( Executor { futures: FuturesUnordered::new(), new_futures, ready_futures, waker: Arc::new(ExecWaker { ping }), }, Scheduler { sender }, )) } impl 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 ()), { // fetch all newly inserted futures and push them to the container let futures = &mut self.futures; self.new_futures .process_events(readiness, token, |evt, _| { if let Event::Msg(fut) = evt { futures.push(fut); } }) .map_err(ExecutorError::NewFutureError)?; // process ping events to make it non-ready again self.ready_futures .process_events(readiness, token, |(), _| {}) .map_err(ExecutorError::WakeError)?; // advance all available futures as much as possible let waker = waker_ref(&self.waker); let mut cx = Context::from_waker(&waker); while let FutPoll::Ready(Some(ret)) = Pin::new(&mut self.futures).poll_next(&mut cx) { callback(ret, &mut ()); } Ok(PostAction::Continue) } fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> { self.new_futures.register(poll, token_factory)?; self.ready_futures.register(poll, token_factory)?; Ok(()) } fn reregister( &mut self, poll: &mut Poll, token_factory: &mut TokenFactory, ) -> crate::Result<()> { self.new_futures.reregister(poll, token_factory)?; self.ready_futures.reregister(poll, token_factory)?; Ok(()) } fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> { self.new_futures.unregister(poll)?; self.ready_futures.unregister(poll)?; Ok(()) } } /// An error arising from processing events in an async executor event source. #[derive(thiserror::Error, Debug)] pub enum ExecutorError { /// Error while reading new futures added via [`Scheduler::schedule()`]. #[error("error adding new futures")] NewFutureError(ChannelError), /// Error while processing wake events from existing futures. #[error("error processing wake events")] WakeError(PingError), } #[cfg(test)] mod tests { use super::*; #[test] fn ready() { let mut event_loop = crate::EventLoop::::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.10.5/src/sources/generic.rs000064400000000000000000000202671046102023000156030ustar 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. //! //! ``` //! # extern crate calloop; //! use calloop::{generic::Generic, Interest, Mode, PostAction}; //! //! # fn main() { //! # let mut event_loop = calloop::EventLoop::<()>::try_new() //! # .expect("Failed to initialize the event loop!"); //! # let handle = event_loop.handle(); //! # let io_object = 0; //! handle.insert_source( //! // wrap your IO object in a Generic, here we register for read readiness //! // in level-triggering mode //! Generic::new(io_object, Interest::READ, Mode::Level), //! |readiness, io_object, shared_data| { //! // The first argument of the callback is a Readiness //! // The second is a &mut reference to your object //! //! // your callback needs to return a Result //! // if it returns an error, the event loop will consider this event //! // event source as erroring and report it to the user. //! Ok(PostAction::Continue) //! } //! ); //! # } //! ``` //! //! It can also help you implementing your own event sources: just have //! these `Generic<_>` as fields of your event source, and delegate the //! [`EventSource`](crate::EventSource) implementation to them. //! //! If you need to directly work with a [`RawFd`](std::os::unix::io::RawFd), rather than an //! FD-backed object, see [`Generic::from_fd`](Generic#method.from_fd). use std::{marker::PhantomData, os::unix::io::AsRawFd}; use crate::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory}; /// A generic event source wrapping a FD-backed type #[derive(Debug)] pub struct Generic { /// The wrapped FD-backed type pub file: F, /// The programmed interest pub interest: Interest, /// The programmed mode pub mode: Mode, // This token is used by the event loop logic to look up this source when an // event occurs. token: Option, // 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, interest, mode, token: None, _error_type: PhantomData::default(), } } /// Wrap a FD-backed type into a `Generic` event source using an arbitrary error type. pub fn new_with_error(file: F, interest: Interest, mode: Mode) -> Generic { Generic { file, interest, mode, token: None, _error_type: PhantomData::default(), } } } impl Generic { /// Unwrap the `Generic` source to retrieve the underlying type pub fn unwrap(self) -> F { self.file } } impl EventSource for Generic where F: AsRawFd, E: Into>, { type Event = Readiness; type Metadata = F; 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, &mut self.file) } fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> { let token = token_factory.token(); poll.register(self.file.as_raw_fd(), self.interest, self.mode, token)?; self.token = Some(token); Ok(()) } fn reregister( &mut self, poll: &mut Poll, token_factory: &mut TokenFactory, ) -> crate::Result<()> { let token = token_factory.token(); poll.reregister(self.file.as_raw_fd(), self.interest, self.mode, token)?; self.token = Some(token); Ok(()) } fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> { poll.unregister(self.file.as_raw_fd())?; self.token = None; Ok(()) } } #[cfg(test)] mod tests { use std::io::{Read, Write}; use super::Generic; use crate::{Dispatcher, Interest, Mode, PostAction}; #[cfg(unix)] #[test] fn dispatch_unix() { use std::os::unix::net::UnixStream; let mut event_loop = crate::EventLoop::try_new().unwrap(); let handle = event_loop.handle(); let (mut tx, rx) = UnixStream::pair().unwrap(); let generic = Generic::new(rx, Interest::READ, Mode::Level); let mut dispached = false; let _generic_token = handle .insert_source(generic, move |readiness, file, d| { assert!(readiness.readable); // we have not registered for writability assert!(!readiness.writable); let mut buffer = vec![0; 10]; let ret = file.read(&mut buffer).unwrap(); assert_eq!(ret, 6); assert_eq!(&buffer[..6], &[1, 2, 3, 4, 5, 6]); *d = true; Ok(PostAction::Continue) }) .unwrap(); event_loop .dispatch(Some(::std::time::Duration::ZERO), &mut dispached) .unwrap(); assert!(!dispached); let ret = tx.write(&[1, 2, 3, 4, 5, 6]).unwrap(); assert_eq!(ret, 6); tx.flush().unwrap(); event_loop .dispatch(Some(::std::time::Duration::ZERO), &mut dispached) .unwrap(); assert!(dispached); } #[test] fn register_deregister_unix() { use std::os::unix::net::UnixStream; let mut event_loop = crate::EventLoop::try_new().unwrap(); let handle = event_loop.handle(); let (mut tx, rx) = UnixStream::pair().unwrap(); let generic = Generic::new(rx, Interest::READ, Mode::Level); let dispatcher = Dispatcher::new(generic, move |_, _, d| { *d = true; Ok(PostAction::Continue) }); let mut dispached = false; let generic_token = handle.register_dispatcher(dispatcher.clone()).unwrap(); event_loop .dispatch(Some(::std::time::Duration::ZERO), &mut dispached) .unwrap(); assert!(!dispached); // remove the source, and then write something event_loop.handle().remove(generic_token); let ret = tx.write(&[1, 2, 3, 4, 5, 6]).unwrap(); assert_eq!(ret, 6); tx.flush().unwrap(); event_loop .dispatch(Some(::std::time::Duration::ZERO), &mut dispached) .unwrap(); // the source has not been dispatched, as the source is no longer here assert!(!dispached); // insert it again let generic = dispatcher.into_source_inner(); let _generic_token = handle .insert_source(generic, move |readiness, file, d| { assert!(readiness.readable); // we have not registered for writability assert!(!readiness.writable); let mut buffer = vec![0; 10]; let ret = file.read(&mut buffer).unwrap(); assert_eq!(ret, 6); assert_eq!(&buffer[..6], &[1, 2, 3, 4, 5, 6]); *d = true; Ok(PostAction::Continue) }) .unwrap(); event_loop .dispatch(Some(::std::time::Duration::ZERO), &mut dispached) .unwrap(); // the has now been properly dispatched assert!(dispached); } } calloop-0.10.5/src/sources/mod.rs000064400000000000000000000521241046102023000147430ustar 00000000000000use std::{ cell::{Ref, RefCell, RefMut}, ops::{BitOr, BitOrAssign}, rc::Rc, }; use crate::{sys::TokenFactory, Poll, Readiness, Token}; pub mod channel; #[cfg(feature = "executor")] #[cfg_attr(docsrs, doc(cfg(feature = "executor")))] pub mod futures; pub mod generic; pub mod ping; #[cfg(target_os = "linux")] #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))] pub mod signals; pub mod timer; pub mod transient; /// Possible actions that can be requested to the event loop by an /// event source once its events have been processed. /// /// `PostAction` values can be combined with the `|` (bit-or) operator (or with /// `|=`) with the result that: /// - if both values are identical, the result is that value /// - if they are different, the result is [`Reregister`](PostAction::Reregister) /// /// Bit-or-ing these results is useful for composed sources to combine the /// results of their child sources, but note that it only applies to the child /// sources. For example, if every child source returns `Continue`, the result /// will be `Continue`, but the parent source might still need to return /// `Reregister` or something else depending on any additional logic it uses. #[derive(Debug, PartialEq, Copy, Clone)] pub enum PostAction { /// Continue listening for events on this source as before Continue, /// Trigger a re-registration of this source Reregister, /// Disable this source /// /// Has the same effect as [`LoopHandle::disable`](crate::LoopHandle#method.disable) Disable, /// Remove this source from the eventloop /// /// Has the same effect as [`LoopHandle::kill`](crate::LoopHandle#method.kill) Remove, } /// Combines `PostAction` values returned from nested event sources. impl BitOr for PostAction { type Output = Self; fn bitor(self, rhs: Self) -> Self::Output { if matches!(self, x if x == rhs) { self } else { Self::Reregister } } } /// Combines `PostAction` values returned from nested event sources. impl BitOrAssign for PostAction { fn bitor_assign(&mut self, rhs: Self) { if *self != rhs { *self = Self::Reregister; } } } /// Trait representing an event source /// /// This is the trait you need to implement if you wish to create your own /// calloop-compatible event sources. /// /// The 3 associated types define the type of closure the user will need to /// provide to process events for your event source. /// /// The `process_events` method will be called when one of the FD you registered /// is ready, with the associated readiness and token. /// /// The `register`, `reregister` and `unregister` methods are plumbing to let your /// source register itself with the polling system. See their documentation for details. /// /// In case your event source needs to do some special processing before or after a /// polling session occurs (to prepare the underlying source for polling, and cleanup /// after that), you can override the `pre_run` and `post_run`, that do nothing by /// default. Depending on the underlying events, `process_events` may be invoked once, /// several times, or none at all between `pre_run` and `post_run` are called, but when /// it is invoked, it'll always be between those two. pub trait EventSource { /// The type of events generated by your source. type Event; /// Some metadata of your event source /// /// This is typically useful if your source contains some internal state that /// the user may need to interact with when processing events. The user callback /// will receive a `&mut Metadata` reference. /// /// Set to `()` if not needed. type Metadata; /// The return type of the user callback /// /// If the user needs to return some value back to your event source once its /// processing is finshed (to indicate success or failure for example), you can /// specify it using this type. /// /// Set to `()` if not needed. type Ret; /// The error type returned from /// [`process_events()`](Self::process_events()) (not the user callback!). type Error: Into>; /// 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<()>; /// Notification that a polling session is going to start /// /// You can generate events from this method as you would from `process_events`. fn pre_run(&mut self, _callback: F) -> crate::Result<()> where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { Ok(()) } /// Notification that the current polling session ended /// /// You can generate events from this method as you would from `process_events`. fn post_run(&mut self, _callback: F) -> crate::Result<()> where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { Ok(()) } } /// Blanket implementation for boxed event sources. [`EventSource`] is not an /// object safe trait, so this does not include trait objects. impl 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) } fn pre_run(&mut self, callback: F) -> crate::Result<()> where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { T::pre_run(&mut **self, callback) } fn post_run(&mut self, callback: F) -> crate::Result<()> where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { T::post_run(&mut **self, callback) } } /// Blanket implementation for exclusive references to event sources. /// [`EventSource`] is not an object safe trait, so this does not include trait /// objects. impl 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) } fn pre_run(&mut self, callback: F) -> crate::Result<()> where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { T::pre_run(&mut **self, callback) } fn post_run(&mut self, callback: F) -> crate::Result<()> where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { T::post_run(&mut **self, callback) } } pub(crate) struct DispatcherInner { source: S, callback: F, } 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; source .process_events(readiness, token, |event, meta| callback(event, meta, data)) .map_err(|e| crate::Error::OtherError(e.into())) } fn register(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> { self.borrow_mut().source.register(poll, token_factory) } fn reregister(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result { if let Ok(mut me) = self.try_borrow_mut() { me.source.reregister(poll, token_factory)?; Ok(true) } else { Ok(false) } } fn unregister(&self, poll: &mut Poll) -> crate::Result { if let Ok(mut me) = self.try_borrow_mut() { me.source.unregister(poll)?; Ok(true) } else { Ok(false) } } fn pre_run(&self, data: &mut Data) -> crate::Result<()> { let mut disp = self.borrow_mut(); let DispatcherInner { ref mut source, ref mut callback, } = *disp; source.pre_run(|event, meta| callback(event, meta, data)) } fn post_run(&self, data: &mut Data) -> crate::Result<()> { let mut disp = self.borrow_mut(); let DispatcherInner { ref mut source, ref mut callback, } = *disp; source.post_run(|event, meta| callback(event, meta, data)) } } pub(crate) trait EventDispatcher { fn process_events( &self, readiness: Readiness, token: Token, data: &mut Data, ) -> crate::Result; fn register(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()>; fn reregister(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result; fn unregister(&self, poll: &mut Poll) -> crate::Result; fn pre_run(&self, data: &mut Data) -> crate::Result<()>; fn post_run(&self, data: &mut Data) -> crate::Result<()>; } // An internal trait to erase the `F` type parameter of `DispatcherInner` trait ErasedDispatcher<'a, S, Data> { fn as_source_ref(&self) -> Ref; 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(coverage, no_coverage)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("Dispatcher { ... }") } } impl<'a, S, Data> Dispatcher<'a, S, Data> where S: EventSource + 'a, { /// Builds a dispatcher. /// /// The resulting `Dispatcher` pub fn new(source: S, callback: F) -> Self where F: FnMut(S::Event, &mut S::Metadata, &mut Data) -> S::Ret + 'a, { Dispatcher(Rc::new(RefCell::new(DispatcherInner { source, callback }))) } /// Returns an immutable reference to the event source. /// /// # Panics /// /// Has the same semantics as `RefCell::borrow()`. /// /// The dispatcher being mutably borrowed while its events are dispatched, /// this method will panic if invoked from within the associated dispatching closure. pub fn as_source_ref(&self) -> Ref { 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(coverage, no_coverage)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("Idle { ... }") } } impl<'i> Idle<'i> { /// Cancel the idle callback if it was not already run pub fn cancel(self) { self.callback.borrow_mut().cancel(); } } pub(crate) trait CancellableIdle { fn cancel(&mut self); } impl 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.10.5/src/sources/ping/eventfd.rs000064400000000000000000000136271046102023000165610ustar 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::RawFd, sync::Arc}; use nix::sys::eventfd::{eventfd, EfdFlags}; use nix::unistd::{read, write}; use super::{CloseOnDrop, PingError}; use crate::{ generic::Generic, EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory, }; // These are not bitfields! They are increments to add to the eventfd counter. // Since the fd can only be closed once, we can effectively use the // INCREMENT_CLOSE value as a bitmask when checking. const INCREMENT_PING: u64 = 0x2; const INCREMENT_CLOSE: u64 = 0x1; #[inline] pub fn make_ping() -> std::io::Result<(Ping, PingSource)> { let read = eventfd(0, EfdFlags::EFD_CLOEXEC | EfdFlags::EFD_NONBLOCK)?; // We only have one fd for the eventfd. If the sending end closes it when // all copies are dropped, the receiving end will be closed as well. We need // to make sure the fd is not closed until all holders of it have dropped // it. let fd_arc = Arc::new(CloseOnDrop(read)); let ping = Ping { event: Arc::new(FlagOnDrop(Arc::clone(&fd_arc))), }; let source = PingSource { event: Generic::new(read, Interest::READ, Mode::Level), _fd: fd_arc, }; Ok((ping, source)) } // Helper functions for the event source IO. #[inline] fn send_ping(fd: RawFd, count: u64) -> std::io::Result<()> { assert!(count > 0); match write(fd, &count.to_ne_bytes()) { // The write succeeded, the ping will wake up the loop. Ok(_) => Ok(()), // The counter hit its cap, which means previous calls to write() will // wake up the loop. Err(nix::errno::Errno::EAGAIN) => Ok(()), // Anything else is a real error. Err(e) => Err(e.into()), } } #[inline] fn drain_ping(fd: RawFd) -> std::io::Result { // The eventfd counter is effectively a u64. const NBYTES: usize = 8; let mut buf = [0u8; NBYTES]; match read(fd, &mut buf) { // Reading from an eventfd should only ever produce 8 bytes. No looping // is required. Ok(NBYTES) => Ok(u64::from_ne_bytes(buf)), Ok(_) => unreachable!(), // Any other error can be propagated. Err(e) => Err(e.into()), } } // The event source is simply a generic source with one of the eventfds. #[derive(Debug)] pub struct PingSource { event: Generic, // This is held only to ensure that there is an owner of the fd that lives // as long as the Generic source, so that the fd is not closed unexpectedly // when all the senders are dropped. _fd: Arc, } 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, |_, &mut fd| { let counter = drain_ping(fd)?; // If the LSB is set, it means we were closed. If anything else // is also set, it means we were pinged. The two are not // mutually exclusive. let close = (counter & INCREMENT_CLOSE) != 0; let ping = (counter & (u64::MAX - 1)) != 0; if ping { callback((), &mut ()); } if close { Ok(PostAction::Remove) } else { Ok(PostAction::Continue) } }) .map_err(|e| PingError(e.into())) } fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> { self.event.register(poll, token_factory) } fn reregister( &mut self, poll: &mut Poll, token_factory: &mut TokenFactory, ) -> crate::Result<()> { self.event.reregister(poll, token_factory) } fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> { self.event.unregister(poll) } } #[derive(Clone, Debug)] pub struct Ping { // This is an Arc because it's potentially shared with clones. The last one // dropped needs to signal to the event source via the eventfd. event: Arc, } impl Ping { /// Send a ping to the `PingSource`. pub fn ping(&self) { if let Err(e) = send_ping(self.event.0 .0, INCREMENT_PING) { log::warn!("[calloop] Failed to write a ping: {:?}", e); } } } /// This manages signalling to the PingSource when it's dropped. There should /// only ever be one of these per PingSource. #[derive(Debug)] struct FlagOnDrop(Arc); impl Drop for FlagOnDrop { fn drop(&mut self) { if let Err(e) = send_ping(self.0 .0, INCREMENT_CLOSE) { log::warn!("[calloop] Failed to send close ping: {:?}", e); } } } calloop-0.10.5/src/sources/ping/pipe.rs000064400000000000000000000107371046102023000160620ustar 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::RawFd, sync::Arc}; use nix::fcntl::OFlag; use nix::unistd::{close, read, write}; use super::{CloseOnDrop, PingError}; use crate::{ generic::Generic, EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory, }; #[cfg(target_os = "macos")] #[inline] fn make_ends() -> std::io::Result<(RawFd, RawFd)> { // macOS does not have pipe2, but we can emulate the behavior of pipe2 by // setting the flags after calling pipe. use nix::{ fcntl::{fcntl, FcntlArg}, unistd::pipe, }; let (read, write) = pipe()?; let read_flags = OFlag::from_bits_truncate(fcntl(read, FcntlArg::F_GETFD)?) | OFlag::O_CLOEXEC | OFlag::O_NONBLOCK; let write_flags = OFlag::from_bits_truncate(fcntl(write, FcntlArg::F_GETFD)?) | OFlag::O_CLOEXEC | OFlag::O_NONBLOCK; fcntl(read, FcntlArg::F_SETFL(read_flags))?; fcntl(write, FcntlArg::F_SETFL(write_flags))?; Ok((read, write)) } #[cfg(not(target_os = "macos"))] #[inline] fn make_ends() -> std::io::Result<(RawFd, RawFd)> { Ok(nix::unistd::pipe2(OFlag::O_CLOEXEC | OFlag::O_NONBLOCK)?) } #[inline] pub fn make_ping() -> std::io::Result<(Ping, PingSource)> { let (read, write) = make_ends()?; let source = PingSource { pipe: Generic::new(read, Interest::READ, Mode::Level), }; let ping = Ping { pipe: Arc::new(CloseOnDrop(write)), }; Ok((ping, source)) } // Helper functions for the event source IO. #[inline] fn send_ping(fd: RawFd) -> std::io::Result<()> { write(fd, &[0u8])?; Ok(()) } // The event source is simply a generic source with the FD of the read end of // the pipe. #[derive(Debug)] pub struct PingSource { pipe: Generic, } 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, |_, &mut fd| { let mut buf = [0u8; 32]; let mut read_something = false; let mut action = PostAction::Continue; loop { match read(fd, &mut buf) { Ok(0) => { // The other end of the pipe was closed, mark ourselves // for removal. action = PostAction::Remove; break; } // Got one or more pings. Ok(_) => read_something = true, // Nothing more to read. Err(nix::errno::Errno::EAGAIN) => break, // Propagate error. Err(e) => return Err(e.into()), } } if read_something { callback((), &mut ()); } Ok(action) }) .map_err(|e| PingError(e.into())) } fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> { self.pipe.register(poll, token_factory) } fn reregister( &mut self, poll: &mut Poll, token_factory: &mut TokenFactory, ) -> crate::Result<()> { self.pipe.reregister(poll, token_factory) } fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> { self.pipe.unregister(poll) } } impl Drop for PingSource { fn drop(&mut self) { if let Err(e) = close(self.pipe.file) { log::warn!("[calloop] Failed to close read ping: {:?}", e); } } } // The sending end of the ping writes zeroes to the write end of the pipe. #[derive(Clone, Debug)] pub struct Ping { pipe: Arc, } // The sending end of the ping writes zeroes to the write end of the pipe. impl Ping { /// Send a ping to the `PingSource` pub fn ping(&self) { if let Err(e) = send_ping(self.pipe.0) { log::warn!("[calloop] Failed to write a ping: {:?}", e); } } } calloop-0.10.5/src/sources/ping.rs000064400000000000000000000215251046102023000151220ustar 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). use nix::unistd::close; use std::os::unix::io::RawFd; // The ping source has platform-dependent implementations provided by modules // under this one. These modules should expose: // - a make_ping() function // - a Ping type // - a PingSource type // // See eg. the pipe implementation for these items' specific requirements. #[cfg(target_os = "linux")] mod eventfd; #[cfg(target_os = "linux")] use eventfd as platform; #[cfg(not(target_os = "linux"))] mod pipe; #[cfg(not(target_os = "linux"))] use pipe as platform; /// Create a new ping event source /// /// you are given a [`Ping`] instance, which can be cloned and used to ping the /// event loop, and a [`PingSource`], which you can insert in your event loop to /// receive the pings. pub fn make_ping() -> std::io::Result<(Ping, PingSource)> { platform::make_ping() } /// The ping event source /// /// You can insert it in your event loop to receive pings. /// /// If you use it directly, it will automatically remove itself from the event loop /// once all [`Ping`] instances are dropped. pub type Ping = platform::Ping; /// The Ping handle /// /// This handle can be cloned and sent accross threads. It can be used to /// send pings to the `PingSource`. pub type PingSource = platform::PingSource; /// An error arising from processing events for a ping. #[derive(thiserror::Error, Debug)] #[error(transparent)] pub struct PingError(Box); #[derive(Debug)] struct CloseOnDrop(RawFd); impl Drop for CloseOnDrop { fn drop(&mut self) { if let Err(e) = close(self.0) { log::warn!("[calloop] Failed to close ping fd: {:?}", e); } } } #[cfg(test)] mod tests { use crate::transient::TransientSource; use std::time::Duration; use super::*; #[test] fn ping() { let mut event_loop = crate::EventLoop::::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!(!matches!(wrapper, TransientSource::None)); // Put the source in the loop. let dispatcher = crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true); let token = event_loop .handle() .register_dispatcher(dispatcher.clone()) .unwrap(); // Drop the sender and check that it's actually removed. drop(sender); // There should be no event, but the loop still needs to wake up to // process the close event (just like in the ping_closed() test). event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(!dispatched); // Pull the source wrapper out. event_loop.handle().remove(token); let wrapper = dispatcher.into_source_inner(); // Check that the inner source is now gone. assert!(matches!(wrapper, TransientSource::None)); } #[test] fn ping_fired_and_removed() { // This is like ping_removed() with the single difference that we fire a // ping and drop it between two successive dispatches of the loop. // This keeps track of whether the event fired. let mut dispatched = false; let mut event_loop = crate::EventLoop::::try_new().unwrap(); let (sender, source) = make_ping().unwrap(); let wrapper = TransientSource::from(source); // Check that the source starts off in the wrapper. assert!(!matches!(wrapper, TransientSource::None)); // Put the source in the loop. let dispatcher = crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true); let token = event_loop .handle() .register_dispatcher(dispatcher.clone()) .unwrap(); // Send a ping AND drop the sender and check that it's actually removed. sender.ping(); drop(sender); // There should be an event, but the source should be removed from the // loop immediately after. event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(dispatched); // Pull the source wrapper out. event_loop.handle().remove(token); let wrapper = dispatcher.into_source_inner(); // Check that the inner source is now gone. assert!(matches!(wrapper, TransientSource::None)); } #[test] fn ping_multiple_senders() { // This is like ping_removed() but for testing the behaviour of multiple // senders. // This keeps track of whether the event fired. let mut dispatched = false; let mut event_loop = crate::EventLoop::::try_new().unwrap(); let (sender0, source) = make_ping().unwrap(); let wrapper = TransientSource::from(source); let sender1 = sender0.clone(); let sender2 = sender1.clone(); // Check that the source starts off in the wrapper. assert!(!matches!(wrapper, TransientSource::None)); // Put the source in the loop. let dispatcher = crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true); let token = event_loop .handle() .register_dispatcher(dispatcher.clone()) .unwrap(); // Send a ping AND drop the sender and check that it's actually removed. sender0.ping(); drop(sender0); // There should be an event, and the source should remain in the loop. event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(dispatched); // Now test that the clones still work. Drop after the dispatch loop // instead of before, this time. dispatched = false; sender1.ping(); event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(dispatched); // Finally, drop all of them without sending anything. dispatched = false; drop(sender1); drop(sender2); event_loop .dispatch(Duration::ZERO, &mut dispatched) .unwrap(); assert!(!dispatched); // Pull the source wrapper out. event_loop.handle().remove(token); let wrapper = dispatcher.into_source_inner(); // Check that the inner source is now gone. assert!(matches!(wrapper, TransientSource::None)); } } calloop-0.10.5/src/sources/signals.rs000064400000000000000000000123451046102023000156250ustar 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::os::raw::c_int; use nix::sys::signal::SigSet; pub use nix::sys::signal::Signal; pub use nix::sys::signalfd::siginfo; use nix::sys::signalfd::{SfdFlags, SignalFd}; use super::generic::Generic; use crate::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory}; /// An event generated by the signal event source #[derive(Copy, Clone, Debug)] pub struct Event { info: siginfo, } impl Event { /// Retrieve the signal number that was receive pub fn signal(&self) -> Signal { Signal::try_from(self.info.ssi_signo as c_int).unwrap() } /// Access the full `siginfo_t` associated with this signal event pub fn full_info(&self) -> siginfo { self.info } } /// An event source for receiving Unix signals #[derive(Debug)] pub struct Signals { sfd: Generic, 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()?; // Create the SignalFd let sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK | SfdFlags::SFD_CLOEXEC)?; Ok(Signals { sfd: Generic::new(sfd, Interest::READ, Mode::Level), mask, }) } /// Add a list of signals to the signals source /// /// If this function returns an error, the signal mask of the thread may /// have still been changed. pub fn add_signals(&mut self, signals: &[Signal]) -> crate::Result<()> { for &s in signals { self.mask.add(s); } self.mask.thread_block()?; self.sfd.file.set_mask(&self.mask)?; Ok(()) } /// Remove a list of signals from the signals source /// /// If this function returns an error, the signal mask of the thread may /// have still been changed. pub fn remove_signals(&mut self, signals: &[Signal]) -> crate::Result<()> { let mut removed = SigSet::empty(); for &s in signals { self.mask.remove(s); removed.add(s); } removed.thread_unblock()?; self.sfd.file.set_mask(&self.mask)?; Ok(()) } /// Replace the list of signals of the source /// /// If this function returns an error, the signal mask of the thread may /// have still been changed. pub fn set_signals(&mut self, signals: &[Signal]) -> crate::Result<()> { let mut new_mask = SigSet::empty(); for &s in signals { new_mask.add(s); } self.mask.thread_unblock()?; new_mask.thread_block()?; self.sfd.file.set_mask(&new_mask)?; self.mask = new_mask; Ok(()) } } impl Drop for Signals { fn drop(&mut self) { // we cannot handle error here if let Err(e) = self.mask.thread_unblock() { log::warn!("[calloop] Failed to unmask signals: {:?}", e); } } } impl EventSource for Signals { type Event = Event; type Metadata = (); type Ret = (); type Error = SignalError; fn process_events( &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 sfd.read_signal() { Ok(Some(info)) => callback(Event { info }, &mut ()), Ok(None) => break, Err(e) => { log::warn!("[callop] Error reading from signalfd: {}", e); return Err(e.into()); } } } Ok(PostAction::Continue) }) .map_err(|e| SignalError(e.into())) } fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> { self.sfd.register(poll, token_factory) } fn reregister( &mut self, poll: &mut Poll, token_factory: &mut TokenFactory, ) -> crate::Result<()> { self.sfd.reregister(poll, token_factory) } fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> { self.sfd.unregister(poll) } } /// An error arising from processing events for a process signal. #[derive(thiserror::Error, Debug)] #[error(transparent)] pub struct SignalError(Box); calloop-0.10.5/src/sources/timer.rs000064400000000000000000000417551046102023000153140ustar 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. //! //! The timer precision depends on whether the loop was initialized in high-precision mode. If not, //! you can expect precision of order of 1 millisecond, if you need sub-millisecond precision, //! make sure you initialize the [`EventLoop`](crate::EventLoop) using //! [`EventLoop::try_new_high_precision()`](crate::EventLoop::try_new_high_precision). Note also //! that if you need to rely on good precision timers in general, you may need to enable realtime //! features of your OS to ensure your thread is quickly woken up by the system scheduler. //! //! The provided event is an [`Instant`] representing the deadline for which this timer has fired //! (which can be earlier than the current time depending on the event loop congestion). //! //! The callback associated with this event source is expected to return a [`TimeoutAction`], which //! can be used to implement self-repeating timers by telling calloop to reprogram the same timer //! for a later timeout after it has fired. /* * This module provides two main types: * * - `Timer` is the user-facing type that represents a timer event source * - `TimerWheel` is an internal data structure for tracking registered timeouts, it is used by * the polling logic in sys/mod.rs */ use std::{ cell::RefCell, collections::BinaryHeap, rc::Rc, task::Waker, time::{Duration, Instant}, }; use crate::{EventSource, LoopHandle, Poll, PostAction, Readiness, Token, TokenFactory}; #[derive(Debug)] struct Registration { token: Token, wheel: Rc>, 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: Instant, } impl Timer { /// Create a timer that will fire immediately when inserted in the event loop pub fn immediate() -> Timer { Self::from_deadline(Instant::now()) } /// Create a timer that will fire after a given duration from now pub fn from_duration(duration: Duration) -> Timer { Self::from_deadline(Instant::now() + duration) } /// Create a timer that will fire at a given instant pub fn from_deadline(deadline: Instant) -> Timer { Timer { registration: None, deadline, } } /// Changes the deadline of this timer to an [`Instant`] /// /// If the `Timer` is currently registered in the event loop, it needs to be /// re-registered for this change to take effect. pub fn set_deadline(&mut self, deadline: Instant) { self.deadline = deadline; } /// Changes the deadline of this timer to a [`Duration`] from now /// /// If the `Timer` is currently registered in the event loop, it needs to be /// re-registered for this change to take effect. pub fn set_duration(&mut self, duration: Duration) { self.set_deadline(Instant::now() + duration) } /// Get the current deadline of this `Timer` pub fn current_deadline(&self) -> Instant { self.deadline } } impl EventSource for Timer { type Event = Instant; type Metadata = (); type Ret = TimeoutAction; type Error = std::io::Error; fn process_events( &mut self, _: Readiness, token: Token, mut callback: F, ) -> Result where F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, { if let Some(ref registration) = self.registration { if registration.token != token { return Ok(PostAction::Continue); } let new_deadline = match callback(self.deadline, &mut ()) { TimeoutAction::Drop => return Ok(PostAction::Remove), TimeoutAction::ToInstant(instant) => instant, TimeoutAction::ToDuration(duration) => Instant::now() + duration, }; // If we received an event, we MUST have a valid counter value registration.wheel.borrow_mut().insert_reuse( registration.counter, new_deadline, registration.token, ); self.deadline = new_deadline; } Ok(PostAction::Continue) } fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> { let wheel = poll.timers.clone(); let token = token_factory.token(); let counter = wheel.borrow_mut().insert(self.deadline, token); self.registration = Some(Registration { token, wheel, counter, }); Ok(()) } fn reregister( &mut self, poll: &mut Poll, token_factory: &mut TokenFactory, ) -> crate::Result<()> { self.unregister(poll)?; self.register(poll, token_factory) } fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> { if let Some(registration) = self.registration.take() { poll.timers.borrow_mut().cancel(registration.counter); } Ok(()) } } /// Action to reschedule a timeout if necessary #[derive(Debug)] pub enum TimeoutAction { /// Don't reschedule this timer Drop, /// Reschedule this timer to a given [`Instant`] ToInstant(Instant), /// Reschedule this timer to a given [`Duration`] in the future ToDuration(Duration), } // Internal representation of a timeout registered in the TimerWheel #[derive(Debug)] struct TimeoutData { deadline: Instant, token: RefCell>, 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(coverage, no_coverage)] fn eq(&self, other: &Self) -> bool { self.deadline == other.deadline } } impl std::cmp::Eq for TimeoutData {} // Logic for timer futures /// A future that resolves once a certain timeout is expired pub struct TimeoutFuture { deadline: Instant, waker: Rc>>, } impl std::fmt::Debug for TimeoutFuture { #[cfg_attr(coverage, no_coverage)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TimeoutFuture") .field("deadline", &self.deadline) .finish_non_exhaustive() } } impl TimeoutFuture { /// Create a future that resolves after a given duration pub fn from_duration(handle: &LoopHandle<'_, Data>, duration: Duration) -> TimeoutFuture { Self::from_deadline(handle, Instant::now() + duration) } /// Create a future that resolves at a given instant pub fn from_deadline(handle: &LoopHandle<'_, Data>, deadline: Instant) -> TimeoutFuture { let timer = Timer::from_deadline(deadline); let waker = Rc::new(RefCell::new(None::)); let waker2 = waker.clone(); handle .insert_source(timer, move |_, &mut (), _| { if let Some(waker) = waker2.borrow_mut().clone() { waker.wake() } TimeoutAction::Drop }) .unwrap(); TimeoutFuture { deadline, waker } } } impl std::future::Future for TimeoutFuture { type Output = (); fn poll( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll { if std::time::Instant::now() >= self.deadline { return std::task::Poll::Ready(()); } *self.waker.borrow_mut() = Some(cx.waker().clone()); std::task::Poll::Pending } } #[cfg(test)] mod tests { use super::*; use crate::*; use std::time::Duration; #[test] fn simple_timer() { let mut event_loop = EventLoop::try_new().unwrap(); let mut dispatched = false; event_loop .handle() .insert_source( Timer::from_duration(Duration::from_millis(100)), |_, &mut (), dispatched| { *dispatched = true; TimeoutAction::Drop }, ) .unwrap(); event_loop .dispatch(Some(Duration::ZERO), &mut dispatched) .unwrap(); // not yet dispatched assert!(!dispatched); event_loop .dispatch(Some(Duration::from_millis(150)), &mut dispatched) .unwrap(); // now dispatched assert!(dispatched); } #[test] fn simple_timer_instant() { let mut event_loop = EventLoop::try_new().unwrap(); let mut dispatched = false; event_loop .handle() .insert_source( Timer::from_duration(Duration::from_millis(100)), |_, &mut (), dispatched| { *dispatched = true; TimeoutAction::Drop }, ) .unwrap(); event_loop .dispatch(Some(Duration::from_millis(150)), &mut dispatched) .unwrap(); // now dispatched assert!(dispatched); } #[test] fn immediate_timer() { let mut event_loop = EventLoop::try_new().unwrap(); let mut dispatched = false; event_loop .handle() .insert_source(Timer::immediate(), |_, &mut (), dispatched| { *dispatched = true; TimeoutAction::Drop }) .unwrap(); event_loop .dispatch(Some(Duration::ZERO), &mut dispatched) .unwrap(); // now dispatched assert!(dispatched); } // We cannot actually test high precision timers, as they are only high precision in release mode // This test is here to ensure that the high-precision codepath are executed and work as intended // even if we cannot test if they are actually high precision #[test] fn high_precision_timer() { let mut event_loop = EventLoop::try_new_high_precision().unwrap(); let mut dispatched = false; event_loop .handle() .insert_source( Timer::from_duration(Duration::from_millis(100)), |_, &mut (), dispatched| { *dispatched = true; TimeoutAction::Drop }, ) .unwrap(); event_loop .dispatch(Some(Duration::ZERO), &mut dispatched) .unwrap(); // not yet dispatched assert!(!dispatched); event_loop .dispatch(Some(Duration::from_micros(10200)), &mut dispatched) .unwrap(); // yet not dispatched assert!(!dispatched); event_loop .dispatch(Some(Duration::from_millis(100)), &mut dispatched) .unwrap(); // now dispatched assert!(dispatched); } #[test] fn cancel_timer() { let mut event_loop = EventLoop::try_new().unwrap(); let mut dispatched = false; let token = event_loop .handle() .insert_source( Timer::from_duration(Duration::from_millis(100)), |_, &mut (), dispatched| { *dispatched = true; TimeoutAction::Drop }, ) .unwrap(); event_loop .dispatch(Some(Duration::ZERO), &mut dispatched) .unwrap(); // not yet dispatched assert!(!dispatched); event_loop.handle().remove(token); event_loop .dispatch(Some(Duration::from_millis(150)), &mut dispatched) .unwrap(); // still not dispatched assert!(!dispatched); } #[test] fn repeating_timer() { let mut event_loop = EventLoop::try_new().unwrap(); let mut dispatched = 0; event_loop .handle() .insert_source( Timer::from_duration(Duration::from_millis(500)), |_, &mut (), dispatched| { *dispatched += 1; TimeoutAction::ToDuration(Duration::from_millis(500)) }, ) .unwrap(); event_loop .dispatch(Some(Duration::from_millis(250)), &mut dispatched) .unwrap(); assert_eq!(dispatched, 0); event_loop .dispatch(Some(Duration::from_millis(510)), &mut dispatched) .unwrap(); assert_eq!(dispatched, 1); event_loop .dispatch(Some(Duration::from_millis(510)), &mut dispatched) .unwrap(); assert_eq!(dispatched, 2); event_loop .dispatch(Some(Duration::from_millis(510)), &mut dispatched) .unwrap(); assert_eq!(dispatched, 3); } #[cfg(feature = "executor")] #[test] fn timeout_future() { let mut event_loop = EventLoop::try_new().unwrap(); let mut dispatched = 0; let timeout_1 = TimeoutFuture::from_duration(&event_loop.handle(), Duration::from_millis(500)); let timeout_2 = TimeoutFuture::from_duration(&event_loop.handle(), Duration::from_millis(1500)); let (exec, sched) = crate::sources::futures::executor().unwrap(); event_loop .handle() .insert_source(exec, move |(), &mut (), got| { *got += 1; }) .unwrap(); sched.schedule(timeout_1).unwrap(); sched.schedule(timeout_2).unwrap(); // We do a 0-timeout dispatch after every regular dispatch to let the timeout triggers // flow back to the executor event_loop .dispatch(Some(Duration::ZERO), &mut dispatched) .unwrap(); event_loop .dispatch(Some(Duration::ZERO), &mut dispatched) .unwrap(); assert_eq!(dispatched, 0); event_loop .dispatch(Some(Duration::from_millis(1000)), &mut dispatched) .unwrap(); event_loop .dispatch(Some(Duration::ZERO), &mut dispatched) .unwrap(); assert_eq!(dispatched, 1); event_loop .dispatch(Some(Duration::from_millis(1100)), &mut dispatched) .unwrap(); event_loop .dispatch(Some(Duration::ZERO), &mut dispatched) .unwrap(); assert_eq!(dispatched, 2); } } calloop-0.10.5/src/sources/transient.rs000064400000000000000000000543271046102023000162020ustar 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(); /// ``` /// /// `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. #[derive(Debug)] pub enum TransientSource { /// The source should be kept in the loop. Keep(T), /// The source needs to be registered with the loop. Register(T), /// The source needs to be disabled but kept. Disable(T), /// The source needs to be removed from the loop. Remove(T), /// The source has been removed from the loop and dropped (this might also /// be observed if there is a panic while changing states). None, } impl TransientSource { /// Apply a function to the enclosed source, if it exists. It will be /// appplied even if the source is ready to be removed or is disabled. pub fn map(&mut self, f: F) -> Option where F: FnOnce(&mut T) -> U, { match self { TransientSource::Keep(source) | TransientSource::Register(source) | TransientSource::Disable(source) | TransientSource::Remove(source) => Some(f(source)), TransientSource::None => None, } } /// If a caller needs to flag the contained source for removal or /// registration, we need to replace the enum variant safely. This requires /// having a `None` value in there temporarily while we do the swap. /// /// If the variant is `None` the value will not change and `replacer` will /// not be called. /// /// The `replacer` function here is expected to be one of the enum variant /// constructors eg. `replace(TransientSource::Remove)`. fn replace(&mut self, replacer: F) where F: FnOnce(T) -> Self, { *self = match std::mem::replace(self, TransientSource::None) { TransientSource::Keep(source) | TransientSource::Register(source) | TransientSource::Remove(source) | TransientSource::Disable(source) => replacer(source), TransientSource::None => return, }; } } impl From for TransientSource { fn from(source: T) -> Self { Self::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 TransientSource::Keep(ref mut source) = self { let child_post_action = source.process_events(readiness, token, callback)?; match child_post_action { // Nothing needs to change. crate::PostAction::Continue => false, // Our child source needs re-registration, therefore this // wrapper needs re-registration. crate::PostAction::Reregister => true, // If our nested source needs to be removed or disabled, we need // to swap it out for the "Remove" or "Disable" variant. crate::PostAction::Disable => { self.replace(TransientSource::Disable); true } crate::PostAction::Remove => { self.replace(TransientSource::Remove); true } } } else { false }; let post_action = if reregister { crate::PostAction::Reregister } else { crate::PostAction::Continue }; Ok(post_action) } fn register( &mut self, poll: &mut crate::Poll, token_factory: &mut crate::TokenFactory, ) -> crate::Result<()> { match self { TransientSource::Keep(source) => { source.register(poll, token_factory)?; } TransientSource::Register(source) | TransientSource::Disable(source) => { source.register(poll, token_factory)?; self.replace(TransientSource::Keep); } TransientSource::Remove(_source) => { *self = TransientSource::None; } TransientSource::None => (), } Ok(()) } fn reregister( &mut self, poll: &mut crate::Poll, token_factory: &mut crate::TokenFactory, ) -> crate::Result<()> { match self { TransientSource::Keep(source) => source.reregister(poll, token_factory)?, TransientSource::Register(source) => { source.register(poll, token_factory)?; self.replace(TransientSource::Keep); } TransientSource::Disable(source) => { source.unregister(poll)?; } TransientSource::Remove(source) => { source.unregister(poll)?; *self = TransientSource::None; } TransientSource::None => (), } Ok(()) } fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> { match self { TransientSource::Keep(source) | TransientSource::Register(source) | TransientSource::Disable(source) => source.unregister(poll)?, TransientSource::Remove(source) => { source.unregister(poll)?; *self = TransientSource::None; } TransientSource::None => (), } Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::{ channel::{channel, Event}, ping::{make_ping, PingSource}, Dispatcher, EventSource, PostAction, }; use std::{ sync::atomic::{AtomicBool, Ordering}, time::Duration, }; #[test] fn test_transient_drop() { // A test source that sets a flag when it's dropped. struct TestSource<'a> { dropped: &'a AtomicBool, ping: PingSource, } impl<'a> Drop for TestSource<'a> { fn drop(&mut self) { self.dropped.store(true, Ordering::Relaxed) } } impl<'a> crate::EventSource for TestSource<'a> { type Event = (); type Metadata = (); type Ret = (); type Error = Box; 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); } } calloop-0.10.5/src/sys/epoll.rs000064400000000000000000000134421046102023000144320ustar 00000000000000use std::os::unix::io::{AsRawFd, RawFd}; use super::{Interest, Mode, PollEvent, Readiness, Token}; use nix::sys::{ epoll::{ epoll_create1, epoll_ctl, epoll_wait, EpollCreateFlags, EpollEvent, EpollFlags, EpollOp, }, time::TimeSpec, timerfd::{ClockId, Expiration, TimerFd, TimerFlags, TimerSetTimeFlags}, }; pub struct Epoll { epoll_fd: RawFd, timer_fd: Option, } const TIMER_DATA: u64 = u64::MAX; fn make_flags(interest: Interest, mode: Mode) -> EpollFlags { let mut flags = EpollFlags::empty(); if interest.readable { flags |= EpollFlags::EPOLLIN; } if interest.writable { flags |= EpollFlags::EPOLLOUT; } match mode { Mode::Level => { /* This is the default */ } Mode::Edge => flags |= EpollFlags::EPOLLET, Mode::OneShot => flags |= EpollFlags::EPOLLONESHOT, } flags } fn flags_to_readiness(flags: EpollFlags) -> Readiness { Readiness { readable: flags.contains(EpollFlags::EPOLLIN), writable: flags.contains(EpollFlags::EPOLLOUT), error: flags.contains(EpollFlags::EPOLLERR), } } impl Epoll { pub(crate) fn new(high_precision: bool) -> crate::Result { let epoll_fd = epoll_create1(EpollCreateFlags::EPOLL_CLOEXEC)?; let mut timer_fd = None; if high_precision { // Prepare a timerfd for precise time tracking and register it to the event queue // This timerfd allows for nanosecond precision in setting the timout up (though in practice // we rather get ~10 microsecond precision), while epoll_wait() API only allows millisecond // granularity let timer = TimerFd::new( ClockId::CLOCK_MONOTONIC, TimerFlags::TFD_CLOEXEC | TimerFlags::TFD_NONBLOCK, )?; let mut timer_event = EpollEvent::new(EpollFlags::EPOLLIN, TIMER_DATA); epoll_ctl( epoll_fd, EpollOp::EpollCtlAdd, timer.as_raw_fd(), &mut timer_event, )?; timer_fd = Some(timer); } Ok(Epoll { epoll_fd, timer_fd }) } pub(crate) fn poll( &mut self, timeout: Option, ) -> crate::Result> { let mut buffer = [EpollEvent::empty(); 32]; if let Some(ref timer) = self.timer_fd { if let Some(timeout) = timeout { // Set up the precise timer timer.set( Expiration::OneShot(TimeSpec::from_duration(timeout)), TimerSetTimeFlags::empty(), )?; } } // add 1 to the millisecond wait, to round up for timer tracking. If the high precision timer is set up // it'll fire before that timeout let timeout = timeout.map(|d| (d.as_millis() + 1) as isize).unwrap_or(-1); let n_ready = epoll_wait(self.epoll_fd, &mut buffer, timeout)?; let events = buffer .iter() .take(n_ready) .flat_map(|event| { if event.data() == TIMER_DATA { // We woke up because the high-precision timer fired, we need to disarm it by reading its // contents to ensure it will be ready for next time // Timer is created in non-blocking mode, and should have already fired anyway, this // cannot possibly block let _ = self .timer_fd .as_ref() .expect("Got an event from high-precision timer while it is not set up?!") .wait(); // don't forward this event to downstream None } else { // In C, the underlying data type is a union including a void // pointer; in Rust's FFI bindings, it only exposes the u64. The // round-trip conversion is valid however. let token_ptr = event.data() as usize as *const Token; Some(PollEvent { readiness: flags_to_readiness(event.events()), // Why this is safe: it points to memory boxed and owned by // the parent Poller type. token: unsafe { *token_ptr }, }) } }) .collect(); if let Some(ref timer) = self.timer_fd { // in all cases, disarm the timer timer.unset()?; // clear the timer in case it fired between epoll_wait and now, as timer is in // non-blocking mode, this will return Err(WouldBlock) if it had not fired, so // we ignore the error let _ = timer.wait(); } Ok(events) } pub fn register( &mut self, fd: RawFd, interest: Interest, mode: Mode, token: *const Token, ) -> crate::Result<()> { let mut event = EpollEvent::new(make_flags(interest, mode), token as usize as u64); epoll_ctl(self.epoll_fd, EpollOp::EpollCtlAdd, fd, &mut event).map_err(Into::into) } pub fn reregister( &mut self, fd: RawFd, interest: Interest, mode: Mode, token: *const Token, ) -> crate::Result<()> { let mut event = EpollEvent::new(make_flags(interest, mode), token as usize as u64); epoll_ctl(self.epoll_fd, EpollOp::EpollCtlMod, fd, &mut event).map_err(Into::into) } pub fn unregister(&mut self, fd: RawFd) -> crate::Result<()> { epoll_ctl(self.epoll_fd, EpollOp::EpollCtlDel, fd, None).map_err(Into::into) } } impl Drop for Epoll { fn drop(&mut self) { let _ = nix::unistd::close(self.epoll_fd); } } calloop-0.10.5/src/sys/kqueue.rs000064400000000000000000000143051046102023000146150ustar 00000000000000use std::{io, os::unix::io::RawFd}; use nix::{ libc::{c_long, time_t, timespec}, sys::event::{kevent_ts, kqueue, EventFilter, EventFlag, FilterFlag, KEvent}, }; use super::{Interest, Mode, PollEvent, Readiness, Token}; pub struct Kqueue { kq: RawFd, } fn mode_to_flag(mode: Mode) -> EventFlag { match mode { Mode::Level => EventFlag::empty(), Mode::OneShot => EventFlag::EV_DISPATCH, Mode::Edge => EventFlag::EV_CLEAR, } } impl Kqueue { // Kqueue is always high precision pub(crate) fn new(_high_precision: bool) -> crate::Result { let kq = kqueue()?; Ok(Kqueue { kq }) } pub(crate) fn poll( &mut self, timeout: Option, ) -> crate::Result> { let mut buffer = [KEvent::new( 0, EventFilter::EVFILT_READ, EventFlag::empty(), FilterFlag::empty(), 0, 0, ); 32]; let nevents = kevent_ts( self.kq, &[], &mut buffer, timeout.map(|d| timespec { tv_sec: d.as_secs() as time_t, tv_nsec: d.subsec_nanos() as c_long, }), )?; let ret = buffer .iter() .take(nevents) .map(|event| { // The kevent data field in Rust's libc FFI bindings is an // intptr_t, which is specified to allow this kind of // conversion. let token_ptr = event.udata() as usize as *const Token; PollEvent { readiness: Readiness { readable: event.filter() == Ok(EventFilter::EVFILT_READ), writable: event.filter() == Ok(EventFilter::EVFILT_WRITE), error: event.flags().contains(EventFlag::EV_ERROR) && event.data() != 0, }, // Why this is safe: it points to memory boxed and owned by // the parent Poller type. token: unsafe { *token_ptr }, } }) .collect(); Ok(ret) } pub fn register( &mut self, fd: RawFd, interest: Interest, mode: Mode, token: *const Token, ) -> crate::Result<()> { self.reregister(fd, interest, mode, token) } pub fn reregister( &mut self, fd: RawFd, interest: Interest, mode: Mode, token: *const Token, ) -> crate::Result<()> { let write_flags = if interest.writable { EventFlag::EV_ADD | EventFlag::EV_RECEIPT | mode_to_flag(mode) } else { EventFlag::EV_DELETE }; let read_flags = if interest.readable { EventFlag::EV_ADD | EventFlag::EV_RECEIPT | mode_to_flag(mode) } else { EventFlag::EV_DELETE }; let changes = [ KEvent::new( fd as usize, EventFilter::EVFILT_WRITE, write_flags, FilterFlag::empty(), 0, token as usize as isize, ), KEvent::new( fd as usize, EventFilter::EVFILT_READ, read_flags, FilterFlag::empty(), 0, token as usize as isize, ), ]; let mut out = [ KEvent::new( 0, EventFilter::EVFILT_WRITE, EventFlag::empty(), FilterFlag::empty(), 0, 0, ), KEvent::new( 0, EventFilter::EVFILT_READ, EventFlag::empty(), FilterFlag::empty(), 0, 0, ), ]; kevent_ts(self.kq, &changes, &mut out, None)?; for o in &out { if o.flags().contains(EventFlag::EV_ERROR) && o.data() != 0 { let e = io::Error::from_raw_os_error(o.data() as i32); // ignore NotFound error which is raised if we tried to remove a non-existent filter if e.kind() != io::ErrorKind::NotFound { return Err(e.into()); } } } Ok(()) } pub fn unregister(&mut self, fd: RawFd) -> crate::Result<()> { let changes = [ KEvent::new( fd as usize, EventFilter::EVFILT_WRITE, EventFlag::EV_DELETE, FilterFlag::empty(), 0, 0, ), KEvent::new( fd as usize, EventFilter::EVFILT_READ, EventFlag::EV_DELETE, FilterFlag::empty(), 0, 0, ), ]; let mut out = [ KEvent::new( 0, EventFilter::EVFILT_WRITE, EventFlag::empty(), FilterFlag::empty(), 0, 0, ), KEvent::new( 0, EventFilter::EVFILT_READ, EventFlag::empty(), FilterFlag::empty(), 0, 0, ), ]; kevent_ts(self.kq, &changes, &mut out, None)?; // Report an error if *both* fd were missing, meaning we were not registered at all let mut notfound = 0; for o in &out { if o.flags().contains(EventFlag::EV_ERROR) && o.data() != 0 { let e = io::Error::from_raw_os_error(o.data() as i32); // ignore NotFound error which is raised if we tried to remove a non-existent filter if e.kind() != io::ErrorKind::NotFound { return Err(e.into()); } else { notfound += 1; } } } if notfound == 2 { return Err(std::io::Error::from(io::ErrorKind::NotFound).into()); } Ok(()) } } impl Drop for Kqueue { fn drop(&mut self) { let _ = nix::unistd::close(self.kq); } } calloop-0.10.5/src/sys/mod.rs000064400000000000000000000314741046102023000141030ustar 00000000000000use std::{cell::RefCell, convert::TryInto, os::unix::io::RawFd, rc::Rc, time::Duration}; use vec_map::VecMap; use crate::{loop_logic::CalloopKey, sources::timer::TimerWheel}; #[cfg(any(target_os = "linux", target_os = "android"))] mod epoll; #[cfg(any(target_os = "linux", target_os = "android"))] use epoll::Epoll as Poller; #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "macos" ))] mod kqueue; #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "macos" ))] use kqueue::Kqueue as Poller; /// Possible modes for registering a file descriptor #[derive(Copy, Clone, Debug)] pub enum Mode { /// Single event generation /// /// This FD will be disabled as soon as it has generated one event. /// /// The user will need to use `LoopHandle::update()` to re-enable it if /// desired. OneShot, /// Level-triggering /// /// This FD will report events on every poll as long as the requested interests /// are available. If the same FD is inserted in multiple event loops, all of /// them are notified of readiness. Level, /// Edge-triggering /// /// This FD will report events only when it *gains* one of the requested interests. /// it must thus be fully processed before it'll generate events again. If the same /// FD is inserted on multiple event loops, it may be that not all of them are notified /// of readiness, and not necessarily always the same(s) (at least one is notified). Edge, } /// Interest to register regarding the file descriptor #[derive(Copy, Clone, Debug)] pub struct Interest { /// Wait for the FD to be readable pub readable: bool, /// Wait for the FD to be writable pub writable: bool, } impl Interest { /// Shorthand for empty interest pub const EMPTY: Interest = Interest { readable: false, writable: false, }; /// Shorthand for read interest pub const READ: Interest = Interest { readable: true, writable: false, }; /// Shorthand for write interest pub const WRITE: Interest = Interest { readable: false, writable: true, }; /// Shorthand for read and write interest pub const BOTH: Interest = Interest { readable: true, writable: true, }; } /// Readiness for a file descriptor notification #[derive(Copy, Clone, Debug)] pub struct Readiness { /// Is the FD readable pub readable: bool, /// Is the FD writable pub writable: bool, /// Is the FD in an error state pub error: bool, } impl Readiness { /// Shorthand for empty readiness pub const EMPTY: Readiness = Readiness { readable: false, writable: false, error: false, }; } #[derive(Debug)] pub(crate) struct PollEvent { pub(crate) readiness: Readiness, pub(crate) token: Token, } /// Factory for creating tokens in your registrations /// /// When composing event sources, each sub-source needs to /// have its own token to identify itself. This factory is /// provided to produce such unique tokens. #[derive(Debug)] pub struct TokenFactory { key: CalloopKey, sub_id: u32, } impl TokenFactory { pub(crate) fn new(key: CalloopKey) -> TokenFactory { TokenFactory { key, sub_id: 0 } } /// Produce a new unique token pub fn token(&mut self) -> Token { let token = Token { key: self.key, sub_id: self.sub_id, }; self.sub_id += 1; token } } /// A token (for implementation of the [`EventSource`](crate::EventSource) trait) /// /// This token is produced by the [`TokenFactory`] and is used when calling the /// [`EventSource`](crate::EventSource) implementations to process event, in order /// to identify which sub-source produced them. /// /// You should forward it to the [`Poll`] when registering your file descriptors. #[derive(Clone, Copy, Debug, PartialEq)] pub struct Token { pub(crate) key: CalloopKey, pub(crate) sub_id: u32, } /// The polling system /// /// This type represents the polling system of calloop, on which you /// can register your file descriptors. This interface is only accessible in /// implementations of the [`EventSource`](crate::EventSource) trait. /// /// You only need to interact with this type if you are implementing your /// own event sources, while implementing the [`EventSource`](crate::EventSource) trait. /// And even in this case, you can often just use the [`Generic`](crate::generic::Generic) event /// source and delegate the implementations to it. pub struct Poll { poller: Poller, // It is essential for safe use of this type that the pointers passed in to // the underlying poller API are properly managed. Each time an event source // is registered, the token it passes in is Boxed and converted to a raw // pointer to be passed to the polling system by FFI. This pointer is what's // stored in the map. When the event source is re- or unregistered, the same // raw pointer can then be converted back into the Box and dropped, safely // deallocating it. To put it another way, we effectively "own" the Token // memory on behalf of the underlying polling mechanism. // // All the platforms we currently support follow the rule that file // descriptors must be "small", positive integers. This means we can use a // VecMap which has that exact constraint for its keys. If that ever // changes, this will need to be changed to a different structure. tokens: VecMap<*mut Token>, pub(crate) timers: Rc>, } impl std::fmt::Debug for Poll { #[cfg_attr(coverage, no_coverage)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("Poll { ... }") } } impl Poll { pub(crate) fn new(high_precision: bool) -> crate::Result { Ok(Poll { poller: Poller::new(high_precision)?, tokens: VecMap::new(), timers: Rc::new(RefCell::new(TimerWheel::new())), }) } pub(crate) fn poll( &mut self, mut timeout: Option, ) -> 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.poller.poll(timeout)?; // Update 'now' as some time may have elapsed in poll() let now = std::time::Instant::now(); let mut timers = self.timers.borrow_mut(); while let Some((_, token)) = timers.next_expired(now) { events.push(PollEvent { readiness: Readiness { readable: true, writable: false, error: false, }, token, }); } Ok(events) } /// Register a new file descriptor for polling /// /// The file descriptor will be registered with given interest, /// mode and token. This function will fail if given a /// bad file descriptor or if the provided file descriptor is already /// registered. /// /// # Leaking tokens /// /// If your event source is dropped without being unregistered, the token /// passed in here will remain on the heap and continue to be used by the /// polling system even though no event source will match it. pub fn register( &mut self, fd: RawFd, interest: Interest, mode: Mode, token: Token, ) -> crate::Result<()> { let token_box = Box::new(token); let token_ptr = Box::into_raw(token_box); let registration_result = self.poller.register(fd, interest, mode, token_ptr); if registration_result.is_err() { // If registration did not work, do not add the file descriptor to // the token map. Instead, reconstruct the Box and drop it. This is // safe because it's from Box::into_raw() above. let token_box = unsafe { Box::from_raw(token_ptr) }; std::mem::drop(token_box); } else { // Registration worked, keep the token pointer until it's replaced // or removed. let index = index_from_fd(fd); if self.tokens.insert(index, token_ptr).is_some() { // If there is already a file descriptor associated with a // token, then replacing that entry will leak the token, but // converting it back into a Box might leave a dangling pointer // somewhere. We can theoretically continue safely by choosing // to leak, but one of our assumptions is no longer valid, so // panic. panic!("File descriptor ({}) already registered", fd); } } registration_result } /// Update the registration for a file descriptor /// /// This allows you to change the interest, mode or token of a file /// descriptor. Fails if the provided fd is not currently registered. /// /// See note on [`register()`](Self::register()) regarding leaking. pub fn reregister( &mut self, fd: RawFd, interest: Interest, mode: Mode, token: Token, ) -> crate::Result<()> { let token_box = Box::new(token); let token_ptr = Box::into_raw(token_box); let reregistration_result = self.poller.reregister(fd, interest, mode, token_ptr); if reregistration_result.is_err() { // If registration did not work, do not add the file descriptor to // the token map. Instead, reconstruct the Box and drop it. This is // safe because it's from Box::into_raw() above. let token_box = unsafe { Box::from_raw(token_ptr) }; std::mem::drop(token_box); } else { // Registration worked, drop the old token memory and keep the new // token pointer until it's replaced or removed. let index = index_from_fd(fd); if let Some(previous) = self.tokens.insert(index, token_ptr) { // This is safe because it's from Box::into_raw() from a // previous (re-)register() call. let token_box = unsafe { Box::from_raw(previous) }; std::mem::drop(token_box); } else { // If there is no previous token registered for this file // descriptor, either the event source has wrongly called // reregister() without first being registered, or the // underlying poller has a dangling pointer. In the first case, // the reregistration should have failed; in the second case, we // cannot safely proceed. panic!("File descriptor ({}) had no previous registration", fd); } } reregistration_result } /// Unregister a file descriptor /// /// This file descriptor will no longer generate events. Fails if the /// provided file descriptor is not currently registered. pub fn unregister(&mut self, fd: RawFd) -> crate::Result<()> { let unregistration_result = self.poller.unregister(fd); if unregistration_result.is_ok() { // The source was unregistered, we can remove the old token data. let index = index_from_fd(fd); if let Some(previous) = self.tokens.remove(index) { // This is safe because it's from Box::into_raw() from a // previous (re-)register() call. let token_box = unsafe { Box::from_raw(previous) }; std::mem::drop(token_box); } else { // If there is no previous token registered for this file // descriptor, either the event source has wrongly called // unregister() without first being registered, or the // underlying poller has a dangling pointer. In the first case, // the reregistration should have failed; in the second case, we // cannot safely proceed. panic!("File descriptor ({}) had no previous registration", fd); } } unregistration_result } } /// Converts a file descriptor into an index for the token map. Panics if the /// file descriptor is negative. fn index_from_fd(fd: RawFd) -> usize { fd.try_into() .unwrap_or_else(|_| panic!("File descriptor ({}) is invalid", fd)) } calloop-0.10.5/tests/signals.rs000064400000000000000000000076401046102023000145170ustar 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(target_os = "linux")] fn main() { for test in self::test::TESTS { test(); // reset the signal mask between tests self::test::reset_mask(); } } #[cfg(not(target_os = "linux"))] fn main() {} #[cfg(target_os = "linux")] mod test { extern crate calloop; extern crate nix; use std::time::Duration; use self::calloop::signals::{Signal, Signals}; use self::calloop::{Dispatcher, EventLoop}; use self::nix::sys::signal::{kill, SigSet}; use self::nix::unistd::Pid; pub const TESTS: &[fn()] = &[single_usr1, usr2_added_afterwards, usr2_signal_removed]; pub fn reset_mask() { SigSet::empty().thread_set_mask().unwrap(); } fn single_usr1() { let mut event_loop = EventLoop::try_new().unwrap(); let mut signal_received = false; let _signal_source = event_loop .handle() .insert_source( Signals::new(&[Signal::SIGUSR1]).unwrap(), move |evt, &mut (), rcv| { assert!(evt.signal() == Signal::SIGUSR1); *rcv = true; }, ) .unwrap(); // send ourselves a SIGUSR1 kill(Pid::this(), Signal::SIGUSR1).unwrap(); event_loop .dispatch(Some(Duration::from_millis(10)), &mut signal_received) .unwrap(); assert!(signal_received); } fn usr2_added_afterwards() { let mut event_loop = EventLoop::try_new().unwrap(); let mut signal_received = None; let dispatcher = Dispatcher::new( Signals::new(&[Signal::SIGUSR1]).unwrap(), move |evt, &mut (), rcv| { *rcv = Some(evt.signal()); }, ); let _signal_token = event_loop .handle() .register_dispatcher(dispatcher.clone()) .unwrap(); dispatcher .as_source_mut() .add_signals(&[Signal::SIGUSR2]) .unwrap(); // send ourselves a SIGUSR2 kill(Pid::this(), Signal::SIGUSR2).unwrap(); event_loop .dispatch(Some(Duration::from_millis(10)), &mut signal_received) .unwrap(); assert_eq!(signal_received, Some(Signal::SIGUSR2)); } fn usr2_signal_removed() { let mut event_loop = EventLoop::try_new().unwrap(); let mut signal_received = None; let dispatcher = Dispatcher::new( Signals::new(&[Signal::SIGUSR1, Signal::SIGUSR2]).unwrap(), move |evt, &mut (), rcv| { *rcv = Some(evt.signal()); }, ); let _signal_token = event_loop .handle() .register_dispatcher(dispatcher.clone()) .unwrap(); dispatcher .as_source_mut() .remove_signals(&[Signal::SIGUSR2]) .unwrap(); // block sigusr2 anyway, to not be killed by it let mut set = SigSet::empty(); set.add(Signal::SIGUSR2); set.thread_block().unwrap(); // send ourselves a SIGUSR2 kill(Pid::this(), Signal::SIGUSR2).unwrap(); event_loop .dispatch(Some(Duration::from_millis(10)), &mut signal_received) .unwrap(); // we should not have received anything, as we don't listen to SIGUSR2 any more assert!(signal_received.is_none()); // swap the signals from [SIGUSR1] to [SIGUSR2] dispatcher .as_source_mut() .set_signals(&[Signal::SIGUSR2]) .unwrap(); event_loop .dispatch(Some(Duration::from_millis(10)), &mut signal_received) .unwrap(); // we should get back the pending SIGUSR2 now assert_eq!(signal_received, Some(Signal::SIGUSR2)); } }