Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
@@ -0,0 +1 @@
{"$comment":"This file only protects against accidental modifications. It is not a security mechanism and does not protect against malicious changes.","files":{".cargo_vcs_info.json":"7215e75d675466d6a3552bd74d237c7a02d67322005d482e8ee2d7fee5575512",".rustfmt.toml":"ad860a6eb3241ca19b7d0fe82a055b42998f5513883fddd889266f2bd2117dff",".travis.yml":"aba459e159744c4430f57b7de2d6b8fc85ee9fd2c1db28e48a3b63409c1c31ff","Cargo.lock":"6621858e11b23b6732b5c1e98fec64dfb2fe3f08e4a210e40c2e394c50f11238","Cargo.toml":"633ef8199b2f1f2a4406ec2cc2858d36ae1936a82cea0e09c5847c8d4527dc9c","Cargo.toml.orig":"f7f4dc5ff23a865f67da480ce35913afe47515ddc74c98ab8bb95fbf0686dde2","LICENSE-APACHE":"4618209e13998293f82505724827ea83d927070af1c038b11e7a2ff6fd12d911","LICENSE-MIT":"161c4bcb09e94e97cf5d917129bbd78574ca2238704d602465cd54ebb9e570d6","benches/bench_mutex.sh":"8a38aad4b38ac53146f3027af22e11dfdca23b3de1f253a37216af664b2b759d","benches/mpmc_channel.rs":"3ac84661034d830f70e5912c4c31456a884b763a86ecea301c0e8a340d55aa52","benches/mutex.rs":"aa3e4469157a7be2c2ab58a16b41c22cc9bcdd8d2b9e6647db5c5fae666bb10c","benches/semaphore.rs":"3f769411840989c9bfaca801a3afb0627edc4f2c3ea9f27b01e7d8c24ad4a6c7","benches/utils/mod.rs":"a00db13c6f6945a1d1e390e0a35811ac47e355976cf0fff93574da15dfcc4e34","examples/cancellation.rs":"dac769d193a92ef4ddda2c9ab0b454f0b38c9c112121a77dff2070fa61b7b46e","examples/philosophers.rs":"543a4b9380b7b430db4f2db164a2d35f881a1d200a80379f089d9c26ea1654f6","examples/readme.md":"48cff1f35ed9aa8416c44e09e78bd9626e8364a9064e460c0f69e95e00af3e6f","readme.md":"abbeef37d552601ce91f8328c9e34c465297d5334418cd705b6da150d14a62b0","src/buffer/mod.rs":"10c1832b42fb86f6787e5aa994c010e51ba62672d46a1bd910f2060d63d4cd20","src/buffer/real_array.rs":"c0ac5f0684e6679ca3b30b8647e212f7445fc3914214a69f527a20f1bd57eb7d","src/buffer/ring_buffer.rs":"5e829d368e41e4f720578471cd02b43e6d5c8d9eddb1e1480d82235d960d54ad","src/channel/channel_future.rs":"8c71a88a5fc6d8eb78efef89ccfc1c614c39923ec5e391c71c5dcf27c3689f4d","src/channel/error.rs":"43d5a23d7d948e5d6807803d3463e1fc1af52432c3bc47d8d711db25fda537bd","src/channel/mod.rs":"fbc7a7a8e3825e0ecf31458027835167d098b9de4443aff7af95d1e1da2d3136","src/channel/mpmc.rs":"2884e8497c0403953e6f39372b5ecaa7935015bafa06254c2caac63a69e27501","src/channel/oneshot.rs":"a52447f085f8418d1dc1026fb768528d0da2e98a6140e1728689219f6d6c6386","src/channel/oneshot_broadcast.rs":"5405f643084a8f2d7ab5dd2cbea0cb2283e2cf30bf6662965233564446e0d81e","src/channel/state_broadcast.rs":"5feb9fb526427a1a8183d486730ef25dc2eb75c5cf8649af1d3533a14a5fcc60","src/intrusive_double_linked_list.rs":"917dac018ef005ad0c692143d2a3991188a9a4ecd6b0fddd0db91145b9bc824a","src/intrusive_pairing_heap.rs":"dee582e8d1b16e5894733f0f7d19bfa7e5e27bfcf254ce1b088e284af094ba0b","src/lib.rs":"1bddd27bcf6988c954766539b4fe2cd2f99d9b46eb7de0abb8abbf1ab0d13d67","src/noop_lock.rs":"d038bf90e3cb86a3b2cd3a7734bea1b0ae88bde33a9b6a1eb3d86ba391845491","src/sync/manual_reset_event.rs":"36996245e63e8ff050402ab8dd79471e042c32ef82e899075d3c842b9453ee9a","src/sync/mod.rs":"064213746b5f5205c88e5cb6fdb8df8a1898af9f4f693e3f32a0ce4fec3eb45a","src/sync/mutex.rs":"59dee60cd1066a064ac121ed65dc62024ffea14a23427cee9a0b3db5a248c0c0","src/sync/semaphore.rs":"b285256231347ab9eb434a1e473e7687be41c64b007d9a0f446233aa274e0baa","src/timer/clock.rs":"16d95a905146ce00f634615503625638c6f89f5032a64880e1bd1d713a7aaaa3","src/timer/mod.rs":"a3aa6e0f94e54e2d5bef80921e886601114aa09a01093896ad61b265ddc27b27","src/timer/timer.rs":"843ed5912b65bf743974e8eb9f95df3f0126f57594ac919b7dd962490b9b1b99","src/utils/mod.rs":"abbc5ca54d78663090d53fbbb9b9e9d89614ab8481ddd791666d41931b988f79","tests/manual_reset_event.rs":"b972bb26439be2d30f090011451f8598df11a044352eb6ae46b77f4f362f6e8d","tests/mpmc_channel.rs":"47935945afd6a9621cca8c16ae901906fa1f4da3498fc9dc9b4d69688e34bcd7","tests/mutex.rs":"5e0e13a0aa3c96c0d86b973495a38abf82d1c3039929cbfc28cfd7222980af39","tests/oneshot_channel.rs":"01fa47e4418b1795d327b210495628612d6e6b4cf951b430b6801f729064f90a","tests/semaphore.rs":"ca2adad9accc2bef2b091d197f3c93fce904c3a54a74761fa5092352e9f8a15b","tests/state_broadcast_channel.rs":"915236fa9e445d6cc303481f3fa25a50d1cfd6b53524b34827c1ab8b991caaa6","tests/timer.rs":"edf6a9e0d72b91eacd93665847e3191ba81b053cbe5ce55b1f227354b3d18386"},"package":"1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"}
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "0adfe0e6690d95d8c1d958416955b7a69b203655"
},
"path_in_vcs": ""
}
+1
View File
@@ -0,0 +1 @@
max_width = 80
+18
View File
@@ -0,0 +1,18 @@
language: rust
rust:
- stable
env:
- RUST_BACKTRACE=1
cache:
directories:
- /home/travis/.cargo
before_cache:
- cargo cache -r registry
before_script:
- rustup component add rustfmt
- (test -x $HOME/.cargo/bin/cargo-cache || cargo install cargo-cache)
script:
- cargo fmt --all -- --check
- cargo test --no-default-features
- cargo test --no-default-features --features alloc
- cargo test --all-targets --all-features
+1439
View File
File diff suppressed because it is too large Load Diff
+98
View File
@@ -0,0 +1,98 @@
# 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 = "futures-intrusive"
version = "0.5.0"
authors = ["Matthias Einwag <matthias.einwag@live.com>"]
description = """
Futures based on intrusive data structures - for std and no-std environments.
"""
homepage = "https://github.com/Matthias247/futures-intrusive"
license = "MIT OR Apache-2.0"
repository = "https://github.com/Matthias247/futures-intrusive"
[lib]
name = "futures_intrusive"
[[example]]
name = "cancellation"
required-features = ["std"]
[[example]]
name = "philosophers"
required-features = ["std"]
[[bench]]
name = "mpmc_channel"
harness = false
[[bench]]
name = "mutex"
harness = false
[[bench]]
name = "semaphore"
harness = false
[dependencies.futures-core]
version = "^0.3"
default-features = false
[dependencies.lock_api]
version = "0.4.1"
[dependencies.parking_lot]
version = "0.12.0"
optional = true
[dev-dependencies.async-std]
version = "1.4"
[dev-dependencies.criterion]
version = "0.3.0"
[dev-dependencies.crossbeam]
version = "0.7"
[dev-dependencies.futures]
version = "0.3.0"
features = ["async-await"]
default-features = true
[dev-dependencies.futures-test]
version = "0.3.0"
default-features = true
[dev-dependencies.lazy_static]
version = "1.4.0"
[dev-dependencies.pin-utils]
version = "0.1.0"
[dev-dependencies.rand]
version = "0.7"
[dev-dependencies.signal-hook]
version = "0.1.11"
[dev-dependencies.tokio]
version = "1.14"
features = ["full"]
[features]
alloc = ["futures-core/alloc"]
default = ["std"]
std = [
"alloc",
"parking_lot",
]
+56
View File
@@ -0,0 +1,56 @@
[package]
name = "futures-intrusive"
edition = "2018"
version = "0.5.0"
authors = ["Matthias Einwag <matthias.einwag@live.com>"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/Matthias247/futures-intrusive"
homepage = "https://github.com/Matthias247/futures-intrusive"
description = """
Futures based on intrusive data structures - for std and no-std environments.
"""
[lib]
name = "futures_intrusive"
[features]
alloc = ["futures-core/alloc"]
std = ["alloc", "parking_lot"]
default = ["std"]
[dependencies]
futures-core = { version = "^0.3", default-features = false }
lock_api = "0.4.1"
parking_lot = { version = "0.12.0", optional = true }
[dev-dependencies]
futures = { version = "0.3.0", default-features = true, features=["async-await"] }
futures-test = { version = "0.3.0", default-features = true }
pin-utils = "0.1.0"
criterion = "0.3.0"
crossbeam = "0.7" # For channel benchmarks
lazy_static = "1.4.0"
rand = "0.7"
async-std = "1.4" # For benchmarks
tokio = { version = "1.14", features = ["full"] } # For channel benchmarks
signal-hook = "0.1.11" # For cancellation example
[[bench]]
name = "mpmc_channel"
harness = false
[[bench]]
name = "mutex"
harness = false
[[bench]]
name = "semaphore"
harness = false
[[example]]
name = "cancellation"
required-features = ["std"]
[[example]]
name = "philosophers"
required-features = ["std"]
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2019 Matthias Einwag
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+25
View File
@@ -0,0 +1,25 @@
Copyright (c) 2019 Matthias Einwag
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.
@@ -0,0 +1,2 @@
# This is just a convenience script to filter the important facts out of the criterion report
cargo bench --bench mutex | grep -E "cont|time" | grep -v -E "Warming|Analyzing|Benchmarking|Warning"
@@ -0,0 +1,371 @@
use criterion::{
criterion_group, criterion_main, Criterion, ParameterizedBenchmark,
};
use futures::{
executor::block_on, future::join_all, join, sink::SinkExt,
stream::StreamExt, FutureExt,
};
use futures_intrusive::channel::{
shared::channel, shared::unbuffered_channel, LocalChannel,
};
use std::time::Duration;
/// Elements to transfer per producer
const ELEMS_TO_SEND: usize = 1000;
/// Buffer size for buffered channels
const CHANNEL_BUFFER_SIZE: usize = 20;
/// Benchmark for Crossbeam channels
fn crossbeam_channel_variable_tx(producers: usize) {
let elems_per_producer = ELEMS_TO_SEND / producers;
let (tx, rx) = crossbeam::channel::bounded(CHANNEL_BUFFER_SIZE);
for _i in 0..producers {
let tx = tx.clone();
std::thread::spawn(move || {
for _i in 0..elems_per_producer {
tx.send(4).unwrap();
}
});
}
drop(tx);
loop {
let res = rx.recv();
if res.is_err() {
break;
}
}
}
/// variable producers, single consumer
fn futchan_bounded_variable_tx(producers: usize) {
use futures::channel::mpsc::channel;
let elems_per_producer = ELEMS_TO_SEND / producers;
let (tx, mut rx) = channel(CHANNEL_BUFFER_SIZE);
for _i in 0..producers {
let mut tx = tx.clone();
std::thread::spawn(move || {
block_on(async {
for _i in 0..elems_per_producer {
tx.send(4).await.unwrap();
}
});
});
}
drop(tx);
block_on(async {
loop {
let res = rx.next().await;
if res.is_none() {
break;
}
}
});
}
/// variable producers, single consumer
fn tokiochan_bounded_variable_tx(producers: usize) {
let elems_per_producer = ELEMS_TO_SEND / producers;
let (tx, mut rx) = tokio::sync::mpsc::channel(CHANNEL_BUFFER_SIZE);
for _i in 0..producers {
let tx = tx.clone();
std::thread::spawn(move || {
block_on(async {
for _i in 0..elems_per_producer {
tx.send(4).await.unwrap();
}
});
});
}
drop(tx);
block_on(async {
loop {
let res = rx.recv().await;
if res.is_none() {
break;
}
}
});
}
macro_rules! intrusive_channel_variable_tx {
($producers: expr, $channel_constructor: expr) => {
let elems_per_producer = ELEMS_TO_SEND / $producers;
let (tx, rx) = $channel_constructor;
for _i in 0..$producers {
let tx = tx.clone();
std::thread::spawn(move || {
block_on(async {
for _i in 0..elems_per_producer {
let r = tx.send(4).await;
assert!(r.is_ok());
}
});
});
}
drop(tx);
block_on(async {
loop {
let res = rx.receive().await;
if res.is_none() {
break;
}
}
});
};
}
/// variable producers, single consumer
fn intrusivechan_bounded_variable_tx(producers: usize) {
intrusive_channel_variable_tx!(
producers,
channel::<i32>(CHANNEL_BUFFER_SIZE)
);
}
/// variable producers, single consumer
fn intrusivechan_unbuffered_variable_tx(producers: usize) {
intrusive_channel_variable_tx!(producers, unbuffered_channel::<i32>());
}
/// variable producers, single consumer
fn futchan_bounded_variable_tx_single_thread(producers: usize) {
let elems_per_producer = ELEMS_TO_SEND / producers;
block_on(async {
let (tx, mut rx) = futures::channel::mpsc::channel(CHANNEL_BUFFER_SIZE);
let produce_done = join_all((0..producers).into_iter().map(|_| {
let mut tx = tx.clone();
async move {
for _i in 0..elems_per_producer {
tx.send(4).await.unwrap();
}
}
.boxed()
}));
drop(tx);
let consume_done = async {
loop {
let res = rx.next().await;
if res.is_none() {
break;
}
}
};
join!(produce_done, consume_done);
});
}
/// variable producers, single consumer
fn tokiochan_bounded_variable_tx_single_thread(producers: usize) {
let elems_per_producer = ELEMS_TO_SEND / producers;
block_on(async {
let (tx, mut rx) = tokio::sync::mpsc::channel(CHANNEL_BUFFER_SIZE);
let produce_done = join_all((0..producers).into_iter().map(|_| {
let tx = tx.clone();
async move {
for _i in 0..elems_per_producer {
tx.send(4).await.unwrap();
}
}
.boxed()
}));
drop(tx);
let consume_done = async {
loop {
let res = rx.recv().await;
if res.is_none() {
break;
}
}
};
join!(produce_done, consume_done);
});
}
macro_rules! intrusive_channel_variable_tx_single_thread {
($producers: expr, $channel_constructor: expr) => {
let elems_per_producer = ELEMS_TO_SEND / $producers;
block_on(async {
let (tx, rx) = $channel_constructor;
let produce_done =
join_all((0..$producers).into_iter().map(|_| {
let tx = tx.clone();
Box::pin(async move {
for _i in 0..elems_per_producer {
let r = tx.send(4).await;
assert!(r.is_ok());
}
})
}));
drop(tx);
let consume_done = async {
loop {
let res = rx.receive().await;
if res.is_none() {
break;
}
}
};
join!(produce_done, consume_done);
});
};
}
/// variable producers, single consumer
fn intrusivechan_bounded_variable_tx_single_thread(producers: usize) {
intrusive_channel_variable_tx_single_thread!(
producers,
channel::<i32>(CHANNEL_BUFFER_SIZE)
);
}
/// variable producers, single consumer
fn intrusivechan_unbuffered_variable_tx_single_thread(producers: usize) {
intrusive_channel_variable_tx_single_thread!(
producers,
unbuffered_channel::<i32>()
);
}
/// variable producers, single consumer
fn intrusive_local_chan_bounded_variable_tx_single_thread(producers: usize) {
let elems_per_producer = ELEMS_TO_SEND / producers;
block_on(async {
let rx = LocalChannel::<i32, [i32; CHANNEL_BUFFER_SIZE]>::new();
let produce_done = join_all((0..producers).into_iter().map(|_| {
Box::pin(async {
for _i in 0..elems_per_producer {
let r = rx.send(4).await;
assert!(r.is_ok());
}
})
}));
let consume_done = async {
let mut count = 0;
let needed = elems_per_producer * producers;
loop {
let _ = rx.receive().await.unwrap();
// The channel doesn't automatically get closed when producers are
// gone since producer and consumer are the same object type.
// Therefore we need to count receives.
count += 1;
if count == needed {
break;
}
}
};
join!(produce_done, consume_done);
});
}
fn criterion_benchmark(c: &mut Criterion) {
// Producer and consumer are running on the same thread
c.bench(
"Channels (Single Threaded)",
ParameterizedBenchmark::new(
"intrusive local channel with producers",
|b, &&producers| {
b.iter(|| {
intrusive_local_chan_bounded_variable_tx_single_thread(
producers,
)
})
},
&[5, 20, 100],
)
.with_function("intrusive channel with producers", |b, &&producers| {
b.iter(|| {
intrusivechan_bounded_variable_tx_single_thread(producers)
})
})
.with_function(
"intrusive unbuffered channel with producers",
|b, &&producers| {
b.iter(|| {
intrusivechan_unbuffered_variable_tx_single_thread(
producers,
)
})
},
)
.with_function(
"futures::channel::mpsc with producers",
|b, &&producers| {
b.iter(|| futchan_bounded_variable_tx_single_thread(producers))
},
)
.with_function(
"tokio::sync::mpsc with producers",
|b, &&producers| {
b.iter(|| {
tokiochan_bounded_variable_tx_single_thread(producers)
})
},
),
);
// Producer and consume run on a different thread
c.bench(
"Channels (Thread per producer)",
ParameterizedBenchmark::new(
"crossbeam channel with producers",
|b, &&producers| {
b.iter(|| crossbeam_channel_variable_tx(producers))
},
&[5, 20, 100],
)
.with_function("intrusive channel with producers", |b, &&producers| {
b.iter(|| intrusivechan_bounded_variable_tx(producers))
})
.with_function(
"intrusive unbuffered channel with producers",
|b, &&producers| {
b.iter(|| intrusivechan_unbuffered_variable_tx(producers))
},
)
.with_function(
"futures::channel::mpsc with producers",
|b, &&producers| b.iter(|| futchan_bounded_variable_tx(producers)),
)
.with_function(
"tokio::sync::mpsc with producers",
|b, &&producers| {
b.iter(|| tokiochan_bounded_variable_tx(producers))
},
),
);
}
criterion_group! {
name = benches;
config = Criterion::default().measurement_time(Duration::from_secs(10)).nresamples(50);
targets = criterion_benchmark
}
criterion_main!(benches);
+248
View File
@@ -0,0 +1,248 @@
//! Benchmarks for asynchronous Mutex implementations
use async_std::{sync::Mutex as AsyncStdMutex, task};
use criterion::{criterion_group, criterion_main, Benchmark, Criterion};
use futures_intrusive::sync::{Mutex as IntrusiveMutex, Semaphore};
use tokio::sync::Mutex as TokioMutex;
use std::future::Future;
use std::sync::Arc;
use std::time::Duration;
mod utils;
use utils::Yield;
const ITERATIONS: usize = 300;
const CONTENTION_THREADS: usize = 10;
/// With a chance of 25% chance the operation inside the async Mutex blocks,
/// which is emulated by yielding `NR_YIELD` times back to the executor.
const YIELD_CHANCE: usize = 25;
const NR_YIELDS: usize = 10;
/// Extension trait to add support for `block_on` for runtimes which not
/// natively support it as member function
trait Block {
fn block_on<F: Future<Output = ()>>(&self, f: F);
}
struct FakeAsyncStdRuntime;
impl Block for FakeAsyncStdRuntime {
fn block_on<F: Future<Output = ()>>(&self, f: F) {
task::block_on(f);
}
}
macro_rules! run_with_mutex {
(
$mutex_constructor: expr,
$nr_tasks: expr,
$nr_iterations: expr,
$spawn_fn: expr
) => {
let m = Arc::new($mutex_constructor);
let mut tasks = Vec::new();
let sem = Arc::new(Semaphore::new(false, 0));
for _ in 0..$nr_tasks {
let m = m.clone();
let s = sem.clone();
tasks.push($spawn_fn(async move {
for count in 0..$nr_iterations {
let _ = m.lock().await;
// Asynchronous mutexes are intended to guard over
// operations which are potentially task-blocking and take
// a certain amount of time to complete. In order to simulate
// the behavior we yield a certain amount of times to back
// to the executor. This is more consistent than e.g. using
// a timer, and the overhead of yielding is the same for the
// various Mutex implementations.
if YIELD_CHANCE != 0 && (count % (100 / YIELD_CHANCE) == 0) {
Yield::new(NR_YIELDS).await;
}
}
s.release(1);
}));
}
sem.acquire($nr_tasks).await;
};
}
macro_rules! contention {
(
$b: ident,
$rt_setup: expr, $spawn_fn: expr,
$mutex_constructor: expr, $nr_iterations: expr
) => {
#[allow(unused_mut)] // mut is only required for some runtimes
let mut rt = $rt_setup;
$b.iter(|| {
rt.block_on(async {
run_with_mutex!(
$mutex_constructor,
CONTENTION_THREADS,
$nr_iterations,
$spawn_fn
);
})
});
};
}
macro_rules! no_contention {
(
$b: ident,
$rt_setup: expr, $spawn_fn: expr,
$mutex_constructor: expr, $nr_iterations: expr
) => {
#[allow(unused_mut)] // mut is only required for some runtimes
let mut rt = $rt_setup;
$b.iter(|| {
rt.block_on(async {
run_with_mutex!(
$mutex_constructor,
1,
$nr_iterations,
$spawn_fn
);
})
});
};
}
macro_rules! benchmarks {
(
$c: ident,
$rt_name: literal, $rt_setup: expr, $spawn_fn: expr,
$mutex_name: literal, $mutex_constructor: expr
) => {
$c.bench(
concat!($rt_name, "/", $mutex_name),
Benchmark::new("contention", |b| {
contention!(
b,
$rt_setup,
$spawn_fn,
$mutex_constructor,
ITERATIONS
);
})
.with_function("no_contention", |b| {
no_contention!(
b,
$rt_setup,
$spawn_fn,
$mutex_constructor,
ITERATIONS
);
}),
);
};
}
fn tokio_rt_intrusive_fair_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"tokio_rt",
tokio::runtime::Runtime::new().unwrap(),
tokio::spawn,
"futures_intrusive(fair=true)",
IntrusiveMutex::new((), true)
);
}
fn tokio_rt_intrusive_unfair_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"tokio_rt",
tokio::runtime::Runtime::new().unwrap(),
tokio::spawn,
"futures_intrusive(fair=false)",
IntrusiveMutex::new((), false)
);
}
fn tokio_rt_async_std_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"tokio_rt",
tokio::runtime::Runtime::new().unwrap(),
tokio::spawn,
"async_std",
AsyncStdMutex::new(())
);
}
fn tokio_rt_tokio_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"tokio_rt",
tokio::runtime::Runtime::new().unwrap(),
tokio::spawn,
"tokio",
TokioMutex::new(())
);
}
fn async_std_intrusive_fair_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"async_std_rt",
FakeAsyncStdRuntime {},
task::spawn,
"futures_intrusive(fair=true)",
IntrusiveMutex::new((), true)
);
}
fn async_std_intrusive_unfair_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"async_std_rt",
FakeAsyncStdRuntime {},
task::spawn,
"futures_intrusive(fair=false)",
IntrusiveMutex::new((), false)
);
}
fn async_std_async_std_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"async_std_rt",
FakeAsyncStdRuntime {},
task::spawn,
"async_std",
AsyncStdMutex::new(())
);
}
fn async_std_tokio_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"async_std_rt",
FakeAsyncStdRuntime {},
task::spawn,
"tokio",
TokioMutex::new(())
);
}
criterion_group! {
name = benches;
config = Criterion::default().measurement_time(Duration::from_secs(10));
targets =
// tokio
tokio_rt_intrusive_fair_benchmarks,
tokio_rt_intrusive_unfair_benchmarks,
tokio_rt_async_std_benchmarks,
tokio_rt_tokio_benchmarks,
// async-std
async_std_intrusive_fair_benchmarks,
async_std_intrusive_unfair_benchmarks,
async_std_async_std_benchmarks,
async_std_tokio_benchmarks
}
criterion_main!(benches);
@@ -0,0 +1,213 @@
//! Benchmarks for asynchronous Semaphore implementations
use criterion::{criterion_group, criterion_main, Benchmark, Criterion};
use futures_intrusive::sync::{
Semaphore as IntrusiveSemaphore,
SemaphoreReleaser as IntrusiveSemaphoreReleaser,
};
use tokio::sync::{
Semaphore as TokioSemaphore, SemaphorePermit as TokioSemaphorePermit,
};
use std::future::Future;
use std::sync::Arc;
use std::time::Duration;
mod utils;
use utils::Yield;
/// How often each task should acquire the semaphore
const NR_ACQUIRES: usize = 50;
/// How many tasks are used
const TASKS: usize = 200;
/// The amount of available permits when we are testing strong contention
const CONTENTION_PERMITS: usize = 100;
/// The amount of available permits when testing light contention
const NORMAL_PERMITS: usize = 180;
/// The amount of available permits when testing no contention
const UNCONTENDED_PERMITS: usize = TASKS;
/// The number of yields we perform after the Semaphore was acquired
const NR_YIELDS: usize = 4;
/// Extension trait to add support for `block_on` for runtimes which not
/// natively support it as member function
trait Block {
fn block_on<F: Future<Output = ()>>(&self, f: F);
}
fn create_intrusive_fair_semaphore(permits: usize) -> IntrusiveSemaphore {
IntrusiveSemaphore::new(true, permits)
}
fn create_intrusive_unfair_semaphore(permits: usize) -> IntrusiveSemaphore {
IntrusiveSemaphore::new(false, permits)
}
fn create_tokio_semaphore(permits: usize) -> TokioSemaphore {
TokioSemaphore::new(permits)
}
async fn acquire_intrusive_semaphore(
sem: &IntrusiveSemaphore,
) -> IntrusiveSemaphoreReleaser<'_> {
sem.acquire(1).await
}
async fn acquire_tokio_semaphore(
sem: &TokioSemaphore,
) -> TokioSemaphorePermit<'_> {
sem.acquire().await.unwrap()
}
macro_rules! run_with_semaphore {
(
$nr_tasks: expr,
$nr_iterations: expr,
$nr_permits: expr,
$spawn_fn: expr,
$create_semaphore_fn: ident,
$acquire_fn: ident,
) => {
let semaphore = Arc::new($create_semaphore_fn($nr_permits));
let mut tasks = Vec::new();
let sem = Arc::new(IntrusiveSemaphore::new(false, 0));
for _ in 0..$nr_tasks {
let semaphore = semaphore.clone();
let s = sem.clone();
tasks.push($spawn_fn(async move {
for _count in 0..$nr_iterations {
let _releaser = $acquire_fn(&*semaphore).await;
Yield::new(NR_YIELDS).await;
}
s.release(1);
}));
}
sem.acquire($nr_tasks).await;
};
}
macro_rules! bench {
(
$b: ident,
$rt_setup: expr,
$spawn_fn: expr,
$nr_iterations: expr,
$nr_permits: expr,
$create_semaphore_fn: ident,
$acquire_fn: ident,
) => {
#[allow(unused_mut)] // mut is only required for some runtimes
let mut rt = $rt_setup;
$b.iter(|| {
rt.block_on(async {
run_with_semaphore!(
TASKS,
$nr_iterations,
$nr_permits,
$spawn_fn,
$create_semaphore_fn,
$acquire_fn,
);
})
});
};
}
macro_rules! benchmarks {
(
$c: ident,
$rt_name: literal,
$rt_setup: expr,
$spawn_fn: expr,
$semaphore_name: literal,
$create_semaphore_fn: ident,
$acquire_fn: ident,
) => {
$c.bench(
concat!($rt_name, "/", $semaphore_name),
Benchmark::new("heavy contention", |b| {
bench!(
b,
$rt_setup,
$spawn_fn,
NR_ACQUIRES,
CONTENTION_PERMITS,
$create_semaphore_fn,
$acquire_fn,
);
})
.with_function("normal contention", |b| {
bench!(
b,
$rt_setup,
$spawn_fn,
NR_ACQUIRES,
NORMAL_PERMITS,
$create_semaphore_fn,
$acquire_fn,
);
})
.with_function("no contention", |b| {
bench!(
b,
$rt_setup,
$spawn_fn,
NR_ACQUIRES,
UNCONTENDED_PERMITS,
$create_semaphore_fn,
$acquire_fn,
);
}),
);
};
}
fn tokio_rt_intrusive_fair_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"tokio_rt",
tokio::runtime::Runtime::new().unwrap(),
tokio::spawn,
"futures_intrusive(fair=true)",
create_intrusive_fair_semaphore,
acquire_intrusive_semaphore,
);
}
fn tokio_rt_intrusive_unfair_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"tokio_rt",
tokio::runtime::Runtime::new().unwrap(),
tokio::spawn,
"futures_intrusive(fair=false)",
create_intrusive_unfair_semaphore,
acquire_intrusive_semaphore,
);
}
fn tokio_rt_tokio_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"tokio_rt",
tokio::runtime::Runtime::new().unwrap(),
tokio::spawn,
"tokio",
create_tokio_semaphore,
acquire_tokio_semaphore,
);
}
criterion_group! {
name = benches;
config = Criterion::default().measurement_time(Duration::from_secs(10));
targets =
tokio_rt_intrusive_fair_benchmarks,
tokio_rt_intrusive_unfair_benchmarks,
tokio_rt_tokio_benchmarks,
}
criterion_main!(benches);
@@ -0,0 +1,31 @@
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
/// A Future which yields to the executor for a given amount of iterations
/// and resolves after this
pub struct Yield {
iter: usize,
}
impl Yield {
pub fn new(iter: usize) -> Yield {
Yield { iter }
}
}
impl Future for Yield {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if self.iter == 0 {
Poll::Ready(())
} else {
self.iter -= 1;
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
@@ -0,0 +1,312 @@
//! This example demonstrates the application of structured concurrency and
//! gracefully cancellation in an async Rust application.
//! An async [`ManualResetEvent`] as provided by `futures-intrusive` is a used
//! as the main signalization mechanism for cooperative cancellation.
//!
//! Usage: cargo run --example cancellation
//! After some seconds, press Ctrl+C and observe the results
//!
//! Structured concurrency is an application model where the lifetime of any
//! concurrent operation is strictly contained within the lifetime of it's
//! parent operation.
//!
//! The concept is described in further detail within
//! https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/
//! https://trio.discourse.group/t/structured-concurrency-kickoff/55
//!
//! The application of structured concurrency principles simplifies concurrent
//! program. It allows for an easier reasoning about which concurrent tasks run
//! at a given point of time, since no subtask will ever run without it's original
//! parent task already having finished. This makes it impossible for the subtask
//! to wait on a certain condition that will no longer occur - or to modify the
//! state of the program when we no longer expect it.
//!
//! One challenge for structured concurrency is the graceful cancellation of
//! subtasks. Within Rusts `Future`s and `async/await` programming model it is
//! generally easy to stop asynchronous subtasks: We can just `drop` their
//! associated `Future`s, which will cancel those tasks. However this foceful
//! cancellation comes with several downsides:
//! - The subtasks can't perform any cleanup work anymore that might be helpful.
//! Only code inside their destructors can run if the tasks are cancelled.
//! - The subtasks can't return any value.
//!
//! Therefore a cooperative and graceful cancellation is sometimes preferred. In
//! this example we implement graceful cancellation in order to allow a sub task
//! to return it's calculated values.
//!
//! Graceful cancellation is implemented in 3 steps:
//! 1. Signalling the cancellation: One component signals the sub-tasks that they
//! should stop their work as soon as it is convenient for them. The
//! cancellation signal can either originate from a parent task, the sub task
//! itself, or one of the sibling tasks. In order to distribute cancellation
//! signals we utilize an async `ManualResetEvent` as a cancellation token.
//! This datastructure allows to signal an arbitrary amount of tasks.
//! The signal can be emitted by any component which has access to
//! `ManualResetEvent`.
//! 2. Detecting the signal inside sub-tasks and shutting down. In order to
//! support graceful cancellation, subtasks need to detect the condition that
//! they are supposed to shut down. In order to do this we use the futures-rs
//! `select!` macro to wait in parallel for either the async calculation on
//! the "normal path" to complete or for the cancellation to get signalled.
//! Not all subtasks have to explicitly support this. Some of them just need
//! to forward the cancellation token to their child tasks. When these finish
//! early due to cancellation, then the parent will also finish early.
//! Child tasks can return an error result in order to indicate that they have
//! returned due the explicit cancellation. E.g. `Err(Cancelled)` could be
//! returned to the parent.
//! 3. The parent tasks waits for all sub-tasks to shut down, via waiting on
//! their wait-handles (which in our case are `Future`s that can be awaited
//! via `await` or various `join` functions).
//!
//! After these steps have completed all sub tasks of a given parent have
//! completed and the parent task can also finish. It can thereby return the
//! results of the child tasks if required.
//!
//! The implementation is similar in spirit to cancellation in the Go programming
//! language trough the Context parameter (https://blog.golang.org/context).
//! The main difference is that a `ManualResetEvent` is used for signalling
//! cancellation instead of a `Channel` - and that we can check for the
//! cancellation signal on every `await` of a `Future`. Checking for cancellation
//! is not constrained to interaction with `Channel` types.
//! E.g. we can easily wait on receiving data on a socket while in parallel
//! waiting for cancellation. This is not directly possible in Go.
//!
//! It also similar to the `CancellationToken` mechanism in .NET. There the
//! `CancellationToken` also needs to get forwarded as a parameter.
//!
//! This example demonstrates the mechanisms via a distributed "FizzBuzz" checker.
//! The "algorithm" uses a parent tasks which uses 2 child tasks for it's work.
//! When the user cancels the program, a graceful shutdown as described should
//! be performed. This allows the user to retrieve the results of the algorithm.
use futures::{executor::block_on, join, select};
use futures_intrusive::{
channel::LocalUnbufferedChannel,
sync::{LocalManualResetEvent, ManualResetEvent},
timer::{StdClock, Timer, TimerService},
};
use lazy_static::lazy_static;
use signal_hook;
use std::{
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread::{sleep, spawn},
time::Duration,
};
/// The result of our search for FizzBuzz values
#[derive(Debug, Default)]
struct SearchResult {
highest_fizz: Option<usize>,
highest_buzz: Option<usize>,
highest_fizzbuzz: Option<usize>,
}
/// This is our main async function that supports cooperative cancellation.
/// The purpose of this function is to check values up to `max` for their
/// fizzbuzzness and return the highest values in each category.
///
/// The method can be be cancelled by signalling the cancellation token. In this
/// case the method will return its latest findings.
/// This is in contrast to just cancelling a `Future` - which would not allow us
/// to return any results. Cancellation tokens can be passed as `Arc<ManualResetEvent>`
/// if multiple independent subtasks need to get cancelled, or as a plain reference
/// if only subtasks of a single task need to get signalled. For tasks which run
/// on a singlethreaded executor `LocalManualResetEvent` provides an even higher
/// lower overhead solution which does not require any internal synchronization.
async fn fizzbuzz_search(
max: usize,
cancellation_token: Arc<ManualResetEvent>,
) -> SearchResult {
// We start two child-tasks:
// - One produces values to check
// - The other task will check the values and store the results in the
// result data structure.
//
// Both tasks are connected via a channel. Since the tasks are running as
// subtasks of the same task in a singlethreaded executor, we can use an
// extremely efficient LocalChannel for this.
//
// In order to make things a bit more interesting we do not utilize the same
// cancellation signal for both tasks (which would also be a valid solution).
// Instead we implement a sequential shutdown:
// - When the main `cancellation_token` is signalled from the outside,
// only the producer task will shut down.
// - Before the producer task exits, it will signal another cancellation
// token. That one will lead the checker task to shut down.
let channel = LocalUnbufferedChannel::<usize>::new();
let checker_cancellation_token = LocalManualResetEvent::new(false);
let producer_future = producer_task(
max,
&channel,
&cancellation_token,
&checker_cancellation_token,
);
let checker_future = check_task(&channel, &checker_cancellation_token);
// Here we wait for both tasks to complete. Waiting for all subtasks to
// complete is one important part of structured concurrency.
let results = join!(producer_future, checker_future);
println!("All subtasks have completed");
// Since we waited for all subtasks to complete we can return the search
// result.
// If the async subtasks had been forcefully instead of cooperatively
// cancelled the results would not have been available.
results.1
}
/// The producing task produces all values that need to get checked for
/// fizzbuzzness.
/// The task will run until it either has generated all values to check or
/// until the task gets cancelled.
async fn producer_task(
max: usize,
channel: &LocalUnbufferedChannel<usize>,
main_cancellation_token: &ManualResetEvent,
consumer_cancellation_token: &LocalManualResetEvent,
) {
for value in 1..max {
select! {
result = channel.send(value) => {
if !result.is_ok() {
unreachable!("This can not happen in this example");
}
},
_ = main_cancellation_token.wait() => {
// The operation was cancelled
break;
}
};
}
// No more values to check or we had been cancelled.
// In this case we signal the `cancellation_token`, in order to let the
// consumer shut down.
// We should here have alternatively `.close()`d the channel to signal the
// consumer to join. However we want mainly want to demonstrate the
// cancellation concept here.
println!("Goodbye from the producer. Now signalling the checker");
consumer_cancellation_token.set();
}
/// The check task runs until it gets cancelled. That can happen either due
/// to a cancellation being signalled, or due to the input channel getting
/// closed. In a real application one of those strategies would be good sufficient.
/// Since this example focusses on cancellation and structured concurrency, this
/// task will **always** get shut down via the cancellation token.
///
/// It is important that this tasks runs to completion instead of getting
/// forcefully cancelled. Otherwise no results would be available.
async fn check_task(
channel: &LocalUnbufferedChannel<usize>,
cancellation_token: &LocalManualResetEvent,
) -> SearchResult {
// Initialize the result with `None`s
let mut result: SearchResult = Default::default();
loop {
select! {
value = channel.receive() => {
if let Some(value) = value {
// Received a value that needs to get checked for fizzbuzzness
println!("Checking {} of fizzbuzzness", value);
match (value % 3 == 0, value % 5 == 0) {
(true, true) => result.highest_fizzbuzz = Some(value),
(true, false) => result.highest_fizz = Some(value),
(false, true) => result.highest_buzz = Some(value),
_ => {},
}
} else {
unreachable!("this is not allowed in this example");
// Otherwise just doing the following here would be ok:
// break;
}
},
_ = cancellation_token.wait() => {
// The operation was cancelled
break;
}
};
// Waits until the timer elapses or the task gets cancelled - whatever
// comes first. This slows down our consumer, and introduces another
// cancellation point. Since we use an unbuffered channel to accept
// values to check from the producer, the producer is slowed down by
// the same amount of time.
select! {
_ = get_timer().delay(Duration::from_millis(1000)) => {},
_ = cancellation_token.wait() => {
// The operation was cancelled
break;
},
}
}
println!("Goodbye from the checker");
result
}
fn main() {
// Spawn a background thread which advances the timer
let timer_join_handle = spawn(move || {
timer_thread();
});
// This is the asynchronous ManualResetEvent that will be used as a cancellation
// token. When the cancellation is requested, the token will be set. Thereby
// all tasks which are waiting for cancellation will get signalled and awoken.
let cancellation_token = Arc::new(ManualResetEvent::new(false));
// This sets up a signal listener. When SIGINT (Ctrl+C) is signalled,
// the Cancellation Token is set - which will lead the async task to run
// to completion. Since setting the cancellation token is not signal safe,
// we apply a workaround and set only an atomic variable in the signal handler.
// A background thread regularly checks the signal and sets the event once
// the signal had been observed.
let cloned_token = cancellation_token.clone(); // Clone for the background thread
std::thread::spawn(move || {
let term = Arc::new(AtomicBool::new(false));
signal_hook::flag::register(signal_hook::SIGINT, Arc::clone(&term))
.unwrap();
while !term.load(Ordering::Relaxed) {
std::thread::sleep(Duration::from_millis(100));
}
println!("Starting cancellation");
cloned_token.set();
});
// Start our async task. This gets the cancellation token passed as argument
let result = block_on(fizzbuzz_search(std::usize::MAX, cancellation_token));
// At this point in time, the task has finished - either due to running to
// completion or due to being cancelled. The task can return results in both
// situations.
println!("Discovered these awesome results: {:?}", result);
// Stop the timer thread
STOP_TIMER.store(true, Ordering::Relaxed);
timer_join_handle.join().unwrap();
}
// Some setup for the asynchronously awaitable timer
lazy_static! {
static ref STD_CLOCK: StdClock = StdClock::new();
static ref TIMER_SERVICE: TimerService = TimerService::new(&*STD_CLOCK);
static ref STOP_TIMER: AtomicBool = AtomicBool::new(false);
}
/// Returns a reference to the global timer
fn get_timer() -> &'static dyn Timer {
&*TIMER_SERVICE
}
/// A background thread that drives the async timer service
fn timer_thread() {
while !STOP_TIMER.load(Ordering::Relaxed) {
sleep(Duration::from_millis(25));
TIMER_SERVICE.check_expirations();
}
}
@@ -0,0 +1,165 @@
//! The example in this file demonstrates a solution for the
//! [Dining Philosophers Problem](https://en.wikipedia.org/wiki/Dining_philosophers_problem),
//! which uses async tasks and futures_intrusive primitives in order to
//! simulate philosophers.
#![recursion_limit = "256"]
use futures::{executor::block_on, join, select};
use futures_intrusive::{
sync::LocalMutex,
timer::{StdClock, Timer, TimerService},
};
use lazy_static::lazy_static;
use pin_utils::pin_mut;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{sleep, spawn};
use std::time::Duration;
/// We simulate the ownership of a fork through an asynchronously awaitable mutex.
/// In order to acquire a fork, the philosopher acquires the Mutex.
/// In order to release a fork, the philosopher releases the LockGuard. This
/// happens automatically, when the LockGuard goes out of scope.
/// Since all philosophers are subtasks of the same top-level `async` task,
/// a lightweight non thread-safe `LocalMutex` can be utilized.
type Fork = LocalMutex<()>;
// Some setup for the asynchronously awaitable timer
lazy_static! {
static ref STD_CLOCK: StdClock = StdClock::new();
static ref TIMER_SERVICE: TimerService = TimerService::new(&*STD_CLOCK);
static ref STOP_TIMER: AtomicBool = AtomicBool::new(false);
}
/// Returns a reference to the global timer
fn get_timer() -> &'static dyn Timer {
&*TIMER_SERVICE
}
/// Returns a random delay duration between `min` and `(min + max_extra)`
fn rand_delay(min: Duration, max_extra: Duration) -> Duration {
let extra_ms = rand::random::<u64>() % (max_extra.as_millis() as u64);
min + Duration::from_millis(extra_ms)
}
/// How often a philosopher should eat
const TO_EAT: usize = 5;
/// Simulates a single philosopher
async fn philosopher_task<'a>(
name: &'a str,
left_fork: &'a Fork,
right_fork: &'a Fork,
) {
println!("{} is ready to go", name);
let mut eaten: usize = 0;
while eaten != TO_EAT {
println!("{} is thinking", name);
get_timer()
.delay(rand_delay(
Duration::from_millis(1000),
Duration::from_millis(1000),
))
.await;
{
println!("{} is starting to pick up forks", name);
// Create futures for acquiring both forks
let get_left_fork_future = left_fork.lock();
pin_mut!(get_left_fork_future);
let get_right_fork_future = right_fork.lock();
pin_mut!(get_right_fork_future);
// This sets up a timer. If the philosopher can't obtain both forks
// during that, they put back all acquired forks and start thinking
// again.
let abort_get_forks_future =
get_timer().delay(Duration::from_millis(300));
pin_mut!(abort_get_forks_future);
select! {
_ = get_left_fork_future => {
println!("{} got the left fork and tries to get the right fork", name);
select! {
_ = get_right_fork_future => {
println!("{} got the right fork and starts eating", name);
get_timer().delay(
rand_delay(Duration::from_millis(1000),
Duration::from_millis(200))).await;
eaten += 1;
println!("{} has finished eating [ate {} times]", name, eaten);
},
_ = abort_get_forks_future => {
println!("{} could not acquire the right fork", name);
},
}
},
_ = get_right_fork_future => {
println!("{} got the right fork and tries to get the left fork", name);
select! {
_ = get_left_fork_future => {
println!("{} got the left fork and starts eating", name);
get_timer().delay(
rand_delay(Duration::from_millis(1000),
Duration::from_millis(200))).await;
eaten += 1;
println!("{} has finished eating [ate {} times]", name, eaten);
},
_ = abort_get_forks_future => {
println!("{} could not acquire the left fork", name);
},
}
},
_ = abort_get_forks_future => {
println!("{} could not acquire any fork", name);
},
}
}
}
println!("{} has finished", name);
}
async fn simulate_philosophers() {
// Create the forks for the philosophers
let forks: [Fork; 5] = [
Fork::new((), true),
Fork::new((), true),
Fork::new((), true),
Fork::new((), true),
Fork::new((), true),
];
// Create a task for each philosopher
let p1 = philosopher_task("A", &forks[4], &forks[0]);
let p2 = philosopher_task("B", &forks[0], &forks[1]);
let p3 = philosopher_task("C", &forks[1], &forks[2]);
let p4 = philosopher_task("D", &forks[2], &forks[3]);
let p5 = philosopher_task("E", &forks[3], &forks[4]);
// Wait until all philosophers have finished eating
join!(p1, p2, p3, p4, p5);
}
fn main() {
// Spawn a background thread which advances the timer
let join_handle = spawn(move || {
timer_thread();
});
// And simulate the philosophers
block_on(simulate_philosophers());
// Stop the timer thread
STOP_TIMER.store(true, Ordering::Relaxed);
join_handle.join().unwrap();
}
fn timer_thread() {
while !STOP_TIMER.load(Ordering::Relaxed) {
sleep(Duration::from_millis(25));
TIMER_SERVICE.check_expirations();
}
}
@@ -0,0 +1,8 @@
# Examples
This folder contains examples for the usage of this library.
Examples can be started in the following fashion:
```
cargo run --example name_of_example
```
+54
View File
@@ -0,0 +1,54 @@
futures-intrusive
=================
This crate provides a variety of `Futures`-based and `async/await` compatible
types that are based on the idea of intrusive collections:
- Channels in a variety of flavors:
- Oneshot
- Multi-Producer Multi-Consumer (MPMC)
- State Broadcast
- Synchronization Primitives:
- Manual Reset Event
- Mutex
- Semaphore
- A timer
Please refer to the [documentation](https://docs.rs/futures-intrusive) for details.
In addition to the documentation the examples provide a quick overview on how
the primitives can be used.
## Usage
Add this to your `Cargo.toml`:
```toml
[dependencies]
futures-intrusive = "^0.5"
```
In order to use the crate in a `no-std` environment, it needs to be compiled
without default features:
```toml
[dependencies]
futures-intrusive = { version = "^0.5", default-features = false }
```
The crate defines a feature `alloc`, which can be used in order to re-enable
`alloc` features. Also defined is `std`, which can be used in order to re-enable
`std` features.
## Minimum Rust version
The minimum required Rust version is 1.36, due to reliance on stable
`Future`, `Context` and `Waker` types.
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
@@ -0,0 +1,12 @@
//! Buffer types
mod real_array;
pub use real_array::RealArray;
mod ring_buffer;
pub use ring_buffer::{ArrayBuf, RingBuf};
#[cfg(feature = "alloc")]
pub use ring_buffer::FixedHeapBuf;
#[cfg(feature = "alloc")]
pub use ring_buffer::GrowingHeapBuf;
@@ -0,0 +1,62 @@
/// A marker trait which may only be implemented for native array types, like
/// `[T; 2]`. The library incorporates several components that are parameterized
/// by array types, but currently Rust provides no safe mechanism to express
/// that.
///
/// In order to work around the limitations, these methods only accept arrays
/// which implement the `RealArray` type. The library provides an implementation
/// of `RealArray` for arrays up to length 64, as well as for all powers of 2
/// up to 64k.
///
/// In order to let the library accept arrays of bigger sizes, `RealArray` can
/// be implemented by users via newtypes. A type as defined in the following
/// example can be passed to the library:
///
/// ```
/// use futures_intrusive::buffer::RealArray;
/// use futures_intrusive::channel::LocalChannel;
///
/// struct I32x384Array([i32; 384]);
/// unsafe impl RealArray<i32> for I32x384Array {
/// const LEN: usize = 384;
/// }
///
/// impl AsMut<[i32]> for I32x384Array {
/// fn as_mut(&mut self) -> &mut [i32] {
/// &mut self.0
/// }
/// }
///
/// impl AsRef<[i32]> for I32x384Array {
/// fn as_ref(&self) -> &[i32] {
/// &self.0
/// }
/// }
///
/// fn main() {
/// let channel = LocalChannel::<i32, I32x384Array>::new();
/// }
///
/// ```
pub unsafe trait RealArray<T> {
/// The length of the array
const LEN: usize;
}
macro_rules! real_array {
($($N:expr),+) => {
$(
unsafe impl<T> RealArray<T> for [T; $N] {
const LEN: usize = $N;
}
)+
}
}
real_array!(
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
59, 60, 61, 62, 63, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384,
32768, 65536
);
@@ -0,0 +1,379 @@
use super::RealArray;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
/// A Ring Buffer of items
pub trait RingBuf {
/// The type of stored items inside the Ring Buffer
type Item;
/// Creates a new instance of the Ring Buffer
fn new() -> Self;
/// Creates a new instance of the Ring Buffer with the given capacity.
/// `RingBuf` implementations are allowed to ignore the `capacity` hint and
/// utilize their default capacity.
fn with_capacity(cap: usize) -> Self;
/// The capacity of the buffer
fn capacity(&self) -> usize;
/// The amount of stored items in the buffer
fn len(&self) -> usize;
/// Returns true if no item is stored inside the buffer.
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns true if there is enough space in the buffer to
/// store another item.
fn can_push(&self) -> bool;
/// Stores the item at the end of the buffer.
/// Panics if there is not enough free space.
fn push(&mut self, item: Self::Item);
/// Returns the oldest item inside the buffer.
/// Panics if there is no available item.
fn pop(&mut self) -> Self::Item;
}
/// An array-backed Ring Buffer
///
/// `A` is the type of the backing array. The backing array must be a real
/// array. In order to verify this it must satisfy the [`RealArray`] constraint.
/// In order to create a Ring Buffer backed by an array of 5 integer elements,
/// the following code can be utilized:
///
/// ```
/// use futures_intrusive::buffer::{ArrayBuf, RingBuf};
///
/// type Buffer5 = ArrayBuf<i32, [i32; 5]>;
/// let buffer = Buffer5::new();
/// ```
pub struct ArrayBuf<T, A>
where
A: core::convert::AsMut<[T]> + core::convert::AsRef<[T]> + RealArray<T>,
{
buffer: MaybeUninit<A>,
size: usize,
recv_idx: usize,
send_idx: usize,
_phantom: PhantomData<T>,
}
impl<T, A> core::fmt::Debug for ArrayBuf<T, A>
where
A: core::convert::AsMut<[T]> + core::convert::AsRef<[T]> + RealArray<T>,
{
fn fmt(
&self,
f: &mut core::fmt::Formatter,
) -> Result<(), core::fmt::Error> {
f.debug_struct("ArrayBuf")
.field("size", &self.size)
.field("cap", &self.capacity())
.finish()
}
}
impl<T, A> ArrayBuf<T, A>
where
A: core::convert::AsMut<[T]> + core::convert::AsRef<[T]> + RealArray<T>,
{
fn next_idx(&mut self, last_idx: usize) -> usize {
if last_idx + 1 == self.capacity() {
return 0;
}
last_idx + 1
}
}
impl<T, A> RingBuf for ArrayBuf<T, A>
where
A: core::convert::AsMut<[T]> + core::convert::AsRef<[T]> + RealArray<T>,
{
type Item = T;
fn new() -> Self {
ArrayBuf {
buffer: MaybeUninit::uninit(),
send_idx: 0,
recv_idx: 0,
size: 0,
_phantom: PhantomData,
}
}
fn with_capacity(_cap: usize) -> Self {
// The fixed size array backed Ring Buffer doesn't support an adjustable
// capacity. Therefore only the default capacity is utilized.
Self::new()
}
#[inline]
fn capacity(&self) -> usize {
A::LEN
}
#[inline]
fn len(&self) -> usize {
self.size
}
#[inline]
fn can_push(&self) -> bool {
self.len() != self.capacity()
}
#[inline]
fn push(&mut self, value: Self::Item) {
assert!(self.can_push());
// Safety: We asserted that there is available space for an item.
// Therefore the memory address is valid.
unsafe {
let arr_ptr = self.buffer.as_mut_ptr() as *mut T;
arr_ptr.add(self.send_idx).write(value);
}
self.send_idx = self.next_idx(self.send_idx);
self.size += 1;
}
#[inline]
fn pop(&mut self) -> Self::Item {
assert!(self.size > 0);
// Safety: We asserted that there is an element available, so it must
// have been written before.
let val = unsafe {
let arr_ptr = self.buffer.as_mut_ptr() as *mut T;
arr_ptr.add(self.recv_idx).read()
};
self.recv_idx = self.next_idx(self.recv_idx);
self.size -= 1;
val
}
}
impl<T, A> Drop for ArrayBuf<T, A>
where
A: core::convert::AsMut<[T]> + core::convert::AsRef<[T]> + RealArray<T>,
{
fn drop(&mut self) {
// Drop all elements which are still stored inside the buffer
while self.size > 0 {
// Safety: This drops only as many elements as have been written via
// ptr::write and haven't read via ptr::read before
unsafe {
let arr_ptr = self.buffer.as_mut_ptr() as *mut T;
arr_ptr.add(self.recv_idx).drop_in_place();
}
self.recv_idx = self.next_idx(self.recv_idx);
self.size -= 1;
}
}
}
#[cfg(feature = "alloc")]
mod if_alloc {
use super::*;
use alloc::collections::VecDeque;
/// A Ring Buffer which stores all items on the heap.
///
/// The `FixedHeapBuf` will allocate its capacity ahead of time. This is good
/// fit when you have a constant latency between two components.
pub struct FixedHeapBuf<T> {
buffer: VecDeque<T>,
/// The capacity is stored extra, since VecDeque can allocate space for
/// more elements than specified.
cap: usize,
}
impl<T> core::fmt::Debug for FixedHeapBuf<T> {
fn fmt(
&self,
f: &mut core::fmt::Formatter,
) -> Result<(), core::fmt::Error> {
f.debug_struct("FixedHeapBuf")
.field("size", &self.buffer.len())
.field("cap", &self.cap)
.finish()
}
}
impl<T> RingBuf for FixedHeapBuf<T> {
type Item = T;
fn new() -> Self {
FixedHeapBuf {
buffer: VecDeque::new(),
cap: 0,
}
}
fn with_capacity(cap: usize) -> Self {
FixedHeapBuf {
buffer: VecDeque::with_capacity(cap),
cap,
}
}
#[inline]
fn capacity(&self) -> usize {
self.cap
}
#[inline]
fn len(&self) -> usize {
self.buffer.len()
}
#[inline]
fn can_push(&self) -> bool {
self.buffer.len() != self.cap
}
#[inline]
fn push(&mut self, value: Self::Item) {
assert!(self.can_push());
self.buffer.push_back(value);
}
#[inline]
fn pop(&mut self) -> Self::Item {
assert!(self.buffer.len() > 0);
self.buffer.pop_front().unwrap()
}
}
/// A Ring Buffer which stores all items on the heap but grows dynamically.
///
/// A `GrowingHeapBuf` does not allocate the capacity ahead of time, as
/// opposed to the `FixedHeapBuf`. This makes it a good fit when you have
/// unpredictable latency between two components, when you want to
/// amortize your allocation costs or when you are using an external
/// back-pressure mechanism.
pub struct GrowingHeapBuf<T> {
buffer: VecDeque<T>,
/// The maximum number of elements in the buffer.
limit: usize,
}
impl<T> core::fmt::Debug for GrowingHeapBuf<T> {
fn fmt(
&self,
f: &mut core::fmt::Formatter,
) -> Result<(), core::fmt::Error> {
f.debug_struct("GrowingHeapBuf")
.field("size", &self.buffer.len())
.field("limit", &self.limit)
.finish()
}
}
impl<T> RingBuf for GrowingHeapBuf<T> {
type Item = T;
fn new() -> Self {
GrowingHeapBuf {
buffer: VecDeque::new(),
limit: 0,
}
}
fn with_capacity(limit: usize) -> Self {
GrowingHeapBuf {
buffer: VecDeque::new(),
limit,
}
}
#[inline]
fn capacity(&self) -> usize {
self.limit
}
#[inline]
fn len(&self) -> usize {
self.buffer.len()
}
#[inline]
fn can_push(&self) -> bool {
self.buffer.len() != self.limit
}
#[inline]
fn push(&mut self, value: Self::Item) {
debug_assert!(self.can_push());
self.buffer.push_back(value);
}
#[inline]
fn pop(&mut self) -> Self::Item {
debug_assert!(self.buffer.len() > 0);
self.buffer.pop_front().unwrap()
}
}
}
#[cfg(feature = "alloc")]
pub use if_alloc::*;
#[cfg(test)]
#[cfg(feature = "alloc")]
mod tests {
use super::*;
use crate::buffer::ring_buffer::if_alloc::FixedHeapBuf;
fn test_ring_buf<Buf: RingBuf<Item = u32>>(mut buf: Buf) {
assert_eq!(5, buf.capacity());
assert_eq!(0, buf.len());
assert_eq!(true, buf.is_empty());
assert_eq!(true, buf.can_push());
buf.push(1);
buf.push(2);
buf.push(3);
assert_eq!(5, buf.capacity());
assert_eq!(3, buf.len());
assert_eq!(false, buf.is_empty());
assert_eq!(true, buf.can_push());
assert_eq!(1, buf.pop());
assert_eq!(2, buf.pop());
assert_eq!(1, buf.len());
assert_eq!(false, buf.is_empty());
assert_eq!(3, buf.pop());
assert_eq!(0, buf.len());
assert_eq!(true, buf.is_empty());
for (i, val) in [4, 5, 6, 7, 8].iter().enumerate() {
buf.push(*val);
assert_eq!(i + 1, buf.len());
assert_eq!(i != 4, buf.can_push());
assert_eq!(false, buf.is_empty());
}
for (i, val) in [4, 5, 6, 7, 8].iter().enumerate() {
assert_eq!(*val, buf.pop());
assert_eq!(4 - i, buf.len());
assert_eq!(true, buf.can_push());
assert_eq!(i == 4, buf.is_empty());
}
}
#[test]
fn test_array_ring_buf() {
let buf = ArrayBuf::<u32, [u32; 5]>::new();
test_ring_buf(buf);
}
#[test]
fn test_heap_ring_buf() {
let buf = FixedHeapBuf::<u32>::with_capacity(5);
test_ring_buf(buf);
}
#[test]
fn test_growing_ring_buf() {
let buf = GrowingHeapBuf::<u32>::with_capacity(5);
test_ring_buf(buf);
}
}
@@ -0,0 +1,519 @@
use super::ChannelSendError;
use crate::intrusive_double_linked_list::ListNode;
use core::marker::PhantomData;
use core::pin::Pin;
use futures_core::future::{FusedFuture, Future};
use futures_core::task::{Context, Poll, Waker};
/// Conveys additional information regarding the status of a channel
/// following a `close` operation.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum CloseStatus {
/// The channel has just been closed by the operation.
NewlyClosed,
/// The channel was already closed prior to the operation.
AlreadyClosed,
}
impl CloseStatus {
/// Returns whether the value is the `NewlyClosed` variant.
pub fn is_newly_closed(self) -> bool {
match self {
Self::NewlyClosed => true,
_ => false,
}
}
/// Returns whether the value is the `AlreadyClosed` variant.
pub fn is_already_closed(self) -> bool {
match self {
Self::AlreadyClosed => true,
_ => false,
}
}
}
/// Tracks how the future had interacted with the channel
#[derive(PartialEq, Debug)]
pub enum RecvPollState {
/// The task is not registered at the wait queue at the channel
Unregistered,
/// The task was added to the wait queue at the channel.
Registered,
/// The task was notified that a value is available or can be sent,
/// but hasn't interacted with the channel since then
Notified,
}
/// Tracks the channel futures waiting state.
/// Access to this struct is synchronized through the channel.
#[derive(Debug)]
pub struct RecvWaitQueueEntry {
/// The task handle of the waiting task
pub task: Option<Waker>,
/// Current polling state
pub state: RecvPollState,
}
impl RecvWaitQueueEntry {
/// Creates a new RecvWaitQueueEntry
pub fn new() -> RecvWaitQueueEntry {
RecvWaitQueueEntry {
task: None,
state: RecvPollState::Unregistered,
}
}
}
/// Tracks how the future had interacted with the channel
#[derive(PartialEq, Debug)]
pub enum SendPollState {
/// The task is not registered at the wait queue at the channel
Unregistered,
/// The task was added to the wait queue at the channel.
Registered,
/// The value has been transmitted to the other task
SendComplete,
}
/// Tracks the channel futures waiting state.
/// Access to this struct is synchronized through the channel.
pub struct SendWaitQueueEntry<T> {
/// The task handle of the waiting task
pub task: Option<Waker>,
/// Current polling state
pub state: SendPollState,
/// The value to send
pub value: Option<T>,
}
impl<T> core::fmt::Debug for SendWaitQueueEntry<T> {
fn fmt(
&self,
fmt: &mut core::fmt::Formatter<'_>,
) -> core::result::Result<(), core::fmt::Error> {
fmt.debug_struct("SendWaitQueueEntry")
.field("task", &self.task)
.field("state", &self.state)
.finish()
}
}
impl<T> SendWaitQueueEntry<T> {
/// Creates a new SendWaitQueueEntry
pub fn new(value: T) -> SendWaitQueueEntry<T> {
SendWaitQueueEntry {
task: None,
state: SendPollState::Unregistered,
value: Some(value),
}
}
}
/// Adapter trait that allows Futures to generically interact with Channel
/// implementations via dynamic dispatch.
pub trait ChannelSendAccess<T> {
unsafe fn send_or_register(
&self,
wait_node: &mut ListNode<SendWaitQueueEntry<T>>,
cx: &mut Context<'_>,
) -> (Poll<()>, Option<T>);
fn remove_send_waiter(
&self,
wait_node: &mut ListNode<SendWaitQueueEntry<T>>,
);
}
/// Adapter trait that allows Futures to generically interact with Channel
/// implementations via dynamic dispatch.
pub trait ChannelReceiveAccess<T> {
unsafe fn receive_or_register(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<Option<T>>;
fn remove_receive_waiter(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
);
}
/// A Future that is returned by the `receive` function on a channel.
/// The future gets resolved with `Some(value)` when a value could be
/// received from the channel.
/// If the channels gets closed and no items are still enqueued inside the
/// channel, the future will resolve to `None`.
#[must_use = "futures do nothing unless polled"]
pub struct ChannelReceiveFuture<'a, MutexType, T> {
/// The channel that is associated with this ChannelReceiveFuture
pub(crate) channel: Option<&'a dyn ChannelReceiveAccess<T>>,
/// Node for waiting on the channel
pub(crate) wait_node: ListNode<RecvWaitQueueEntry>,
/// Marker for mutex type
pub(crate) _phantom: PhantomData<MutexType>,
}
// Safety: Channel futures can be sent between threads as long as the underlying
// channel is thread-safe (Sync), which allows to poll/register/unregister from
// a different thread.
unsafe impl<'a, MutexType: Sync, T: Send> Send
for ChannelReceiveFuture<'a, MutexType, T>
{
}
impl<'a, MutexType, T> core::fmt::Debug
for ChannelReceiveFuture<'a, MutexType, T>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("ChannelReceiveFuture").finish()
}
}
impl<'a, MutexType, T> Future for ChannelReceiveFuture<'a, MutexType, T> {
type Output = Option<T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
// It might be possible to use Pin::map_unchecked here instead of the two unsafe APIs.
// However this didn't seem to work for some borrow checker reasons
// Safety: The next operations are safe, because Pin promises us that
// the address of the wait queue entry inside ChannelReceiveFuture is stable,
// and we don't move any fields inside the future until it gets dropped.
let mut_self: &mut ChannelReceiveFuture<MutexType, T> =
unsafe { Pin::get_unchecked_mut(self) };
let channel = mut_self
.channel
.expect("polled ChannelReceiveFuture after completion");
let poll_res =
unsafe { channel.receive_or_register(&mut mut_self.wait_node, cx) };
if poll_res.is_ready() {
// A value was available
mut_self.channel = None;
}
poll_res
}
}
impl<'a, MutexType, T> FusedFuture for ChannelReceiveFuture<'a, MutexType, T> {
fn is_terminated(&self) -> bool {
self.channel.is_none()
}
}
impl<'a, MutexType, T> Drop for ChannelReceiveFuture<'a, MutexType, T> {
fn drop(&mut self) {
// If this ChannelReceiveFuture has been polled and it was added to the
// wait queue at the channel, it must be removed before dropping.
// Otherwise the channel would access invalid memory.
if let Some(channel) = self.channel {
channel.remove_receive_waiter(&mut self.wait_node);
}
}
}
/// A Future that is returned by the `send` function on a channel.
/// The future gets resolved with `None` when a value could be
/// written to the channel.
/// If the channel gets closed the send operation will fail, and the
/// Future will resolve to `ChannelSendError(T)` and return the item to send.
#[must_use = "futures do nothing unless polled"]
pub struct ChannelSendFuture<'a, MutexType, T> {
/// The Channel that is associated with this ChannelSendFuture
pub(crate) channel: Option<&'a dyn ChannelSendAccess<T>>,
/// Node for waiting on the channel
pub(crate) wait_node: ListNode<SendWaitQueueEntry<T>>,
/// Marker for mutex type
pub(crate) _phantom: PhantomData<MutexType>,
}
// Safety: Channel futures can be sent between threads as long as the underlying
// channel is thread-safe (Sync), which allows to poll/register/unregister from
// a different thread.
unsafe impl<'a, MutexType: Sync, T: Send> Send
for ChannelSendFuture<'a, MutexType, T>
{
}
impl<'a, MutexType, T> core::fmt::Debug
for ChannelSendFuture<'a, MutexType, T>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("ChannelSendFuture").finish()
}
}
impl<'a, MutexType, T> ChannelSendFuture<'a, MutexType, T> {
/// Tries to cancel the ongoing send operation
pub fn cancel(&mut self) -> Option<T> {
let channel = self.channel.take();
match channel {
None => None,
Some(channel) => {
channel.remove_send_waiter(&mut self.wait_node);
self.wait_node.value.take()
}
}
}
}
impl<'a, MutexType, T> Future for ChannelSendFuture<'a, MutexType, T> {
type Output = Result<(), ChannelSendError<T>>;
fn poll(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), ChannelSendError<T>>> {
// It might be possible to use Pin::map_unchecked here instead of the two unsafe APIs.
// However this didn't seem to work for some borrow checker reasons
// Safety: The next operations are safe, because Pin promises us that
// the address of the wait queue entry inside ChannelSendFuture is stable,
// and we don't move any fields inside the future until it gets dropped.
let mut_self: &mut ChannelSendFuture<MutexType, T> =
unsafe { Pin::get_unchecked_mut(self) };
let channel = mut_self
.channel
.expect("polled ChannelSendFuture after completion");
let send_res =
unsafe { channel.send_or_register(&mut mut_self.wait_node, cx) };
match send_res.0 {
Poll::Ready(()) => {
// Value has been transmitted or channel was closed
mut_self.channel = None;
match send_res.1 {
Some(v) => {
// Channel must have been closed
Poll::Ready(Err(ChannelSendError(v)))
}
None => Poll::Ready(Ok(())),
}
}
Poll::Pending => Poll::Pending,
}
}
}
impl<'a, MutexType, T> FusedFuture for ChannelSendFuture<'a, MutexType, T> {
fn is_terminated(&self) -> bool {
self.channel.is_none()
}
}
impl<'a, MutexType, T> Drop for ChannelSendFuture<'a, MutexType, T> {
fn drop(&mut self) {
// If this ChannelSendFuture has been polled and it was added to the
// wait queue at the channel, it must be removed before dropping.
// Otherwise the channel would access invalid memory.
if let Some(channel) = self.channel {
channel.remove_send_waiter(&mut self.wait_node);
}
}
}
#[cfg(feature = "alloc")]
mod if_alloc {
use super::*;
pub mod shared {
use super::*;
/// A Future that is returned by the `receive` function on a channel.
/// The future gets resolved with `Some(value)` when a value could be
/// received from the channel.
/// If the channels gets closed and no items are still enqueued inside the
/// channel, the future will resolve to `None`.
#[must_use = "futures do nothing unless polled"]
pub struct ChannelReceiveFuture<MutexType, T> {
/// The Channel that is associated with this ChannelReceiveFuture
pub(crate) channel:
Option<alloc::sync::Arc<dyn ChannelReceiveAccess<T>>>,
/// Node for waiting on the channel
pub(crate) wait_node: ListNode<RecvWaitQueueEntry>,
/// Marker for mutex type
pub(crate) _phantom: PhantomData<MutexType>,
}
// Safety: Channel futures can be sent between threads as long as the underlying
// channel is thread-safe (Sync), which allows to poll/register/unregister from
// a different thread.
unsafe impl<MutexType: Sync, T: Send> Send
for ChannelReceiveFuture<MutexType, T>
{
}
impl<MutexType, T> core::fmt::Debug for ChannelReceiveFuture<MutexType, T> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("ChannelReceiveFuture").finish()
}
}
impl<MutexType, T> Future for ChannelReceiveFuture<MutexType, T> {
type Output = Option<T>;
fn poll(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<T>> {
// It might be possible to use Pin::map_unchecked here instead of the two unsafe APIs.
// However this didn't seem to work for some borrow checker reasons
// Safety: The next operations are safe, because Pin promises us that
// the address of the wait queue entry inside ChannelReceiveFuture is stable,
// and we don't move any fields inside the future until it gets dropped.
let mut_self: &mut ChannelReceiveFuture<MutexType, T> =
unsafe { Pin::get_unchecked_mut(self) };
let channel = mut_self
.channel
.take()
.expect("polled ChannelReceiveFuture after completion");
let poll_res = unsafe {
channel.receive_or_register(&mut mut_self.wait_node, cx)
};
if poll_res.is_ready() {
// A value was available
mut_self.channel = None;
} else {
mut_self.channel = Some(channel)
}
poll_res
}
}
impl<MutexType, T> FusedFuture for ChannelReceiveFuture<MutexType, T> {
fn is_terminated(&self) -> bool {
self.channel.is_none()
}
}
impl<MutexType, T> Drop for ChannelReceiveFuture<MutexType, T> {
fn drop(&mut self) {
// If this ChannelReceiveFuture has been polled and it was added to the
// wait queue at the channel, it must be removed before dropping.
// Otherwise the channel would access invalid memory.
if let Some(channel) = &self.channel {
channel.remove_receive_waiter(&mut self.wait_node);
}
}
}
/// A Future that is returned by the `send` function on a channel.
/// The future gets resolved with `None` when a value could be
/// written to the channel.
/// If the channel gets closed the send operation will fail, and the
/// Future will resolve to `ChannelSendError(T)` and return the item
/// to send.
#[must_use = "futures do nothing unless polled"]
pub struct ChannelSendFuture<MutexType, T> {
/// The LocalChannel that is associated with this ChannelSendFuture
pub(crate) channel:
Option<alloc::sync::Arc<dyn ChannelSendAccess<T>>>,
/// Node for waiting on the channel
pub(crate) wait_node: ListNode<SendWaitQueueEntry<T>>,
/// Marker for mutex type
pub(crate) _phantom: PhantomData<MutexType>,
}
impl<MutexType, T> ChannelSendFuture<MutexType, T> {
/// Tries to cancel the ongoing send operation
pub fn cancel(&mut self) -> Option<T> {
let channel = self.channel.take();
match channel {
None => None,
Some(channel) => {
channel.remove_send_waiter(&mut self.wait_node);
self.wait_node.value.take()
}
}
}
}
// Safety: Channel futures can be sent between threads as long as the underlying
// channel is thread-safe (Sync), which allows to poll/register/unregister from
// a different thread.
unsafe impl<MutexType: Sync, T: Send> Send for ChannelSendFuture<MutexType, T> {}
impl<MutexType, T> core::fmt::Debug for ChannelSendFuture<MutexType, T> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("ChannelSendFuture").finish()
}
}
impl<MutexType, T> Future for ChannelSendFuture<MutexType, T> {
type Output = Result<(), ChannelSendError<T>>;
fn poll(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), ChannelSendError<T>>> {
// It might be possible to use Pin::map_unchecked here instead of the two unsafe APIs.
// However this didn't seem to work for some borrow checker reasons
// Safety: The next operations are safe, because Pin promises us that
// the address of the wait queue entry inside ChannelSendFuture is stable,
// and we don't move any fields inside the future until it gets dropped.
let mut_self: &mut ChannelSendFuture<MutexType, T> =
unsafe { Pin::get_unchecked_mut(self) };
let channel = mut_self
.channel
.take()
.expect("polled ChannelSendFuture after completion");
let send_res = unsafe {
channel.send_or_register(&mut mut_self.wait_node, cx)
};
match send_res.0 {
Poll::Ready(()) => {
// Value has been transmitted or channel was closed
match send_res.1 {
Some(v) => {
// Channel must have been closed
Poll::Ready(Err(ChannelSendError(v)))
}
None => Poll::Ready(Ok(())),
}
}
Poll::Pending => {
mut_self.channel = Some(channel);
Poll::Pending
}
}
}
}
impl<MutexType, T> FusedFuture for ChannelSendFuture<MutexType, T> {
fn is_terminated(&self) -> bool {
self.channel.is_none()
}
}
impl<MutexType, T> Drop for ChannelSendFuture<MutexType, T> {
fn drop(&mut self) {
// If this ChannelSendFuture has been polled and it was added to the
// wait queue at the channel, it must be removed before dropping.
// Otherwise the channel would access invalid memory.
if let Some(channel) = &self.channel {
channel.remove_send_waiter(&mut self.wait_node);
}
}
}
}
}
#[cfg(feature = "alloc")]
pub use self::if_alloc::*;
@@ -0,0 +1,72 @@
/// The error which is returned when sending a value into a channel fails.
///
/// The `send` operation can only fail if the channel has been closed, which
/// would prevent the other actors to ever retrieve the value.
///
/// The error recovers the value that has been sent.
#[derive(PartialEq, Debug)]
pub struct ChannelSendError<T>(pub T);
/// The error which is returned when trying to receive from a channel
/// without waiting fails.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum TryReceiveError {
/// The channel is empty. No value is available for reception.
Empty,
/// The channel had been closed and no more value is available for reception.
Closed,
}
impl TryReceiveError {
/// Returns whether the error is the `Empty` variant.
pub fn is_empty(self) -> bool {
match self {
Self::Empty => true,
_ => false,
}
}
/// Returns whether the error is the `Closed` variant.
pub fn is_closed(self) -> bool {
match self {
Self::Closed => true,
_ => false,
}
}
}
/// The error which is returned when trying to send on a channel
/// without waiting fails.
#[derive(PartialEq, Debug)]
pub enum TrySendError<T> {
/// The channel is full.
Full(T),
/// The channel was closed.
Closed(T),
}
impl<T> TrySendError<T> {
/// Converts the error into its inner value.
pub fn into_inner(self) -> T {
match self {
Self::Closed(inner) => inner,
Self::Full(inner) => inner,
}
}
/// Returns whether the error is the `WouldBlock` variant.
pub fn is_full(&self) -> bool {
match self {
Self::Full(_) => true,
_ => false,
}
}
/// Returns whether the error is the `Closed` variant.
pub fn is_closed(&self) -> bool {
match self {
Self::Closed(_) => true,
_ => false,
}
}
}
@@ -0,0 +1,69 @@
//! Asynchronous channels.
//!
//! This module provides various channels that can be used to communicate between
//! asynchronous tasks.
mod error;
pub use self::error::{ChannelSendError, TryReceiveError, TrySendError};
mod channel_future;
use channel_future::{
ChannelReceiveAccess, ChannelSendAccess, RecvPollState, RecvWaitQueueEntry,
SendPollState, SendWaitQueueEntry,
};
pub use channel_future::{
ChannelReceiveFuture, ChannelSendFuture, CloseStatus,
};
mod oneshot;
pub use self::oneshot::{GenericOneshotChannel, LocalOneshotChannel};
#[cfg(feature = "std")]
pub use self::oneshot::OneshotChannel;
mod oneshot_broadcast;
pub use self::oneshot_broadcast::{
GenericOneshotBroadcastChannel, LocalOneshotBroadcastChannel,
};
#[cfg(feature = "std")]
pub use self::oneshot_broadcast::OneshotBroadcastChannel;
mod state_broadcast;
pub use state_broadcast::{
GenericStateBroadcastChannel, LocalStateBroadcastChannel, StateId,
StateReceiveFuture,
};
#[cfg(feature = "std")]
pub use self::state_broadcast::StateBroadcastChannel;
mod mpmc;
pub use self::mpmc::{
ChannelStream, GenericChannel, LocalChannel, LocalUnbufferedChannel,
};
#[cfg(feature = "std")]
pub use self::mpmc::{Channel, UnbufferedChannel};
#[cfg(feature = "alloc")]
mod if_alloc {
/// Channel implementations where Sender and Receiver sides are cloneable
/// and owned.
/// The Futures produced by channels in this module don't require a lifetime
/// parameter.
pub mod shared {
pub use super::super::channel_future::shared::*;
pub use super::super::mpmc::shared::*;
pub use super::super::oneshot::shared::*;
pub use super::super::oneshot_broadcast::shared::*;
pub use super::super::state_broadcast::shared::*;
}
}
#[cfg(feature = "alloc")]
pub use self::if_alloc::*;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,450 @@
//! An asynchronously awaitable oneshot channel
use super::{
ChannelReceiveAccess, ChannelReceiveFuture, ChannelSendError, CloseStatus,
RecvPollState, RecvWaitQueueEntry,
};
use crate::{
intrusive_double_linked_list::{LinkedList, ListNode},
utils::update_waker_ref,
NoopLock,
};
use core::marker::PhantomData;
use futures_core::task::{Context, Poll};
use lock_api::{Mutex, RawMutex};
fn wake_waiters(waiters: &mut LinkedList<RecvWaitQueueEntry>) {
// Remove all waiters from the waiting list in reverse order and wake them.
// We reverse the waiter list, so that the oldest waker (which is
// at the end of the list), gets woken first and has the best
// chance to grab the channel value.
waiters.reverse_drain(|waiter| {
if let Some(handle) = waiter.task.take() {
handle.wake();
}
waiter.state = RecvPollState::Unregistered;
});
}
/// Internal state of the oneshot channel
struct ChannelState<T> {
/// Whether the channel had been fulfilled before
is_fulfilled: bool,
/// The value which is stored inside the channel
value: Option<T>,
/// The list of waiters, which are waiting for the channel to get fulfilled
waiters: LinkedList<RecvWaitQueueEntry>,
}
impl<T> ChannelState<T> {
fn new() -> ChannelState<T> {
ChannelState::<T> {
is_fulfilled: false,
value: None,
waiters: LinkedList::new(),
}
}
/// Writes a single value to the channel.
/// If a value had been written to the channel before, the new value will be rejected.
fn send(&mut self, value: T) -> Result<(), ChannelSendError<T>> {
if self.is_fulfilled {
return Err(ChannelSendError(value));
}
self.value = Some(value);
self.is_fulfilled = true;
// Wakeup all waiters
wake_waiters(&mut self.waiters);
Ok(())
}
fn close(&mut self) -> CloseStatus {
if self.is_fulfilled {
return CloseStatus::AlreadyClosed;
}
self.is_fulfilled = true;
// Wakeup all waiters
wake_waiters(&mut self.waiters);
CloseStatus::NewlyClosed
}
/// Tries to read the value from the channel.
/// If the value isn't available yet, the ChannelReceiveFuture gets added to the
/// wait queue at the channel, and will be signalled once ready.
/// This function is only safe as long as the `wait_node`s address is guaranteed
/// to be stable until it gets removed from the queue.
unsafe fn try_receive(
&mut self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<Option<T>> {
match wait_node.state {
RecvPollState::Unregistered => {
let maybe_val = self.value.take();
match maybe_val {
Some(v) => {
// A value was available inside the channel and was fetched
Poll::Ready(Some(v))
}
None => {
// Check if something was written into the channel before
// or the channel was closed.
if self.is_fulfilled {
Poll::Ready(None)
} else {
// Added the task to the wait queue
wait_node.task = Some(cx.waker().clone());
wait_node.state = RecvPollState::Registered;
self.waiters.add_front(wait_node);
Poll::Pending
}
}
}
}
RecvPollState::Registered => {
// Since the channel wakes up all waiters and moves their states
// to unregistered there can't be any value in the channel in this state.
// However the caller might have passed a different `Waker`.
// In this case we need to update it.
update_waker_ref(&mut wait_node.task, cx);
Poll::Pending
}
RecvPollState::Notified => {
unreachable!("Not possible for Oneshot");
}
}
}
fn remove_waiter(&mut self, wait_node: &mut ListNode<RecvWaitQueueEntry>) {
// ChannelReceiveFuture only needs to get removed if it had been added to
// the wait queue of the channel. This has happened in the RecvPollState::Waiting case.
if let RecvPollState::Registered = wait_node.state {
// Safety: Due to the state, we know that the node must be part
// of the waiter list
if !unsafe { self.waiters.remove(wait_node) } {
// Panic if the address isn't found. This can only happen if the contract was
// violated, e.g. the RecvWaitQueueEntry got moved after the initial poll.
panic!("Future could not be removed from wait queue");
}
wait_node.state = RecvPollState::Unregistered;
}
}
}
/// A channel which can be used to exchange a single value between two
/// concurrent tasks.
///
/// Tasks can wait for the value to get delivered via `receive`.
/// The returned Future will get fulfilled when a value is sent into the channel.
///
/// The value can only be extracted by a single receiving task. Once the value
/// has been retrieved from the Channel, the Channel is closed and subsequent
/// receive calls will return `None`.
pub struct GenericOneshotChannel<MutexType: RawMutex, T> {
inner: Mutex<MutexType, ChannelState<T>>,
}
// The channel can be sent to other threads as long as it's not borrowed and the
// value in it can be sent to other threads.
unsafe impl<MutexType: RawMutex + Send, T: Send> Send
for GenericOneshotChannel<MutexType, T>
{
}
// The channel is thread-safe as long as a thread-safe mutex is used
unsafe impl<MutexType: RawMutex + Sync, T: Send> Sync
for GenericOneshotChannel<MutexType, T>
{
}
impl<MutexType: RawMutex, T> core::fmt::Debug
for GenericOneshotChannel<MutexType, T>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("GenericOneshotChannel").finish()
}
}
impl<MutexType: RawMutex, T> GenericOneshotChannel<MutexType, T> {
/// Creates a new OneshotChannel in the given state
pub fn new() -> GenericOneshotChannel<MutexType, T> {
GenericOneshotChannel {
inner: Mutex::new(ChannelState::new()),
}
}
/// Writes a single value to the channel.
///
/// This will notify waiters about the availability of the value.
/// If a value had been written to the channel before, or if the
/// channel is closed, the new value will be rejected and
/// returned inside the error variant.
pub fn send(&self, value: T) -> Result<(), ChannelSendError<T>> {
self.inner.lock().send(value)
}
/// Closes the channel.
///
/// This will notify waiters about closure, by fulfilling pending `Future`s
/// with `None`.
/// `send(value)` attempts which follow this call will fail with a
/// [`ChannelSendError`].
pub fn close(&self) -> CloseStatus {
self.inner.lock().close()
}
/// Returns a future that gets fulfilled when a value is written to the channel
/// or the channel is closed.
pub fn receive(&self) -> ChannelReceiveFuture<MutexType, T> {
ChannelReceiveFuture {
channel: Some(self),
wait_node: ListNode::new(RecvWaitQueueEntry::new()),
_phantom: PhantomData,
}
}
}
impl<MutexType: RawMutex, T> ChannelReceiveAccess<T>
for GenericOneshotChannel<MutexType, T>
{
unsafe fn receive_or_register(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<Option<T>> {
self.inner.lock().try_receive(wait_node, cx)
}
fn remove_receive_waiter(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
) {
self.inner.lock().remove_waiter(wait_node)
}
}
// Export a non thread-safe version using NoopLock
/// A [`GenericOneshotChannel`] which is not thread-safe.
pub type LocalOneshotChannel<T> = GenericOneshotChannel<NoopLock, T>;
#[cfg(feature = "std")]
mod if_std {
use super::*;
// Export a thread-safe version using parking_lot::RawMutex
/// A [`GenericOneshotChannel`] implementation backed by [`parking_lot`].
pub type OneshotChannel<T> =
GenericOneshotChannel<parking_lot::RawMutex, T>;
}
#[cfg(feature = "std")]
pub use self::if_std::*;
#[cfg(feature = "alloc")]
mod if_alloc {
use super::*;
pub mod shared {
use super::*;
use crate::channel::shared::ChannelReceiveFuture;
struct GenericOneshotChannelSharedState<MutexType, T>
where
MutexType: RawMutex,
T: 'static,
{
channel: GenericOneshotChannel<MutexType, T>,
}
// Implement ChannelReceiveAccess trait for SharedChannelState, so that it can
// be used for dynamic dispatch in futures.
impl<MutexType, T> ChannelReceiveAccess<T>
for GenericOneshotChannelSharedState<MutexType, T>
where
MutexType: RawMutex,
{
unsafe fn receive_or_register(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<Option<T>> {
self.channel.receive_or_register(wait_node, cx)
}
fn remove_receive_waiter(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
) {
self.channel.remove_receive_waiter(wait_node)
}
}
/// The sending side of a channel which can be used to exchange values
/// between concurrent tasks.
///
/// Values can be sent into the channel through `send`.
pub struct GenericOneshotSender<MutexType, T>
where
MutexType: RawMutex,
T: 'static,
{
inner: alloc::sync::Arc<
GenericOneshotChannelSharedState<MutexType, T>,
>,
}
/// The receiving side of a channel which can be used to exchange values
/// between concurrent tasks.
///
/// Tasks can receive values from the channel through the `receive` method.
/// The returned Future will get resolved when a value is sent into the channel.
pub struct GenericOneshotReceiver<MutexType, T>
where
MutexType: RawMutex,
T: 'static,
{
inner: alloc::sync::Arc<
GenericOneshotChannelSharedState<MutexType, T>,
>,
}
impl<MutexType, T> core::fmt::Debug for GenericOneshotSender<MutexType, T>
where
MutexType: RawMutex,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("OneshotSender").finish()
}
}
impl<MutexType, T> core::fmt::Debug for GenericOneshotReceiver<MutexType, T>
where
MutexType: RawMutex,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("OneshotReceiver").finish()
}
}
impl<MutexType, T> Drop for GenericOneshotSender<MutexType, T>
where
MutexType: RawMutex,
{
fn drop(&mut self) {
// Close the channel, before last sender gets destroyed
// TODO: We could potentially avoid this, if no receiver is left
self.inner.channel.close();
}
}
impl<MutexType, T> Drop for GenericOneshotReceiver<MutexType, T>
where
MutexType: RawMutex,
{
fn drop(&mut self) {
// Close the channel, before last receiver gets destroyed
// TODO: We could potentially avoid this, if no sender is left
self.inner.channel.close();
}
}
/// Creates a new oneshot channel which can be used to exchange values
/// of type `T` between concurrent tasks.
/// The ends of the Channel are represented through
/// the returned Sender and Receiver.
///
/// As soon es either the senders or receivers is closed, the channel
/// itself will be closed.
pub fn generic_oneshot_channel<MutexType, T>() -> (
GenericOneshotSender<MutexType, T>,
GenericOneshotReceiver<MutexType, T>,
)
where
MutexType: RawMutex,
T: Send,
{
let inner =
alloc::sync::Arc::new(GenericOneshotChannelSharedState {
channel: GenericOneshotChannel::new(),
});
let sender = GenericOneshotSender {
inner: inner.clone(),
};
let receiver = GenericOneshotReceiver { inner };
(sender, receiver)
}
impl<MutexType, T> GenericOneshotSender<MutexType, T>
where
MutexType: RawMutex + 'static,
{
/// Writes a single value to the channel.
///
/// This will notify waiters about the availability of the value.
/// If a value had been written to the channel before, or if the
/// channel is closed, the new value will be rejected and
/// returned inside the error variant.
pub fn send(&self, value: T) -> Result<(), ChannelSendError<T>> {
self.inner.channel.send(value)
}
}
impl<MutexType, T> GenericOneshotReceiver<MutexType, T>
where
MutexType: RawMutex + 'static,
{
/// Returns a future that gets fulfilled when a value is written to the channel.
/// If the channels gets closed, the future will resolve to `None`.
pub fn receive(&self) -> ChannelReceiveFuture<MutexType, T> {
ChannelReceiveFuture {
channel: Some(self.inner.clone()),
wait_node: ListNode::new(RecvWaitQueueEntry::new()),
_phantom: PhantomData,
}
}
}
// Export parking_lot based shared channels in std mode
#[cfg(feature = "std")]
mod if_std {
use super::*;
/// A [`GenericOneshotSender`] implementation backed by [`parking_lot`].
pub type OneshotSender<T> =
GenericOneshotSender<parking_lot::RawMutex, T>;
/// A [`GenericOneshotReceiver`] implementation backed by [`parking_lot`].
pub type OneshotReceiver<T> =
GenericOneshotReceiver<parking_lot::RawMutex, T>;
/// Creates a new oneshot channel.
///
/// Refer to [`generic_oneshot_channel`] for details.
///
/// Example for creating a channel to transmit an integer value:
///
/// ```
/// # use futures_intrusive::channel::shared::oneshot_channel;
/// let (sender, receiver) = oneshot_channel::<i32>();
/// ```
pub fn oneshot_channel<T>() -> (OneshotSender<T>, OneshotReceiver<T>)
where
T: Send,
{
generic_oneshot_channel::<parking_lot::RawMutex, T>()
}
}
#[cfg(feature = "std")]
pub use self::if_std::*;
}
}
#[cfg(feature = "alloc")]
pub use self::if_alloc::*;
@@ -0,0 +1,487 @@
//! An asynchronously awaitable oneshot channel which can be awaited by
//! multiple consumers.
use super::{
ChannelReceiveAccess, ChannelReceiveFuture, ChannelSendError, CloseStatus,
RecvPollState, RecvWaitQueueEntry,
};
use crate::{
intrusive_double_linked_list::{LinkedList, ListNode},
utils::update_waker_ref,
NoopLock,
};
use core::marker::PhantomData;
use futures_core::task::{Context, Poll};
use lock_api::{Mutex, RawMutex};
fn wake_waiters(waiters: &mut LinkedList<RecvWaitQueueEntry>) {
// Remove all waiters from the waiting list in reverse order and wake them.
// We reverse the waiter list, so that the oldest waker (which is
// at the end of the list), gets woken first and has the best
// chance to grab the channel value.
waiters.reverse_drain(|waiter| {
if let Some(handle) = waiter.task.take() {
handle.wake();
}
waiter.state = RecvPollState::Unregistered;
});
}
/// Internal state of the oneshot channel
struct ChannelState<T> {
/// Whether the channel had been fulfilled before
is_fulfilled: bool,
/// The value which is stored inside the channel
value: Option<T>,
/// The list of waiters, which are waiting for the channel to get fulfilled
waiters: LinkedList<RecvWaitQueueEntry>,
}
impl<T> ChannelState<T>
where
T: Clone,
{
fn new() -> ChannelState<T> {
ChannelState::<T> {
is_fulfilled: false,
value: None,
waiters: LinkedList::new(),
}
}
/// Writes a single value to the channel.
/// If a value had been written to the channel before, the new value will be rejected.
fn send(&mut self, value: T) -> Result<(), ChannelSendError<T>> {
if self.is_fulfilled {
return Err(ChannelSendError(value));
}
self.value = Some(value);
self.is_fulfilled = true;
// Wakeup all waiters
wake_waiters(&mut self.waiters);
Ok(())
}
fn close(&mut self) -> CloseStatus {
if self.is_fulfilled {
return CloseStatus::AlreadyClosed;
}
self.is_fulfilled = true;
// Wakeup all waiters
wake_waiters(&mut self.waiters);
CloseStatus::NewlyClosed
}
/// Tries to read the value from the channel.
/// If the value isn't available yet, the ChannelReceiveFuture gets added to the
/// wait queue at the channel, and will be signalled once ready.
/// This function is only safe as long as the `wait_node`s address is guaranteed
/// to be stable until it gets removed from the queue.
unsafe fn try_receive(
&mut self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<Option<T>> {
match wait_node.state {
RecvPollState::Unregistered => {
match &self.value {
Some(v) => {
// A value was available inside the channel and was fetched.
// TODO: If the same waiter asks again, they will always
// get the same value, instead of `None`. Is that reasonable?
Poll::Ready(Some(v.clone()))
}
None => {
// Check if something was written into the channel before
// or the channel was closed.
if self.is_fulfilled {
Poll::Ready(None)
} else {
// Added the task to the wait queue
wait_node.task = Some(cx.waker().clone());
wait_node.state = RecvPollState::Registered;
self.waiters.add_front(wait_node);
Poll::Pending
}
}
}
}
RecvPollState::Registered => {
// Since the channel wakes up all waiters and moves their states
// to unregistered there can't be any value in the channel in this state.
// However the caller might have passed a different `Waker`.
// In this case we need to update it.
update_waker_ref(&mut wait_node.task, cx);
Poll::Pending
}
RecvPollState::Notified => {
unreachable!("Not possible for Oneshot Broadcast");
}
}
}
fn remove_waiter(&mut self, wait_node: &mut ListNode<RecvWaitQueueEntry>) {
// ChannelReceiveFuture only needs to get removed if it had been added to
// the wait queue of the channel. This has happened in the RecvPollState::Waiting case.
if let RecvPollState::Registered = wait_node.state {
// Safety: Due to the state, we know that the node must be part
// of the waiter list
if !unsafe { self.waiters.remove(wait_node) } {
// Panic if the address isn't found. This can only happen if the contract was
// violated, e.g. the RecvWaitQueueEntry got moved after the initial poll.
panic!("Future could not be removed from wait queue");
}
wait_node.state = RecvPollState::Unregistered;
}
}
}
/// A channel which can be used to exchange a single value between two or more
/// concurrent tasks.
///
/// The value which gets sent will get stored inside the Channel, and can be
/// retrieved by an arbitrary number of tasks afterwards.
///
/// Tasks can wait for the value to get delivered via `receive`.
/// The returned Future will get fulfilled when a value is sent into the channel.
pub struct GenericOneshotBroadcastChannel<MutexType: RawMutex, T> {
inner: Mutex<MutexType, ChannelState<T>>,
}
// The channel can be sent to other threads as long as it's not borrowed and the
// value in it can be sent to other threads.
unsafe impl<MutexType: RawMutex + Send, T: Send> Send
for GenericOneshotBroadcastChannel<MutexType, T>
{
}
// The channel is thread-safe as long as a thread-safe mutex is used
unsafe impl<MutexType: RawMutex + Sync, T: Send> Sync
for GenericOneshotBroadcastChannel<MutexType, T>
{
}
impl<MutexType: RawMutex, T> core::fmt::Debug
for GenericOneshotBroadcastChannel<MutexType, T>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("GenericOneshotBroadcastChannel").finish()
}
}
impl<MutexType: RawMutex, T> GenericOneshotBroadcastChannel<MutexType, T>
where
T: Clone,
{
/// Creates a new OneshotBroadcastChannel in the given state
pub fn new() -> GenericOneshotBroadcastChannel<MutexType, T> {
GenericOneshotBroadcastChannel {
inner: Mutex::new(ChannelState::new()),
}
}
/// Writes a single value to the channel.
///
/// This will notify waiters about the availability of the value.
/// If a value had been written to the channel before, or if the
/// channel is closed, the new value will be rejected and
/// returned inside the error variant.
pub fn send(&self, value: T) -> Result<(), ChannelSendError<T>> {
self.inner.lock().send(value)
}
/// Closes the channel.
///
/// This will notify waiters about closure, by fulfilling pending `Future`s
/// with `None`.
/// `send(value)` attempts which follow this call will fail with a
/// [`ChannelSendError`].
pub fn close(&self) -> CloseStatus {
self.inner.lock().close()
}
/// Returns a future that gets fulfilled when a value is written to the channel
/// or the channel is closed.
pub fn receive(&self) -> ChannelReceiveFuture<MutexType, T> {
ChannelReceiveFuture {
channel: Some(self),
wait_node: ListNode::new(RecvWaitQueueEntry::new()),
_phantom: PhantomData,
}
}
}
impl<MutexType: RawMutex, T> ChannelReceiveAccess<T>
for GenericOneshotBroadcastChannel<MutexType, T>
where
T: Clone,
{
unsafe fn receive_or_register(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<Option<T>> {
self.inner.lock().try_receive(wait_node, cx)
}
fn remove_receive_waiter(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
) {
self.inner.lock().remove_waiter(wait_node)
}
}
// Export a non thread-safe version using NoopLock
/// A [`GenericOneshotBroadcastChannel`] which is not thread-safe.
pub type LocalOneshotBroadcastChannel<T> =
GenericOneshotBroadcastChannel<NoopLock, T>;
#[cfg(feature = "std")]
mod if_std {
use super::*;
// Export a thread-safe version using parking_lot::RawMutex
/// A [`GenericOneshotBroadcastChannel`] implementation backed by [`parking_lot`].
pub type OneshotBroadcastChannel<T> =
GenericOneshotBroadcastChannel<parking_lot::RawMutex, T>;
}
#[cfg(feature = "std")]
pub use self::if_std::*;
#[cfg(feature = "alloc")]
mod if_alloc {
use super::*;
pub mod shared {
use super::*;
use crate::channel::shared::ChannelReceiveFuture;
struct GenericOneshotChannelSharedState<MutexType, T>
where
MutexType: RawMutex,
T: 'static,
{
channel: GenericOneshotBroadcastChannel<MutexType, T>,
}
// Implement ChannelReceiveAccess trait for SharedChannelState, so that it can
// be used for dynamic dispatch in futures.
impl<MutexType, T> ChannelReceiveAccess<T>
for GenericOneshotChannelSharedState<MutexType, T>
where
MutexType: RawMutex,
T: Clone,
{
unsafe fn receive_or_register(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<Option<T>> {
self.channel.receive_or_register(wait_node, cx)
}
fn remove_receive_waiter(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
) {
self.channel.remove_receive_waiter(wait_node)
}
}
/// The sending side of a channel which can be used to exchange values
/// between concurrent tasks.
///
/// Values can be sent into the channel through `send`.
pub struct GenericOneshotBroadcastSender<MutexType, T>
where
MutexType: RawMutex,
T: Clone + 'static,
{
inner: alloc::sync::Arc<
GenericOneshotChannelSharedState<MutexType, T>,
>,
}
/// The receiving side of a channel which can be used to exchange values
/// between concurrent tasks.
///
/// Tasks can receive values from the channel through the `receive` method.
/// The returned Future will get resolved when a value is sent into the channel.
pub struct GenericOneshotBroadcastReceiver<MutexType, T>
where
MutexType: RawMutex,
T: Clone + 'static,
{
inner: alloc::sync::Arc<
GenericOneshotChannelSharedState<MutexType, T>,
>,
}
// Manual `Clone` implementation, since #[derive(Clone)] also requires
// the Mutex to be `Clone`
impl<MutexType, T> Clone for GenericOneshotBroadcastReceiver<MutexType, T>
where
MutexType: RawMutex,
T: Clone + 'static,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<MutexType, T> core::fmt::Debug
for GenericOneshotBroadcastSender<MutexType, T>
where
MutexType: RawMutex,
T: Clone,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("OneshotBroadcastSender").finish()
}
}
impl<MutexType, T> core::fmt::Debug
for GenericOneshotBroadcastReceiver<MutexType, T>
where
MutexType: RawMutex,
T: Clone,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("OneshotBroadcastReceiver").finish()
}
}
impl<MutexType, T> Drop for GenericOneshotBroadcastSender<MutexType, T>
where
MutexType: RawMutex,
T: Clone,
{
fn drop(&mut self) {
// Close the channel, before last sender gets destroyed
// TODO: We could potentially avoid this, if no receiver is left
self.inner.channel.close();
}
}
impl<MutexType, T> Drop for GenericOneshotBroadcastReceiver<MutexType, T>
where
MutexType: RawMutex,
T: Clone,
{
fn drop(&mut self) {
// TODO: This is broken, since it will already close the channel if only one receiver is closed.
// We need to count receivers, as in mpmc queue.
// Close the channel, before last receiver gets destroyed
// TODO: We could potentially avoid this, if no sender is left
self.inner.channel.close();
}
}
/// Creates a new oneshot broadcast channel which can be used to exchange values
/// of type `T` between concurrent tasks.
/// The ends of the Channel are represented through
/// the returned `Sender` and `Receiver`. The `Receiver` can be cloned.
///
/// As soon es either the senders or all receivers is closed, the channel
/// itself will be closed.
pub fn generic_oneshot_broadcast_channel<MutexType, T>() -> (
GenericOneshotBroadcastSender<MutexType, T>,
GenericOneshotBroadcastReceiver<MutexType, T>,
)
where
MutexType: RawMutex,
T: Send + Clone,
{
let inner =
alloc::sync::Arc::new(GenericOneshotChannelSharedState {
channel: GenericOneshotBroadcastChannel::new(),
});
let sender = GenericOneshotBroadcastSender {
inner: inner.clone(),
};
let receiver = GenericOneshotBroadcastReceiver { inner };
(sender, receiver)
}
impl<MutexType, T> GenericOneshotBroadcastSender<MutexType, T>
where
MutexType: RawMutex + 'static,
T: Clone,
{
/// Writes a single value to the channel.
///
/// This will notify waiters about the availability of the value.
/// If a value had been written to the channel before, or if the
/// channel is closed, the new value will be rejected and
/// returned inside the error variant.
pub fn send(&self, value: T) -> Result<(), ChannelSendError<T>> {
self.inner.channel.send(value)
}
}
impl<MutexType, T> GenericOneshotBroadcastReceiver<MutexType, T>
where
MutexType: RawMutex + 'static,
T: Clone,
{
/// Returns a future that gets fulfilled when a value is written to the channel.
/// If the channels gets closed, the future will resolve to `None`.
pub fn receive(&self) -> ChannelReceiveFuture<MutexType, T> {
ChannelReceiveFuture {
channel: Some(self.inner.clone()),
wait_node: ListNode::new(RecvWaitQueueEntry::new()),
_phantom: PhantomData,
}
}
}
// Export parking_lot based shared channels in std mode
#[cfg(feature = "std")]
mod if_std {
use super::*;
/// A [`GenericOneshotBroadcastSender`] implementation backed by [`parking_lot`].
pub type OneshotBroadcastSender<T> =
GenericOneshotBroadcastSender<parking_lot::RawMutex, T>;
/// A [`GenericOneshotBroadcastReceiver`] implementation backed by [`parking_lot`].
pub type OneshotBroadcastReceiver<T> =
GenericOneshotBroadcastReceiver<parking_lot::RawMutex, T>;
/// Creates a new oneshot broadcast channel.
///
/// Refer to [`generic_oneshot_broadcast_channel`] for details.
///
/// Example for creating a channel to transmit an integer value:
///
/// ```
/// # use futures_intrusive::channel::shared::oneshot_broadcast_channel;
/// let (sender, receiver) = oneshot_broadcast_channel::<i32>();
/// ```
pub fn oneshot_broadcast_channel<T>(
) -> (OneshotBroadcastSender<T>, OneshotBroadcastReceiver<T>)
where
T: Send + Clone,
{
generic_oneshot_broadcast_channel::<parking_lot::RawMutex, T>()
}
}
#[cfg(feature = "std")]
pub use self::if_std::*;
}
}
#[cfg(feature = "alloc")]
pub use self::if_alloc::*;
@@ -0,0 +1,813 @@
//! An asynchronously awaitable state broadcasting channel
use super::{ChannelSendError, CloseStatus};
use crate::{
intrusive_double_linked_list::{LinkedList, ListNode},
utils::update_waker_ref,
NoopLock,
};
use core::marker::PhantomData;
use core::pin::Pin;
use futures_core::{
future::{FusedFuture, Future},
task::{Context, Poll, Waker},
};
use lock_api::{Mutex, RawMutex};
/// An ID, which allows to differentiate states received from a Channel.
/// Elements with a bigger state ID (`id > otherId`) have been published more
/// recently into the Channel.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Ord, PartialOrd)]
pub struct StateId(u64);
impl StateId {
/// Returns the initial StateId, which is guaranteed to return the
/// oldest buffered value available.
pub fn new() -> Self {
StateId(0)
}
}
/// Tracks how the future had interacted with the channel
#[derive(PartialEq, Debug)]
pub enum RecvPollState {
/// The task is not registered at the wait queue at the channel
Unregistered,
/// The task was added to the wait queue at the channel.
Registered,
}
/// Tracks the channel futures waiting state.
/// Access to this struct is synchronized through the channel.
#[derive(Debug)]
pub struct RecvWaitQueueEntry {
/// The task handle of the waiting task
task: Option<Waker>,
/// Current polling state
state: RecvPollState,
/// The minimum state ID we are waiting for
state_id: StateId,
}
impl RecvWaitQueueEntry {
/// Creates a new RecvWaitQueueEntry
pub fn new(state_id: StateId) -> RecvWaitQueueEntry {
RecvWaitQueueEntry {
task: None,
state_id,
state: RecvPollState::Unregistered,
}
}
}
/// Adapter trait that allows Futures to generically interact with Channel
/// implementations via dynamic dispatch.
pub trait ChannelReceiveAccess<T> {
unsafe fn receive_or_register(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<Option<(StateId, T)>>;
fn remove_receive_waiter(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
);
}
/// A Future that is returned by the `receive` function on a state broadcast channel.
/// The future gets resolved with `Some((state_id, state))` when a value could be
/// received from the channel.
///
/// `state` represents the new state which had been retrieved from the channel.
///
/// `state_id` is the [`StateId`] which can be passed as a parameter to
/// `receive()` in order to fetch the next state from the channel.
///
/// If the channels gets closed and no items are still enqueued inside the
/// channel, the future will resolve to `None`.
#[must_use = "futures do nothing unless polled"]
pub struct StateReceiveFuture<'a, MutexType, T>
where
T: Clone,
{
/// The channel that is associated with this StateReceiveFuture
channel: Option<&'a dyn ChannelReceiveAccess<T>>,
/// Node for waiting on the channel
wait_node: ListNode<RecvWaitQueueEntry>,
/// Marker for mutex type
_phantom: PhantomData<MutexType>,
}
// Safety: Channel futures can be sent between threads as long as the underlying
// channel is thread-safe (Sync), which allows to poll/register/unregister from
// a different thread.
unsafe impl<'a, MutexType: Sync, T: Clone + Send> Send
for StateReceiveFuture<'a, MutexType, T>
{
}
impl<'a, MutexType, T: Clone> core::fmt::Debug
for StateReceiveFuture<'a, MutexType, T>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("StateReceiveFuture").finish()
}
}
impl<'a, MutexType, T: Clone> Future for StateReceiveFuture<'a, MutexType, T> {
type Output = Option<(StateId, T)>;
fn poll(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<(StateId, T)>> {
// It might be possible to use Pin::map_unchecked here instead of the two unsafe APIs.
// However this didn't seem to work for some borrow checker reasons
// Safety: The next operations are safe, because Pin promises us that
// the address of the wait queue entry inside StateReceiveFuture is stable,
// and we don't move any fields inside the future until it gets dropped.
let mut_self: &mut StateReceiveFuture<MutexType, T> =
unsafe { Pin::get_unchecked_mut(self) };
let channel = mut_self
.channel
.expect("polled StateReceiveFuture after completion");
let poll_res =
unsafe { channel.receive_or_register(&mut mut_self.wait_node, cx) };
if poll_res.is_ready() {
// A value was available
mut_self.channel = None;
}
poll_res
}
}
impl<'a, MutexType, T: Clone> FusedFuture
for StateReceiveFuture<'a, MutexType, T>
{
fn is_terminated(&self) -> bool {
self.channel.is_none()
}
}
impl<'a, MutexType, T: Clone> Drop for StateReceiveFuture<'a, MutexType, T> {
fn drop(&mut self) {
// If this StateReceiveFuture has been polled and it was added to the
// wait queue at the channel, it must be removed before dropping.
// Otherwise the channel would access invalid memory.
if let Some(channel) = self.channel {
channel.remove_receive_waiter(&mut self.wait_node);
}
}
}
fn wake_waiters(waiters: &mut LinkedList<RecvWaitQueueEntry>) {
// Remove all waiters from the waiting list in reverse order and wake them.
// We reverse the waiter list, so that the oldest waker (which is
// at the end of the list), gets woken first and has the best
// chance to grab the channel value.
waiters.reverse_drain(|waiter| {
if let Some(handle) = waiter.task.take() {
handle.wake();
}
waiter.state = RecvPollState::Unregistered;
});
}
/// Internal state of the state broadcast channel
struct ChannelState<T> {
/// Whether the channel was actively closed
is_closed: bool,
/// The ID of the next state.
state_id: StateId,
/// The value which is stored inside the channel
value: Option<T>,
/// The list of waiters, which are waiting for the channel to get fulfilled
waiters: LinkedList<RecvWaitQueueEntry>,
}
impl<T> ChannelState<T>
where
T: Clone,
{
fn new() -> ChannelState<T> {
ChannelState::<T> {
is_closed: false,
state_id: StateId(0),
value: None,
waiters: LinkedList::new(),
}
}
/// Writes a single value to the channel.
/// If the maximum amount of values had been written, the new value will be rejected.
fn send(&mut self, value: T) -> Result<(), ChannelSendError<T>> {
if self.is_closed || self.state_id.0 == core::u64::MAX {
return Err(ChannelSendError(value));
}
self.value = Some(value);
self.state_id.0 += 1;
// Wakeup all waiters
wake_waiters(&mut self.waiters);
Ok(())
}
fn close(&mut self) -> CloseStatus {
if self.is_closed {
return CloseStatus::AlreadyClosed;
}
self.is_closed = true;
// Wakeup all waiters
wake_waiters(&mut self.waiters);
CloseStatus::NewlyClosed
}
fn try_receive(&mut self, state_id: StateId) -> Option<(StateId, T)> {
let val = self.value.as_ref()?;
if state_id < self.state_id {
Some((self.state_id, val.clone()))
} else {
None
}
}
/// Tries to read the value from the channel.
/// If the value isn't available yet, the StateReceiveFuture gets added to the
/// wait queue at the channel, and will be signalled once ready.
/// This function is only safe as long as the `wait_node`s address is guaranteed
/// to be stable until it gets removed from the queue.
unsafe fn receive_or_register(
&mut self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<Option<(StateId, T)>> {
match wait_node.state {
RecvPollState::Unregistered => {
// The caller must wait for a value if either there is no value
// available yet, or if the value isn't newer than what the
// caller requested.
let val_to_deliver = match &self.value {
Some(ref v) if wait_node.state_id < self.state_id => {
Some(v.clone())
}
Some(_) | None => None,
};
match val_to_deliver {
Some(v) => {
// A value that satisfies the caller is available.
Poll::Ready(Some((self.state_id, v)))
}
None => {
// Check if something was written into the channel before
// or the channel was closed.
if self.is_closed {
Poll::Ready(None)
} else {
// Added the task to the wait queue
wait_node.task = Some(cx.waker().clone());
wait_node.state = RecvPollState::Registered;
self.waiters.add_front(wait_node);
Poll::Pending
}
}
}
}
RecvPollState::Registered => {
// Since the channel wakes up all waiters and moves their states
// to unregistered there can't be any value in the channel in this state.
// However the caller might have passed a different `Waker`.
// In this case we need to update it.
update_waker_ref(&mut wait_node.task, cx);
Poll::Pending
}
}
}
fn remove_waiter(&mut self, wait_node: &mut ListNode<RecvWaitQueueEntry>) {
// StateReceiveFuture only needs to get removed if it had been added to
// the wait queue of the channel. This has happened in the RecvPollState::Waiting case.
if let RecvPollState::Registered = wait_node.state {
// Safety: Due to the state, we know that the node must be part
// of the waiter list
if !unsafe { self.waiters.remove(wait_node) } {
// Panic if the address isn't found. This can only happen if the contract was
// violated, e.g. the RecvWaitQueueEntry got moved after the initial poll.
panic!("Future could not be removed from wait queue");
}
wait_node.state = RecvPollState::Unregistered;
}
}
}
/// A channel which can be used to synchronize the state between a sender an
/// arbitrary number of receivers.
///
/// The sender can publish its state.
///
/// The receivers can wait for state updates by announcing the most recent state
/// that is already known to them.
pub struct GenericStateBroadcastChannel<MutexType: RawMutex, T> {
inner: Mutex<MutexType, ChannelState<T>>,
}
// The channel can be sent to other threads as long as it's not borrowed and the
// value in it can be sent to other threads.
unsafe impl<MutexType: RawMutex + Send, T: Send> Send
for GenericStateBroadcastChannel<MutexType, T>
{
}
// The channel is thread-safe as long as a thread-safe mutex is used
unsafe impl<MutexType: RawMutex + Sync, T: Send> Sync
for GenericStateBroadcastChannel<MutexType, T>
{
}
impl<MutexType: RawMutex, T> core::fmt::Debug
for GenericStateBroadcastChannel<MutexType, T>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("GenericStateBroadcastChannel").finish()
}
}
impl<MutexType: RawMutex, T> GenericStateBroadcastChannel<MutexType, T>
where
T: Clone,
{
/// Creates a new State Broadcast Channel in the given state
pub fn new() -> GenericStateBroadcastChannel<MutexType, T>
where
T: Clone,
{
GenericStateBroadcastChannel {
inner: Mutex::new(ChannelState::new()),
}
}
/// Writes a single value to the channel.
///
/// This will notify waiters about the availability of the value.
/// If the maximum amount of values had been written to the channel,
/// or if the channel is closed, the new value will be rejected and
/// returned inside the error variant.
pub fn send(&self, value: T) -> Result<(), ChannelSendError<T>> {
self.inner.lock().send(value)
}
/// Closes the channel.
///
/// This will notify waiters about closure, by fulfilling pending `Future`s
/// with `None`.
/// `send(value)` attempts which follow this call will fail with a
/// [`ChannelSendError`].
pub fn close(&self) -> CloseStatus {
self.inner.lock().close()
}
/// Returns a future that gets fulfilled when a value is written to the channel
/// or the channel is closed.
/// `state_id` specifies the minimum state ID that should be retrieved
/// by the `receive` operation.
///
/// The returned [`StateReceiveFuture`] will get fulfilled with the
/// retrieved value as well as the [`StateId`] which is required to retrieve
/// the following state.
pub fn receive(
&self,
state_id: StateId,
) -> StateReceiveFuture<MutexType, T> {
StateReceiveFuture {
channel: Some(self),
wait_node: ListNode::new(RecvWaitQueueEntry::new(state_id)),
_phantom: PhantomData,
}
}
/// Attempt to retrieve a value whose `StateId` is greater than the one provided.
///
/// Returns `None` if no value is found in the channel, or if the current `StateId`
/// of the value is less or equal to the one provided.
pub fn try_receive(&self, state_id: StateId) -> Option<(StateId, T)> {
self.inner.lock().try_receive(state_id)
}
}
impl<MutexType: RawMutex, T: Clone> ChannelReceiveAccess<T>
for GenericStateBroadcastChannel<MutexType, T>
{
unsafe fn receive_or_register(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<Option<(StateId, T)>> {
self.inner.lock().receive_or_register(wait_node, cx)
}
fn remove_receive_waiter(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
) {
self.inner.lock().remove_waiter(wait_node)
}
}
// Export a non thread-safe version using NoopLock
/// A [`GenericStateBroadcastChannel`] which is not thread-safe.
pub type LocalStateBroadcastChannel<T> =
GenericStateBroadcastChannel<NoopLock, T>;
#[cfg(feature = "std")]
mod if_std {
use super::*;
// Export a thread-safe version using parking_lot::RawMutex
/// A [`GenericStateBroadcastChannel`] implementation backed by [`parking_lot`].
pub type StateBroadcastChannel<T> =
GenericStateBroadcastChannel<parking_lot::RawMutex, T>;
}
#[cfg(feature = "std")]
pub use self::if_std::*;
#[cfg(feature = "alloc")]
mod if_alloc {
use super::*;
pub mod shared {
use super::*;
use core::sync::atomic::{AtomicUsize, Ordering};
struct GenericStateBroadcastChannelSharedState<MutexType, T>
where
MutexType: RawMutex,
T: Clone + 'static,
{
/// The amount of [`GenericSender`] instances which reference this state.
senders: AtomicUsize,
/// The amount of [`GenericReceiver`] instances which reference this state.
receivers: AtomicUsize,
/// The channel on which is acted.
channel: GenericStateBroadcastChannel<MutexType, T>,
}
// Implement ChannelReceiveAccess trait for SharedChannelState, so that it can
// be used for dynamic dispatch in futures.
impl<MutexType, T> ChannelReceiveAccess<T>
for GenericStateBroadcastChannelSharedState<MutexType, T>
where
MutexType: RawMutex,
T: Clone + 'static,
{
unsafe fn receive_or_register(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<Option<(StateId, T)>> {
self.channel.receive_or_register(wait_node, cx)
}
fn remove_receive_waiter(
&self,
wait_node: &mut ListNode<RecvWaitQueueEntry>,
) {
self.channel.remove_receive_waiter(wait_node)
}
}
/// A Future that is returned by the `receive` function on a state broadcast channel.
/// The future gets resolved with `Some((state_id, state))` when a value could be
/// received from the channel.
///
/// `state` represents the new state which had been retrieved from the channel.
///
/// `state_id` is the [`StateId`] which can be passed as a parameter to
/// `receive()` in order to fetch the next state from the channel.
///
/// If the channels gets closed and no items are still enqueued inside the
/// channel, the future will resolve to `None`.
#[must_use = "futures do nothing unless polled"]
pub struct StateReceiveFuture<MutexType, T> {
/// The Channel that is associated with this StateReceiveFuture
channel: Option<alloc::sync::Arc<dyn ChannelReceiveAccess<T>>>,
/// Node for waiting on the channel
wait_node: ListNode<RecvWaitQueueEntry>,
/// Marker for mutex type
_phantom: PhantomData<MutexType>,
}
// Safety: Channel futures can be sent between threads as long as the underlying
// channel is thread-safe (Sync), which allows to poll/register/unregister from
// a different thread.
unsafe impl<MutexType: Sync, T: Clone + Send> Send
for StateReceiveFuture<MutexType, T>
{
}
impl<MutexType, T> core::fmt::Debug for StateReceiveFuture<MutexType, T> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("StateReceiveFuture").finish()
}
}
impl<MutexType, T> Future for StateReceiveFuture<MutexType, T> {
type Output = Option<(StateId, T)>;
fn poll(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<(StateId, T)>> {
// It might be possible to use Pin::map_unchecked here instead of the two unsafe APIs.
// However this didn't seem to work for some borrow checker reasons
// Safety: The next operations are safe, because Pin promises us that
// the address of the wait queue entry inside StateReceiveFuture is stable,
// and we don't move any fields inside the future until it gets dropped.
let mut_self: &mut StateReceiveFuture<MutexType, T> =
unsafe { Pin::get_unchecked_mut(self) };
let channel = mut_self
.channel
.take()
.expect("polled StateReceiveFuture after completion");
let poll_res = unsafe {
channel.receive_or_register(&mut mut_self.wait_node, cx)
};
if poll_res.is_ready() {
// A value was available
mut_self.channel = None;
} else {
mut_self.channel = Some(channel)
}
poll_res
}
}
impl<MutexType, T> FusedFuture for StateReceiveFuture<MutexType, T> {
fn is_terminated(&self) -> bool {
self.channel.is_none()
}
}
impl<MutexType, T> Drop for StateReceiveFuture<MutexType, T> {
fn drop(&mut self) {
// If this StateReceiveFuture has been polled and it was added to the
// wait queue at the channel, it must be removed before dropping.
// Otherwise the channel would access invalid memory.
if let Some(channel) = &self.channel {
channel.remove_receive_waiter(&mut self.wait_node);
}
}
}
/// The sending side of a channel which can be used to exchange values
/// between concurrent tasks.
///
/// Values can be sent into the channel through `send`.
pub struct GenericStateSender<MutexType, T>
where
MutexType: RawMutex,
T: Clone + 'static,
{
inner: alloc::sync::Arc<
GenericStateBroadcastChannelSharedState<MutexType, T>,
>,
}
/// The receiving side of a channel which can be used to exchange values
/// between concurrent tasks.
///
/// Tasks can receive values from the channel through the `receive` method.
/// The returned Future will get resolved when a value is sent into the channel.
pub struct GenericStateReceiver<MutexType, T>
where
MutexType: RawMutex,
T: Clone + 'static,
{
inner: alloc::sync::Arc<
GenericStateBroadcastChannelSharedState<MutexType, T>,
>,
}
impl<MutexType, T> core::fmt::Debug for GenericStateSender<MutexType, T>
where
MutexType: RawMutex,
T: Clone,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("StateSender").finish()
}
}
impl<MutexType, T> core::fmt::Debug for GenericStateReceiver<MutexType, T>
where
MutexType: RawMutex,
T: Clone,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("StateReceiver").finish()
}
}
impl<MutexType, T> Clone for GenericStateSender<MutexType, T>
where
MutexType: RawMutex,
T: Clone,
{
fn clone(&self) -> Self {
let old_size =
self.inner.senders.fetch_add(1, Ordering::Relaxed);
if old_size > (core::isize::MAX) as usize {
panic!("Reached maximum refcount");
}
GenericStateSender {
inner: self.inner.clone(),
}
}
}
impl<MutexType, T> Drop for GenericStateSender<MutexType, T>
where
MutexType: RawMutex,
T: Clone,
{
fn drop(&mut self) {
if self.inner.senders.fetch_sub(1, Ordering::Release) != 1 {
return;
}
core::sync::atomic::fence(Ordering::Acquire);
// Close the channel, before last sender gets destroyed
// TODO: We could potentially avoid this, if no receiver is left
self.inner.channel.close();
}
}
impl<MutexType, T> Clone for GenericStateReceiver<MutexType, T>
where
MutexType: RawMutex,
T: Clone,
{
fn clone(&self) -> Self {
let old_size =
self.inner.receivers.fetch_add(1, Ordering::Relaxed);
if old_size > (core::isize::MAX) as usize {
panic!("Reached maximum refcount");
}
GenericStateReceiver {
inner: self.inner.clone(),
}
}
}
impl<MutexType, T> Drop for GenericStateReceiver<MutexType, T>
where
MutexType: RawMutex,
T: Clone,
{
fn drop(&mut self) {
if self.inner.receivers.fetch_sub(1, Ordering::Release) != 1 {
return;
}
core::sync::atomic::fence(Ordering::Acquire);
// Close the channel, before last receiver gets destroyed
// TODO: We could potentially avoid this, if no sender is left
self.inner.channel.close();
}
}
/// Creates a new state broadcast channel which can be used to exchange values
/// of type `T` between concurrent tasks.
/// The ends of the Channel are represented through
/// the returned Sender and Receiver.
///
/// As soon es either the senders or receivers is closed, the channel
/// itself will be closed.
pub fn generic_state_broadcast_channel<MutexType, T>() -> (
GenericStateSender<MutexType, T>,
GenericStateReceiver<MutexType, T>,
)
where
MutexType: RawMutex,
T: Clone + Send,
{
let inner = alloc::sync::Arc::new(
GenericStateBroadcastChannelSharedState {
channel: GenericStateBroadcastChannel::new(),
senders: AtomicUsize::new(1),
receivers: AtomicUsize::new(1),
},
);
let sender = GenericStateSender {
inner: inner.clone(),
};
let receiver = GenericStateReceiver { inner };
(sender, receiver)
}
impl<MutexType, T> GenericStateSender<MutexType, T>
where
MutexType: RawMutex + 'static,
T: Clone,
{
/// Writes a single value to the channel.
///
/// This will notify waiters about the availability of the value.
/// If a value had been written to the channel before, or if the
/// channel is closed, the new value will be rejected and
/// returned inside the error variant.
pub fn send(&self, value: T) -> Result<(), ChannelSendError<T>> {
self.inner.channel.send(value)
}
}
impl<MutexType, T> GenericStateReceiver<MutexType, T>
where
MutexType: RawMutex + 'static,
T: Clone,
{
/// Returns a future that gets fulfilled when a value is written to the channel
/// or the channel is closed.
/// `state_id` specifies the minimum state ID that should be retrieved
/// by the `receive` operation.
///
/// The returned [`StateReceiveFuture`] will get fulfilled with the
/// retrieved value as well as the [`StateId`] which is required to retrieve
/// the following state
pub fn receive(
&self,
state_id: StateId,
) -> StateReceiveFuture<MutexType, T> {
StateReceiveFuture {
channel: Some(self.inner.clone()),
wait_node: ListNode::new(RecvWaitQueueEntry::new(state_id)),
_phantom: PhantomData,
}
}
/// Attempt to retrieve a value whose `StateId` is greater than the one provided.
///
/// Returns `None` if no value is found in the channel, or if the current `StateId`
/// of the value is less or equal to the one provided.
pub fn try_receive(
&self,
state_id: StateId,
) -> Option<(StateId, T)> {
self.inner.channel.try_receive(state_id)
}
}
// Export parking_lot based shared channels in std mode
#[cfg(feature = "std")]
mod if_std {
use super::*;
/// A [`GenericStateSender`] implementation backed by [`parking_lot`].
pub type StateSender<T> =
GenericStateSender<parking_lot::RawMutex, T>;
/// A [`GenericStateReceiver`] implementation backed by [`parking_lot`].
pub type StateReceiver<T> =
GenericStateReceiver<parking_lot::RawMutex, T>;
/// Creates a new state broadcast channel.
///
/// Refer to [`generic_state_broadcast_channel`] for details.
///
/// Example for creating a channel to transmit an integer value:
///
/// ```
/// # use futures_intrusive::channel::shared::state_broadcast_channel;
/// let (sender, receiver) = state_broadcast_channel::<i32>();
/// ```
pub fn state_broadcast_channel<T>(
) -> (StateSender<T>, StateReceiver<T>)
where
T: Clone + Send,
{
generic_state_broadcast_channel::<parking_lot::RawMutex, T>()
}
}
#[cfg(feature = "std")]
pub use self::if_std::*;
}
}
#[cfg(feature = "alloc")]
pub use self::if_alloc::*;
@@ -0,0 +1,701 @@
//! An intrusive double linked list of data
use core::{
marker::PhantomPinned,
ops::{Deref, DerefMut},
ptr::NonNull,
};
/// A node which carries data of type `T` and is stored in an intrusive list
#[derive(Debug)]
pub struct ListNode<T> {
/// The previous node in the list. `None` if there is no previous node.
prev: Option<NonNull<ListNode<T>>>,
/// The next node in the list. `None` if there is no previous node.
next: Option<NonNull<ListNode<T>>>,
/// The data which is associated to this list item
data: T,
/// Prevents `ListNode`s from being `Unpin`. They may never be moved, since
/// the list semantics require addresses to be stable.
_pin: PhantomPinned,
}
impl<T> ListNode<T> {
/// Creates a new node with the associated data
pub fn new(data: T) -> ListNode<T> {
ListNode::<T> {
prev: None,
next: None,
data,
_pin: PhantomPinned,
}
}
}
impl<T> Deref for ListNode<T> {
type Target = T;
fn deref(&self) -> &T {
&self.data
}
}
impl<T> DerefMut for ListNode<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.data
}
}
/// An intrusive linked list of nodes, where each node carries associated data
/// of type `T`.
#[derive(Debug)]
pub struct LinkedList<T> {
head: Option<NonNull<ListNode<T>>>,
tail: Option<NonNull<ListNode<T>>>,
}
impl<T> LinkedList<T> {
/// Creates an empty linked list
pub fn new() -> Self {
LinkedList::<T> {
head: None,
tail: None,
}
}
/// Adds a node at the front of the linked list.
/// Safety: This function is only safe as long as `node` is guaranteed to
/// get removed from the list before it gets moved or dropped.
/// In addition to this `node` may not be added to another other list before
/// it is removed from the current one.
pub unsafe fn add_front(&mut self, node: &mut ListNode<T>) {
node.next = self.head;
node.prev = None;
match self.head {
Some(mut head) => head.as_mut().prev = Some(node.into()),
None => {}
};
self.head = Some(node.into());
if self.tail.is_none() {
self.tail = Some(node.into());
}
}
/// Returns a reference to the first node in the linked list
/// The function is only safe as long as valid pointers are stored inside
/// the linked list.
/// The returned pointer is only guaranteed to be valid as long as the list
/// is not mutated
pub fn peek_first(&self) -> Option<&ListNode<T>> {
// Safety: When the node was inserted it was promised that it is alive
// until it gets removed from the list.
// The returned node has a pointer which constrains it to the lifetime
// of the list. This is ok, since the Node is supposed to outlive
// its insertion in the list.
unsafe {
self.head
.map(|node| &*(node.as_ptr() as *const ListNode<T>))
}
}
/// Returns a mutable reference to the first node in the linked list
/// The function is only safe as long as valid pointers are stored inside
/// the linked list.
/// The returned pointer is only guaranteed to be valid as long as the list
/// is not mutated
pub fn peek_first_mut(&mut self) -> Option<&mut ListNode<T>> {
// Safety: When the node was inserted it was promised that it is alive
// until it gets removed from the list.
// The returned node has a pointer which constrains it to the lifetime
// of the list. This is ok, since the Node is supposed to outlive
// its insertion in the list.
unsafe {
self.head
.map(|mut node| &mut *(node.as_mut() as *mut ListNode<T>))
}
}
/// Returns a reference to the last node in the linked list
/// The function is only safe as long as valid pointers are stored inside
/// the linked list.
/// The returned pointer is only guaranteed to be valid as long as the list
/// is not mutated
pub fn peek_last(&self) -> Option<&ListNode<T>> {
// Safety: When the node was inserted it was promised that it is alive
// until it gets removed from the list.
// The returned node has a pointer which constrains it to the lifetime
// of the list. This is ok, since the Node is supposed to outlive
// its insertion in the list.
unsafe {
self.tail
.map(|node| &*(node.as_ptr() as *const ListNode<T>))
}
}
/// Returns a mutable reference to the last node in the linked list
/// The function is only safe as long as valid pointers are stored inside
/// the linked list.
/// The returned pointer is only guaranteed to be valid as long as the list
/// is not mutated
pub fn peek_last_mut(&mut self) -> Option<&mut ListNode<T>> {
// Safety: When the node was inserted it was promised that it is alive
// until it gets removed from the list.
// The returned node has a pointer which constrains it to the lifetime
// of the list. This is ok, since the Node is supposed to outlive
// its insertion in the list.
unsafe {
self.tail
.map(|mut node| &mut *(node.as_mut() as *mut ListNode<T>))
}
}
/// Removes the first node from the linked list
pub fn remove_first(&mut self) -> Option<&mut ListNode<T>> {
// Safety: When the node was inserted it was promised that it is alive
// until it gets removed from the list
unsafe {
let mut head = self.head?;
self.head = head.as_mut().next;
let first_ref = head.as_mut();
match first_ref.next {
None => {
// This was the only node in the list
debug_assert_eq!(Some(first_ref.into()), self.tail);
self.tail = None;
}
Some(mut next) => {
next.as_mut().prev = None;
}
}
first_ref.prev = None;
first_ref.next = None;
Some(&mut *(first_ref as *mut ListNode<T>))
}
}
/// Removes the last node from the linked list and returns it
pub fn remove_last(&mut self) -> Option<&mut ListNode<T>> {
// Safety: When the node was inserted it was promised that it is alive
// until it gets removed from the list
unsafe {
let mut tail = self.tail?;
self.tail = tail.as_mut().prev;
let last_ref = tail.as_mut();
match last_ref.prev {
None => {
// This was the last node in the list
debug_assert_eq!(Some(last_ref.into()), self.head);
self.head = None;
}
Some(mut prev) => {
prev.as_mut().next = None;
}
}
last_ref.prev = None;
last_ref.next = None;
Some(&mut *(last_ref as *mut ListNode<T>))
}
}
/// Returns whether the linked list doesn not contain any node
pub fn is_empty(&self) -> bool {
if !self.head.is_none() {
return false;
}
debug_assert!(self.tail.is_none());
true
}
/// Removes the given `node` from the linked list.
/// Returns whether the `node` was removed.
/// It is also only save if it is known that the `node` is either part of this
/// list, or of no list at all. If `node` is part of another list, the
/// behavior is undefined.
pub unsafe fn remove(&mut self, node: &mut ListNode<T>) -> bool {
match node.prev {
None => {
// This might be the first node in the list. If it is not, the
// node is not in the list at all. Since our precondition is that
// the node must either be in this list or in no list, we check that
// the node is really in no list.
if self.head != Some(node.into()) {
debug_assert!(node.next.is_none());
return false;
}
self.head = node.next;
}
Some(mut prev) => {
debug_assert_eq!(prev.as_ref().next, Some(node.into()));
prev.as_mut().next = node.next;
}
}
match node.next {
None => {
// This must be the last node in our list. Otherwise the list
// is inconsistent.
debug_assert_eq!(self.tail, Some(node.into()));
self.tail = node.prev;
}
Some(mut next) => {
debug_assert_eq!(next.as_mut().prev, Some(node.into()));
next.as_mut().prev = node.prev;
}
}
node.next = None;
node.prev = None;
true
}
/// Drains the list iby calling a callback on each list node
///
/// The method does not return an iterator since stopping or deferring
/// draining the list is not permitted. If the method would push nodes to
/// an iterator we could not guarantee that the nodes do not get utilized
/// after having been removed from the list anymore.
pub fn drain<F>(&mut self, mut func: F)
where
F: FnMut(&mut ListNode<T>),
{
let mut current = self.head;
self.head = None;
self.tail = None;
while let Some(mut node) = current {
// Safety: The nodes have not been removed from the list yet and must
// therefore contain valid data. The nodes can also not be added to
// the list again during iteration, since the list is mutably borrowed.
unsafe {
let node_ref = node.as_mut();
current = node_ref.next;
node_ref.next = None;
node_ref.prev = None;
// Note: We do not reset the pointers from the next element in the
// list to the current one since we will iterate over the whole
// list anyway, and therefore clean up all pointers.
func(node_ref);
}
}
}
/// Drains the list in reverse order by calling a callback on each list node
///
/// The method does not return an iterator since stopping or deferring
/// draining the list is not permitted. If the method would push nodes to
/// an iterator we could not guarantee that the nodes do not get utilized
/// after having been removed from the list anymore.
pub fn reverse_drain<F>(&mut self, mut func: F)
where
F: FnMut(&mut ListNode<T>),
{
let mut current = self.tail;
self.head = None;
self.tail = None;
while let Some(mut node) = current {
// Safety: The nodes have not been removed from the list yet and must
// therefore contain valid data. The nodes can also not be added to
// the list again during iteration, since the list is mutably borrowed.
unsafe {
let node_ref = node.as_mut();
current = node_ref.prev;
node_ref.next = None;
node_ref.prev = None;
// Note: We do not reset the pointers from the next element in the
// list to the current one since we will iterate over the whole
// list anyway, and therefore clean up all pointers.
func(node_ref);
}
}
}
}
#[cfg(all(test, feature = "alloc"))] // Tests make use of Vec at the moment
mod tests {
use super::*;
use alloc::vec::Vec;
fn collect_list<T: Copy>(mut list: LinkedList<T>) -> Vec<T> {
let mut result = Vec::new();
list.drain(|node| {
result.push(**node);
});
result
}
fn collect_reverse_list<T: Copy>(mut list: LinkedList<T>) -> Vec<T> {
let mut result = Vec::new();
list.reverse_drain(|node| {
result.push(**node);
});
result
}
unsafe fn add_nodes(
list: &mut LinkedList<i32>,
nodes: &mut [&mut ListNode<i32>],
) {
for node in nodes.iter_mut() {
list.add_front(node);
}
}
unsafe fn assert_clean<T>(node: &mut ListNode<T>) {
assert!(node.next.is_none());
assert!(node.prev.is_none());
}
#[test]
fn insert_and_iterate() {
unsafe {
let mut a = ListNode::new(5);
let mut b = ListNode::new(7);
let mut c = ListNode::new(31);
let mut setup = |list: &mut LinkedList<i32>| {
assert_eq!(true, list.is_empty());
list.add_front(&mut c);
assert_eq!(31, **list.peek_first().unwrap());
assert_eq!(false, list.is_empty());
list.add_front(&mut b);
assert_eq!(7, **list.peek_first().unwrap());
list.add_front(&mut a);
assert_eq!(5, **list.peek_first().unwrap());
};
let mut list = LinkedList::new();
setup(&mut list);
let items: Vec<i32> = collect_list(list);
assert_eq!([5, 7, 31].to_vec(), items);
let mut list = LinkedList::new();
setup(&mut list);
let items: Vec<i32> = collect_reverse_list(list);
assert_eq!([31, 7, 5].to_vec(), items);
}
}
#[test]
fn drain_and_collect() {
unsafe {
let mut a = ListNode::new(5);
let mut b = ListNode::new(7);
let mut c = ListNode::new(31);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut c, &mut b, &mut a]);
let taken_items: Vec<i32> = collect_list(list);
assert_eq!([5, 7, 31].to_vec(), taken_items);
}
}
#[test]
fn peek_last() {
unsafe {
let mut a = ListNode::new(5);
let mut b = ListNode::new(7);
let mut c = ListNode::new(31);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut c, &mut b, &mut a]);
let last = list.peek_last();
assert_eq!(31, **last.unwrap());
list.remove_last();
let last = list.peek_last();
assert_eq!(7, **last.unwrap());
list.remove_last();
let last = list.peek_last();
assert_eq!(5, **last.unwrap());
list.remove_last();
let last = list.peek_last();
assert!(last.is_none());
}
}
#[test]
fn remove_first() {
unsafe {
// We iterate forward and backwards through the manipulated lists
// to make sure pointers in both directions are still ok.
let mut a = ListNode::new(5);
let mut b = ListNode::new(7);
let mut c = ListNode::new(31);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut c, &mut b, &mut a]);
let removed = list.remove_first().unwrap();
assert_clean(removed);
assert!(!list.is_empty());
let items: Vec<i32> = collect_list(list);
assert_eq!([7, 31].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut c, &mut b, &mut a]);
let removed = list.remove_first().unwrap();
assert_clean(removed);
assert!(!list.is_empty());
let items: Vec<i32> = collect_reverse_list(list);
assert_eq!([31, 7].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut b, &mut a]);
let removed = list.remove_first().unwrap();
assert_clean(removed);
assert!(!list.is_empty());
let items: Vec<i32> = collect_list(list);
assert_eq!([7].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut b, &mut a]);
let removed = list.remove_first().unwrap();
assert_clean(removed);
assert!(!list.is_empty());
let items: Vec<i32> = collect_reverse_list(list);
assert_eq!([7].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut a]);
let removed = list.remove_first().unwrap();
assert_clean(removed);
assert!(list.is_empty());
let items: Vec<i32> = collect_list(list);
assert!(items.is_empty());
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut a]);
let removed = list.remove_first().unwrap();
assert_clean(removed);
assert!(list.is_empty());
let items: Vec<i32> = collect_reverse_list(list);
assert!(items.is_empty());
}
}
#[test]
fn remove_last() {
unsafe {
// We iterate forward and backwards through the manipulated lists
// to make sure pointers in both directions are still ok.
let mut a = ListNode::new(5);
let mut b = ListNode::new(7);
let mut c = ListNode::new(31);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut c, &mut b, &mut a]);
let removed = list.remove_last().unwrap();
assert_clean(removed);
assert!(!list.is_empty());
let items: Vec<i32> = collect_list(list);
assert_eq!([5, 7].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut c, &mut b, &mut a]);
let removed = list.remove_last().unwrap();
assert_clean(removed);
assert!(!list.is_empty());
let items: Vec<i32> = collect_reverse_list(list);
assert_eq!([7, 5].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut b, &mut a]);
let removed = list.remove_last().unwrap();
assert_clean(removed);
assert!(!list.is_empty());
let items: Vec<i32> = collect_list(list);
assert_eq!([5].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut b, &mut a]);
let removed = list.remove_last().unwrap();
assert_clean(removed);
assert!(!list.is_empty());
let items: Vec<i32> = collect_reverse_list(list);
assert_eq!([5].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut a]);
let removed = list.remove_last().unwrap();
assert_clean(removed);
assert!(list.is_empty());
let items: Vec<i32> = collect_list(list);
assert!(items.is_empty());
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut a]);
let removed = list.remove_last().unwrap();
assert_clean(removed);
assert!(list.is_empty());
let items: Vec<i32> = collect_reverse_list(list);
assert!(items.is_empty());
}
}
#[test]
fn remove_by_address() {
unsafe {
let mut a = ListNode::new(5);
let mut b = ListNode::new(7);
let mut c = ListNode::new(31);
{
// Remove first
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut c, &mut b, &mut a]);
assert_eq!(true, list.remove(&mut a));
assert_clean((&mut a).into());
// a should be no longer there and can't be removed twice
assert_eq!(false, list.remove(&mut a));
assert_eq!(Some((&mut b).into()), list.head);
assert_eq!(Some((&mut c).into()), b.next);
assert_eq!(Some((&mut b).into()), c.prev);
let items: Vec<i32> = collect_list(list);
assert_eq!([7, 31].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut c, &mut b, &mut a]);
assert_eq!(true, list.remove(&mut a));
assert_clean((&mut a).into());
// a should be no longer there and can't be removed twice
assert_eq!(false, list.remove(&mut a));
assert_eq!(Some((&mut c).into()), b.next);
assert_eq!(Some((&mut b).into()), c.prev);
let items: Vec<i32> = collect_reverse_list(list);
assert_eq!([31, 7].to_vec(), items);
}
{
// Remove middle
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut c, &mut b, &mut a]);
assert_eq!(true, list.remove(&mut b));
assert_clean((&mut b).into());
assert_eq!(Some((&mut c).into()), a.next);
assert_eq!(Some((&mut a).into()), c.prev);
let items: Vec<i32> = collect_list(list);
assert_eq!([5, 31].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut c, &mut b, &mut a]);
assert_eq!(true, list.remove(&mut b));
assert_clean((&mut b).into());
assert_eq!(Some((&mut c).into()), a.next);
assert_eq!(Some((&mut a).into()), c.prev);
let items: Vec<i32> = collect_reverse_list(list);
assert_eq!([31, 5].to_vec(), items);
}
{
// Remove last
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut c, &mut b, &mut a]);
assert_eq!(true, list.remove(&mut c));
assert_clean((&mut c).into());
assert!(b.next.is_none());
assert_eq!(Some((&mut b).into()), list.tail);
let items: Vec<i32> = collect_list(list);
assert_eq!([5, 7].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut c, &mut b, &mut a]);
assert_eq!(true, list.remove(&mut c));
assert_clean((&mut c).into());
assert!(b.next.is_none());
assert_eq!(Some((&mut b).into()), list.tail);
let items: Vec<i32> = collect_reverse_list(list);
assert_eq!([7, 5].to_vec(), items);
}
{
// Remove first of two
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut b, &mut a]);
assert_eq!(true, list.remove(&mut a));
assert_clean((&mut a).into());
// a should be no longer there and can't be removed twice
assert_eq!(false, list.remove(&mut a));
assert_eq!(Some((&mut b).into()), list.head);
assert_eq!(Some((&mut b).into()), list.tail);
assert!(b.next.is_none());
assert!(b.prev.is_none());
let items: Vec<i32> = collect_list(list);
assert_eq!([7].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut b, &mut a]);
assert_eq!(true, list.remove(&mut a));
assert_clean((&mut a).into());
// a should be no longer there and can't be removed twice
assert_eq!(false, list.remove(&mut a));
assert_eq!(Some((&mut b).into()), list.head);
assert_eq!(Some((&mut b).into()), list.tail);
assert!(b.next.is_none());
assert!(b.prev.is_none());
let items: Vec<i32> = collect_reverse_list(list);
assert_eq!([7].to_vec(), items);
}
{
// Remove last of two
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut b, &mut a]);
assert_eq!(true, list.remove(&mut b));
assert_clean((&mut b).into());
assert_eq!(Some((&mut a).into()), list.head);
assert_eq!(Some((&mut a).into()), list.tail);
assert!(a.next.is_none());
assert!(a.prev.is_none());
let items: Vec<i32> = collect_list(list);
assert_eq!([5].to_vec(), items);
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut b, &mut a]);
assert_eq!(true, list.remove(&mut b));
assert_clean((&mut b).into());
assert_eq!(Some((&mut a).into()), list.head);
assert_eq!(Some((&mut a).into()), list.tail);
assert!(a.next.is_none());
assert!(a.prev.is_none());
let items: Vec<i32> = collect_reverse_list(list);
assert_eq!([5].to_vec(), items);
}
{
// Remove last item
let mut list = LinkedList::new();
add_nodes(&mut list, &mut [&mut a]);
assert_eq!(true, list.remove(&mut a));
assert_clean((&mut a).into());
assert!(list.head.is_none());
assert!(list.tail.is_none());
let items: Vec<i32> = collect_list(list);
assert!(items.is_empty());
}
{
// Remove missing
let mut list = LinkedList::new();
list.add_front(&mut b);
list.add_front(&mut a);
assert_eq!(false, list.remove(&mut c));
}
}
}
}
@@ -0,0 +1,437 @@
//! Implements an intrusive priority queue based on a pairing heap.
//!
//! A [pairing heap] is a heap data structure (i.e. a tree whose nodes carry
//! values, with the property that every node's value is lesser or equal to its
//! children's) that supports the following operations:
//!
//! - finding a minimum element in `O(1)`
//! - This is trivial: the heap property guarantees that the root is a
//! minimum element.
//! - insertion of a new node in `O(1)`
//! - deletion in `O(log n)`, _amortized_
//! - However, note that any _individual_ deletion may take `O(N)` time. For
//! example, if we take an empty heap and insert N elements, the tree will
//! have a very degenerate (shallow) shape. Then, deleting the root will
//! take `O(N)` time, but it will also reorganize the tree to make
//! successive deletes cheaper.
//!
//! [pairing heap]: https://en.wikipedia.org/wiki/Pairing_heap
use core::{
marker::PhantomPinned,
mem,
ops::{Deref, DerefMut, Drop},
ptr::NonNull,
};
/// Compares `a` and `b` without unwinding.
/// This is necessary to avoid reentrancy in the heap.
fn safe_lesser<T: Ord>(a: &T, b: &T) -> bool {
struct DropBomb;
impl Drop for DropBomb {
fn drop(&mut self) {
panic!("Panicked while comparing");
}
}
// If `T::cmp` panics, force a double-panic (and therefore an abort).
let bomb = DropBomb;
let ordering = a < b;
mem::forget(bomb);
ordering
}
/// A node which carries data of type `T` and is stored in an intrusive heap.
///
/// Nodes will be compared based on `T`'s [`Ord`] impl. Those comparisons must
/// not panic - otherwise, the program will abort.
#[derive(Debug)]
pub struct HeapNode<T> {
/// The parent. `None` if this is the root.
parent: Option<NonNull<HeapNode<T>>>,
/// The previous sibling. `None` if there is no previous sibling.
prev: Option<NonNull<HeapNode<T>>>,
/// The next sibling. `None` if there is no next sibling.
next: Option<NonNull<HeapNode<T>>>,
/// The first child. `None` if there are no children.
first_child: Option<NonNull<HeapNode<T>>>,
/// The data which is associated to this heap item.
data: T,
/// Prevents `HeapNode`s from being `Unpin`. They may never be moved, since
/// the heap semantics require addresses to be stable.
_pin: PhantomPinned,
}
impl<T> HeapNode<T> {
/// Creates a new node with the associated data
pub fn new(data: T) -> HeapNode<T> {
HeapNode::<T> {
parent: None,
prev: None,
next: None,
first_child: None,
data,
_pin: PhantomPinned,
}
}
fn is_root(&self) -> bool {
if self.parent.is_none() {
debug_assert_eq!(self.prev, None);
debug_assert_eq!(self.next, None);
true
} else {
false
}
}
}
impl<T> Deref for HeapNode<T> {
type Target = T;
fn deref(&self) -> &T {
&self.data
}
}
impl<T> DerefMut for HeapNode<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.data
}
}
/// Add a child to a node.
unsafe fn add_child<T: Ord>(
mut parent: NonNull<HeapNode<T>>,
mut child: NonNull<HeapNode<T>>,
) {
// require parent <= child
debug_assert!(!safe_lesser(&child.as_ref().data, &parent.as_ref().data));
if let Some(mut old_first_child) = parent.as_mut().first_child.take() {
child.as_mut().next = Some(old_first_child);
debug_assert_eq!(old_first_child.as_ref().prev, None);
old_first_child.as_mut().prev = Some(child);
}
parent.as_mut().first_child = Some(child);
child.as_mut().parent = Some(parent);
}
/// Merge two root heaps. Returns the new root.
unsafe fn meld<T: Ord>(
left: NonNull<HeapNode<T>>,
right: NonNull<HeapNode<T>>,
) -> NonNull<HeapNode<T>> {
debug_assert!(left.as_ref().is_root());
debug_assert!(right.as_ref().is_root());
// The lesser node should become the root.
if safe_lesser(&left.as_ref().data, &right.as_ref().data) {
add_child(left, right);
left
} else {
add_child(right, left);
right
}
}
/// Merge two root heaps, where the left might be empty. Returns the new root.
unsafe fn maybe_meld<T: Ord>(
left: Option<NonNull<HeapNode<T>>>,
right: NonNull<HeapNode<T>>,
) -> NonNull<HeapNode<T>> {
if let Some(left) = left {
meld(left, right)
} else {
right
}
}
/// Given the first child in a child list, traverse and find the last child.
unsafe fn last_child<T>(
first_child: NonNull<HeapNode<T>>,
) -> NonNull<HeapNode<T>> {
let mut cur = first_child;
while let Some(next) = cur.as_ref().next {
cur = next;
}
cur
}
/// Given a pointer to the last node in a child list, unlink it and return the
/// previous node (which has become the last node in its list).
///
/// That is, given a list `A <-> B <-> C`, `unlink_prev(C)` will return `B` and
/// also unlink `C` to become `A <-> B C`.
///
/// If the node was a lone child, returns `None`.
///
/// Parent/child pointers are untouched.
unsafe fn unlink_prev<T>(
mut node: NonNull<HeapNode<T>>,
) -> Option<NonNull<HeapNode<T>>> {
debug_assert_eq!(node.as_ref().next, None);
let mut prev = node.as_mut().prev.take()?;
debug_assert_eq!(prev.as_ref().next, Some(node));
prev.as_mut().next = None;
Some(prev)
}
/// Merge together a child list. Each child in the child list is expected to
/// have an equal `parent`. Returns the new merged root, whose `parent` will be unset.
unsafe fn merge_children<T: Ord>(
first_child: NonNull<HeapNode<T>>,
) -> NonNull<HeapNode<T>> {
let common_parent = first_child.as_ref().parent;
debug_assert!(common_parent.is_some());
// Traverse the children right-to-left. This is important for the analysis
// to work. Reading: "Pairing heaps: the forward variant",
// https://arxiv.org/pdf/1709.01152.pdf
let mut node = last_child(first_child);
let mut current = None;
// Loop invariant: `node` is the first unprocessed child, `current`
// is the merged result of all processed children.
loop {
// All nodes in the list should have the same parent.
let node_parent = node.as_mut().parent.take();
debug_assert_eq!(node_parent, common_parent);
// Grab the last two unprocessed elements.
let mut prev = if let Some(prev) = unlink_prev(node) {
prev
} else {
// Odd case.
return maybe_meld(current, node);
};
// All nodes in the list should have the same parent.
let prev_parent = prev.as_mut().parent.take();
debug_assert_eq!(prev_parent, common_parent);
// Unlink `prev` from `prev.prev`.
let prev_prev = unlink_prev(prev);
// Meld the pair, then meld it into the accumulator.
let cur = maybe_meld(current, meld(prev, node));
if let Some(prev_prev) = prev_prev {
node = prev_prev;
current = Some(cur);
continue;
} else {
// Even case.
return cur;
}
}
}
/// An intrusive min-heap of nodes, where each node carries associated data
/// of type `T`.
#[derive(Debug)]
pub struct PairingHeap<T> {
root: Option<NonNull<HeapNode<T>>>,
}
impl<T: Ord> PairingHeap<T> {
/// Creates an empty heap
pub fn new() -> Self {
PairingHeap::<T> { root: None }
}
/// Adds a node to the heap.
/// Safety: This function is only safe as long as `node` is guaranteed to
/// get removed from the list before it gets moved or dropped.
/// In addition to this `node` may not be added to another other heap before
/// it is removed from the current one.
pub unsafe fn insert(&mut self, node: &mut HeapNode<T>) {
// The node should not already be in a heap.
debug_assert!(node.is_root());
debug_assert_eq!(node.first_child, None);
if let Some(root) = self.root {
self.root = Some(meld(root, node.into()));
} else {
self.root = Some(node.into());
}
}
/// Returns the smallest element in the heap without removing it.
/// The function is only safe as long as valid pointers are stored inside
/// the heap.
/// The returned pointer is only guaranteed to be valid as long as the heap
/// is not mutated
pub fn peek_min(&self) -> Option<NonNull<HeapNode<T>>> {
self.root
}
/// Removes the given node from the heap.
/// The node must be a member of this heap, and not a member of any other
/// heap.
pub unsafe fn remove(&mut self, node: &mut HeapNode<T>) {
let parent = node.parent.take();
if let Some(mut parent) = parent {
// Unlink this node from its parent.
if let Some(mut prev) = node.prev {
prev.as_mut().next = node.next;
} else {
parent.as_mut().first_child = node.next;
}
if let Some(mut next) = node.next {
next.as_mut().prev = node.prev;
}
node.next = None;
node.prev = None;
} else {
debug_assert_eq!(node.next, None);
debug_assert_eq!(node.prev, None);
debug_assert_eq!(self.root, Some(node.into()));
self.root = None;
}
if let Some(first_child) = node.first_child.take() {
// Merge together the children.
let children = merge_children(first_child);
// Add the children back into the parent.
if let Some(parent) = parent {
// The heap property is preserved because we had `parent.data`
// <= `node.data`, and `node.data` <= `child.data` for all
// children.
add_child(parent, children);
} else {
self.root = Some(children);
}
}
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::{HeapNode, PairingHeap};
use core::ptr::NonNull;
// Recursively check the provided node and all descendants for:
// - pointer consistency: parent pointers and next/prev
// - the heap property: `node.data <= child.data` for all children
unsafe fn validate_heap_node<T: Ord>(
node: &HeapNode<T>,
parent: Option<&HeapNode<T>>,
) {
assert_eq!(node.parent, parent.map(NonNull::from));
if let Some(p) = parent {
assert!(p.data <= node.data);
}
if let Some(prev) = node.prev {
assert_eq!(prev.as_ref().next, Some(node.into()));
}
if let Some(next) = node.next {
assert_eq!(next.as_ref().prev, Some(node.into()));
}
let mut child = node.first_child;
while let Some(c) = child {
validate_heap_node(c.as_ref(), Some(node));
child = c.as_ref().next;
}
}
fn validate_heap<T: Ord>(heap: &PairingHeap<T>) {
if let Some(root) = heap.root {
// This is also sufficient to check that `heap.root` is indeed a
// minimum element of the heap.
unsafe {
validate_heap_node(root.as_ref(), None);
}
}
}
#[test]
fn insert_and_remove() {
// This test exhaustively covers every possible schedule of inserting,
// then removing, each of five different nodes from the heap.
#[derive(Copy, Clone, Debug)]
enum Action {
Insert(u8),
Remove(u8),
}
fn generate_schedules(
current: &mut Vec<Action>,
available: &mut Vec<Action>,
f: fn(&[Action]),
) {
for i in 0..available.len() {
let action = available.swap_remove(i);
current.push(action);
f(current);
if let Action::Insert(j) = action {
available.push(Action::Remove(j));
}
generate_schedules(current, available, f);
if let Action::Insert(_) = action {
available.pop();
}
current.pop();
// the opposite of `swap_remove`
available.push(action);
let len = available.len();
available.swap(i, len - 1);
}
}
let max = if cfg!(miri) {
// Miri is really slow, make things easier.
3
} else {
// 5 runs in a reasonable amount of time but still exercises
// interesting cases.
5
};
generate_schedules(
&mut vec![],
&mut (0..max).map(Action::Insert).collect(),
|schedule| unsafe {
let mut nodes = [
HeapNode::new(0u8),
HeapNode::new(1),
HeapNode::new(2),
HeapNode::new(3),
HeapNode::new(4),
];
let mut heap = PairingHeap::new();
for action in schedule {
match *action {
Action::Insert(n) => {
heap.insert(&mut nodes[n as usize]);
validate_heap(&heap);
}
Action::Remove(n) => {
heap.remove(&mut nodes[n as usize]);
assert!(nodes[n as usize].is_root());
assert_eq!(nodes[n as usize].first_child, None);
validate_heap(&heap);
}
}
}
},
);
}
#[test]
fn equal_values() {
// Check that things behave properly in the presence of equal values.
unsafe {
let mut nodes = [
HeapNode::new(0u8),
HeapNode::new(0),
HeapNode::new(0),
HeapNode::new(0),
HeapNode::new(0),
];
let mut heap = PairingHeap::new();
for node in &mut nodes {
heap.insert(node);
validate_heap(&heap);
}
for _ in 0..5 {
heap.remove(heap.peek_min().unwrap().as_mut());
validate_heap(&heap);
}
assert_eq!(heap.peek_min(), None);
}
}
}
+235
View File
@@ -0,0 +1,235 @@
//! Synchronization primitives and utilities based on intrusive collections.
//!
//! This crate provides a variety of `Futures`-based and `async/await` compatible
//! types that are based on the idea of intrusive collections:
//! - Channels in a variety of flavors:
//! - Oneshot
//! - Multi-Producer Multi-Consumer (MPMC)
//! - State Broadcast
//! - Synchronization Primitives:
//! - Manual Reset Event
//! - Mutex
//! - Semaphore
//! - A timer
//!
//! ## Intrusive collections?
//!
//! In an intrusive collection, the elements that want to get stored inside the
//! collection provide the means to store themselves inside the collection.
//! E.g. in an intrusive linked list, each element that gets stored inside the
//! list contains a pointer field that points to the next list element. E.g.
//!
//! ```
//! // The element which is intended to be stored inside an intrusive container
//! struct ListElement {
//! data: u32,
//! next: *mut ListElement,
//! }
//!
//! // The intrusive container
//! struct List {
//! head: *mut ListElement,
//! }
//! ```
//!
//! The advantage here is that the intrusive collection (here: the list) requires
//! only a fixed amount of memory. In this case it only needs a pointer to the
//! first element.
//!
//! The list container itself has a fixed size of a single pointer independent
//! of the number of stored elements.
//!
//! Intrusive lists are often used in low-level code like in operating system
//! kernels. E.g. they can be used for storing elements that represent threads
//! that are blocked and waiting on queue. In that case the stored elements can
//! be on the call stack of the caller of each blocked thread, since the
//! call stack won't change as long as the thread is blocked.
//!
//! ### Application in Futures
//!
//! This library brings this idea into the world of Rusts `Future`s. Due to the
//! addition of `Pin`ning, the address of a certain `Future` is not allowed to
//! change between the first call to `poll()` and when the `Future` is dropped.
//! This means the data inside the `Future` itself can be inserted into an
//! intrusive container. If the the call to `Future::poll()` is not immedately
//! ready, some parts of the `Future` itself are registered in the type which
//! yielded the `Future`. Each `Future` can store a `Waker`. When the original
//! type becomes ready, it can iterate through the list of registered `Future`s,
//! wakeup associated tasks, and potentially remove them from its queue.
//!
//! The result is that the future-yielding type is not required to copy an
//! arbitrary number of `Waker` objects into itself, and thereby does not require
//! dynamic memory for this task.
//!
//! When a `Future` gets destructed/dropped, it must make sure to remove itself
//! from any collections that refer to it to avoid invalid memory accesses.
//!
//! This library implements common synchronization primitives for the usage in
//! asychronous code based on this concept.
//!
//! The implementation requires the usage of a fair chunk of `unsafe`
//! annotations. However the provided user-level API is intended to be fully safe.
//!
//! ## Features of this library
//!
//! The following types are currently implemented:
//! - Channels (oneshot and multi-producer-multi-consumer)
//! - Synchronization primitives (async mutexes and events)
//! - Timers
//!
//! ## Design goals for the library
//!
//! - Provide implementations of common synchronization primitives in a platform
//! independent fashion.
//! - Support `no-std` environments. As many types as possible are also provided
//! for `no-std` environments. The library should boost the ability to use
//! async Rust code in environments like:
//! - Microcontrollers (RTOS and bare-metal)
//! - Kernels
//! - Drivers
//! - Avoid dynamic memory allocations at runtime. After objects from this
//! library have been created, they should not require allocation of any
//! further memory at runtime. E.g. they should not need to allocate memory
//! for each call to an asynchronous function or each time a new task accesses
//! the same object in parallel.
//! - Offer familiar APIs.
//! The library tries to mimic the APIs of existing Rust libraries like the
//! standard library and `futures-rs` as closely as possible.
//!
//! ## Non goals
//!
//! - Provide IO primitives (like sockets), or platform specific implementations.
//! - Reach the highest possible performance in terms of throughput and latency.
//! While code in this library is optimized for performance, portability
//! and deterministic memory usage are more important goals.
//! - Provide future wrappers for platform-specific APIs.
//!
//! ## Local, Non-local and shared flavors
//!
//! The library provides types in a variety of flavors:
//!
//! - A local flavor (e.g. [`channel::LocalChannel`])
//! - A non-local flavor (e.g. [`channel::Channel`])
//! - A shared flavor (e.g. [`channel::shared::Sender`])
//! - A generic flavor (e.g. [`channel::GenericChannel`] and
//! [`channel::shared::GenericSender`])
//!
//! The difference between these types lie in their thread-safety. The non-local
//! flavors of types can be accessed from multiple threads (and thereby also
//! futures tasks) concurrently. This means they implement the `Sync` trait in
//! addition to the `Send` trait.
//! The local flavors only implement the `Send` trait.
//!
//! ### Local flavor
//!
//! The local flavors will require no internal synchronization (e.g. internal
//! Mutexes) and can therefore be provided for all platforms (including `no-std`).
//! Due the lack of required synchronization, they are also very fast.
//!
//! It might seem counter-intuitive to provide synchronization primitives that
//! only work within a single task. However there are a variety of applications
//! where these can be used to coordinate sub-tasks (futures that are polled on
//! a single task concurrently).
//!
//! The following example demonstrates this use-case:
//!
//! ```
//! # use futures::join;
//! # use futures_intrusive::sync::LocalManualResetEvent;
//! async fn async_fn() {
//! let event = LocalManualResetEvent::new(false);
//! let task_a = async {
//! // Wait for the event
//! event.wait().await;
//! // Do something with the knowledge that task_b reached a certain state
//! };
//! let task_b = async {
//! // Some complex asynchronous workflow here
//! // ...
//! // Signal task_a
//! event.set();
//! };
//! join!(task_a, task_b);
//! }
//! ```
//!
//! ### Non-local flavor
//!
//! The non-local flavors can be used between arbitrary tasks and threads. They
//! use internal synchronization for this in form of an embedded `Mutex` of
//! [`parking_lot::Mutex`] type.
//!
//! The non-local flavors are only available in `alloc` environments.
//!
//! ### Shared flavor
//!
//! For some types a shared flavor is provided. Non-local flavors of types are
//! `Sync`, but they still can only be shared by reference between various tasks.
//! Shared flavors are also `Sync`, but the types additionally implement the
//! `Clone` trait, which allows duplicating the object, and passing ownership of
//! it to a different task. These types allow avoiding references (and thereby
//! lifetimes) in some scenarios, which makes them more convenient to use. The
//! types also return `Future`s which do not have an associated lifetime. This
//! allows using those types as implementations of traits without the need for
//! generic associated types (GATs).
//!
//! Due to the requirement of atomic reference counting, these types are
//! currently only available for `alloc` environments.
//!
//! ### Generic flavor
//!
//! The generic flavors of provided types are parameterized around a
//! [`lock_api::RawMutex`] type. These form the base for the non-local and shared
//! flavors which simply parameterize the generic flavor in either a
//! non-thread-safe or thread-safe fashion.
//!
//! Users can directly use the generic flavors to adapt the provided thread-safe
//! types for use in `no-std` environments.
//!
//! E.g. by providing a custom [`lock_api::RawMutex`]
//! implementation, the following platforms can be supported:
//!
//! - For RTOS platforms, RTOS-specific mutexes can be wrapped.
//! - For kernel development, spinlock based mutexes can be created.
//! - For embedded development, mutexes which just disable interrupts can be
//! utilized.
//!
//!
//! ## Relation to types in other libraries
//!
//! Other libraries (e.g. `futures-rs` and `tokio`) provide many primitives that
//! are comparable feature-wise to the types in this library.
//!
//! The most important differences are:
//! - This library has a bigger focus on `no-std` environments, and does not
//! only try to provide an implementation for `alloc` or `std`.
//! - The types in this library do not require dynamic memory allocation for
//! waking up an arbitrary number of tasks waiting on a particular
//! `Future`. Other libraries typically require heap-allocated nodes of
//! growing vectors for handling a varying number of tasks.
//! - The `Future`s produced by this library are all `!Unpin`, which might make
//! them less ergonomic to use.
//!
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs, missing_debug_implementations)]
#![deny(bare_trait_objects)]
#[cfg(feature = "alloc")]
extern crate alloc;
mod noop_lock;
use noop_lock::NoopLock;
pub mod buffer;
#[allow(dead_code)]
mod intrusive_double_linked_list;
mod intrusive_pairing_heap;
pub mod channel;
pub mod sync;
pub mod timer;
mod utils;
+27
View File
@@ -0,0 +1,27 @@
//! An unsafe (non-thread-safe) lock, equivalent to UnsafeCell
use core::marker::PhantomData;
use lock_api::{GuardSend, RawMutex};
/// An unsafe (non-thread-safe) lock, equivalent to UnsafeCell
#[derive(Debug)]
pub struct NoopLock {
/// Assigned in order to make the type !Sync
_phantom: PhantomData<*mut ()>,
}
unsafe impl RawMutex for NoopLock {
const INIT: NoopLock = NoopLock {
_phantom: PhantomData,
};
type GuardMarker = GuardSend;
fn lock(&self) {}
fn try_lock(&self) -> bool {
true
}
unsafe fn unlock(&self) {}
}
@@ -0,0 +1,317 @@
//! An asynchronously awaitable event for signalization between tasks
use crate::{
intrusive_double_linked_list::{LinkedList, ListNode},
utils::update_waker_ref,
NoopLock,
};
use core::pin::Pin;
use futures_core::{
future::{FusedFuture, Future},
task::{Context, Poll, Waker},
};
use lock_api::{Mutex, RawMutex};
/// Tracks how the future had interacted with the event
#[derive(PartialEq)]
enum PollState {
/// The task has never interacted with the event.
New,
/// The task was added to the wait queue at the event.
Waiting,
/// The task has been polled to completion.
Done,
}
/// Tracks the WaitForEventFuture waiting state.
/// Access to this struct is synchronized through the mutex in the Event.
struct WaitQueueEntry {
/// The task handle of the waiting task
task: Option<Waker>,
/// Current polling state
state: PollState,
}
impl WaitQueueEntry {
/// Creates a new WaitQueueEntry
fn new() -> WaitQueueEntry {
WaitQueueEntry {
task: None,
state: PollState::New,
}
}
}
/// Internal state of the `ManualResetEvent` pair above
struct EventState {
is_set: bool,
waiters: LinkedList<WaitQueueEntry>,
}
impl EventState {
fn new(is_set: bool) -> EventState {
EventState {
is_set,
waiters: LinkedList::new(),
}
}
fn reset(&mut self) {
self.is_set = false;
}
fn set(&mut self) {
if self.is_set != true {
self.is_set = true;
// Wakeup all waiters
// This happens inside the lock to make cancellation reliable
// If we would access waiters outside of the lock, the pointers
// may no longer be valid.
// Typically this shouldn't be an issue, since waking a task should
// only move it from the blocked into the ready state and not have
// further side effects.
// Use a reverse iterator, so that the oldest waiter gets
// scheduled first
self.waiters.reverse_drain(|waiter| {
if let Some(handle) = waiter.task.take() {
handle.wake();
}
waiter.state = PollState::Done;
});
}
}
fn is_set(&self) -> bool {
self.is_set
}
/// Checks if the event is set. If it is this returns immediately.
/// If the event isn't set, the WaitForEventFuture gets added to the wait
/// queue at the event, and will be signalled once ready.
/// This function is only safe as long as the `wait_node`s address is guaranteed
/// to be stable until it gets removed from the queue.
unsafe fn try_wait(
&mut self,
wait_node: &mut ListNode<WaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<()> {
match wait_node.state {
PollState::New => {
if self.is_set {
// The event is already signaled
wait_node.state = PollState::Done;
Poll::Ready(())
} else {
// Added the task to the wait queue
wait_node.task = Some(cx.waker().clone());
wait_node.state = PollState::Waiting;
self.waiters.add_front(wait_node);
Poll::Pending
}
}
PollState::Waiting => {
// The WaitForEventFuture is already in the queue.
// The event can't have been set, since this would change the
// waitstate inside the mutex. However the caller might have
// passed a different `Waker`. In this case we need to update it.
update_waker_ref(&mut wait_node.task, cx);
Poll::Pending
}
PollState::Done => {
// We have been woken up by the event.
// This does not guarantee that the event is still set. It could
// have been reset it in the meantime.
Poll::Ready(())
}
}
}
fn remove_waiter(&mut self, wait_node: &mut ListNode<WaitQueueEntry>) {
// WaitForEventFuture only needs to get removed if it has been added to
// the wait queue of the Event. This has happened in the PollState::Waiting case.
if let PollState::Waiting = wait_node.state {
// Safety: Due to the state, we know that the node must be part
// of the waiter list
if !unsafe { self.waiters.remove(wait_node) } {
// Panic if the address isn't found. This can only happen if the contract was
// violated, e.g. the WaitQueueEntry got moved after the initial poll.
panic!("Future could not be removed from wait queue");
}
wait_node.state = PollState::Done;
}
}
}
/// A synchronization primitive which can be either in the set or reset state.
///
/// Tasks can wait for the event to get set by obtaining a Future via `wait`.
/// This Future will get fulfilled when the event has been set.
pub struct GenericManualResetEvent<MutexType: RawMutex> {
inner: Mutex<MutexType, EventState>,
}
// The Event is can be sent to other threads as long as it's not borrowed
unsafe impl<MutexType: RawMutex + Send> Send
for GenericManualResetEvent<MutexType>
{
}
// The Event is thread-safe as long as the utilized Mutex is thread-safe
unsafe impl<MutexType: RawMutex + Sync> Sync
for GenericManualResetEvent<MutexType>
{
}
impl<MutexType: RawMutex> core::fmt::Debug
for GenericManualResetEvent<MutexType>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("ManualResetEvent").finish()
}
}
impl<MutexType: RawMutex> GenericManualResetEvent<MutexType> {
/// Creates a new ManualResetEvent in the given state
pub fn new(is_set: bool) -> GenericManualResetEvent<MutexType> {
GenericManualResetEvent {
inner: Mutex::<MutexType, EventState>::new(EventState::new(is_set)),
}
}
/// Sets the event.
///
/// Setting the event will notify all pending waiters.
pub fn set(&self) {
self.inner.lock().set()
}
/// Resets the event.
pub fn reset(&self) {
self.inner.lock().reset()
}
/// Returns whether the event is set
pub fn is_set(&self) -> bool {
self.inner.lock().is_set()
}
/// Returns a future that gets fulfilled when the event is set.
pub fn wait(&self) -> GenericWaitForEventFuture<MutexType> {
GenericWaitForEventFuture {
event: Some(self),
wait_node: ListNode::new(WaitQueueEntry::new()),
}
}
unsafe fn try_wait(
&self,
wait_node: &mut ListNode<WaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<()> {
self.inner.lock().try_wait(wait_node, cx)
}
fn remove_waiter(&self, wait_node: &mut ListNode<WaitQueueEntry>) {
self.inner.lock().remove_waiter(wait_node)
}
}
/// A Future that is resolved once the corresponding ManualResetEvent has been set
#[must_use = "futures do nothing unless polled"]
pub struct GenericWaitForEventFuture<'a, MutexType: RawMutex> {
/// The ManualResetEvent that is associated with this WaitForEventFuture
event: Option<&'a GenericManualResetEvent<MutexType>>,
/// Node for waiting at the event
wait_node: ListNode<WaitQueueEntry>,
}
// Safety: Futures can be sent between threads as long as the underlying
// event is thread-safe (Sync), which allows to poll/register/unregister from
// a different thread.
unsafe impl<'a, MutexType: RawMutex + Sync> Send
for GenericWaitForEventFuture<'a, MutexType>
{
}
impl<'a, MutexType: RawMutex> core::fmt::Debug
for GenericWaitForEventFuture<'a, MutexType>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("GenericWaitForEventFuture").finish()
}
}
impl<'a, MutexType: RawMutex> Future
for GenericWaitForEventFuture<'a, MutexType>
{
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
// It might be possible to use Pin::map_unchecked here instead of the two unsafe APIs.
// However this didn't seem to work for some borrow checker reasons
// Safety: The next operations are safe, because Pin promises us that
// the address of the wait queue entry inside MutexLocalFuture is stable,
// and we don't move any fields inside the future until it gets dropped.
let mut_self: &mut GenericWaitForEventFuture<MutexType> =
unsafe { Pin::get_unchecked_mut(self) };
let event = mut_self
.event
.expect("polled WaitForEventFuture after completion");
let poll_res = unsafe { event.try_wait(&mut mut_self.wait_node, cx) };
if let Poll::Ready(()) = poll_res {
// The event was set
mut_self.event = None;
}
poll_res
}
}
impl<'a, MutexType: RawMutex> FusedFuture
for GenericWaitForEventFuture<'a, MutexType>
{
fn is_terminated(&self) -> bool {
self.event.is_none()
}
}
impl<'a, MutexType: RawMutex> Drop
for GenericWaitForEventFuture<'a, MutexType>
{
fn drop(&mut self) {
// If this WaitForEventFuture has been polled and it was added to the
// wait queue at the event, it must be removed before dropping.
// Otherwise the event would access invalid memory.
if let Some(ev) = self.event {
ev.remove_waiter(&mut self.wait_node);
}
}
}
// Export a non thread-safe version using NoopLock
/// A [`GenericManualResetEvent`] which is not thread-safe.
pub type LocalManualResetEvent = GenericManualResetEvent<NoopLock>;
/// A [`GenericWaitForEventFuture`] for [`LocalManualResetEvent`].
pub type LocalWaitForEventFuture<'a> = GenericWaitForEventFuture<'a, NoopLock>;
#[cfg(feature = "std")]
mod if_std {
use super::*;
// Export a thread-safe version using parking_lot::RawMutex
/// A [`GenericManualResetEvent`] implementation backed by [`parking_lot`].
pub type ManualResetEvent = GenericManualResetEvent<parking_lot::RawMutex>;
/// A [`GenericWaitForEventFuture`] for [`ManualResetEvent`].
pub type WaitForEventFuture<'a> =
GenericWaitForEventFuture<'a, parking_lot::RawMutex>;
}
#[cfg(feature = "std")]
pub use self::if_std::*;
+43
View File
@@ -0,0 +1,43 @@
//! Asynchronous synchronization primitives based on intrusive collections.
//!
//! This module provides various primitives for synchronizing concurrently
//! executing futures.
mod manual_reset_event;
pub use self::manual_reset_event::{
GenericManualResetEvent, GenericWaitForEventFuture, LocalManualResetEvent,
LocalWaitForEventFuture,
};
#[cfg(feature = "std")]
pub use self::manual_reset_event::{ManualResetEvent, WaitForEventFuture};
mod mutex;
pub use self::mutex::{
GenericMutex, GenericMutexGuard, GenericMutexLockFuture, LocalMutex,
LocalMutexGuard, LocalMutexLockFuture,
};
#[cfg(feature = "std")]
pub use self::mutex::{Mutex, MutexGuard, MutexLockFuture};
mod semaphore;
pub use self::semaphore::{
GenericSemaphore, GenericSemaphoreAcquireFuture, GenericSemaphoreReleaser,
LocalSemaphore, LocalSemaphoreAcquireFuture, LocalSemaphoreReleaser,
};
#[cfg(feature = "alloc")]
pub use self::semaphore::{
GenericSharedSemaphore, GenericSharedSemaphoreAcquireFuture,
GenericSharedSemaphoreReleaser,
};
#[cfg(feature = "std")]
pub use self::semaphore::{
Semaphore, SemaphoreAcquireFuture, SemaphoreReleaser, SharedSemaphore,
SharedSemaphoreAcquireFuture, SharedSemaphoreReleaser,
};
+505
View File
@@ -0,0 +1,505 @@
//! An asynchronously awaitable mutex for synchronization between concurrently
//! executing futures.
use crate::{
intrusive_double_linked_list::{LinkedList, ListNode},
utils::update_waker_ref,
NoopLock,
};
use core::{
cell::UnsafeCell,
ops::{Deref, DerefMut},
pin::Pin,
};
use futures_core::{
future::{FusedFuture, Future},
task::{Context, Poll, Waker},
};
use lock_api::{Mutex as LockApiMutex, RawMutex};
/// Tracks how the future had interacted with the mutex
#[derive(PartialEq)]
enum PollState {
/// The task has never interacted with the mutex.
New,
/// The task was added to the wait queue at the mutex.
Waiting,
/// The task had previously waited on the mutex, but was notified
/// that the mutex was released in the meantime.
Notified,
/// The task had been polled to completion.
Done,
}
/// Tracks the MutexLockFuture waiting state.
/// Access to this struct is synchronized through the mutex in the Event.
struct WaitQueueEntry {
/// The task handle of the waiting task
task: Option<Waker>,
/// Current polling state
state: PollState,
}
impl WaitQueueEntry {
/// Creates a new WaitQueueEntry
fn new() -> WaitQueueEntry {
WaitQueueEntry {
task: None,
state: PollState::New,
}
}
}
/// Internal state of the `Mutex`
struct MutexState {
is_fair: bool,
is_locked: bool,
waiters: LinkedList<WaitQueueEntry>,
}
impl MutexState {
fn new(is_fair: bool) -> Self {
MutexState {
is_fair,
is_locked: false,
waiters: LinkedList::new(),
}
}
/// Returns the `Waker` associated with the up the last waiter
///
/// If the Mutex is not fair, removes the associated wait node also from
/// the wait queue
fn return_last_waiter(&mut self) -> Option<Waker> {
let last_waiter = if self.is_fair {
self.waiters.peek_last_mut()
} else {
self.waiters.remove_last()
};
if let Some(last_waiter) = last_waiter {
// Notify the waiter that it can try to lock the mutex again.
// The notification gets tracked inside the waiter.
// If the waiter aborts it's wait (drops the future), another task
// must be woken.
last_waiter.state = PollState::Notified;
let task = &mut last_waiter.task;
return task.take();
}
None
}
fn is_locked(&self) -> bool {
self.is_locked
}
/// Unlocks the mutex
///
/// This is expected to be only called from the current holder of the mutex.
/// The method returns the `Waker` which is associated with the task that
/// needs to get woken due to the unlock.
fn unlock(&mut self) -> Option<Waker> {
if self.is_locked {
self.is_locked = false;
// TODO: Does this require a memory barrier for the actual data,
// or is this covered by unlocking the mutex which protects the data?
// Wakeup the last waiter
self.return_last_waiter()
} else {
None
}
}
/// Tries to lock the mutex synchronously.
///
/// Returns true if the lock obtained and false otherwise.
fn try_lock_sync(&mut self) -> bool {
// The lock can only be obtained synchronously if
// - it is not locked
// - the Semaphore is either not fair, or there are no waiters
// - required_permits == 0
if !self.is_locked && (!self.is_fair || self.waiters.is_empty()) {
self.is_locked = true;
true
} else {
false
}
}
/// Tries to acquire the Mutex from a WaitQueueEntry.
///
/// If it isn't available, the WaitQueueEntry gets added to the wait
/// queue at the Mutex, and will be signalled once ready.
/// This function is only safe as long as the `wait_node`s address is guaranteed
/// to be stable until it gets removed from the queue.
unsafe fn try_lock(
&mut self,
wait_node: &mut ListNode<WaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<()> {
match wait_node.state {
PollState::New => {
// The fast path - the Mutex isn't locked by anyone else.
// If the mutex is fair, noone must be in the wait list before us.
if self.try_lock_sync() {
wait_node.state = PollState::Done;
Poll::Ready(())
} else {
// Add the task to the wait queue
wait_node.task = Some(cx.waker().clone());
wait_node.state = PollState::Waiting;
self.waiters.add_front(wait_node);
Poll::Pending
}
}
PollState::Waiting => {
// The MutexLockFuture is already in the queue.
if self.is_fair {
// The task needs to wait until it gets notified in order to
// maintain the ordering. However the caller might have
// passed a different `Waker`. In this case we need to update it.
update_waker_ref(&mut wait_node.task, cx);
Poll::Pending
} else {
// For throughput improvement purposes, grab the lock immediately
// if it's available.
if !self.is_locked {
self.is_locked = true;
wait_node.state = PollState::Done;
// Since this waiter has been registered before, it must
// get removed from the waiter list.
// Safety: Due to the state, we know that the node must be part
// of the waiter list
self.force_remove_waiter(wait_node);
Poll::Ready(())
} else {
// The caller might have passed a different `Waker`.
// In this case we need to update it.
update_waker_ref(&mut wait_node.task, cx);
Poll::Pending
}
}
}
PollState::Notified => {
// We had been woken by the mutex, since the mutex is available again.
// The mutex thereby removed us from the waiters list.
// Just try to lock again. If the mutex isn't available,
// we need to add it to the wait queue again.
if !self.is_locked {
if self.is_fair {
// In a fair Mutex, the WaitQueueEntry is kept in the
// linked list and must be removed here
// Safety: Due to the state, we know that the node must be part
// of the waiter list
self.force_remove_waiter(wait_node);
}
self.is_locked = true;
wait_node.state = PollState::Done;
Poll::Ready(())
} else {
// Fair mutexes should always be able to acquire the lock
// after they had been notified
debug_assert!(!self.is_fair);
// Add to queue
wait_node.task = Some(cx.waker().clone());
wait_node.state = PollState::Waiting;
self.waiters.add_front(wait_node);
Poll::Pending
}
}
PollState::Done => {
// The future had been polled to completion before
panic!("polled Mutex after completion");
}
}
}
/// Tries to remove a waiter from the wait queue, and panics if the
/// waiter is no longer valid.
unsafe fn force_remove_waiter(
&mut self,
wait_node: &mut ListNode<WaitQueueEntry>,
) {
if !self.waiters.remove(wait_node) {
// Panic if the address isn't found. This can only happen if the contract was
// violated, e.g. the WaitQueueEntry got moved after the initial poll.
panic!("Future could not be removed from wait queue");
}
}
/// Removes the waiter from the list.
///
/// This function is only safe as long as the reference that is passed here
/// equals the reference/address under which the waiter was added.
/// The waiter must not have been moved in between.
///
/// Returns the `Waker` of another task which might get ready to run due to
/// this.
fn remove_waiter(
&mut self,
wait_node: &mut ListNode<WaitQueueEntry>,
) -> Option<Waker> {
// MutexLockFuture only needs to get removed if it had been added to
// the wait queue of the Mutex. This has happened in the PollState::Waiting case.
// If the current waiter was notified, another waiter must get notified now.
match wait_node.state {
PollState::Notified => {
if self.is_fair {
// In a fair Mutex, the WaitQueueEntry is kept in the
// linked list and must be removed here
// Safety: Due to the state, we know that the node must be part
// of the waiter list
unsafe { self.force_remove_waiter(wait_node) };
}
wait_node.state = PollState::Done;
// Since the task was notified but did not lock the Mutex,
// another task gets the chance to run.
self.return_last_waiter()
}
PollState::Waiting => {
// Remove the WaitQueueEntry from the linked list
// Safety: Due to the state, we know that the node must be part
// of the waiter list
unsafe { self.force_remove_waiter(wait_node) };
wait_node.state = PollState::Done;
None
}
PollState::New | PollState::Done => None,
}
}
}
/// An RAII guard returned by the `lock` and `try_lock` methods.
/// When this structure is dropped (falls out of scope), the lock will be
/// unlocked.
pub struct GenericMutexGuard<'a, MutexType: RawMutex, T: 'a> {
/// The Mutex which is associated with this Guard
mutex: &'a GenericMutex<MutexType, T>,
}
impl<MutexType: RawMutex, T: core::fmt::Debug> core::fmt::Debug
for GenericMutexGuard<'_, MutexType, T>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("GenericMutexGuard").finish()
}
}
impl<MutexType: RawMutex, T> Drop for GenericMutexGuard<'_, MutexType, T> {
fn drop(&mut self) {
// Release the mutex
let waker = { self.mutex.state.lock().unlock() };
if let Some(waker) = waker {
waker.wake();
}
}
}
impl<MutexType: RawMutex, T> Deref for GenericMutexGuard<'_, MutexType, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.mutex.value.get() }
}
}
impl<MutexType: RawMutex, T> DerefMut for GenericMutexGuard<'_, MutexType, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.mutex.value.get() }
}
}
// Safety: GenericMutexGuard may only be used across threads if the underlying
// type is Sync.
unsafe impl<MutexType: RawMutex, T: Sync> Sync
for GenericMutexGuard<'_, MutexType, T>
{
}
/// A future which resolves when the target mutex has been successfully acquired.
#[must_use = "futures do nothing unless polled"]
pub struct GenericMutexLockFuture<'a, MutexType: RawMutex, T: 'a> {
/// The Mutex which should get locked trough this Future
mutex: Option<&'a GenericMutex<MutexType, T>>,
/// Node for waiting at the mutex
wait_node: ListNode<WaitQueueEntry>,
}
// Safety: Futures can be sent between threads as long as the underlying
// mutex is thread-safe (Sync), which allows to poll/register/unregister from
// a different thread.
unsafe impl<'a, MutexType: RawMutex + Sync, T: 'a> Send
for GenericMutexLockFuture<'a, MutexType, T>
{
}
impl<'a, MutexType: RawMutex, T: core::fmt::Debug> core::fmt::Debug
for GenericMutexLockFuture<'a, MutexType, T>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("GenericMutexLockFuture").finish()
}
}
impl<'a, MutexType: RawMutex, T> Future
for GenericMutexLockFuture<'a, MutexType, T>
{
type Output = GenericMutexGuard<'a, MutexType, T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// Safety: The next operations are safe, because Pin promises us that
// the address of the wait queue entry inside GenericMutexLockFuture is stable,
// and we don't move any fields inside the future until it gets dropped.
let mut_self: &mut GenericMutexLockFuture<MutexType, T> =
unsafe { Pin::get_unchecked_mut(self) };
let mutex = mut_self
.mutex
.expect("polled GenericMutexLockFuture after completion");
let mut mutex_state = mutex.state.lock();
let poll_res =
unsafe { mutex_state.try_lock(&mut mut_self.wait_node, cx) };
match poll_res {
Poll::Pending => Poll::Pending,
Poll::Ready(()) => {
// The mutex was acquired
mut_self.mutex = None;
Poll::Ready(GenericMutexGuard::<'a, MutexType, T> { mutex })
}
}
}
}
impl<'a, MutexType: RawMutex, T> FusedFuture
for GenericMutexLockFuture<'a, MutexType, T>
{
fn is_terminated(&self) -> bool {
self.mutex.is_none()
}
}
impl<'a, MutexType: RawMutex, T> Drop
for GenericMutexLockFuture<'a, MutexType, T>
{
fn drop(&mut self) {
// If this GenericMutexLockFuture has been polled and it was added to the
// wait queue at the mutex, it must be removed before dropping.
// Otherwise the mutex would access invalid memory.
let waker = if let Some(mutex) = self.mutex {
let mut mutex_state = mutex.state.lock();
mutex_state.remove_waiter(&mut self.wait_node)
} else {
None
};
if let Some(waker) = waker {
waker.wake();
}
}
}
/// A futures-aware mutex.
pub struct GenericMutex<MutexType: RawMutex, T> {
value: UnsafeCell<T>,
state: LockApiMutex<MutexType, MutexState>,
}
// It is safe to send mutexes between threads, as long as they are not used and
// thereby borrowed
unsafe impl<T: Send, MutexType: RawMutex + Send> Send
for GenericMutex<MutexType, T>
{
}
// The mutex is thread-safe as long as the utilized mutex is thread-safe
unsafe impl<T: Send, MutexType: RawMutex + Sync> Sync
for GenericMutex<MutexType, T>
{
}
impl<MutexType: RawMutex, T: core::fmt::Debug> core::fmt::Debug
for GenericMutex<MutexType, T>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("Mutex")
.field("is_locked", &self.is_locked())
.finish()
}
}
impl<MutexType: RawMutex, T> GenericMutex<MutexType, T> {
/// Creates a new futures-aware mutex.
///
/// `is_fair` defines whether the `Mutex` should behave be fair regarding the
/// order of waiters. A fair `Mutex` will only allow the first waiter which
/// tried to lock but failed to lock the `Mutex` once it's available again.
/// Other waiters must wait until either this locking attempt completes, and
/// the `Mutex` gets unlocked again, or until the `MutexLockFuture` which
/// tried to gain the lock is dropped.
pub fn new(value: T, is_fair: bool) -> GenericMutex<MutexType, T> {
GenericMutex::<MutexType, T> {
value: UnsafeCell::new(value),
state: LockApiMutex::new(MutexState::new(is_fair)),
}
}
/// Acquire the mutex asynchronously.
///
/// This method returns a future that will resolve once the mutex has been
/// successfully acquired.
pub fn lock(&self) -> GenericMutexLockFuture<'_, MutexType, T> {
GenericMutexLockFuture::<MutexType, T> {
mutex: Some(&self),
wait_node: ListNode::new(WaitQueueEntry::new()),
}
}
/// Tries to acquire the mutex
///
/// If acquiring the mutex is successful, a [`GenericMutexGuard`]
/// will be returned, which allows to access the contained data.
///
/// Otherwise `None` will be returned.
pub fn try_lock(&self) -> Option<GenericMutexGuard<'_, MutexType, T>> {
if self.state.lock().try_lock_sync() {
Some(GenericMutexGuard { mutex: self })
} else {
None
}
}
/// Returns whether the mutex is locked.
pub fn is_locked(&self) -> bool {
self.state.lock().is_locked()
}
}
// Export a non thread-safe version using NoopLock
/// A [`GenericMutex`] which is not thread-safe.
pub type LocalMutex<T> = GenericMutex<NoopLock, T>;
/// A [`GenericMutexGuard`] for [`LocalMutex`].
pub type LocalMutexGuard<'a, T> = GenericMutexGuard<'a, NoopLock, T>;
/// A [`GenericMutexLockFuture`] for [`LocalMutex`].
pub type LocalMutexLockFuture<'a, T> = GenericMutexLockFuture<'a, NoopLock, T>;
#[cfg(feature = "std")]
mod if_std {
use super::*;
// Export a thread-safe version using parking_lot::RawMutex
/// A [`GenericMutex`] backed by [`parking_lot`].
pub type Mutex<T> = GenericMutex<parking_lot::RawMutex, T>;
/// A [`GenericMutexGuard`] for [`Mutex`].
pub type MutexGuard<'a, T> =
GenericMutexGuard<'a, parking_lot::RawMutex, T>;
/// A [`GenericMutexLockFuture`] for [`Mutex`].
pub type MutexLockFuture<'a, T> =
GenericMutexLockFuture<'a, parking_lot::RawMutex, T>;
}
#[cfg(feature = "std")]
pub use self::if_std::*;
@@ -0,0 +1,846 @@
//! An asynchronously awaitable semaphore for synchronization between concurrently
//! executing futures.
use crate::{
intrusive_double_linked_list::{LinkedList, ListNode},
utils::update_waker_ref,
NoopLock,
};
use core::pin::Pin;
use futures_core::{
future::{FusedFuture, Future},
task::{Context, Poll, Waker},
};
use lock_api::{Mutex as LockApiMutex, RawMutex};
/// Tracks how the future had interacted with the semaphore
#[derive(PartialEq)]
enum PollState {
/// The task has never interacted with the semaphore.
New,
/// The task was added to the wait queue at the semaphore.
Waiting,
/// The task had previously waited on the semaphore, but was notified
/// that the semaphore was released in the meantime and that the task
/// thereby could retry.
Notified,
/// The task had been polled to completion.
Done,
}
/// Tracks the SemaphoreAcquireFuture waiting state.
struct WaitQueueEntry {
/// The task handle of the waiting task
task: Option<Waker>,
/// Current polling state
state: PollState,
/// The amount of permits that should be obtained
required_permits: usize,
}
impl WaitQueueEntry {
/// Creates a new WaitQueueEntry
fn new(required_permits: usize) -> WaitQueueEntry {
WaitQueueEntry {
task: None,
state: PollState::New,
required_permits,
}
}
}
/// Internal state of the `Semaphore`
struct SemaphoreState {
is_fair: bool,
permits: usize,
waiters: LinkedList<WaitQueueEntry>,
}
impl SemaphoreState {
fn new(is_fair: bool, permits: usize) -> Self {
SemaphoreState {
is_fair,
permits,
waiters: LinkedList::new(),
}
}
/// Wakes up the last waiter and removes it from the wait queue
fn wakeup_waiters(&mut self) {
// Wake as many tasks as the permits allow
let mut available = self.permits;
loop {
match self.waiters.peek_last_mut() {
None => return,
Some(last_waiter) => {
// Check if enough permits are available for this waiter.
// If not then a wakeup attempt won't be successful.
if available < last_waiter.required_permits {
return;
}
available -= last_waiter.required_permits;
// Notify the waiter that it can try to acquire the semaphore again.
// The notification gets tracked inside the waiter.
// If the waiter aborts it's wait (drops the future), another task
// must be woken.
if last_waiter.state != PollState::Notified {
last_waiter.state = PollState::Notified;
let task = &last_waiter.task;
if let Some(ref handle) = task {
handle.wake_by_ref();
}
}
// In the case of a non-fair semaphore, the waiters are directly
// removed from the semaphores wait queue when woken.
// That avoids having to remove the wait element later.
if !self.is_fair {
self.waiters.remove_last();
} else {
// For a fair Semaphore we never wake more than 1 task.
// That one needs to acquire the Semaphore.
// TODO: We actually should be able to wake more, since
// it's guaranteed that both tasks could make progress.
// However the we currently can't peek iterate in reverse order.
return;
}
}
}
}
}
fn permits(&self) -> usize {
self.permits
}
/// Releases a certain amount of permits back to the semaphore
fn release(&mut self, permits: usize) {
if permits == 0 {
return;
}
// TODO: Overflow check
self.permits += permits;
// Wakeup the last waiter
self.wakeup_waiters();
}
/// Tries to acquire the given amount of permits synchronously.
///
/// Returns true if the permits were obtained and false otherwise.
fn try_acquire_sync(&mut self, required_permits: usize) -> bool {
// Permits can only be obtained synchronously if there are
// - enough permits available
// - the Semaphore is either not fair, or there are no waiters
// - required_permits == 0
if (self.permits >= required_permits)
&& (!self.is_fair
|| self.waiters.is_empty()
|| required_permits == 0)
{
self.permits -= required_permits;
true
} else {
false
}
}
/// Tries to acquire the Semaphore from a WaitQueueEntry.
/// If it isn't available, the WaitQueueEntry gets added to the wait
/// queue at the Semaphore, and will be signalled once ready.
/// This function is only safe as long as the `wait_node`s address is guaranteed
/// to be stable until it gets removed from the queue.
unsafe fn try_acquire(
&mut self,
wait_node: &mut ListNode<WaitQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<()> {
match wait_node.state {
PollState::New => {
// The fast path - enough permits are available
if self.try_acquire_sync(wait_node.required_permits) {
wait_node.state = PollState::Done;
Poll::Ready(())
} else {
// Add the task to the wait queue
wait_node.task = Some(cx.waker().clone());
wait_node.state = PollState::Waiting;
self.waiters.add_front(wait_node);
Poll::Pending
}
}
PollState::Waiting => {
// The SemaphoreAcquireFuture is already in the queue.
if self.is_fair {
// The task needs to wait until it gets notified in order to
// maintain the ordering.
// However the caller might have passed a different `Waker`.
// In this case we need to update it.
update_waker_ref(&mut wait_node.task, cx);
Poll::Pending
} else {
// For throughput improvement purposes, check immediately
// if enough permits are available
if self.permits >= wait_node.required_permits {
self.permits -= wait_node.required_permits;
wait_node.state = PollState::Done;
// Since this waiter has been registered before, it must
// get removed from the waiter list.
// Safety: Due to the state, we know that the node must be part
// of the waiter list
self.force_remove_waiter(wait_node);
Poll::Ready(())
} else {
// The caller might have passed a different `Waker`.
// In this case we need to update it.
update_waker_ref(&mut wait_node.task, cx);
Poll::Pending
}
}
}
PollState::Notified => {
// We had been woken by the semaphore, since the semaphore is available again.
// The semaphore thereby removed us from the waiters list.
// Just try to lock again. If the semaphore isn't available,
// we need to add it to the wait queue again.
if self.permits >= wait_node.required_permits {
if self.is_fair {
// In a fair Semaphore, the WaitQueueEntry is kept in the
// linked list and must be removed here
// Safety: Due to the state, we know that the node must be part
// of the waiter list
self.force_remove_waiter(wait_node);
}
self.permits -= wait_node.required_permits;
if self.is_fair {
// There might be another task which is ready to run,
// but couldn't, since it was blocked behind the fair waiter.
self.wakeup_waiters();
}
wait_node.state = PollState::Done;
Poll::Ready(())
} else {
// A fair semaphore should never end up in that branch, since
// it's only notified when it's permits are guaranteed to
// be available. assert! in order to find logic bugs
assert!(
!self.is_fair,
"Fair semaphores should always be ready when notified"
);
// Add to queue
wait_node.task = Some(cx.waker().clone());
wait_node.state = PollState::Waiting;
self.waiters.add_front(wait_node);
Poll::Pending
}
}
PollState::Done => {
// The future had been polled to completion before
panic!("polled Mutex after completion");
}
}
}
/// Tries to remove a waiter from the wait queue, and panics if the
/// waiter is no longer valid.
unsafe fn force_remove_waiter(
&mut self,
wait_node: &mut ListNode<WaitQueueEntry>,
) {
if !self.waiters.remove(wait_node) {
// Panic if the address isn't found. This can only happen if the contract was
// violated, e.g. the WaitQueueEntry got moved after the initial poll.
panic!("Future could not be removed from wait queue");
}
}
/// Removes the waiter from the list.
/// This function is only safe as long as the reference that is passed here
/// equals the reference/address under which the waiter was added.
/// The waiter must not have been moved in between.
fn remove_waiter(&mut self, wait_node: &mut ListNode<WaitQueueEntry>) {
// SemaphoreAcquireFuture only needs to get removed if it had been added to
// the wait queue of the Semaphore. This has happened in the PollState::Waiting case.
// If the current waiter was notified, another waiter must get notified now.
match wait_node.state {
PollState::Notified => {
if self.is_fair {
// In a fair Mutex, the WaitQueueEntry is kept in the
// linked list and must be removed here
// Safety: Due to the state, we know that the node must be part
// of the waiter list
unsafe { self.force_remove_waiter(wait_node) };
}
wait_node.state = PollState::Done;
// Wakeup more waiters
self.wakeup_waiters();
}
PollState::Waiting => {
// Remove the WaitQueueEntry from the linked list
// Safety: Due to the state, we know that the node must be part
// of the waiter list
unsafe { self.force_remove_waiter(wait_node) };
wait_node.state = PollState::Done;
}
PollState::New | PollState::Done => {}
}
}
}
/// An RAII guard returned by the `acquire` and `try_acquire` methods.
///
/// When this structure is dropped (falls out of scope),
/// the amount of permits that was used in the `acquire()` call will be released
/// back to the Semaphore.
pub struct GenericSemaphoreReleaser<'a, MutexType: RawMutex> {
/// The Semaphore which is associated with this Releaser
semaphore: &'a GenericSemaphore<MutexType>,
/// The amount of permits to release
permits: usize,
}
impl<MutexType: RawMutex> core::fmt::Debug
for GenericSemaphoreReleaser<'_, MutexType>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("GenericSemaphoreReleaser").finish()
}
}
impl<MutexType: RawMutex> GenericSemaphoreReleaser<'_, MutexType> {
/// Prevents the SemaphoreReleaser from automatically releasing the permits
/// when it gets dropped.
/// This is helpful if the permits must be acquired for a longer lifetime
/// than the one of the SemaphoreReleaser.
/// If this method is used it is important to release the acquired permits
/// manually back to the Semaphore.
pub fn disarm(&mut self) -> usize {
let permits = self.permits;
self.permits = 0;
permits
}
}
impl<MutexType: RawMutex> Drop for GenericSemaphoreReleaser<'_, MutexType> {
fn drop(&mut self) {
// Release the requested amount of permits to the semaphore
if self.permits != 0 {
self.semaphore.state.lock().release(self.permits);
}
}
}
/// A future which resolves when the target semaphore has been successfully acquired.
#[must_use = "futures do nothing unless polled"]
pub struct GenericSemaphoreAcquireFuture<'a, MutexType: RawMutex> {
/// The Semaphore which should get acquired trough this Future
semaphore: Option<&'a GenericSemaphore<MutexType>>,
/// Node for waiting at the semaphore
wait_node: ListNode<WaitQueueEntry>,
/// Whether the obtained permits should automatically be released back
/// to the semaphore.
auto_release: bool,
}
// Safety: Futures can be sent between threads as long as the underlying
// semaphore is thread-safe (Sync), which allows to poll/register/unregister from
// a different thread.
unsafe impl<'a, MutexType: RawMutex + Sync> Send
for GenericSemaphoreAcquireFuture<'a, MutexType>
{
}
impl<'a, MutexType: RawMutex> core::fmt::Debug
for GenericSemaphoreAcquireFuture<'a, MutexType>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("GenericSemaphoreAcquireFuture").finish()
}
}
impl<'a, MutexType: RawMutex> Future
for GenericSemaphoreAcquireFuture<'a, MutexType>
{
type Output = GenericSemaphoreReleaser<'a, MutexType>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// Safety: The next operations are safe, because Pin promises us that
// the address of the wait queue entry inside GenericSemaphoreAcquireFuture is stable,
// and we don't move any fields inside the future until it gets dropped.
let mut_self: &mut GenericSemaphoreAcquireFuture<MutexType> =
unsafe { Pin::get_unchecked_mut(self) };
let semaphore = mut_self
.semaphore
.expect("polled GenericSemaphoreAcquireFuture after completion");
let mut semaphore_state = semaphore.state.lock();
let poll_res =
unsafe { semaphore_state.try_acquire(&mut mut_self.wait_node, cx) };
match poll_res {
Poll::Pending => Poll::Pending,
Poll::Ready(()) => {
// The semaphore was acquired.
mut_self.semaphore = None;
let to_release = match mut_self.auto_release {
true => mut_self.wait_node.required_permits,
false => 0,
};
Poll::Ready(GenericSemaphoreReleaser::<'a, MutexType> {
semaphore,
permits: to_release,
})
}
}
}
}
impl<'a, MutexType: RawMutex> FusedFuture
for GenericSemaphoreAcquireFuture<'a, MutexType>
{
fn is_terminated(&self) -> bool {
self.semaphore.is_none()
}
}
impl<'a, MutexType: RawMutex> Drop
for GenericSemaphoreAcquireFuture<'a, MutexType>
{
fn drop(&mut self) {
// If this GenericSemaphoreAcquireFuture has been polled and it was added to the
// wait queue at the semaphore, it must be removed before dropping.
// Otherwise the semaphore would access invalid memory.
if let Some(semaphore) = self.semaphore {
let mut semaphore_state = semaphore.state.lock();
// Analysis: Does the number of permits play a role here?
// The future was notified because there was a certain amount of permits
// available.
// Removing the waiter will wake up as many tasks as there are permits
// available inside the Semaphore now. If this is bigger than the
// amount of permits required for this task, then additional new
// tasks might get woken. However that isn't bad, since
// those tasks should get into the wait state anyway.
semaphore_state.remove_waiter(&mut self.wait_node);
}
}
}
/// A futures-aware semaphore.
pub struct GenericSemaphore<MutexType: RawMutex> {
state: LockApiMutex<MutexType, SemaphoreState>,
}
// It is safe to send semaphores between threads, as long as they are not used and
// thereby borrowed
unsafe impl<MutexType: RawMutex + Send> Send for GenericSemaphore<MutexType> {}
// The Semaphore is thread-safe as long as the utilized Mutex is thread-safe
unsafe impl<MutexType: RawMutex + Sync> Sync for GenericSemaphore<MutexType> {}
impl<MutexType: RawMutex> core::fmt::Debug for GenericSemaphore<MutexType> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("Semaphore")
.field("permits", &self.permits())
.finish()
}
}
impl<MutexType: RawMutex> GenericSemaphore<MutexType> {
/// Creates a new futures-aware semaphore.
///
/// `is_fair` defines whether the `Semaphore` should behave be fair regarding the
/// order of waiters. A fair `Semaphore` will only allow the oldest waiter on
/// a `Semaphore` to retry acquiring it once it's available again.
/// Other waiters must wait until either this acquire attempt completes, and
/// the `Semaphore` has enough permits after that, or until the
/// [`SemaphoreAcquireFuture`] which tried to acquire the `Semaphore` is dropped.
///
/// If the `Semaphore` isn't fair, waiters that wait for a high amount of
/// permits might never succeed since the permits might be stolen in between
/// by other waiters. Therefore use-cases which make use of very different
/// amount of permits per acquire should use fair semaphores.
/// For use-cases where each `acquire()` tries to acquire the same amount of
/// permits an unfair `Semaphore` might provide throughput advantages.
///
/// `permits` is the amount of permits that a semaphore should hold when
/// created.
pub fn new(is_fair: bool, permits: usize) -> GenericSemaphore<MutexType> {
GenericSemaphore::<MutexType> {
state: LockApiMutex::new(SemaphoreState::new(is_fair, permits)),
}
}
/// Acquire a certain amount of permits on a semaphore asynchronously.
///
/// This method returns a future that will resolve once the given amount of
/// permits have been acquired.
/// The Future will resolve to a [`GenericSemaphoreReleaser`], which will
/// release all acquired permits automatically when dropped.
pub fn acquire(
&self,
nr_permits: usize,
) -> GenericSemaphoreAcquireFuture<'_, MutexType> {
GenericSemaphoreAcquireFuture::<MutexType> {
semaphore: Some(&self),
wait_node: ListNode::new(WaitQueueEntry::new(nr_permits)),
auto_release: true,
}
}
/// Tries to acquire a certain amount of permits on a semaphore.
///
/// If acquiring the permits is successful, a [`GenericSemaphoreReleaser`]
/// will be returned, which will release all acquired permits automatically
/// when dropped.
///
/// Otherwise `None` will be returned.
pub fn try_acquire(
&self,
nr_permits: usize,
) -> Option<GenericSemaphoreReleaser<'_, MutexType>> {
if self.state.lock().try_acquire_sync(nr_permits) {
Some(GenericSemaphoreReleaser {
semaphore: self,
permits: nr_permits,
})
} else {
None
}
}
/// Releases the given amount of permits back to the semaphore.
///
/// This method should in most cases not be used, since the
/// [`GenericSemaphoreReleaser`] which is obtained when acquiring a Semaphore
/// will automatically release the obtained permits again.
///
/// Therefore this method should only be used if the automatic release was
/// disabled by calling [`GenericSemaphoreReleaser::disarm`],
/// or when the amount of permits in the Semaphore
/// should increase from the initial amount.
pub fn release(&self, nr_permits: usize) {
self.state.lock().release(nr_permits)
}
/// Returns the amount of permits that are available on the semaphore
pub fn permits(&self) -> usize {
self.state.lock().permits()
}
}
// Export a non thread-safe version using NoopLock
/// A [`GenericSemaphore`] which is not thread-safe.
pub type LocalSemaphore = GenericSemaphore<NoopLock>;
/// A [`GenericSemaphoreReleaser`] for [`LocalSemaphore`].
pub type LocalSemaphoreReleaser<'a> = GenericSemaphoreReleaser<'a, NoopLock>;
/// A [`GenericSemaphoreAcquireFuture`] for [`LocalSemaphore`].
pub type LocalSemaphoreAcquireFuture<'a> =
GenericSemaphoreAcquireFuture<'a, NoopLock>;
#[cfg(feature = "std")]
mod if_std {
use super::*;
// Export a thread-safe version using parking_lot::RawMutex
/// A [`GenericSemaphore`] backed by [`parking_lot`].
pub type Semaphore = GenericSemaphore<parking_lot::RawMutex>;
/// A [`GenericSemaphoreReleaser`] for [`Semaphore`].
pub type SemaphoreReleaser<'a> =
GenericSemaphoreReleaser<'a, parking_lot::RawMutex>;
/// A [`GenericSemaphoreAcquireFuture`] for [`Semaphore`].
pub type SemaphoreAcquireFuture<'a> =
GenericSemaphoreAcquireFuture<'a, parking_lot::RawMutex>;
}
#[cfg(feature = "std")]
pub use self::if_std::*;
#[cfg(feature = "alloc")]
mod if_alloc {
use super::*;
use alloc::sync::Arc;
/// An RAII guard returned by the `acquire` and `try_acquire` methods.
///
/// When this structure is dropped (falls out of scope),
/// the amount of permits that was used in the `acquire()` call will be released
/// back to the Semaphore.
pub struct GenericSharedSemaphoreReleaser<MutexType: RawMutex> {
/// The Semaphore which is associated with this Releaser
semaphore: GenericSharedSemaphore<MutexType>,
/// The amount of permits to release
permits: usize,
}
impl<MutexType: RawMutex> core::fmt::Debug
for GenericSharedSemaphoreReleaser<MutexType>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("GenericSharedSemaphoreReleaser").finish()
}
}
impl<MutexType: RawMutex> GenericSharedSemaphoreReleaser<MutexType> {
/// Prevents the SharedSemaphoreReleaser from automatically releasing the permits
/// when it gets dropped.
///
/// This is helpful if the permits must be acquired for a longer lifetime
/// than the one of the SemaphoreReleaser.
///
/// If this method is used it is important to release the acquired permits
/// manually back to the Semaphore.
pub fn disarm(&mut self) -> usize {
let permits = self.permits;
self.permits = 0;
permits
}
}
impl<MutexType: RawMutex> Drop for GenericSharedSemaphoreReleaser<MutexType> {
fn drop(&mut self) {
// Release the requested amount of permits to the semaphore
if self.permits != 0 {
self.semaphore.state.lock().release(self.permits);
}
}
}
/// A future which resolves when the target semaphore has been successfully acquired.
#[must_use = "futures do nothing unless polled"]
pub struct GenericSharedSemaphoreAcquireFuture<MutexType: RawMutex> {
/// The Semaphore which should get acquired trough this Future
semaphore: Option<GenericSharedSemaphore<MutexType>>,
/// Node for waiting at the semaphore
wait_node: ListNode<WaitQueueEntry>,
/// Whether the obtained permits should automatically be released back
/// to the semaphore.
auto_release: bool,
}
// Safety: Futures can be sent between threads as long as the underlying
// semaphore is thread-safe (Sync), which allows to poll/register/unregister from
// a different thread.
unsafe impl<MutexType: RawMutex + Sync> Send
for GenericSharedSemaphoreAcquireFuture<MutexType>
{
}
impl<MutexType: RawMutex> core::fmt::Debug
for GenericSharedSemaphoreAcquireFuture<MutexType>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("GenericSharedSemaphoreAcquireFuture")
.finish()
}
}
impl<MutexType: RawMutex> Future
for GenericSharedSemaphoreAcquireFuture<MutexType>
{
type Output = GenericSharedSemaphoreReleaser<MutexType>;
fn poll(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Self::Output> {
// Safety: The next operations are safe, because Pin promises us that
// the address of the wait queue entry inside
// GenericSharedSemaphoreAcquireFuture is stable,
// and we don't move any fields inside the future until it gets dropped.
let mut_self: &mut GenericSharedSemaphoreAcquireFuture<MutexType> =
unsafe { Pin::get_unchecked_mut(self) };
let semaphore = mut_self.semaphore.take().expect(
"polled GenericSharedSemaphoreAcquireFuture after completion",
);
let poll_res = unsafe {
let mut semaphore_state = semaphore.state.lock();
semaphore_state.try_acquire(&mut mut_self.wait_node, cx)
};
match poll_res {
Poll::Pending => {
mut_self.semaphore.replace(semaphore);
Poll::Pending
}
Poll::Ready(()) => {
let to_release = match mut_self.auto_release {
true => mut_self.wait_node.required_permits,
false => 0,
};
Poll::Ready(GenericSharedSemaphoreReleaser::<MutexType> {
semaphore,
permits: to_release,
})
}
}
}
}
impl<MutexType: RawMutex> FusedFuture
for GenericSharedSemaphoreAcquireFuture<MutexType>
{
fn is_terminated(&self) -> bool {
self.semaphore.is_none()
}
}
impl<MutexType: RawMutex> Drop
for GenericSharedSemaphoreAcquireFuture<MutexType>
{
fn drop(&mut self) {
// If this GenericSharedSemaphoreAcquireFuture has been polled and it was added to the
// wait queue at the semaphore, it must be removed before dropping.
// Otherwise the semaphore would access invalid memory.
if let Some(semaphore) = self.semaphore.take() {
let mut semaphore_state = semaphore.state.lock();
// Analysis: Does the number of permits play a role here?
// The future was notified because there was a certain amount of permits
// available.
// Removing the waiter will wake up as many tasks as there are permits
// available inside the Semaphore now. If this is bigger than the
// amount of permits required for this task, then additional new
// tasks might get woken. However that isn't bad, since
// those tasks should get into the wait state anyway.
semaphore_state.remove_waiter(&mut self.wait_node);
}
}
}
/// A futures-aware shared semaphore.
pub struct GenericSharedSemaphore<MutexType: RawMutex> {
state: Arc<LockApiMutex<MutexType, SemaphoreState>>,
}
impl<MutexType: RawMutex> Clone for GenericSharedSemaphore<MutexType> {
fn clone(&self) -> Self {
Self {
state: self.state.clone(),
}
}
}
// It is safe to send semaphores between threads, as long as they are not used and
// thereby borrowed
unsafe impl<MutexType: RawMutex + Send + Sync> Send
for GenericSharedSemaphore<MutexType>
{
}
// The Semaphore is thread-safe as long as the utilized Mutex is thread-safe
unsafe impl<MutexType: RawMutex + Sync> Sync
for GenericSharedSemaphore<MutexType>
{
}
impl<MutexType: RawMutex> core::fmt::Debug
for GenericSharedSemaphore<MutexType>
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("Semaphore")
.field("permits", &self.permits())
.finish()
}
}
impl<MutexType: RawMutex> GenericSharedSemaphore<MutexType> {
/// Creates a new futures-aware shared semaphore.
///
/// See `GenericSharedSemaphore` for more information.
pub fn new(
is_fair: bool,
permits: usize,
) -> GenericSharedSemaphore<MutexType> {
GenericSharedSemaphore::<MutexType> {
state: Arc::new(LockApiMutex::new(SemaphoreState::new(
is_fair, permits,
))),
}
}
/// Acquire a certain amount of permits on a semaphore asynchronously.
///
/// This method returns a future that will resolve once the given amount of
/// permits have been acquired.
/// The Future will resolve to a [`GenericSharedSemaphoreReleaser`], which will
/// release all acquired permits automatically when dropped.
pub fn acquire(
&self,
nr_permits: usize,
) -> GenericSharedSemaphoreAcquireFuture<MutexType> {
GenericSharedSemaphoreAcquireFuture::<MutexType> {
semaphore: Some(self.clone()),
wait_node: ListNode::new(WaitQueueEntry::new(nr_permits)),
auto_release: true,
}
}
/// Tries to acquire a certain amount of permits on a semaphore.
///
/// If acquiring the permits is successful, a [`GenericSharedSemaphoreReleaser`]
/// will be returned, which will release all acquired permits automatically
/// when dropped.
///
/// Otherwise `None` will be returned.
pub fn try_acquire(
&self,
nr_permits: usize,
) -> Option<GenericSharedSemaphoreReleaser<MutexType>> {
if self.state.lock().try_acquire_sync(nr_permits) {
Some(GenericSharedSemaphoreReleaser {
semaphore: self.clone(),
permits: nr_permits,
})
} else {
None
}
}
/// Releases the given amount of permits back to the semaphore.
///
/// This method should in most cases not be used, since the
/// [`GenericSharedSemaphoreReleaser`] which is obtained when acquiring a Semaphore
/// will automatically release the obtained permits again.
///
/// Therefore this method should only be used if the automatic release was
/// disabled by calling [`GenericSharedSemaphoreReleaser::disarm`],
/// or when the amount of permits in the Semaphore
/// should increase from the initial amount.
pub fn release(&self, nr_permits: usize) {
self.state.lock().release(nr_permits)
}
/// Returns the amount of permits that are available on the semaphore
pub fn permits(&self) -> usize {
self.state.lock().permits()
}
}
// Export parking_lot based shared semaphores in std mode
#[cfg(feature = "std")]
mod if_std {
use super::*;
/// A [`GenericSharedSemaphore`] backed by [`parking_lot`].
pub type SharedSemaphore =
GenericSharedSemaphore<parking_lot::RawMutex>;
/// A [`GenericSharedSemaphoreReleaser`] for [`SharedSemaphore`].
pub type SharedSemaphoreReleaser =
GenericSharedSemaphoreReleaser<parking_lot::RawMutex>;
/// A [`GenericSharedSemaphoreAcquireFuture`] for [`SharedSemaphore`].
pub type SharedSemaphoreAcquireFuture =
GenericSharedSemaphoreAcquireFuture<parking_lot::RawMutex>;
}
#[cfg(feature = "std")]
pub use self::if_std::*;
}
#[cfg(feature = "alloc")]
pub use self::if_alloc::*;
@@ -0,0 +1,101 @@
//! Monotonic clocks
use core::sync::atomic::{AtomicUsize, Ordering};
/// A monotonic source of time.
///
/// Clocks must always returning increasing timestamps.
pub trait Clock: Sync {
/// Returns a timestamp in milliseconds which represents the current time
/// according to the clock.
///
/// Clocks must only return timestamps that are bigger or equal than what
/// they returned on the last call to `now()`.
fn now(&self) -> u64;
}
/// A [`Clock`] which can be set to arbitrary timestamps for testing purposes.
///
/// It can be used in a test case as demonstrated in the following example:
/// ```
/// use futures_intrusive::timer::MockClock;
/// # #[cfg(feature = "std")]
/// # use futures_intrusive::timer::TimerService;
///
/// static TEST_CLOCK: MockClock = MockClock::new();
/// TEST_CLOCK.set_time(2300); // Set the current time
/// # #[cfg(feature = "std")]
/// let timer = TimerService::new(&TEST_CLOCK);
/// ```
pub struct MockClock {
now: core::sync::atomic::AtomicUsize,
}
impl core::fmt::Debug for MockClock {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let now = self.now();
f.debug_struct("MockClock").field("now", &now).finish()
}
}
impl MockClock {
/// Creates a new instance of the [`MockClock`], which is initialized to
/// timestamp 0.
pub const fn new() -> MockClock {
MockClock {
now: AtomicUsize::new(0),
}
}
/// Sets the current timestamp inside to [`MockClock`] to the given value
pub fn set_time(&self, timestamp: u64) {
if timestamp > (core::usize::MAX as u64) {
panic!("timestamps bigger than usize::MAX are not supported")
}
let to_set = timestamp as usize;
self.now.store(to_set, Ordering::Release);
}
}
impl Clock for MockClock {
fn now(&self) -> u64 {
self.now.load(Ordering::Relaxed) as u64
}
}
#[cfg(feature = "std")]
mod if_std {
use super::*;
use std::time::Instant;
/// A Clock that makes use of the Standard libraries [`std::time::Instant`]
/// functionality in order to generate monotonically increasing timestamps.
pub struct StdClock {
start: Instant,
}
impl core::fmt::Debug for StdClock {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("StdClock").finish()
}
}
impl StdClock {
/// Creates a new [`StdClock`]
pub fn new() -> StdClock {
StdClock {
start: Instant::now(),
}
}
}
impl Clock for StdClock {
fn now(&self) -> u64 {
let elapsed = Instant::now() - self.start;
elapsed.as_millis() as u64
}
}
}
#[cfg(feature = "std")]
pub use self::if_std::*;
+22
View File
@@ -0,0 +1,22 @@
//! Asynchronous timers.
//!
//! This module provides a timer implementation which returns awaitable
//! `Future`s.
//! The timer can work with a configurable clock source. In order to utilize
//! the system clock, a global instance `StdClock` can be utilized.
mod clock;
pub use self::clock::{Clock, MockClock};
#[cfg(feature = "std")]
pub use self::clock::StdClock;
mod timer;
pub use self::timer::{
GenericTimerService, LocalTimer, LocalTimerFuture, LocalTimerService,
Timer, TimerFuture,
};
#[cfg(feature = "std")]
pub use self::timer::TimerService;
@@ -0,0 +1,460 @@
//! An asynchronously awaitable timer
use super::clock::Clock;
use crate::{
intrusive_pairing_heap::{HeapNode, PairingHeap},
utils::update_waker_ref,
NoopLock,
};
use core::{pin::Pin, time::Duration};
use futures_core::{
future::{FusedFuture, Future},
task::{Context, Poll, Waker},
};
use lock_api::{Mutex, RawMutex};
/// Tracks how the future had interacted with the timer
#[derive(PartialEq)]
enum PollState {
/// The task is not registered at the wait queue at the timer
Unregistered,
/// The task was added to the wait queue at the timer
Registered,
/// The timer has expired and was thereby removed from the wait queue at
/// the timer. Having this extra state avoids to query the clock for an
/// extra time.
Expired,
}
/// Tracks the timer futures waiting state.
struct TimerQueueEntry {
/// Timestamp when the timer expires
expiry: u64,
/// The task handle of the waiting task
task: Option<Waker>,
/// Current polling state
state: PollState,
}
impl TimerQueueEntry {
/// Creates a new TimerQueueEntry
fn new(expiry: u64) -> TimerQueueEntry {
TimerQueueEntry {
expiry,
task: None,
state: PollState::Unregistered,
}
}
}
impl PartialEq for TimerQueueEntry {
fn eq(&self, other: &TimerQueueEntry) -> bool {
// This is technically not correct. However for the usage in this module
// we only need to compare timers by expiration.
self.expiry == other.expiry
}
}
impl Eq for TimerQueueEntry {}
impl PartialOrd for TimerQueueEntry {
fn partial_cmp(
&self,
other: &TimerQueueEntry,
) -> Option<core::cmp::Ordering> {
// Compare timer queue entries by expiration time
self.expiry.partial_cmp(&other.expiry)
}
}
impl Ord for TimerQueueEntry {
fn cmp(&self, other: &TimerQueueEntry) -> core::cmp::Ordering {
self.expiry.cmp(&other.expiry)
}
}
/// Internal state of the timer
struct TimerState {
/// The clock which is utilized
clock: &'static dyn Clock,
/// The heap of waiters, which are waiting for their timer to expire
waiters: PairingHeap<TimerQueueEntry>,
}
impl TimerState {
fn new(clock: &'static dyn Clock) -> TimerState {
TimerState {
clock,
waiters: PairingHeap::new(),
}
}
/// Registers the timer future at the Timer.
/// This function is only safe as long as the `wait_node`s address is guaranteed
/// to be stable until it gets removed from the queue.
unsafe fn try_wait(
&mut self,
wait_node: &mut HeapNode<TimerQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<()> {
match wait_node.state {
PollState::Unregistered => {
let now = self.clock.now();
if now >= wait_node.expiry {
// The timer is already expired
wait_node.state = PollState::Expired;
Poll::Ready(())
} else {
// Added the task to the wait queue
wait_node.task = Some(cx.waker().clone());
wait_node.state = PollState::Registered;
self.waiters.insert(wait_node);
Poll::Pending
}
}
PollState::Registered => {
// Since the timer wakes up all waiters and moves their states to
// Expired when the timer expired, it can't be expired here yet.
// However the caller might have passed a different `Waker`.
// In this case we need to update it.
update_waker_ref(&mut wait_node.task, cx);
Poll::Pending
}
PollState::Expired => Poll::Ready(()),
}
}
fn remove_waiter(&mut self, wait_node: &mut HeapNode<TimerQueueEntry>) {
// TimerFuture only needs to get removed if it had been added to
// the wait queue of the timer. This has happened in the PollState::Registered case.
if let PollState::Registered = wait_node.state {
// Safety: Due to the state, we know that the node must be part
// of the waiter heap
unsafe { self.waiters.remove(wait_node) };
wait_node.state = PollState::Unregistered;
}
}
/// Returns a timestamp when the next timer expires.
///
/// For thread-safe timers, the returned value is not precise and subject to
/// race-conditions, since other threads can add timer in the meantime.
fn next_expiration(&self) -> Option<u64> {
// Safety: We ensure that any node in the heap remains alive
unsafe { self.waiters.peek_min().map(|first| first.as_ref().expiry) }
}
/// Checks whether any of the attached Futures is expired
fn check_expirations(&mut self) {
let now = self.clock.now();
while let Some(mut first) = self.waiters.peek_min() {
// Safety: We ensure that any node in the heap remains alive
unsafe {
let entry = first.as_mut();
let first_expiry = entry.expiry;
if now >= first_expiry {
// The timer is expired.
entry.state = PollState::Expired;
if let Some(task) = entry.task.take() {
task.wake();
}
} else {
// Remaining timers are not expired
break;
}
// Remove the expired timer
self.waiters.remove(entry);
}
}
}
}
/// Adapter trait that allows Futures to generically interact with timer
/// implementations via dynamic dispatch.
trait TimerAccess {
unsafe fn try_wait(
&self,
wait_node: &mut HeapNode<TimerQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<()>;
fn remove_waiter(&self, wait_node: &mut HeapNode<TimerQueueEntry>);
}
/// An asynchronously awaitable timer which is bound to a thread.
///
/// The timer operates on millisecond precision and makes use of a configurable
/// clock source.
///
/// The timer allows to wait asynchronously either for a certain duration,
/// or until the provided [`Clock`] reaches a certain timestamp.
pub trait LocalTimer {
/// Returns a future that gets fulfilled after the given `Duration`
fn delay(&self, delay: Duration) -> LocalTimerFuture;
/// Returns a future that gets fulfilled when the utilized [`Clock`] reaches
/// the given timestamp.
fn deadline(&self, timestamp: u64) -> LocalTimerFuture;
}
/// An asynchronously awaitable thread-safe timer.
///
/// The timer operates on millisecond precision and makes use of a configurable
/// clock source.
///
/// The timer allows to wait asynchronously either for a certain duration,
/// or until the provided [`Clock`] reaches a certain timestamp.
pub trait Timer {
/// Returns a future that gets fulfilled after the given `Duration`
fn delay(&self, delay: Duration) -> TimerFuture;
/// Returns a future that gets fulfilled when the utilized [`Clock`] reaches
/// the given timestamp.
fn deadline(&self, timestamp: u64) -> TimerFuture;
}
/// An asynchronously awaitable timer.
///
/// The timer operates on millisecond precision and makes use of a configurable
/// clock source.
///
/// The timer allows to wait asynchronously either for a certain duration,
/// or until the provided [`Clock`] reaches a certain timestamp.
///
/// In order to unblock tasks that are waiting on the timer,
/// [`check_expirations`](GenericTimerService::check_expirations)
/// must be called in regular intervals on this timer service.
///
/// The timer can either be running on a separate timer thread (in case a
/// thread-safe timer type is utilize), or it can be integrated into an executor
/// in order to minimize context switches.
pub struct GenericTimerService<MutexType: RawMutex> {
inner: Mutex<MutexType, TimerState>,
}
// The timer can be sent to other threads as long as it's not borrowed
unsafe impl<MutexType: RawMutex + Send> Send
for GenericTimerService<MutexType>
{
}
// The timer is thread-safe as long as it uses a thread-safe mutex
unsafe impl<MutexType: RawMutex + Sync> Sync
for GenericTimerService<MutexType>
{
}
impl<MutexType: RawMutex> core::fmt::Debug for GenericTimerService<MutexType> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("TimerService").finish()
}
}
impl<MutexType: RawMutex> GenericTimerService<MutexType> {
/// Creates a new Timer in the given state.
///
/// The Timer will query the provided [`Clock`] instance for the current
/// time whenever required.
///
/// In order to create a create a clock which utilizes system time,
/// [`StdClock`](super::StdClock) can be utilized.
/// In order to simulate time for test purposes,
/// [`MockClock`](super::MockClock) can be utilized.
pub fn new(clock: &'static dyn Clock) -> GenericTimerService<MutexType> {
GenericTimerService::<MutexType> {
inner: Mutex::new(TimerState::new(clock)),
}
}
/// Returns a timestamp when the next timer expires.
///
/// For thread-safe timers, the returned value is not precise and subject to
/// race-conditions, since other threads can add timer in the meantime.
///
/// Therefore adding any timer to the [`GenericTimerService`] should also
/// make sure to wake up the executor which polls for timeouts, in order to
/// let it capture the latest change.
pub fn next_expiration(&self) -> Option<u64> {
self.inner.lock().next_expiration()
}
/// Checks whether any of the attached [`TimerFuture`]s has expired.
/// In this case the associated task is woken up.
pub fn check_expirations(&self) {
self.inner.lock().check_expirations()
}
/// Returns a deadline based on the current timestamp plus the given Duration
fn deadline_from_now(&self, duration: Duration) -> u64 {
let now = self.inner.lock().clock.now();
let duration_ms =
core::cmp::min(duration.as_millis(), core::u64::MAX as u128) as u64;
now.saturating_add(duration_ms)
}
}
impl<MutexType: RawMutex> LocalTimer for GenericTimerService<MutexType> {
/// Returns a future that gets fulfilled after the given [`Duration`]
fn delay(&self, delay: Duration) -> LocalTimerFuture {
let deadline = self.deadline_from_now(delay);
LocalTimer::deadline(&*self, deadline)
}
/// Returns a future that gets fulfilled when the utilized [`Clock`] reaches
/// the given timestamp.
fn deadline(&self, timestamp: u64) -> LocalTimerFuture {
LocalTimerFuture {
timer: Some(self),
wait_node: HeapNode::new(TimerQueueEntry::new(timestamp)),
}
}
}
impl<MutexType: RawMutex> Timer for GenericTimerService<MutexType>
where
MutexType: Sync,
{
/// Returns a future that gets fulfilled after the given [`Duration`]
fn delay(&self, delay: Duration) -> TimerFuture {
let deadline = self.deadline_from_now(delay);
Timer::deadline(&*self, deadline)
}
/// Returns a future that gets fulfilled when the utilized [`Clock`] reaches
/// the given timestamp.
fn deadline(&self, timestamp: u64) -> TimerFuture {
TimerFuture {
timer_future: LocalTimerFuture {
timer: Some(self),
wait_node: HeapNode::new(TimerQueueEntry::new(timestamp)),
},
}
}
}
impl<MutexType: RawMutex> TimerAccess for GenericTimerService<MutexType> {
unsafe fn try_wait(
&self,
wait_node: &mut HeapNode<TimerQueueEntry>,
cx: &mut Context<'_>,
) -> Poll<()> {
self.inner.lock().try_wait(wait_node, cx)
}
fn remove_waiter(&self, wait_node: &mut HeapNode<TimerQueueEntry>) {
self.inner.lock().remove_waiter(wait_node)
}
}
/// A Future that is resolved once the requested time has elapsed.
#[must_use = "futures do nothing unless polled"]
pub struct LocalTimerFuture<'a> {
/// The Timer that is associated with this TimerFuture
timer: Option<&'a dyn TimerAccess>,
/// Node for waiting on the timer
wait_node: HeapNode<TimerQueueEntry>,
}
impl<'a> core::fmt::Debug for LocalTimerFuture<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("LocalTimerFuture").finish()
}
}
impl<'a> Future for LocalTimerFuture<'a> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
// It might be possible to use Pin::map_unchecked here instead of the two unsafe APIs.
// However this didn't seem to work for some borrow checker reasons
// Safety: The next operations are safe, because Pin promises us that
// the address of the wait queue entry inside TimerFuture is stable,
// and we don't move any fields inside the future until it gets dropped.
let mut_self: &mut LocalTimerFuture =
unsafe { Pin::get_unchecked_mut(self) };
let timer =
mut_self.timer.expect("polled TimerFuture after completion");
let poll_res = unsafe { timer.try_wait(&mut mut_self.wait_node, cx) };
if poll_res.is_ready() {
// A value was available
mut_self.timer = None;
}
poll_res
}
}
impl<'a> FusedFuture for LocalTimerFuture<'a> {
fn is_terminated(&self) -> bool {
self.timer.is_none()
}
}
impl<'a> Drop for LocalTimerFuture<'a> {
fn drop(&mut self) {
// If this TimerFuture has been polled and it was added to the
// wait queue at the timer, it must be removed before dropping.
// Otherwise the timer would access invalid memory.
if let Some(timer) = self.timer {
timer.remove_waiter(&mut self.wait_node);
}
}
}
/// A Future that is resolved once the requested time has elapsed.
#[must_use = "futures do nothing unless polled"]
pub struct TimerFuture<'a> {
/// The Timer that is associated with this TimerFuture
timer_future: LocalTimerFuture<'a>,
}
// Safety: TimerFutures are only returned by GenericTimerService instances which
// are thread-safe (RawMutex: Sync).
unsafe impl<'a> Send for TimerFuture<'a> {}
impl<'a> core::fmt::Debug for TimerFuture<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("TimerFuture").finish()
}
}
impl<'a> Future for TimerFuture<'a> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
// Safety: TimerFuture is a pure wrapper around LocalTimerFuture.
// The inner value is never moved
let inner_pin = unsafe {
Pin::map_unchecked_mut(self, |fut| &mut fut.timer_future)
};
inner_pin.poll(cx)
}
}
impl<'a> FusedFuture for TimerFuture<'a> {
fn is_terminated(&self) -> bool {
self.timer_future.is_terminated()
}
}
// Export a non thread-safe version using NoopLock
/// A [`GenericTimerService`] implementation which is not thread-safe.
pub type LocalTimerService = GenericTimerService<NoopLock>;
#[cfg(feature = "std")]
mod if_std {
use super::*;
// Export a thread-safe version using parking_lot::RawMutex
/// A [`GenericTimerService`] implementation backed by [`parking_lot`].
pub type TimerService = GenericTimerService<parking_lot::RawMutex>;
}
#[cfg(feature = "std")]
pub use self::if_std::*;
+14
View File
@@ -0,0 +1,14 @@
//! Utilities which are used within the library
use core::task::{Context, Waker};
/// Updates a `Waker` which is stored inside a `Option` to the newest value
/// which is delivered via a `Context`.
pub fn update_waker_ref(waker_option: &mut Option<Waker>, cx: &Context) {
if waker_option
.as_ref()
.map_or(true, |stored_waker| !stored_waker.will_wake(cx.waker()))
{
*waker_option = Some(cx.waker().clone());
}
}
@@ -0,0 +1,215 @@
use futures::future::{FusedFuture, Future};
use futures::task::Context;
use futures_intrusive::sync::LocalManualResetEvent;
use futures_test::task::{new_count_waker, panic_waker};
use pin_utils::pin_mut;
macro_rules! gen_event_tests {
($mod_name:ident, $event_type:ident) => {
mod $mod_name {
use super::*;
#[test]
fn synchronous() {
let event = $event_type::new(false);
assert!(!event.is_set());
event.set();
assert!(event.is_set());
event.reset();
assert!(!event.is_set());
}
#[test]
fn immediately_ready_event() {
let event = $event_type::new(true);
let waker = &panic_waker();
let cx = &mut Context::from_waker(&waker);
assert!(event.is_set());
let poll = event.wait();
pin_mut!(poll);
assert!(!poll.as_mut().is_terminated());
assert!(poll.as_mut().poll(cx).is_ready());
assert!(poll.as_mut().is_terminated());
}
#[test]
fn cancel_mid_wait() {
let event = $event_type::new(false);
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
{
// Cancel a wait in between other waits
// In order to arbitrarily drop a non movable future we have to box and pin it
let mut poll1 = Box::pin(event.wait());
let mut poll2 = Box::pin(event.wait());
let mut poll3 = Box::pin(event.wait());
let mut poll4 = Box::pin(event.wait());
let mut poll5 = Box::pin(event.wait());
assert!(poll1.as_mut().poll(cx).is_pending());
assert!(poll2.as_mut().poll(cx).is_pending());
assert!(poll3.as_mut().poll(cx).is_pending());
assert!(poll4.as_mut().poll(cx).is_pending());
assert!(poll5.as_mut().poll(cx).is_pending());
assert!(!poll1.is_terminated());
assert!(!poll2.is_terminated());
assert!(!poll3.is_terminated());
assert!(!poll4.is_terminated());
assert!(!poll5.is_terminated());
// Cancel 2 futures. Only the remaining ones should get completed
drop(poll2);
drop(poll4);
assert!(poll1.as_mut().poll(cx).is_pending());
assert!(poll3.as_mut().poll(cx).is_pending());
assert!(poll5.as_mut().poll(cx).is_pending());
assert_eq!(count, 0);
event.set();
assert!(poll1.as_mut().poll(cx).is_ready());
assert!(poll3.as_mut().poll(cx).is_ready());
assert!(poll5.as_mut().poll(cx).is_ready());
assert!(poll1.is_terminated());
assert!(poll3.is_terminated());
assert!(poll5.is_terminated());
}
assert_eq!(count, 3);
}
#[test]
fn cancel_end_wait() {
let event = $event_type::new(false);
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let poll1 = event.wait();
let poll2 = event.wait();
let poll3 = event.wait();
let poll4 = event.wait();
pin_mut!(poll1);
pin_mut!(poll2);
pin_mut!(poll3);
pin_mut!(poll4);
assert!(poll1.as_mut().poll(cx).is_pending());
assert!(poll2.as_mut().poll(cx).is_pending());
// Start polling some wait handles which get cancelled
// before new ones are attached
{
let poll5 = event.wait();
let poll6 = event.wait();
pin_mut!(poll5);
pin_mut!(poll6);
assert!(poll5.as_mut().poll(cx).is_pending());
assert!(poll6.as_mut().poll(cx).is_pending());
}
assert!(poll3.as_mut().poll(cx).is_pending());
assert!(poll4.as_mut().poll(cx).is_pending());
event.set();
assert!(poll1.as_mut().poll(cx).is_ready());
assert!(poll2.as_mut().poll(cx).is_ready());
assert!(poll3.as_mut().poll(cx).is_ready());
assert!(poll4.as_mut().poll(cx).is_ready());
assert_eq!(count, 4);
}
#[test]
fn poll_from_multiple_executors() {
let (waker_1, count_1) = new_count_waker();
let (waker_2, count_2) = new_count_waker();
let event = $event_type::new(false);
let cx_1 = &mut Context::from_waker(&waker_1);
let cx_2 = &mut Context::from_waker(&waker_2);
let fut = event.wait();
pin_mut!(fut);
assert!(fut.as_mut().poll(cx_1).is_pending());
assert!(fut.as_mut().poll(cx_2).is_pending());
event.set();
assert!(event.is_set());
assert_eq!(count_1, 0);
assert_eq!(count_2, 1);
assert!(fut.as_mut().poll(cx_2).is_ready());
assert!(fut.as_mut().is_terminated());
}
}
};
}
gen_event_tests!(local_manual_reset_event_tests, LocalManualResetEvent);
#[cfg(feature = "std")]
mod if_std {
use super::*;
use futures::executor::block_on;
use futures_intrusive::sync::ManualResetEvent;
use std::sync::Arc;
use std::thread;
use std::time;
gen_event_tests!(manual_reset_event_tests, ManualResetEvent);
fn is_send<T: Send>(_: &T) {}
fn is_send_value<T: Send>(_: T) {}
fn is_sync<T: Sync>(_: &T) {}
#[test]
fn event_futures_are_send() {
let event = ManualResetEvent::new(false);
is_sync(&event);
{
let wait_fut = event.wait();
is_send(&wait_fut);
pin_mut!(wait_fut);
is_send(&wait_fut);
}
is_send_value(event);
}
#[test]
fn multithreaded_smoke() {
let event = Arc::new(ManualResetEvent::new(false));
let waiters: Vec<thread::JoinHandle<time::Instant>> = [1..4]
.iter()
.map(|_| {
let ev = event.clone();
thread::spawn(move || {
block_on(ev.wait());
time::Instant::now()
})
})
.collect();
let start = time::Instant::now();
thread::sleep(time::Duration::from_millis(100));
event.set();
for waiter in waiters.into_iter() {
let end_time = waiter.join().unwrap();
let diff = end_time - start;
assert!(diff > time::Duration::from_millis(50));
}
}
}
File diff suppressed because it is too large Load Diff
+476
View File
@@ -0,0 +1,476 @@
use futures::future::{FusedFuture, Future};
use futures::task::{Context, Poll};
use futures_intrusive::sync::LocalMutex;
use futures_test::task::{new_count_waker, panic_waker};
use pin_utils::pin_mut;
macro_rules! gen_mutex_tests {
($mod_name:ident, $mutex_type:ident) => {
mod $mod_name {
use super::*;
#[test]
fn uncontended_lock() {
for is_fair in &[true, false] {
let waker = &panic_waker();
let cx = &mut Context::from_waker(&waker);
let mtx = $mutex_type::new(5, *is_fair);
assert_eq!(false, mtx.is_locked());
{
let mutex_fut = mtx.lock();
pin_mut!(mutex_fut);
match mutex_fut.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(mut guard) => {
assert_eq!(true, mtx.is_locked());
assert_eq!(5, *guard);
*guard += 7;
assert_eq!(12, *guard);
}
};
assert!(mutex_fut.as_mut().is_terminated());
}
assert_eq!(false, mtx.is_locked());
{
let mutex_fut = mtx.lock();
pin_mut!(mutex_fut);
match mutex_fut.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(guard) => {
assert_eq!(true, mtx.is_locked());
assert_eq!(12, *guard);
}
};
}
assert_eq!(false, mtx.is_locked());
}
}
#[test]
#[should_panic]
fn poll_after_completion_should_panic() {
for is_fair in &[true, false] {
let waker = &panic_waker();
let cx = &mut Context::from_waker(&waker);
let mtx = $mutex_type::new(5, *is_fair);
let mutex_fut = mtx.lock();
pin_mut!(mutex_fut);
let guard = match mutex_fut.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(guard) => guard,
};
assert_eq!(5, *guard);
assert!(mutex_fut.as_mut().is_terminated());
let _ = mutex_fut.poll(cx);
}
}
#[test]
fn contended_lock() {
for is_fair in &[true, false] {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let mtx = $mutex_type::new(5, *is_fair);
let mutex_fut1 = mtx.lock();
pin_mut!(mutex_fut1);
// Lock the mutex
let mut guard1 = match mutex_fut1.poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(guard) => guard,
};
*guard1 = 27;
// The second and third lock attempt must fail
let mutex_fut2 = mtx.lock();
pin_mut!(mutex_fut2);
assert!(mutex_fut2.as_mut().poll(cx).is_pending());
assert!(!mutex_fut2.as_mut().is_terminated());
let mutex_fut3 = mtx.lock();
pin_mut!(mutex_fut3);
assert!(mutex_fut3.as_mut().poll(cx).is_pending());
assert!(!mutex_fut3.as_mut().is_terminated());
assert_eq!(count, 0);
// Unlock - mutex should be available again
drop(guard1);
assert_eq!(count, 1);
let mut guard2 = match mutex_fut2.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(guard) => guard,
};
assert_eq!(27, *guard2);
*guard2 = 72;
assert!(mutex_fut2.as_mut().is_terminated());
assert!(mutex_fut3.as_mut().poll(cx).is_pending());
assert!(!mutex_fut3.as_mut().is_terminated());
assert_eq!(count, 1);
// Unlock - mutex should be available again
drop(guard2);
assert_eq!(count, 2);
let guard3 = match mutex_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(guard) => guard,
};
assert_eq!(72, *guard3);
assert!(mutex_fut3.as_mut().is_terminated());
drop(guard3);
assert_eq!(count, 2);
}
}
#[test]
fn lock_synchronously() {
for is_fair in &[true] {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let mtx = $mutex_type::new(5, *is_fair);
let mutex_fut1 = mtx.lock();
pin_mut!(mutex_fut1);
// Lock the mutex
let mut guard1 = match mutex_fut1.poll(cx) {
Poll::Pending => panic!("Expect mutex to get acquired 1"),
Poll::Ready(guard) => guard,
};
*guard1 = 7;
assert_eq!(true, mtx.is_locked());
// Synchronous lock attempt fails
assert!(mtx.try_lock().is_none());
// Add an async waiter
let mut mutex_fut2 = Box::pin(mtx.lock());
assert!(mutex_fut2.as_mut().poll(cx).is_pending());
assert_eq!(count, 0);
// Release - mutex should be available again
drop(guard1);
assert_eq!(false, mtx.is_locked());
// In the fair case we shouldn't be able to obtain the
// mutex asynchronously. In the unfair case it should
// be possible.
if *is_fair {
assert!(mtx.try_lock().is_none());
// Cancel async lock attempt
drop(mutex_fut2);
// Now the mutex should be lockable
}
let guard = mtx.try_lock().unwrap();
assert_eq!(true, mtx.is_locked());
assert_eq!(*guard, 7);
drop(guard);
}
}
#[test]
fn cancel_wait_for_mutex() {
for is_fair in &[true, false] {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let mtx = $mutex_type::new(5, *is_fair);
// Lock the mutex
let mut guard1 = mtx.try_lock().unwrap();
*guard1 = 27;
// The second and third lock attempt must fail
let mut mutex_fut2 = Box::pin(mtx.lock());
let mut mutex_fut3 = Box::pin(mtx.lock());
assert!(mutex_fut2.as_mut().poll(cx).is_pending());
assert!(mutex_fut3.as_mut().poll(cx).is_pending());
// Before the mutex gets available, cancel one lock attempt
drop(mutex_fut2);
// Unlock - mutex should be available again. Mutex2 should have been notified
drop(guard1);
assert_eq!(count, 1);
// Unlock - mutex should be available again
match mutex_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(guard) => guard,
};
}
}
#[test]
fn unlock_next_when_notification_is_not_used() {
for is_fair in &[true, false] {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let mtx = $mutex_type::new(5, *is_fair);
// Lock the mutex
let mut guard1 = mtx.try_lock().unwrap();
*guard1 = 27;
// The second and third lock attempt must fail
let mut mutex_fut2 = Box::pin(mtx.lock());
let mut mutex_fut3 = Box::pin(mtx.lock());
assert!(mutex_fut2.as_mut().poll(cx).is_pending());
assert!(!mutex_fut2.as_mut().is_terminated());
assert!(mutex_fut3.as_mut().poll(cx).is_pending());
assert!(!mutex_fut3.as_mut().is_terminated());
assert_eq!(count, 0);
// Unlock - mutex should be available again. Mutex2 should have been notified
drop(guard1);
assert_eq!(count, 1);
// We don't use the notification. Expect the next waiting task to be woken up
drop(mutex_fut2);
assert_eq!(count, 2);
// Unlock - mutex should be available again
match mutex_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(guard) => guard,
};
}
}
#[test]
fn new_waiters_on_unfair_mutex_can_acquire_future_while_one_task_is_notified() {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let mtx = $mutex_type::new(5, false);
// Lock the mutex
let mut guard1 = mtx.try_lock().unwrap();
*guard1 = 27;
// The second and third lock attempt must fail
let mut mutex_fut2 = Box::pin(mtx.lock());
let mut mutex_fut3 = Box::pin(mtx.lock());
assert!(mutex_fut2.as_mut().poll(cx).is_pending());
// Unlock - mutex should be available again. fut2 should have been notified
drop(guard1);
assert_eq!(count, 1);
// Lock fut3 in between. This should succeed
let guard3 = match mutex_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(guard) => guard,
};
// Now fut2 can't use it's notification and is still pending
assert!(mutex_fut2.as_mut().poll(cx).is_pending());
// When we drop fut3, the mutex should signal that it's available for fut2,
// which needs to have re-registered
drop(guard3);
assert_eq!(count, 2);
match mutex_fut2.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(_guard) => {}
};
}
#[test]
fn waiters_on_unfair_mutex_can_acquire_future_through_repolling_if_one_task_is_notified(
) {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let mtx = $mutex_type::new(5, false);
// Lock the mutex
let mut guard1 = mtx.try_lock().unwrap();
*guard1 = 27;
// The second and third lock attempt must fail
let mut mutex_fut2 = Box::pin(mtx.lock());
let mut mutex_fut3 = Box::pin(mtx.lock());
assert!(mutex_fut2.as_mut().poll(cx).is_pending());
assert!(mutex_fut3.as_mut().poll(cx).is_pending());
// Unlock - mutex should be available again. fut2 should have been notified
drop(guard1);
assert_eq!(count, 1);
// Lock fut3 in between. This should succeed
let guard3 = match mutex_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(guard) => guard,
};
// Now fut2 can't use it's notification and is still pending
assert!(mutex_fut2.as_mut().poll(cx).is_pending());
// When we drop fut3, the mutex should signal that it's available for fut2,
// which needs to have re-registered
drop(guard3);
assert_eq!(count, 2);
match mutex_fut2.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(_guard) => {}
};
}
#[test]
fn new_waiters_on_fair_mutex_cant_acquire_future_while_one_task_is_notified() {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let mtx = $mutex_type::new(5, true);
// Lock the mutex
let mut guard1 = mtx.try_lock().unwrap();
*guard1 = 27;
// The second and third lock attempt must fail
let mut mutex_fut2 = Box::pin(mtx.lock());
let mut mutex_fut3 = Box::pin(mtx.lock());
assert!(mutex_fut2.as_mut().poll(cx).is_pending());
// Unlock - mutex should be available again. fut2 should have been notified
drop(guard1);
assert_eq!(count, 1);
// Lock fut3 in between. This should fail
assert!(mutex_fut3.as_mut().poll(cx).is_pending());
// fut2 should be lockable
match mutex_fut2.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(_guard) => {}
};
// Now fut3 should have been signaled and be lockable
assert_eq!(count, 2);
match mutex_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(_guard) => {}
};
}
#[test]
fn waiters_on_fair_mutex_cant_acquire_future_through_repolling_if_one_task_is_notified()
{
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let mtx = $mutex_type::new(5, true);
// Lock the mutex
let mut guard1 = mtx.try_lock().unwrap();
*guard1 = 27;
// The second and third lock attempt must fail
let mut mutex_fut2 = Box::pin(mtx.lock());
let mut mutex_fut3 = Box::pin(mtx.lock());
assert!(mutex_fut2.as_mut().poll(cx).is_pending());
assert!(mutex_fut3.as_mut().poll(cx).is_pending());
// Unlock - mutex should be available again. fut2 should have been notified
drop(guard1);
assert_eq!(count, 1);
// Lock fut3 in between. This should fail, since fut2 should get the mutex first
assert!(mutex_fut3.as_mut().poll(cx).is_pending());
// fut2 should be lockable
match mutex_fut2.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(_guard) => {}
};
// Now fut3 should be lockable
assert_eq!(count, 2);
match mutex_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect mutex to get locked"),
Poll::Ready(_guard) => {}
};
}
#[test]
fn poll_from_multiple_executors() {
for is_fair in &[true, false] {
let (waker_1, count_1) = new_count_waker();
let (waker_2, count_2) = new_count_waker();
let mtx = $mutex_type::new(5, *is_fair);
// Lock the mutex
let mut guard1 = mtx.try_lock().unwrap();
*guard1 = 27;
let cx_1 = &mut Context::from_waker(&waker_1);
let cx_2 = &mut Context::from_waker(&waker_2);
let fut = mtx.lock();
pin_mut!(fut);
assert!(fut.as_mut().poll(cx_1).is_pending());
assert!(fut.as_mut().poll(cx_2).is_pending());
drop(guard1);
assert_eq!(count_1, 0);
assert_eq!(count_2, 1);
assert!(fut.as_mut().poll(cx_2).is_ready());
assert!(fut.as_mut().is_terminated());
}
}
}
};
}
gen_mutex_tests!(local_mutex_tests, LocalMutex);
#[cfg(feature = "std")]
mod if_std {
use super::*;
use futures::FutureExt;
use futures_intrusive::sync::Mutex;
gen_mutex_tests!(mutex_tests, Mutex);
fn is_send<T: Send>(_: &T) {}
fn is_send_value<T: Send>(_: T) {}
fn is_sync<T: Sync>(_: &T) {}
#[test]
fn mutex_futures_are_send() {
let mutex = Mutex::new(true, true);
is_sync(&mutex);
{
let lock_fut = mutex.lock();
is_send(&lock_fut);
pin_mut!(lock_fut);
is_send(&lock_fut);
let waker = &panic_waker();
let cx = &mut Context::from_waker(&waker);
pin_mut!(lock_fut);
let res = lock_fut.poll_unpin(cx);
let guard = match res {
Poll::Ready(v) => v,
Poll::Pending => panic!("Expected to be ready"),
};
is_send(&guard);
is_send_value(guard);
}
is_send_value(mutex);
}
}
@@ -0,0 +1,333 @@
use futures::future::{FusedFuture, Future};
use futures::task::{Context, Poll};
use futures_intrusive::channel::{ChannelSendError, LocalOneshotChannel};
use futures_test::task::{new_count_waker, panic_waker};
use pin_utils::pin_mut;
macro_rules! gen_oneshot_tests {
($mod_name:ident, $channel_type:ident) => {
mod $mod_name {
use super::*;
fn assert_receive_done<FutureType, T>(
cx: &mut Context,
receive_fut: &mut core::pin::Pin<&mut FutureType>,
value: Option<T>,
) where
FutureType: Future<Output = Option<T>> + FusedFuture,
T: PartialEq + core::fmt::Debug,
{
match receive_fut.as_mut().poll(cx) {
Poll::Pending => panic!("future is not ready"),
Poll::Ready(res) => {
if res != value {
panic!("Unexpected value {:?}", res);
}
}
};
assert!(receive_fut.as_mut().is_terminated());
}
#[test]
fn send_on_closed_channel() {
let channel = $channel_type::<i32>::new();
assert!(channel.close().is_newly_closed());
assert_eq!(Err(ChannelSendError(5)), channel.send(5));
}
#[test]
fn close_status() {
let channel = $channel_type::<i32>::new();
assert!(channel.close().is_newly_closed());
assert!(channel.close().is_already_closed());
assert!(channel.close().is_already_closed());
}
#[test]
fn close_unblocks_receive() {
let channel = $channel_type::<i32>::new();
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let fut = channel.receive();
pin_mut!(fut);
assert!(fut.as_mut().poll(cx).is_pending());
let fut2 = channel.receive();
pin_mut!(fut2);
assert!(fut2.as_mut().poll(cx).is_pending());
assert_eq!(count, 0);
assert!(channel.close().is_newly_closed());
assert_eq!(count, 2);
assert_receive_done(cx, &mut fut, None);
assert_receive_done(cx, &mut fut2, None);
}
#[test]
fn receive_after_send() {
let channel = $channel_type::<i32>::new();
let waker = &panic_waker();
let cx = &mut Context::from_waker(&waker);
channel.send(5).unwrap();
let receive_fut = channel.receive();
pin_mut!(receive_fut);
assert!(!receive_fut.as_mut().is_terminated());
assert_receive_done(cx, &mut receive_fut, Some(5));
// A second receive attempt must yield None, since the
// value was taken out of the channel
let receive_fut2 = channel.receive();
pin_mut!(receive_fut2);
assert_receive_done(cx, &mut receive_fut2, None);
}
#[test]
fn send_after_receive() {
let channel = $channel_type::<i32>::new();
let (waker, _) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let receive_fut1 = channel.receive();
let receive_fut2 = channel.receive();
pin_mut!(receive_fut1);
pin_mut!(receive_fut2);
assert!(!receive_fut1.as_mut().is_terminated());
assert!(!receive_fut2.as_mut().is_terminated());
let poll_res1 = receive_fut1.as_mut().poll(cx);
let poll_res2 = receive_fut2.as_mut().poll(cx);
assert!(poll_res1.is_pending());
assert!(poll_res2.is_pending());
channel.send(5).unwrap();
assert_receive_done(cx, &mut receive_fut1, Some(5));
// receive_fut2 isn't terminated, since it hasn't been polled
assert!(!receive_fut2.as_mut().is_terminated());
// When it gets polled, it must evaluate to None
assert_receive_done(cx, &mut receive_fut2, None);
}
#[test]
fn second_send_rejects_value() {
let channel = $channel_type::<i32>::new();
let (waker, _) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let receive_fut1 = channel.receive();
pin_mut!(receive_fut1);
assert!(!receive_fut1.as_mut().is_terminated());
assert!(receive_fut1.as_mut().poll(cx).is_pending());
// First send
channel.send(5).unwrap();
assert!(receive_fut1.as_mut().poll(cx).is_ready());
// Second send
let send_res = channel.send(7);
match send_res {
Err(ChannelSendError(7)) => {} // expected
_ => panic!("Second second should reject"),
}
}
#[test]
fn cancel_mid_wait() {
let channel = $channel_type::new();
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
{
// Cancel a wait in between other waits
// In order to arbitrarily drop a non movable future we have to box and pin it
let mut poll1 = Box::pin(channel.receive());
let mut poll2 = Box::pin(channel.receive());
let mut poll3 = Box::pin(channel.receive());
let mut poll4 = Box::pin(channel.receive());
let mut poll5 = Box::pin(channel.receive());
assert!(poll1.as_mut().poll(cx).is_pending());
assert!(poll2.as_mut().poll(cx).is_pending());
assert!(poll3.as_mut().poll(cx).is_pending());
assert!(poll4.as_mut().poll(cx).is_pending());
assert!(poll5.as_mut().poll(cx).is_pending());
assert!(!poll1.is_terminated());
assert!(!poll2.is_terminated());
assert!(!poll3.is_terminated());
assert!(!poll4.is_terminated());
assert!(!poll5.is_terminated());
// Cancel 2 futures. Only the remaining ones should get completed
drop(poll2);
drop(poll4);
assert!(poll1.as_mut().poll(cx).is_pending());
assert!(poll3.as_mut().poll(cx).is_pending());
assert!(poll5.as_mut().poll(cx).is_pending());
assert_eq!(count, 0);
channel.send(7).unwrap();
assert_eq!(count, 3);
assert!(poll1.as_mut().poll(cx).is_ready());
assert!(poll3.as_mut().poll(cx).is_ready());
assert!(poll5.as_mut().poll(cx).is_ready());
assert!(poll1.is_terminated());
assert!(poll3.is_terminated());
assert!(poll5.is_terminated());
}
assert_eq!(count, 3)
}
#[test]
fn cancel_end_wait() {
let channel = $channel_type::new();
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let poll1 = channel.receive();
let poll2 = channel.receive();
let poll3 = channel.receive();
let poll4 = channel.receive();
pin_mut!(poll1);
pin_mut!(poll2);
pin_mut!(poll3);
pin_mut!(poll4);
assert!(poll1.as_mut().poll(cx).is_pending());
assert!(poll2.as_mut().poll(cx).is_pending());
// Start polling some wait handles which get cancelled
// before new ones are attached
{
let poll5 = channel.receive();
let poll6 = channel.receive();
pin_mut!(poll5);
pin_mut!(poll6);
assert!(poll5.as_mut().poll(cx).is_pending());
assert!(poll6.as_mut().poll(cx).is_pending());
}
assert!(poll3.as_mut().poll(cx).is_pending());
assert!(poll4.as_mut().poll(cx).is_pending());
channel.send(99).unwrap();
assert!(poll1.as_mut().poll(cx).is_ready());
assert!(poll2.as_mut().poll(cx).is_ready());
assert!(poll3.as_mut().poll(cx).is_ready());
assert!(poll4.as_mut().poll(cx).is_ready());
assert_eq!(count, 4)
}
#[test]
fn poll_from_multiple_executors() {
let (waker_1, count_1) = new_count_waker();
let (waker_2, count_2) = new_count_waker();
let channel = $channel_type::new();
let cx_1 = &mut Context::from_waker(&waker_1);
let cx_2 = &mut Context::from_waker(&waker_2);
let fut = channel.receive();
pin_mut!(fut);
assert!(fut.as_mut().poll(cx_1).is_pending());
assert!(fut.as_mut().poll(cx_2).is_pending());
channel.send(99).unwrap();
assert_eq!(count_1, 0);
assert_eq!(count_2, 1);
assert_receive_done(cx_2, &mut fut, Some(99));
}
}
};
}
gen_oneshot_tests!(local_oneshot_channel_tests, LocalOneshotChannel);
#[cfg(feature = "std")]
mod if_std {
use super::*;
use futures_intrusive::channel::shared::oneshot_channel;
use futures_intrusive::channel::OneshotChannel;
gen_oneshot_tests!(oneshot_channel_tests, OneshotChannel);
fn is_send<T: Send>(_: &T) {}
fn is_send_value<T: Send>(_: T) {}
fn is_sync<T: Sync>(_: &T) {}
#[test]
fn channel_futures_are_send() {
let channel = OneshotChannel::<i32>::new();
is_sync(&channel);
{
let recv_fut = channel.receive();
is_send(&recv_fut);
pin_mut!(recv_fut);
is_send(&recv_fut);
let send_fut = channel.send(3);
is_send(&send_fut);
pin_mut!(send_fut);
is_send(&send_fut);
}
is_send_value(channel);
}
#[test]
fn shared_channel_futures_are_send() {
let (sender, receiver) = oneshot_channel::<i32>();
is_sync(&sender);
is_sync(&receiver);
let recv_fut = receiver.receive();
is_send(&recv_fut);
pin_mut!(recv_fut);
is_send(&recv_fut);
let send_fut = sender.send(3);
is_send(&send_fut);
pin_mut!(send_fut);
is_send(&send_fut);
is_send_value(sender);
is_send_value(receiver);
}
#[test]
fn dropping_shared_channel_senders_closes_channel() {
let (waker, _) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let (sender, receiver) = oneshot_channel::<i32>();
let fut = receiver.receive();
pin_mut!(fut);
assert!(fut.as_mut().poll(cx).is_pending());
drop(sender);
match fut.as_mut().poll(cx) {
Poll::Ready(None) => {}
Poll::Ready(Some(_)) => panic!("Expected no value"),
Poll::Pending => panic!("Expected channel to be closed"),
}
}
#[test]
fn dropping_shared_channel_receivers_closes_channel() {
let (sender, receiver) = oneshot_channel::<i32>();
drop(receiver);
assert_eq!(Err(ChannelSendError(5)), sender.send(5));
}
}
@@ -0,0 +1,606 @@
use futures::future::{FusedFuture, Future};
use futures::task::{Context, Poll};
use futures_intrusive::sync::LocalSemaphore;
use futures_test::task::{new_count_waker, panic_waker};
use pin_utils::pin_mut;
macro_rules! gen_semaphore_tests {
($mod_name:ident, $semaphore_type:ident) => {
mod $mod_name {
use super::*;
#[test]
fn uncontended_acquire() {
for is_fair in &[true, false] {
let waker = &panic_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(*is_fair, 2);
assert_eq!(2, sem.permits());
{
let sem_fut = sem.acquire(1);
pin_mut!(sem_fut);
match sem_fut.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(_guard) => {
assert_eq!(1, sem.permits());
},
};
assert!(sem_fut.as_mut().is_terminated());
assert_eq!(2, sem.permits());
}
assert_eq!(2, sem.permits());
{
let sem_fut = sem.acquire(2);
pin_mut!(sem_fut);
match sem_fut.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(_guard) => {
assert_eq!(0, sem.permits());
},
};
assert!(sem_fut.as_mut().is_terminated());
}
assert_eq!(2, sem.permits());
}
}
#[test]
fn manual_release_via_disarm() {
for is_fair in &[true, false] {
let waker = &panic_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(*is_fair, 2);
assert_eq!(2, sem.permits());
{
let sem_fut = sem.acquire(1);
pin_mut!(sem_fut);
match sem_fut.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(mut guard) => {
assert_eq!(1, sem.permits());
guard.disarm();
},
};
assert!(sem_fut.as_mut().is_terminated());
assert_eq!(1, sem.permits());
}
assert_eq!(1, sem.permits());
{
let sem_fut = sem.acquire(1);
pin_mut!(sem_fut);
match sem_fut.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(mut guard) => {
assert_eq!(0, sem.permits());
guard.disarm();
},
};
assert!(sem_fut.as_mut().is_terminated());
}
assert_eq!(0, sem.permits());
sem.release(2);
assert_eq!(2, sem.permits());
}
}
#[test]
#[should_panic]
fn poll_after_completion_should_panic() {
for is_fair in &[true, false] {
let waker = &panic_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(*is_fair, 2);
let sem_fut = sem.acquire(2);
pin_mut!(sem_fut);
match sem_fut.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(guard) => guard,
};
assert!(sem_fut.as_mut().is_terminated());
let _ = sem_fut.poll(cx);
}
}
#[test]
fn contended_acquire() {
for is_fair in &[false, true] {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(*is_fair, 3);
let sem_fut1 = sem.acquire(3);
pin_mut!(sem_fut1);
// Acquire the semaphore
let guard1 = match sem_fut1.poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired 1"),
Poll::Ready(guard) => guard
};
// The next acquire attempts must fail
let sem_fut2 = sem.acquire(1);
pin_mut!(sem_fut2);
assert!(sem_fut2.as_mut().poll(cx).is_pending());
assert!(!sem_fut2.as_mut().is_terminated());
let sem_fut3 = sem.acquire(2);
pin_mut!(sem_fut3);
assert!(sem_fut3.as_mut().poll(cx).is_pending());
assert!(!sem_fut3.as_mut().is_terminated());
let sem_fut4 = sem.acquire(2);
pin_mut!(sem_fut4);
assert!(sem_fut4.as_mut().poll(cx).is_pending());
assert!(!sem_fut4.as_mut().is_terminated());
assert_eq!(count, 0);
// Release - semaphore should be available again and allow
// fut2 and fut3 to complete
assert_eq!(0, sem.permits());
drop(guard1);
assert_eq!(3, sem.permits());
// At least one task should be awoken.
if *is_fair {
assert_eq!(count, 1);
}
else {
assert_eq!(count, 2);
}
let guard2 = match sem_fut2.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired 2"),
Poll::Ready(guard) => guard
};
assert!(sem_fut2.as_mut().is_terminated());
assert_eq!(2, sem.permits());
// In the fair case, the next task should be woken up here
assert_eq!(count, 2);
let guard3 = match sem_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired 3"),
Poll::Ready(guard) => guard
};
assert!(sem_fut3.as_mut().is_terminated());
assert_eq!(0, sem.permits());
assert!(sem_fut4.as_mut().poll(cx).is_pending());
assert!(!sem_fut4.as_mut().is_terminated());
// Release - some permits should be available again
drop(guard2);
assert_eq!(1, sem.permits());
assert_eq!(count, 2);
assert!(sem_fut4.as_mut().poll(cx).is_pending());
assert!(!sem_fut4.as_mut().is_terminated());
// After releasing the permits from fut3, there should be
// enough permits for fut4 getting woken.
drop(guard3);
assert_eq!(3, sem.permits());
assert_eq!(count, 3);
let guard4 = match sem_fut4.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired 4"),
Poll::Ready(guard) => guard
};
assert!(sem_fut4.as_mut().is_terminated());
drop(guard4);
assert_eq!(3, sem.permits());
assert_eq!(count, 3);
}
}
#[test]
fn acquire_synchronously() {
for is_fair in &[true] {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(*is_fair, 3);
let sem_fut1 = sem.acquire(3);
pin_mut!(sem_fut1);
// Acquire the semaphore
let guard1 = match sem_fut1.poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired 1"),
Poll::Ready(guard) => guard
};
// Some failing acquire attempts
assert!(sem.try_acquire(1).is_none());
// Add an async waiter
let mut sem_fut2 = Box::pin(sem.acquire(1));
assert!(sem_fut2.as_mut().poll(cx).is_pending());
assert_eq!(count, 0);
// Release - semaphore should be available again
drop(guard1);
assert_eq!(3, sem.permits());
// In the fair case we shouldn't be able to obtain the
// semaphore asynchronously. In the unfair case it should
// be possible.
if *is_fair {
assert!(sem.try_acquire(1).is_none());
// Cancel async acquire attempt
drop(sem_fut2);
// Now the semaphore should be acquireable
}
let guard = sem.try_acquire(1).unwrap();
assert_eq!(2, sem.permits());
let mut guard2 = sem.try_acquire(2).unwrap();
assert_eq!(0, sem.permits());
guard2.disarm();
sem.release(2);
drop(guard);
}
}
#[test]
fn acquire_0_permits_without_other_waiters() {
for is_fair in &[false, true] {
let (waker, _count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(*is_fair, 3);
// Acquire the semaphore
let guard1 = sem.try_acquire(3).unwrap();
assert_eq!(0, sem.permits());
let sem_fut2 = sem.acquire(0);
pin_mut!(sem_fut2);
let guard2 = match sem_fut2.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired 2"),
Poll::Ready(guard) => guard
};
drop(guard2);
assert_eq!(0, sem.permits());
drop(guard1);
assert_eq!(3, sem.permits());
}
}
#[test]
fn acquire_0_permits_with_other_waiters() {
for is_fair in &[false, true] {
let (waker, _count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(*is_fair, 3);
// Acquire the semaphore
let guard1 = sem.try_acquire(3).unwrap();
assert_eq!(0, sem.permits());
let sem_fut2 = sem.acquire(1);
pin_mut!(sem_fut2);
assert!(sem_fut2.as_mut().poll(cx).is_pending());
let sem_fut3 = sem.acquire(0);
pin_mut!(sem_fut3);
let guard3 = match sem_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired 3"),
Poll::Ready(guard) => guard
};
drop(guard3);
assert_eq!(0, sem.permits());
drop(guard1);
assert_eq!(3, sem.permits());
let guard2 = match sem_fut2.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired 2"),
Poll::Ready(guard) => guard
};
assert_eq!(2, sem.permits());
drop(guard2);
}
}
#[test]
fn cancel_wait_for_semaphore() {
for is_fair in &[true, false] {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(*is_fair, 5);
// Acquire the semaphore
let guard1 = sem.try_acquire(5).unwrap();
// The second and third lock attempt must fail
let mut sem_fut2 = Box::pin(sem.acquire(1));
let mut sem_fut3 = Box::pin(sem.acquire(1));
assert!(sem_fut2.as_mut().poll(cx).is_pending());
assert!(sem_fut3.as_mut().poll(cx).is_pending());
// Before the semaphore gets available, cancel one acquire attempt
drop(sem_fut2);
// Unlock - semaphore should be available again.
// fut2 should have been notified
drop(guard1);
assert_eq!(count, 1);
// Unlock - semaphore should be available again
match sem_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(guard) => guard
};
}
}
#[test]
fn unlock_next_when_notification_is_not_used() {
for is_fair in &[true, false] {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(*is_fair, 2);
let guard1 = sem.try_acquire(2).unwrap();
// The second and third acquire attempt must fail
let mut sem_fut2 = Box::pin(sem.acquire(1));
let mut sem_fut3 = Box::pin(sem.acquire(1));
assert!(sem_fut2.as_mut().poll(cx).is_pending());
assert!(!sem_fut2.as_mut().is_terminated());
assert!(sem_fut3.as_mut().poll(cx).is_pending());
assert!(!sem_fut3.as_mut().is_terminated());
assert_eq!(count, 0);
// Release - semaphore should be available again. fut2 should have been notified
drop(guard1);
if *is_fair {
assert_eq!(count, 1);
}
else {
assert_eq!(count, 2);
}
// We don't use the notification. Expect the next waiting task to be woken up
drop(sem_fut2);
assert_eq!(count, 2);
match sem_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(guard) => guard
};
}
}
#[test]
fn new_waiters_on_unfair_semaphore_can_acquire_future_while_one_task_is_notified() {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(false, 3);
// Acquire the semaphore
let guard1 = sem.try_acquire(3).unwrap();
// The second and third acquire attempt must fail
let mut sem_fut2 = Box::pin(sem.acquire(3));
let mut sem_fut3 = Box::pin(sem.acquire(3));
assert!(sem_fut2.as_mut().poll(cx).is_pending());
// Release - Semaphore should be available again. fut2 should have been notified
drop(guard1);
assert_eq!(count, 1);
// Acquire fut3 in between. This should succeed
let guard3 = match sem_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(guard) => guard
};
// Now fut2 can't use it's notification and is still pending
assert!(sem_fut2.as_mut().poll(cx).is_pending());
// When we drop fut3, the semaphore should signal that it's available for fut2,
// which needs to have re-registered
drop(guard3);
assert_eq!(count, 2);
match sem_fut2.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(_guard) => {},
};
}
#[test]
fn waiters_on_unfair_semaphore_can_acquire_future_through_repolling_if_one_task_is_notified() {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(false, 3);
// Acquire the semaphore
let guard1 = sem.try_acquire(3).unwrap();
// The second and third acquire attempt must fail
let mut sem_fut2 = Box::pin(sem.acquire(3));
let mut sem_fut3 = Box::pin(sem.acquire(3));
// Start polling both futures, which means both are waiters
assert!(sem_fut2.as_mut().poll(cx).is_pending());
assert!(sem_fut3.as_mut().poll(cx).is_pending());
// Release - semaphore should be available again. fut2 should have been notified
drop(guard1);
assert_eq!(count, 1);
// Acquire fut3 in between. This should succeed
let guard3 = match sem_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(guard) => guard
};
// Now fut2 can't use it's notification and is still pending
assert!(sem_fut2.as_mut().poll(cx).is_pending());
// When we drop fut3, the mutex should signal that it's available for fut2,
// which needs to have re-registered
drop(guard3);
assert_eq!(count, 2);
match sem_fut2.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(_guard) => {},
};
}
#[test]
fn new_waiters_on_fair_semaphore_cant_acquire_future_while_one_task_is_notified() {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(true, 3);
// Acquire the semaphore
let guard1 = sem.try_acquire(3).unwrap();
// The second and third acquire attempt must fail
let mut sem_fut2 = Box::pin(sem.acquire(3));
let mut sem_fut3 = Box::pin(sem.acquire(3));
assert!(sem_fut2.as_mut().poll(cx).is_pending());
// Release - semaphore should be available again. fut2 should have been notified
drop(guard1);
assert_eq!(count, 1);
// Try to acquire fut3 in between. This should fail
assert!(sem_fut3.as_mut().poll(cx).is_pending());
// fut2 should be be able to get acquired
match sem_fut2.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(_guard) => {},
};
// Now fut3 should have been signaled and should be able to get acquired
assert_eq!(count, 2);
match sem_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(_guard) => {},
};
}
#[test]
fn waiters_on_fair_semaphore_cant_acquire_future_through_repolling_if_one_task_is_notified() {
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let sem = $semaphore_type::new(true, 3);
// Acquire the semaphore
let guard1 = sem.try_acquire(3).unwrap();
// The second and third acquire attempt must fail
let mut sem_fut2 = Box::pin(sem.acquire(3));
let mut sem_fut3 = Box::pin(sem.acquire(3));
assert!(sem_fut2.as_mut().poll(cx).is_pending());
assert!(sem_fut3.as_mut().poll(cx).is_pending());
// Release - semaphore should be available again. fut2 should have been notified
drop(guard1);
assert_eq!(count, 1);
// Acquire fut3 in between. This should fail, since fut2 should get the permits first
assert!(sem_fut3.as_mut().poll(cx).is_pending());
// fut2 should be acquired
match sem_fut2.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(_guard) => {},
};
// Now fut3 should be able to get acquired
assert_eq!(count, 2);
match sem_fut3.as_mut().poll(cx) {
Poll::Pending => panic!("Expect semaphore to get acquired"),
Poll::Ready(_guard) => {},
};
}
#[test]
fn poll_from_multiple_executors() {
for is_fair in &[true, false] {
let (waker_1, count_1) = new_count_waker();
let (waker_2, count_2) = new_count_waker();
let sem = $semaphore_type::new(*is_fair, 3);
// Acquire the semaphore
let guard = sem.try_acquire(3).unwrap();
let fut = sem.acquire(1);
pin_mut!(fut);
let cx_1 = &mut Context::from_waker(&waker_1);
let cx_2 = &mut Context::from_waker(&waker_2);
assert!(fut.as_mut().poll(cx_1).is_pending());
assert!(fut.as_mut().poll(cx_2).is_pending());
drop(guard);
assert_eq!(count_1, 0);
assert_eq!(count_2, 1);
assert!(fut.as_mut().poll(cx_2).is_ready());
assert!(fut.as_mut().is_terminated());
}
}
}
}
}
gen_semaphore_tests!(local_semaphore_tests, LocalSemaphore);
#[cfg(feature = "std")]
mod if_std {
use super::*;
use futures::FutureExt;
use futures_intrusive::sync::{Semaphore, SharedSemaphore};
gen_semaphore_tests!(semaphore_tests, Semaphore);
gen_semaphore_tests!(shared_semaphore_tests, SharedSemaphore);
fn is_send<T: Send>(_: &T) {}
fn is_send_value<T: Send>(_: T) {}
fn is_sync<T: Sync>(_: &T) {}
#[test]
fn semaphore_futures_are_send() {
let sem = Semaphore::new(true, 3);
is_sync(&sem);
{
let wait_fut = sem.acquire(3);
is_send(&wait_fut);
pin_mut!(wait_fut);
is_send(&wait_fut);
let waker = &panic_waker();
let cx = &mut Context::from_waker(&waker);
pin_mut!(wait_fut);
let res = wait_fut.poll_unpin(cx);
let releaser = match res {
Poll::Ready(v) => v,
Poll::Pending => panic!("Expected to be ready"),
};
is_send(&releaser);
is_send_value(releaser);
}
is_send_value(sem);
}
}
@@ -0,0 +1,472 @@
use futures::future::{FusedFuture, Future};
use futures::task::{Context, Poll};
use futures_intrusive::channel::{
ChannelSendError, LocalStateBroadcastChannel, StateId,
};
use futures_test::task::{new_count_waker, panic_waker};
use pin_utils::pin_mut;
macro_rules! gen_state_broadcast_tests {
($mod_name:ident, $channel_type:ident) => {
mod $mod_name {
use super::*;
type ChannelType = $channel_type<i32>;
fn assert_send(channel: &ChannelType, value: i32) {
assert_eq!(Ok(()), channel.send(value));
}
fn assert_receive_value<FutureType, T>(
cx: &mut Context,
receive_fut: &mut core::pin::Pin<&mut FutureType>,
expected: T,
) -> StateId
where
FutureType: Future<Output = Option<(StateId, T)>> + FusedFuture,
T: PartialEq + core::fmt::Debug,
{
let id = match receive_fut.as_mut().poll(cx) {
Poll::Pending => panic!("future is not ready"),
Poll::Ready(None) => panic!("channel is closed"),
Poll::Ready(Some((id, val))) => {
if val != expected {
panic!("Unexpected value {:?}", val);
}
id
}
};
assert!(receive_fut.as_mut().is_terminated());
id
}
fn assert_receive_closed<FutureType, T>(
cx: &mut Context,
receive_fut: &mut core::pin::Pin<&mut FutureType>,
) where
FutureType: Future<Output = Option<(StateId, T)>> + FusedFuture,
T: PartialEq + core::fmt::Debug,
{
match receive_fut.as_mut().poll(cx) {
Poll::Pending => panic!("future is not ready"),
Poll::Ready(None) => {}
Poll::Ready(Some(_)) => panic!("future has a value"),
};
assert!(receive_fut.as_mut().is_terminated());
}
macro_rules! assert_receive {
($cx:ident, $channel:expr, $expected: expr, $state_id: expr) => {{
let receive_fut = $channel.receive($state_id);
pin_mut!(receive_fut);
assert!(!receive_fut.as_mut().is_terminated());
assert_receive_value($cx, &mut receive_fut, $expected)
}};
}
#[test]
fn close_status() {
let channel = ChannelType::new();
assert!(channel.close().is_newly_closed());
assert!(channel.close().is_already_closed());
assert!(channel.close().is_already_closed());
assert!(channel.close().is_already_closed());
}
#[test]
fn send_on_closed_channel() {
let channel = ChannelType::new();
assert!(channel.close().is_newly_closed());
assert_eq!(Err(ChannelSendError(5)), channel.send(5));
}
#[test]
fn close_unblocks_receive() {
let channel = ChannelType::new();
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let fut = channel.receive(Default::default());
pin_mut!(fut);
assert!(fut.as_mut().poll(cx).is_pending());
let fut2 = channel.receive(Default::default());
pin_mut!(fut2);
assert!(fut2.as_mut().poll(cx).is_pending());
assert_eq!(count, 0);
assert!(channel.close().is_newly_closed());
assert_eq!(count, 2);
assert_receive_closed(cx, &mut fut);
assert_receive_closed(cx, &mut fut2);
}
#[test]
fn receive_after_send() {
let channel = ChannelType::new();
let waker = &panic_waker();
let cx = &mut Context::from_waker(&waker);
let mut state_id = StateId::new();
assert_send(&channel, 1);
assert_send(&channel, 2);
assert_receive!(cx, &channel, 2, state_id);
state_id = assert_receive!(cx, &channel, 2, state_id);
assert_send(&channel, 5);
assert_send(&channel, 6);
assert_send(&channel, 7);
assert!(channel.close().is_newly_closed());
assert_receive!(cx, &channel, 7, state_id);
assert_receive!(cx, &channel, 7, state_id);
state_id = assert_receive!(cx, &channel, 7, state_id);
let receive_fut = channel.receive(state_id);
pin_mut!(receive_fut);
assert_receive_closed(cx, &mut receive_fut);
}
#[test]
fn try_receive() {
let channel = ChannelType::new();
let state_id = StateId::new();
assert!(channel.try_receive(state_id).is_none());
assert_send(&channel, 0);
let (state_id, _) = channel.try_receive(state_id).unwrap();
assert!(channel.try_receive(state_id).is_none());
}
#[test]
fn send_unblocks_receive() {
let channel = ChannelType::new();
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let fut = channel.receive(Default::default());
pin_mut!(fut);
assert!(fut.as_mut().poll(cx).is_pending());
assert_eq!(count, 0);
let fut2 = channel.receive(Default::default());
pin_mut!(fut2);
assert!(fut2.as_mut().poll(cx).is_pending());
assert_eq!(count, 0);
assert_send(&channel, 99);
assert_eq!(count, 2);
let next_state_id = assert_receive_value(cx, &mut fut, 99);
assert_eq!(next_state_id, assert_receive_value(cx, &mut fut2, 99));
}
#[test]
fn get_increasing_state_id() {
let channel = ChannelType::new();
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let state_id_0 = StateId::new();
let fut01 = channel.receive(state_id_0);
let fut02 = channel.receive(state_id_0);
pin_mut!(fut01, fut02);
assert!(fut01.as_mut().poll(cx).is_pending());
assert!(fut02.as_mut().poll(cx).is_pending());
assert_eq!(count, 0);
assert_send(&channel, 99);
let state_id_1 = assert_receive_value(cx, &mut fut01, 99);
assert_eq!(state_id_1, assert_receive_value(cx, &mut fut02, 99));
assert!(state_id_1 != state_id_0);
let fut11 = channel.receive(state_id_1);
let fut12 = channel.receive(state_id_1);
pin_mut!(fut11, fut12);
assert!(fut11.as_mut().poll(cx).is_pending());
assert!(fut12.as_mut().poll(cx).is_pending());
assert_eq!(count, 2);
assert_send(&channel, 100);
let state_id_2 = assert_receive_value(cx, &mut fut11, 100);
assert_eq!(state_id_2, assert_receive_value(cx, &mut fut12, 100));
assert!(state_id_2 != state_id_1);
let fut21 = channel.receive(state_id_2);
let fut22 = channel.receive(state_id_2);
pin_mut!(fut21, fut22);
assert!(fut21.as_mut().poll(cx).is_pending());
assert!(fut22.as_mut().poll(cx).is_pending());
assert_eq!(count, 4);
assert_send(&channel, 101);
let state_id_3 = assert_receive_value(cx, &mut fut21, 101);
assert_eq!(state_id_3, assert_receive_value(cx, &mut fut22, 101));
assert!(state_id_3 != state_id_2);
let fut31 = channel.receive(state_id_3);
pin_mut!(fut31);
assert!(fut31.as_mut().poll(cx).is_pending());
assert!(channel.close().is_newly_closed());
assert_receive_closed(cx, &mut fut31);
}
#[test]
fn get_same_element_for_same_state_id() {
let channel = ChannelType::new();
let (waker, _count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let state_id = StateId::new();
assert_send(&channel, 1);
let receive_fut = channel.receive(state_id);
pin_mut!(receive_fut);
let (state_id_21, val) = match receive_fut.as_mut().poll(cx) {
Poll::Ready(Some(res)) => res,
_ => panic!("future is not ready or closed"),
};
assert_eq!(1, val);
assert!(state_id != state_id_21);
let receive_fut_2 = channel.receive(state_id);
pin_mut!(receive_fut_2);
let (state_id_22, val) = match receive_fut_2.as_mut().poll(cx) {
Poll::Ready(Some(res)) => res,
_ => panic!("future is not ready or closed"),
};
assert_eq!(1, val);
assert!(state_id != state_id_22);
assert_eq!(state_id_21, state_id_22);
}
#[test]
fn cancel_receive_mid_wait() {
let channel = ChannelType::new();
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
{
let mut poll1 = Box::pin(channel.receive(Default::default()));
let mut poll2 = Box::pin(channel.receive(Default::default()));
let mut poll3 = Box::pin(channel.receive(Default::default()));
let mut poll4 = Box::pin(channel.receive(Default::default()));
let mut poll5 = Box::pin(channel.receive(Default::default()));
assert!(poll1.as_mut().poll(cx).is_pending());
assert!(poll2.as_mut().poll(cx).is_pending());
assert!(poll3.as_mut().poll(cx).is_pending());
assert!(poll4.as_mut().poll(cx).is_pending());
assert!(poll5.as_mut().poll(cx).is_pending());
assert!(!poll1.is_terminated());
assert!(!poll2.is_terminated());
assert!(!poll3.is_terminated());
assert!(!poll4.is_terminated());
assert!(!poll5.is_terminated());
// Cancel 2 futures. Only the remaining ones should get completed
drop(poll2);
drop(poll4);
assert!(poll1.as_mut().poll(cx).is_pending());
assert!(poll3.as_mut().poll(cx).is_pending());
assert!(poll5.as_mut().poll(cx).is_pending());
assert_send(&channel, 1);
assert_eq!(count, 3);
assert_receive_value(cx, &mut poll1.as_mut(), 1);
assert_receive_value(cx, &mut poll3.as_mut(), 1);
assert_receive_value(cx, &mut poll5.as_mut(), 1);
}
}
#[test]
fn cancel_receive_end_wait() {
let channel = ChannelType::new();
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let poll1 = channel.receive(Default::default());
let poll2 = channel.receive(Default::default());
let poll3 = channel.receive(Default::default());
let poll4 = channel.receive(Default::default());
pin_mut!(poll1);
pin_mut!(poll2);
pin_mut!(poll3);
pin_mut!(poll4);
assert!(poll1.as_mut().poll(cx).is_pending());
assert!(poll2.as_mut().poll(cx).is_pending());
// Start polling some wait handles which get cancelled
// before new ones are attached
{
let poll5 = channel.receive(Default::default());
let poll6 = channel.receive(Default::default());
pin_mut!(poll5);
pin_mut!(poll6);
assert!(poll5.as_mut().poll(cx).is_pending());
assert!(poll6.as_mut().poll(cx).is_pending());
}
assert!(poll3.as_mut().poll(cx).is_pending());
assert!(poll4.as_mut().poll(cx).is_pending());
assert_send(&channel, 0);
assert_send(&channel, 1);
assert_send(&channel, 2);
assert_receive_value(cx, &mut poll1, 2);
assert_receive_value(cx, &mut poll2, 2);
assert_receive_value(cx, &mut poll3, 2);
assert_send(&channel, 3);
assert_receive_value(cx, &mut poll4, 3);
assert_eq!(count, 4);
}
#[test]
fn poll_from_multiple_executors() {
let (waker_1, count_1) = new_count_waker();
let (waker_2, count_2) = new_count_waker();
let channel = ChannelType::new();
let cx_1 = &mut Context::from_waker(&waker_1);
let cx_2 = &mut Context::from_waker(&waker_2);
let fut = channel.receive(Default::default());
pin_mut!(fut);
assert!(fut.as_mut().poll(cx_1).is_pending());
assert!(fut.as_mut().poll(cx_2).is_pending());
assert_send(&channel, 99);
assert_eq!(count_1, 0);
assert_eq!(count_2, 1);
let _next_state_id = assert_receive_value(cx_2, &mut fut, 99);
}
}
};
}
gen_state_broadcast_tests!(
local_state_broadcast_channel_tests,
LocalStateBroadcastChannel
);
#[cfg(feature = "std")]
mod if_std {
use super::*;
use futures_intrusive::channel::{
shared::state_broadcast_channel, StateBroadcastChannel,
};
gen_state_broadcast_tests!(
state_broadcast_channel_tests,
StateBroadcastChannel
);
fn is_send<T: Send>(_: &T) {}
fn is_send_value<T: Send>(_: T) {}
fn is_sync<T: Sync>(_: &T) {}
#[test]
fn channel_futures_are_send() {
let channel = StateBroadcastChannel::<i32>::new();
is_sync(&channel);
{
let state_id = StateId::new();
let recv_fut = channel.receive(state_id);
is_send(&recv_fut);
pin_mut!(recv_fut);
is_send(&recv_fut);
let send_fut = channel.send(3);
is_send(&send_fut);
pin_mut!(send_fut);
is_send(&send_fut);
}
is_send_value(channel);
}
#[test]
fn shared_channel_futures_are_send() {
let (sender, receiver) = state_broadcast_channel::<i32>();
is_sync(&sender);
is_sync(&receiver);
is_send_value(sender.clone());
is_send_value(receiver.clone());
let state_id = StateId::new();
let recv_fut = receiver.receive(state_id);
is_send(&recv_fut);
pin_mut!(recv_fut);
is_send(&recv_fut);
let send_fut = sender.send(3);
is_send(&send_fut);
pin_mut!(send_fut);
is_send(&send_fut);
}
#[test]
fn dropping_shared_channel_senders_closes_channel() {
let (waker, _) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let state_id = StateId::new();
let (sender, receiver) = state_broadcast_channel::<i32>();
let sender2 = sender.clone();
let receiver2 = receiver.clone();
let fut = receiver.receive(state_id);
pin_mut!(fut);
assert!(fut.as_mut().poll(cx).is_pending());
let fut2 = receiver2.receive(state_id);
pin_mut!(fut2);
assert!(fut2.as_mut().poll(cx).is_pending());
drop(sender);
assert!(fut.as_mut().poll(cx).is_pending());
assert!(fut2.as_mut().poll(cx).is_pending());
drop(sender2);
match fut.as_mut().poll(cx) {
Poll::Ready(None) => {}
Poll::Ready(Some(_)) => panic!("Expected no value"),
Poll::Pending => panic!("Expected channel to be closed"),
}
match fut2.as_mut().poll(cx) {
Poll::Ready(None) => {}
Poll::Ready(Some(_)) => panic!("Expected no value"),
Poll::Pending => panic!("Expected channel to be closed"),
}
}
#[test]
fn dropping_shared_channel_receivers_closes_channel() {
let (sender, receiver) = state_broadcast_channel::<i32>();
let sender2 = sender.clone();
let receiver2 = receiver.clone();
drop(receiver);
assert_eq!(Ok(()), sender.send(5));
assert_eq!(Ok(()), sender2.send(7));
drop(receiver2);
assert_eq!(Err(ChannelSendError(5)), sender.send(5));
assert_eq!(Err(ChannelSendError(7)), sender2.send(7));
}
#[test]
fn try_receive() {
let (sender, receiver) = state_broadcast_channel::<i32>();
let state_id = StateId::new();
assert!(receiver.try_receive(state_id).is_none());
sender.send(1).unwrap();
let (state_id, _) = receiver.try_receive(state_id).unwrap();
assert!(receiver.try_receive(state_id).is_none());
}
}
+266
View File
@@ -0,0 +1,266 @@
use core::time::Duration;
use futures::future::{FusedFuture, Future};
use futures::task::Context;
use futures_intrusive::timer::{LocalTimerService, MockClock};
use futures_test::task::{new_count_waker, panic_waker};
use pin_utils::pin_mut;
macro_rules! gen_timer_tests {
($mod_name:ident, $timer_type:ident, $timer_trait_type:ident) => {
mod $mod_name {
use super::*;
use futures_intrusive::timer::$timer_trait_type;
#[test]
fn start_and_expire_timers() {
static TEST_CLOCK: MockClock = MockClock::new();
TEST_CLOCK.set_time(200);
let timer = $timer_type::new(&TEST_CLOCK);
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
assert!(timer.next_expiration().is_none());
let fut = timer.deadline(999);
pin_mut!(fut);
assert!(fut.as_mut().poll(cx).is_pending());
assert_eq!(Some(999), timer.next_expiration());
let fut2 = timer.delay(Duration::from_millis(300));
pin_mut!(fut2);
assert!(fut2.as_mut().poll(cx).is_pending());
assert_eq!(Some(500), timer.next_expiration());
let fut3 = timer.delay(Duration::from_millis(500));
pin_mut!(fut3);
assert!(fut3.as_mut().poll(cx).is_pending());
assert_eq!(Some(500), timer.next_expiration());
TEST_CLOCK.set_time(500);
timer.check_expirations();
assert_eq!(count, 1);
assert!(fut.as_mut().poll(cx).is_pending());
assert!(fut2.as_mut().poll(cx).is_ready());
assert!(fut3.as_mut().poll(cx).is_pending());
assert_eq!(Some(700), timer.next_expiration());
TEST_CLOCK.set_time(699);
timer.check_expirations();
assert_eq!(count, 1);
TEST_CLOCK.set_time(700);
timer.check_expirations();
assert_eq!(count, 2);
assert!(fut.as_mut().poll(cx).is_pending());
assert!(fut3.as_mut().poll(cx).is_ready());
assert_eq!(Some(999), timer.next_expiration());
TEST_CLOCK.set_time(1000);
timer.check_expirations();
assert_eq!(count, 3);
assert!(fut.as_mut().poll(cx).is_ready());
assert_eq!(None, timer.next_expiration());
}
#[test]
fn immediately_ready_timer() {
static TEST_CLOCK: MockClock = MockClock::new();
TEST_CLOCK.set_time(400);
let timer = $timer_type::new(&TEST_CLOCK);
let waker = &panic_waker();
let cx = &mut Context::from_waker(&waker);
let fut = timer.delay(Duration::from_millis(0));
pin_mut!(fut);
assert!(fut.as_mut().poll(cx).is_ready());
for ts in 389..=400 {
let fut2 = timer.deadline(ts);
pin_mut!(fut2);
assert!(fut2.as_mut().poll(cx).is_ready());
}
}
#[test]
fn can_use_timer_as_trait_object() {
static TEST_CLOCK: MockClock = MockClock::new();
TEST_CLOCK.set_time(340);
let timer = $timer_type::new(&TEST_CLOCK);
let (waker, _count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let mut inner = |dyn_timer: &dyn $timer_trait_type| {
let fut = dyn_timer.delay(Duration::from_millis(10));
pin_mut!(fut);
assert!(fut.as_mut().poll(cx).is_pending());
TEST_CLOCK.set_time(350);
timer.check_expirations();
assert!(fut.as_mut().poll(cx).is_ready());
};
inner(&timer);
}
#[test]
fn cancel_mid_wait() {
static TEST_CLOCK: MockClock = MockClock::new();
TEST_CLOCK.set_time(1300);
let timer = $timer_type::new(&TEST_CLOCK);
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
{
// Cancel a wait in between other waits
// In order to arbitrarily drop a non movable future we have to box and pin it
let mut poll1 = Box::pin(timer.deadline(1400));
let mut poll2 = Box::pin(timer.deadline(1500));
let mut poll3 = Box::pin(timer.deadline(1600));
let mut poll4 = Box::pin(timer.deadline(1700));
let mut poll5 = Box::pin(timer.deadline(1800));
assert!(poll1.as_mut().poll(cx).is_pending());
assert!(poll2.as_mut().poll(cx).is_pending());
assert!(poll3.as_mut().poll(cx).is_pending());
assert!(poll4.as_mut().poll(cx).is_pending());
assert!(poll5.as_mut().poll(cx).is_pending());
assert!(!poll1.is_terminated());
assert!(!poll2.is_terminated());
assert!(!poll3.is_terminated());
assert!(!poll4.is_terminated());
assert!(!poll5.is_terminated());
// Cancel 2 futures. Only the remaining ones should get completed
drop(poll2);
drop(poll4);
assert!(poll1.as_mut().poll(cx).is_pending());
assert!(poll3.as_mut().poll(cx).is_pending());
assert!(poll5.as_mut().poll(cx).is_pending());
assert_eq!(count, 0);
TEST_CLOCK.set_time(1800);
timer.check_expirations();
assert!(poll1.as_mut().poll(cx).is_ready());
assert!(poll3.as_mut().poll(cx).is_ready());
assert!(poll5.as_mut().poll(cx).is_ready());
assert!(poll1.is_terminated());
assert!(poll3.is_terminated());
assert!(poll5.is_terminated());
}
assert_eq!(count, 3);
}
#[test]
fn cancel_end_wait() {
static TEST_CLOCK: MockClock = MockClock::new();
TEST_CLOCK.set_time(2300);
let timer = $timer_type::new(&TEST_CLOCK);
let (waker, count) = new_count_waker();
let cx = &mut Context::from_waker(&waker);
let poll1 = timer.deadline(2400);
let poll2 = timer.deadline(2500);
let poll3 = timer.deadline(2600);
let poll4 = timer.deadline(2700);
pin_mut!(poll1);
pin_mut!(poll2);
pin_mut!(poll3);
pin_mut!(poll4);
assert!(poll1.as_mut().poll(cx).is_pending());
assert!(poll2.as_mut().poll(cx).is_pending());
// Start polling some wait handles which get cancelled
// before new ones are attached
{
let poll5 = timer.deadline(2350);
let poll6 = timer.deadline(2650);
pin_mut!(poll5);
pin_mut!(poll6);
assert!(poll5.as_mut().poll(cx).is_pending());
assert!(poll6.as_mut().poll(cx).is_pending());
}
assert!(poll3.as_mut().poll(cx).is_pending());
assert!(poll4.as_mut().poll(cx).is_pending());
TEST_CLOCK.set_time(2700);
timer.check_expirations();
assert!(poll1.as_mut().poll(cx).is_ready());
assert!(poll2.as_mut().poll(cx).is_ready());
assert!(poll3.as_mut().poll(cx).is_ready());
assert!(poll4.as_mut().poll(cx).is_ready());
assert_eq!(count, 4);
}
#[test]
fn poll_from_multiple_executors() {
static TEST_CLOCK: MockClock = MockClock::new();
TEST_CLOCK.set_time(2300);
let timer = $timer_type::new(&TEST_CLOCK);
let (waker_1, count_1) = new_count_waker();
let (waker_2, count_2) = new_count_waker();
let cx_1 = &mut Context::from_waker(&waker_1);
let cx_2 = &mut Context::from_waker(&waker_2);
let fut = timer.deadline(2400);
pin_mut!(fut);
assert!(fut.as_mut().poll(cx_1).is_pending());
assert!(fut.as_mut().poll(cx_2).is_pending());
TEST_CLOCK.set_time(2700);
timer.check_expirations();
assert_eq!(count_1, 0);
assert_eq!(count_2, 1);
assert!(fut.as_mut().poll(cx_2).is_ready());
assert!(fut.as_mut().is_terminated());
}
}
};
}
gen_timer_tests!(local_timer_service_tests, LocalTimerService, LocalTimer);
#[cfg(feature = "std")]
mod if_std {
use super::*;
use futures_intrusive::timer::{Timer, TimerService};
gen_timer_tests!(timer_service_tests, TimerService, Timer);
fn is_send<T: Send>(_: &T) {}
fn is_send_value<T: Send>(_: T) {}
fn is_sync<T: Sync>(_: &T) {}
#[test]
fn timer_futures_are_send() {
static TEST_CLOCK: MockClock = MockClock::new();
TEST_CLOCK.set_time(2300);
let timer = TimerService::new(&TEST_CLOCK);
is_sync(&timer);
{
let deadline = timer.deadline(2400);
is_send(&deadline);
pin_mut!(deadline);
is_send(&deadline);
let delay_fut = timer.delay(Duration::from_millis(1000));
is_send(&delay_fut);
pin_mut!(delay_fut);
is_send(&delay_fut);
}
is_send_value(timer);
}
}