Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potential deadlock and resource leak when Receiver is dropped in crossbeam_channel's bounded channel #1102

Open
eval-exec opened this issue Apr 16, 2024 · 1 comment · May be fixed by #1121

Comments

@eval-exec
Copy link

eval-exec commented Apr 16, 2024

I have encountered a situation where the code gets stuck indefinitely and the content in the channel's buffer is not dropped when the receiver is dropped.

I'm not sure if this behavior is intended or if it represents a bug in the crossbeam_channel library. The situation is causing a potential deadlock and resource leaks in my code. I would appreciate further investigation by the upstream maintainers to clarify this behavior.

Here is the minimal reproducing code:

use crossbeam_channel::Sender;
use std::io::Error;

pub struct Request<A, R> {
    pub responder: Sender<R>,
    pub arguments: A,
}

impl<A, R> Request<A, R> {
    /// Call the service with the arguments and wait for the response.
    pub fn call(sender: &Sender<Request<A, R>>, arguments: A) -> Option<R> {
        let (responder, response) = crossbeam_channel::bounded(0);
        sender
            .send(Request {
                responder,
                arguments,
            })
            .unwrap();
        response.recv().ok()
    }
}

#[derive(Debug)]
struct Block {
    b: usize,
}

impl Drop for Block {
    fn drop(&mut self) {
        println!("dropping block {}", self.b);
    }
}

type ProcessBlockRequest = Request<Block, Result<(), Error>>;

struct ChainController {
    process_block_sender: Sender<ProcessBlockRequest>,
}

fn chain_service() -> ChainController {
    let (ct, cx) = crossbeam_channel::bounded::<ProcessBlockRequest>(10);

    std::thread::spawn(move || {
        let _cx_clone = cx.clone();
        return;
    });

    ChainController {
        process_block_sender: ct,
    }
}

impl ChainController {
    fn process_block(&self, b: Block) {
        Request::call(&self.process_block_sender, b);
    }
}

fn main() {
    let chain_controller = chain_service();
    chain_controller.process_block(Block { b: 1 });
}

I expect that fn main won't get stuck and I expect to see "dropping block 1" printed to the terminal. However, in reality, the code gets stuck and "dropping block 1" is not printed.

If I comment out let _cx_clone = cx.clone();, then it does not hang.

@taiki-e
Copy link
Member

taiki-e commented Apr 16, 2024

Same issue as rust-lang/rust#107466? If so we just need to port its fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

2 participants