<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{counters, executor_proxy::ExecutorProxyTrait, LedgerInfo, PeerId};
use config::config::{NodeConfig, RoleType, StateSyncConfig};
use crypto::ed25519::*;
use execution_proto::proto::execution::{ExecuteChunkRequest, ExecuteChunkResponse};
use failure::prelude::*;
use futures::{
    channel::{mpsc, oneshot},
    compat::Stream01CompatExt,
    stream::{futures_unordered::FuturesUnordered, Fuse},
    StreamExt,
};
use logger::prelude::*;
use network::{
    proto::{GetChunkRequest, GetChunkResponse, StateSynchronizerMsg},
    validator_network::{Event, StateSynchronizerEvents, StateSynchronizerSender},
};
use proto_conv::{FromProto, IntoProto};
use rand::{thread_rng, Rng};
use std::{
    collections::HashMap,
    time::{Duration, SystemTime, UNIX_EPOCH},
};
use tokio::timer::Interval;
use types::{ledger_info::LedgerInfoWithSignatures, proto::transaction::TransactionListWithProof};

/// message used by StateSyncClient for communication with Coordinator
pub enum CoordinatorMessage {
    // used to initiate new sync
    Requested(LedgerInfo, oneshot::Sender<bool>),
    // used to notify about new txn commit
    Commit(u64),
    GetState(oneshot::Sender<u64>),
}

/// used to coordinate synchronization process
/// handles external sync requests and drives synchronization with remote peers
pub(crate) struct SyncCoordinator<T> {
    // used for interaction with remote peers
    network_sender: StateSynchronizerSender,
    // used for receiving events from peers
    network_events: Fuse<StateSynchronizerEvents>,
    // used to process client requests
    client_events: mpsc::UnboundedReceiver<CoordinatorMessage>,

    // last committed version that validator is aware of
    known_version: u64,
    // target state to sync to
    target: Option<LedgerInfo>,
    // config
    config: StateSyncConfig,
    // if autosync is on, StateSynchronizer will keep fetching chunks from upstream peers
    // even if no target state was specified
    autosync: bool,
    // peers used for synchronization. TBD: value is meta information about peer sync quality
    peers: HashMap<PeerId, ()>,
    // option callback. Called when state sync reaches target version
    callback: Option<oneshot::Sender<bool>>,
    // timestamp of next peer sync request
    next_sync: Option<SystemTime>,
    // queue of incoming long polling requests
    // peer will be notified about new chunk of transactions if it's available before expiry time
    // value format is (expiration_time, known_version, limit)
    subscriptions: HashMap<PeerId, (SystemTime, u64, u64)>,
    executor_proxy: T,
}

impl<T: ExecutorProxyTrait> SyncCoordinator<T> {
    pub fn new(
        network_sender: StateSynchronizerSender,
        network_events: StateSynchronizerEvents,
        client_events: mpsc::UnboundedReceiver<CoordinatorMessage>,
        node_config: &NodeConfig,
        executor_proxy: T,
    ) -> Self {
        Self {
            network_sender,
            network_events: network_events.fuse(),
            client_events,

            known_version: 0,
            target: None,
            config: node_config.state_sync.clone(),
            autosync: node_config.base.get_role() == RoleType::FullNode,
            peers: HashMap::new(),
            subscriptions: HashMap::new(),
            callback: None,
            next_sync: None,
            executor_proxy,
        }
    }

    /// main routine. starts sync coordinator that listens for CoordinatorMsg
    pub async fn start(mut self) {
        self.known_version = self
            .executor_proxy
            .get_latest_version()
            .await
            .expect("[start sync] failed to fetch latest version from storage");

        let mut interval =
            Interval::new_interval(Duration::from_millis(self.config.tick_interval_ms))
                .compat()
                .fuse();

        loop {
            ::futures::select! {
                msg = self.client_events.select_next_some() => {
                    match msg {
                        CoordinatorMessage::Requested(target, subscription) => {
                            self.request_sync(target, subscription).await;
                        }
                        CoordinatorMessage::Commit(version) => {
                             self.commit(version).await;
                        }
                        CoordinatorMessage::GetState(callback) => {
                            self.get_state(callback);
                        }
                    };
                },
                network_event = self.network_events.select_next_some() => {
                    match network_event {
                        Ok(event) => {
                            match event {
                                Event::NewPeer(peer_id) => {
                                    if self.autosync {
                                        self.peers.insert(peer_id, ());
                                        self.check_progress().await;
                                    }
                                }
                                Event::LostPeer(peer_id) => {
                                    self.peers.remove(&peer_id);
                                }
                                Event::Message((peer_id, mut message)) => {
                                    if message.has_chunk_request() {
                                        if let Err(err) = self.process_chunk_request(peer_id, message.take_chunk_request()).await {
                                            error!("[state sync] failed to serve chunk request: {:?}", err);
                                        }
                                    }
                                    if message.has_chunk_response() {
                                        self.process_chunk_response(message.take_chunk_response()).await;
                                    }
                                }
                                _ => {}
                            }
                        },
                        Err(err) => { error!("[state sync] network error {:?}", err); },
                    }
                },
                _ = interval.select_next_some() => {
                    self.check_progress().await;
                }
            }
        }
    }

    fn target_version(&self) -> u64 {
        match &self.target {
            Some(target) => target.ledger_info().version(),
            None => 0,
        }
    }

    async fn request_sync(&mut self, target: LedgerInfo, callback: oneshot::Sender<bool>) {
        let requested_version = target.ledger_info().version();
        self.known_version = self
            .executor_proxy
            .get_latest_version()
            .await
            .expect("[state sync] failed to fetch latest version from storage");

        // if requested version equals to current committed, just pass ledger info to executor
        // there might be still empty blocks between committed state and requested
        if requested_version <= self.known_version {
            self.store_transactions(target.clone(), TransactionListWithProof::new())
                .await
                .expect("[state sync] failed to execute empty blocks");
            if callback.send(true).is_err() {
                error!("[state sync] coordinator failed to notify subscriber");
            }
            return;
        }

        if requested_version > self.target_version() {
            self.peers = target.signatures().keys().map(|p| (*p, ())).collect();
            self.target = Some(target);
            self.request_next_chunk(0).await;
        }
        self.callback = Some(callback);
    }

    async fn commit(&mut self, version: u64) {
        if version <= self.known_version {
            error!(
                "[state sync] invalid commit. Known version: {}, commit version: {}",
                self.known_version, version
            );
            return;
        }
        self.known_version = version;
        if let Err(err) = self.check_subscriptions().await {
            error!("[state sync] failed to check subscriptions: {:?}", err);
        }
        if self.known_version == self.target_version() {
            if let Some(cb) = self.callback.take() {
                if cb.send(true).is_err() {
                    error!("[state sync] failed to notify subscriber");
                }
            }
        }
    }

    fn get_state(&self, callback: oneshot::Sender<u64>) {
        if callback.send(self.known_version).is_err() {
            error!("[state sync] failed to fetch internal state");
        }
    }

    /// Get a batch of transactions
    async fn process_chunk_request(
        &mut self,
        peer_id: PeerId,
        mut request: GetChunkRequest,
    ) -> Result<()> {
        if request.timeout > self.config.max_timeout_ms
            || request.limit > self.config.max_chunk_limit
        {
            return Err(format_err!(
                "[state sync] timeout: {:?}, chunk limit: {:?}, but timeout must not exceed {:?} ms, and chunk limit must not exceed {:?}",
                request.timeout,
                request.limit,
                self.config.max_timeout_ms,
                self.config.max_chunk_limit
            ));
        }

        let latest_ledger_info = self.executor_proxy.get_latest_ledger_info().await?;
        let target = LedgerInfo::from_proto(request.take_ledger_info_with_sigs())
            .unwrap_or(latest_ledger_info);

        // if upstream synchronizer doesn't have new data and request timeout is set
        // add peer request into subscription queue
        if self.known_version <= request.known_version && request.timeout > 0 {
            let expiration_time =
                SystemTime::now().checked_add(Duration::from_millis(request.timeout));
            if let Some(time) = expiration_time {
                self.subscriptions
                    .insert(peer_id, (time, request.known_version, request.limit));
            }
            Ok(())
        } else {
            self.deliver_chunk(
                peer_id,
                request.known_version,
                request.limit,
                target,
                self.network_sender.clone(),
            )
            .await
        }
    }

    async fn deliver_chunk(
        &self,
        peer_id: PeerId,
        known_version: u64,
        limit: u64,
        target: LedgerInfo,
        mut network_sender: StateSynchronizerSender,
    ) -> Result<()> {
        let response = self
            .executor_proxy
            .get_chunk(known_version, limit, target)
            .await?;
        let mut msg = StateSynchronizerMsg::new();
        msg.set_chunk_response(response);
        if network_sender.send_to(peer_id, msg).await.is_err() {
            error!("[state sync] failed to send p2p message");
        }
        Ok(())
    }

    /// processes batch of transactions downloaded from peer
    /// executes transactions, updates progress state, calls callback if some sync is finished
    async fn process_chunk_response(&mut self, mut response: GetChunkResponse) {
        let txn_list_with_proof = response.take_txn_list_with_proof();
        // optimistically fetch next chunk
        let chunk_size = txn_list_with_proof.get_transactions().len() as u64;
        self.request_next_chunk(chunk_size).await;

        match LedgerInfoWithSignatures::<Ed25519Signature>::from_proto(
            response.take_ledger_info_with_sigs(),
        ) {
            Ok(target) => {
                if let Err(err) = self.store_transactions(target, txn_list_with_proof).await {
                    error!("[state sync] failed to apply chunk {:?}", err);
                }
                counters::STATE_SYNC_TXN_REPLAYED.inc_by(chunk_size as i64);
                match self.executor_proxy.get_latest_version().await {
                    Ok(version) => {
                        self.commit(version).await;
                    }
                    Err(err) => {
                        error!("[state sync] storage version read failed {:?}", err);
                    }
                }
            }
            Err(err) => {
                error!("[state sync] invalid ledger info {:?}", err);
            }
        }
    }

    /// ensures that StateSynchronizer makes progress
    /// if peer is not responding, issues new sync request
    async fn check_progress(&mut self) {
        if !self.peers.is_empty() && (self.autosync || self.target.is_some()) {
            let timestamp = self.next_sync.unwrap_or(UNIX_EPOCH);
            if SystemTime::now().duration_since(timestamp).is_ok() {
                self.request_next_chunk(0).await;
            }
        }
    }

    async fn request_next_chunk(&mut self, offset: u64) {
        if self.autosync || self.known_version + offset < self.target_version() {
            let idx = thread_rng().gen_range(0, self.peers.len());
            let peer_id = self
                .peers
                .keys()
                .nth(idx)
                .cloned()
                .expect("[state synchronizer] failed to pick peer from ledger info");

            let mut req = GetChunkRequest::new();
            req.set_known_version(self.known_version + offset);
            req.set_limit(self.config.chunk_limit);
            let timeout = match &self.target {
                Some(target) => {
                    req.set_ledger_info_with_sigs(target.clone().into_proto());
                    0
                }
                None => {
                    req.set_timeout(self.config.long_poll_timeout_ms);
                    self.config.long_poll_timeout_ms
                }
            };

            let mut msg = StateSynchronizerMsg::new();
            msg.set_chunk_request(req);

            self.next_sync = SystemTime::now().checked_add(Duration::from_millis(
                timeout + self.config.tick_interval_ms,
            ));
            if self.network_sender.send_to(peer_id, msg).await.is_err() {
                error!("[state sync] failed to send p2p message");
            }
        }
    }

    async fn store_transactions(
        &self,
        ledger_info: LedgerInfoWithSignatures<Ed25519Signature>,
        txn_list_with_proof: TransactionListWithProof,
    ) -> Result<ExecuteChunkResponse> {
        let mut req = ExecuteChunkRequest::new();
        req.set_txn_list_with_proof(txn_list_with_proof);
        req.set_ledger_info_with_sigs(ledger_info.into_proto());
        self.executor_proxy.execute_chunk(req).await
    }

    async fn check_subscriptions(&mut self) -> Result<()> {
        let ledger_info = self.executor_proxy.get_latest_ledger_info().await?;
        let committed_version = self.known_version;
        let mut ready = vec![];

        self.subscriptions
            .retain(|peer_id, (expiry, known_version, limit)| {
                // filter out expired peer requests
                if SystemTime::now().duration_since(expiry.clone()).is_ok() {
                    return false;
                }
                if *known_version < committed_version {
                    ready.push((*peer_id, *known_version, *limit));
                    false
                } else {
                    true
                }
            });

        let mut futures: FuturesUnordered<_> = ready
            .into_iter()
            .map(|(peer_id, known_version, limit)| {
                self.deliver_chunk(
                    peer_id,
                    known_version,
                    limit,
                    ledger_info.clone(),
                    self.network_sender.clone(),
                )
            })
            .collect();
        while let Some(res) = futures.next().await {
            if let Err(err) = res {
                error!("[state sync] failed to notify subscriber {:?}", err);
            }
        }
        Ok(())
    }
}
<commit_msg>[state sync] add logging<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{counters, executor_proxy::ExecutorProxyTrait, LedgerInfo, PeerId};
use config::config::{NodeConfig, RoleType, StateSyncConfig};
use crypto::ed25519::*;
use execution_proto::proto::execution::{ExecuteChunkRequest, ExecuteChunkResponse};
use failure::prelude::*;
use futures::{
    channel::{mpsc, oneshot},
    compat::Stream01CompatExt,
    stream::{futures_unordered::FuturesUnordered, Fuse},
    StreamExt,
};
use logger::prelude::*;
use network::{
    proto::{GetChunkRequest, GetChunkResponse, StateSynchronizerMsg},
    validator_network::{Event, StateSynchronizerEvents, StateSynchronizerSender},
};
use proto_conv::{FromProto, IntoProto};
use rand::{thread_rng, Rng};
use std::{
    collections::HashMap,
    time::{Duration, SystemTime, UNIX_EPOCH},
};
use tokio::timer::Interval;
use types::{ledger_info::LedgerInfoWithSignatures, proto::transaction::TransactionListWithProof};

/// message used by StateSyncClient for communication with Coordinator
pub enum CoordinatorMessage {
    // used to initiate new sync
    Requested(LedgerInfo, oneshot::Sender<bool>),
    // used to notify about new txn commit
    Commit(u64),
    GetState(oneshot::Sender<u64>),
}

/// used to coordinate synchronization process
/// handles external sync requests and drives synchronization with remote peers
pub(crate) struct SyncCoordinator<T> {
    // used for interaction with remote peers
    network_sender: StateSynchronizerSender,
    // used for receiving events from peers
    network_events: Fuse<StateSynchronizerEvents>,
    // used to process client requests
    client_events: mpsc::UnboundedReceiver<CoordinatorMessage>,

    // last committed version that validator is aware of
    known_version: u64,
    // target state to sync to
    target: Option<LedgerInfo>,
    // config
    config: StateSyncConfig,
    // if autosync is on, StateSynchronizer will keep fetching chunks from upstream peers
    // even if no target state was specified
    autosync: bool,
    // peers used for synchronization. TBD: value is meta information about peer sync quality
    peers: HashMap<PeerId, ()>,
    // option callback. Called when state sync reaches target version
    callback: Option<oneshot::Sender<bool>>,
    // timestamp of next peer sync request
    next_sync: Option<SystemTime>,
    // queue of incoming long polling requests
    // peer will be notified about new chunk of transactions if it's available before expiry time
    // value format is (expiration_time, known_version, limit)
    subscriptions: HashMap<PeerId, (SystemTime, u64, u64)>,
    executor_proxy: T,
}

impl<T: ExecutorProxyTrait> SyncCoordinator<T> {
    pub fn new(
        network_sender: StateSynchronizerSender,
        network_events: StateSynchronizerEvents,
        client_events: mpsc::UnboundedReceiver<CoordinatorMessage>,
        node_config: &NodeConfig,
        executor_proxy: T,
    ) -> Self {
        Self {
            network_sender,
            network_events: network_events.fuse(),
            client_events,

            known_version: 0,
            target: None,
            config: node_config.state_sync.clone(),
            autosync: node_config.base.get_role() == RoleType::FullNode,
            peers: HashMap::new(),
            subscriptions: HashMap::new(),
            callback: None,
            next_sync: None,
            executor_proxy,
        }
    }

    /// main routine. starts sync coordinator that listens for CoordinatorMsg
    pub async fn start(mut self) {
        self.known_version = self
            .executor_proxy
            .get_latest_version()
            .await
            .expect("[start sync] failed to fetch latest version from storage");

        let mut interval =
            Interval::new_interval(Duration::from_millis(self.config.tick_interval_ms))
                .compat()
                .fuse();

        loop {
            ::futures::select! {
                msg = self.client_events.select_next_some() => {
                    match msg {
                        CoordinatorMessage::Requested(target, subscription) => {
                            self.request_sync(target, subscription).await;
                        }
                        CoordinatorMessage::Commit(version) => {
                             self.commit(version).await;
                        }
                        CoordinatorMessage::GetState(callback) => {
                            self.get_state(callback);
                        }
                    };
                },
                network_event = self.network_events.select_next_some() => {
                    match network_event {
                        Ok(event) => {
                            match event {
                                Event::NewPeer(peer_id) => {
                                    if self.autosync {
                                        self.peers.insert(peer_id, ());
                                        self.check_progress().await;
                                    }
                                }
                                Event::LostPeer(peer_id) => {
                                    self.peers.remove(&peer_id);
                                }
                                Event::Message((peer_id, mut message)) => {
                                    if message.has_chunk_request() {
                                        if let Err(err) = self.process_chunk_request(peer_id, message.take_chunk_request()).await {
                                            error!("[state sync] failed to serve chunk request: {:?}", err);
                                        }
                                    }
                                    if message.has_chunk_response() {
                                        self.process_chunk_response(message.take_chunk_response()).await;
                                    }
                                }
                                _ => {}
                            }
                        },
                        Err(err) => { error!("[state sync] network error {:?}", err); },
                    }
                },
                _ = interval.select_next_some() => {
                    self.check_progress().await;
                }
            }
        }
    }

    fn target_version(&self) -> u64 {
        match &self.target {
            Some(target) => target.ledger_info().version(),
            None => 0,
        }
    }

    async fn request_sync(&mut self, target: LedgerInfo, callback: oneshot::Sender<bool>) {
        let requested_version = target.ledger_info().version();
        self.known_version = self
            .executor_proxy
            .get_latest_version()
            .await
            .expect("[state sync] failed to fetch latest version from storage");

        debug!(
            "[state sync] sync requested. Known version: {}, requested_version: {}",
            self.known_version, requested_version
        );

        // if requested version equals to current committed, just pass ledger info to executor
        // there might be still empty blocks between committed state and requested
        if requested_version <= self.known_version {
            debug!("[state sync] sync contains only empty blocks");
            self.store_transactions(target.clone(), TransactionListWithProof::new())
                .await
                .expect("[state sync] failed to execute empty blocks");
            if callback.send(true).is_err() {
                error!("[state sync] coordinator failed to notify subscriber");
            }
            return;
        }

        if requested_version > self.target_version() {
            self.peers = target.signatures().keys().map(|p| (*p, ())).collect();
            self.target = Some(target);
            self.request_next_chunk(0).await;
        }
        self.callback = Some(callback);
    }

    async fn commit(&mut self, version: u64) {
        debug!(
            "[state sync] commit. Known version: {}, version: {}",
            self.known_version, version
        );
        let is_update = version > self.known_version;
        self.known_version = std::cmp::max(version, self.known_version);
        if is_update {
            if let Err(err) = self.check_subscriptions().await {
                error!("[state sync] failed to check subscriptions: {:?}", err);
            }
        }
        if self.known_version == self.target_version() {
            debug!("[state sync] synchronization is finished");
            if let Some(cb) = self.callback.take() {
                if cb.send(true).is_err() {
                    error!("[state sync] failed to notify subscriber");
                }
            }
        }
    }

    fn get_state(&self, callback: oneshot::Sender<u64>) {
        if callback.send(self.known_version).is_err() {
            error!("[state sync] failed to fetch internal state");
        }
    }

    /// Get a batch of transactions
    async fn process_chunk_request(
        &mut self,
        peer_id: PeerId,
        mut request: GetChunkRequest,
    ) -> Result<()> {
        if request.timeout > self.config.max_timeout_ms
            || request.limit > self.config.max_chunk_limit
        {
            return Err(format_err!(
                "[state sync] timeout: {:?}, chunk limit: {:?}, but timeout must not exceed {:?} ms, and chunk limit must not exceed {:?}",
                request.timeout,
                request.limit,
                self.config.max_timeout_ms,
                self.config.max_chunk_limit
            ));
        }

        let latest_ledger_info = self.executor_proxy.get_latest_ledger_info().await?;
        let target = LedgerInfo::from_proto(request.take_ledger_info_with_sigs())
            .unwrap_or_else(|_| latest_ledger_info.clone());
        debug!("[state sync] chunk request: peer_id: {:?}, known_version: {}, latest_ledger_info: {}, target: {}", peer_id, request.known_version, latest_ledger_info.ledger_info().version(), target.ledger_info().version());

        // if upstream synchronizer doesn't have new data and request timeout is set
        // add peer request into subscription queue
        if self.known_version <= request.known_version && request.timeout > 0 {
            let expiration_time =
                SystemTime::now().checked_add(Duration::from_millis(request.timeout));
            if let Some(time) = expiration_time {
                self.subscriptions
                    .insert(peer_id, (time, request.known_version, request.limit));
            }
            Ok(())
        } else {
            self.deliver_chunk(
                peer_id,
                request.known_version,
                request.limit,
                target,
                self.network_sender.clone(),
            )
            .await
        }
    }

    async fn deliver_chunk(
        &self,
        peer_id: PeerId,
        known_version: u64,
        limit: u64,
        target: LedgerInfo,
        mut network_sender: StateSynchronizerSender,
    ) -> Result<()> {
        let response = self
            .executor_proxy
            .get_chunk(known_version, limit, target)
            .await?;
        let mut msg = StateSynchronizerMsg::new();
        msg.set_chunk_response(response);
        if network_sender.send_to(peer_id, msg).await.is_err() {
            error!("[state sync] failed to send p2p message");
        }
        Ok(())
    }

    /// processes batch of transactions downloaded from peer
    /// executes transactions, updates progress state, calls callback if some sync is finished
    async fn process_chunk_response(&mut self, mut response: GetChunkResponse) {
        let txn_list_with_proof = response.take_txn_list_with_proof();
        // optimistically fetch next chunk
        let chunk_size = txn_list_with_proof.get_transactions().len() as u64;
        self.request_next_chunk(chunk_size).await;
        debug!(
            "[state sync] process chunk response. chunk_size: {}",
            chunk_size
        );

        match LedgerInfoWithSignatures::<Ed25519Signature>::from_proto(
            response.take_ledger_info_with_sigs(),
        ) {
            Ok(target) => {
                if let Err(err) = self.store_transactions(target, txn_list_with_proof).await {
                    error!("[state sync] failed to apply chunk {:?}", err);
                }
                counters::STATE_SYNC_TXN_REPLAYED.inc_by(chunk_size as i64);
                match self.executor_proxy.get_latest_version().await {
                    Ok(version) => {
                        self.commit(version).await;
                    }
                    Err(err) => {
                        error!("[state sync] storage version read failed {:?}", err);
                    }
                }
            }
            Err(err) => {
                error!("[state sync] invalid ledger info {:?}", err);
            }
        }
    }

    /// ensures that StateSynchronizer makes progress
    /// if peer is not responding, issues new sync request
    async fn check_progress(&mut self) {
        if !self.peers.is_empty() && (self.autosync || self.target.is_some()) {
            let timestamp = self.next_sync.unwrap_or(UNIX_EPOCH);
            if SystemTime::now().duration_since(timestamp).is_ok() {
                self.request_next_chunk(0).await;
            }
        }
    }

    async fn request_next_chunk(&mut self, offset: u64) {
        if self.autosync || self.known_version + offset < self.target_version() {
            let idx = thread_rng().gen_range(0, self.peers.len());
            let peer_id = self
                .peers
                .keys()
                .nth(idx)
                .cloned()
                .expect("[state synchronizer] failed to pick peer from ledger info");

            let mut req = GetChunkRequest::new();
            req.set_known_version(self.known_version + offset);
            req.set_limit(self.config.chunk_limit);
            let timeout = match &self.target {
                Some(target) => {
                    req.set_ledger_info_with_sigs(target.clone().into_proto());
                    0
                }
                None => {
                    req.set_timeout(self.config.long_poll_timeout_ms);
                    self.config.long_poll_timeout_ms
                }
            };
            debug!(
                "[state sync] request next chunk. peer_id: {:?}, known_version: {}, timeout: {}",
                peer_id,
                self.known_version + offset,
                timeout
            );

            let mut msg = StateSynchronizerMsg::new();
            msg.set_chunk_request(req);

            self.next_sync = SystemTime::now().checked_add(Duration::from_millis(
                timeout + self.config.tick_interval_ms,
            ));
            if self.network_sender.send_to(peer_id, msg).await.is_err() {
                error!("[state sync] failed to send p2p message");
            }
        }
    }

    async fn store_transactions(
        &self,
        ledger_info: LedgerInfoWithSignatures<Ed25519Signature>,
        txn_list_with_proof: TransactionListWithProof,
    ) -> Result<ExecuteChunkResponse> {
        let mut req = ExecuteChunkRequest::new();
        req.set_txn_list_with_proof(txn_list_with_proof);
        req.set_ledger_info_with_sigs(ledger_info.into_proto());
        self.executor_proxy.execute_chunk(req).await
    }

    async fn check_subscriptions(&mut self) -> Result<()> {
        let ledger_info = self.executor_proxy.get_latest_ledger_info().await?;
        let committed_version = self.known_version;
        let mut ready = vec![];

        self.subscriptions
            .retain(|peer_id, (expiry, known_version, limit)| {
                // filter out expired peer requests
                if SystemTime::now().duration_since(expiry.clone()).is_ok() {
                    return false;
                }
                if *known_version < committed_version {
                    ready.push((*peer_id, *known_version, *limit));
                    false
                } else {
                    true
                }
            });

        let mut futures: FuturesUnordered<_> = ready
            .into_iter()
            .map(|(peer_id, known_version, limit)| {
                self.deliver_chunk(
                    peer_id,
                    known_version,
                    limit,
                    ledger_info.clone(),
                    self.network_sender.clone(),
                )
            })
            .collect();
        while let Some(res) = futures.next().await {
            if let Err(err) = res {
                error!("[state sync] failed to notify subscriber {:?}", err);
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use syn;
use proc_macro2::{TokenStream, Span};
use sha1::Sha1;

use utils::dummy_idents;

#[derive(Clone, Serialize, Deserialize, Debug)]
struct Snippet {
    name: String,
    code: String,
    arg_count: usize
}

fn hash( string: &str ) -> String {
    let mut hasher = Sha1::new();
    hasher.update( string.as_bytes() );
    format!( "{}", hasher.digest() )
}

pub fn js_shim_extern_code( code: &str, arg_count: usize ) -> (syn::Ident, TokenStream) {
    let snippet = Snippet {
        name: format!( "__cargo_web_snippet_{}", hash( code ) ),
        code: code.to_owned(),
        arg_count
    };

    let blob: Vec< u8 > = serde_json::to_string( &snippet ).unwrap().into_bytes();
    let blob_length = blob.len();

    let code_bytes = syn::LitByteStr::new( format!( "{}\0", code ).as_str().as_bytes(), Span::call_site() );

    let shim_name = syn::Ident::new( &snippet.name, Span::call_site() );
    let shim_args: Vec< _ > = dummy_idents( arg_count ).map( |name| quote! { #name: *const u8 } ).collect();
    let shim_args = &shim_args;
    let shim_args_passthrough = dummy_idents( arg_count ).map( |name| quote! { #name } );
    let output = quote! {
        #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
        #[link_section = "cargo-web-js"]
        static SNIPPET: [u8; #blob_length] = [#(#blob),*];

        #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
        extern "C" {
            pub fn #shim_name( #(#shim_args),* ) -> i32;
        }

        #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
        const SNIPPET: &'static [u8] = #code_bytes;

        #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
        fn #shim_name( #(#shim_args),* ) -> i32 {
            extern "C" {
                pub fn emscripten_asm_const_int( code: *const u8, ... ) -> i32;
            }
            unsafe {
                emscripten_asm_const_int( SNIPPET as *const _ as *const u8, #(#shim_args_passthrough),* )
            }
        }
    };

    (shim_name, output)
}
<commit_msg>Do not use custom sections after all<commit_after>use syn;
use proc_macro2::{TokenStream, Span};
use sha1::Sha1;

use utils::dummy_idents;

fn hash( string: &str ) -> String {
    let mut hasher = Sha1::new();
    hasher.update( string.as_bytes() );
    format!( "{}", hasher.digest() )
}

pub fn js_shim_extern_code( code: &str, arg_count: usize ) -> (syn::Ident, TokenStream) {
    let name = format!( "__cargo_web_snippet_{}", hash( code ) );
    let code_bytes = syn::LitByteStr::new( format!( "{}\0", code ).as_str().as_bytes(), Span::call_site() );

    let shim_name = syn::Ident::new( &name, Span::call_site() );
    let shim_args: Vec< _ > = dummy_idents( arg_count ).map( |name| quote! { #name: *const u8 } ).collect();
    let shim_args = &shim_args;
    let shim_args_passthrough: Vec< _ > = dummy_idents( arg_count ).map( |name| quote! { #name } ).collect();
    let shim_args_passthrough = &shim_args_passthrough;

    let asm_fn_name = syn::Ident::new( &format!( "__js_{}", arg_count ), Span::call_site() );
    let asm_fn_name = &asm_fn_name;

    let output = quote! {
        const SNIPPET: &'static [u8] = #code_bytes;

        #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
        fn #shim_name( #(#shim_args),* ) -> i32 {
            extern "C" {
                pub fn #asm_fn_name( #(#shim_args,)* code: *const u8 ) -> i32;
            }
            unsafe {
                #asm_fn_name( #(#shim_args_passthrough,)* SNIPPET as *const _ as *const u8 )
            }
        }

        #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
        fn #shim_name( #(#shim_args),* ) -> i32 {
            extern "C" {
                pub fn emscripten_asm_const_int( code: *const u8, ... ) -> i32;
            }
            unsafe {
                emscripten_asm_const_int( SNIPPET as *const _ as *const u8, #(#shim_args_passthrough),* )
            }
        }
    };

    (shim_name, output)
}
<|endoftext|>


<commit_before>use crate::modules::*;
use criterion::Criterion;
use lucet_runtime::{DlModule, InstanceHandle, Limits, Module, Region, RegionCreate};
use lucet_wasi::WasiCtxBuilder;
use std::path::Path;
use std::sync::Arc;
use tempfile::TempDir;

/// End-to-end instance instantiation.
///
/// This is meant to simulate our startup time when we start from scratch, with no module loaded and
/// no region created at all. This would be unusual for a server application, but reflects what
/// one-shot command-line tools like `lucet-wasi` do.
///
/// To minimize the effects of filesystem cache on the `DlModule::load()`, this runs `sync` between
/// each iteration.
fn load_mkregion_and_instantiate<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body<R: RegionCreate + 'static>(so_file: &Path) -> InstanceHandle {
        let module = DlModule::load(so_file).unwrap();
        let region = R::create(1, &Limits::default()).unwrap();
        region.new_instance(module).unwrap()
    }

    let workdir = TempDir::new().expect("create working directory");

    let so_file = workdir.path().join("out.so");
    compile_hello(&so_file);

    c.bench_function(
        &format!("load_mkregion_and_instantiate ({})", R::TYPE_NAME),
        move |b| {
            b.iter_batched(
                || nix::unistd::sync(),
                |_| body::<R>(&so_file),
                criterion::BatchSize::PerIteration,
            )
        },
    );

    workdir.close().unwrap();
}

/// Instance instantiation.
///
/// This simulates a typical case for a server process like Terrarium: the region and module stay
/// initialized, but a new instance is created for each request.
fn instantiate<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body<R: Region>(module: Arc<dyn Module>, region: Arc<R>) -> InstanceHandle {
        region.new_instance(module).unwrap()
    }

    let workdir = TempDir::new().expect("create working directory");

    let so_file = workdir.path().join("out.so");
    compile_hello(&so_file);

    let module = DlModule::load(&so_file).unwrap();
    let region = R::create(1, &Limits::default()).unwrap();

    c.bench_function(&format!("instantiate ({})", R::TYPE_NAME), move |b| {
        b.iter(|| body(module.clone(), region.clone()))
    });

    workdir.close().unwrap();
}

/// Instance instantiation with a large, dense heap.
fn instantiate_large_dense<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body<R: Region>(module: Arc<dyn Module>, region: Arc<R>) -> InstanceHandle {
        region.new_instance(module).unwrap()
    }

    let module = large_dense_heap_mock();

    let limits = Limits {
        heap_memory_size: 1024 * 1024 * 1024,
        ..Limits::default()
    };

    let region = R::create(1, &limits).unwrap();

    c.bench_function(
        &format!("instantiate_large_dense ({})", R::TYPE_NAME),
        move |b| b.iter(|| body(module.clone(), region.clone())),
    );
}

/// Instance instantiation with a large, sparse heap.
fn instantiate_large_sparse<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body<R: Region>(module: Arc<dyn Module>, region: Arc<R>) -> InstanceHandle {
        region.new_instance(module).unwrap()
    }

    let module = large_sparse_heap_mock();

    let limits = Limits {
        heap_memory_size: 1024 * 1024 * 1024,
        ..Limits::default()
    };

    let region = R::create(1, &limits).unwrap();

    c.bench_function(
        &format!("instantiate_large_sparse ({})", R::TYPE_NAME),
        move |b| b.iter(|| body(module.clone(), region.clone())),
    );
}

/// Instance destruction.
///
/// Instances have some cleanup to do with memory management and freeing their slot on their region.
fn drop_instance<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(_inst: InstanceHandle) {}

    let workdir = TempDir::new().expect("create working directory");

    let so_file = workdir.path().join("out.so");
    compile_hello(&so_file);

    let module = DlModule::load(&so_file).unwrap();
    let region = R::create(1, &Limits::default()).unwrap();

    c.bench_function(&format!("drop_instance ({})", R::TYPE_NAME), move |b| {
        b.iter_batched(
            || region.new_instance(module.clone()).unwrap(),
            |inst| body(inst),
            criterion::BatchSize::PerIteration,
        )
    });

    workdir.close().unwrap();
}

/// Instance destruction with a large, dense heap.
fn drop_instance_large_dense<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(_inst: InstanceHandle) {}

    let limits = Limits {
        heap_memory_size: 1024 * 1024 * 1024,
        ..Limits::default()
    };

    let module = large_dense_heap_mock();
    let region = R::create(1, &limits).unwrap();

    c.bench_function(
        &format!("drop_instance_large_dense ({})", R::TYPE_NAME),
        move |b| {
            b.iter_batched(
                || region.new_instance(module.clone()).unwrap(),
                |inst| body(inst),
                criterion::BatchSize::PerIteration,
            )
        },
    );
}

/// Instance destruction with a large, sparse heap.
fn drop_instance_large_sparse<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(_inst: InstanceHandle) {}

    let limits = Limits {
        heap_memory_size: 1024 * 1024 * 1024,
        ..Limits::default()
    };

    let module = large_sparse_heap_mock();
    let region = R::create(1, &limits).unwrap();

    c.bench_function(
        &format!("drop_instance_large_sparse ({})", R::TYPE_NAME),
        move |b| {
            b.iter_batched(
                || region.new_instance(module.clone()).unwrap(),
                |inst| body(inst),
                criterion::BatchSize::PerIteration,
            )
        },
    );
}

/// Run a trivial guest function.
///
/// This is primarily a measurement of the signal handler installation and removal, and the context
/// switching overhead.
fn run_null<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(inst: &mut InstanceHandle) {
        inst.run(b"f", &[]).unwrap();
    }

    let module = null_mock();
    let region = R::create(1, &Limits::default()).unwrap();

    c.bench_function(&format!("run_null ({})", R::TYPE_NAME), move |b| {
        b.iter_batched_ref(
            || region.new_instance(module.clone()).unwrap(),
            |inst| body(inst),
            criterion::BatchSize::PerIteration,
        )
    });
}

/// Run a computation-heavy guest function from a mock module.
///
/// Since this is running code in a mock module, the cost of the computation should overwhelm the
/// cost of the Lucet runtime.
fn run_fib<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(inst: &mut InstanceHandle) {
        inst.run(b"f", &[]).unwrap();
    }

    let module = fib_mock();
    let region = R::create(1, &Limits::default()).unwrap();

    c.bench_function(&format!("run_fib ({})", R::TYPE_NAME), move |b| {
        b.iter_batched_ref(
            || region.new_instance(module.clone()).unwrap(),
            |inst| body(inst),
            criterion::BatchSize::PerIteration,
        )
    });
}

/// Run a trivial WASI program.
fn run_hello<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(inst: &mut InstanceHandle) {
        inst.run(b"_start", &[]).unwrap();
    }

    let workdir = TempDir::new().expect("create working directory");

    let so_file = workdir.path().join("out.so");
    compile_hello(&so_file);

    let module = DlModule::load(&so_file).unwrap();
    let region = R::create(1, &Limits::default()).unwrap();

    c.bench_function(&format!("run_hello ({})", R::TYPE_NAME), move |b| {
        b.iter_batched_ref(
            || {
                let null = std::fs::File::open("/dev/null").unwrap();
                let ctx = WasiCtxBuilder::new()
                    .args(&["hello"])
                    .fd(1, null)
                    .build()
                    .unwrap();
                region
                    .new_instance_builder(module.clone())
                    .with_embed_ctx(ctx)
                    .build()
                    .unwrap()
            },
            |inst| body(inst),
            criterion::BatchSize::PerIteration,
        )
    });
}

/// Run a trivial guest function that takes a bunch of arguments.
///
/// This is primarily interesting as a comparison to `run_null`; the difference is the overhead of
/// installing the arguments into the guest registers and stack.
///
/// `rustfmt` hates this function.
fn run_many_args<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(inst: &mut InstanceHandle) {
        inst.run(
            b"f",
            &[
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
            ],
        )
        .unwrap();
    }

    let module = many_args_mock();
    let region = R::create(1, &Limits::default()).unwrap();

    c.bench_function(&format!("run_many_args ({})", R::TYPE_NAME), move |b| {
        b.iter_batched_ref(
            || region.new_instance(module.clone()).unwrap(),
            |inst| body(inst),
            criterion::BatchSize::PerIteration,
        )
    });
}

pub fn seq_benches<R: RegionCreate + 'static>(c: &mut Criterion) {
    load_mkregion_and_instantiate::<R>(c);
    instantiate::<R>(c);
    instantiate_large_dense::<R>(c);
    instantiate_large_sparse::<R>(c);
    drop_instance::<R>(c);
    drop_instance_large_dense::<R>(c);
    drop_instance_large_sparse::<R>(c);
    run_null::<R>(c);
    run_fib::<R>(c);
    run_hello::<R>(c);
    run_many_args::<R>(c);
}
<commit_msg>For some reason, nix forgot to define unistd::sync() on macOS<commit_after>use crate::modules::*;
use criterion::Criterion;
use lucet_runtime::{DlModule, InstanceHandle, Limits, Module, Region, RegionCreate};
use lucet_wasi::WasiCtxBuilder;
use std::path::Path;
use std::sync::Arc;
use tempfile::TempDir;

/// End-to-end instance instantiation.
///
/// This is meant to simulate our startup time when we start from scratch, with no module loaded and
/// no region created at all. This would be unusual for a server application, but reflects what
/// one-shot command-line tools like `lucet-wasi` do.
///
/// To minimize the effects of filesystem cache on the `DlModule::load()`, this runs `sync` between
/// each iteration.
fn load_mkregion_and_instantiate<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body<R: RegionCreate + 'static>(so_file: &Path) -> InstanceHandle {
        let module = DlModule::load(so_file).unwrap();
        let region = R::create(1, &Limits::default()).unwrap();
        region.new_instance(module).unwrap()
    }

    let workdir = TempDir::new().expect("create working directory");

    let so_file = workdir.path().join("out.so");
    compile_hello(&so_file);

    c.bench_function(
        &format!("load_mkregion_and_instantiate ({})", R::TYPE_NAME),
        move |b| {
            b.iter_batched(
                || unsafe { nix::libc::sync() },
                |_| body::<R>(&so_file),
                criterion::BatchSize::PerIteration,
            )
        },
    );

    workdir.close().unwrap();
}

/// Instance instantiation.
///
/// This simulates a typical case for a server process like Terrarium: the region and module stay
/// initialized, but a new instance is created for each request.
fn instantiate<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body<R: Region>(module: Arc<dyn Module>, region: Arc<R>) -> InstanceHandle {
        region.new_instance(module).unwrap()
    }

    let workdir = TempDir::new().expect("create working directory");

    let so_file = workdir.path().join("out.so");
    compile_hello(&so_file);

    let module = DlModule::load(&so_file).unwrap();
    let region = R::create(1, &Limits::default()).unwrap();

    c.bench_function(&format!("instantiate ({})", R::TYPE_NAME), move |b| {
        b.iter(|| body(module.clone(), region.clone()))
    });

    workdir.close().unwrap();
}

/// Instance instantiation with a large, dense heap.
fn instantiate_large_dense<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body<R: Region>(module: Arc<dyn Module>, region: Arc<R>) -> InstanceHandle {
        region.new_instance(module).unwrap()
    }

    let module = large_dense_heap_mock();

    let limits = Limits {
        heap_memory_size: 1024 * 1024 * 1024,
        ..Limits::default()
    };

    let region = R::create(1, &limits).unwrap();

    c.bench_function(
        &format!("instantiate_large_dense ({})", R::TYPE_NAME),
        move |b| b.iter(|| body(module.clone(), region.clone())),
    );
}

/// Instance instantiation with a large, sparse heap.
fn instantiate_large_sparse<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body<R: Region>(module: Arc<dyn Module>, region: Arc<R>) -> InstanceHandle {
        region.new_instance(module).unwrap()
    }

    let module = large_sparse_heap_mock();

    let limits = Limits {
        heap_memory_size: 1024 * 1024 * 1024,
        ..Limits::default()
    };

    let region = R::create(1, &limits).unwrap();

    c.bench_function(
        &format!("instantiate_large_sparse ({})", R::TYPE_NAME),
        move |b| b.iter(|| body(module.clone(), region.clone())),
    );
}

/// Instance destruction.
///
/// Instances have some cleanup to do with memory management and freeing their slot on their region.
fn drop_instance<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(_inst: InstanceHandle) {}

    let workdir = TempDir::new().expect("create working directory");

    let so_file = workdir.path().join("out.so");
    compile_hello(&so_file);

    let module = DlModule::load(&so_file).unwrap();
    let region = R::create(1, &Limits::default()).unwrap();

    c.bench_function(&format!("drop_instance ({})", R::TYPE_NAME), move |b| {
        b.iter_batched(
            || region.new_instance(module.clone()).unwrap(),
            |inst| body(inst),
            criterion::BatchSize::PerIteration,
        )
    });

    workdir.close().unwrap();
}

/// Instance destruction with a large, dense heap.
fn drop_instance_large_dense<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(_inst: InstanceHandle) {}

    let limits = Limits {
        heap_memory_size: 1024 * 1024 * 1024,
        ..Limits::default()
    };

    let module = large_dense_heap_mock();
    let region = R::create(1, &limits).unwrap();

    c.bench_function(
        &format!("drop_instance_large_dense ({})", R::TYPE_NAME),
        move |b| {
            b.iter_batched(
                || region.new_instance(module.clone()).unwrap(),
                |inst| body(inst),
                criterion::BatchSize::PerIteration,
            )
        },
    );
}

/// Instance destruction with a large, sparse heap.
fn drop_instance_large_sparse<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(_inst: InstanceHandle) {}

    let limits = Limits {
        heap_memory_size: 1024 * 1024 * 1024,
        ..Limits::default()
    };

    let module = large_sparse_heap_mock();
    let region = R::create(1, &limits).unwrap();

    c.bench_function(
        &format!("drop_instance_large_sparse ({})", R::TYPE_NAME),
        move |b| {
            b.iter_batched(
                || region.new_instance(module.clone()).unwrap(),
                |inst| body(inst),
                criterion::BatchSize::PerIteration,
            )
        },
    );
}

/// Run a trivial guest function.
///
/// This is primarily a measurement of the signal handler installation and removal, and the context
/// switching overhead.
fn run_null<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(inst: &mut InstanceHandle) {
        inst.run(b"f", &[]).unwrap();
    }

    let module = null_mock();
    let region = R::create(1, &Limits::default()).unwrap();

    c.bench_function(&format!("run_null ({})", R::TYPE_NAME), move |b| {
        b.iter_batched_ref(
            || region.new_instance(module.clone()).unwrap(),
            |inst| body(inst),
            criterion::BatchSize::PerIteration,
        )
    });
}

/// Run a computation-heavy guest function from a mock module.
///
/// Since this is running code in a mock module, the cost of the computation should overwhelm the
/// cost of the Lucet runtime.
fn run_fib<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(inst: &mut InstanceHandle) {
        inst.run(b"f", &[]).unwrap();
    }

    let module = fib_mock();
    let region = R::create(1, &Limits::default()).unwrap();

    c.bench_function(&format!("run_fib ({})", R::TYPE_NAME), move |b| {
        b.iter_batched_ref(
            || region.new_instance(module.clone()).unwrap(),
            |inst| body(inst),
            criterion::BatchSize::PerIteration,
        )
    });
}

/// Run a trivial WASI program.
fn run_hello<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(inst: &mut InstanceHandle) {
        inst.run(b"_start", &[]).unwrap();
    }

    let workdir = TempDir::new().expect("create working directory");

    let so_file = workdir.path().join("out.so");
    compile_hello(&so_file);

    let module = DlModule::load(&so_file).unwrap();
    let region = R::create(1, &Limits::default()).unwrap();

    c.bench_function(&format!("run_hello ({})", R::TYPE_NAME), move |b| {
        b.iter_batched_ref(
            || {
                let null = std::fs::File::open("/dev/null").unwrap();
                let ctx = WasiCtxBuilder::new()
                    .args(&["hello"])
                    .fd(1, null)
                    .build()
                    .unwrap();
                region
                    .new_instance_builder(module.clone())
                    .with_embed_ctx(ctx)
                    .build()
                    .unwrap()
            },
            |inst| body(inst),
            criterion::BatchSize::PerIteration,
        )
    });
}

/// Run a trivial guest function that takes a bunch of arguments.
///
/// This is primarily interesting as a comparison to `run_null`; the difference is the overhead of
/// installing the arguments into the guest registers and stack.
///
/// `rustfmt` hates this function.
fn run_many_args<R: RegionCreate + 'static>(c: &mut Criterion) {
    fn body(inst: &mut InstanceHandle) {
        inst.run(
            b"f",
            &[
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
                0xAFu8.into(),
                0xAFu16.into(),
                0xAFu32.into(),
                0xAFu64.into(),
                175.0f32.into(),
                175.0f64.into(),
            ],
        )
        .unwrap();
    }

    let module = many_args_mock();
    let region = R::create(1, &Limits::default()).unwrap();

    c.bench_function(&format!("run_many_args ({})", R::TYPE_NAME), move |b| {
        b.iter_batched_ref(
            || region.new_instance(module.clone()).unwrap(),
            |inst| body(inst),
            criterion::BatchSize::PerIteration,
        )
    });
}

pub fn seq_benches<R: RegionCreate + 'static>(c: &mut Criterion) {
    load_mkregion_and_instantiate::<R>(c);
    instantiate::<R>(c);
    instantiate_large_dense::<R>(c);
    instantiate_large_sparse::<R>(c);
    drop_instance::<R>(c);
    drop_instance_large_dense::<R>(c);
    drop_instance_large_sparse::<R>(c);
    run_null::<R>(c);
    run_fib::<R>(c);
    run_hello::<R>(c);
    run_many_args::<R>(c);
}
<|endoftext|>


<commit_before>#![feature(path_ext)]

extern crate bindgen;
extern crate pkg_config;

use std::env;
use std::fs::PathExt;
use std::path::Path;
use std::process::Command;

static VERSION: &'static str = "1.1.10"; // Should be the same as the version in the manifest

fn main() {
    let out_dir     = env::var("OUT_DIR").unwrap();
    let current_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let driver_path = format!(
        "mongo-c-driver-{}",
        VERSION
    );

    let libmongoc_path = Path::new(&out_dir).join("lib/libmongoc-1.0.a");
    if !libmongoc_path.exists() { // TODO: This should check if we're at the right version
        // Download and extract driver archive
        let url = format!(
            "https://github.com/mongodb/mongo-c-driver/releases/download/{}/mongo-c-driver-{}.tar.gz",
            VERSION,
            VERSION
        );
        assert!(Command::new("curl").arg("-O") // Save to disk
                                    .arg("-L") // Follow redirects
                                    .arg(url)
                                    .status()
                                    .unwrap()
                                    .success());

        let archive_name = format!(
            "mongo-c-driver-{}.tar.gz",
            VERSION
        );
        assert!(Command::new("tar").arg("xzf")
                                   .arg(&archive_name)
                                   .status()
                                   .unwrap()
                                   .success());

        // Configure and install
        assert!(Command::new("sh").arg("configure")
                          .arg("--enable-ssl=yes")
                          .arg("--enable-sasl=no")
                          .arg("--enable-static=yes")
                          .arg("--enable-shared=no")
                          .arg("--with-libbson=bundled")
                          .arg(format!("--prefix={}", &out_dir))
                          .current_dir(&driver_path)
                          .status()
                          .unwrap()
                          .success());
        assert!(Command::new("make").current_dir(&driver_path).status().unwrap().success());
        assert!(Command::new("make").arg("install").current_dir(&driver_path).status().unwrap().success());

        // Generate bindings
        let bindings_rs_path = Path::new(&current_dir).join("src/bindings.rs");
        let mongo_h_path     = Path::new(&current_dir).join(&driver_path).join("src/mongoc/mongoc.h");
        let bson_path        = Path::new(&current_dir).join(&driver_path).join("src/libbson/src/bson");
        let mongo_h_path_arg = mongo_h_path.to_str().unwrap();
        let bson_path_arg    = bson_path.to_str().unwrap();

        let mut builder = bindgen::builder();
        builder.emit_builtins();
        builder.header(mongo_h_path_arg);
        builder.clang_arg("-I".to_owned());
        builder.clang_arg(bson_path_arg);

        // Add clang include dir if it's detected by bindgen.
        if let Some(path) = bindgen::get_include_dir() {
            builder.clang_arg("-I".to_owned());
            builder.clang_arg(path);
        }

        // Add clang include dir from env var, use as a last resort
        // if include cannot be detected normally.
        if let Ok(path) = env::var("CLANG_INCLUDE_DIR") {
            builder.clang_arg("-I".to_owned());
            builder.clang_arg(path);
        }

        let binding = builder.generate().unwrap();
        binding.write_to_file(&bindings_rs_path).unwrap();
    }

    // Output to Cargo
    println!("cargo:root={}", &out_dir);
    println!("cargo:libdir={}/lib", &out_dir);
    println!("cargo:include={}/include", &out_dir);
    println!("cargo:rustc-link-search={}/lib", &out_dir);
    println!("cargo:rustc-link-lib=static=bson-1.0");
    println!("cargo:rustc-link-lib=static=mongoc-1.0");

    for link_path in pkg_config::find_library("openssl").unwrap().link_paths.iter(){
        println!("cargo:rustc-link-search=framework={}", &link_path.display());
    }
}
<commit_msg>Enable fPIC for bson and allow choosing the target<commit_after>#![feature(path_ext)]

extern crate bindgen;
extern crate pkg_config;

use std::env;
use std::fs::PathExt;
use std::path::Path;
use std::process::Command;

static VERSION: &'static str = "1.1.10"; // Should be the same as the version in the manifest

fn main() {
    let out_dir     = env::var("OUT_DIR").unwrap();
    let current_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let driver_path = format!(
        "mongo-c-driver-{}",
        VERSION
    );

    let libmongoc_path = Path::new(&out_dir).join("lib/libmongoc-1.0.a");
    if !libmongoc_path.exists() { // TODO: This should check if we're at the right version
        // Download and extract driver archive
        let url = format!(
            "https://github.com/mongodb/mongo-c-driver/releases/download/{}/mongo-c-driver-{}.tar.gz",
            VERSION,
            VERSION
        );
        assert!(Command::new("curl").arg("-O") // Save to disk
                                    .arg("-L") // Follow redirects
                                    .arg(url)
                                    .status()
                                    .unwrap()
                                    .success());

        let archive_name = format!(
            "mongo-c-driver-{}.tar.gz",
            VERSION
        );
        assert!(Command::new("tar").arg("xzf")
                                   .arg(&archive_name)
                                   .status()
                                   .unwrap()
                                   .success());

        // Configure and install
        let mut command = Command::new("sh");
        command.arg("configure");
        command.arg("--enable-ssl=yes");
        command.arg("--enable-sasl=no");
        command.arg("--enable-static=yes");
        command.arg("--enable-shared=no");
        command.arg("--with-libbson=bundled");
        command.arg(format!("--prefix={}", &out_dir));
        command.env("BSON_CFLAGS", "-fPIC");
        command.current_dir(&driver_path);

        // Use env var to override build target if set
        if let Ok(target) = env::var("MONGO_C_DRIVER_TARGET") {
            command.arg(format!("--target={}", target));
        }

        assert!(command.status().unwrap().success());
        assert!(Command::new("make").current_dir(&driver_path).status().unwrap().success());
        assert!(Command::new("make").arg("install").current_dir(&driver_path).status().unwrap().success());

        // Generate bindings
        let bindings_rs_path = Path::new(&current_dir).join("src/bindings.rs");
        let mongo_h_path     = Path::new(&current_dir).join(&driver_path).join("src/mongoc/mongoc.h");
        let bson_path        = Path::new(&current_dir).join(&driver_path).join("src/libbson/src/bson");
        let mongo_h_path_arg = mongo_h_path.to_str().unwrap();
        let bson_path_arg    = bson_path.to_str().unwrap();

        let mut builder = bindgen::builder();
        builder.emit_builtins();
        builder.header(mongo_h_path_arg);
        builder.clang_arg("-I".to_owned());
        builder.clang_arg(bson_path_arg);

        // Add clang include dir if it's detected by bindgen.
        if let Some(path) = bindgen::get_include_dir() {
            builder.clang_arg("-I".to_owned());
            builder.clang_arg(path);
        }

        // Add clang include dir from env var, use as a last resort
        // if include cannot be detected normally.
        if let Ok(path) = env::var("CLANG_INCLUDE_DIR") {
            builder.clang_arg("-I".to_owned());
            builder.clang_arg(path);
        }

        let binding = builder.generate().unwrap();
        binding.write_to_file(&bindings_rs_path).unwrap();
    }

    // Output to Cargo
    println!("cargo:root={}", &out_dir);
    println!("cargo:libdir={}/lib", &out_dir);
    println!("cargo:include={}/include", &out_dir);
    println!("cargo:rustc-link-search={}/lib", &out_dir);
    println!("cargo:rustc-link-lib=static=bson-1.0");
    println!("cargo:rustc-link-lib=static=mongoc-1.0");

    for link_path in pkg_config::find_library("openssl").unwrap().link_paths.iter(){
        println!("cargo:rustc-link-search=framework={}", &link_path.display());
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use data_model::*;
use std;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::result;
use sys_util::{Error as SysError, EventFd};

use std::fs::File;
use std::net::{TcpListener, TcpStream, UdpSocket};
use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};

#[derive(Debug, PartialEq)]
/// An error during transaction or serialization/deserialization.
pub enum MsgError {
    /// Error while sending a request or response.
    Send(SysError),
    /// Error while receiving a request or response.
    Recv(SysError),
    /// The type of a received request or response is unknown.
    InvalidType,
    /// There was not the expected amount of data when receiving a message. The inner
    /// value is how much data is needed.
    BadRecvSize(usize),
    /// There was no associated file descriptor received for a request that expected it.
    ExpectFd,
    /// There was some associated file descriptor received but not used when deserialize.
    NotExpectFd,
    /// Trying to serialize/deserialize, but fd buffer size is too small. This typically happens
    /// when max_fd_count() returns a value that is too small.
    WrongFdBufferSize,
    /// Trying to serialize/deserialize, but msg buffer size is too small. This typically happens
    /// when msg_size() returns a value that is too small.
    WrongMsgBufferSize,
}

pub type MsgResult<T> = result::Result<T, MsgError>;

/// A msg that could be serialized to and deserialize from array in little endian.
///
/// For structs, we always have fixed size of bytes and fixed count of fds.
/// For enums, the size needed might be different for each variant.
///
/// e.g.
/// ```
/// use std::os::unix::io::RawFd;
/// enum Message {
///     VariantA(u8),
///     VariantB(u32, RawFd),
///     VariantC,
/// }
/// ```
///
/// For variant A, we need 1 byte to store its inner value.
/// For variant B, we need 4 bytes and 1 RawFd to store its inner value.
/// For variant C, we need 0 bytes to store its inner value.
/// When we serialize Message to (buffer, fd_buffer), we always use fixed number of bytes in
/// the buffer. Unused buffer bytes will be padded with zero.
/// However, for fd_buffer, we could not do the same thing. Otherwise, we are essentially sending
/// fd 0 through the socket.
/// Thus, read/write functions always the return correct count of fds in this variant. There will be
/// no padding in fd_buffer.
pub trait MsgOnSocket: Sized {
    /// Size of message in bytes.
    fn msg_size() -> usize;
    /// Max possible fd count in this type.
    fn max_fd_count() -> usize {
        0
    }
    /// Returns (self, fd read count).
    /// This function is safe only when:
    ///     0. fds contains valid fds, received from socket, serialized by Self::write_to_buffer.
    ///     1. For enum, fds contains correct fd layout of the particular variant.
    ///     2. write_to_buffer is implemented correctly(put valid fds into the buffer, has no padding,
    ///        return correct count).
    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)>;
    /// Serialize self to buffers.
    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize>;
}

impl MsgOnSocket for SysError {
    fn msg_size() -> usize {
        u32::msg_size()
    }
    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
        let (v, size) = u32::read_from_buffer(buffer, fds)?;
        Ok((SysError::new(v as i32), size))
    }
    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
        let v = self.errno() as u32;
        v.write_to_buffer(buffer, fds)
    }
}

impl MsgOnSocket for RawFd {
    fn msg_size() -> usize {
        0
    }
    fn max_fd_count() -> usize {
        1
    }
    unsafe fn read_from_buffer(_buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
        if fds.len() < 1 {
            return Err(MsgError::ExpectFd);
        }
        Ok((fds[0], 1))
    }
    fn write_to_buffer(&self, _buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
        if fds.len() < 1 {
            return Err(MsgError::WrongFdBufferSize);
        }
        fds[0] = self.clone();
        Ok(1)
    }
}

macro_rules! rawfd_impl {
    ($type:ident) => {
        impl MsgOnSocket for $type {
            fn msg_size() -> usize {
                0
            }
            fn max_fd_count() -> usize {
                1
            }
            unsafe fn read_from_buffer(_buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
                if fds.len() < 1 {
                    return Err(MsgError::ExpectFd);
                }
                Ok(($type::from_raw_fd(fds[0].clone()), 1))
            }
            fn write_to_buffer(&self, _buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
                if fds.len() < 1 {
                    return Err(MsgError::WrongFdBufferSize);
                }
                fds[0] = self.as_raw_fd();
                Ok(1)
            }
        }
    };
}

rawfd_impl!(EventFd);
rawfd_impl!(File);
rawfd_impl!(UnixStream);
rawfd_impl!(TcpStream);
rawfd_impl!(TcpListener);
rawfd_impl!(UdpSocket);
rawfd_impl!(UnixListener);
rawfd_impl!(UnixDatagram);

// This trait is unsafe as it use uninitialized memory.
// Please only implement it for primitive types.
unsafe trait AlignedNew: Sized + DataInit {
    unsafe fn from_unaligned(buffer: &[u8]) -> Option<Self> {
        let mut value = std::mem::uninitialized::<Self>();
        {
            let value_mem = value.as_mut_slice();
            if value_mem.len() != buffer.len() {
                return None;
            }
            value_mem.copy_from_slice(buffer);
        }
        Some(value)
    }
}

unsafe impl AlignedNew for u8 {}
unsafe impl AlignedNew for u16 {}
unsafe impl AlignedNew for u32 {}
unsafe impl AlignedNew for u64 {}

unsafe impl AlignedNew for Le16 {}
unsafe impl AlignedNew for Le32 {}
unsafe impl AlignedNew for Le64 {}

// usize could be different sizes on different targets. We always use u64.
impl MsgOnSocket for usize {
    fn msg_size() -> usize {
        std::mem::size_of::<u64>()
    }
    unsafe fn read_from_buffer(buffer: &[u8], _fds: &[RawFd]) -> MsgResult<(Self, usize)> {
        if buffer.len() < std::mem::size_of::<u64>() {
            return Err(MsgError::WrongMsgBufferSize);
        }
        let t: u64 = Le64::from_unaligned(&buffer[0..Self::msg_size()])
            .unwrap()
            .into();
        Ok((t as usize, 0))
    }

    fn write_to_buffer(&self, buffer: &mut [u8], _fds: &mut [RawFd]) -> MsgResult<usize> {
        if buffer.len() < std::mem::size_of::<u64>() {
            return Err(MsgError::WrongMsgBufferSize);
        }
        let t: Le64 = (*self as u64).into();
        buffer[0..Self::msg_size()].copy_from_slice(t.as_slice());
        Ok(0)
    }
}

macro_rules! le_impl {
    ($type:ident, $le_type:ident) => {
        impl MsgOnSocket for $type {
            fn msg_size() -> usize {
                std::mem::size_of::<$le_type>()
            }
            unsafe fn read_from_buffer(buffer: &[u8], _fds: &[RawFd]) -> MsgResult<(Self, usize)> {
                if buffer.len() < std::mem::size_of::<$le_type>() {
                    return Err(MsgError::WrongMsgBufferSize);
                }
                let t = $le_type::from_unaligned(&buffer[0..Self::msg_size()])
                    .unwrap();
                Ok((t.into(), 0))
            }

            fn write_to_buffer(&self, buffer: &mut [u8], _fds: &mut [RawFd]) -> MsgResult<usize> {
                if buffer.len() < std::mem::size_of::<$le_type>() {
                    return Err(MsgError::WrongMsgBufferSize);
                }
                let t: $le_type = self.clone().into();
                buffer[0..Self::msg_size()].copy_from_slice(t.as_slice());
                Ok(0)
            }
        }
    };
}

le_impl!(u8, u8);
le_impl!(u16, Le16);
le_impl!(u32, Le32);
le_impl!(u64, Le64);

le_impl!(Le16, Le16);
le_impl!(Le32, Le32);
le_impl!(Le64, Le64);

macro_rules! array_impls {
    ($N:expr, $t: ident $($ts:ident)*)
    => {
        impl<T: MsgOnSocket + Clone> MsgOnSocket for [T; $N] {
            fn msg_size() -> usize {
                T::msg_size() * $N
            }
            fn max_fd_count() -> usize {
                T::max_fd_count() * $N
            }
            unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
                if buffer.len() < Self::msg_size() {
                    return Err(MsgError::WrongMsgBufferSize);
                }
                let mut offset = 0usize;
                let mut fd_offset = 0usize;
                let ($t, fd_size) =
                    T::read_from_buffer(&buffer[offset..], &fds[fd_offset..])?;
                offset += T::msg_size();
                fd_offset += fd_size;
                $(
                    let ($ts, fd_size) =
                        T::read_from_buffer(&buffer[offset..], &fds[fd_offset..])?;
                    offset += T::msg_size();
                    fd_offset += fd_size;
                    )*
                assert_eq!(offset, Self::msg_size());
                Ok(([$t, $($ts),*], fd_offset))
            }

            fn write_to_buffer(
                &self,
                buffer: &mut [u8],
                fds: &mut [RawFd],
                ) -> MsgResult<usize> {
                if buffer.len() < Self::msg_size() {
                    return Err(MsgError::WrongMsgBufferSize);
                }
                let mut offset = 0usize;
                let mut fd_offset = 0usize;
                for idx in 0..$N {
                    let fd_size = self[idx].clone().write_to_buffer(&mut buffer[offset..],
                                                            &mut fds[fd_offset..])?;
                    offset += T::msg_size();
                    fd_offset += fd_size;
                }

                Ok(fd_offset)
            }
        }
        array_impls!(($N - 1), $($ts)*);
    };
    {$N:expr, } => {};
}

array_impls! {
    32, tmp1 tmp2 tmp3 tmp4 tmp5 tmp6 tmp7 tmp8 tmp9 tmp10 tmp11 tmp12 tmp13 tmp14 tmp15 tmp16
        tmp17 tmp18 tmp19 tmp20 tmp21 tmp22 tmp23 tmp24 tmp25 tmp26 tmp27 tmp28 tmp29 tmp30 tmp31
        tmp32
}

// TODO(jkwang) Define MsgOnSocket for tuple?
<commit_msg>msg_socket: reformat with rust fmt<commit_after>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use data_model::*;
use std;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::result;
use sys_util::{Error as SysError, EventFd};

use std::fs::File;
use std::net::{TcpListener, TcpStream, UdpSocket};
use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};

#[derive(Debug, PartialEq)]
/// An error during transaction or serialization/deserialization.
pub enum MsgError {
    /// Error while sending a request or response.
    Send(SysError),
    /// Error while receiving a request or response.
    Recv(SysError),
    /// The type of a received request or response is unknown.
    InvalidType,
    /// There was not the expected amount of data when receiving a message. The inner
    /// value is how much data is needed.
    BadRecvSize(usize),
    /// There was no associated file descriptor received for a request that expected it.
    ExpectFd,
    /// There was some associated file descriptor received but not used when deserialize.
    NotExpectFd,
    /// Trying to serialize/deserialize, but fd buffer size is too small. This typically happens
    /// when max_fd_count() returns a value that is too small.
    WrongFdBufferSize,
    /// Trying to serialize/deserialize, but msg buffer size is too small. This typically happens
    /// when msg_size() returns a value that is too small.
    WrongMsgBufferSize,
}

pub type MsgResult<T> = result::Result<T, MsgError>;

/// A msg that could be serialized to and deserialize from array in little endian.
///
/// For structs, we always have fixed size of bytes and fixed count of fds.
/// For enums, the size needed might be different for each variant.
///
/// e.g.
/// ```
/// use std::os::unix::io::RawFd;
/// enum Message {
///     VariantA(u8),
///     VariantB(u32, RawFd),
///     VariantC,
/// }
/// ```
///
/// For variant A, we need 1 byte to store its inner value.
/// For variant B, we need 4 bytes and 1 RawFd to store its inner value.
/// For variant C, we need 0 bytes to store its inner value.
/// When we serialize Message to (buffer, fd_buffer), we always use fixed number of bytes in
/// the buffer. Unused buffer bytes will be padded with zero.
/// However, for fd_buffer, we could not do the same thing. Otherwise, we are essentially sending
/// fd 0 through the socket.
/// Thus, read/write functions always the return correct count of fds in this variant. There will be
/// no padding in fd_buffer.
pub trait MsgOnSocket: Sized {
    /// Size of message in bytes.
    fn msg_size() -> usize;
    /// Max possible fd count in this type.
    fn max_fd_count() -> usize {
        0
    }
    /// Returns (self, fd read count).
    /// This function is safe only when:
    ///     0. fds contains valid fds, received from socket, serialized by Self::write_to_buffer.
    ///     1. For enum, fds contains correct fd layout of the particular variant.
    ///     2. write_to_buffer is implemented correctly(put valid fds into the buffer, has no padding,
    ///        return correct count).
    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)>;
    /// Serialize self to buffers.
    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize>;
}

impl MsgOnSocket for SysError {
    fn msg_size() -> usize {
        u32::msg_size()
    }
    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
        let (v, size) = u32::read_from_buffer(buffer, fds)?;
        Ok((SysError::new(v as i32), size))
    }
    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
        let v = self.errno() as u32;
        v.write_to_buffer(buffer, fds)
    }
}

impl MsgOnSocket for RawFd {
    fn msg_size() -> usize {
        0
    }
    fn max_fd_count() -> usize {
        1
    }
    unsafe fn read_from_buffer(_buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
        if fds.len() < 1 {
            return Err(MsgError::ExpectFd);
        }
        Ok((fds[0], 1))
    }
    fn write_to_buffer(&self, _buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
        if fds.len() < 1 {
            return Err(MsgError::WrongFdBufferSize);
        }
        fds[0] = self.clone();
        Ok(1)
    }
}

macro_rules! rawfd_impl {
    ($type:ident) => {
        impl MsgOnSocket for $type {
            fn msg_size() -> usize {
                0
            }
            fn max_fd_count() -> usize {
                1
            }
            unsafe fn read_from_buffer(_buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
                if fds.len() < 1 {
                    return Err(MsgError::ExpectFd);
                }
                Ok(($type::from_raw_fd(fds[0].clone()), 1))
            }
            fn write_to_buffer(&self, _buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
                if fds.len() < 1 {
                    return Err(MsgError::WrongFdBufferSize);
                }
                fds[0] = self.as_raw_fd();
                Ok(1)
            }
        }
    };
}

rawfd_impl!(EventFd);
rawfd_impl!(File);
rawfd_impl!(UnixStream);
rawfd_impl!(TcpStream);
rawfd_impl!(TcpListener);
rawfd_impl!(UdpSocket);
rawfd_impl!(UnixListener);
rawfd_impl!(UnixDatagram);

// This trait is unsafe as it use uninitialized memory.
// Please only implement it for primitive types.
unsafe trait AlignedNew: Sized + DataInit {
    unsafe fn from_unaligned(buffer: &[u8]) -> Option<Self> {
        let mut value = std::mem::uninitialized::<Self>();
        {
            let value_mem = value.as_mut_slice();
            if value_mem.len() != buffer.len() {
                return None;
            }
            value_mem.copy_from_slice(buffer);
        }
        Some(value)
    }
}

unsafe impl AlignedNew for u8 {}
unsafe impl AlignedNew for u16 {}
unsafe impl AlignedNew for u32 {}
unsafe impl AlignedNew for u64 {}

unsafe impl AlignedNew for Le16 {}
unsafe impl AlignedNew for Le32 {}
unsafe impl AlignedNew for Le64 {}

// usize could be different sizes on different targets. We always use u64.
impl MsgOnSocket for usize {
    fn msg_size() -> usize {
        std::mem::size_of::<u64>()
    }
    unsafe fn read_from_buffer(buffer: &[u8], _fds: &[RawFd]) -> MsgResult<(Self, usize)> {
        if buffer.len() < std::mem::size_of::<u64>() {
            return Err(MsgError::WrongMsgBufferSize);
        }
        let t: u64 = Le64::from_unaligned(&buffer[0..Self::msg_size()])
            .unwrap()
            .into();
        Ok((t as usize, 0))
    }

    fn write_to_buffer(&self, buffer: &mut [u8], _fds: &mut [RawFd]) -> MsgResult<usize> {
        if buffer.len() < std::mem::size_of::<u64>() {
            return Err(MsgError::WrongMsgBufferSize);
        }
        let t: Le64 = (*self as u64).into();
        buffer[0..Self::msg_size()].copy_from_slice(t.as_slice());
        Ok(0)
    }
}

macro_rules! le_impl {
    ($type:ident, $le_type:ident) => {
        impl MsgOnSocket for $type {
            fn msg_size() -> usize {
                std::mem::size_of::<$le_type>()
            }
            unsafe fn read_from_buffer(buffer: &[u8], _fds: &[RawFd]) -> MsgResult<(Self, usize)> {
                if buffer.len() < std::mem::size_of::<$le_type>() {
                    return Err(MsgError::WrongMsgBufferSize);
                }
                let t = $le_type::from_unaligned(&buffer[0..Self::msg_size()]).unwrap();
                Ok((t.into(), 0))
            }

            fn write_to_buffer(&self, buffer: &mut [u8], _fds: &mut [RawFd]) -> MsgResult<usize> {
                if buffer.len() < std::mem::size_of::<$le_type>() {
                    return Err(MsgError::WrongMsgBufferSize);
                }
                let t: $le_type = self.clone().into();
                buffer[0..Self::msg_size()].copy_from_slice(t.as_slice());
                Ok(0)
            }
        }
    };
}

le_impl!(u8, u8);
le_impl!(u16, Le16);
le_impl!(u32, Le32);
le_impl!(u64, Le64);

le_impl!(Le16, Le16);
le_impl!(Le32, Le32);
le_impl!(Le64, Le64);

macro_rules! array_impls {
    ($N:expr, $t: ident $($ts:ident)*)
    => {
        impl<T: MsgOnSocket + Clone> MsgOnSocket for [T; $N] {
            fn msg_size() -> usize {
                T::msg_size() * $N
            }
            fn max_fd_count() -> usize {
                T::max_fd_count() * $N
            }
            unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
                if buffer.len() < Self::msg_size() {
                    return Err(MsgError::WrongMsgBufferSize);
                }
                let mut offset = 0usize;
                let mut fd_offset = 0usize;
                let ($t, fd_size) =
                    T::read_from_buffer(&buffer[offset..], &fds[fd_offset..])?;
                offset += T::msg_size();
                fd_offset += fd_size;
                $(
                    let ($ts, fd_size) =
                        T::read_from_buffer(&buffer[offset..], &fds[fd_offset..])?;
                    offset += T::msg_size();
                    fd_offset += fd_size;
                    )*
                assert_eq!(offset, Self::msg_size());
                Ok(([$t, $($ts),*], fd_offset))
            }

            fn write_to_buffer(
                &self,
                buffer: &mut [u8],
                fds: &mut [RawFd],
                ) -> MsgResult<usize> {
                if buffer.len() < Self::msg_size() {
                    return Err(MsgError::WrongMsgBufferSize);
                }
                let mut offset = 0usize;
                let mut fd_offset = 0usize;
                for idx in 0..$N {
                    let fd_size = self[idx].clone().write_to_buffer(&mut buffer[offset..],
                                                            &mut fds[fd_offset..])?;
                    offset += T::msg_size();
                    fd_offset += fd_size;
                }

                Ok(fd_offset)
            }
        }
        array_impls!(($N - 1), $($ts)*);
    };
    {$N:expr, } => {};
}

array_impls! {
    32, tmp1 tmp2 tmp3 tmp4 tmp5 tmp6 tmp7 tmp8 tmp9 tmp10 tmp11 tmp12 tmp13 tmp14 tmp15 tmp16
        tmp17 tmp18 tmp19 tmp20 tmp21 tmp22 tmp23 tmp24 tmp25 tmp26 tmp27 tmp28 tmp29 tmp30 tmp31
        tmp32
}

// TODO(jkwang) Define MsgOnSocket for tuple?
<|endoftext|>