diff --git a/src/connection/stream.rs b/src/connection/stream.rs index e9290b8b..d35c9ff7 100644 --- a/src/connection/stream.rs +++ b/src/connection/stream.rs @@ -330,6 +330,7 @@ impl AsyncWrite for Stream { return Poll::Pending } let k = std::cmp::min(shared.credit as usize, buf.len()); + let k = std::cmp::min(k, self.config.split_send_size); shared.credit = shared.credit.saturating_sub(k as u32); Vec::from(&buf[.. k]) }; diff --git a/src/lib.rs b/src/lib.rs index 44528100..b2a8740c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,22 @@ pub use crate::frame::{FrameDecodeError, header::{HeaderDecodeError, StreamId}}; const DEFAULT_CREDIT: u32 = 256 * 1024; // as per yamux specification +/// Default maximum number of bytes a Yamux data frame might carry as its +/// payload when being send. Larger Payloads will be split. +/// +/// The data frame payload size is not restricted by the yamux specification. +/// Still, this implementation restricts the size to: +/// +/// 1. Reduce delays sending time-sensitive frames, e.g. window updates. +/// 2. Minimize head-of-line blocking across streams. +/// 3. Enable better interleaving of send and receive operations, as each is +/// carried out atomically instead of concurrently with its respective +/// counterpart. +/// +/// For details on why this concrete value was chosen, see +/// https://github.com/paritytech/yamux/issues/100. +const DEFAULT_SPLIT_SEND_SIZE: usize = 16 * 1024; + /// Specifies when window update frames are sent. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum WindowUpdateMode { @@ -76,13 +92,15 @@ pub enum WindowUpdateMode { /// - max. number of streams = 8192 /// - window update mode = on receive /// - read after close = true +/// - split send size = 16 KiB #[derive(Debug, Clone)] pub struct Config { receive_window: u32, max_buffer_size: usize, max_num_streams: usize, window_update_mode: WindowUpdateMode, - read_after_close: bool + read_after_close: bool, + split_send_size: usize } impl Default for Config { @@ -92,7 +110,8 @@ impl Default for Config { max_buffer_size: 1024 * 1024, max_num_streams: 8192, window_update_mode: WindowUpdateMode::OnReceive, - read_after_close: true + read_after_close: true, + split_send_size: DEFAULT_SPLIT_SEND_SIZE } } } @@ -133,6 +152,13 @@ impl Config { self.read_after_close = b; self } + + /// Set the max. payload size used when sending data frames. Payloads larger + /// than the configured max. will be split. + pub fn set_split_send_size(&mut self, n: usize) -> &mut Self { + self.split_send_size = n; + self + } } // Check that we can safely cast a `usize` to a `u64`.