Bug 1493204 - Add pushed streams to the priority tree. r=dragana

Previously, we had not put pushed streams in the priority tree, we just
let them be top-level items in the tree. With this change, we will put
them into the tree initially based on the priority of the associated
stream. The only exception is if the associated stream is either a
Leader or Urgent Start (in which case, we will turn the pushed streams
into followers).

Once the pushed stream is matched with a request generated by gecko,
that pushed stream will be re-prioritized based on the priority gecko
has for the request, just like a regular pulled stream.

This also allows us to re-prioritize pushed streams into the background
on tab switch (we assume that, before they are matched, they belong to
the same window as the associated stream).

Differential Revision: https://phabricator.services.mozilla.com/D7223

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Nicholas Hurley 2018-10-03 09:40:23 +00:00
Родитель a56f9d6b1a
Коммит 44ca650a2b
5 изменённых файлов: 96 добавлений и 32 удалений

Просмотреть файл

@ -81,7 +81,18 @@ Http2PushedStream::Http2PushedStream(Http2PushTransactionBuffer *aTransaction,
mBufferedPush->SetPushStream(this);
mRequestContext = aAssociatedStream->RequestContext();
mLastRead = TimeStamp::Now();
SetPriority(aAssociatedStream->Priority() + 1);
mPriorityDependency = aAssociatedStream->PriorityDependency();
if (mPriorityDependency == Http2Session::kUrgentStartGroupID ||
mPriorityDependency == Http2Session::kLeaderGroupID) {
mPriorityDependency = Http2Session::kFollowerGroupID;
}
// Cache this for later use in case of tab switch.
mDefaultPriorityDependency = mPriorityDependency;
SetPriorityDependency(aAssociatedStream->Priority() + 1, mPriorityDependency);
// Assume we are on the same tab as our associated stream, for priority purposes.
// It's possible this could change when we get paired with a sink, but it's unlikely
// and doesn't much matter anyway.
mTransactionTabId = aAssociatedStream->TransactionTabId();
}
bool
@ -310,6 +321,36 @@ Http2PushedStream::GetBufferedData(char *buf,
return rv;
}
void
Http2PushedStream::TopLevelOuterContentWindowIdChanged(uint64_t windowId)
{
if (mConsumerStream) {
// Pass through to our sink, who will handle things appropriately.
mConsumerStream->TopLevelOuterContentWindowIdChangedInternal(windowId);
return;
}
MOZ_ASSERT(gHttpHandler->ActiveTabPriority());
mCurrentForegroundTabOuterContentWindowId = windowId;
if (!mSession->UseH2Deps()) {
return;
}
uint32_t oldDependency = mPriorityDependency;
if (mTransactionTabId != mCurrentForegroundTabOuterContentWindowId) {
mPriorityDependency = Http2Session::kBackgroundGroupID;
nsHttp::NotifyActiveTabLoadOptimization();
} else {
mPriorityDependency = mDefaultPriorityDependency;
}
if (mPriorityDependency != oldDependency) {
mSession->SendPriorityFrame(mStreamID, mPriorityDependency, mPriorityWeight);
}
}
//////////////////////////////////////////
// Http2PushTransactionBuffer
// This is the nsAHttpTransction owned by the stream when the pushed

Просмотреть файл

@ -69,6 +69,7 @@ public:
// overload of Http2Stream
virtual bool HasSink() override { return !!mConsumerStream; }
virtual void SetPushComplete() override { mPushCompleted = true; }
virtual void TopLevelOuterContentWindowIdChanged(uint64_t) override;
nsCString &GetRequestString() { return mRequestString; }
@ -99,6 +100,7 @@ private:
bool mOnPushFailed;
nsCString mRequestString;
uint32_t mDefaultPriorityDependency;
};
class Http2PushTransactionBuffer final : public nsAHttpTransaction

Просмотреть файл

@ -1533,11 +1533,11 @@ Http2Session::RecvPriority(Http2Session *self)
bool exclusive = !!(newPriorityDependency & 0x80000000);
newPriorityDependency &= 0x7fffffff;
uint8_t newPriorityWeight = *(self->mInputFrameBuffer.get() + kFrameHeaderBytes + 4);
if (self->mInputFrameDataStream) {
self->mInputFrameDataStream->SetPriorityDependency(newPriorityDependency,
newPriorityWeight,
exclusive);
}
// undefined what it means when the server sends a priority frame. ignore it.
LOG3(("Http2Session::RecvPriority %p 0x%X received dependency=0x%X "
"weight=%u exclusive=%d", self->mInputFrameDataStream, self->mInputFrameID,
newPriorityDependency, newPriorityWeight, exclusive));
self->ResetDownstreamState();
return NS_OK;
@ -1982,10 +1982,9 @@ Http2Session::RecvPushPromise(Http2Session *self)
pushedStream->SetHTTPState(Http2Stream::RESERVED_BY_REMOTE);
static_assert(Http2Stream::kWorstPriority >= 0,
"kWorstPriority out of range");
uint8_t priorityWeight = (nsISupportsPriority::PRIORITY_LOWEST + 1) -
(Http2Stream::kWorstPriority - Http2Stream::kNormalPriority);
pushedStream->SetPriority(Http2Stream::kWorstPriority);
self->GeneratePriority(promisedID, priorityWeight);
uint32_t priorityDependency = pushedStream->PriorityDependency();
uint8_t priorityWeight = pushedStream->PriorityWeight();
self->SendPriorityFrame(promisedID, priorityDependency, priorityWeight);
self->ResetDownstreamState();
return NS_OK;
}

Просмотреть файл

@ -51,6 +51,8 @@ Http2Stream::Http2Stream(nsAHttpTransaction *httpTransaction,
, mAllHeadersReceived(0)
, mQueued(0)
, mSocketTransport(session->SocketTransport())
, mCurrentForegroundTabOuterContentWindowId(windowId)
, mTransactionTabId(0)
, mTransaction(httpTransaction)
, mChunkSize(session->SendingChunkSize())
, mRequestBlockedOnRead(0)
@ -73,8 +75,6 @@ Http2Stream::Http2Stream(nsAHttpTransaction *httpTransaction,
, mTotalRead(0)
, mPushSource(nullptr)
, mAttempting0RTT(false)
, mCurrentForegroundTabOuterContentWindowId(windowId)
, mTransactionTabId(0)
, mIsTunnel(false)
, mPlainTextTunnel(false)
{
@ -789,6 +789,9 @@ Http2Stream::AdjustPushedPriority()
if (mPushSource->RecvdFin() || mPushSource->RecvdReset())
return;
// Ensure we pick up the right dependency to place the pushed stream under.
UpdatePriorityDependency();
EnsureBuffer(mTxInlineFrame, mTxInlineFrameUsed + Http2Session::kFrameHeaderBytes + 5,
mTxInlineFrameUsed, mTxInlineFrameSize);
uint8_t *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
@ -798,12 +801,13 @@ Http2Stream::AdjustPushedPriority()
Http2Session::FRAME_TYPE_PRIORITY, 0,
mPushSource->mStreamID);
mPushSource->SetPriority(mPriority);
memset(packet + Http2Session::kFrameHeaderBytes, 0, 4);
mPushSource->SetPriorityDependency(mPriority, mPriorityDependency);
uint32_t wireDep = PR_htonl(mPriorityDependency);
memcpy(packet + Http2Session::kFrameHeaderBytes, &wireDep, 4);
memcpy(packet + Http2Session::kFrameHeaderBytes + 4, &mPriorityWeight, 1);
LOG3(("AdjustPushedPriority %p id 0x%X to weight %X\n", this, mPushSource->mStreamID,
mPriorityWeight));
LOG3(("AdjustPushedPriority %p id 0x%X to dep %X weight %X\n", this, mPushSource->mStreamID,
mPriorityDependency, mPriorityWeight));
}
void
@ -1241,13 +1245,10 @@ Http2Stream::SetPriority(uint32_t newPriority)
}
void
Http2Stream::SetPriorityDependency(uint32_t newDependency, uint8_t newWeight,
bool exclusive)
Http2Stream::SetPriorityDependency(uint32_t newPriority, uint32_t newDependency)
{
// undefined what it means when the server sends a priority frame. ignore it.
LOG3(("Http2Stream::SetPriorityDependency %p 0x%X received dependency=0x%X "
"weight=%u exclusive=%d", this, mStreamID, newDependency, newWeight,
exclusive));
SetPriority(newPriority);
mPriorityDependency = newDependency;
}
static uint32_t
@ -1332,6 +1333,19 @@ Http2Stream::UpdatePriorityDependency()
void
Http2Stream::TopLevelOuterContentWindowIdChanged(uint64_t windowId)
{
if (!mStreamID) {
// For pushed streams, we ignore the direct call from the session and
// instead let it come to the internal function from the pushed stream, so
// we don't accidentally send two PRIORITY frames for the same stream.
return;
}
TopLevelOuterContentWindowIdChangedInternal(windowId);
}
void
Http2Stream::TopLevelOuterContentWindowIdChangedInternal(uint64_t windowId)
{
MOZ_ASSERT(gHttpHandler->ActiveTabPriority());
@ -1368,8 +1382,12 @@ Http2Stream::TopLevelOuterContentWindowIdChanged(uint64_t windowId)
"depends on stream 0x%X\n", this, mPriorityDependency));
}
if (mStreamID) {
mSession->SendPriorityFrame(mStreamID, mPriorityDependency, mPriorityWeight);
uint32_t modifyStreamID = mStreamID;
if (!modifyStreamID && mPushSource) {
modifyStreamID = mPushSource->StreamID();
}
if (modifyStreamID) {
mSession->SendPriorityFrame(modifyStreamID, mPriorityDependency, mPriorityWeight);
}
}

Просмотреть файл

@ -148,10 +148,14 @@ public:
bool BlockedOnRwin() { return mBlockedOnRwin; }
uint32_t Priority() { return mPriority; }
uint32_t PriorityDependency() { return mPriorityDependency; }
uint8_t PriorityWeight() { return mPriorityWeight; }
void SetPriority(uint32_t);
void SetPriorityDependency(uint32_t, uint8_t, bool);
void SetPriorityDependency(uint32_t, uint32_t);
void UpdatePriorityDependency();
uint64_t TransactionTabId() { return mTransactionTabId; }
// A pull stream has an implicit sink, a pushed stream has a sink
// once it is matched to a pull stream.
virtual bool HasSink() { return true; }
@ -176,7 +180,8 @@ public:
nsresult GetOriginAttributes(mozilla::OriginAttributes *oa);
void TopLevelOuterContentWindowIdChanged(uint64_t windowId);
virtual void TopLevelOuterContentWindowIdChanged(uint64_t windowId);
void TopLevelOuterContentWindowIdChangedInternal(uint64_t windowId); // For use by pushed streams only
protected:
static void CreatePushHashKey(const nsCString &scheme,
@ -241,6 +246,11 @@ protected:
// The underlying socket transport object is needed to propogate some events
nsISocketTransport *mSocketTransport;
uint8_t mPriorityWeight; // h2 weight
uint32_t mPriorityDependency; // h2 stream id this one depends on
uint64_t mCurrentForegroundTabOuterContentWindowId;
uint64_t mTransactionTabId;
private:
friend class nsAutoPtr<Http2Stream>;
@ -318,8 +328,6 @@ private:
int64_t mRequestBodyLenRemaining;
uint32_t mPriority; // geckoish weight
uint32_t mPriorityDependency; // h2 stream id 3 - 0xb
uint8_t mPriorityWeight; // h2 weight
// mClientReceiveWindow, mServerReceiveWindow, and mLocalUnacked are for flow control.
// *window are signed because the race conditions in asynchronous SETTINGS
@ -355,10 +363,6 @@ private:
bool mAttempting0RTT;
uint64_t mCurrentForegroundTabOuterContentWindowId;
uint64_t mTransactionTabId;
/// connect tunnels
public:
bool IsTunnel() { return mIsTunnel; }