| #include "http2/adapter/window_manager.h" |
| |
| #include <utility> |
| |
| #include "common/platform/api/quiche_bug_tracker.h" |
| #include "common/platform/api/quiche_logging.h" |
| |
| namespace http2 { |
| namespace adapter { |
| |
| WindowManager::WindowManager(size_t window_size_limit, |
| WindowUpdateListener listener) |
| : limit_(window_size_limit), window_(window_size_limit), buffered_(0), |
| listener_(std::move(listener)) {} |
| |
| void WindowManager::OnWindowSizeLimitChange(const size_t new_limit) { |
| QUICHE_VLOG(2) << "WindowManager@" << this |
| << " OnWindowSizeLimitChange from old limit of " << limit_ |
| << " to new limit of " << new_limit; |
| if (new_limit > limit_) { |
| window_ += (new_limit - limit_); |
| } else { |
| QUICHE_BUG(H2 window decrease) |
| << "Window size limit decrease not currently supported."; |
| } |
| limit_ = new_limit; |
| } |
| |
| void WindowManager::SetWindowSizeLimit(size_t new_limit) { |
| QUICHE_VLOG(2) << "WindowManager@" << this |
| << " SetWindowSizeLimit from old limit of " << limit_ |
| << " to new limit of " << new_limit; |
| limit_ = new_limit; |
| MaybeNotifyListener(); |
| } |
| |
| bool WindowManager::MarkDataBuffered(size_t bytes) { |
| QUICHE_VLOG(2) << "WindowManager@" << this << " window: " << window_ |
| << " bytes: " << bytes; |
| if (window_ < bytes) { |
| QUICHE_VLOG(2) << "WindowManager@" << this << " window underflow " |
| << "window: " << window_ << " bytes: " << bytes; |
| window_ = 0; |
| } else { |
| window_ -= bytes; |
| } |
| buffered_ += bytes; |
| if (window_ == 0) { |
| // If data hasn't been flushed in a while there may be space available. |
| MaybeNotifyListener(); |
| } |
| return window_ > 0; |
| } |
| |
| void WindowManager::MarkDataFlushed(size_t bytes) { |
| QUICHE_VLOG(2) << "WindowManager@" << this << " buffered: " << buffered_ |
| << " bytes: " << bytes; |
| if (buffered_ < bytes) { |
| QUICHE_BUG(bug_2816_1) << "WindowManager@" << this << " buffered underflow " |
| << "buffered_: " << buffered_ << " bytes: " << bytes; |
| buffered_ = 0; |
| } else { |
| buffered_ -= bytes; |
| } |
| MaybeNotifyListener(); |
| } |
| |
| void WindowManager::MaybeNotifyListener() { |
| if (buffered_ + window_ > limit_) { |
| QUICHE_LOG(ERROR) << "Flow control violation; limit: " << limit_ |
| << " buffered: " << buffered_ << " window: " << window_; |
| return; |
| } |
| // For the sake of efficiency, we want to send window updates if less than |
| // half of the max quota is available to the peer at any point in time. |
| const size_t kDesiredMinWindow = limit_ / 2; |
| const size_t kDesiredMinDelta = limit_ / 3; |
| const size_t delta = limit_ - (buffered_ + window_); |
| bool send_update = false; |
| if (delta >= kDesiredMinDelta) { |
| // This particular window update was sent because the available delta |
| // exceeded the desired minimum. |
| send_update = true; |
| } else if (window_ < kDesiredMinWindow) { |
| // This particular window update was sent because the quota available to the |
| // peer at this moment is less than the desired minimum. |
| send_update = true; |
| } |
| if (send_update && delta > 0) { |
| QUICHE_VLOG(2) << "WindowManager@" << this |
| << " Informing listener of delta: " << delta; |
| listener_(delta); |
| window_ += delta; |
| } |
| } |
| |
| } // namespace adapter |
| } // namespace http2 |