Allow exclusive dependency to stream 0

This commit is contained in:
Tatsuhiro Tsujikawa 2014-04-17 21:15:14 +09:00
parent ac86b51e37
commit aa4d43f31e
10 changed files with 446 additions and 25 deletions

View File

@ -2105,8 +2105,6 @@ void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec);
* @function
*
* Returns nonzero if the |pri_spec| is filled with default values.
* `pri_spec->exclusive` is ignored since it is irrelevant when
* `pri_spec->stream_id == 0`.
*/
int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);

View File

@ -42,7 +42,7 @@ void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec)
int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec)
{
/* Ignore exclusive flag */
return pri_spec->stream_id == 0 &&
pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT;
pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT &&
pri_spec->exclusive == 0;
}

View File

@ -280,6 +280,8 @@ static int nghttp2_session_new(nghttp2_session **session_ptr,
goto fail_map;
}
nghttp2_stream_roots_init(&(*session_ptr)->roots);
(*session_ptr)->next_seq = 0;
(*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
@ -436,6 +438,8 @@ void nghttp2_session_del(nghttp2_session *session)
}
free(session->inflight_iv);
nghttp2_stream_roots_free(&session->roots);
/* Have to free streams first, so that we can check
stream->data_item->queued */
nghttp2_map_each_free(&session->streams, nghttp2_free_streams, NULL);
@ -470,14 +474,21 @@ int nghttp2_session_reprioritize_stream
/* We have to update weight after removing stream from tree */
stream->weight = pri_spec->weight;
rv = nghttp2_stream_dep_make_root(stream, &session->ob_pq);
if(pri_spec->exclusive &&
session->roots.num_streams <= NGHTTP2_MAX_DEP_TREE_LENGTH) {
rv = nghttp2_stream_dep_all_your_stream_are_belong_to_us
(stream, &session->ob_pq);
} else {
rv = nghttp2_stream_dep_make_root(stream, &session->ob_pq);
}
return rv;
}
dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id);
if(dep_stream == NULL) {
if(!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) {
return 0;
}
@ -711,7 +722,7 @@ nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session,
}
nghttp2_stream_init(stream, stream_id, flags, initial_state,
pri_spec->weight,
pri_spec->weight, &session->roots,
session->remote_settings
[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
session->local_settings
@ -747,9 +758,29 @@ nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session,
return stream;
}
if(pri_spec->stream_id == 0) {
++session->roots.num_streams;
if(pri_spec->exclusive &&
session->roots.num_streams <= NGHTTP2_MAX_DEP_TREE_LENGTH) {
rv = nghttp2_stream_dep_all_your_stream_are_belong_to_us
(stream, &session->ob_pq);
/* Since no dpri is changed in dependency tree, the above
function call never fail. */
assert(rv == 0);
} else {
nghttp2_stream_roots_add(&session->roots, stream);
}
return stream;
}
dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id);
if(!dep_stream) {
/* If dep_stream is not part of dependency tree, we don't use it. */
if(!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) {
return stream;
}
@ -831,7 +862,9 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
/* Closes both directions just in case they are not closed yet */
stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED;
if(session->server && !nghttp2_session_is_my_stream_id(session, stream_id)) {
if(session->server &&
nghttp2_stream_in_dep_tree(stream) &&
!nghttp2_session_is_my_stream_id(session, stream_id)) {
/* On server side, retain incoming stream object at most
MAX_CONCURRENT_STREAMS combined with the current active streams
to make dependency tree work better. */

View File

@ -117,6 +117,7 @@ typedef enum {
struct nghttp2_session {
nghttp2_map /* <nghttp2_stream*> */ streams;
nghttp2_stream_roots roots;
/* Queue for outbound frames other than stream-creating HEADERS */
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_pq;
/* Queue for outbound stream-creating HEADERS frame */

View File

@ -33,6 +33,7 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
uint8_t flags,
nghttp2_stream_state initial_state,
int32_t weight,
nghttp2_stream_roots *roots,
int32_t remote_initial_window_size,
int32_t local_initial_window_size,
void *stream_user_data)
@ -61,6 +62,10 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
stream->weight = weight;
stream->effective_weight = stream->weight;
stream->sum_dep_weight = 0;
stream->roots = roots;
stream->root_prev = NULL;
stream->root_next = NULL;
}
void nghttp2_stream_free(nghttp2_stream *stream)
@ -474,6 +479,8 @@ void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
stream_update_dep_length(dep_stream, 1);
stream_update_dep_effective_weight(dep_stream);
++stream->roots->num_streams;
}
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
@ -502,6 +509,8 @@ void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
}
stream_update_dep_effective_weight(dep_stream);
++stream->roots->num_streams;
}
void nghttp2_stream_dep_remove(nghttp2_stream *stream)
@ -568,6 +577,8 @@ void nghttp2_stream_dep_remove(nghttp2_stream *stream)
dep_next = NULL;
}
} else {
nghttp2_stream_roots_remove(stream->roots, stream);
dep_next = NULL;
/* stream is a root of tree. Removing stream makes its
@ -583,6 +594,8 @@ void nghttp2_stream_dep_remove(nghttp2_stream *stream)
/* We already distributed weight of |stream| to this. */
si->effective_weight = si->weight;
nghttp2_stream_roots_add(si->roots, si);
si = next;
}
}
@ -606,6 +619,8 @@ void nghttp2_stream_dep_remove(nghttp2_stream *stream)
stream->dep_next = NULL;
stream->sib_prev = NULL;
stream->sib_next = NULL;
--stream->roots->num_streams;
}
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
@ -748,6 +763,8 @@ void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream)
}
} else {
nghttp2_stream_roots_remove(stream->roots, stream);
dep_prev = NULL;
}
@ -771,6 +788,8 @@ int nghttp2_stream_dep_make_root(nghttp2_stream *stream, nghttp2_pq *pq)
DEBUGF(fprintf(stderr, "stream: dep_make_root stream(%p)=%d\n",
stream, stream->stream_id));
nghttp2_stream_roots_add(stream->roots, stream);
stream_update_dep_set_rest(stream);
stream->effective_weight = stream->weight;
@ -785,3 +804,141 @@ int nghttp2_stream_dep_make_root(nghttp2_stream *stream, nghttp2_pq *pq)
return 0;
}
int nghttp2_stream_dep_all_your_stream_are_belong_to_us
(nghttp2_stream *stream, nghttp2_pq *pq)
{
nghttp2_stream *first, *si;
DEBUGF(fprintf(stderr, "stream: ALL YOUR STREAM ARE BELONG TO US "
"stream(%p)=%d\n",
stream, stream->stream_id));
first = stream->roots->head;
/* stream must not be include in stream->roots->head list */
assert(first != stream);
if(first) {
nghttp2_stream *prev;
prev = first;
DEBUGF(fprintf(stderr, "stream: root stream(%p)=%d\n",
first, first->stream_id));
stream->sum_dep_weight += first->weight;
stream->num_substreams += first->num_substreams;
for(si = first->root_next; si; si = si->root_next) {
assert(si != stream);
DEBUGF(fprintf(stderr, "stream: root stream(%p)=%d\n",
si, si->stream_id));
fprintf(stderr, "w=%d, sum=%d\n",
si->weight, stream->sum_dep_weight);
stream->sum_dep_weight += si->weight;
stream->num_substreams += si->num_substreams;
si->sib_prev = prev;
prev->sib_next = si;
prev = si;
}
if(stream->dep_next) {
nghttp2_stream *last_sib;
last_sib = stream_last_sib(stream->dep_next);
last_sib->sib_next = first;
first->sib_prev = last_sib;
} else {
stream->dep_next = first;
first->dep_prev = stream;
}
}
nghttp2_stream_roots_remove_all(stream->roots);
return nghttp2_stream_dep_make_root(stream, pq);
}
int nghttp2_stream_in_dep_tree(nghttp2_stream *stream)
{
return stream->dep_prev || stream->dep_next ||
stream->sib_prev || stream->sib_next ||
stream->root_next || stream->root_prev ||
stream->roots->head == stream;
}
void nghttp2_stream_roots_init(nghttp2_stream_roots *roots)
{
roots->head = NULL;
roots->num_streams = 0;
}
void nghttp2_stream_roots_free(nghttp2_stream_roots *roots)
{}
void nghttp2_stream_roots_add(nghttp2_stream_roots *roots,
nghttp2_stream *stream)
{
if(roots->head) {
nghttp2_stream *si;
for(si = roots->head; si; si = si->root_next) {
if(si == stream) {
assert(0);
}
}
stream->root_next = roots->head;
roots->head->root_prev = stream;
}
roots->head = stream;
}
void nghttp2_stream_roots_remove(nghttp2_stream_roots *roots,
nghttp2_stream *stream)
{
nghttp2_stream *root_prev, *root_next;
root_prev = stream->root_prev;
root_next = stream->root_next;
if(root_prev) {
root_prev->root_next = root_next;
if(root_next) {
root_next->root_prev = root_prev;
}
} else {
if(root_next) {
root_next->root_prev = NULL;
}
roots->head = root_next;
}
stream->root_prev = NULL;
stream->root_next = NULL;
}
void nghttp2_stream_roots_remove_all(nghttp2_stream_roots *roots)
{
nghttp2_stream *si, *next;
for(si = roots->head; si;) {
next = si->root_next;
si->root_prev = NULL;
si->root_next = NULL;
si = next;
}
roots->head = NULL;
}

View File

@ -102,6 +102,10 @@ typedef enum {
NGHTTP2_STREAM_DPRI_REST = 0x04
} nghttp2_stream_dpri;
struct nghttp2_stream_roots;
typedef struct nghttp2_stream_roots nghttp2_stream_roots;
struct nghttp2_stream;
typedef struct nghttp2_stream nghttp2_stream;
@ -118,11 +122,17 @@ struct nghttp2_stream {
dep_prev and sib_prev are NULL. */
nghttp2_stream *dep_prev, *dep_next;
nghttp2_stream *sib_prev, *sib_next;
/* pointers to track dependency tree root streams. This is
doubly-linked list and first element is pointed by
roots->head. */
nghttp2_stream *root_prev, *root_next;
/* When stream is kept after closure, it may be kept in single
linked list pointed by nghttp2_session closed_stream_head.
closed_next points to the next stream object if it is the element
of the list. */
nghttp2_stream *closed_next;
/* pointer to roots, which tracks dependency tree roots */
nghttp2_stream_roots *roots;
/* The arbitrary data provided by user for this stream. */
void *stream_user_data;
/* DATA frame item */
@ -165,6 +175,7 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
uint8_t flags,
nghttp2_stream_state initial_state,
int32_t weight,
nghttp2_stream_roots *roots,
int32_t remote_initial_window_size,
int32_t local_initial_window_size,
void *stream_user_data);
@ -380,4 +391,40 @@ void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream);
*/
int nghttp2_stream_dep_make_root(nghttp2_stream *stream, nghttp2_pq *pq);
/*
* Makes the |stream| as root and all existing root streams become
* direct children of |stream|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_dep_all_your_stream_are_belong_to_us
(nghttp2_stream *stream, nghttp2_pq *pq);
/*
* Returns nonzero if |stream| is in any dependency tree.
*/
int nghttp2_stream_in_dep_tree(nghttp2_stream *stream);
struct nghttp2_stream_roots {
nghttp2_stream *head;
int32_t num_streams;
};
void nghttp2_stream_roots_init(nghttp2_stream_roots *roots);
void nghttp2_stream_roots_free(nghttp2_stream_roots *roots);
void nghttp2_stream_roots_add(nghttp2_stream_roots *roots,
nghttp2_stream *stream);
void nghttp2_stream_roots_remove(nghttp2_stream_roots *roots,
nghttp2_stream *stream);
void nghttp2_stream_roots_remove_all(nghttp2_stream_roots *roots);
#endif /* NGHTTP2_STREAM */

View File

@ -103,10 +103,6 @@ static int nghttp2_submit_headers_shared
static void adjust_priority_spec_weight(nghttp2_priority_spec *pri_spec)
{
if(pri_spec->stream_id == 0) {
pri_spec->exclusive = 0;
}
if(pri_spec->weight < NGHTTP2_MIN_WEIGHT) {
pri_spec->weight = NGHTTP2_MIN_WEIGHT;
} else if(pri_spec->weight > NGHTTP2_MAX_WEIGHT) {

View File

@ -222,6 +222,8 @@ int main(int argc, char* argv[])
test_nghttp2_session_stream_dep_add_subtree) ||
!CU_add_test(pSuite, "session_stream_dep_remove_subtree",
test_nghttp2_session_stream_dep_remove_subtree) ||
!CU_add_test(pSuite, "session_stream_dep_all_your_stream_are_belong_to_us",
test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us) ||
!CU_add_test(pSuite, "session_stream_attach_data",
test_nghttp2_session_stream_attach_data) ||
!CU_add_test(pSuite, "session_stream_attach_data_subtree",

View File

@ -4873,6 +4873,10 @@ void test_nghttp2_session_stream_dep_add(void)
check_stream_dep_sib(c, NULL, d, b, NULL);
check_stream_dep_sib(d, c, NULL, NULL, NULL);
CU_ASSERT(4 == session->roots.num_streams);
CU_ASSERT(a == session->roots.head);
CU_ASSERT(NULL == a->root_next);
e = open_stream_with_dep_excl(session, 9, a);
/* a
@ -4902,6 +4906,10 @@ void test_nghttp2_session_stream_dep_add(void)
check_stream_dep_sib(c, NULL, d, b, NULL);
check_stream_dep_sib(d, c, NULL, NULL, NULL);
CU_ASSERT(5 == session->roots.num_streams);
CU_ASSERT(a == session->roots.head);
CU_ASSERT(NULL == a->root_next);
nghttp2_session_del(session);
}
@ -4951,6 +4959,11 @@ void test_nghttp2_session_stream_dep_remove(void)
check_stream_dep_sib(c, NULL, d, NULL, NULL);
check_stream_dep_sib(d, c, NULL, NULL, NULL);
CU_ASSERT(3 == session->roots.num_streams);
CU_ASSERT(c == session->roots.head);
CU_ASSERT(b == c->root_next);
CU_ASSERT(NULL == b->root_next);
nghttp2_session_del(session);
/* Remove left most stream */
@ -4993,6 +5006,10 @@ void test_nghttp2_session_stream_dep_remove(void)
check_stream_dep_sib(c, a, d, NULL, NULL);
check_stream_dep_sib(d, c, NULL, NULL, NULL);
CU_ASSERT(3 == session->roots.num_streams);
CU_ASSERT(a == session->roots.head);
CU_ASSERT(NULL == a->root_next);
nghttp2_session_del(session);
/* Remove right most stream */
@ -5348,6 +5365,131 @@ void test_nghttp2_session_stream_dep_remove_subtree(void)
nghttp2_session_del(session);
}
void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void)
{
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
nghttp2_stream *a, *b, *c, *d;
memset(&callbacks, 0, sizeof(callbacks));
nghttp2_session_server_new(&session, &callbacks, NULL);
a = open_stream(session, 1);
b = open_stream_with_dep(session, 3, a);
c = open_stream(session, 5);
/* a c
* |
* b
*/
nghttp2_stream_dep_remove_subtree(c);
CU_ASSERT(0 == nghttp2_stream_dep_all_your_stream_are_belong_to_us
(c, &session->ob_pq));
/*
* c
* |
* a
* |
* b
*/
CU_ASSERT(3 == c->num_substreams);
CU_ASSERT(2 == a->num_substreams);
CU_ASSERT(1 == b->num_substreams);
CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight);
CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
CU_ASSERT(0 == b->sum_dep_weight);
check_stream_dep_sib(c, NULL, a, NULL, NULL);
check_stream_dep_sib(a, c, b, NULL, NULL);
check_stream_dep_sib(b, a, NULL, NULL, NULL);
nghttp2_session_del(session);
nghttp2_session_server_new(&session, &callbacks, NULL);
a = open_stream(session, 1);
b = open_stream(session, 3);
c = open_stream(session, 5);
/*
* a b c
*/
nghttp2_stream_dep_remove_subtree(c);
CU_ASSERT(0 == nghttp2_stream_dep_all_your_stream_are_belong_to_us
(c, &session->ob_pq));
/*
* c
* |
* b--a
*/
CU_ASSERT(3 == c->num_substreams);
CU_ASSERT(1 == a->num_substreams);
CU_ASSERT(1 == b->num_substreams);
CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight);
CU_ASSERT(0 == b->sum_dep_weight);
CU_ASSERT(0 == a->sum_dep_weight);
check_stream_dep_sib(c, NULL, b, NULL, NULL);
check_stream_dep_sib(b, c, NULL, NULL, a);
check_stream_dep_sib(a, NULL, NULL, b, NULL);
nghttp2_session_del(session);
nghttp2_session_server_new(&session, &callbacks, NULL);
a = open_stream(session, 1);
b = open_stream_with_dep(session, 3, a);
c = open_stream(session, 5);
d = open_stream_with_dep(session, 7, c);
/* a c
* | |
* b d
*/
nghttp2_stream_dep_remove_subtree(c);
CU_ASSERT(0 == nghttp2_stream_dep_all_your_stream_are_belong_to_us
(c, &session->ob_pq));
/*
* c
* |
* d--a
* |
* b
*/
CU_ASSERT(4 == c->num_substreams);
CU_ASSERT(1 == d->num_substreams);
CU_ASSERT(2 == a->num_substreams);
CU_ASSERT(1 == b->num_substreams);
CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight);
CU_ASSERT(0 == d->sum_dep_weight);
CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
CU_ASSERT(0 == b->sum_dep_weight);
check_stream_dep_sib(c, NULL, d, NULL, NULL);
check_stream_dep_sib(d, c, NULL, NULL, a);
check_stream_dep_sib(a, NULL, b, d, NULL);
check_stream_dep_sib(b, a, NULL, NULL, NULL);
nghttp2_session_del(session);
}
static nghttp2_outbound_item* create_data_ob_item(void)
{
nghttp2_outbound_item *item;
@ -5488,6 +5630,7 @@ void test_nghttp2_session_stream_attach_data_subtree(void)
/* Insert subtree e under a */
nghttp2_stream_dep_remove_subtree(e);
nghttp2_stream_dep_insert_subtree(a, e, &session->ob_pq);
/*
@ -5570,6 +5713,7 @@ void test_nghttp2_session_stream_attach_data_subtree(void)
/* Add subtree c to a */
nghttp2_stream_dep_remove_subtree(c);
nghttp2_stream_dep_add_subtree(a, c, &session->ob_pq);
/*
@ -5589,6 +5733,7 @@ void test_nghttp2_session_stream_attach_data_subtree(void)
/* Insert b under a */
nghttp2_stream_dep_remove_subtree(b);
nghttp2_stream_dep_insert_subtree(a, b, &session->ob_pq);
/*
@ -5608,21 +5753,10 @@ void test_nghttp2_session_stream_attach_data_subtree(void)
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_DATA == f->dpri);
nghttp2_stream_dep_make_root(a, &session->ob_pq);
/*
* a
* |
* b
* |
* e--c
* | |
* f d
*/
/* Remove subtree b */
nghttp2_stream_dep_remove_subtree(b);
nghttp2_stream_dep_make_root(b, &session->ob_pq);
/*
* b a
@ -5870,6 +6004,58 @@ void test_nghttp2_session_stream_dep_effective_weight(void)
CU_ASSERT(16 * 4 / 20 == d->effective_weight);
nghttp2_session_del(session);
nghttp2_session_server_new(&session, &callbacks, NULL);
a = open_stream(session, 1);
b = open_stream_with_dep_weight(session, 3, 8, a);
c = open_stream_with_dep_weight(session, 5, 4, a);
d = open_stream_with_dep_weight(session, 7, 1, c);
e = open_stream(session, 9);
f = open_stream_with_dep_weight(session, 11, 12, e);
/* a e
* | |
* b--c f
* |
* d
*/
CU_ASSERT(e->roots->head == e);
CU_ASSERT(a == e->root_next);
/* Insert e under stream 0 */
nghttp2_stream_dep_remove_subtree(e);
CU_ASSERT(a == e->roots->head);
CU_ASSERT(0 == nghttp2_stream_dep_all_your_stream_are_belong_to_us
(e, &session->ob_pq));
/* e
* |
* f--a
* |
* b--c
* |
* d
*/
CU_ASSERT(6 == e->num_substreams);
CU_ASSERT(28 == e->sum_dep_weight);
CU_ASSERT(16 == e->effective_weight);
CU_ASSERT(16 * 16 / 28 == a->effective_weight);
CU_ASSERT(16 * 12 / 28 == f->effective_weight);
CU_ASSERT((16 * 16 / 28) * 8 / 12 == b->effective_weight);
CU_ASSERT((16 * 16 / 28) * 4 / 12 == c->effective_weight);
CU_ASSERT((16 * 16 / 28) * 4 / 12 == d->effective_weight);
nghttp2_session_del(session);
}
void test_nghttp2_session_keep_closed_stream(void)

View File

@ -103,6 +103,7 @@ void test_nghttp2_session_stream_dep_add(void);
void test_nghttp2_session_stream_dep_remove(void);
void test_nghttp2_session_stream_dep_add_subtree(void);
void test_nghttp2_session_stream_dep_remove_subtree(void);
void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void);
void test_nghttp2_session_stream_attach_data(void);
void test_nghttp2_session_stream_attach_data_subtree(void);
void test_nghttp2_session_stream_dep_effective_weight(void);