blob: 81eaa775cad0279f337a7ef7734860c5d3a37ea9 [file] [log] [blame]
#include "quiche/http2/adapter/window_manager.h"
#include <utility>
#include "quiche/common/platform/api/quiche_bug_tracker.h"
#include "quiche/common/platform/api/quiche_logging.h"
namespace http2 {
namespace adapter {
bool DefaultShouldWindowUpdateFn(int64_t limit, int64_t window, int64_t delta) {
// 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 int64_t kDesiredMinWindow = limit / 2;
const int64_t kDesiredMinDelta = limit / 3;
if (delta >= kDesiredMinDelta) {
// This particular window update was sent because the available delta
// exceeded the desired minimum.
return 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.
return true;
}
return false;
}
WindowManager::WindowManager(int64_t window_size_limit,
WindowUpdateListener listener,
ShouldWindowUpdateFn should_window_update_fn,
bool update_window_on_notify)
: limit_(window_size_limit),
window_(window_size_limit),
buffered_(0),
listener_(std::move(listener)),
should_window_update_fn_(std::move(should_window_update_fn)),
update_window_on_notify_(update_window_on_notify) {
if (!should_window_update_fn_) {
should_window_update_fn_ = DefaultShouldWindowUpdateFn;
}
}
void WindowManager::OnWindowSizeLimitChange(const int64_t new_limit) {
QUICHE_VLOG(2) << "WindowManager@" << this
<< " OnWindowSizeLimitChange from old limit of " << limit_
<< " to new limit of " << new_limit;
window_ += (new_limit - limit_);
limit_ = new_limit;
}
void WindowManager::SetWindowSizeLimit(int64_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(int64_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(int64_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() {
const int64_t delta = limit_ - (buffered_ + window_);
if (should_window_update_fn_(limit_, window_, delta) && delta > 0) {
QUICHE_VLOG(2) << "WindowManager@" << this
<< " Informing listener of delta: " << delta;
listener_(delta);
if (update_window_on_notify_) {
window_ += delta;
}
}
}
} // namespace adapter
} // namespace http2