Demonstrate deadlock
This commit is contained in:
commit
2f9440066f
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
/.direnv
|
||||
/.envrc
|
221
Cargo.lock
generated
Normal file
221
Cargo.lock
generated
Normal file
|
@ -0,0 +1,221 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"nanorand",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume-deadlock"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"flume",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
|
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "flume-deadlock"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
flume = "0.11.0"
|
61
flake.lock
Normal file
61
flake.lock
Normal file
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1694529238,
|
||||
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1700612854,
|
||||
"narHash": "sha256-yrQ8osMD+vDLGFX7pcwsY/Qr5PUd6OmDMYJZzZi0+zc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "19cbff58383a4ae384dea4d1d0c823d72b49d614",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
25
flake.nix
Normal file
25
flake.nix
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
description = "async-cpupool";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
in
|
||||
{
|
||||
packages.default = pkgs.hello;
|
||||
|
||||
devShell = with pkgs; mkShell {
|
||||
nativeBuildInputs = [ cargo cargo-outdated clippy rust-analyzer rustc rustfmt ];
|
||||
|
||||
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
|
||||
};
|
||||
});
|
||||
}
|
84
src/main.rs
Normal file
84
src/main.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
mod selector;
|
||||
|
||||
fn main() {
|
||||
let (sender, receiver) = flume::bounded(8);
|
||||
|
||||
let mut signals: Vec<flume::Sender<()>> = Vec::new();
|
||||
|
||||
let mut launch = true;
|
||||
|
||||
for i in 0u64.. {
|
||||
if i % 100000 == 0 {
|
||||
println!("looping");
|
||||
}
|
||||
|
||||
if i % 10000 == 0 {
|
||||
if signals.len() >= 3 || !launch {
|
||||
launch = false;
|
||||
|
||||
if let Some(signal_tx) = signals.pop() {
|
||||
signal_tx.send(()).expect("Sent");
|
||||
drop(signal_tx);
|
||||
}
|
||||
}
|
||||
|
||||
if signals.len() <= 1 || launch {
|
||||
launch = true;
|
||||
|
||||
let (signal_tx, signal) = flume::bounded(1);
|
||||
|
||||
signals.push(signal_tx);
|
||||
|
||||
let rx2 = receiver.clone();
|
||||
std::thread::spawn(move || {
|
||||
println!("Launching thread");
|
||||
while !race(&rx2, &signal) {
|
||||
// spin
|
||||
}
|
||||
println!("Closing thread");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let (dropper_tx, dropper) = flume::bounded(1);
|
||||
|
||||
sender.send(Dropper { sender: dropper_tx }).expect("sent");
|
||||
|
||||
dropper.recv().expect("received");
|
||||
}
|
||||
}
|
||||
|
||||
struct Dropper {
|
||||
sender: flume::Sender<()>,
|
||||
}
|
||||
|
||||
impl Drop for Dropper {
|
||||
fn drop(&mut self) {
|
||||
self.sender.send(()).expect("sent");
|
||||
}
|
||||
}
|
||||
|
||||
/* working selector
|
||||
* fn race(receiver: &flume::Receiver<Dropper>, signal: &flume::Receiver<()>) -> bool {
|
||||
* match selector::blocking_select(receiver.recv_async(), signal.recv_async()) {
|
||||
* selector::Either::Left(res) => {
|
||||
* let out = res.is_err();
|
||||
* drop(res);
|
||||
* out
|
||||
* }
|
||||
* selector::Either::Right(_res) => true,
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
// broken selector
|
||||
fn race(receiver: &flume::Receiver<Dropper>, signal: &flume::Receiver<()>) -> bool {
|
||||
flume::Selector::new()
|
||||
.recv(receiver, |res| {
|
||||
let out = res.is_err();
|
||||
drop(res);
|
||||
out
|
||||
})
|
||||
.recv(signal, |_res| true)
|
||||
.wait()
|
||||
}
|
146
src/selector.rs
Normal file
146
src/selector.rs
Normal file
|
@ -0,0 +1,146 @@
|
|||
use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
task::{Context, Poll, Wake, Waker},
|
||||
};
|
||||
|
||||
struct ThreadWaker {
|
||||
thread: std::thread::Thread,
|
||||
}
|
||||
|
||||
impl Wake for ThreadWaker {
|
||||
fn wake(self: Arc<Self>) {
|
||||
self.thread.unpark();
|
||||
}
|
||||
|
||||
fn wake_by_ref(self: &Arc<Self>) {
|
||||
self.thread.unpark();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) enum Either<L, R> {
|
||||
Left(L),
|
||||
Right(R),
|
||||
}
|
||||
|
||||
struct Select<F1, F2> {
|
||||
left: F1,
|
||||
left_woken: Arc<AtomicBool>,
|
||||
|
||||
right: F2,
|
||||
right_woken: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
struct SelectWaker {
|
||||
inner: Waker,
|
||||
flag: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl Wake for SelectWaker {
|
||||
fn wake_by_ref(self: &Arc<Self>) {
|
||||
self.flag.store(true, Ordering::Release);
|
||||
|
||||
self.inner.wake_by_ref();
|
||||
}
|
||||
|
||||
fn wake(self: Arc<Self>) {
|
||||
self.flag.store(true, Ordering::Release);
|
||||
|
||||
match Arc::try_unwrap(self) {
|
||||
Ok(this) => this.inner.wake(),
|
||||
Err(this) => this.inner.wake_by_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F1, F2> Future for Select<F1, F2>
|
||||
where
|
||||
F1: Future + Unpin,
|
||||
F2: Future + Unpin,
|
||||
{
|
||||
type Output = Either<F1::Output, F2::Output>;
|
||||
|
||||
fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let left_waker = Arc::new(SelectWaker {
|
||||
inner: cx.waker().clone(),
|
||||
flag: self.left_woken.clone(),
|
||||
})
|
||||
.into();
|
||||
|
||||
let mut left_ctx = Context::from_waker(&left_waker);
|
||||
|
||||
if let Poll::Ready(left_out) = Pin::new(&mut self.left).poll(&mut left_ctx) {
|
||||
return Poll::Ready(Either::Left(left_out));
|
||||
}
|
||||
|
||||
let right_waker = Arc::new(SelectWaker {
|
||||
inner: cx.waker().clone(),
|
||||
flag: self.right_woken.clone(),
|
||||
})
|
||||
.into();
|
||||
|
||||
let mut right_ctx = Context::from_waker(&right_waker);
|
||||
|
||||
if let Poll::Ready(right_out) = Pin::new(&mut self.right).poll(&mut right_ctx) {
|
||||
return Poll::Ready(Either::Right(right_out));
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn blocking_select<Left, Right>(
|
||||
left: Left,
|
||||
right: Right,
|
||||
) -> Either<Left::Output, Right::Output>
|
||||
where
|
||||
Left: Future,
|
||||
Right: Future,
|
||||
{
|
||||
block_on(select(left, right))
|
||||
}
|
||||
|
||||
fn block_on<F>(fut: F) -> F::Output
|
||||
where
|
||||
F: Future,
|
||||
{
|
||||
let thread_waker = Arc::new(ThreadWaker {
|
||||
thread: std::thread::current(),
|
||||
})
|
||||
.into();
|
||||
|
||||
let mut ctx = Context::from_waker(&thread_waker);
|
||||
|
||||
let mut fut = std::pin::pin!(fut);
|
||||
|
||||
loop {
|
||||
if let Poll::Ready(out) = fut.as_mut().poll(&mut ctx) {
|
||||
return out;
|
||||
}
|
||||
|
||||
// doesn't race - unpark followed by park will result in park returning immediately
|
||||
std::thread::park();
|
||||
}
|
||||
}
|
||||
|
||||
async fn select<Left, Right>(left: Left, right: Right) -> Either<Left::Output, Right::Output>
|
||||
where
|
||||
Left: Future,
|
||||
Right: Future,
|
||||
{
|
||||
let left = std::pin::pin!(left);
|
||||
let right = std::pin::pin!(right);
|
||||
|
||||
Select {
|
||||
left,
|
||||
left_woken: Arc::new(AtomicBool::new(true)),
|
||||
|
||||
right,
|
||||
right_woken: Arc::new(AtomicBool::new(true)),
|
||||
}
|
||||
.await
|
||||
}
|
Loading…
Reference in a new issue