| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/spdy/spdy_write_queue.h" |
| |
| #include <cstddef> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/stl_util.h" |
| #include "net/spdy/spdy_buffer.h" |
| #include "net/spdy/spdy_buffer_producer.h" |
| #include "net/spdy/spdy_stream.h" |
| |
| namespace net { |
| |
| SpdyWriteQueue::PendingWrite::PendingWrite() : frame_producer(NULL) {} |
| |
| SpdyWriteQueue::PendingWrite::PendingWrite( |
| SpdyFrameType frame_type, |
| SpdyBufferProducer* frame_producer, |
| const base::WeakPtr<SpdyStream>& stream) |
| : frame_type(frame_type), |
| frame_producer(frame_producer), |
| stream(stream), |
| has_stream(stream.get() != NULL) {} |
| |
| SpdyWriteQueue::PendingWrite::PendingWrite(const PendingWrite& other) = default; |
| |
| SpdyWriteQueue::PendingWrite::~PendingWrite() {} |
| |
| SpdyWriteQueue::SpdyWriteQueue() : removing_writes_(false) {} |
| |
| SpdyWriteQueue::~SpdyWriteQueue() { |
| Clear(); |
| } |
| |
| bool SpdyWriteQueue::IsEmpty() const { |
| for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; i++) { |
| if (!queue_[i].empty()) |
| return false; |
| } |
| return true; |
| } |
| |
| void SpdyWriteQueue::Enqueue(RequestPriority priority, |
| SpdyFrameType frame_type, |
| std::unique_ptr<SpdyBufferProducer> frame_producer, |
| const base::WeakPtr<SpdyStream>& stream) { |
| CHECK(!removing_writes_); |
| CHECK_GE(priority, MINIMUM_PRIORITY); |
| CHECK_LE(priority, MAXIMUM_PRIORITY); |
| if (stream.get()) |
| DCHECK_EQ(stream->priority(), priority); |
| queue_[priority].push_back( |
| PendingWrite(frame_type, frame_producer.release(), stream)); |
| } |
| |
| bool SpdyWriteQueue::Dequeue( |
| SpdyFrameType* frame_type, |
| std::unique_ptr<SpdyBufferProducer>* frame_producer, |
| base::WeakPtr<SpdyStream>* stream) { |
| CHECK(!removing_writes_); |
| for (int i = MAXIMUM_PRIORITY; i >= MINIMUM_PRIORITY; --i) { |
| if (!queue_[i].empty()) { |
| PendingWrite pending_write = queue_[i].front(); |
| queue_[i].pop_front(); |
| *frame_type = pending_write.frame_type; |
| frame_producer->reset(pending_write.frame_producer); |
| *stream = pending_write.stream; |
| if (pending_write.has_stream) |
| DCHECK(stream->get()); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void SpdyWriteQueue::RemovePendingWritesForStream( |
| const base::WeakPtr<SpdyStream>& stream) { |
| CHECK(!removing_writes_); |
| removing_writes_ = true; |
| RequestPriority priority = stream->priority(); |
| CHECK_GE(priority, MINIMUM_PRIORITY); |
| CHECK_LE(priority, MAXIMUM_PRIORITY); |
| |
| DCHECK(stream.get()); |
| #if DCHECK_IS_ON() |
| // |stream| should not have pending writes in a queue not matching |
| // its priority. |
| for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) { |
| if (priority == i) |
| continue; |
| for (std::deque<PendingWrite>::const_iterator it = queue_[i].begin(); |
| it != queue_[i].end(); ++it) { |
| DCHECK_NE(it->stream.get(), stream.get()); |
| } |
| } |
| #endif |
| |
| // Defer deletion until queue iteration is complete, as |
| // SpdyBuffer::~SpdyBuffer() can result in callbacks into SpdyWriteQueue. |
| std::vector<SpdyBufferProducer*> erased_buffer_producers; |
| |
| // Do the actual deletion and removal, preserving FIFO-ness. |
| std::deque<PendingWrite>* queue = &queue_[priority]; |
| std::deque<PendingWrite>::iterator out_it = queue->begin(); |
| for (std::deque<PendingWrite>::const_iterator it = queue->begin(); |
| it != queue->end(); ++it) { |
| if (it->stream.get() == stream.get()) { |
| erased_buffer_producers.push_back(it->frame_producer); |
| } else { |
| *out_it = *it; |
| ++out_it; |
| } |
| } |
| queue->erase(out_it, queue->end()); |
| removing_writes_ = false; |
| base::STLDeleteElements(&erased_buffer_producers); // Invokes callbacks. |
| } |
| |
| void SpdyWriteQueue::RemovePendingWritesForStreamsAfter( |
| SpdyStreamId last_good_stream_id) { |
| CHECK(!removing_writes_); |
| removing_writes_ = true; |
| std::vector<SpdyBufferProducer*> erased_buffer_producers; |
| |
| for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) { |
| // Do the actual deletion and removal, preserving FIFO-ness. |
| std::deque<PendingWrite>* queue = &queue_[i]; |
| std::deque<PendingWrite>::iterator out_it = queue->begin(); |
| for (std::deque<PendingWrite>::const_iterator it = queue->begin(); |
| it != queue->end(); ++it) { |
| if (it->stream.get() && (it->stream->stream_id() > last_good_stream_id || |
| it->stream->stream_id() == 0)) { |
| erased_buffer_producers.push_back(it->frame_producer); |
| } else { |
| *out_it = *it; |
| ++out_it; |
| } |
| } |
| queue->erase(out_it, queue->end()); |
| } |
| removing_writes_ = false; |
| base::STLDeleteElements(&erased_buffer_producers); // Invokes callbacks. |
| } |
| |
| void SpdyWriteQueue::Clear() { |
| CHECK(!removing_writes_); |
| removing_writes_ = true; |
| std::vector<SpdyBufferProducer*> erased_buffer_producers; |
| |
| for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) { |
| for (std::deque<PendingWrite>::iterator it = queue_[i].begin(); |
| it != queue_[i].end(); ++it) { |
| erased_buffer_producers.push_back(it->frame_producer); |
| } |
| queue_[i].clear(); |
| } |
| removing_writes_ = false; |
| base::STLDeleteElements(&erased_buffer_producers); // Invokes callbacks. |
| } |
| |
| } // namespace net |