Fix HTTP/2 dependency tree corruption
Motivation: Chrome was randomly getting stuck loading the tiles examples. Investigation showed that the Netty flow controller thought it had nothing to send for the connection even though some streams has queued data and window available. Modifications: Fixed an accounting error where an implicitly created parent was not being added to the dependency tree, thus it and all of its children were orphaned from the connection's tree and would never have data written. Result: Fixes #6621
This commit is contained in:
parent
c6ad9338b3
commit
799350c369
@ -229,6 +229,10 @@ public final class WeightedFairQueueByteDistributor implements StreamByteDistrib
|
|||||||
newParent = new State(parentStreamId);
|
newParent = new State(parentStreamId);
|
||||||
stateOnlyRemovalQueue.add(newParent);
|
stateOnlyRemovalQueue.add(newParent);
|
||||||
stateOnlyMap.put(parentStreamId, newParent);
|
stateOnlyMap.put(parentStreamId, newParent);
|
||||||
|
// Only the stream which was just added will change parents. So we only need an array of size 1.
|
||||||
|
List<ParentChangedEvent> events = new ArrayList<ParentChangedEvent>(1);
|
||||||
|
connectionState.takeChild(newParent, false, events);
|
||||||
|
notifyParentChanged(events);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if activeCountForTree == 0 then it will not be in its parent's pseudoTimeQueue and thus should not be counted
|
// if activeCountForTree == 0 then it will not be in its parent's pseudoTimeQueue and thus should not be counted
|
||||||
|
@ -899,4 +899,38 @@ public class WeightedFairQueueByteDistributorDependencyTreeTest extends
|
|||||||
assertTrue(distributor.isChild(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT));
|
assertTrue(distributor.isChild(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT));
|
||||||
assertEquals(0, distributor.numChildren(streamE.id()));
|
assertEquals(0, distributor.numChildren(streamE.id()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unknown parent streams can come about in two ways:
|
||||||
|
// 1. Because the stream is old and its state was purged
|
||||||
|
// 2. This is the first reference to the stream, as implied at least by RFC7540§5.3.1:
|
||||||
|
// > A dependency on a stream that is not currently in the tree — such as a stream in the
|
||||||
|
// > "idle" state — results in that stream being given a default priority
|
||||||
|
@Test
|
||||||
|
public void unknownParentShouldBeCreatedUnderConnection() throws Exception {
|
||||||
|
setup(5);
|
||||||
|
|
||||||
|
// Purposefully avoid creating streamA's Http2Stream so that is it completely unknown.
|
||||||
|
// It shouldn't matter whether the ID is before or after streamB.id()
|
||||||
|
int streamAId = 1;
|
||||||
|
Http2Stream streamB = connection.local().createStream(3, false);
|
||||||
|
|
||||||
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
||||||
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
||||||
|
|
||||||
|
// Build the tree
|
||||||
|
setPriority(streamB.id(), streamAId, DEFAULT_PRIORITY_WEIGHT, false);
|
||||||
|
|
||||||
|
assertEquals(1, connection.numActiveStreams());
|
||||||
|
|
||||||
|
// Level 0
|
||||||
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
||||||
|
|
||||||
|
// Level 1
|
||||||
|
assertTrue(distributor.isChild(streamAId, connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
||||||
|
assertEquals(1, distributor.numChildren(streamAId));
|
||||||
|
|
||||||
|
// Level 2
|
||||||
|
assertTrue(distributor.isChild(streamB.id(), streamAId, DEFAULT_PRIORITY_WEIGHT));
|
||||||
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user