More flow control work
This commit is contained in:
@@ -38,6 +38,8 @@ impl WindowUpdate {
|
|||||||
// when received.
|
// when received.
|
||||||
let size_increment = unpack_octets_4!(payload, 0, u32) & !SIZE_INCREMENT_MASK;
|
let size_increment = unpack_octets_4!(payload, 0, u32) & !SIZE_INCREMENT_MASK;
|
||||||
|
|
||||||
|
// TODO: the size_increment must be greater than 0
|
||||||
|
|
||||||
Ok(WindowUpdate {
|
Ok(WindowUpdate {
|
||||||
stream_id: head.stream_id(),
|
stream_id: head.stream_id(),
|
||||||
size_increment,
|
size_increment,
|
||||||
|
|||||||
@@ -142,11 +142,17 @@ impl<B> Prioritize<B>
|
|||||||
// capacity list first.
|
// capacity list first.
|
||||||
|
|
||||||
if self.flow_control.has_capacity() && !self.pending_capacity.is_empty() {
|
if self.flow_control.has_capacity() && !self.pending_capacity.is_empty() {
|
||||||
let mut stream = self.pending_capacity.pop(store).unwrap();
|
let mut stream = self.pending_capacity
|
||||||
|
.pop::<stream::Next>(store)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
stream.is_pending_send = false;
|
stream.is_pending_send = false;
|
||||||
Some(stream)
|
Some(stream)
|
||||||
} else {
|
} else {
|
||||||
match self.pending_send.pop(store) {
|
let stream = self.pending_send
|
||||||
|
.pop::<stream::Next>(store);
|
||||||
|
|
||||||
|
match stream {
|
||||||
Some(mut stream) => {
|
Some(mut stream) => {
|
||||||
stream.is_pending_send = false;
|
stream.is_pending_send = false;
|
||||||
Some(stream)
|
Some(stream)
|
||||||
@@ -159,6 +165,6 @@ impl<B> Prioritize<B>
|
|||||||
|
|
||||||
fn push_sender<B>(list: &mut store::List<B>, stream: &mut store::Ptr<B>) {
|
fn push_sender<B>(list: &mut store::List<B>, stream: &mut store::Ptr<B>) {
|
||||||
debug_assert!(!stream.is_pending_send);
|
debug_assert!(!stream.is_pending_send);
|
||||||
list.push(stream);
|
list.push::<stream::Next>(stream);
|
||||||
stream.is_pending_send = true;
|
stream.is_pending_send = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ impl<B> Recv<B> where B: Buf {
|
|||||||
// Only servers can receive a headers frame that initiates the stream.
|
// Only servers can receive a headers frame that initiates the stream.
|
||||||
// This is verified in `Streams` before calling this function.
|
// This is verified in `Streams` before calling this function.
|
||||||
if P::is_server() {
|
if P::is_server() {
|
||||||
self.pending_accept.push(stream);
|
self.pending_accept.push::<stream::Next>(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -226,7 +226,7 @@ impl<B> Recv<B> where B: Buf {
|
|||||||
let mut new_stream = stream.store()
|
let mut new_stream = stream.store()
|
||||||
.insert(frame.promised_id(), new_stream);
|
.insert(frame.promised_id(), new_stream);
|
||||||
|
|
||||||
ppp.push(&mut new_stream);
|
ppp.push::<stream::Next>(&mut new_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.pending_push_promises = ppp;
|
stream.pending_push_promises = ppp;
|
||||||
@@ -381,7 +381,7 @@ impl<B> Recv<B> where B: Buf {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
pub fn next_incoming(&mut self, store: &mut Store<B>) -> Option<store::Key> {
|
pub fn next_incoming(&mut self, store: &mut Store<B>) -> Option<store::Key> {
|
||||||
self.pending_accept.pop(store)
|
self.pending_accept.pop::<stream::Next>(store)
|
||||||
.map(|ptr| ptr.key())
|
.map(|ptr| ptr.key())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ pub(super) struct Send<B> {
|
|||||||
/// Initial window size of locally initiated streams
|
/// Initial window size of locally initiated streams
|
||||||
init_window_sz: WindowSize,
|
init_window_sz: WindowSize,
|
||||||
|
|
||||||
|
/// List of streams waiting for outbound connection capacity
|
||||||
|
pending_capacity: store::List<B>,
|
||||||
|
|
||||||
|
/// Prioritization layer
|
||||||
prioritize: Prioritize<B>,
|
prioritize: Prioritize<B>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +46,7 @@ impl<B> Send<B> where B: Buf {
|
|||||||
num_streams: 0,
|
num_streams: 0,
|
||||||
next_stream_id: next_stream_id.into(),
|
next_stream_id: next_stream_id.into(),
|
||||||
init_window_sz: config.init_local_window_sz,
|
init_window_sz: config.init_local_window_sz,
|
||||||
|
pending_capacity: store::List::new(),
|
||||||
prioritize: Prioritize::new(config),
|
prioritize: Prioritize::new(config),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,49 +152,21 @@ impl<B> Send<B> where B: Buf {
|
|||||||
self.prioritize.poll_complete(store, dst)
|
self.prioritize.poll_complete(store, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
pub fn recv_connection_window_update(&mut self,
|
||||||
/// Get pending window updates
|
frame: frame::WindowUpdate,
|
||||||
pub fn poll_window_update(&mut self, streams: &mut Store<B>)
|
store: &mut Store<B>)
|
||||||
-> Poll<WindowUpdate, ConnectionError>
|
|
||||||
{
|
|
||||||
// This biases connection window updates, which probably makes sense.
|
|
||||||
//
|
|
||||||
// TODO: We probably don't want to expose connection level updates
|
|
||||||
if let Some(incr) = self.flow_control.apply_window_update() {
|
|
||||||
return Ok(Async::Ready(WindowUpdate::new(StreamId::zero(), incr)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO this should probably account for stream priority?
|
|
||||||
let update = self.pending_window_updates.pop_front()
|
|
||||||
.and_then(|id| {
|
|
||||||
streams.find_mut(&id)
|
|
||||||
.and_then(|stream| stream.into_mut().send_flow_control())
|
|
||||||
.and_then(|flow| flow.apply_window_update())
|
|
||||||
.map(|incr| WindowUpdate::new(id, incr))
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(update) = update {
|
|
||||||
return Ok(Async::Ready(update));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the task.
|
|
||||||
//
|
|
||||||
// TODO: Extract this "gate" logic
|
|
||||||
self.blocked = Some(task::current());
|
|
||||||
|
|
||||||
return Ok(Async::NotReady);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn recv_connection_window_update(&mut self, frame: frame::WindowUpdate)
|
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), ConnectionError>
|
||||||
{
|
{
|
||||||
self.prioritize.recv_window_update(frame)?;
|
self.prioritize.recv_window_update(frame)?;
|
||||||
|
|
||||||
// TODO: If there is available connection capacity, release pending
|
// TODO: If there is available connection capacity, release pending
|
||||||
// streams.
|
// streams.
|
||||||
|
//
|
||||||
|
// Walk each stream pending capacity and see if this change to the
|
||||||
|
// connection window can increase the advertised capacity of the stream.
|
||||||
|
|
||||||
Ok(())
|
unimplemented!();
|
||||||
|
// Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recv_stream_window_update(&mut self,
|
pub fn recv_stream_window_update(&mut self,
|
||||||
@@ -216,7 +193,10 @@ impl<B> Send<B> where B: Buf {
|
|||||||
if connection < effective_window_size {
|
if connection < effective_window_size {
|
||||||
stream.unadvertised_send_window = effective_window_size - connection;
|
stream.unadvertised_send_window = effective_window_size - connection;
|
||||||
|
|
||||||
// TODO: Queue the stream in a pending connection capacity list.
|
if !stream.is_pending_send_capacity {
|
||||||
|
stream.is_pending_send_capacity = true;
|
||||||
|
self.pending_capacity.push::<stream::NextCapacity>(stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if stream.unadvertised_send_window == frame.size_increment() + unadvertised {
|
if stream.unadvertised_send_window == frame.size_increment() + unadvertised {
|
||||||
@@ -225,9 +205,9 @@ impl<B> Send<B> where B: Buf {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Notify the send task that there is additional capacity
|
stream.notify_send();
|
||||||
|
|
||||||
unimplemented!();
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dec_num_streams(&mut self) {
|
pub fn dec_num_streams(&mut self) {
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ pub(super) struct List<B> {
|
|||||||
_p: PhantomData<B>,
|
_p: PhantomData<B>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) trait Next {
|
||||||
|
fn next<B>(stream: &Stream<B>) -> Option<Key>;
|
||||||
|
|
||||||
|
fn set_next<B>(stream: &mut Stream<B>, key: Key);
|
||||||
|
|
||||||
|
fn take_next<B>(stream: &mut Stream<B>) -> Key;
|
||||||
|
}
|
||||||
|
|
||||||
/// A linked list
|
/// A linked list
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct Indices {
|
struct Indices {
|
||||||
@@ -138,15 +146,18 @@ impl<B> List<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, stream: &mut store::Ptr<B>) {
|
pub fn push<N>(&mut self, stream: &mut store::Ptr<B>)
|
||||||
|
where N: Next,
|
||||||
|
{
|
||||||
// The next pointer shouldn't be set
|
// The next pointer shouldn't be set
|
||||||
debug_assert!(stream.next.is_none());
|
debug_assert!(N::next(stream).is_none());
|
||||||
|
|
||||||
// Queue the stream
|
// Queue the stream
|
||||||
match self.indices {
|
match self.indices {
|
||||||
Some(ref mut idxs) => {
|
Some(ref mut idxs) => {
|
||||||
// Update the current tail node to point to `stream`
|
// Update the current tail node to point to `stream`
|
||||||
stream.resolve(idxs.tail).next = Some(stream.key());
|
let key = stream.key();
|
||||||
|
N::set_next(&mut stream.resolve(idxs.tail), key);
|
||||||
|
|
||||||
// Update the tail pointer
|
// Update the tail pointer
|
||||||
idxs.tail = stream.key();
|
idxs.tail = stream.key();
|
||||||
@@ -160,15 +171,17 @@ impl<B> List<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop<'a>(&mut self, store: &'a mut Store<B>) -> Option<store::Ptr<'a, B>> {
|
pub fn pop<'a, N>(&mut self, store: &'a mut Store<B>) -> Option<store::Ptr<'a, B>>
|
||||||
|
where N: Next,
|
||||||
|
{
|
||||||
if let Some(mut idxs) = self.indices {
|
if let Some(mut idxs) = self.indices {
|
||||||
let mut stream = store.resolve(idxs.head);
|
let mut stream = store.resolve(idxs.head);
|
||||||
|
|
||||||
if idxs.head == idxs.tail {
|
if idxs.head == idxs.tail {
|
||||||
assert!(stream.next.is_none());
|
assert!(N::next(&*stream).is_none());
|
||||||
self.indices = None;
|
self.indices = None;
|
||||||
} else {
|
} else {
|
||||||
idxs.head = stream.next.take().unwrap();
|
idxs.head = N::take_next(&mut *stream);
|
||||||
self.indices = Some(idxs);
|
self.indices = Some(idxs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ pub(super) struct Stream<B> {
|
|||||||
/// Task tracking receiving frames
|
/// Task tracking receiving frames
|
||||||
pub recv_task: Option<task::Task>,
|
pub recv_task: Option<task::Task>,
|
||||||
|
|
||||||
|
/// Task tracking additional send capacity (i.e. window updates).
|
||||||
|
pub send_task: Option<task::Task>,
|
||||||
|
|
||||||
/// Frames pending for this stream being sent to the socket
|
/// Frames pending for this stream being sent to the socket
|
||||||
pub pending_send: buffer::Deque<B>,
|
pub pending_send: buffer::Deque<B>,
|
||||||
|
|
||||||
@@ -23,6 +26,13 @@ pub(super) struct Stream<B> {
|
|||||||
/// state.
|
/// state.
|
||||||
pub next: Option<store::Key>,
|
pub next: Option<store::Key>,
|
||||||
|
|
||||||
|
/// Next node in the linked list of streams waiting for additional
|
||||||
|
/// connection level capacity.
|
||||||
|
pub next_capacity: Option<store::Key>,
|
||||||
|
|
||||||
|
/// True if the stream is waiting for outbound connection capacity
|
||||||
|
pub is_pending_send_capacity: bool,
|
||||||
|
|
||||||
/// The stream's pending push promises
|
/// The stream's pending push promises
|
||||||
pub pending_push_promises: store::List<B>,
|
pub pending_push_promises: store::List<B>,
|
||||||
|
|
||||||
@@ -35,6 +45,12 @@ pub(super) struct Stream<B> {
|
|||||||
pub unadvertised_send_window: WindowSize,
|
pub unadvertised_send_window: WindowSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) struct Next;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) struct NextCapacity;
|
||||||
|
|
||||||
impl<B> Stream<B> {
|
impl<B> Stream<B> {
|
||||||
pub fn new(id: StreamId) -> Stream<B> {
|
pub fn new(id: StreamId) -> Stream<B> {
|
||||||
Stream {
|
Stream {
|
||||||
@@ -42,8 +58,11 @@ impl<B> Stream<B> {
|
|||||||
state: State::default(),
|
state: State::default(),
|
||||||
pending_recv: buffer::Deque::new(),
|
pending_recv: buffer::Deque::new(),
|
||||||
recv_task: None,
|
recv_task: None,
|
||||||
|
send_task: None,
|
||||||
pending_send: buffer::Deque::new(),
|
pending_send: buffer::Deque::new(),
|
||||||
next: None,
|
next: None,
|
||||||
|
next_capacity: None,
|
||||||
|
is_pending_send_capacity: false,
|
||||||
pending_push_promises: store::List::new(),
|
pending_push_promises: store::List::new(),
|
||||||
is_pending_send: false,
|
is_pending_send: false,
|
||||||
unadvertised_send_window: 0,
|
unadvertised_send_window: 0,
|
||||||
@@ -60,9 +79,45 @@ impl<B> Stream<B> {
|
|||||||
self.state.recv_flow_control()
|
self.state.recv_flow_control()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn notify_send(&mut self) {
|
||||||
|
if let Some(task) = self.send_task.take() {
|
||||||
|
task.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn notify_recv(&mut self) {
|
pub fn notify_recv(&mut self) {
|
||||||
if let Some(ref mut task) = self.recv_task {
|
if let Some(task) = self.recv_task.take() {
|
||||||
task.notify();
|
task.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl store::Next for Next {
|
||||||
|
fn next<B>(stream: &Stream<B>) -> Option<store::Key> {
|
||||||
|
stream.next
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_next<B>(stream: &mut Stream<B>, key: store::Key) {
|
||||||
|
debug_assert!(stream.next.is_none());
|
||||||
|
stream.next = Some(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_next<B>(stream: &mut Stream<B>) -> store::Key {
|
||||||
|
stream.next.take().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl store::Next for NextCapacity {
|
||||||
|
fn next<B>(stream: &Stream<B>) -> Option<store::Key> {
|
||||||
|
stream.next_capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_next<B>(stream: &mut Stream<B>, key: store::Key) {
|
||||||
|
debug_assert!(stream.next_capacity.is_none());
|
||||||
|
stream.next_capacity = Some(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_next<B>(stream: &mut Stream<B>) -> store::Key {
|
||||||
|
stream.next_capacity.take().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -161,7 +161,8 @@ impl<B> Streams<B>
|
|||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
|
|
||||||
if id.is_zero() {
|
if id.is_zero() {
|
||||||
try!(me.actions.send.recv_connection_window_update(frame));
|
me.actions.send.recv_connection_window_update(
|
||||||
|
frame, &mut me.store)?;
|
||||||
} else {
|
} else {
|
||||||
// The remote may send window updates for streams that the local now
|
// The remote may send window updates for streams that the local now
|
||||||
// considers closed. It's ok...
|
// considers closed. It's ok...
|
||||||
|
|||||||
Reference in New Issue
Block a user