Skip to content

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

@eval-exec

Description

@eval-exec

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions