Skip to content

bug: fix deadlock when having multiple websocket listeners in anchor-client#4250

Open
cryptopapi997 wants to merge 3 commits intosolana-foundation:masterfrom
cryptopapi997:ns/fix-listener-deadlock
Open

bug: fix deadlock when having multiple websocket listeners in anchor-client#4250
cryptopapi997 wants to merge 3 commits intosolana-foundation:masterfrom
cryptopapi997:ns/fix-listener-deadlock

Conversation

@cryptopapi997
Copy link
Contributor

@cryptopapi997 cryptopapi997 commented Feb 17, 2026

When we create a new listener using .on, it does the following:

  • Acquire a write-lock on the websocket listener
  • Check if it's already initialized (and init it if not)
  • Drop the write-lock
  • Acquire a read-lock on the websocket listener
  • Wait for events
  • Drop the read-lock

This means if we want to listen to two events at once, the second listener will hang on aquiring the write-lock forever, as the first event listener will hold its read-lock until it exits. This is unneccesary, since we just need to write if the websocket listener doesn't exist yet, so a read-lock would do. This PR fixes that.

For verification of the existence of this bug, here's a quick MRE also (just run solana-test-validator in a separate terminal first and then run this & observe how it hangs forever):

fn mre() {
    let client = Client::new(Cluster::Localnet, std::rc::Rc::new(MockSigner));
    let program = client.program(Pubkey::new_unique()).unwrap();
    
    let listener1 = program
        .on::<MockEvent>(|_ctx, _event| {
            println!("Listener 1 received event");
        })
        .unwrap();
    std::thread::sleep(std::time::Duration::from_secs(1));
    
    // Will hang forever 
    let listener2 = program
        .on::<MockEvent>(|_ctx, _event| {
            println!("Listener 2 received event");
        })
        .unwrap();
    
    // Should hang forever if the deadlock is still present
    
    listener2.unsubscribe();
    listener1.unsubscribe();    
}

struct MockSigner;

impl solana_signer::Signer for MockSigner {
    fn pubkey(&self) -> Pubkey {
        Pubkey::default()
    }

    fn try_pubkey(&self) -> std::result::Result<Pubkey, SignerError> {
        Ok(Pubkey::default())
    }

    fn sign_message(&self, _message: &[u8]) -> Signature {
        Signature::default()
    }

    fn try_sign_message(
        &self,
        _message: &[u8],
    ) -> std::result::Result<Signature, SignerError> {
        Ok(Signature::default())
    }

    fn is_interactive(&self) -> bool {
        false
    }

}

@vercel
Copy link

vercel bot commented Feb 17, 2026

@cryptopapi997 is attempting to deploy a commit to the Solana Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@cryptopapi997 cryptopapi997 changed the title bug: fix deadlock when having multiple websocket listeners in anchor-client bug: fix deadlock when having multiple websocket listeners in anchor-client Feb 17, 2026
@cryptopapi997
Copy link
Contributor Author

wdyt @jamie-osec

@cryptopapi997 cryptopapi997 requested a review from jamie-osec March 1, 2026 06:50
@cryptopapi997 cryptopapi997 force-pushed the ns/fix-listener-deadlock branch from 20f02c4 to 0a6beb5 Compare March 1, 2026 07:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants