<commit_before>extern crate rmp_serde;
extern crate time;

use std::cmp;
use std::collections::{HashMap, HashSet};
use std::ops::Add;

use commands::{Command, CommandExecutor};
use commands::pool::PoolCommand;
use errors::common::CommonError;
use errors::pool::PoolError;
use self::time::Duration;
use super::{
    MerkleTree,
    RemoteNode,
};
use super::rust_base58::{FromBase58, ToBase58};
use super::types::*;
use services::pool::PoolWorker;

pub const CATCHUP_ROUND_TIMEOUT: i64 = 50;

enum CatchupStepResult {
    Finished,
    Continue,
    FailedAtNode(usize),
}

enum CatchupProgress {
    ShouldBeStarted,
    NotNeeded,
    Finished(MerkleTree),
    InProgress,
}

pub struct CatchupHandler {
    pub f: usize,
    pub ledger_status_same: usize,
    pub merkle_tree: MerkleTree,
    pub target_mt_size: usize,
    pub target_mt_root: Vec<u8>,
    pub new_mt_vote: usize,
    pub nodes: Vec<RemoteNode>,
    pub initiate_cmd_id: i32,
    pub is_refresh: bool,
    pub pending_catchup: Option<CatchUpProcess>,
    pub timeout: time::Tm,
    pub pool_id: i32,
    pub nodes_votes: Vec<Option<(String, usize, Option<Vec<String>>)>>,
    pub pool_name: String,
}

impl Default for CatchupHandler {
    fn default() -> Self {
        CatchupHandler {
            f: 0,
            ledger_status_same: 0,
            merkle_tree: MerkleTree::from_vec(Vec::new()).unwrap(),
            nodes: Vec::new(),
            target_mt_size: 0,
            new_mt_vote: 0,
            target_mt_root: Vec::new(),
            pending_catchup: None,
            initiate_cmd_id: 0,
            is_refresh: false,
            pool_id: 0,
            nodes_votes: Vec::new(),
            timeout: time::now_utc(),
            pool_name: "".to_string(),
        }
    }
}

impl CatchupHandler {
    pub fn process_msg(&mut self, msg: Message, _raw_msg: &String, src_ind: usize) -> Result<Option<MerkleTree>, PoolError> {
        let catchup_status: CatchupProgress = match msg {
            Message::Pong => {
                //sending ledger status
                //TODO not send ledger status directly as response on ping, wait pongs from all nodes?
                let ls: LedgerStatus = LedgerStatus {
                    txnSeqNo: self.nodes.len(),
                    merkleRoot: self.merkle_tree.root_hash().as_slice().to_base58(),
                    ledgerId: 0,
                    ppSeqNo: None,
                    viewNo: None,
                };
                let resp_msg: Message = Message::LedgerStatus(ls);
                self.nodes[src_ind].send_msg(&resp_msg)?;
                CatchupProgress::InProgress
            }
            Message::LedgerStatus(ledger_status) => {
                self.nodes_votes[src_ind] = Some((ledger_status.merkleRoot, ledger_status.txnSeqNo, None));
                self.check_nodes_responses_on_status()?
            }
            Message::ConsistencyProof(cons_proof) => {
                self.nodes_votes[src_ind] = Some((cons_proof.newMerkleRoot, cons_proof.seqNoEnd, Some(cons_proof.hashes)));
                self.check_nodes_responses_on_status()?
            }
            Message::CatchupRep(catchup) => {
                self.process_catchup_rep(catchup, src_ind)?
            }
            _ => {
                warn!("unhandled msg {:?}", msg);
                CatchupProgress::InProgress
            }
        };

        match catchup_status {
            CatchupProgress::Finished(mt) => return Ok(Some(mt)),
            CatchupProgress::NotNeeded => return Ok(Some(self.merkle_tree.clone())),
            CatchupProgress::ShouldBeStarted => self.start_catchup()?,
            CatchupProgress::InProgress => { /* nothing to do */ }
        }
        Ok(None)
    }

    fn check_nodes_responses_on_status(&mut self) -> Result<CatchupProgress, PoolError> {
        if self.pending_catchup.is_some() {
            return Ok(CatchupProgress::InProgress);
        }

        let votes: HashMap<(String, usize, Option<Vec<String>>), usize> = self.nodes_votes.iter().cloned()
            .filter_map(|e| e)
            .fold(HashMap::new(), |mut acc, vote| {
                let cnt = acc.get(&vote).unwrap_or(&0) + 1;
                acc.insert(vote, cnt);
                acc
            });

        if let Some((most_popular_vote, votes_cnt)) = votes.iter().max_by_key(|entry| entry.1) {
            if *votes_cnt == self.nodes.len() - self.f {
                return self._try_to_catch_up(most_popular_vote).or_else(|err| {
                    if PoolWorker::drop_saved_txns(&self.pool_name).is_ok() {
                        self.merkle_tree = PoolWorker::restore_merkle_tree_from_pool_name(&self.pool_name)?;
                        self._try_to_catch_up(most_popular_vote)
                    } else {
                        Err(err)
                    }
                })
            }
        }
        Ok(CatchupProgress::InProgress)
    }

    fn _try_to_catch_up(&mut self, ledger_status: &(String, usize, Option<Vec<String>>)) -> Result<CatchupProgress, PoolError> {
        let &(ref target_mt_root, target_mt_size, ref hashes) = ledger_status;
        let cur_mt_size = self.merkle_tree.count();
        let cur_mt_hash = self.merkle_tree.root_hash().to_base58();
        if target_mt_size == cur_mt_size {
            if cur_mt_hash.eq(target_mt_root) {
                return Ok(CatchupProgress::NotNeeded);
            } else {
                return Err(PoolError::CommonError(CommonError::InvalidState(
                    "Ledger merkle tree doesn't acceptable for current tree.".to_string())));
            }
        } else if target_mt_size > cur_mt_size {
            self.target_mt_size = target_mt_size;
            self.target_mt_root = target_mt_root.from_base58().map_err(|_|
                CommonError::InvalidStructure(
                    "Can't parse target MerkleTree hash from nodes responses".to_string()))?;
            match hashes {
                &None => (),
                &Some(ref hashes) => {
                    match CatchupHandler::check_cons_proofs(&self.merkle_tree, hashes, &self.target_mt_root, self.target_mt_size) {
                        Ok(_) => (),
                        Err(err) => {
                            return Err(PoolError::from(err));
                        }
                    }
                }
            };
            return Ok(CatchupProgress::ShouldBeStarted);
        } else {
            return Err(PoolError::CommonError(CommonError::InvalidState(
                "Local merkle tree greater than mt from ledger".to_string())));
        }
    }

    pub fn reset_nodes_votes(&mut self) {
        self.nodes_votes.clear();
        self.nodes_votes.resize(self.nodes.len(), None);
    }

    pub fn start_catchup(&mut self) -> Result<(), PoolError> {
        trace!("start_catchup");
        if self.pending_catchup.is_some() {
            return Err(PoolError::CommonError(
                CommonError::InvalidState(
                    "CatchUp already started for the pool".to_string())));
        }

        let active_node_cnt = self.nodes.iter().filter(|node| !node.is_blacklisted).count();

        if active_node_cnt == 0 {
            // TODO FIXME
            return Err(PoolError::Terminate);
        }

        let txns_cnt_in_cur_mt = self.merkle_tree.count();
        let cnt_to_catchup = self.target_mt_size - txns_cnt_in_cur_mt;
        if cnt_to_catchup <= 0 {
            return Err(PoolError::CommonError(CommonError::InvalidState(
                "Nothing to CatchUp, but started".to_string())));
        }

        self.pending_catchup = Some(CatchUpProcess {
            merkle_tree: self.merkle_tree.clone(),
            pending_reps: Vec::new(),
            resp_not_received_node_idx: HashSet::new(),
        });
        self.timeout = time::now_utc().add(Duration::seconds(CATCHUP_ROUND_TIMEOUT));

        let portion = (cnt_to_catchup + active_node_cnt - 1) / active_node_cnt; //TODO check standard round up div
        let mut catchup_req = CatchupReq {
            ledgerId: 0,
            seqNoStart: txns_cnt_in_cur_mt + 1,
            seqNoEnd: txns_cnt_in_cur_mt + 1 + portion - 1,
            catchupTill: self.target_mt_size,
        };
        for idx in 0..self.nodes.len() {
            let node = &self.nodes[idx];
            //TODO do not perform duplicate requests, update resp_not_received_node_idx
            if node.is_blacklisted {
                continue;
            }
            self.pending_catchup.as_mut().map(|pc| {
                pc.resp_not_received_node_idx.insert(idx);
            });
            node.send_msg(&Message::CatchupReq(catchup_req.clone()))?;
            catchup_req.seqNoStart += portion;
            catchup_req.seqNoEnd = cmp::min(catchup_req.seqNoStart + portion - 1,
                                            catchup_req.catchupTill);

            if catchup_req.seqNoStart > catchup_req.seqNoEnd {
                // We don't have more portions to ask
                break;
            }
        }
        Ok(())
    }

    fn process_catchup_rep(&mut self, catchup: CatchupRep, node_idx: usize) -> Result<CatchupProgress, PoolError> {
        trace!("append {:?}", catchup);
        let catchup_finished = self.catchup_step(catchup, node_idx)?;
        match catchup_finished {
            CatchupStepResult::Finished => return Ok(CatchupProgress::Finished(self.finish_catchup()?)),
            CatchupStepResult::Continue => { /* nothing to do */ }
            CatchupStepResult::FailedAtNode(failed_node_idx) => {
                warn!("Fail to continue catch-up by response from node with idx {}. Node will be blacklisted and catchup will be restarted", failed_node_idx);
                self.nodes[failed_node_idx].is_blacklisted = true;
                self.pending_catchup = None;
                // TODO may be send ledger status again and re-obtain target MerkleTree params
                self.start_catchup()?
            }
        }
        Ok(CatchupProgress::InProgress)
    }

    fn catchup_step(&mut self, catchup: CatchupRep, node_idx: usize) -> Result<CatchupStepResult, PoolError> {
        let process = self.pending_catchup.as_mut()
            .ok_or(CommonError::InvalidState("Process non-existing CatchUp".to_string()))?;
        process.pending_reps.push((catchup, node_idx));
        process.resp_not_received_node_idx.remove(&node_idx);

        while !process.pending_reps.is_empty() {
            let index = process.pending_reps.get_min_index()?;
            {
                let &mut (ref mut first_resp, node_idx) = process.pending_reps.get_mut(index)
                    .ok_or(CommonError::InvalidStructure(format!("Element not Found")))?;
                if first_resp.min_tx()? - 1 != process.merkle_tree.count() { break; }

                let mut temp_mt = process.merkle_tree.clone();
                let mut vec_to_dump: Vec<Vec<u8>> = vec![];
                while !first_resp.txns.is_empty() {
                    let key = first_resp.min_tx()?.to_string();
                    let new_gen_tx = first_resp.txns.remove(&key).unwrap();
                    if let Ok(new_get_txn_bytes) = rmp_serde::to_vec_named(&new_gen_tx) {
                        temp_mt.append(new_get_txn_bytes.clone())?;
                        vec_to_dump.push(new_get_txn_bytes);
                    } else {
                        return Ok(CatchupStepResult::FailedAtNode(node_idx));
                    }
                }

                if CatchupHandler::check_cons_proofs(&temp_mt, &first_resp.consProof, &self.target_mt_root, self.target_mt_size)
                    .map_err(map_err_err!()).is_err() {
                    return Ok(CatchupStepResult::FailedAtNode(node_idx));
                }

                PoolWorker::dump_new_txns(&self.pool_name, &vec_to_dump)?;

                process.merkle_tree = temp_mt;
            }
            process.pending_reps.remove(index);
        }
        trace!("updated mt hash {}, tree {:?}", process.merkle_tree.root_hash().as_slice().to_base58(), process.merkle_tree);
        if &process.merkle_tree.count() == &self.target_mt_size {
            if process.merkle_tree.root_hash().ne(&self.target_mt_root) {
                return Err(PoolError::CommonError(CommonError::InvalidState(
                    "CatchUp failed: all transactions added, proofs checked, but root hash differ with target".to_string())));
            }
            return Ok(CatchupStepResult::Finished);
        } else {
            return Ok(CatchupStepResult::Continue);
        }
    }

    fn check_cons_proofs(mt: &MerkleTree, cons_proofs: &Vec<String>, target_mt_root: &Vec<u8>, target_mt_size: usize) -> Result<(), CommonError> {
        let mut bytes_proofs: Vec<Vec<u8>> = Vec::new();
        for cons_proof in cons_proofs {
            let cons_proof: &String = cons_proof;
            bytes_proofs.push(cons_proof.from_base58().map_err(|err|
                CommonError::InvalidStructure(
                    format!("Can't decode node consistency proof: {}", err)))?)
        }
        if !mt.consistency_proof(target_mt_root, target_mt_size, &bytes_proofs)? {
            return Err(CommonError::InvalidStructure("Consistency proof verification failed".to_string()));
        }
        Ok(())
    }

    fn finish_catchup(&mut self) -> Result<MerkleTree, PoolError> {
        Ok(self.pending_catchup.take().
            ok_or(CommonError::InvalidState("Try to finish non-existing CatchUp".to_string()))?
            .merkle_tree)
    }

    pub fn flush_requests(&mut self, status: Result<(), PoolError>) -> Result<(), PoolError> {
        if self.initiate_cmd_id == -1 {
            return Ok(());
        }
        let cmd = if self.is_refresh {
            PoolCommand::RefreshAck(self.initiate_cmd_id, status)
        } else {
            PoolCommand::OpenAck(self.initiate_cmd_id, self.pool_id, status)
        };
        self.initiate_cmd_id = -1;
        CommandExecutor::instance()
            .send(Command::Pool(cmd))
            .map_err(|err|
                PoolError::CommonError(
                    CommonError::InvalidState(format!("Can't send ACK cmd: {:?}", err))))
    }

    pub fn get_upcoming_timeout(&self) -> Option<time::Tm> {
        Some(self.timeout)
    }

    pub fn process_timeout(&mut self) -> Result<(), PoolError> {
        let pc = if let Some(pc) = self.pending_catchup.take() { pc } else {
            return Err(PoolError::Timeout);
        };
        warn!("Fail to continue catch-up response(s) not received from nodes with idx {:?}. Node will be blacklisted and catchup will be restarted", pc.resp_not_received_node_idx);
        pc.resp_not_received_node_idx.iter()
            .for_each(|idx| self.nodes[*idx].is_blacklisted = true);
        // TODO may be send ledger status again and re-obtain target MerkleTree params
        self.start_catchup()
    }
}
<commit_msg>Fix error for no hashes in consensus with cath up needed. Add comment about cloning of votes<commit_after>extern crate rmp_serde;
extern crate time;

use std::cmp;
use std::collections::{HashMap, HashSet};
use std::ops::Add;

use commands::{Command, CommandExecutor};
use commands::pool::PoolCommand;
use errors::common::CommonError;
use errors::pool::PoolError;
use self::time::Duration;
use super::{
    MerkleTree,
    RemoteNode,
};
use super::rust_base58::{FromBase58, ToBase58};
use super::types::*;
use services::pool::PoolWorker;

pub const CATCHUP_ROUND_TIMEOUT: i64 = 50;

enum CatchupStepResult {
    Finished,
    Continue,
    FailedAtNode(usize),
}

enum CatchupProgress {
    ShouldBeStarted,
    NotNeeded,
    Finished(MerkleTree),
    InProgress,
}

pub struct CatchupHandler {
    pub f: usize,
    pub ledger_status_same: usize,
    pub merkle_tree: MerkleTree,
    pub target_mt_size: usize,
    pub target_mt_root: Vec<u8>,
    pub new_mt_vote: usize,
    pub nodes: Vec<RemoteNode>,
    pub initiate_cmd_id: i32,
    pub is_refresh: bool,
    pub pending_catchup: Option<CatchUpProcess>,
    pub timeout: time::Tm,
    pub pool_id: i32,
    pub nodes_votes: Vec<Option<(String, usize, Option<Vec<String>>)>>,
    pub pool_name: String,
}

impl Default for CatchupHandler {
    fn default() -> Self {
        CatchupHandler {
            f: 0,
            ledger_status_same: 0,
            merkle_tree: MerkleTree::from_vec(Vec::new()).unwrap(),
            nodes: Vec::new(),
            target_mt_size: 0,
            new_mt_vote: 0,
            target_mt_root: Vec::new(),
            pending_catchup: None,
            initiate_cmd_id: 0,
            is_refresh: false,
            pool_id: 0,
            nodes_votes: Vec::new(),
            timeout: time::now_utc(),
            pool_name: "".to_string(),
        }
    }
}

impl CatchupHandler {
    pub fn process_msg(&mut self, msg: Message, _raw_msg: &String, src_ind: usize) -> Result<Option<MerkleTree>, PoolError> {
        let catchup_status: CatchupProgress = match msg {
            Message::Pong => {
                //sending ledger status
                //TODO not send ledger status directly as response on ping, wait pongs from all nodes?
                let ls: LedgerStatus = LedgerStatus {
                    txnSeqNo: self.nodes.len(),
                    merkleRoot: self.merkle_tree.root_hash().as_slice().to_base58(),
                    ledgerId: 0,
                    ppSeqNo: None,
                    viewNo: None,
                };
                let resp_msg: Message = Message::LedgerStatus(ls);
                self.nodes[src_ind].send_msg(&resp_msg)?;
                CatchupProgress::InProgress
            }
            Message::LedgerStatus(ledger_status) => {
                self.nodes_votes[src_ind] = Some((ledger_status.merkleRoot, ledger_status.txnSeqNo, None));
                self.check_nodes_responses_on_status()?
            }
            Message::ConsistencyProof(cons_proof) => {
                self.nodes_votes[src_ind] = Some((cons_proof.newMerkleRoot, cons_proof.seqNoEnd, Some(cons_proof.hashes)));
                self.check_nodes_responses_on_status()?
            }
            Message::CatchupRep(catchup) => {
                self.process_catchup_rep(catchup, src_ind)?
            }
            _ => {
                warn!("unhandled msg {:?}", msg);
                CatchupProgress::InProgress
            }
        };

        match catchup_status {
            CatchupProgress::Finished(mt) => return Ok(Some(mt)),
            CatchupProgress::NotNeeded => return Ok(Some(self.merkle_tree.clone())),
            CatchupProgress::ShouldBeStarted => self.start_catchup()?,
            CatchupProgress::InProgress => { /* nothing to do */ }
        }
        Ok(None)
    }

    fn check_nodes_responses_on_status(&mut self) -> Result<CatchupProgress, PoolError> {
        if self.pending_catchup.is_some() {
            return Ok(CatchupProgress::InProgress);
        }

        let votes: HashMap<(String, usize, Option<Vec<String>>), usize> = self.nodes_votes.iter().cloned()
            .filter_map(|e| e)
            .fold(HashMap::new(), |mut acc, vote| {
                //WARN: It can be processed without excessive cloning two steps before, but then we will face double write borrowing.
                //WARN: This is the most readable variant. However, not the most effective.
                let cnt = acc.get(&vote).unwrap_or(&0) + 1;
                acc.insert(vote, cnt);
                acc
            });

        if let Some((most_popular_vote, votes_cnt)) = votes.iter().max_by_key(|entry| entry.1) {
            if *votes_cnt == self.nodes.len() - self.f {
                return self._try_to_catch_up(most_popular_vote).or_else(|err| {
                    if PoolWorker::drop_saved_txns(&self.pool_name).is_ok() {
                        self.merkle_tree = PoolWorker::restore_merkle_tree_from_pool_name(&self.pool_name)?;
                        self._try_to_catch_up(most_popular_vote)
                    } else {
                        Err(err)
                    }
                })
            }
        }
        Ok(CatchupProgress::InProgress)
    }

    fn _try_to_catch_up(&mut self, ledger_status: &(String, usize, Option<Vec<String>>)) -> Result<CatchupProgress, PoolError> {
        let &(ref target_mt_root, target_mt_size, ref hashes) = ledger_status;
        let cur_mt_size = self.merkle_tree.count();
        let cur_mt_hash = self.merkle_tree.root_hash().to_base58();
        if target_mt_size == cur_mt_size {
            if cur_mt_hash.eq(target_mt_root) {
                return Ok(CatchupProgress::NotNeeded);
            } else {
                return Err(PoolError::CommonError(CommonError::InvalidState(
                    "Ledger merkle tree doesn't acceptable for current tree.".to_string())));
            }
        } else if target_mt_size > cur_mt_size {
            self.target_mt_size = target_mt_size;
            self.target_mt_root = target_mt_root.from_base58().map_err(|_|
                CommonError::InvalidStructure(
                    "Can't parse target MerkleTree hash from nodes responses".to_string()))?;
            match hashes {
                &None => {return Err(PoolError::from(CommonError::InvalidState("Empty consistency proof but catch up needed".to_string())));},
                &Some(ref hashes) => {
                    match CatchupHandler::check_cons_proofs(&self.merkle_tree, hashes, &self.target_mt_root, self.target_mt_size) {
                        Ok(_) => (),
                        Err(err) => {
                            return Err(PoolError::from(err));
                        }
                    }
                }
            };
            return Ok(CatchupProgress::ShouldBeStarted);
        } else {
            return Err(PoolError::CommonError(CommonError::InvalidState(
                "Local merkle tree greater than mt from ledger".to_string())));
        }
    }

    pub fn reset_nodes_votes(&mut self) {
        self.nodes_votes.clear();
        self.nodes_votes.resize(self.nodes.len(), None);
    }

    pub fn start_catchup(&mut self) -> Result<(), PoolError> {
        trace!("start_catchup");
        if self.pending_catchup.is_some() {
            return Err(PoolError::CommonError(
                CommonError::InvalidState(
                    "CatchUp already started for the pool".to_string())));
        }

        let active_node_cnt = self.nodes.iter().filter(|node| !node.is_blacklisted).count();

        if active_node_cnt == 0 {
            // TODO FIXME
            return Err(PoolError::Terminate);
        }

        let txns_cnt_in_cur_mt = self.merkle_tree.count();
        let cnt_to_catchup = self.target_mt_size - txns_cnt_in_cur_mt;
        if cnt_to_catchup <= 0 {
            return Err(PoolError::CommonError(CommonError::InvalidState(
                "Nothing to CatchUp, but started".to_string())));
        }

        self.pending_catchup = Some(CatchUpProcess {
            merkle_tree: self.merkle_tree.clone(),
            pending_reps: Vec::new(),
            resp_not_received_node_idx: HashSet::new(),
        });
        self.timeout = time::now_utc().add(Duration::seconds(CATCHUP_ROUND_TIMEOUT));

        let portion = (cnt_to_catchup + active_node_cnt - 1) / active_node_cnt; //TODO check standard round up div
        let mut catchup_req = CatchupReq {
            ledgerId: 0,
            seqNoStart: txns_cnt_in_cur_mt + 1,
            seqNoEnd: txns_cnt_in_cur_mt + 1 + portion - 1,
            catchupTill: self.target_mt_size,
        };
        for idx in 0..self.nodes.len() {
            let node = &self.nodes[idx];
            //TODO do not perform duplicate requests, update resp_not_received_node_idx
            if node.is_blacklisted {
                continue;
            }
            self.pending_catchup.as_mut().map(|pc| {
                pc.resp_not_received_node_idx.insert(idx);
            });
            node.send_msg(&Message::CatchupReq(catchup_req.clone()))?;
            catchup_req.seqNoStart += portion;
            catchup_req.seqNoEnd = cmp::min(catchup_req.seqNoStart + portion - 1,
                                            catchup_req.catchupTill);

            if catchup_req.seqNoStart > catchup_req.seqNoEnd {
                // We don't have more portions to ask
                break;
            }
        }
        Ok(())
    }

    fn process_catchup_rep(&mut self, catchup: CatchupRep, node_idx: usize) -> Result<CatchupProgress, PoolError> {
        trace!("append {:?}", catchup);
        let catchup_finished = self.catchup_step(catchup, node_idx)?;
        match catchup_finished {
            CatchupStepResult::Finished => return Ok(CatchupProgress::Finished(self.finish_catchup()?)),
            CatchupStepResult::Continue => { /* nothing to do */ }
            CatchupStepResult::FailedAtNode(failed_node_idx) => {
                warn!("Fail to continue catch-up by response from node with idx {}. Node will be blacklisted and catchup will be restarted", failed_node_idx);
                self.nodes[failed_node_idx].is_blacklisted = true;
                self.pending_catchup = None;
                // TODO may be send ledger status again and re-obtain target MerkleTree params
                self.start_catchup()?
            }
        }
        Ok(CatchupProgress::InProgress)
    }

    fn catchup_step(&mut self, catchup: CatchupRep, node_idx: usize) -> Result<CatchupStepResult, PoolError> {
        let process = self.pending_catchup.as_mut()
            .ok_or(CommonError::InvalidState("Process non-existing CatchUp".to_string()))?;
        process.pending_reps.push((catchup, node_idx));
        process.resp_not_received_node_idx.remove(&node_idx);

        while !process.pending_reps.is_empty() {
            let index = process.pending_reps.get_min_index()?;
            {
                let &mut (ref mut first_resp, node_idx) = process.pending_reps.get_mut(index)
                    .ok_or(CommonError::InvalidStructure(format!("Element not Found")))?;
                if first_resp.min_tx()? - 1 != process.merkle_tree.count() { break; }

                let mut temp_mt = process.merkle_tree.clone();
                let mut vec_to_dump: Vec<Vec<u8>> = vec![];
                while !first_resp.txns.is_empty() {
                    let key = first_resp.min_tx()?.to_string();
                    let new_gen_tx = first_resp.txns.remove(&key).unwrap();
                    if let Ok(new_get_txn_bytes) = rmp_serde::to_vec_named(&new_gen_tx) {
                        temp_mt.append(new_get_txn_bytes.clone())?;
                        vec_to_dump.push(new_get_txn_bytes);
                    } else {
                        return Ok(CatchupStepResult::FailedAtNode(node_idx));
                    }
                }

                if CatchupHandler::check_cons_proofs(&temp_mt, &first_resp.consProof, &self.target_mt_root, self.target_mt_size)
                    .map_err(map_err_err!()).is_err() {
                    return Ok(CatchupStepResult::FailedAtNode(node_idx));
                }

                PoolWorker::dump_new_txns(&self.pool_name, &vec_to_dump)?;

                process.merkle_tree = temp_mt;
            }
            process.pending_reps.remove(index);
        }
        trace!("updated mt hash {}, tree {:?}", process.merkle_tree.root_hash().as_slice().to_base58(), process.merkle_tree);
        if &process.merkle_tree.count() == &self.target_mt_size {
            if process.merkle_tree.root_hash().ne(&self.target_mt_root) {
                return Err(PoolError::CommonError(CommonError::InvalidState(
                    "CatchUp failed: all transactions added, proofs checked, but root hash differ with target".to_string())));
            }
            return Ok(CatchupStepResult::Finished);
        } else {
            return Ok(CatchupStepResult::Continue);
        }
    }

    fn check_cons_proofs(mt: &MerkleTree, cons_proofs: &Vec<String>, target_mt_root: &Vec<u8>, target_mt_size: usize) -> Result<(), CommonError> {
        let mut bytes_proofs: Vec<Vec<u8>> = Vec::new();
        for cons_proof in cons_proofs {
            let cons_proof: &String = cons_proof;
            bytes_proofs.push(cons_proof.from_base58().map_err(|err|
                CommonError::InvalidStructure(
                    format!("Can't decode node consistency proof: {}", err)))?)
        }
        if !mt.consistency_proof(target_mt_root, target_mt_size, &bytes_proofs)? {
            return Err(CommonError::InvalidStructure("Consistency proof verification failed".to_string()));
        }
        Ok(())
    }

    fn finish_catchup(&mut self) -> Result<MerkleTree, PoolError> {
        Ok(self.pending_catchup.take().
            ok_or(CommonError::InvalidState("Try to finish non-existing CatchUp".to_string()))?
            .merkle_tree)
    }

    pub fn flush_requests(&mut self, status: Result<(), PoolError>) -> Result<(), PoolError> {
        if self.initiate_cmd_id == -1 {
            return Ok(());
        }
        let cmd = if self.is_refresh {
            PoolCommand::RefreshAck(self.initiate_cmd_id, status)
        } else {
            PoolCommand::OpenAck(self.initiate_cmd_id, self.pool_id, status)
        };
        self.initiate_cmd_id = -1;
        CommandExecutor::instance()
            .send(Command::Pool(cmd))
            .map_err(|err|
                PoolError::CommonError(
                    CommonError::InvalidState(format!("Can't send ACK cmd: {:?}", err))))
    }

    pub fn get_upcoming_timeout(&self) -> Option<time::Tm> {
        Some(self.timeout)
    }

    pub fn process_timeout(&mut self) -> Result<(), PoolError> {
        let pc = if let Some(pc) = self.pending_catchup.take() { pc } else {
            return Err(PoolError::Timeout);
        };
        warn!("Fail to continue catch-up response(s) not received from nodes with idx {:?}. Node will be blacklisted and catchup will be restarted", pc.resp_not_received_node_idx);
        pc.resp_not_received_node_idx.iter()
            .for_each(|idx| self.nodes[*idx].is_blacklisted = true);
        // TODO may be send ledger status again and re-obtain target MerkleTree params
        self.start_catchup()
    }
}
<|endoftext|>


<commit_before>//! KCP adapter for benchmarking.
//!
//! KCP is just a protocol layer, and does not establish connections. Connection
//! establishment is not benchmarked, so this adapter rolls its own.
//!
//! A UDP socket for a server listens for messages with a new connection id,
//! what KCP calls a "conv". The server stream of connections then ends (only
//! one connection per server; that's all the benchmark needs).
//!
//! The server's socket sends the conv back to the client to establish
//! connection.

use crate::{tcp, Result, *};
use async_std::net::*;
use bincode::*;
use futures::{
    channel::mpsc,
    prelude::*,
    stream::{Fuse, FusedStream, LocalBoxStream, StreamExt},
};
use std::sync::Arc;

use std::ffi::c_void;
use std::os::raw::c_int;
use std::os::unix::io::{AsRawFd, FromRawFd};

use std::pin::Pin;

use std::task::{Context, Poll};
use std::time::*;

#[allow(warnings)]
mod kcp {
    include!(concat!(env!("OUT_DIR"), "/kcp.rs"));
}

pub struct KcpServer {
    peers: Fuse<LocalBoxStream<'static, Result<KcpConnection>>>,
}

impl Server<KcpConnection> for KcpServer {}

impl KcpServer {
    pub async fn bind(
        address: impl ToSocketAddrs + Clone + 'static,
    ) -> Result<Self> {
        let tcp = tcp::TcpServer::bind(address.clone()).await?;

        let peers = tcp.then(move |tcp_connection| {
            let address = address.clone();
            async move {
                let mut tcp_connection = tcp_connection?;

                let udp = UdpSocket::bind(address.clone()).await?;
                let port = udp.local_addr()?.port();

                tcp_connection
                    .send(SendCmd {
                        data: serialize(&port)?,
                        delivery_mode: DeliveryMode::ReliableOrdered(StreamId(
                            0,
                        )),
                        ..SendCmd::default()
                    })
                    .await?;

                let client_port = tcp_connection
                    .next()
                    .await
                    .expect("Confirmation of port")?;
                let client_port: u16 =
                    deserialize(client_port.data.as_slice())?;

                let mut client_addr = tcp_connection.peer_addr();
                client_addr.set_port(client_port);

                Ok(KcpConnection::from_socket(tcp_connection, udp, client_addr)
                    .await)
            }
        });

        Ok(Self {
            peers: peers.boxed_local().fuse(),
        })
    }
}

impl Stream for KcpServer {
    type Item = Result<KcpConnection>;
    fn poll_next(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.peers).poll_next(ctx)
    }
}

impl FusedStream for KcpServer {
    fn is_terminated(&self) -> bool {
        self.peers.is_terminated()
    }
}

pub struct KcpConnection {
    #[allow(unused)]
    tcp_connection: tcp::TcpConnection,
    receiver: mpsc::Receiver<Datagram>,
    sender:
        Pin<Box<dyn Sink<SendCmd, Error = Box<dyn std::error::Error>> + Unpin>>,
}

impl KcpConnection {
    pub async fn connect(mut server: SocketAddr) -> Result<Self> {
        let mut tcp_connection = tcp::TcpConnection::connect(server).await?;

        let port =
            tcp_connection.next().await.expect("udp port from server")?;
        let port: u16 = deserialize(port.data.as_slice())?;
        let _udp_addr = server.set_port(port);

        let udp = UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0))
            .await?;
        let our_port = udp.local_addr()?.port();
        tcp_connection
            .send(SendCmd {
                data: serialize(&our_port)?,
                delivery_mode: DeliveryMode::ReliableOrdered(StreamId(0)),
                ..SendCmd::default()
            })
            .await?;

        Ok(Self::from_socket(tcp_connection, udp, server).await)
    }

    async fn from_socket(
        tcp_connection: tcp::TcpConnection,
        socket: UdpSocket,
        peer: SocketAddr,
    ) -> Self {
        let (command_sink, command_stream) = mpsc::channel(100);
        let (datagram_sink, datagram_stream) = mpsc::channel(100);

        async_std::task::spawn(
            Self::driver(socket, peer, command_stream, datagram_sink).map(drop),
        );

        Self {
            tcp_connection,
            receiver: datagram_stream,
            sender: Pin::new(Box::new(command_sink.sink_err_into())),
        }
    }

    async fn driver(
        socket: UdpSocket,
        peer: SocketAddr,
        mut command_stream: mpsc::Receiver<SendCmd>,
        datagram_sink: mpsc::Sender<Datagram>,
    ) -> Result<()> {
        socket.connect(peer).await?;

        let output_callback =
            |buf: *const i8, len: i32, _cb: *mut kcp::ikcpcb| -> c_int {
                let data: &[u8] = unsafe {
                    std::slice::from_raw_parts(buf as *const u8, len as usize)
                };
                match async_std::task::block_on(socket.send(data)) {
                    Err(_) => -1,
                    Ok(v) => v as i32,
                }
            };
        let cb = {
            let (callback, state) =
                unsafe { wrap_output_callback(&output_callback) };
            let cb = unsafe {
                kcp::ikcp_create(/*conv=*/ 0, state)
            };
            unsafe { kcp::ikcp_setoutput(cb, Some(callback)) }
            Cb(cb)
        };

        struct CbDropper(Cb);

        impl Drop for CbDropper {
            fn drop(&mut self) {
                let CbDropper(Cb(ptr)) = *self;
                unsafe { kcp::ikcp_release(ptr) }
            }
        }

        let _dropper = CbDropper(cb);

        let mut servicer = KcpServicer {
            epoch: Instant::now(),
            datagram_sink,
            cb,
            sequence_number: 0,
        };

        let mut buffer = [0u8; 65535];
        let mut service_ticker = ticker(1000).fuse();
        loop {
            if command_stream.is_terminated() {
                return Ok(());
            }

            futures::select! {
                read_result = socket.recv(&mut buffer).fuse() => {
                    {
                        let len = read_result?;
                        let data = buffer.as_ptr() as *const i8;
                        let code = unsafe {
                            kcp::ikcp_input(cb.0, data, len as i64)
                        };
                        if code < 0 {
                            panic!("kcp panic; input error: {:?}", code);
                        }
                    }

                    servicer.service().await;
                }
                send_cmd = command_stream.select_next_some() => unsafe {
                /*            match send_cmd.delivery_mode {
                                DeliveryMode::ReliableOrdered(StreamId(0)) => {},
                                _ => panic!("KCP only supports a single reliable channel"),
                            };*/

                            {
                                let data = send_cmd.data.as_ptr() as *const i8;
                                let code = kcp::ikcp_send(
                                   cb.0,
                                   data,
                                   send_cmd.data.len() as i32
                                );
                                if code < 0 {
                                    panic!("kcp input failed: {:?}", code);
                                }
                            }

                            servicer.service().await;

                            kcp::ikcp_flush(cb.0);
                },
                _ = service_ticker.select_next_some() => servicer.service().await,
            }
        }
    }
}

#[derive(Copy, Clone)]
struct Cb(*mut kcp::ikcpcb);

unsafe impl Send for Cb {}

struct KcpServicer {
    epoch: Instant,
    datagram_sink: mpsc::Sender<Datagram>,
    cb: Cb,
    sequence_number: u32,
}

impl KcpServicer {
    fn current_time_ms(&self) -> u32 {
        Instant::now().duration_since(self.epoch).as_millis() as u32
    }

    async fn service(&mut self) {
        unsafe { kcp::ikcp_update(self.cb.0, self.current_time_ms()) };

        let mut buffer = [0; 65535];
        #[allow(unused_assignments)]
        let mut len = i32::default();

        while {
            let buffer_ptr = buffer.as_mut_ptr() as *mut i8;
            len = unsafe {
                kcp::ikcp_recv(self.cb.0, buffer_ptr, buffer.len() as i32)
            };
            len > 0
        } {
            let _ = self
                .datagram_sink
                .send(Datagram {
                    data: buffer[0..(len as usize)].to_vec(),
                    stream_position: Some(StreamPosition {
                        stream_id: StreamId(0),
                        index: StreamIndex::Ordinal(self.sequence_number),
                    }),
                })
                .await;
            self.sequence_number += 1;
        }
    }
}

impl Connection for KcpConnection {}

impl Sink<SendCmd> for KcpConnection {
    type Error = Box<dyn std::error::Error>;
    fn poll_ready(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Result<()>> {
        Pin::new(&mut self.sender)
            .poll_ready(ctx)
            .map_err(Into::into)
    }
    fn start_send(mut self: Pin<&mut Self>, item: SendCmd) -> Result<()> {
        Pin::new(&mut self.sender)
            .start_send(item)
            .map_err(Into::into)
    }
    fn poll_flush(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Result<()>> {
        Pin::new(&mut self.sender)
            .poll_flush(ctx)
            .map_err(Into::into)
    }
    fn poll_close(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Result<()>> {
        Pin::new(&mut self.sender)
            .poll_close(ctx)
            .map_err(Into::into)
    }
}

impl Stream for KcpConnection {
    type Item = Result<Datagram>;
    fn poll_next(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.receiver)
            .poll_next(ctx)
            .map(|d| d.map(Ok))
    }
}

impl FusedStream for KcpConnection {
    fn is_terminated(&self) -> bool {
        false
    }
}

type OutputCallback = unsafe extern "C" fn(
    *const i8,
    c_int,
    *mut kcp::ikcpcb,
    *mut c_void,
) -> c_int;

unsafe fn wrap_output_callback<F>(f: &F) -> (OutputCallback, *mut c_void)
where
    F: Fn(*const i8, c_int, *mut kcp::ikcpcb) -> c_int,
{
    unsafe extern "C" fn trampoline<F>(
        buf: *const i8,
        len: c_int,
        cb: *mut kcp::ikcpcb,
        callback: *mut c_void,
    ) -> c_int
    where
        F: Fn(*const i8, c_int, *mut kcp::ikcpcb) -> c_int,
    {
        let callback: &F = &(*(callback as *const F));
        callback(buf, len, cb)
    }

    (trampoline::<F>, f as *const F as *mut c_void)
}
<commit_msg>cargo fix<commit_after>//! KCP adapter for benchmarking.
//!
//! KCP is just a protocol layer, and does not establish connections. Connection
//! establishment is not benchmarked, so this adapter rolls its own.
//!
//! A UDP socket for a server listens for messages with a new connection id,
//! what KCP calls a "conv". The server stream of connections then ends (only
//! one connection per server; that's all the benchmark needs).
//!
//! The server's socket sends the conv back to the client to establish
//! connection.

use crate::{tcp, Result, *};
use async_std::net::*;
use bincode::*;
use futures::{
    channel::mpsc,
    prelude::*,
    stream::{Fuse, FusedStream, LocalBoxStream, StreamExt},
};

use std::ffi::c_void;
use std::os::raw::c_int;

use std::pin::Pin;

use std::task::{Context, Poll};
use std::time::*;

#[allow(warnings)]
mod kcp {
    include!(concat!(env!("OUT_DIR"), "/kcp.rs"));
}

pub struct KcpServer {
    peers: Fuse<LocalBoxStream<'static, Result<KcpConnection>>>,
}

impl Server<KcpConnection> for KcpServer {}

impl KcpServer {
    pub async fn bind(
        address: impl ToSocketAddrs + Clone + 'static,
    ) -> Result<Self> {
        let tcp = tcp::TcpServer::bind(address.clone()).await?;

        let peers = tcp.then(move |tcp_connection| {
            let address = address.clone();
            async move {
                let mut tcp_connection = tcp_connection?;

                let udp = UdpSocket::bind(address.clone()).await?;
                let port = udp.local_addr()?.port();

                tcp_connection
                    .send(SendCmd {
                        data: serialize(&port)?,
                        delivery_mode: DeliveryMode::ReliableOrdered(StreamId(
                            0,
                        )),
                        ..SendCmd::default()
                    })
                    .await?;

                let client_port = tcp_connection
                    .next()
                    .await
                    .expect("Confirmation of port")?;
                let client_port: u16 =
                    deserialize(client_port.data.as_slice())?;

                let mut client_addr = tcp_connection.peer_addr();
                client_addr.set_port(client_port);

                Ok(KcpConnection::from_socket(tcp_connection, udp, client_addr)
                    .await)
            }
        });

        Ok(Self {
            peers: peers.boxed_local().fuse(),
        })
    }
}

impl Stream for KcpServer {
    type Item = Result<KcpConnection>;
    fn poll_next(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.peers).poll_next(ctx)
    }
}

impl FusedStream for KcpServer {
    fn is_terminated(&self) -> bool {
        self.peers.is_terminated()
    }
}

pub struct KcpConnection {
    #[allow(unused)]
    tcp_connection: tcp::TcpConnection,
    receiver: mpsc::Receiver<Datagram>,
    sender:
        Pin<Box<dyn Sink<SendCmd, Error = Box<dyn std::error::Error>> + Unpin>>,
}

impl KcpConnection {
    pub async fn connect(mut server: SocketAddr) -> Result<Self> {
        let mut tcp_connection = tcp::TcpConnection::connect(server).await?;

        let port =
            tcp_connection.next().await.expect("udp port from server")?;
        let port: u16 = deserialize(port.data.as_slice())?;
        let _udp_addr = server.set_port(port);

        let udp = UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0))
            .await?;
        let our_port = udp.local_addr()?.port();
        tcp_connection
            .send(SendCmd {
                data: serialize(&our_port)?,
                delivery_mode: DeliveryMode::ReliableOrdered(StreamId(0)),
                ..SendCmd::default()
            })
            .await?;

        Ok(Self::from_socket(tcp_connection, udp, server).await)
    }

    async fn from_socket(
        tcp_connection: tcp::TcpConnection,
        socket: UdpSocket,
        peer: SocketAddr,
    ) -> Self {
        let (command_sink, command_stream) = mpsc::channel(100);
        let (datagram_sink, datagram_stream) = mpsc::channel(100);

        async_std::task::spawn(
            Self::driver(socket, peer, command_stream, datagram_sink).map(drop),
        );

        Self {
            tcp_connection,
            receiver: datagram_stream,
            sender: Pin::new(Box::new(command_sink.sink_err_into())),
        }
    }

    async fn driver(
        socket: UdpSocket,
        peer: SocketAddr,
        mut command_stream: mpsc::Receiver<SendCmd>,
        datagram_sink: mpsc::Sender<Datagram>,
    ) -> Result<()> {
        socket.connect(peer).await?;

        let output_callback =
            |buf: *const i8, len: i32, _cb: *mut kcp::ikcpcb| -> c_int {
                let data: &[u8] = unsafe {
                    std::slice::from_raw_parts(buf as *const u8, len as usize)
                };
                match async_std::task::block_on(socket.send(data)) {
                    Err(_) => -1,
                    Ok(v) => v as i32,
                }
            };
        let cb = {
            let (callback, state) =
                unsafe { wrap_output_callback(&output_callback) };
            let cb = unsafe {
                kcp::ikcp_create(/*conv=*/ 0, state)
            };
            unsafe { kcp::ikcp_setoutput(cb, Some(callback)) }
            Cb(cb)
        };

        struct CbDropper(Cb);

        impl Drop for CbDropper {
            fn drop(&mut self) {
                let CbDropper(Cb(ptr)) = *self;
                unsafe { kcp::ikcp_release(ptr) }
            }
        }

        let _dropper = CbDropper(cb);

        let mut servicer = KcpServicer {
            epoch: Instant::now(),
            datagram_sink,
            cb,
            sequence_number: 0,
        };

        let mut buffer = [0u8; 65535];
        let mut service_ticker = ticker(1000).fuse();
        loop {
            if command_stream.is_terminated() {
                return Ok(());
            }

            futures::select! {
                read_result = socket.recv(&mut buffer).fuse() => {
                    {
                        let len = read_result?;
                        let data = buffer.as_ptr() as *const i8;
                        let code = unsafe {
                            kcp::ikcp_input(cb.0, data, len as i64)
                        };
                        if code < 0 {
                            panic!("kcp panic; input error: {:?}", code);
                        }
                    }

                    servicer.service().await;
                }
                send_cmd = command_stream.select_next_some() => unsafe {
                /*            match send_cmd.delivery_mode {
                                DeliveryMode::ReliableOrdered(StreamId(0)) => {},
                                _ => panic!("KCP only supports a single reliable channel"),
                            };*/

                            {
                                let data = send_cmd.data.as_ptr() as *const i8;
                                let code = kcp::ikcp_send(
                                   cb.0,
                                   data,
                                   send_cmd.data.len() as i32
                                );
                                if code < 0 {
                                    panic!("kcp input failed: {:?}", code);
                                }
                            }

                            servicer.service().await;

                            kcp::ikcp_flush(cb.0);
                },
                _ = service_ticker.select_next_some() => servicer.service().await,
            }
        }
    }
}

#[derive(Copy, Clone)]
struct Cb(*mut kcp::ikcpcb);

unsafe impl Send for Cb {}

struct KcpServicer {
    epoch: Instant,
    datagram_sink: mpsc::Sender<Datagram>,
    cb: Cb,
    sequence_number: u32,
}

impl KcpServicer {
    fn current_time_ms(&self) -> u32 {
        Instant::now().duration_since(self.epoch).as_millis() as u32
    }

    async fn service(&mut self) {
        unsafe { kcp::ikcp_update(self.cb.0, self.current_time_ms()) };

        let mut buffer = [0; 65535];
        #[allow(unused_assignments)]
        let mut len = i32::default();

        while {
            let buffer_ptr = buffer.as_mut_ptr() as *mut i8;
            len = unsafe {
                kcp::ikcp_recv(self.cb.0, buffer_ptr, buffer.len() as i32)
            };
            len > 0
        } {
            let _ = self
                .datagram_sink
                .send(Datagram {
                    data: buffer[0..(len as usize)].to_vec(),
                    stream_position: Some(StreamPosition {
                        stream_id: StreamId(0),
                        index: StreamIndex::Ordinal(self.sequence_number),
                    }),
                })
                .await;
            self.sequence_number += 1;
        }
    }
}

impl Connection for KcpConnection {}

impl Sink<SendCmd> for KcpConnection {
    type Error = Box<dyn std::error::Error>;
    fn poll_ready(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Result<()>> {
        Pin::new(&mut self.sender)
            .poll_ready(ctx)
            .map_err(Into::into)
    }
    fn start_send(mut self: Pin<&mut Self>, item: SendCmd) -> Result<()> {
        Pin::new(&mut self.sender)
            .start_send(item)
            .map_err(Into::into)
    }
    fn poll_flush(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Result<()>> {
        Pin::new(&mut self.sender)
            .poll_flush(ctx)
            .map_err(Into::into)
    }
    fn poll_close(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Result<()>> {
        Pin::new(&mut self.sender)
            .poll_close(ctx)
            .map_err(Into::into)
    }
}

impl Stream for KcpConnection {
    type Item = Result<Datagram>;
    fn poll_next(
        mut self: Pin<&mut Self>,
        ctx: &mut Context,
    ) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.receiver)
            .poll_next(ctx)
            .map(|d| d.map(Ok))
    }
}

impl FusedStream for KcpConnection {
    fn is_terminated(&self) -> bool {
        false
    }
}

type OutputCallback = unsafe extern "C" fn(
    *const i8,
    c_int,
    *mut kcp::ikcpcb,
    *mut c_void,
) -> c_int;

unsafe fn wrap_output_callback<F>(f: &F) -> (OutputCallback, *mut c_void)
where
    F: Fn(*const i8, c_int, *mut kcp::ikcpcb) -> c_int,
{
    unsafe extern "C" fn trampoline<F>(
        buf: *const i8,
        len: c_int,
        cb: *mut kcp::ikcpcb,
        callback: *mut c_void,
    ) -> c_int
    where
        F: Fn(*const i8, c_int, *mut kcp::ikcpcb) -> c_int,
    {
        let callback: &F = &(*(callback as *const F));
        callback(buf, len, cb)
    }

    (trampoline::<F>, f as *const F as *mut c_void)
}
<|endoftext|>


<commit_before>// Rust has a perfectly good Semaphore type already.  It lacks count(), though, so we can't use it
// directly.
#![feature(unsafe_destructor)]

extern crate sync;

use std::io::timer;
use std::sync::Arc;
use std::sync::atomic::AtomicUint;
use std::sync::atomics;
use std::time::duration::Duration;

pub struct CountingSemaphore {
    count: AtomicUint, // Remaining resource count
    backoff: Duration, // How long to sleep if a resource is being contended
}

pub struct CountingSemaphoreGuard<'a> {
    sem: &'a CountingSemaphore,
}

impl CountingSemaphore {
    pub fn new(max: uint, backoff: Duration) -> CountingSemaphore {
        CountingSemaphore { count: AtomicUint::new(max), backoff: backoff }
    }

    pub fn acquire(&self) -> CountingSemaphoreGuard {
        // Spinlock, more or less
        loop {
            let count = self.count.load(atomics::SeqCst);
            if count == 0 || self.count.compare_and_swap(count, count - 1, atomics::SeqCst) != count {
                timer::sleep(self.backoff);
            } else {
                break
            }
        }
        CountingSemaphoreGuard { sem: self }
    }

    pub fn count(&self) -> uint {
        self.count.load(atomics::SeqCst)
    }
}

#[unsafe_destructor]
impl<'a> Drop for CountingSemaphoreGuard<'a> {
    // When the guard is dropped, a resource is released back to the pool.
    fn drop(&mut self) {
        self.sem.count.fetch_add(1, atomics::SeqCst);
    }
}

fn metered(duration: Duration, backoff: Duration) {
    static MAX_COUNT: uint = 4;
    static NUM_THREADS: u8 = 10;
    let sem = Arc::new(CountingSemaphore::new(MAX_COUNT, backoff));
    let (tx, rx) = channel();
    for i in range(0, NUM_THREADS) {
        let sem = sem.clone();
        let tx = tx.clone();
        spawn(proc() {
            let guard = sem.acquire();
            let count = sem.count();
            assert!(count <= MAX_COUNT);
            println!("Worker {} after acquire: count = {}", i, count);
            timer::sleep(duration);
            drop(guard);
            let count = sem.count();
            assert!(count <= MAX_COUNT);
            println!("Worker {} after release: count = {}", i, count);
            tx.send(());
        })
    }
    drop(tx);
    for _ in range(0, NUM_THREADS) {
        rx.recv();
    }
}

#[test]
fn test_metered_concurrency() {
    metered(Duration::seconds(1) / 20, Duration::seconds(1) / 20);
}

#[cfg(not(test))]
fn main() {
    metered(Duration::seconds(2), Duration::seconds(1) / 10);
}
<commit_msg>Adding better comments.<commit_after>// Implements http://rosettacode.org/wiki/Metered_concurrency
// Rust has a perfectly good Semaphore type already.  It lacks count(), though, so we can't use it
// directly.
#![feature(unsafe_destructor)]

extern crate sync;

use std::io::timer;
use std::sync::Arc;
use std::sync::atomic::AtomicUint;
use std::sync::atomics;
use std::time::duration::Duration;

pub struct CountingSemaphore {
    count: AtomicUint, // Remaining resource count
    backoff: Duration, // How long to sleep if a resource is being contended
}

pub struct CountingSemaphoreGuard<'a> {
    sem: &'a CountingSemaphore, // A reference to the owning semaphore.
}

impl CountingSemaphore {
    // Create a semaphore with `max` available resources and a linearly increasing backoff of
    // `backoff` (used during spinlock contention).
    pub fn new(max: uint, backoff: Duration) -> CountingSemaphore {
        CountingSemaphore { count: AtomicUint::new(max), backoff: backoff }
    }

    // Acquire a resource, returning a RAII CountingSemaphoreGuard.
    pub fn acquire(&self) -> CountingSemaphoreGuard {
        // Spinlock until remaining resource count is at least 1
        let mut backoff: Duration = self.backoff;
        loop {
            // Probably don't need SeqCst here, but it doesn't hurt.
            let count = self.count.load(atomics::SeqCst);
            // The check for 0 is necessary to make sure we don't go negative, which is why this
            // must be a compare-and-swap rather than a straight decrement.
            if count == 0 || self.count.compare_and_swap(count, count - 1, atomics::SeqCst) != count {
                // Linear backoff a la Servo's spinlock contention.
                timer::sleep(backoff);
                backoff = backoff + self.backoff;
            } else {
                // We successfully acquired the resource.
                break
            }
        }
        CountingSemaphoreGuard { sem: self }
    }

    // Return remaining resource count
    pub fn count(&self) -> uint {
        self.count.load(atomics::SeqCst)
    }
}

#[unsafe_destructor]
impl<'a> Drop for CountingSemaphoreGuard<'a> {
    // When the guard is dropped, a resource is released back to the pool.
    fn drop(&mut self) {
        self.sem.count.fetch_add(1, atomics::SeqCst);
    }
}

fn metered(duration: Duration, backoff: Duration) {
    static MAX_COUNT: uint = 4; // Total available resources
    static NUM_WORKERS: u8 = 10; // Number of workers contending for the resources
    let sem = Arc::new(CountingSemaphore::new(MAX_COUNT, backoff));
    let (tx, rx) = channel();
    for i in range(0, NUM_WORKERS) {
        let sem = sem.clone();
        let tx = tx.clone();
        spawn(proc() {
            let guard = sem.acquire();
            let count = sem.count();
            assert!(count < MAX_COUNT);
            println!("Worker {} after acquire: count = {}", i, count);
            timer::sleep(duration);
            drop(guard);
            let count = sem.count();
            assert!(count <= MAX_COUNT);
            println!("Worker {} after release: count = {}", i, count);
            tx.send(());
        })
    }
    drop(tx);
    for _ in range(0, NUM_WORKERS) {
        rx.recv();
    }
}

#[test]
fn test_metered_concurrency() {
    metered(Duration::seconds(1) / 20, Duration::seconds(1) / 20);
}

#[cfg(not(test))]
fn main() {
    metered(Duration::seconds(2), Duration::seconds(1) / 10);
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate ipc_channel;
extern crate test;

/// Allows doing multiple inner iterations per bench.iter() run.
///
/// This is mostly to amortise the overhead of spawning a thread in the benchmark
/// when sending larger messages (that might be fragmented).
///
/// Note that you need to compensate the displayed results
/// for the proportionally longer runs yourself,
/// as the benchmark framework doesn't know about the inner iterations...
const ITERATIONS: usize = 1;

mod platform {
    extern crate crossbeam;

    use ipc_channel::platform;
    use ITERATIONS;

    use std::sync::{mpsc, Mutex};
    use test;

    #[bench]
    fn create_channel(b: &mut test::Bencher) {
        b.iter(|| {
            for _ in 0..ITERATIONS {
                platform::channel().unwrap();
            }
        });
    }

    fn bench_transfer_data(b: &mut test::Bencher, size: usize) {
        let data: Vec<u8> = (0..size).map(|i| (i % 251) as u8).collect();
        let (tx, rx) = platform::channel().unwrap();

        let (wait_tx, wait_rx) = mpsc::channel();
        let wait_rx = Mutex::new(wait_rx);

        if size > platform::OsIpcSender::get_max_fragment_size() {
            b.iter(|| {
                crossbeam::scope(|scope| {
                    let tx = tx.clone();
                    scope.spawn(|| {
                        let wait_rx = wait_rx.lock().unwrap();
                        let tx = tx;
                        for _ in 0..ITERATIONS {
                            tx.send(&data, vec![], vec![]).unwrap();
                            if ITERATIONS > 1 {
                                // Prevent beginning of the next send
                                // from overlapping with receive of last fragment,
                                // as otherwise results of runs with a large tail fragment
                                // are significantly skewed.
                                wait_rx.recv().unwrap();
                            }
                        }
                    });
                    for _ in 0..ITERATIONS {
                        rx.recv().unwrap();
                        if ITERATIONS > 1 {
                            wait_tx.send(()).unwrap();
                        }
                    }
                    // For reasons mysterious to me,
                    // not returning a value *from every branch*
                    // adds some 100 ns or so of overhead to all results --
                    // which is quite significant for very short tests...
                    0
                })
            });
        } else {
            b.iter(|| {
                for _ in 0..ITERATIONS {
                    tx.send(&data, vec![], vec![]).unwrap();
                    rx.recv().unwrap();
                }
                0
            });
        }
    }

    #[bench]
    fn transfer_data_00_1(b: &mut test::Bencher) {
        bench_transfer_data(b, 1);
    }
    #[bench]
    fn transfer_data_01_2(b: &mut test::Bencher) {
        bench_transfer_data(b, 2);
    }
    #[bench]
    fn transfer_data_02_4(b: &mut test::Bencher) {
        bench_transfer_data(b, 4);
    }
    #[bench]
    fn transfer_data_03_8(b: &mut test::Bencher) {
        bench_transfer_data(b, 8);
    }
    #[bench]
    fn transfer_data_04_16(b: &mut test::Bencher) {
        bench_transfer_data(b, 16);
    }
    #[bench]
    fn transfer_data_05_32(b: &mut test::Bencher) {
        bench_transfer_data(b, 32);
    }
    #[bench]
    fn transfer_data_06_64(b: &mut test::Bencher) {
        bench_transfer_data(b, 64);
    }
    #[bench]
    fn transfer_data_07_128(b: &mut test::Bencher) {
        bench_transfer_data(b, 128);
    }
    #[bench]
    fn transfer_data_08_256(b: &mut test::Bencher) {
        bench_transfer_data(b, 256);
    }
    #[bench]
    fn transfer_data_09_512(b: &mut test::Bencher) {
        bench_transfer_data(b, 512);
    }
    #[bench]
    fn transfer_data_10_1k(b: &mut test::Bencher) {
        bench_transfer_data(b, 1 * 1024);
    }
    #[bench]
    fn transfer_data_11_2k(b: &mut test::Bencher) {
        bench_transfer_data(b, 2 * 1024);
    }
    #[bench]
    fn transfer_data_12_4k(b: &mut test::Bencher) {
        bench_transfer_data(b, 4 * 1024);
    }
    #[bench]
    fn transfer_data_13_8k(b: &mut test::Bencher) {
        bench_transfer_data(b, 8 * 1024);
    }
    #[bench]
    fn transfer_data_14_16k(b: &mut test::Bencher) {
        bench_transfer_data(b, 16 * 1024);
    }
    #[bench]
    fn transfer_data_15_32k(b: &mut test::Bencher) {
        bench_transfer_data(b, 32 * 1024);
    }
    #[bench]
    fn transfer_data_16_64k(b: &mut test::Bencher) {
        bench_transfer_data(b, 64 * 1024);
    }
    #[bench]
    fn transfer_data_17_128k(b: &mut test::Bencher) {
        bench_transfer_data(b, 128 * 1024);
    }
    #[bench]
    fn transfer_data_18_256k(b: &mut test::Bencher) {
        bench_transfer_data(b, 256 * 1024);
    }
    #[bench]
    fn transfer_data_19_512k(b: &mut test::Bencher) {
        bench_transfer_data(b, 512 * 1024);
    }
    #[bench]
    fn transfer_data_20_1m(b: &mut test::Bencher) {
        bench_transfer_data(b, 1 * 1024 * 1024);
    }
    #[bench]
    fn transfer_data_21_2m(b: &mut test::Bencher) {
        bench_transfer_data(b, 2 * 1024 * 1024);
    }
    #[bench]
    fn transfer_data_22_4m(b: &mut test::Bencher) {
        bench_transfer_data(b, 4 * 1024 * 1024);
    }
    #[bench]
    fn transfer_data_23_8m(b: &mut test::Bencher) {
        bench_transfer_data(b, 8 * 1024 * 1024);
    }
}

mod ipc {
    mod receiver_set {
        use ipc_channel::ipc::{self, IpcReceiverSet};
        use test;

        // Benchmark selecting over a set of `n` receivers,
        // with `to_send` of them actually having pending data.
        fn gen_select_test(b: &mut test::Bencher, to_send: usize, n: usize) -> () {
            let mut active = Vec::with_capacity(to_send);
            let mut dormant = Vec::with_capacity(n - to_send);
            let mut rx_set = IpcReceiverSet::new().unwrap();
            for _ in 0..to_send {
                let (tx, rx) = ipc::channel().unwrap();
                rx_set.add(rx).unwrap();
                active.push(tx);
            }
            for _ in to_send..n {
                let (tx, rx) = ipc::channel::<()>().unwrap();
                rx_set.add(rx).unwrap();
                dormant.push(tx);
            }
            b.iter(|| {
                for tx in active.iter() {
                    tx.send(()).unwrap();
                }
                let mut received = 0;
                while received < to_send {
                    for result in rx_set.select().unwrap().into_iter() {
                        let (_, _) = result.unwrap();
                        received += 1;
                    }
                }
            });
        }

        #[bench]
        fn send_on_1_of_1(b: &mut test::Bencher) -> () {
            gen_select_test(b, 1, 1);
        }

        #[bench]
        fn send_on_1_of_5(b: &mut test::Bencher) -> () {
            gen_select_test(b, 1, 5);
        }

        #[bench]
        fn send_on_2_of_5(b: &mut test::Bencher) -> () {
            gen_select_test(b, 2, 5);
        }

        #[bench]
        fn send_on_5_of_5(b: &mut test::Bencher) -> () {
            gen_select_test(b, 5, 5);
        }

        #[bench]
        fn send_on_1_of_20(b: &mut test::Bencher) -> () {
            gen_select_test(b, 1, 20);
        }

        #[bench]
        fn send_on_5_of_20(b: &mut test::Bencher) -> () {
            gen_select_test(b, 5, 20);
        }

        #[bench]
        fn send_on_20_of_20(b: &mut test::Bencher) -> () {
            gen_select_test(b, 20, 20);
        }

        #[bench]
        fn send_on_1_of_100(b: &mut test::Bencher) -> () {
            gen_select_test(b, 1, 100);
        }

        #[bench]
        fn send_on_5_of_100(b: &mut test::Bencher) -> () {
            gen_select_test(b, 5, 100);
        }
        #[bench]
        fn send_on_20_of_100(b: &mut test::Bencher) -> () {
            gen_select_test(b, 20, 100);
        }

        #[bench]
        fn send_on_100_of_100(b: &mut test::Bencher) -> () {
            gen_select_test(b, 100, 100);
        }

        fn create_set_of_n(n: usize) -> IpcReceiverSet {
            let mut rx_set = IpcReceiverSet::new().unwrap();
            for _ in 0..n {
                let (_, rx) = ipc::channel::<()>().unwrap();
                rx_set.add(rx).unwrap();
            }
            rx_set
        }

        #[bench]
        fn create_and_destroy_empty_set(b: &mut test::Bencher) -> () {
            b.iter(|| {
                create_set_of_n(0);
            });
        }

        #[bench]
        fn create_and_destroy_set_of_1(b: &mut test::Bencher) -> () {
            b.iter(|| {
                create_set_of_n(1);
            });
        }

        #[bench]
        fn create_and_destroy_set_of_10(b: &mut test::Bencher) -> () {
            b.iter(|| {
                create_set_of_n(10);
            });
        }

        #[bench]
        fn create_and_destroy_set_of_100(b: &mut test::Bencher) -> () {
            b.iter(|| {
                create_set_of_n(100);
            });
        }

        #[bench]
        // Benchmark performance of removing closed receivers from set.
        // This also includes the time for adding receivers,
        // as there is no way to measure the removing in isolation.
        fn add_and_remove_closed_receivers(b: &mut test::Bencher) -> () {
            b.iter(|| {
                let mut rx_set = create_set_of_n(1);
                // On `select()`, receivers with a "ClosedChannel" event will be closed,
                // and automatically dropped from the set.
                rx_set.select().unwrap();
            });
        }
    }
}
<commit_msg>Benchmarks: Also test dropping more than one closed receiver<commit_after>#![feature(test)]

extern crate ipc_channel;
extern crate test;

/// Allows doing multiple inner iterations per bench.iter() run.
///
/// This is mostly to amortise the overhead of spawning a thread in the benchmark
/// when sending larger messages (that might be fragmented).
///
/// Note that you need to compensate the displayed results
/// for the proportionally longer runs yourself,
/// as the benchmark framework doesn't know about the inner iterations...
const ITERATIONS: usize = 1;

mod platform {
    extern crate crossbeam;

    use ipc_channel::platform;
    use ITERATIONS;

    use std::sync::{mpsc, Mutex};
    use test;

    #[bench]
    fn create_channel(b: &mut test::Bencher) {
        b.iter(|| {
            for _ in 0..ITERATIONS {
                platform::channel().unwrap();
            }
        });
    }

    fn bench_transfer_data(b: &mut test::Bencher, size: usize) {
        let data: Vec<u8> = (0..size).map(|i| (i % 251) as u8).collect();
        let (tx, rx) = platform::channel().unwrap();

        let (wait_tx, wait_rx) = mpsc::channel();
        let wait_rx = Mutex::new(wait_rx);

        if size > platform::OsIpcSender::get_max_fragment_size() {
            b.iter(|| {
                crossbeam::scope(|scope| {
                    let tx = tx.clone();
                    scope.spawn(|| {
                        let wait_rx = wait_rx.lock().unwrap();
                        let tx = tx;
                        for _ in 0..ITERATIONS {
                            tx.send(&data, vec![], vec![]).unwrap();
                            if ITERATIONS > 1 {
                                // Prevent beginning of the next send
                                // from overlapping with receive of last fragment,
                                // as otherwise results of runs with a large tail fragment
                                // are significantly skewed.
                                wait_rx.recv().unwrap();
                            }
                        }
                    });
                    for _ in 0..ITERATIONS {
                        rx.recv().unwrap();
                        if ITERATIONS > 1 {
                            wait_tx.send(()).unwrap();
                        }
                    }
                    // For reasons mysterious to me,
                    // not returning a value *from every branch*
                    // adds some 100 ns or so of overhead to all results --
                    // which is quite significant for very short tests...
                    0
                })
            });
        } else {
            b.iter(|| {
                for _ in 0..ITERATIONS {
                    tx.send(&data, vec![], vec![]).unwrap();
                    rx.recv().unwrap();
                }
                0
            });
        }
    }

    #[bench]
    fn transfer_data_00_1(b: &mut test::Bencher) {
        bench_transfer_data(b, 1);
    }
    #[bench]
    fn transfer_data_01_2(b: &mut test::Bencher) {
        bench_transfer_data(b, 2);
    }
    #[bench]
    fn transfer_data_02_4(b: &mut test::Bencher) {
        bench_transfer_data(b, 4);
    }
    #[bench]
    fn transfer_data_03_8(b: &mut test::Bencher) {
        bench_transfer_data(b, 8);
    }
    #[bench]
    fn transfer_data_04_16(b: &mut test::Bencher) {
        bench_transfer_data(b, 16);
    }
    #[bench]
    fn transfer_data_05_32(b: &mut test::Bencher) {
        bench_transfer_data(b, 32);
    }
    #[bench]
    fn transfer_data_06_64(b: &mut test::Bencher) {
        bench_transfer_data(b, 64);
    }
    #[bench]
    fn transfer_data_07_128(b: &mut test::Bencher) {
        bench_transfer_data(b, 128);
    }
    #[bench]
    fn transfer_data_08_256(b: &mut test::Bencher) {
        bench_transfer_data(b, 256);
    }
    #[bench]
    fn transfer_data_09_512(b: &mut test::Bencher) {
        bench_transfer_data(b, 512);
    }
    #[bench]
    fn transfer_data_10_1k(b: &mut test::Bencher) {
        bench_transfer_data(b, 1 * 1024);
    }
    #[bench]
    fn transfer_data_11_2k(b: &mut test::Bencher) {
        bench_transfer_data(b, 2 * 1024);
    }
    #[bench]
    fn transfer_data_12_4k(b: &mut test::Bencher) {
        bench_transfer_data(b, 4 * 1024);
    }
    #[bench]
    fn transfer_data_13_8k(b: &mut test::Bencher) {
        bench_transfer_data(b, 8 * 1024);
    }
    #[bench]
    fn transfer_data_14_16k(b: &mut test::Bencher) {
        bench_transfer_data(b, 16 * 1024);
    }
    #[bench]
    fn transfer_data_15_32k(b: &mut test::Bencher) {
        bench_transfer_data(b, 32 * 1024);
    }
    #[bench]
    fn transfer_data_16_64k(b: &mut test::Bencher) {
        bench_transfer_data(b, 64 * 1024);
    }
    #[bench]
    fn transfer_data_17_128k(b: &mut test::Bencher) {
        bench_transfer_data(b, 128 * 1024);
    }
    #[bench]
    fn transfer_data_18_256k(b: &mut test::Bencher) {
        bench_transfer_data(b, 256 * 1024);
    }
    #[bench]
    fn transfer_data_19_512k(b: &mut test::Bencher) {
        bench_transfer_data(b, 512 * 1024);
    }
    #[bench]
    fn transfer_data_20_1m(b: &mut test::Bencher) {
        bench_transfer_data(b, 1 * 1024 * 1024);
    }
    #[bench]
    fn transfer_data_21_2m(b: &mut test::Bencher) {
        bench_transfer_data(b, 2 * 1024 * 1024);
    }
    #[bench]
    fn transfer_data_22_4m(b: &mut test::Bencher) {
        bench_transfer_data(b, 4 * 1024 * 1024);
    }
    #[bench]
    fn transfer_data_23_8m(b: &mut test::Bencher) {
        bench_transfer_data(b, 8 * 1024 * 1024);
    }
}

mod ipc {
    mod receiver_set {
        use ipc_channel::ipc::{self, IpcReceiverSet};
        use test;

        // Benchmark selecting over a set of `n` receivers,
        // with `to_send` of them actually having pending data.
        fn gen_select_test(b: &mut test::Bencher, to_send: usize, n: usize) -> () {
            let mut active = Vec::with_capacity(to_send);
            let mut dormant = Vec::with_capacity(n - to_send);
            let mut rx_set = IpcReceiverSet::new().unwrap();
            for _ in 0..to_send {
                let (tx, rx) = ipc::channel().unwrap();
                rx_set.add(rx).unwrap();
                active.push(tx);
            }
            for _ in to_send..n {
                let (tx, rx) = ipc::channel::<()>().unwrap();
                rx_set.add(rx).unwrap();
                dormant.push(tx);
            }
            b.iter(|| {
                for tx in active.iter() {
                    tx.send(()).unwrap();
                }
                let mut received = 0;
                while received < to_send {
                    for result in rx_set.select().unwrap().into_iter() {
                        let (_, _) = result.unwrap();
                        received += 1;
                    }
                }
            });
        }

        #[bench]
        fn send_on_1_of_1(b: &mut test::Bencher) -> () {
            gen_select_test(b, 1, 1);
        }

        #[bench]
        fn send_on_1_of_5(b: &mut test::Bencher) -> () {
            gen_select_test(b, 1, 5);
        }

        #[bench]
        fn send_on_2_of_5(b: &mut test::Bencher) -> () {
            gen_select_test(b, 2, 5);
        }

        #[bench]
        fn send_on_5_of_5(b: &mut test::Bencher) -> () {
            gen_select_test(b, 5, 5);
        }

        #[bench]
        fn send_on_1_of_20(b: &mut test::Bencher) -> () {
            gen_select_test(b, 1, 20);
        }

        #[bench]
        fn send_on_5_of_20(b: &mut test::Bencher) -> () {
            gen_select_test(b, 5, 20);
        }

        #[bench]
        fn send_on_20_of_20(b: &mut test::Bencher) -> () {
            gen_select_test(b, 20, 20);
        }

        #[bench]
        fn send_on_1_of_100(b: &mut test::Bencher) -> () {
            gen_select_test(b, 1, 100);
        }

        #[bench]
        fn send_on_5_of_100(b: &mut test::Bencher) -> () {
            gen_select_test(b, 5, 100);
        }
        #[bench]
        fn send_on_20_of_100(b: &mut test::Bencher) -> () {
            gen_select_test(b, 20, 100);
        }

        #[bench]
        fn send_on_100_of_100(b: &mut test::Bencher) -> () {
            gen_select_test(b, 100, 100);
        }

        fn create_set_of_n(n: usize) -> IpcReceiverSet {
            let mut rx_set = IpcReceiverSet::new().unwrap();
            for _ in 0..n {
                let (_, rx) = ipc::channel::<()>().unwrap();
                rx_set.add(rx).unwrap();
            }
            rx_set
        }

        #[bench]
        fn create_and_destroy_empty_set(b: &mut test::Bencher) -> () {
            b.iter(|| {
                create_set_of_n(0);
            });
        }

        #[bench]
        fn create_and_destroy_set_of_1(b: &mut test::Bencher) -> () {
            b.iter(|| {
                create_set_of_n(1);
            });
        }

        #[bench]
        fn create_and_destroy_set_of_10(b: &mut test::Bencher) -> () {
            b.iter(|| {
                create_set_of_n(10);
            });
        }

        #[bench]
        fn create_and_destroy_set_of_100(b: &mut test::Bencher) -> () {
            b.iter(|| {
                create_set_of_n(100);
            });
        }

        // Benchmark performance of removing closed receivers from set.
        // This also includes the time for adding receivers,
        // as there is no way to measure the removing in isolation.
        fn bench_remove_closed(b: &mut test::Bencher, n: usize) {
            b.iter(|| {
                let mut rx_set = create_set_of_n(n);

                let mut dropped_count = 0;
                while dropped_count < n {
                    // On `select()`, receivers with a "ClosedChannel" event will be closed,
                    // and automatically dropped from the set.
                    dropped_count += rx_set.select().unwrap().len();
                }
            });
        }

        #[bench]
        fn add_and_remove_1_closed_receivers(b: &mut test::Bencher) {
            bench_remove_closed(b, 1);
        }

        #[bench]
        fn add_and_remove_10_closed_receivers(b: &mut test::Bencher) {
            bench_remove_closed(b, 10);
        }

        #[bench]
        fn add_and_remove_100_closed_receivers(b: &mut test::Bencher) {
            bench_remove_closed(b, 100);
        }
    }
}
<|endoftext|>


<commit_before>extern crate steamid_ng;
extern crate serde_json;
use steamid_ng::*;

#[test]
fn test_manual_construction() {
    let mut s = SteamID::from(0);
    s.set_account_id(1234);
    s.set_instance(Instance::Console);
    s.set_account_type(AccountType::Chat);
    s.set_universe(Universe::Beta);

    assert_eq!(s.account_id(), 1234);
    assert_eq!(s.instance(), Instance::Console);
    assert_eq!(s.account_type(), AccountType::Chat);
    assert_eq!(s.universe(), Universe::Beta);
    assert_eq!(
        s,
        SteamID::new(1234, Instance::Console, AccountType::Chat, Universe::Beta)
    );

    s.set_account_id(4567);
    assert_eq!(s.account_id(), 4567);
    assert_eq!(s.instance(), Instance::Console);
    assert_eq!(s.account_type(), AccountType::Chat);
    assert_eq!(s.universe(), Universe::Beta);

    s.set_universe(Universe::Dev);
    assert_eq!(s.account_id(), 4567);
    assert_eq!(s.instance(), Instance::Console);
    assert_eq!(s.account_type(), AccountType::Chat);
    assert_eq!(s.universe(), Universe::Dev);

    s.set_instance(Instance::Web);
    assert_eq!(s.account_id(), 4567);
    assert_eq!(s.instance(), Instance::Web);
    assert_eq!(s.account_type(), AccountType::Chat);
    assert_eq!(s.universe(), Universe::Dev);

    s.set_account_type(AccountType::GameServer);
    assert_eq!(s.account_id(), 4567);
    assert_eq!(s.instance(), Instance::Web);
    assert_eq!(s.account_type(), AccountType::GameServer);
    assert_eq!(s.universe(), Universe::Dev);
}

#[test]
fn test_from_u64() {
    let s = SteamID::from(103582791432294076);
    assert_eq!(s.account_id(), 2772668);
    assert_eq!(s.instance(), Instance::All);
    assert_eq!(s.account_type(), AccountType::Clan);
    assert_eq!(s.universe(), Universe::Public);

    let s = SteamID::from(157626004137848889);
    assert_eq!(s.account_id(), 12345);
    assert_eq!(s.instance(), Instance::Web);
    assert_eq!(s.account_type(), AccountType::GameServer);
    assert_eq!(s.universe(), Universe::Beta);
}

#[test]
fn test_steam2() {
    let mut s = SteamID::from(76561197969249708);

    assert_eq!(s.steam2(), "STEAM_1:0:4491990");
    s.set_universe(Universe::Invalid);
    assert_eq!(s.steam2(), "STEAM_0:0:4491990");
    s.set_universe(Universe::Beta);
    assert_eq!(s.steam2(), "STEAM_2:0:4491990");
    s.set_account_type(AccountType::GameServer);
    assert_eq!(s.steam2(), "157625991261918636");
}

#[test]
fn test_from_steam2() {
    let s = SteamID::from_steam2("STEAM_0:0:4491990").unwrap();
    assert_eq!(s.account_id(), 8983980);
    assert_eq!(s.instance(), Instance::Desktop);
    assert_eq!(s.account_type(), AccountType::Individual);
    assert_eq!(s.universe(), Universe::Public);

    let s = SteamID::from_steam2("STEAM_0:1:4491990").unwrap();
    assert_eq!(s.account_id(), 8983981);
    assert_eq!(s.instance(), Instance::Desktop);
    assert_eq!(s.account_type(), AccountType::Individual);
    assert_eq!(s.universe(), Universe::Public);

    let s = SteamID::from_steam2("STEAM_1:1:4491990").unwrap();
    assert_eq!(s.account_id(), 8983981);
    assert_eq!(s.instance(), Instance::Desktop);
    assert_eq!(s.account_type(), AccountType::Individual);
    assert_eq!(s.universe(), Universe::Public);

    assert_eq!(
        SteamID::from_steam2("STEAM_bogus:bogus:bogus"),
        Err(SteamIDParseError::default())
    );
}

#[test]
fn test_steam3_symmetric() {
    let steam3ids = vec![
        "[U:1:123]",
        "[U:1:123:2]",
        "[G:1:626]",
        "[A:2:165:1]",
        "[T:1:123]",
        "[c:1:123]",
        "[L:1:123]",
    ];

    for id in steam3ids {
        assert_eq!(SteamID::from_steam3(id).unwrap().steam3(), id);
    }
}

#[test]
fn test_from_steam3() {
    let s = SteamID::from_steam3("[U:1:123]").unwrap();
    assert_eq!(s.account_id(), 123);
    assert_eq!(s.instance(), Instance::Desktop);
    assert_eq!(s.account_type(), AccountType::Individual);
    assert_eq!(s.universe(), Universe::Public);

    let s = SteamID::from_steam3("[A:1:123:4]").unwrap();
    assert_eq!(s.account_id(), 123);
    assert_eq!(s.instance(), Instance::Web);
    assert_eq!(s.account_type(), AccountType::AnonGameServer);
    assert_eq!(s.universe(), Universe::Public);

    let s = SteamID::from_steam3("[L:2:123]").unwrap();
    assert_eq!(s.account_id(), 123);
    assert_eq!(s.instance(), Instance::FlagLobby);
    assert_eq!(s.account_type(), AccountType::Chat);
    assert_eq!(s.universe(), Universe::Beta);

    let s = SteamID::from_steam3("[c:3:123]").unwrap();
    assert_eq!(s.account_id(), 123);
    assert_eq!(s.instance(), Instance::FlagClan);
    assert_eq!(s.account_type(), AccountType::Chat);
    assert_eq!(s.universe(), Universe::Internal);

    assert_eq!(
        SteamID::from_steam3("[bogus:bogus:bogus]"),
        Err(SteamIDParseError::default())
    );
}

#[test]
fn test_serde() {
    let s = SteamID::new(1234, Instance::Console, AccountType::Chat, Universe::Beta);
    let serialized: String = serde_json::to_string(&s).unwrap();
    let deserialized: SteamID = serde_json::from_str(&serialized).unwrap();
    assert_eq!(s, deserialized);

    let deserialized: SteamID = serde_json::from_str("\"STEAM_0:0:4491990\"").unwrap();
    assert_eq!(
        deserialized,
        SteamID::new(
            8983980,
            Instance::Desktop,
            AccountType::Individual,
            Universe::Public,
        ),
    );

    let deserialized: SteamID = serde_json::from_str("\"[U:1:123]\"").unwrap();
    assert_eq!(
        deserialized,
        SteamID::new(
            123,
            Instance::Desktop,
            AccountType::Individual,
            Universe::Public,
        ),
    );
}
<commit_msg>Fix a bug<commit_after>extern crate steamid_ng;
extern crate serde_json;
use steamid_ng::*;

#[test]
fn test_manual_construction() {
    let mut s = SteamID::from(0);
    s.set_account_id(1234);
    s.set_instance(Instance::Console);
    s.set_account_type(AccountType::Chat);
    s.set_universe(Universe::Beta);

    assert_eq!(s.account_id(), 1234);
    assert_eq!(s.instance(), Instance::Console);
    assert_eq!(s.account_type(), AccountType::Chat);
    assert_eq!(s.universe(), Universe::Beta);
    assert_eq!(
        s,
        SteamID::new(1234, Instance::Console, AccountType::Chat, Universe::Beta)
    );

    s.set_account_id(4567);
    assert_eq!(s.account_id(), 4567);
    assert_eq!(s.instance(), Instance::Console);
    assert_eq!(s.account_type(), AccountType::Chat);
    assert_eq!(s.universe(), Universe::Beta);

    s.set_universe(Universe::Dev);
    assert_eq!(s.account_id(), 4567);
    assert_eq!(s.instance(), Instance::Console);
    assert_eq!(s.account_type(), AccountType::Chat);
    assert_eq!(s.universe(), Universe::Dev);

    s.set_instance(Instance::Web);
    assert_eq!(s.account_id(), 4567);
    assert_eq!(s.instance(), Instance::Web);
    assert_eq!(s.account_type(), AccountType::Chat);
    assert_eq!(s.universe(), Universe::Dev);

    s.set_account_type(AccountType::GameServer);
    assert_eq!(s.account_id(), 4567);
    assert_eq!(s.instance(), Instance::Web);
    assert_eq!(s.account_type(), AccountType::GameServer);
    assert_eq!(s.universe(), Universe::Dev);
}

#[test]
fn test_from_u64() {
    let s = SteamID::from(103582791432294076);
    assert_eq!(s.account_id(), 2772668);
    assert_eq!(s.instance(), Instance::All);
    assert_eq!(s.account_type(), AccountType::Clan);
    assert_eq!(s.universe(), Universe::Public);

    let s = SteamID::from(157626004137848889);
    assert_eq!(s.account_id(), 12345);
    assert_eq!(s.instance(), Instance::Web);
    assert_eq!(s.account_type(), AccountType::GameServer);
    assert_eq!(s.universe(), Universe::Beta);
}

#[test]
fn test_steam2() {
    let mut s = SteamID::from(76561197969249708);

    assert_eq!(s.steam2(), "STEAM_1:0:4491990");
    s.set_universe(Universe::Invalid);
    assert_eq!(s.steam2(), "STEAM_0:0:4491990");
    s.set_universe(Universe::Beta);
    assert_eq!(s.steam2(), "STEAM_2:0:4491990");
    s.set_account_type(AccountType::GameServer);
    assert_eq!(s.steam2(), "157625991261918636");
}

#[test]
fn test_from_steam2() {
    let s = SteamID::from_steam2("STEAM_0:0:4491990").unwrap();
    assert_eq!(s.account_id(), 8983980);
    assert_eq!(s.instance(), Instance::Desktop);
    assert_eq!(s.account_type(), AccountType::Individual);
    assert_eq!(s.universe(), Universe::Public);

    let s = SteamID::from_steam2("STEAM_0:1:4491990").unwrap();
    assert_eq!(s.account_id(), 8983981);
    assert_eq!(s.instance(), Instance::Desktop);
    assert_eq!(s.account_type(), AccountType::Individual);
    assert_eq!(s.universe(), Universe::Public);

    let s = SteamID::from_steam2("STEAM_1:1:4491990").unwrap();
    assert_eq!(s.account_id(), 8983981);
    assert_eq!(s.instance(), Instance::Desktop);
    assert_eq!(s.account_type(), AccountType::Individual);
    assert_eq!(s.universe(), Universe::Public);

    assert_eq!(
        SteamID::from_steam2("STEAM_bogus:bogus:bogus"),
        Err(SteamIDParseError::default())
    );
}

#[test]
fn test_steam3_symmetric() {
    let steam3ids = vec![
        "[U:1:123]",
        "[U:1:123:2]",
        "[G:1:626]",
        "[A:2:165:1]",
        "[T:1:123]",
        "[c:1:123]",
        "[L:1:123]",
    ];

    for id in steam3ids {
        assert_eq!(SteamID::from_steam3(id).unwrap().steam3(), id);
    }
}

#[test]
fn test_from_steam3() {
    let s = SteamID::from_steam3("[U:1:123]").unwrap();
    assert_eq!(s.account_id(), 123);
    assert_eq!(s.instance(), Instance::Desktop);
    assert_eq!(s.account_type(), AccountType::Individual);
    assert_eq!(s.universe(), Universe::Public);

    let s = SteamID::from_steam3("[A:1:123:4]").unwrap();
    assert_eq!(s.account_id(), 123);
    assert_eq!(s.instance(), Instance::Web);
    assert_eq!(s.account_type(), AccountType::AnonGameServer);
    assert_eq!(s.universe(), Universe::Public);

    let s = SteamID::from_steam3("[L:2:123]").unwrap();
    assert_eq!(s.account_id(), 123);
    assert_eq!(s.instance(), Instance::FlagLobby);
    assert_eq!(s.account_type(), AccountType::Chat);
    assert_eq!(s.universe(), Universe::Beta);

    let s = SteamID::from_steam3("[c:3:123]").unwrap();
    assert_eq!(s.account_id(), 123);
    assert_eq!(s.instance(), Instance::FlagClan);
    assert_eq!(s.account_type(), AccountType::Chat);
    assert_eq!(s.universe(), Universe::Internal);

    assert_eq!(
        SteamID::from_steam3("[bogus:bogus:bogus]"),
        Err(SteamIDParseError::default())
    );
}

#[test]
fn test_serde() {
    let s = SteamID::new(1234, Instance::Console, AccountType::Chat, Universe::Beta);
    let serialized: String = serde_json::to_string(&s).unwrap();
    let deserialized: SteamID = serde_json::from_str(&serialized).unwrap();
    assert_eq!(s, deserialized);

    let deserialized: SteamID = serde_json::from_str("\"STEAM_0:0:4491990\"").unwrap();
    // Prevent rustfmt bug where it adds an extra comma even though assert_eq! is a macro
    #[cfg_attr(rustfmt, rustfmt_skip)]
    assert_eq!(
        deserialized,
        SteamID::new(
            8983980,
            Instance::Desktop,
            AccountType::Individual,
            Universe::Public,
        )
    );

    let deserialized: SteamID = serde_json::from_str("\"[U:1:123]\"").unwrap();
    #[cfg_attr(rustfmt, rustfmt_skip)]
    assert_eq!(
        deserialized,
        SteamID::new(
            123,
            Instance::Desktop,
            AccountType::Individual,
            Universe::Public,
        )
    );
}
<|endoftext|>