blob: de45dc3497bfd39a9adcd36dd5109885f61d23e7 [file] [log] [blame]
#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