Rewrite priority handling

We now use priority queue per stream, which contains the stream which
has ready to send a frame, or one of its descendants have a frame to
send.  We maintain invariant that if a stream is queued, then its
ancestors are also queued (except for root).  When we re-schedule
stream after transmission, we re-schedule all ancestors, so that
streams on the other path can get a chance to send.  This is basically
the same mechanism h2o project uses, but there are differences in the
details.
This commit is contained in:
Tatsuhiro Tsujikawa 2015-08-16 19:01:10 +09:00
parent 73b77964ef
commit 5b59e46e2b
12 changed files with 1056 additions and 1168 deletions

View File

@ -30,6 +30,7 @@
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include <stddef.h>
#include <nghttp2/nghttp2.h>
#include "nghttp2_mem.h"
@ -39,6 +40,12 @@
#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
#define nghttp2_struct_of(ptr, type, member) \
({ \
const typeof(((type *)0)->member) *nghttp2__mptr = (ptr); \
(type *)(void *)((char *)nghttp2__mptr - __builtin_offsetof(type, member)); \
})
/*
* Copies 2 byte unsigned integer |n| in host byte order to |buf| in
* network byte order.

View File

@ -112,7 +112,8 @@ struct nghttp2_outbound_item {
proportional to effective weight (inside a tree). */
uint64_t cycle;
nghttp2_outbound_item *qnext;
/* nonzero if this object is queued. */
/* nonzero if this object is queued, except for DATA or HEADERS
which are attached to stream as item. */
uint8_t queued;
};

View File

@ -24,13 +24,15 @@
*/
#include "nghttp2_pq.h"
#include <stdio.h>
#include <assert.h>
#include "nghttp2_helper.h"
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) {
pq->mem = mem;
pq->capacity = 128;
pq->q = nghttp2_mem_malloc(mem, pq->capacity * sizeof(void *));
if (pq->q == NULL) {
return NGHTTP2_ERR_NOMEM;
}
pq->capacity = 0;
pq->q = NULL;
pq->length = 0;
pq->less = less;
return 0;
@ -42,9 +44,11 @@ void nghttp2_pq_free(nghttp2_pq *pq) {
}
static void swap(nghttp2_pq *pq, size_t i, size_t j) {
void *t = pq->q[i];
nghttp2_pq_entry *t = pq->q[i];
pq->q[i] = pq->q[j];
pq->q[i]->index = i;
pq->q[j] = t;
pq->q[j]->index = j;
}
static void bubble_up(nghttp2_pq *pq, size_t index) {
@ -59,24 +63,29 @@ static void bubble_up(nghttp2_pq *pq, size_t index) {
}
}
int nghttp2_pq_push(nghttp2_pq *pq, void *item) {
int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item) {
if (pq->capacity <= pq->length) {
void *nq;
size_t ncapacity;
ncapacity = nghttp2_max(4, (pq->capacity * 2));
nq = nghttp2_mem_realloc(pq->mem, pq->q,
(pq->capacity * 2) * sizeof(void *));
ncapacity * sizeof(nghttp2_pq_entry *));
if (nq == NULL) {
return NGHTTP2_ERR_NOMEM;
}
pq->capacity *= 2;
pq->capacity = ncapacity;
pq->q = nq;
}
pq->q[pq->length] = item;
item->index = pq->length;
++pq->length;
bubble_up(pq, pq->length - 1);
return 0;
}
void *nghttp2_pq_top(nghttp2_pq *pq) {
nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq) {
if (pq->length == 0) {
return NULL;
} else {
@ -85,11 +94,9 @@ void *nghttp2_pq_top(nghttp2_pq *pq) {
}
static void bubble_down(nghttp2_pq *pq, size_t index) {
size_t lchild = index * 2 + 1;
size_t i, j = index * 2 + 1;
size_t minindex = index;
size_t i, j;
for (i = 0; i < 2; ++i) {
j = lchild + i;
for (i = 0; i < 2; ++i, ++j) {
if (j >= pq->length) {
break;
}
@ -106,11 +113,31 @@ static void bubble_down(nghttp2_pq *pq, size_t index) {
void nghttp2_pq_pop(nghttp2_pq *pq) {
if (pq->length > 0) {
pq->q[0] = pq->q[pq->length - 1];
pq->q[0]->index = 0;
--pq->length;
bubble_down(pq, 0);
}
}
void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item) {
assert(pq->q[item->index] == item);
if (item->index == pq->length - 1) {
--pq->length;
return;
}
pq->q[item->index] = pq->q[pq->length - 1];
pq->q[item->index]->index = item->index;
--pq->length;
if (pq->less(item, pq->q[item->index])) {
bubble_down(pq, item->index);
} else {
bubble_up(pq, item->index);
}
}
int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; }
size_t nghttp2_pq_size(nghttp2_pq *pq) { return pq->length; }
@ -144,3 +171,8 @@ int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) {
}
return 0;
}
void nghttp2_pq_increase_key(nghttp2_pq *pq, nghttp2_pq_entry *item) {
assert(pq->q[item->index] == item);
bubble_down(pq, item->index);
}

View File

@ -35,9 +35,11 @@
/* Implementation of priority queue */
typedef struct { size_t index; } nghttp2_pq_entry;
typedef struct {
/* The pointer to the pointer to the item stored */
void **q;
nghttp2_pq_entry **q;
/* Memory allocator */
nghttp2_mem *mem;
/* The number of items sotred */
@ -75,13 +77,13 @@ void nghttp2_pq_free(nghttp2_pq *pq);
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_pq_push(nghttp2_pq *pq, void *item);
int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item);
/*
* Returns item at the top of the queue |pq|. If the queue is empty,
* this function returns NULL.
*/
void *nghttp2_pq_top(nghttp2_pq *pq);
nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq);
/*
* Pops item at the top of the queue |pq|. The popped item is not
@ -99,7 +101,7 @@ int nghttp2_pq_empty(nghttp2_pq *pq);
*/
size_t nghttp2_pq_size(nghttp2_pq *pq);
typedef int (*nghttp2_pq_item_cb)(void *item, void *arg);
typedef int (*nghttp2_pq_item_cb)(nghttp2_pq_entry *item, void *arg);
/*
* Updates each item in |pq| using function |fun| and re-construct
@ -118,4 +120,15 @@ void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
*/
int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
/*
* Performs "increase-key" operation against |item|, assuming |item|
* is in |pq|, and its key is already updated.
*/
void nghttp2_pq_increase_key(nghttp2_pq *pq, nghttp2_pq_entry *item);
/*
* Removes |item| from priority queue.
*/
void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item);
#endif /* NGHTTP2_PQ_H */

View File

@ -34,6 +34,7 @@
#include "nghttp2_priority_spec.h"
#include "nghttp2_option.h"
#include "nghttp2_http.h"
#include "nghttp2_pq.h"
/*
* Returns non-zero if the number of outgoing opened streams is larger
@ -223,15 +224,6 @@ nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
}
static int outbound_item_less(const void *lhsx, const void *rhsx) {
const nghttp2_outbound_item *lhs, *rhs;
lhs = (const nghttp2_outbound_item *)lhsx;
rhs = (const nghttp2_outbound_item *)rhsx;
return (lhs->cycle < rhs->cycle) ? 1 : 0;
}
static void session_inbound_frame_reset(nghttp2_session *session) {
nghttp2_inbound_frame *iframe = &session->iframe;
nghttp2_mem *mem = &session->mem;
@ -332,11 +324,6 @@ static int session_new(nghttp2_session **session_ptr,
/* next_stream_id is initialized in either
nghttp2_session_client_new2 or nghttp2_session_server_new2 */
rv = nghttp2_pq_init(&(*session_ptr)->ob_da_pq, outbound_item_less, mem);
if (rv != 0) {
goto fail_ob_da_pq;
}
rv = nghttp2_hd_deflate_init(&(*session_ptr)->hd_deflater, mem);
if (rv != 0) {
goto fail_hd_deflater;
@ -352,7 +339,7 @@ static int session_new(nghttp2_session **session_ptr,
nghttp2_stream_init(&(*session_ptr)->root, 0, NGHTTP2_STREAM_FLAG_NONE,
NGHTTP2_STREAM_INITIAL, NGHTTP2_DEFAULT_WEIGHT, 0, 0,
NULL);
NULL, mem);
(*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
(*session_ptr)->recv_window_size = 0;
@ -444,8 +431,6 @@ fail_map:
fail_hd_inflater:
nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater);
fail_hd_deflater:
nghttp2_pq_free(&(*session_ptr)->ob_da_pq);
fail_ob_da_pq:
nghttp2_mem_free(mem, *session_ptr);
fail_session:
return rv;
@ -541,16 +526,6 @@ static int free_streams(nghttp2_map_entry *entry, void *ptr) {
return 0;
}
static void ob_pq_free(nghttp2_pq *pq, nghttp2_mem *mem) {
while (!nghttp2_pq_empty(pq)) {
nghttp2_outbound_item *item = (nghttp2_outbound_item *)nghttp2_pq_top(pq);
nghttp2_outbound_item_free(item, mem);
nghttp2_mem_free(mem, item);
nghttp2_pq_pop(pq);
}
nghttp2_pq_free(pq);
}
static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) {
nghttp2_outbound_item *item, *next;
for (item = q->head; item;) {
@ -620,7 +595,7 @@ void nghttp2_session_del(nghttp2_session *session) {
ob_q_free(&session->ob_urgent, mem);
ob_q_free(&session->ob_reg, mem);
ob_q_free(&session->ob_syn, mem);
ob_pq_free(&session->ob_da_pq, mem);
active_outbound_item_reset(&session->aob, mem);
session_inbound_frame_reset(session);
nghttp2_hd_deflate_free(&session->hd_deflater);
@ -667,14 +642,14 @@ nghttp2_session_reprioritize_stream(nghttp2_session *session,
if (pri_spec->stream_id == 0) {
dep_stream = &session->root;
} else if (nghttp2_stream_dep_subtree_find(stream, dep_stream)) {
} else if (nghttp2_stream_dep_find_ancestor(dep_stream, stream)) {
DEBUGF(fprintf(stderr, "stream: cycle detected, dep_stream(%p)=%d "
"stream(%p)=%d\n",
dep_stream, dep_stream->stream_id, stream,
stream->stream_id));
nghttp2_stream_dep_remove_subtree(dep_stream);
rv = nghttp2_stream_dep_add_subtree(&session->root, dep_stream, session);
rv = nghttp2_stream_dep_add_subtree(&session->root, dep_stream);
if (rv != 0) {
return rv;
}
@ -686,9 +661,9 @@ nghttp2_session_reprioritize_stream(nghttp2_session *session,
stream->weight = pri_spec->weight;
if (pri_spec->exclusive) {
rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream, session);
rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream);
} else {
rv = nghttp2_stream_dep_add_subtree(dep_stream, stream, session);
rv = nghttp2_stream_dep_add_subtree(dep_stream, stream);
}
if (rv != 0) {
@ -719,19 +694,19 @@ int nghttp2_session_add_item(nghttp2_session *session,
/* TODO If 2 HEADERS are submitted for reserved stream, then
both of them are queued into ob_syn, which is not
desirable. */
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST ||
(stream && stream->state == NGHTTP2_STREAM_RESERVED)) {
nghttp2_outbound_queue_push(&session->ob_syn, item);
item->queued = 1;
break;
}
if (stream && (stream->state == NGHTTP2_STREAM_RESERVED ||
item->aux_data.headers.attach_stream)) {
if (stream && item->aux_data.headers.attach_stream) {
if (stream->item) {
return NGHTTP2_ERR_DATA_EXIST;
}
rv = nghttp2_stream_attach_item(stream, item, session);
rv = nghttp2_stream_attach_item(stream, item);
if (rv != 0) {
return rv;
@ -769,7 +744,7 @@ int nghttp2_session_add_item(nghttp2_session *session,
return NGHTTP2_ERR_DATA_EXIST;
}
rv = nghttp2_stream_attach_item(stream, item, session);
rv = nghttp2_stream_attach_item(stream, item);
if (rv != 0) {
return rv;
@ -865,12 +840,18 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
assert(stream->state == NGHTTP2_STREAM_IDLE);
assert(nghttp2_stream_in_dep_tree(stream));
nghttp2_session_detach_idle_stream(session, stream);
nghttp2_stream_dep_remove(stream);
rv = nghttp2_stream_dep_remove(stream);
if (rv != 0) {
return NULL;
}
} else {
if (session->server && initial_state != NGHTTP2_STREAM_IDLE &&
!nghttp2_session_is_my_stream_id(session, stream_id)) {
nghttp2_session_adjust_closed_stream(session, 1);
rv = nghttp2_session_adjust_closed_stream(session, 1);
if (rv != 0) {
return NULL;
}
}
stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream));
@ -916,7 +897,7 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
nghttp2_stream_init(stream, stream_id, flags, initial_state, pri_spec->weight,
session->remote_settings.initial_window_size,
session->local_settings.initial_window_size,
stream_user_data);
stream_user_data, mem);
if (stream_alloc) {
rv = nghttp2_map_insert(&session->streams, &stream->map_entry);
@ -942,7 +923,10 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
/* Idle stream does not count toward the concurrent streams limit.
This is used as anchor node in dependency tree. */
assert(session->server);
nghttp2_session_keep_idle_stream(session, stream);
rv = nghttp2_session_keep_idle_stream(session, stream);
if (rv != 0) {
return NULL;
}
break;
default:
if (nghttp2_session_is_my_stream_id(session, stream_id)) {
@ -968,7 +952,10 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
assert(dep_stream);
if (pri_spec->exclusive) {
nghttp2_stream_dep_insert(dep_stream, stream);
rv = nghttp2_stream_dep_insert(dep_stream, stream);
if (rv != 0) {
return NULL;
}
} else {
nghttp2_stream_dep_add(dep_stream, stream);
}
@ -997,7 +984,7 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
item = stream->item;
rv = nghttp2_stream_detach_item(stream, session);
rv = nghttp2_stream_detach_item(stream);
if (rv != 0) {
return rv;
@ -1045,17 +1032,21 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
/* On server side, retain stream at most MAX_CONCURRENT_STREAMS
combined with the current active incoming streams to make
dependency tree work better. */
nghttp2_session_keep_closed_stream(session, stream);
rv = nghttp2_session_keep_closed_stream(session, stream);
} else {
nghttp2_session_destroy_stream(session, stream);
rv = nghttp2_session_destroy_stream(session, stream);
}
if (rv != 0) {
return rv;
}
return 0;
}
void nghttp2_session_destroy_stream(nghttp2_session *session,
nghttp2_stream *stream) {
int nghttp2_session_destroy_stream(nghttp2_session *session,
nghttp2_stream *stream) {
nghttp2_mem *mem;
int rv;
DEBUGF(fprintf(stderr, "stream: destroy closed stream(%p)=%d\n", stream,
stream->stream_id));
@ -1063,16 +1054,23 @@ void nghttp2_session_destroy_stream(nghttp2_session *session,
mem = &session->mem;
if (nghttp2_stream_in_dep_tree(stream)) {
nghttp2_stream_dep_remove(stream);
rv = nghttp2_stream_dep_remove(stream);
if (rv != 0) {
return rv;
}
}
nghttp2_map_remove(&session->streams, stream->stream_id);
nghttp2_stream_free(stream);
nghttp2_mem_free(mem, stream);
return 0;
}
void nghttp2_session_keep_closed_stream(nghttp2_session *session,
nghttp2_stream *stream) {
int nghttp2_session_keep_closed_stream(nghttp2_session *session,
nghttp2_stream *stream) {
int rv;
DEBUGF(fprintf(stderr, "stream: keep closed stream(%p)=%d, state=%d\n",
stream, stream->stream_id, stream->state));
@ -1086,11 +1084,18 @@ void nghttp2_session_keep_closed_stream(nghttp2_session *session,
++session->num_closed_streams;
nghttp2_session_adjust_closed_stream(session, 0);
rv = nghttp2_session_adjust_closed_stream(session, 0);
if (rv != 0) {
return rv;
}
return 0;
}
void nghttp2_session_keep_idle_stream(nghttp2_session *session,
nghttp2_stream *stream) {
int nghttp2_session_keep_idle_stream(nghttp2_session *session,
nghttp2_stream *stream) {
int rv;
DEBUGF(fprintf(stderr, "stream: keep idle stream(%p)=%d, state=%d\n", stream,
stream->stream_id, stream->state));
@ -1104,7 +1109,12 @@ void nghttp2_session_keep_idle_stream(nghttp2_session *session,
++session->num_idle_streams;
nghttp2_session_adjust_idle_stream(session);
rv = nghttp2_session_adjust_idle_stream(session);
if (rv != 0) {
return rv;
}
return 0;
}
void nghttp2_session_detach_idle_stream(nghttp2_session *session,
@ -1135,9 +1145,10 @@ void nghttp2_session_detach_idle_stream(nghttp2_session *session,
--session->num_idle_streams;
}
void nghttp2_session_adjust_closed_stream(nghttp2_session *session,
ssize_t offset) {
int nghttp2_session_adjust_closed_stream(nghttp2_session *session,
ssize_t offset) {
size_t num_stream_max;
int rv;
num_stream_max = nghttp2_min(session->local_settings.max_concurrent_streams,
session->pending_local_max_concurrent_stream);
@ -1152,12 +1163,22 @@ void nghttp2_session_adjust_closed_stream(nghttp2_session *session,
session->num_closed_streams + session->num_incoming_streams + offset >
num_stream_max) {
nghttp2_stream *head_stream;
nghttp2_stream *next;
head_stream = session->closed_stream_head;
assert(head_stream);
session->closed_stream_head = head_stream->closed_next;
next = head_stream->closed_next;
rv = nghttp2_session_destroy_stream(session, head_stream);
if (rv != 0) {
return rv;
}
/* head_stream is now freed */
session->closed_stream_head = next;
if (session->closed_stream_head) {
session->closed_stream_head->closed_prev = NULL;
@ -1165,14 +1186,15 @@ void nghttp2_session_adjust_closed_stream(nghttp2_session *session,
session->closed_stream_tail = NULL;
}
nghttp2_session_destroy_stream(session, head_stream);
/* head_stream is now freed */
--session->num_closed_streams;
}
return 0;
}
void nghttp2_session_adjust_idle_stream(nghttp2_session *session) {
int nghttp2_session_adjust_idle_stream(nghttp2_session *session) {
size_t max;
int rv;
/* Make minimum number of idle streams 2 so that allocating 2
streams at once is easy. This happens when PRIORITY frame to
@ -1188,11 +1210,21 @@ void nghttp2_session_adjust_idle_stream(nghttp2_session *session) {
while (session->num_idle_streams > max) {
nghttp2_stream *head;
nghttp2_stream *next;
head = session->idle_stream_head;
assert(head);
session->idle_stream_head = head->closed_next;
next = head->closed_next;
rv = nghttp2_session_destroy_stream(session, head);
if (rv != 0) {
return rv;
}
/* head is now destroyed */
session->idle_stream_head = next;
if (session->idle_stream_head) {
session->idle_stream_head->closed_prev = NULL;
@ -1200,10 +1232,10 @@ void nghttp2_session_adjust_idle_stream(nghttp2_session *session) {
session->idle_stream_tail = NULL;
}
nghttp2_session_destroy_stream(session, head);
/* head is now destroyed */
--session->num_idle_streams;
}
return 0;
}
/*
@ -1746,7 +1778,7 @@ static int session_prep_frame(nghttp2_session *session,
if (stream && stream->item == item) {
int rv2;
rv2 = nghttp2_stream_detach_item(stream, session);
rv2 = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv2)) {
return rv2;
@ -1908,7 +1940,7 @@ static int session_prep_frame(nghttp2_session *session,
if (stream) {
int rv2;
rv2 = nghttp2_stream_detach_item(stream, session);
rv2 = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv2)) {
return rv2;
@ -1927,8 +1959,8 @@ static int session_prep_frame(nghttp2_session *session,
queue when session->remote_window_size > 0 */
assert(session->remote_window_size > 0);
rv = nghttp2_stream_defer_item(
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session);
rv = nghttp2_stream_defer_item(stream,
NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
if (nghttp2_is_fatal(rv)) {
return rv;
@ -1943,8 +1975,7 @@ static int session_prep_frame(nghttp2_session *session,
next_readmax, frame, &item->aux_data.data,
stream);
if (rv == NGHTTP2_ERR_DEFERRED) {
rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER,
session);
rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER);
if (nghttp2_is_fatal(rv)) {
return rv;
@ -1955,7 +1986,7 @@ static int session_prep_frame(nghttp2_session *session,
return NGHTTP2_ERR_DEFERRED;
}
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
rv = nghttp2_stream_detach_item(stream, session);
rv = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv)) {
return rv;
@ -1971,7 +2002,7 @@ static int session_prep_frame(nghttp2_session *session,
if (rv != 0) {
int rv2;
rv2 = nghttp2_stream_detach_item(stream, session);
rv2 = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv2)) {
return rv2;
@ -1999,9 +2030,8 @@ nghttp2_session_get_next_ob_item(nghttp2_session *session) {
}
}
if (session->remote_window_size > 0 &&
!nghttp2_pq_empty(&session->ob_da_pq)) {
return nghttp2_pq_top(&session->ob_da_pq);
if (session->remote_window_size > 0) {
return nghttp2_stream_next_outbound_item(&session->root);
}
return NULL;
@ -2034,17 +2064,8 @@ nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
}
}
if (session->remote_window_size > 0 &&
!nghttp2_pq_empty(&session->ob_da_pq)) {
item = nghttp2_pq_top(&session->ob_da_pq);
nghttp2_pq_pop(&session->ob_da_pq);
if (nghttp2_pq_empty(&session->ob_da_pq)) {
session->last_cycle = 0;
}
item->queued = 0;
return item;
if (session->remote_window_size > 0) {
return nghttp2_stream_next_outbound_item(&session->root);
}
return NULL;
@ -2150,24 +2171,10 @@ static int session_close_stream_on_goaway(nghttp2_session *session,
return 0;
}
static void session_outbound_item_schedule(nghttp2_session *session,
nghttp2_outbound_item *item,
int32_t weight) {
/* Schedule next write. Offset proportional to the write size.
Stream with heavier weight is scheduled earlier. */
size_t delta = item->frame.hd.length * NGHTTP2_MAX_WEIGHT / weight;
static void reschedule_stream(nghttp2_stream *stream) {
stream->last_writelen = stream->item->frame.hd.length;
if (session->last_cycle < item->cycle) {
session->last_cycle = item->cycle;
}
/* We pretend to ignore overflow given that the value range of
item->cycle, which is uint64_t. nghttp2 won't explode even when
overflow occurs, there might be some disturbance of priority. We
also reset session->last_cycle to 0, when there is no DATA frame
to send (queue is empty), so the possibility of overflow is
generally very small. */
item->cycle = session->last_cycle + delta;
nghttp2_stream_reschedule(stream);
}
/*
@ -2218,7 +2225,7 @@ static int session_after_frame_sent1(nghttp2_session *session) {
}
if (stream->item == item) {
rv = nghttp2_stream_detach_item(stream, session);
rv = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv)) {
return rv;
@ -2352,7 +2359,7 @@ static int session_after_frame_sent1(nghttp2_session *session) {
}
if (stream && aux_data->eof) {
rv = nghttp2_stream_detach_item(stream, session);
rv = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv)) {
return rv;
@ -2446,7 +2453,6 @@ static int session_after_frame_sent2(nghttp2_session *session) {
return 0;
} else {
nghttp2_outbound_item *next_item;
nghttp2_stream *stream;
nghttp2_data_aux_data *aux_data;
@ -2471,7 +2477,7 @@ static int session_after_frame_sent2(nghttp2_session *session) {
further data. */
if (nghttp2_session_predicate_data_send(session, stream) != 0) {
if (stream) {
rv = nghttp2_stream_detach_item(stream, session);
rv = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv)) {
return rv;
@ -2483,115 +2489,9 @@ static int session_after_frame_sent2(nghttp2_session *session) {
return 0;
}
/* Assuming stream is not NULL */
assert(stream);
next_item = nghttp2_session_get_next_ob_item(session);
/* If priority of this stream is higher or equal to other stream
waiting at the top of the queue, we continue to send this
data. */
if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP &&
(next_item == NULL || (next_item->frame.hd.type == NGHTTP2_DATA &&
outbound_item_less(item, next_item)))) {
size_t next_readmax;
next_readmax = nghttp2_session_next_data_read(session, stream);
if (next_readmax == 0) {
if (session->remote_window_size == 0 &&
stream->remote_window_size > 0) {
/* If DATA cannot be sent solely due to connection level
window size, just push item to queue again. We never pop
DATA item while connection level window size is 0. */
rv = nghttp2_pq_push(&session->ob_da_pq, aob->item);
if (nghttp2_is_fatal(rv)) {
return rv;
}
aob->item->queued = 1;
} else {
rv = nghttp2_stream_defer_item(
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session);
if (nghttp2_is_fatal(rv)) {
return rv;
}
}
aob->item = NULL;
active_outbound_item_reset(aob, mem);
return 0;
}
nghttp2_bufs_reset(framebufs);
rv = nghttp2_session_pack_data(session, framebufs, next_readmax, frame,
aux_data, stream);
if (nghttp2_is_fatal(rv)) {
return rv;
}
if (rv == NGHTTP2_ERR_DEFERRED) {
rv = nghttp2_stream_defer_item(
stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, session);
if (nghttp2_is_fatal(rv)) {
return rv;
}
aob->item = NULL;
active_outbound_item_reset(aob, mem);
return 0;
}
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
/* Stop DATA frame chain and issue RST_STREAM to close the
stream. We don't return
NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE intentionally. */
rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
if (nghttp2_is_fatal(rv)) {
return rv;
}
rv = nghttp2_stream_detach_item(stream, session);
if (nghttp2_is_fatal(rv)) {
return rv;
}
active_outbound_item_reset(aob, mem);
return 0;
}
assert(rv == 0);
if (aux_data->no_copy) {
aob->state = NGHTTP2_OB_SEND_NO_COPY;
} else {
aob->state = NGHTTP2_OB_SEND_DATA;
}
return 0;
}
if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
rv = nghttp2_pq_push(&session->ob_da_pq, aob->item);
if (nghttp2_is_fatal(rv)) {
return rv;
}
aob->item->queued = 1;
}
aob->item = NULL;
active_outbound_item_reset(&session->aob, mem);
return 0;
}
/* Unreachable */
@ -2648,22 +2548,6 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
return 0;
}
if (item->frame.hd.type == NGHTTP2_DATA ||
item->frame.hd.type == NGHTTP2_HEADERS) {
nghttp2_frame *frame;
nghttp2_stream *stream;
frame = &item->frame;
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if (stream && item == stream->item &&
stream->dpri != NGHTTP2_STREAM_DPRI_TOP) {
/* We have DATA with higher priority in queue within the
same dependency tree. */
break;
}
}
rv = session_prep_frame(session, item);
if (rv == NGHTTP2_ERR_DEFERRED) {
DEBUGF(fprintf(stderr, "send: frame transmission deferred\n"));
@ -2835,7 +2719,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
}
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
rv = nghttp2_stream_detach_item(stream, session);
rv = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv)) {
return rv;
@ -3794,7 +3678,7 @@ static int update_remote_initial_window_size_func(nghttp2_map_entry *entry,
nghttp2_stream_check_deferred_by_flow_control(stream)) {
rv = nghttp2_stream_resume_deferred_item(
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, arg->session);
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
if (nghttp2_is_fatal(rv)) {
return rv;
@ -4365,7 +4249,7 @@ static int session_on_stream_window_update_received(nghttp2_session *session,
nghttp2_stream_check_deferred_by_flow_control(stream)) {
rv = nghttp2_stream_resume_deferred_item(
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session);
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
if (nghttp2_is_fatal(rv)) {
return rv;
@ -5970,7 +5854,7 @@ int nghttp2_session_want_write(nghttp2_session *session) {
if (session->aob.item == NULL &&
nghttp2_outbound_queue_top(&session->ob_urgent) == NULL &&
nghttp2_outbound_queue_top(&session->ob_reg) == NULL &&
(nghttp2_pq_empty(&session->ob_da_pq) ||
(nghttp2_pq_empty(&session->root.obq) ||
session->remote_window_size == 0) &&
(nghttp2_outbound_queue_top(&session->ob_syn) == NULL ||
session_is_outgoing_concurrent_streams_max(session))) {
@ -6330,8 +6214,7 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
return rv;
}
session_outbound_item_schedule(
session, stream->item, nghttp2_stream_compute_effective_weight(stream));
reschedule_stream(stream);
return 0;
}
@ -6367,8 +6250,8 @@ int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
rv = nghttp2_stream_resume_deferred_item(
stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, session);
rv = nghttp2_stream_resume_deferred_item(stream,
NGHTTP2_STREAM_FLAG_DEFERRED_USER);
if (nghttp2_is_fatal(rv)) {
return rv;
@ -6380,8 +6263,8 @@ int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) {
return nghttp2_outbound_queue_size(&session->ob_urgent) +
nghttp2_outbound_queue_size(&session->ob_reg) +
nghttp2_outbound_queue_size(&session->ob_syn) +
nghttp2_pq_size(&session->ob_da_pq);
nghttp2_outbound_queue_size(&session->ob_syn);
/* TODO account for item attached to stream */
}
int32_t

View File

@ -30,7 +30,6 @@
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#include "nghttp2_pq.h"
#include "nghttp2_map.h"
#include "nghttp2_frame.h"
#include "nghttp2_hd.h"
@ -163,8 +162,6 @@ struct nghttp2_session {
response) frame, which are subject to
SETTINGS_MAX_CONCURRENT_STREAMS limit. */
nghttp2_outbound_queue ob_syn;
/* Queue for DATA frame */
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_da_pq;
nghttp2_active_outbound_item aob;
nghttp2_inbound_frame iframe;
nghttp2_hd_deflater hd_deflater;
@ -436,26 +433,43 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
* Deletes |stream| from memory. After this function returns, stream
* cannot be accessed.
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
void nghttp2_session_destroy_stream(nghttp2_session *session,
nghttp2_stream *stream);
int nghttp2_session_destroy_stream(nghttp2_session *session,
nghttp2_stream *stream);
/*
* Tries to keep incoming closed stream |stream|. Due to the
* limitation of maximum number of streams in memory, |stream| is not
* closed and just deleted from memory (see
* nghttp2_session_destroy_stream).
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
void nghttp2_session_keep_closed_stream(nghttp2_session *session,
nghttp2_stream *stream);
int nghttp2_session_keep_closed_stream(nghttp2_session *session,
nghttp2_stream *stream);
/*
* Appends |stream| to linked list |session->idle_stream_head|. We
* apply fixed limit for list size. To fit into that limit, one or
* more oldest streams are removed from list as necessary.
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
void nghttp2_session_keep_idle_stream(nghttp2_session *session,
nghttp2_stream *stream);
int nghttp2_session_keep_idle_stream(nghttp2_session *session,
nghttp2_stream *stream);
/*
* Detaches |stream| from idle streams linked list.
@ -469,15 +483,27 @@ void nghttp2_session_detach_idle_stream(nghttp2_session *session,
* stream. If |offset| is nonzero, it is decreased from the maximum
* number of allowed stream when comparing number of active and closed
* stream and the maximum number.
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
void nghttp2_session_adjust_closed_stream(nghttp2_session *session,
ssize_t offset);
int nghttp2_session_adjust_closed_stream(nghttp2_session *session,
ssize_t offset);
/*
* Deletes idle stream to ensure that number of idle streams is in
* certain limit.
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
void nghttp2_session_adjust_idle_stream(nghttp2_session *session);
int nghttp2_session_adjust_idle_stream(nghttp2_session *session);
/*
* If further receptions and transmissions over the stream |stream_id|
@ -699,21 +725,21 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
nghttp2_stream *stream);
/*
* Pops and returns next item to send. If there is no such item,
* Pops and returns next item to send. If there is no such item,
* returns NULL. This function takes into account max concurrent
* streams. That means if session->ob_pq is empty but
* session->ob_ss_pq has item and max concurrent streams is reached,
* then this function returns NULL.
* streams. That means if session->ob_syn has item and max concurrent
* streams is reached, the even if other queues contain items, then
* this function returns NULL.
*/
nghttp2_outbound_item *
nghttp2_session_pop_next_ob_item(nghttp2_session *session);
/*
* Returns next item to send. If there is no such item, this function
* Returns next item to send. If there is no such item, this function
* returns NULL. This function takes into account max concurrent
* streams. That means if session->ob_pq is empty but
* session->ob_ss_pq has item and max concurrent streams is reached,
* then this function returns NULL.
* streams. That means if session->ob_syn has item and max concurrent
* streams is reached, the even if other queues contain items, then
* this function returns NULL.
*/
nghttp2_outbound_item *
nghttp2_session_get_next_ob_item(nghttp2_session *session);

File diff suppressed because it is too large Load Diff

View File

@ -131,13 +131,6 @@ typedef enum {
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13
} nghttp2_http_flag;
typedef enum {
NGHTTP2_STREAM_DPRI_NONE = 0,
NGHTTP2_STREAM_DPRI_NO_ITEM = 0x01,
NGHTTP2_STREAM_DPRI_TOP = 0x02,
NGHTTP2_STREAM_DPRI_REST = 0x04
} nghttp2_stream_dpri;
struct nghttp2_stream_roots;
typedef struct nghttp2_stream_roots nghttp2_stream_roots;
@ -149,6 +142,12 @@ typedef struct nghttp2_stream nghttp2_stream;
struct nghttp2_stream {
/* Intrusive Map */
nghttp2_map_entry map_entry;
/* Entry for dep_prev->obq */
nghttp2_pq_entry pq_entry;
/* Priority Queue storing direct descendant (nghttp2_stream). Only
streams which itself has some data to send, or has a descendant
which has some data to sent. */
nghttp2_pq obq;
/* Content-Length of request/response body. -1 if unknown. */
int64_t content_length;
/* Received body so far */
@ -173,9 +172,6 @@ struct nghttp2_stream {
nghttp2_outbound_item *item;
/* stream ID */
int32_t stream_id;
/* categorized priority of this stream. Only stream bearing
NGHTTP2_STREAM_DPRI_TOP can send item. */
nghttp2_stream_dpri dpri;
/* Current remote window size. This value is computed against the
current initial window size of remote endpoint. */
int32_t remote_window_size;
@ -198,13 +194,6 @@ struct nghttp2_stream {
int32_t weight;
/* sum of weight of direct descendants */
int32_t sum_dep_weight;
/* sum of weight of direct descendants which have at least one
descendant with dpri == NGHTTP2_STREAM_DPRI_TOP. We use this
value to calculate effective weight. This value is only
meaningful iff dpri == NGHTTP2_STREAM_DPRI_NO_ITEM and all
streams along the path to the root stream (follow dep_prev) have
NGHTTP2_STREAM_DPRI_NO_ITEM. */
int32_t sum_norest_weight;
nghttp2_stream_state state;
/* status code from remote server */
int16_t status_code;
@ -214,13 +203,24 @@ struct nghttp2_stream {
uint8_t flags;
/* Bitwise OR of zero or more nghttp2_shut_flag values */
uint8_t shut_flags;
/* Nonzero if this stream has been queued to stream pointed by
dep_prev. We maintain the invariant that if a stream is queued,
then its ancestors, except for root, are also queued. This
invariant may break in fatal error condition. */
uint8_t queued;
/* Base last_cycle for direct descendent streams. */
uint64_t descendant_last_cycle;
/* Next scheduled time to sent item */
uint64_t cycle;
/* Last written length of frame payload */
size_t last_writelen;
};
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
uint8_t flags, nghttp2_stream_state initial_state,
int32_t weight, int32_t remote_initial_window_size,
int32_t local_initial_window_size,
void *stream_user_data);
void *stream_user_data, nghttp2_mem *mem);
void nghttp2_stream_free(nghttp2_stream *stream);
@ -243,8 +243,7 @@ void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag);
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags,
nghttp2_session *session);
int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags);
/*
* Put back deferred data in this stream to active state. The |flags|
@ -253,9 +252,14 @@ int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags,
* NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are
* cleared if they are set. So even if this function is called, if
* one of flag is still set, data does not become active.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags,
nghttp2_session *session);
int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags);
/*
* Returns nonzero if item is deferred by whatever reason.
@ -299,16 +303,10 @@ int nghttp2_stream_update_local_initial_window_size(
void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream);
/*
* Returns the stream positioned in root of the dependency tree the
* |stream| belongs to.
* Returns nonzero if |target| is an ancestor of |stream|.
*/
nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream);
/*
* Returns nonzero if |target| is found in subtree of |stream|.
*/
int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream,
nghttp2_stream *target);
int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream,
nghttp2_stream *target);
/*
* Computes distributed weight of a stream of the |weight| under the
@ -317,34 +315,35 @@ int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream,
int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
int32_t weight);
int32_t nghttp2_stream_compute_effective_weight(nghttp2_stream *stream);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is
* exclusive. All existing direct descendants of |dep_stream| become
* the descendants of the |stream|. This function assumes
* |stream->data| is NULL and no dpri members are changed in this
* dependency tree.
* |stream->item| is NULL.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
nghttp2_stream *stream);
int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
nghttp2_stream *stream);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is
* not exclusive. This function assumes |stream->data| is NULL and no
* dpri members are changed in this dependency tree.
* not exclusive. This function assumes |stream->item| is NULL.
*/
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream);
/*
* Removes the |stream| from the current dependency tree. This
* function assumes |stream->data| is NULL.
* function assumes |stream->item| is NULL.
*/
void nghttp2_stream_dep_remove(nghttp2_stream *stream);
int nghttp2_stream_dep_remove(nghttp2_stream *stream);
/*
* Attaches |item| to |stream|. Updates dpri members in this
* dependency tree.
* Attaches |item| to |stream|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@ -353,13 +352,11 @@ void nghttp2_stream_dep_remove(nghttp2_stream *stream);
* Out of memory
*/
int nghttp2_stream_attach_item(nghttp2_stream *stream,
nghttp2_outbound_item *item,
nghttp2_session *session);
nghttp2_outbound_item *item);
/*
* Detaches |stream->item|. Updates dpri members in this dependency
* tree. This function does not free |stream->item|. The caller must
* free it.
* Detaches |stream->item|. This function does not free
* |stream->item|. The caller must free it.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@ -367,12 +364,11 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream,
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_detach_item(nghttp2_stream *stream,
nghttp2_session *session);
int nghttp2_stream_detach_item(nghttp2_stream *stream);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is
* exclusive. Updates dpri members in this dependency tree.
* exclusive.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@ -381,12 +377,11 @@ int nghttp2_stream_detach_item(nghttp2_stream *stream,
* Out of memory
*/
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream,
nghttp2_session *session);
nghttp2_stream *stream);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is
* not exclusive. Updates dpri members in this dependency tree.
* not exclusive.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@ -395,13 +390,11 @@ int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
* Out of memory
*/
int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream,
nghttp2_session *session);
nghttp2_stream *stream);
/*
* Removes subtree whose root stream is |stream|. Removing subtree
* does not change dpri values. The effective_weight of streams in
* removed subtree is not updated.
* Removes subtree whose root stream is |stream|. The
* effective_weight of streams in removed subtree is not updated.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@ -416,4 +409,16 @@ void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream);
*/
int nghttp2_stream_in_dep_tree(nghttp2_stream *stream);
/*
* Schedules transmission of |stream|'s item, assuming stream->item is
* attached, and stream->last_writelen was updated.
*/
void nghttp2_stream_reschedule(nghttp2_stream *stream);
/*
* Returns a stream which has highest priority.
*/
nghttp2_outbound_item *
nghttp2_stream_next_outbound_item(nghttp2_stream *stream);
#endif /* NGHTTP2_STREAM */

View File

@ -67,6 +67,8 @@ int main(int argc _U_, char *argv[] _U_) {
/* add the tests to the suite */
if (!CU_add_test(pSuite, "pq", test_nghttp2_pq) ||
!CU_add_test(pSuite, "pq_update", test_nghttp2_pq_update) ||
!CU_add_test(pSuite, "pq_remove", test_nghttp2_pq_remove) ||
!CU_add_test(pSuite, "pq_increase_key", test_nghttp2_pq_increase_key) ||
!CU_add_test(pSuite, "map", test_nghttp2_map) ||
!CU_add_test(pSuite, "map_functional", test_nghttp2_map_functional) ||
!CU_add_test(pSuite, "map_each_free", test_nghttp2_map_each_free) ||

View File

@ -28,47 +28,89 @@
#include "nghttp2_pq.h"
typedef struct {
nghttp2_pq_entry ent;
const char *s;
} string_entry;
static string_entry *string_entry_new(const char *s) {
nghttp2_mem *mem;
string_entry *ent;
mem = nghttp2_mem_default();
ent = nghttp2_mem_malloc(mem, sizeof(string_entry));
ent->s = s;
return ent;
}
static void string_entry_del(string_entry *ent) {
free(ent);
}
static int pq_less(const void *lhs, const void *rhs) {
return strcmp(lhs, rhs) < 0;
return strcmp(((string_entry *)lhs)->s, ((string_entry *)rhs)->s) < 0;
}
void test_nghttp2_pq(void) {
int i;
nghttp2_pq pq;
string_entry *top;
nghttp2_pq_init(&pq, pq_less, nghttp2_mem_default());
CU_ASSERT(nghttp2_pq_empty(&pq));
CU_ASSERT(0 == nghttp2_pq_size(&pq));
CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo"));
CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("foo")->ent));
CU_ASSERT(0 == nghttp2_pq_empty(&pq));
CU_ASSERT(1 == nghttp2_pq_size(&pq));
CU_ASSERT(strcmp("foo", nghttp2_pq_top(&pq)) == 0);
CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"bar"));
CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0);
CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"baz"));
CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0);
CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"C"));
top = (string_entry *)nghttp2_pq_top(&pq);
CU_ASSERT(strcmp("foo", top->s) == 0);
CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("bar")->ent));
top = (string_entry *)nghttp2_pq_top(&pq);
CU_ASSERT(strcmp("bar", top->s) == 0);
CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("baz")->ent));
top = (string_entry *)nghttp2_pq_top(&pq);
CU_ASSERT(strcmp("bar", top->s) == 0);
CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("C")->ent));
CU_ASSERT(4 == nghttp2_pq_size(&pq));
CU_ASSERT(strcmp("C", nghttp2_pq_top(&pq)) == 0);
top = (string_entry *)nghttp2_pq_top(&pq);
CU_ASSERT(strcmp("C", top->s) == 0);
string_entry_del(top);
nghttp2_pq_pop(&pq);
CU_ASSERT(3 == nghttp2_pq_size(&pq));
CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0);
top = (string_entry *)nghttp2_pq_top(&pq);
CU_ASSERT(strcmp("bar", top->s) == 0);
nghttp2_pq_pop(&pq);
CU_ASSERT(strcmp("baz", nghttp2_pq_top(&pq)) == 0);
string_entry_del(top);
top = (string_entry *)nghttp2_pq_top(&pq);
CU_ASSERT(strcmp("baz", top->s) == 0);
nghttp2_pq_pop(&pq);
CU_ASSERT(strcmp("foo", nghttp2_pq_top(&pq)) == 0);
string_entry_del(top);
top = (string_entry *)nghttp2_pq_top(&pq);
CU_ASSERT(strcmp("foo", top->s) == 0);
nghttp2_pq_pop(&pq);
string_entry_del(top);
CU_ASSERT(nghttp2_pq_empty(&pq));
CU_ASSERT(0 == nghttp2_pq_size(&pq));
CU_ASSERT(NULL == nghttp2_pq_top(&pq));
/* Add bunch of entry to see realloc works */
for (i = 0; i < 10000; ++i) {
CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo"));
CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("foo")->ent));
CU_ASSERT((size_t)(i + 1) == nghttp2_pq_size(&pq));
}
for (i = 10000; i > 0; --i) {
CU_ASSERT(NULL != nghttp2_pq_top(&pq));
top = (string_entry *)nghttp2_pq_top(&pq);
CU_ASSERT(NULL != top);
nghttp2_pq_pop(&pq);
string_entry_del(top);
CU_ASSERT((size_t)(i - 1) == nghttp2_pq_size(&pq));
}
@ -76,6 +118,7 @@ void test_nghttp2_pq(void) {
}
typedef struct {
nghttp2_pq_entry ent;
int key;
int val;
} node;
@ -86,7 +129,7 @@ static int node_less(const void *lhs, const void *rhs) {
return ln->key < rn->key;
}
static int node_update(void *item, void *arg _U_) {
static int node_update(nghttp2_pq_entry *item, void *arg _U_) {
node *nd = (node *)item;
if ((nd->key % 2) == 0) {
nd->key *= -1;
@ -108,16 +151,107 @@ void test_nghttp2_pq_update(void) {
for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) {
nodes[i].key = i;
nodes[i].val = i;
nghttp2_pq_push(&pq, &nodes[i]);
nghttp2_pq_push(&pq, &nodes[i].ent);
}
nghttp2_pq_update(&pq, node_update, NULL);
for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) {
nd = nghttp2_pq_top(&pq);
nd = (node *)nghttp2_pq_top(&pq);
CU_ASSERT(ans[i] == nd->key);
nghttp2_pq_pop(&pq);
}
nghttp2_pq_free(&pq);
}
static void push_nodes(nghttp2_pq *pq, node *dest, size_t n) {
size_t i;
for (i = 0; i < n; ++i) {
dest[i].key = (int)i;
dest[i].val = (int)i;
nghttp2_pq_push(pq, &dest[i].ent);
}
}
static void check_nodes(nghttp2_pq *pq, size_t n, int *ans_key, int *ans_val) {
size_t i;
for (i = 0; i < n; ++i) {
node *nd = (node *)nghttp2_pq_top(pq);
CU_ASSERT(ans_key[i] == nd->key);
CU_ASSERT(ans_val[i] == nd->val);
nghttp2_pq_pop(pq);
}
}
void test_nghttp2_pq_remove(void) {
nghttp2_pq pq;
node nodes[10];
int ans_key1[] = {1, 2, 3, 4, 5};
int ans_val1[] = {1, 2, 3, 4, 5};
int ans_key2[] = {0, 1, 2, 4, 5};
int ans_val2[] = {0, 1, 2, 4, 5};
int ans_key3[] = {0, 1, 2, 3, 4};
int ans_val3[] = {0, 1, 2, 3, 4};
nghttp2_pq_init(&pq, node_less, nghttp2_mem_default());
push_nodes(&pq, nodes, 6);
nghttp2_pq_remove(&pq, &nodes[0].ent);
check_nodes(&pq, 5, ans_key1, ans_val1);
nghttp2_pq_free(&pq);
nghttp2_pq_init(&pq, node_less, nghttp2_mem_default());
push_nodes(&pq, nodes, 6);
nghttp2_pq_remove(&pq, &nodes[3].ent);
check_nodes(&pq, 5, ans_key2, ans_val2);
nghttp2_pq_free(&pq);
nghttp2_pq_init(&pq, node_less, nghttp2_mem_default());
push_nodes(&pq, nodes, 6);
nghttp2_pq_remove(&pq, &nodes[5].ent);
check_nodes(&pq, 5, ans_key3, ans_val3);
nghttp2_pq_free(&pq);
}
void test_nghttp2_pq_increase_key(void) {
nghttp2_pq pq;
node nodes[10];
int ans_key1[] = {1, 2, 3, 3, 4, 5};
int ans_val1[] = {1, 2, 0, 3, 4, 5};
int ans_key2[] = {0, 1, 2, 3, 4, 6};
int ans_val2[] = {0, 1, 2, 3, 4, 5};
nghttp2_pq_init(&pq, node_less, nghttp2_mem_default());
push_nodes(&pq, nodes, 6);
nodes[0].key = 3;
nghttp2_pq_increase_key(&pq, &nodes[0].ent);
check_nodes(&pq, 6, ans_key1, ans_val1);
nghttp2_pq_free(&pq);
nghttp2_pq_init(&pq, node_less, nghttp2_mem_default());
push_nodes(&pq, nodes, 6);
nodes[5].key = 6;
nghttp2_pq_increase_key(&pq, &nodes[5].ent);
check_nodes(&pq, 6, ans_key2, ans_val2);
nghttp2_pq_free(&pq);
}

View File

@ -27,5 +27,7 @@
void test_nghttp2_pq(void);
void test_nghttp2_pq_update(void);
void test_nghttp2_pq_remove(void);
void test_nghttp2_pq_increase_key(void);
#endif /* NGHTTP2_PQ_TEST_H */

View File

@ -2686,7 +2686,7 @@ void test_nghttp2_session_on_window_update_received(void) {
data_item = create_data_ob_item(mem);
CU_ASSERT(0 == nghttp2_stream_attach_item(stream, data_item, session));
CU_ASSERT(0 == nghttp2_stream_attach_item(stream, data_item));
nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1,
16 * 1024);
@ -2696,9 +2696,8 @@ void test_nghttp2_session_on_window_update_received(void) {
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 ==
stream->remote_window_size);
CU_ASSERT(0 ==
nghttp2_stream_defer_item(
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session));
CU_ASSERT(0 == nghttp2_stream_defer_item(
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL));
CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame));
CU_ASSERT(2 == user_data.frame_recv_cb_called);
@ -4679,7 +4678,7 @@ void test_nghttp2_session_pop_next_ob_item(void) {
stream = nghttp2_session_get_stream(session, 1);
nghttp2_stream_detach_item(stream, session);
nghttp2_stream_detach_item(stream);
nghttp2_outbound_item_free(item, mem);
mem->free(item, NULL);
@ -4887,7 +4886,7 @@ void test_nghttp2_session_defer_data(void) {
/* Resume deferred DATA */
CU_ASSERT(0 == nghttp2_session_resume_data(session, 1));
item = (nghttp2_outbound_item *)nghttp2_pq_top(&session->ob_da_pq);
item = stream->item;
item->aux_data.data.data_prd.read_callback =
fixed_length_data_source_read_callback;
ud.block_count = 1;
@ -4905,7 +4904,6 @@ void test_nghttp2_session_defer_data(void) {
/* Resume deferred DATA */
CU_ASSERT(0 == nghttp2_session_resume_data(session, 1));
item = (nghttp2_outbound_item *)nghttp2_pq_top(&session->ob_da_pq);
item->aux_data.data.data_prd.read_callback =
fixed_length_data_source_read_callback;
ud.block_count = 1;
@ -5165,7 +5163,6 @@ void test_nghttp2_session_data_read_temporal_failure(void) {
CU_ASSERT(data_size - NGHTTP2_INITIAL_WINDOW_SIZE == ud.data_source_length);
stream = nghttp2_session_get_stream(session, 1);
CU_ASSERT(nghttp2_stream_check_deferred_by_flow_control(stream));
CU_ASSERT(NGHTTP2_DATA == stream->item->frame.hd.type);
stream->item->aux_data.data.data_prd.read_callback =
@ -5872,7 +5869,7 @@ void test_nghttp2_session_stream_dep_add_subtree(void) {
*/
nghttp2_stream_dep_remove_subtree(e);
nghttp2_stream_dep_add_subtree(a, e, session);
nghttp2_stream_dep_add_subtree(a, e);
/* becomes
* a
@ -5919,7 +5916,7 @@ void test_nghttp2_session_stream_dep_add_subtree(void) {
*/
nghttp2_stream_dep_remove_subtree(e);
nghttp2_stream_dep_insert_subtree(a, e, session);
nghttp2_stream_dep_insert_subtree(a, e);
/* becomes
* a
@ -6098,7 +6095,7 @@ void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
*/
nghttp2_stream_dep_remove_subtree(c);
CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c, session));
CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c));
/*
* c
@ -6112,9 +6109,9 @@ void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
CU_ASSERT(0 == b->sum_dep_weight);
CU_ASSERT(0 == a->sum_norest_weight);
CU_ASSERT(0 == b->sum_norest_weight);
CU_ASSERT(0 == c->sum_norest_weight);
CU_ASSERT(nghttp2_pq_empty(&a->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(nghttp2_pq_empty(&c->obq));
check_stream_dep_sib(c, root, a, NULL, NULL);
check_stream_dep_sib(a, c, b, NULL, NULL);
@ -6135,7 +6132,7 @@ void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
*/
nghttp2_stream_dep_remove_subtree(c);
CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c, session));
CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c));
/*
* c
@ -6147,9 +6144,9 @@ void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
CU_ASSERT(0 == b->sum_dep_weight);
CU_ASSERT(0 == a->sum_dep_weight);
CU_ASSERT(0 == a->sum_norest_weight);
CU_ASSERT(0 == b->sum_norest_weight);
CU_ASSERT(0 == c->sum_norest_weight);
CU_ASSERT(nghttp2_pq_empty(&a->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(nghttp2_pq_empty(&c->obq));
check_stream_dep_sib(c, root, b, NULL, NULL);
check_stream_dep_sib(b, c, NULL, NULL, a);
@ -6173,7 +6170,7 @@ void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
*/
nghttp2_stream_dep_remove_subtree(c);
CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c, session));
CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c));
/*
* c
@ -6188,10 +6185,10 @@ void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
CU_ASSERT(0 == b->sum_dep_weight);
CU_ASSERT(0 == a->sum_norest_weight);
CU_ASSERT(0 == b->sum_norest_weight);
CU_ASSERT(0 == c->sum_norest_weight);
CU_ASSERT(0 == d->sum_norest_weight);
CU_ASSERT(nghttp2_pq_empty(&a->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(nghttp2_pq_empty(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
check_stream_dep_sib(c, root, d, NULL, NULL);
check_stream_dep_sib(d, c, NULL, NULL, a);
@ -6217,10 +6214,10 @@ void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
db = create_data_ob_item(mem);
nghttp2_stream_attach_item(b, db, session);
nghttp2_stream_attach_item(b, db);
nghttp2_stream_dep_remove_subtree(c);
CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c, session));
CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c));
/*
* c
@ -6229,14 +6226,15 @@ void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
* |
* b
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(16 == a->sum_norest_weight);
CU_ASSERT(16 == c->sum_norest_weight);
CU_ASSERT(0 == d->sum_norest_weight);
CU_ASSERT(c->queued);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(!d->queued);
CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
check_stream_dep_sib(c, root, d, NULL, NULL);
check_stream_dep_sib(d, c, NULL, NULL, a);
@ -6263,11 +6261,11 @@ void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
db = create_data_ob_item(mem);
dc = create_data_ob_item(mem);
nghttp2_stream_attach_item(b, db, session);
nghttp2_stream_attach_item(c, dc, session);
nghttp2_stream_attach_item(b, db);
nghttp2_stream_attach_item(c, dc);
nghttp2_stream_dep_remove_subtree(c);
CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c, session));
CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c));
/*
* c
@ -6277,10 +6275,10 @@ void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
* b
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(c->queued);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(!d->queued);
check_stream_dep_sib(c, root, d, NULL, NULL);
check_stream_dep_sib(d, c, NULL, NULL, a);
@ -6317,94 +6315,82 @@ void test_nghttp2_session_stream_attach_item(void) {
db = create_data_ob_item(mem);
nghttp2_stream_attach_item(b, db, session);
nghttp2_stream_attach_item(b, db);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(!c->queued);
CU_ASSERT(!d->queued);
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(b));
CU_ASSERT(16 == a->sum_norest_weight);
CU_ASSERT(1 == db->queued);
CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
/* Attach item to c */
dc = create_data_ob_item(mem);
nghttp2_stream_attach_item(c, dc, session);
nghttp2_stream_attach_item(c, dc);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(!d->queued);
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(b));
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(c));
CU_ASSERT(32 == a->sum_norest_weight);
CU_ASSERT(1 == dc->queued);
CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
/* Attach item to a */
da = create_data_ob_item(mem);
nghttp2_stream_attach_item(a, da, session);
nghttp2_stream_attach_item(a, da);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(!d->queued);
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(a));
CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
CU_ASSERT(1 == da->queued);
/* Detach item from a */
nghttp2_stream_detach_item(a);
nghttp2_stream_detach_item(a, session);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(!d->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(b));
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(c));
CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
/* Attach item to d */
dd = create_data_ob_item(mem);
nghttp2_stream_attach_item(d, dd, session);
nghttp2_stream_attach_item(d, dd);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(d->queued);
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(b));
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(c));
CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(0 == dd->queued);
/* Detach item from c */
nghttp2_stream_detach_item(c);
nghttp2_stream_detach_item(c, session);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(d->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri);
CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(b));
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(d));
/* Detach item from b */
nghttp2_stream_detach_item(b);
CU_ASSERT(1 == dd->queued);
CU_ASSERT(a->queued);
CU_ASSERT(!b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(d->queued);
nghttp2_stream_detach_item(b, session);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri);
CU_ASSERT(16 * 16 / 16 == nghttp2_stream_compute_effective_weight(d));
CU_ASSERT(1 == dd->queued);
CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
/* exercises insertion */
e = open_stream_with_dep_excl(session, 9, a);
@ -6418,18 +6404,17 @@ void test_nghttp2_session_stream_attach_item(void) {
* d
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(e->queued);
CU_ASSERT(!b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(d->queued);
CU_ASSERT(16 * 16 / 16 == nghttp2_stream_compute_effective_weight(d));
CU_ASSERT(16 == a->sum_norest_weight);
CU_ASSERT(16 == e->sum_norest_weight);
CU_ASSERT(16 == c->sum_norest_weight);
CU_ASSERT(0 == b->sum_norest_weight);
CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(1 == nghttp2_pq_size(&e->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
/* exercises deletion */
nghttp2_stream_dep_remove(e);
@ -6441,18 +6426,28 @@ void test_nghttp2_session_stream_attach_item(void) {
* d
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(!b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(d->queued);
CU_ASSERT(16 * 16 / 16 == nghttp2_stream_compute_effective_weight(d));
CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
/* e's weight 16 is distributed equally among c and b, both now have
weight 8 each. */
CU_ASSERT(8 == a->sum_norest_weight);
CU_ASSERT(16 == c->sum_norest_weight);
CU_ASSERT(0 == b->sum_norest_weight);
CU_ASSERT(8 == b->weight);
CU_ASSERT(8 == c->weight);
/* da, db, dc have been detached */
nghttp2_outbound_item_free(da, mem);
nghttp2_outbound_item_free(db, mem);
nghttp2_outbound_item_free(dc, mem);
free(da);
free(db);
free(dc);
nghttp2_session_del(session);
@ -6474,25 +6469,36 @@ void test_nghttp2_session_stream_attach_item(void) {
db = create_data_ob_item(mem);
dc = create_data_ob_item(mem);
nghttp2_stream_attach_item(a, da, session);
nghttp2_stream_attach_item(b, db, session);
nghttp2_stream_attach_item(c, dc, session);
nghttp2_stream_attach_item(a, da);
nghttp2_stream_attach_item(b, db);
nghttp2_stream_attach_item(c, dc);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(!d->queued);
/* check that all children's item get queued */
nghttp2_stream_detach_item(a, session);
CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(nghttp2_pq_empty(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
/* Detach item from a */
nghttp2_stream_detach_item(a);
CU_ASSERT(1 == db->queued);
CU_ASSERT(1 == dc->queued);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(!d->queued);
CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(nghttp2_pq_empty(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
/* da has been detached */
nghttp2_outbound_item_free(da, mem);
free(da);
nghttp2_session_del(session);
}
@ -6528,30 +6534,30 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
de = create_data_ob_item(mem);
nghttp2_stream_attach_item(e, de, session);
nghttp2_stream_attach_item(e, de);
db = create_data_ob_item(mem);
nghttp2_stream_attach_item(b, db, session);
nghttp2_stream_attach_item(b, db);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(!c->queued);
CU_ASSERT(!d->queued);
CU_ASSERT(e->queued);
CU_ASSERT(!f->queued);
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(b));
CU_ASSERT(32 == nghttp2_stream_compute_effective_weight(e));
CU_ASSERT(16 == a->sum_norest_weight);
CU_ASSERT(0 == c->sum_norest_weight);
CU_ASSERT(0 == d->sum_norest_weight);
CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(nghttp2_pq_empty(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(nghttp2_pq_empty(&e->obq));
CU_ASSERT(nghttp2_pq_empty(&f->obq));
/* Insert subtree e under a */
nghttp2_stream_dep_remove_subtree(e);
nghttp2_stream_dep_insert_subtree(a, e, session);
nghttp2_stream_dep_insert_subtree(a, e);
/*
* a
@ -6563,22 +6569,25 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* d
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(!c->queued);
CU_ASSERT(!d->queued);
CU_ASSERT(e->queued);
CU_ASSERT(!f->queued);
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(e));
CU_ASSERT(32 == a->sum_norest_weight);
CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(nghttp2_pq_empty(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(1 == nghttp2_pq_size(&e->obq));
CU_ASSERT(nghttp2_pq_empty(&f->obq));
/* Remove subtree b */
nghttp2_stream_dep_remove_subtree(b);
CU_ASSERT(0 == nghttp2_stream_dep_add_subtree(&session->root, b, session));
CU_ASSERT(0 == nghttp2_stream_dep_add_subtree(&session->root, b));
/*
* a b
@ -6590,34 +6599,45 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* d
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(!c->queued);
CU_ASSERT(!d->queued);
CU_ASSERT(e->queued);
CU_ASSERT(!f->queued);
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(b));
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(e));
CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(nghttp2_pq_empty(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(nghttp2_pq_empty(&e->obq));
CU_ASSERT(nghttp2_pq_empty(&f->obq));
/* Remove subtree a */
/* Remove subtree a, and add it to root again */
nghttp2_stream_dep_remove_subtree(a);
CU_ASSERT(0 == nghttp2_stream_dep_add_subtree(&session->root, a, session));
CU_ASSERT(0 == nghttp2_stream_dep_add_subtree(&session->root, a));
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(!c->queued);
CU_ASSERT(!d->queued);
CU_ASSERT(e->queued);
CU_ASSERT(!f->queued);
CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(nghttp2_pq_empty(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(nghttp2_pq_empty(&e->obq));
CU_ASSERT(nghttp2_pq_empty(&f->obq));
/* Remove subtree c */
nghttp2_stream_dep_remove_subtree(c);
CU_ASSERT(0 == nghttp2_stream_dep_add_subtree(&session->root, c, session));
CU_ASSERT(0 == nghttp2_stream_dep_add_subtree(&session->root, c));
/*
* a b c
@ -6627,26 +6647,28 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* f
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(!c->queued);
CU_ASSERT(!d->queued);
CU_ASSERT(e->queued);
CU_ASSERT(!f->queued);
CU_ASSERT(32 == a->sum_norest_weight);
CU_ASSERT(0 == c->sum_norest_weight);
CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(nghttp2_pq_empty(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(nghttp2_pq_empty(&e->obq));
CU_ASSERT(nghttp2_pq_empty(&f->obq));
dd = create_data_ob_item(mem);
nghttp2_stream_attach_item(d, dd, session);
CU_ASSERT(16 == c->sum_norest_weight);
nghttp2_stream_attach_item(d, dd);
/* Add subtree c to a */
nghttp2_stream_dep_remove_subtree(c);
nghttp2_stream_dep_add_subtree(a, c, session);
nghttp2_stream_dep_add_subtree(a, c);
/*
* a b
@ -6656,50 +6678,53 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* d f
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(d->queued);
CU_ASSERT(e->queued);
CU_ASSERT(!f->queued);
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(b));
CU_ASSERT(16 * 16 / 48 == nghttp2_stream_compute_effective_weight(d));
CU_ASSERT(16 * 32 / 48 == nghttp2_stream_compute_effective_weight(e));
CU_ASSERT(48 == a->sum_norest_weight);
CU_ASSERT(16 == c->sum_norest_weight);
CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(nghttp2_pq_empty(&e->obq));
CU_ASSERT(nghttp2_pq_empty(&f->obq));
/* Insert b under a */
nghttp2_stream_dep_remove_subtree(b);
nghttp2_stream_dep_insert_subtree(a, b, session);
nghttp2_stream_dep_insert_subtree(a, b);
/*
* a
* |
* b
* |
* e--c
* c--e
* | |
* f d
* d f
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(d->queued);
CU_ASSERT(e->queued);
CU_ASSERT(!f->queued);
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(b));
CU_ASSERT(16 == a->sum_norest_weight);
CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(2 == nghttp2_pq_size(&b->obq));
CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(nghttp2_pq_empty(&e->obq));
CU_ASSERT(nghttp2_pq_empty(&f->obq));
/* Remove subtree b */
nghttp2_stream_dep_remove_subtree(b);
CU_ASSERT(0 == nghttp2_stream_dep_add_subtree(&session->root, b, session));
CU_ASSERT(0 == nghttp2_stream_dep_add_subtree(&session->root, b));
/*
* b a
@ -6709,21 +6734,26 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* f d
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri);
CU_ASSERT(!a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(d->queued);
CU_ASSERT(e->queued);
CU_ASSERT(!f->queued);
CU_ASSERT(0 == a->sum_norest_weight);
CU_ASSERT(nghttp2_pq_empty(&a->obq));
CU_ASSERT(2 == nghttp2_pq_size(&b->obq));
CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(nghttp2_pq_empty(&e->obq));
CU_ASSERT(nghttp2_pq_empty(&f->obq));
/* Remove subtree c, and detach item from b, and then re-add
subtree c under b */
nghttp2_stream_dep_remove_subtree(c);
nghttp2_stream_detach_item(b, session);
nghttp2_stream_dep_add_subtree(b, c, session);
nghttp2_stream_detach_item(b);
nghttp2_stream_dep_add_subtree(b, c);
/*
* b a
@ -6733,21 +6763,26 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* f d
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri);
CU_ASSERT(!a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(d->queued);
CU_ASSERT(e->queued);
CU_ASSERT(!f->queued);
CU_ASSERT(48 == b->sum_norest_weight);
CU_ASSERT(nghttp2_pq_empty(&a->obq));
CU_ASSERT(2 == nghttp2_pq_size(&b->obq));
CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(nghttp2_pq_empty(&e->obq));
CU_ASSERT(nghttp2_pq_empty(&f->obq));
/* Attach data to a, and add subtree a under b */
da = create_data_ob_item(mem);
nghttp2_stream_attach_item(a, da, session);
nghttp2_stream_attach_item(a, da);
nghttp2_stream_dep_remove_subtree(a);
nghttp2_stream_dep_add_subtree(b, a, session);
nghttp2_stream_dep_add_subtree(b, a);
/*
* b
@ -6756,18 +6791,24 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* | |
* f d
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri);
CU_ASSERT(64 == b->sum_norest_weight);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(d->queued);
CU_ASSERT(e->queued);
CU_ASSERT(!f->queued);
CU_ASSERT(nghttp2_pq_empty(&a->obq));
CU_ASSERT(3 == nghttp2_pq_size(&b->obq));
CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(nghttp2_pq_empty(&e->obq));
CU_ASSERT(nghttp2_pq_empty(&f->obq));
/* Remove subtree c, and add under f */
nghttp2_stream_dep_remove_subtree(c);
nghttp2_stream_dep_insert_subtree(f, c, session);
nghttp2_stream_dep_insert_subtree(f, c);
/*
* b
@ -6780,14 +6821,24 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* |
* d
*/
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == a->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == b->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri);
CU_ASSERT(48 == b->sum_norest_weight);
CU_ASSERT(a->queued);
CU_ASSERT(b->queued);
CU_ASSERT(c->queued);
CU_ASSERT(d->queued);
CU_ASSERT(e->queued);
CU_ASSERT(f->queued);
CU_ASSERT(nghttp2_pq_empty(&a->obq));
CU_ASSERT(2 == nghttp2_pq_size(&b->obq));
CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(1 == nghttp2_pq_size(&e->obq));
CU_ASSERT(1 == nghttp2_pq_size(&f->obq));
/* db has been detached */
nghttp2_outbound_item_free(db, mem);
free(db);
nghttp2_session_del(session);
}
@ -6990,7 +7041,7 @@ void test_nghttp2_session_large_dep_tree(void) {
stream_id = 1;
for (i = 0; i < 250; ++i) {
stream = nghttp2_session_get_stream(session, stream_id);
CU_ASSERT(nghttp2_stream_dep_subtree_find(&session->root, stream));
CU_ASSERT(nghttp2_stream_dep_find_ancestor(stream, &session->root));
CU_ASSERT(nghttp2_stream_in_dep_tree(stream));
}