Skip to content

Commit 6721929

Browse files
marshallyaleValuedMammal
authored andcommitted
fix: fix batch ordering issue
Fixes issue #75 Raw client waiting map was using the same channel for every request/response. When items were put back into the channel inside of _reader_thread the waiting receiver in recv would just take the next response without validating it on request id request. This fixes this by using unique channels for each request response inside of the waiting map.
1 parent 372eda9 commit 6721929

File tree

1 file changed

+15
-15
lines changed

1 file changed

+15
-15
lines changed

src/raw_client.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! This module contains the definition of the raw client that wraps the transport method
44
55
use std::borrow::Borrow;
6-
use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
6+
use std::collections::{BTreeMap, HashMap, VecDeque};
77
use std::io::{BufRead, BufReader, Read, Write};
88
use std::mem::drop;
99
use std::net::{TcpStream, ToSocketAddrs};
@@ -539,11 +539,10 @@ impl<S: Read + Write> RawClient<S> {
539539

540540
if let Some(until_message) = until_message {
541541
// If we are trying to start a reader thread but the corresponding sender is
542-
// missing from the map, exit immediately. This can happen with batch calls,
543-
// since the sender is shared for all the individual queries in a call. We
544-
// might have already received a response for that id, but we don't know it
545-
// yet. Exiting here forces the calling code to fallback to the sender-receiver
546-
// method, and it should find a message there waiting for it.
542+
// missing from the map, exit immediately. We might have already received a
543+
// response for that id, but we don't know it yet. Exiting here forces the
544+
// calling code to fallback to the sender-receiver method, and it should find
545+
// a message there waiting for it.
547546
if self.waiting_map.lock()?.get(&until_message).is_none() {
548547
return Err(Error::CouldntLockReader);
549548
}
@@ -762,22 +761,23 @@ impl<T: Read + Write> ElectrumApi for RawClient<T> {
762761
fn batch_call(&self, batch: &Batch) -> Result<Vec<serde_json::Value>, Error> {
763762
let mut raw = Vec::new();
764763

765-
let mut missing_responses = BTreeSet::new();
764+
let mut missing_responses = Vec::new();
766765
let mut answers = BTreeMap::new();
767766

768-
// Add our listener to the map before we send the request, Here we will clone the sender
769-
// for every request id, so that we only have to monitor one receiver.
770-
let (sender, receiver) = channel();
767+
// Add our listener to the map before we send the request
771768

772769
for (method, params) in batch.iter() {
773770
let req = Request::new_id(
774771
self.last_id.fetch_add(1, Ordering::SeqCst),
775772
method,
776773
params.to_vec(),
777774
);
778-
missing_responses.insert(req.id);
775+
// Add distinct channel to each request so when we remove our request id (and sender) from the waiting_map
776+
// we can be sure that the response gets sent to the correct channel in self.recv
777+
let (sender, receiver) = channel();
778+
missing_responses.push((req.id, receiver));
779779

780-
self.waiting_map.lock()?.insert(req.id, sender.clone());
780+
self.waiting_map.lock()?.insert(req.id, sender);
781781

782782
raw.append(&mut serde_json::to_vec(&req)?);
783783
raw.extend_from_slice(b"\n");
@@ -796,16 +796,16 @@ impl<T: Read + Write> ElectrumApi for RawClient<T> {
796796

797797
self.increment_calls();
798798

799-
for req_id in missing_responses.iter() {
800-
match self.recv(&receiver, *req_id) {
799+
for (req_id, receiver) in missing_responses.iter() {
800+
match self.recv(receiver, *req_id) {
801801
Ok(mut resp) => answers.insert(req_id, resp["result"].take()),
802802
Err(e) => {
803803
// In case of error our sender could still be left in the map, depending on where
804804
// the error happened. Just in case, try to remove it here
805805
warn!("got error for req_id {}: {:?}", req_id, e);
806806
warn!("removing all waiting req of this batch");
807807
let mut guard = self.waiting_map.lock()?;
808-
for req_id in missing_responses.iter() {
808+
for (req_id, _) in missing_responses.iter() {
809809
guard.remove(req_id);
810810
}
811811
return Err(e);

0 commit comments

Comments
 (0)