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 */ #endif /* HAVE_CONFIG_H */
#include <string.h> #include <string.h>
#include <stddef.h>
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "nghttp2_mem.h" #include "nghttp2_mem.h"
@ -39,6 +40,12 @@
#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) #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 * Copies 2 byte unsigned integer |n| in host byte order to |buf| in
* network byte order. * network byte order.

View File

@ -112,7 +112,8 @@ struct nghttp2_outbound_item {
proportional to effective weight (inside a tree). */ proportional to effective weight (inside a tree). */
uint64_t cycle; uint64_t cycle;
nghttp2_outbound_item *qnext; 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; uint8_t queued;
}; };

View File

@ -24,13 +24,15 @@
*/ */
#include "nghttp2_pq.h" #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) { int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) {
pq->mem = mem; pq->mem = mem;
pq->capacity = 128; pq->capacity = 0;
pq->q = nghttp2_mem_malloc(mem, pq->capacity * sizeof(void *)); pq->q = NULL;
if (pq->q == NULL) {
return NGHTTP2_ERR_NOMEM;
}
pq->length = 0; pq->length = 0;
pq->less = less; pq->less = less;
return 0; 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) { 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] = pq->q[j];
pq->q[i]->index = i;
pq->q[j] = t; pq->q[j] = t;
pq->q[j]->index = j;
} }
static void bubble_up(nghttp2_pq *pq, size_t index) { 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) { if (pq->capacity <= pq->length) {
void *nq; void *nq;
size_t ncapacity;
ncapacity = nghttp2_max(4, (pq->capacity * 2));
nq = nghttp2_mem_realloc(pq->mem, pq->q, nq = nghttp2_mem_realloc(pq->mem, pq->q,
(pq->capacity * 2) * sizeof(void *)); ncapacity * sizeof(nghttp2_pq_entry *));
if (nq == NULL) { if (nq == NULL) {
return NGHTTP2_ERR_NOMEM; return NGHTTP2_ERR_NOMEM;
} }
pq->capacity *= 2; pq->capacity = ncapacity;
pq->q = nq; pq->q = nq;
} }
pq->q[pq->length] = item; pq->q[pq->length] = item;
item->index = pq->length;
++pq->length; ++pq->length;
bubble_up(pq, pq->length - 1); bubble_up(pq, pq->length - 1);
return 0; return 0;
} }
void *nghttp2_pq_top(nghttp2_pq *pq) { nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq) {
if (pq->length == 0) { if (pq->length == 0) {
return NULL; return NULL;
} else { } else {
@ -85,11 +94,9 @@ void *nghttp2_pq_top(nghttp2_pq *pq) {
} }
static void bubble_down(nghttp2_pq *pq, size_t index) { 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 minindex = index;
size_t i, j; for (i = 0; i < 2; ++i, ++j) {
for (i = 0; i < 2; ++i) {
j = lchild + i;
if (j >= pq->length) { if (j >= pq->length) {
break; break;
} }
@ -106,11 +113,31 @@ static void bubble_down(nghttp2_pq *pq, size_t index) {
void nghttp2_pq_pop(nghttp2_pq *pq) { void nghttp2_pq_pop(nghttp2_pq *pq) {
if (pq->length > 0) { if (pq->length > 0) {
pq->q[0] = pq->q[pq->length - 1]; pq->q[0] = pq->q[pq->length - 1];
pq->q[0]->index = 0;
--pq->length; --pq->length;
bubble_down(pq, 0); 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; } int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; }
size_t nghttp2_pq_size(nghttp2_pq *pq) { return pq->length; } 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; 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 */ /* Implementation of priority queue */
typedef struct { size_t index; } nghttp2_pq_entry;
typedef struct { typedef struct {
/* The pointer to the pointer to the item stored */ /* The pointer to the pointer to the item stored */
void **q; nghttp2_pq_entry **q;
/* Memory allocator */ /* Memory allocator */
nghttp2_mem *mem; nghttp2_mem *mem;
/* The number of items sotred */ /* The number of items sotred */
@ -75,13 +77,13 @@ void nghttp2_pq_free(nghttp2_pq *pq);
* NGHTTP2_ERR_NOMEM * NGHTTP2_ERR_NOMEM
* Out of memory. * 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, * Returns item at the top of the queue |pq|. If the queue is empty,
* this function returns NULL. * 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 * 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); 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 * 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); 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 */ #endif /* NGHTTP2_PQ_H */

View File

@ -34,6 +34,7 @@
#include "nghttp2_priority_spec.h" #include "nghttp2_priority_spec.h"
#include "nghttp2_option.h" #include "nghttp2_option.h"
#include "nghttp2_http.h" #include "nghttp2_http.h"
#include "nghttp2_pq.h"
/* /*
* Returns non-zero if the number of outgoing opened streams is larger * 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); 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) { static void session_inbound_frame_reset(nghttp2_session *session) {
nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_inbound_frame *iframe = &session->iframe;
nghttp2_mem *mem = &session->mem; nghttp2_mem *mem = &session->mem;
@ -332,11 +324,6 @@ static int session_new(nghttp2_session **session_ptr,
/* next_stream_id is initialized in either /* next_stream_id is initialized in either
nghttp2_session_client_new2 or nghttp2_session_server_new2 */ 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); rv = nghttp2_hd_deflate_init(&(*session_ptr)->hd_deflater, mem);
if (rv != 0) { if (rv != 0) {
goto fail_hd_deflater; 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_init(&(*session_ptr)->root, 0, NGHTTP2_STREAM_FLAG_NONE,
NGHTTP2_STREAM_INITIAL, NGHTTP2_DEFAULT_WEIGHT, 0, 0, NGHTTP2_STREAM_INITIAL, NGHTTP2_DEFAULT_WEIGHT, 0, 0,
NULL); NULL, mem);
(*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
(*session_ptr)->recv_window_size = 0; (*session_ptr)->recv_window_size = 0;
@ -444,8 +431,6 @@ fail_map:
fail_hd_inflater: fail_hd_inflater:
nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater); nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater);
fail_hd_deflater: fail_hd_deflater:
nghttp2_pq_free(&(*session_ptr)->ob_da_pq);
fail_ob_da_pq:
nghttp2_mem_free(mem, *session_ptr); nghttp2_mem_free(mem, *session_ptr);
fail_session: fail_session:
return rv; return rv;
@ -541,16 +526,6 @@ static int free_streams(nghttp2_map_entry *entry, void *ptr) {
return 0; 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) { static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) {
nghttp2_outbound_item *item, *next; nghttp2_outbound_item *item, *next;
for (item = q->head; item;) { 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_urgent, mem);
ob_q_free(&session->ob_reg, mem); ob_q_free(&session->ob_reg, mem);
ob_q_free(&session->ob_syn, mem); ob_q_free(&session->ob_syn, mem);
ob_pq_free(&session->ob_da_pq, mem);
active_outbound_item_reset(&session->aob, mem); active_outbound_item_reset(&session->aob, mem);
session_inbound_frame_reset(session); session_inbound_frame_reset(session);
nghttp2_hd_deflate_free(&session->hd_deflater); nghttp2_hd_deflate_free(&session->hd_deflater);
@ -667,14 +642,14 @@ nghttp2_session_reprioritize_stream(nghttp2_session *session,
if (pri_spec->stream_id == 0) { if (pri_spec->stream_id == 0) {
dep_stream = &session->root; 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 " DEBUGF(fprintf(stderr, "stream: cycle detected, dep_stream(%p)=%d "
"stream(%p)=%d\n", "stream(%p)=%d\n",
dep_stream, dep_stream->stream_id, stream, dep_stream, dep_stream->stream_id, stream,
stream->stream_id)); stream->stream_id));
nghttp2_stream_dep_remove_subtree(dep_stream); 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) { if (rv != 0) {
return rv; return rv;
} }
@ -686,9 +661,9 @@ nghttp2_session_reprioritize_stream(nghttp2_session *session,
stream->weight = pri_spec->weight; stream->weight = pri_spec->weight;
if (pri_spec->exclusive) { if (pri_spec->exclusive) {
rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream, session); rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream);
} else { } else {
rv = nghttp2_stream_dep_add_subtree(dep_stream, stream, session); rv = nghttp2_stream_dep_add_subtree(dep_stream, stream);
} }
if (rv != 0) { 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 /* TODO If 2 HEADERS are submitted for reserved stream, then
both of them are queued into ob_syn, which is not both of them are queued into ob_syn, which is not
desirable. */ 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); nghttp2_outbound_queue_push(&session->ob_syn, item);
item->queued = 1; item->queued = 1;
break; break;
} }
if (stream && (stream->state == NGHTTP2_STREAM_RESERVED || if (stream && item->aux_data.headers.attach_stream) {
item->aux_data.headers.attach_stream)) {
if (stream->item) { if (stream->item) {
return NGHTTP2_ERR_DATA_EXIST; return NGHTTP2_ERR_DATA_EXIST;
} }
rv = nghttp2_stream_attach_item(stream, item, session); rv = nghttp2_stream_attach_item(stream, item);
if (rv != 0) { if (rv != 0) {
return rv; return rv;
@ -769,7 +744,7 @@ int nghttp2_session_add_item(nghttp2_session *session,
return NGHTTP2_ERR_DATA_EXIST; return NGHTTP2_ERR_DATA_EXIST;
} }
rv = nghttp2_stream_attach_item(stream, item, session); rv = nghttp2_stream_attach_item(stream, item);
if (rv != 0) { if (rv != 0) {
return rv; return rv;
@ -865,12 +840,18 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
assert(stream->state == NGHTTP2_STREAM_IDLE); assert(stream->state == NGHTTP2_STREAM_IDLE);
assert(nghttp2_stream_in_dep_tree(stream)); assert(nghttp2_stream_in_dep_tree(stream));
nghttp2_session_detach_idle_stream(session, 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 { } else {
if (session->server && initial_state != NGHTTP2_STREAM_IDLE && if (session->server && initial_state != NGHTTP2_STREAM_IDLE &&
!nghttp2_session_is_my_stream_id(session, stream_id)) { !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)); 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, nghttp2_stream_init(stream, stream_id, flags, initial_state, pri_spec->weight,
session->remote_settings.initial_window_size, session->remote_settings.initial_window_size,
session->local_settings.initial_window_size, session->local_settings.initial_window_size,
stream_user_data); stream_user_data, mem);
if (stream_alloc) { if (stream_alloc) {
rv = nghttp2_map_insert(&session->streams, &stream->map_entry); 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. /* Idle stream does not count toward the concurrent streams limit.
This is used as anchor node in dependency tree. */ This is used as anchor node in dependency tree. */
assert(session->server); assert(session->server);
nghttp2_session_keep_idle_stream(session, stream); rv = nghttp2_session_keep_idle_stream(session, stream);
if (rv != 0) {
return NULL;
}
break; break;
default: default:
if (nghttp2_session_is_my_stream_id(session, stream_id)) { 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); assert(dep_stream);
if (pri_spec->exclusive) { 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 { } else {
nghttp2_stream_dep_add(dep_stream, stream); 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; item = stream->item;
rv = nghttp2_stream_detach_item(stream, session); rv = nghttp2_stream_detach_item(stream);
if (rv != 0) { if (rv != 0) {
return rv; 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 /* On server side, retain stream at most MAX_CONCURRENT_STREAMS
combined with the current active incoming streams to make combined with the current active incoming streams to make
dependency tree work better. */ dependency tree work better. */
nghttp2_session_keep_closed_stream(session, stream); rv = nghttp2_session_keep_closed_stream(session, stream);
} else { } else {
nghttp2_session_destroy_stream(session, stream); rv = nghttp2_session_destroy_stream(session, stream);
}
if (rv != 0) {
return rv;
} }
return 0; return 0;
} }
void nghttp2_session_destroy_stream(nghttp2_session *session, int nghttp2_session_destroy_stream(nghttp2_session *session,
nghttp2_stream *stream) { nghttp2_stream *stream) {
nghttp2_mem *mem; nghttp2_mem *mem;
int rv;
DEBUGF(fprintf(stderr, "stream: destroy closed stream(%p)=%d\n", stream, DEBUGF(fprintf(stderr, "stream: destroy closed stream(%p)=%d\n", stream,
stream->stream_id)); stream->stream_id));
@ -1063,16 +1054,23 @@ void nghttp2_session_destroy_stream(nghttp2_session *session,
mem = &session->mem; mem = &session->mem;
if (nghttp2_stream_in_dep_tree(stream)) { 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_map_remove(&session->streams, stream->stream_id);
nghttp2_stream_free(stream); nghttp2_stream_free(stream);
nghttp2_mem_free(mem, stream); nghttp2_mem_free(mem, stream);
return 0;
} }
void nghttp2_session_keep_closed_stream(nghttp2_session *session, int nghttp2_session_keep_closed_stream(nghttp2_session *session,
nghttp2_stream *stream) { nghttp2_stream *stream) {
int rv;
DEBUGF(fprintf(stderr, "stream: keep closed stream(%p)=%d, state=%d\n", DEBUGF(fprintf(stderr, "stream: keep closed stream(%p)=%d, state=%d\n",
stream, stream->stream_id, stream->state)); stream, stream->stream_id, stream->state));
@ -1086,11 +1084,18 @@ void nghttp2_session_keep_closed_stream(nghttp2_session *session,
++session->num_closed_streams; ++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, int nghttp2_session_keep_idle_stream(nghttp2_session *session,
nghttp2_stream *stream) { nghttp2_stream *stream) {
int rv;
DEBUGF(fprintf(stderr, "stream: keep idle stream(%p)=%d, state=%d\n", stream, DEBUGF(fprintf(stderr, "stream: keep idle stream(%p)=%d, state=%d\n", stream,
stream->stream_id, stream->state)); stream->stream_id, stream->state));
@ -1104,7 +1109,12 @@ void nghttp2_session_keep_idle_stream(nghttp2_session *session,
++session->num_idle_streams; ++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, 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; --session->num_idle_streams;
} }
void nghttp2_session_adjust_closed_stream(nghttp2_session *session, int nghttp2_session_adjust_closed_stream(nghttp2_session *session,
ssize_t offset) { ssize_t offset) {
size_t num_stream_max; size_t num_stream_max;
int rv;
num_stream_max = nghttp2_min(session->local_settings.max_concurrent_streams, num_stream_max = nghttp2_min(session->local_settings.max_concurrent_streams,
session->pending_local_max_concurrent_stream); 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 > session->num_closed_streams + session->num_incoming_streams + offset >
num_stream_max) { num_stream_max) {
nghttp2_stream *head_stream; nghttp2_stream *head_stream;
nghttp2_stream *next;
head_stream = session->closed_stream_head; head_stream = session->closed_stream_head;
assert(head_stream); 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) { if (session->closed_stream_head) {
session->closed_stream_head->closed_prev = NULL; 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; session->closed_stream_tail = NULL;
} }
nghttp2_session_destroy_stream(session, head_stream);
/* head_stream is now freed */
--session->num_closed_streams; --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; size_t max;
int rv;
/* Make minimum number of idle streams 2 so that allocating 2 /* Make minimum number of idle streams 2 so that allocating 2
streams at once is easy. This happens when PRIORITY frame to 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) { while (session->num_idle_streams > max) {
nghttp2_stream *head; nghttp2_stream *head;
nghttp2_stream *next;
head = session->idle_stream_head; head = session->idle_stream_head;
assert(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) { if (session->idle_stream_head) {
session->idle_stream_head->closed_prev = NULL; 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; session->idle_stream_tail = NULL;
} }
nghttp2_session_destroy_stream(session, head);
/* head is now destroyed */
--session->num_idle_streams; --session->num_idle_streams;
} }
return 0;
} }
/* /*
@ -1746,7 +1778,7 @@ static int session_prep_frame(nghttp2_session *session,
if (stream && stream->item == item) { if (stream && stream->item == item) {
int rv2; int rv2;
rv2 = nghttp2_stream_detach_item(stream, session); rv2 = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv2)) { if (nghttp2_is_fatal(rv2)) {
return rv2; return rv2;
@ -1908,7 +1940,7 @@ static int session_prep_frame(nghttp2_session *session,
if (stream) { if (stream) {
int rv2; int rv2;
rv2 = nghttp2_stream_detach_item(stream, session); rv2 = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv2)) { if (nghttp2_is_fatal(rv2)) {
return rv2; return rv2;
@ -1927,8 +1959,8 @@ static int session_prep_frame(nghttp2_session *session,
queue when session->remote_window_size > 0 */ queue when session->remote_window_size > 0 */
assert(session->remote_window_size > 0); assert(session->remote_window_size > 0);
rv = nghttp2_stream_defer_item( rv = nghttp2_stream_defer_item(stream,
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session); NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -1943,8 +1975,7 @@ static int session_prep_frame(nghttp2_session *session,
next_readmax, frame, &item->aux_data.data, next_readmax, frame, &item->aux_data.data,
stream); stream);
if (rv == NGHTTP2_ERR_DEFERRED) { if (rv == NGHTTP2_ERR_DEFERRED) {
rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER);
session);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -1955,7 +1986,7 @@ static int session_prep_frame(nghttp2_session *session,
return NGHTTP2_ERR_DEFERRED; return NGHTTP2_ERR_DEFERRED;
} }
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { 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)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -1971,7 +2002,7 @@ static int session_prep_frame(nghttp2_session *session,
if (rv != 0) { if (rv != 0) {
int rv2; int rv2;
rv2 = nghttp2_stream_detach_item(stream, session); rv2 = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv2)) { if (nghttp2_is_fatal(rv2)) {
return rv2; return rv2;
@ -1999,9 +2030,8 @@ nghttp2_session_get_next_ob_item(nghttp2_session *session) {
} }
} }
if (session->remote_window_size > 0 && if (session->remote_window_size > 0) {
!nghttp2_pq_empty(&session->ob_da_pq)) { return nghttp2_stream_next_outbound_item(&session->root);
return nghttp2_pq_top(&session->ob_da_pq);
} }
return NULL; return NULL;
@ -2034,17 +2064,8 @@ nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
} }
} }
if (session->remote_window_size > 0 && if (session->remote_window_size > 0) {
!nghttp2_pq_empty(&session->ob_da_pq)) { return nghttp2_stream_next_outbound_item(&session->root);
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;
} }
return NULL; return NULL;
@ -2150,24 +2171,10 @@ static int session_close_stream_on_goaway(nghttp2_session *session,
return 0; return 0;
} }
static void session_outbound_item_schedule(nghttp2_session *session, static void reschedule_stream(nghttp2_stream *stream) {
nghttp2_outbound_item *item, stream->last_writelen = stream->item->frame.hd.length;
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;
if (session->last_cycle < item->cycle) { nghttp2_stream_reschedule(stream);
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;
} }
/* /*
@ -2218,7 +2225,7 @@ static int session_after_frame_sent1(nghttp2_session *session) {
} }
if (stream->item == item) { if (stream->item == item) {
rv = nghttp2_stream_detach_item(stream, session); rv = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -2352,7 +2359,7 @@ static int session_after_frame_sent1(nghttp2_session *session) {
} }
if (stream && aux_data->eof) { if (stream && aux_data->eof) {
rv = nghttp2_stream_detach_item(stream, session); rv = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -2446,7 +2453,6 @@ static int session_after_frame_sent2(nghttp2_session *session) {
return 0; return 0;
} else { } else {
nghttp2_outbound_item *next_item;
nghttp2_stream *stream; nghttp2_stream *stream;
nghttp2_data_aux_data *aux_data; nghttp2_data_aux_data *aux_data;
@ -2471,7 +2477,7 @@ static int session_after_frame_sent2(nghttp2_session *session) {
further data. */ further data. */
if (nghttp2_session_predicate_data_send(session, stream) != 0) { if (nghttp2_session_predicate_data_send(session, stream) != 0) {
if (stream) { if (stream) {
rv = nghttp2_stream_detach_item(stream, session); rv = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -2483,115 +2489,9 @@ static int session_after_frame_sent2(nghttp2_session *session) {
return 0; 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; aob->item = NULL;
active_outbound_item_reset(&session->aob, mem); active_outbound_item_reset(&session->aob, mem);
return 0; return 0;
} }
/* Unreachable */ /* Unreachable */
@ -2648,22 +2548,6 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
return 0; 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); rv = session_prep_frame(session, item);
if (rv == NGHTTP2_ERR_DEFERRED) { if (rv == NGHTTP2_ERR_DEFERRED) {
DEBUGF(fprintf(stderr, "send: frame transmission deferred\n")); 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) { 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)) { if (nghttp2_is_fatal(rv)) {
return 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)) { nghttp2_stream_check_deferred_by_flow_control(stream)) {
rv = nghttp2_stream_resume_deferred_item( 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)) { if (nghttp2_is_fatal(rv)) {
return 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)) { nghttp2_stream_check_deferred_by_flow_control(stream)) {
rv = nghttp2_stream_resume_deferred_item( 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)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -5970,7 +5854,7 @@ int nghttp2_session_want_write(nghttp2_session *session) {
if (session->aob.item == NULL && if (session->aob.item == NULL &&
nghttp2_outbound_queue_top(&session->ob_urgent) == NULL && nghttp2_outbound_queue_top(&session->ob_urgent) == NULL &&
nghttp2_outbound_queue_top(&session->ob_reg) == 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) && session->remote_window_size == 0) &&
(nghttp2_outbound_queue_top(&session->ob_syn) == NULL || (nghttp2_outbound_queue_top(&session->ob_syn) == NULL ||
session_is_outgoing_concurrent_streams_max(session))) { session_is_outgoing_concurrent_streams_max(session))) {
@ -6330,8 +6214,7 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
return rv; return rv;
} }
session_outbound_item_schedule( reschedule_stream(stream);
session, stream->item, nghttp2_stream_compute_effective_weight(stream));
return 0; return 0;
} }
@ -6367,8 +6250,8 @@ int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
return NGHTTP2_ERR_INVALID_ARGUMENT; return NGHTTP2_ERR_INVALID_ARGUMENT;
} }
rv = nghttp2_stream_resume_deferred_item( rv = nghttp2_stream_resume_deferred_item(stream,
stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, session); NGHTTP2_STREAM_FLAG_DEFERRED_USER);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return 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) { size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) {
return nghttp2_outbound_queue_size(&session->ob_urgent) + return nghttp2_outbound_queue_size(&session->ob_urgent) +
nghttp2_outbound_queue_size(&session->ob_reg) + nghttp2_outbound_queue_size(&session->ob_reg) +
nghttp2_outbound_queue_size(&session->ob_syn) + nghttp2_outbound_queue_size(&session->ob_syn);
nghttp2_pq_size(&session->ob_da_pq); /* TODO account for item attached to stream */
} }
int32_t int32_t

View File

@ -30,7 +30,6 @@
#endif /* HAVE_CONFIG_H */ #endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "nghttp2_pq.h"
#include "nghttp2_map.h" #include "nghttp2_map.h"
#include "nghttp2_frame.h" #include "nghttp2_frame.h"
#include "nghttp2_hd.h" #include "nghttp2_hd.h"
@ -163,8 +162,6 @@ struct nghttp2_session {
response) frame, which are subject to response) frame, which are subject to
SETTINGS_MAX_CONCURRENT_STREAMS limit. */ SETTINGS_MAX_CONCURRENT_STREAMS limit. */
nghttp2_outbound_queue ob_syn; nghttp2_outbound_queue ob_syn;
/* Queue for DATA frame */
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_da_pq;
nghttp2_active_outbound_item aob; nghttp2_active_outbound_item aob;
nghttp2_inbound_frame iframe; nghttp2_inbound_frame iframe;
nghttp2_hd_deflater hd_deflater; 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 * Deletes |stream| from memory. After this function returns, stream
* cannot be accessed. * 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, int nghttp2_session_destroy_stream(nghttp2_session *session,
nghttp2_stream *stream); nghttp2_stream *stream);
/* /*
* Tries to keep incoming closed stream |stream|. Due to the * Tries to keep incoming closed stream |stream|. Due to the
* limitation of maximum number of streams in memory, |stream| is not * limitation of maximum number of streams in memory, |stream| is not
* closed and just deleted from memory (see * closed and just deleted from memory (see
* nghttp2_session_destroy_stream). * 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, int nghttp2_session_keep_closed_stream(nghttp2_session *session,
nghttp2_stream *stream); nghttp2_stream *stream);
/* /*
* Appends |stream| to linked list |session->idle_stream_head|. We * Appends |stream| to linked list |session->idle_stream_head|. We
* apply fixed limit for list size. To fit into that limit, one or * apply fixed limit for list size. To fit into that limit, one or
* more oldest streams are removed from list as necessary. * 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, int nghttp2_session_keep_idle_stream(nghttp2_session *session,
nghttp2_stream *stream); nghttp2_stream *stream);
/* /*
* Detaches |stream| from idle streams linked list. * 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 * stream. If |offset| is nonzero, it is decreased from the maximum
* number of allowed stream when comparing number of active and closed * number of allowed stream when comparing number of active and closed
* stream and the maximum number. * 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, int nghttp2_session_adjust_closed_stream(nghttp2_session *session,
ssize_t offset); ssize_t offset);
/* /*
* Deletes idle stream to ensure that number of idle streams is in * Deletes idle stream to ensure that number of idle streams is in
* certain limit. * 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| * 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); 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 * returns NULL. This function takes into account max concurrent
* streams. That means if session->ob_pq is empty but * streams. That means if session->ob_syn has item and max concurrent
* session->ob_ss_pq has item and max concurrent streams is reached, * streams is reached, the even if other queues contain items, then
* then this function returns NULL. * this function returns NULL.
*/ */
nghttp2_outbound_item * nghttp2_outbound_item *
nghttp2_session_pop_next_ob_item(nghttp2_session *session); 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 * returns NULL. This function takes into account max concurrent
* streams. That means if session->ob_pq is empty but * streams. That means if session->ob_syn has item and max concurrent
* session->ob_ss_pq has item and max concurrent streams is reached, * streams is reached, the even if other queues contain items, then
* then this function returns NULL. * this function returns NULL.
*/ */
nghttp2_outbound_item * nghttp2_outbound_item *
nghttp2_session_get_next_ob_item(nghttp2_session *session); 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_EXPECT_FINAL_RESPONSE = 1 << 13
} nghttp2_http_flag; } 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; struct nghttp2_stream_roots;
typedef struct nghttp2_stream_roots nghttp2_stream_roots; typedef struct nghttp2_stream_roots nghttp2_stream_roots;
@ -149,6 +142,12 @@ typedef struct nghttp2_stream nghttp2_stream;
struct nghttp2_stream { struct nghttp2_stream {
/* Intrusive Map */ /* Intrusive Map */
nghttp2_map_entry map_entry; 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. */ /* Content-Length of request/response body. -1 if unknown. */
int64_t content_length; int64_t content_length;
/* Received body so far */ /* Received body so far */
@ -173,9 +172,6 @@ struct nghttp2_stream {
nghttp2_outbound_item *item; nghttp2_outbound_item *item;
/* stream ID */ /* stream ID */
int32_t 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 remote window size. This value is computed against the
current initial window size of remote endpoint. */ current initial window size of remote endpoint. */
int32_t remote_window_size; int32_t remote_window_size;
@ -198,13 +194,6 @@ struct nghttp2_stream {
int32_t weight; int32_t weight;
/* sum of weight of direct descendants */ /* sum of weight of direct descendants */
int32_t sum_dep_weight; 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; nghttp2_stream_state state;
/* status code from remote server */ /* status code from remote server */
int16_t status_code; int16_t status_code;
@ -214,13 +203,24 @@ struct nghttp2_stream {
uint8_t flags; uint8_t flags;
/* Bitwise OR of zero or more nghttp2_shut_flag values */ /* Bitwise OR of zero or more nghttp2_shut_flag values */
uint8_t shut_flags; 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, void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
uint8_t flags, nghttp2_stream_state initial_state, uint8_t flags, nghttp2_stream_state initial_state,
int32_t weight, int32_t remote_initial_window_size, int32_t weight, int32_t remote_initial_window_size,
int32_t local_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); 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 * NGHTTP2_ERR_NOMEM
* Out of memory * Out of memory
*/ */
int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags, int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags);
nghttp2_session *session);
/* /*
* Put back deferred data in this stream to active state. The |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 * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are
* cleared if they are set. So even if this function is called, if * cleared if they are set. So even if this function is called, if
* one of flag is still set, data does not become active. * 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, int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags);
nghttp2_session *session);
/* /*
* Returns nonzero if item is deferred by whatever reason. * 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); void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream);
/* /*
* Returns the stream positioned in root of the dependency tree the * Returns nonzero if |target| is an ancestor of |stream|.
* |stream| belongs to.
*/ */
nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream); int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream,
nghttp2_stream *target);
/*
* Returns nonzero if |target| is found in subtree of |stream|.
*/
int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream,
nghttp2_stream *target);
/* /*
* Computes distributed weight of a stream of the |weight| under the * 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 nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
int32_t weight); int32_t weight);
int32_t nghttp2_stream_compute_effective_weight(nghttp2_stream *stream);
/* /*
* Makes the |stream| depend on the |dep_stream|. This dependency is * Makes the |stream| depend on the |dep_stream|. This dependency is
* exclusive. All existing direct descendants of |dep_stream| become * exclusive. All existing direct descendants of |dep_stream| become
* the descendants of the |stream|. This function assumes * the descendants of the |stream|. This function assumes
* |stream->data| is NULL and no dpri members are changed in this * |stream->item| is NULL.
* dependency tree. *
* 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, int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
nghttp2_stream *stream); nghttp2_stream *stream);
/* /*
* Makes the |stream| depend on the |dep_stream|. This dependency is * Makes the |stream| depend on the |dep_stream|. This dependency is
* not exclusive. This function assumes |stream->data| is NULL and no * not exclusive. This function assumes |stream->item| is NULL.
* dpri members are changed in this dependency tree.
*/ */
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream); void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream);
/* /*
* Removes the |stream| from the current dependency tree. This * 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 * Attaches |item| to |stream|.
* dependency tree.
* *
* This function returns 0 if it succeeds, or one of the following * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
@ -353,13 +352,11 @@ void nghttp2_stream_dep_remove(nghttp2_stream *stream);
* Out of memory * Out of memory
*/ */
int nghttp2_stream_attach_item(nghttp2_stream *stream, int nghttp2_stream_attach_item(nghttp2_stream *stream,
nghttp2_outbound_item *item, nghttp2_outbound_item *item);
nghttp2_session *session);
/* /*
* Detaches |stream->item|. Updates dpri members in this dependency * Detaches |stream->item|. This function does not free
* tree. This function does not free |stream->item|. The caller must * |stream->item|. The caller must free it.
* free it.
* *
* This function returns 0 if it succeeds, or one of the following * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
@ -367,12 +364,11 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream,
* NGHTTP2_ERR_NOMEM * NGHTTP2_ERR_NOMEM
* Out of memory * Out of memory
*/ */
int nghttp2_stream_detach_item(nghttp2_stream *stream, int nghttp2_stream_detach_item(nghttp2_stream *stream);
nghttp2_session *session);
/* /*
* Makes the |stream| depend on the |dep_stream|. This dependency is * 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 * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
@ -381,12 +377,11 @@ int nghttp2_stream_detach_item(nghttp2_stream *stream,
* Out of memory * Out of memory
*/ */
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream, nghttp2_stream *stream);
nghttp2_session *session);
/* /*
* Makes the |stream| depend on the |dep_stream|. This dependency is * 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 * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
@ -395,13 +390,11 @@ int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
* Out of memory * Out of memory
*/ */
int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream, nghttp2_stream *stream);
nghttp2_session *session);
/* /*
* Removes subtree whose root stream is |stream|. Removing subtree * Removes subtree whose root stream is |stream|. The
* does not change dpri values. The effective_weight of streams in * effective_weight of streams in removed subtree is not updated.
* removed subtree is not updated.
* *
* This function returns 0 if it succeeds, or one of the following * This function returns 0 if it succeeds, or one of the following
* negative error codes: * 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); 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 */ #endif /* NGHTTP2_STREAM */

View File

@ -67,6 +67,8 @@ int main(int argc _U_, char *argv[] _U_) {
/* add the tests to the suite */ /* add the tests to the suite */
if (!CU_add_test(pSuite, "pq", test_nghttp2_pq) || if (!CU_add_test(pSuite, "pq", test_nghttp2_pq) ||
!CU_add_test(pSuite, "pq_update", test_nghttp2_pq_update) || !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", test_nghttp2_map) ||
!CU_add_test(pSuite, "map_functional", test_nghttp2_map_functional) || !CU_add_test(pSuite, "map_functional", test_nghttp2_map_functional) ||
!CU_add_test(pSuite, "map_each_free", test_nghttp2_map_each_free) || !CU_add_test(pSuite, "map_each_free", test_nghttp2_map_each_free) ||

View File

@ -28,47 +28,89 @@
#include "nghttp2_pq.h" #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) { 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) { void test_nghttp2_pq(void) {
int i; int i;
nghttp2_pq pq; nghttp2_pq pq;
string_entry *top;
nghttp2_pq_init(&pq, pq_less, nghttp2_mem_default()); nghttp2_pq_init(&pq, pq_less, nghttp2_mem_default());
CU_ASSERT(nghttp2_pq_empty(&pq)); CU_ASSERT(nghttp2_pq_empty(&pq));
CU_ASSERT(0 == nghttp2_pq_size(&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(0 == nghttp2_pq_empty(&pq));
CU_ASSERT(1 == nghttp2_pq_size(&pq)); CU_ASSERT(1 == nghttp2_pq_size(&pq));
CU_ASSERT(strcmp("foo", nghttp2_pq_top(&pq)) == 0); top = (string_entry *)nghttp2_pq_top(&pq);
CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"bar")); CU_ASSERT(strcmp("foo", top->s) == 0);
CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("bar")->ent));
CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"baz")); top = (string_entry *)nghttp2_pq_top(&pq);
CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); CU_ASSERT(strcmp("bar", top->s) == 0);
CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"C")); 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(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); nghttp2_pq_pop(&pq);
CU_ASSERT(3 == nghttp2_pq_size(&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); 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); 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); nghttp2_pq_pop(&pq);
string_entry_del(top);
CU_ASSERT(nghttp2_pq_empty(&pq)); CU_ASSERT(nghttp2_pq_empty(&pq));
CU_ASSERT(0 == nghttp2_pq_size(&pq)); CU_ASSERT(0 == nghttp2_pq_size(&pq));
CU_ASSERT(NULL == nghttp2_pq_top(&pq)); CU_ASSERT(NULL == nghttp2_pq_top(&pq));
/* Add bunch of entry to see realloc works */ /* Add bunch of entry to see realloc works */
for (i = 0; i < 10000; ++i) { 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)); CU_ASSERT((size_t)(i + 1) == nghttp2_pq_size(&pq));
} }
for (i = 10000; i > 0; --i) { 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); nghttp2_pq_pop(&pq);
string_entry_del(top);
CU_ASSERT((size_t)(i - 1) == nghttp2_pq_size(&pq)); CU_ASSERT((size_t)(i - 1) == nghttp2_pq_size(&pq));
} }
@ -76,6 +118,7 @@ void test_nghttp2_pq(void) {
} }
typedef struct { typedef struct {
nghttp2_pq_entry ent;
int key; int key;
int val; int val;
} node; } node;
@ -86,7 +129,7 @@ static int node_less(const void *lhs, const void *rhs) {
return ln->key < rn->key; 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; node *nd = (node *)item;
if ((nd->key % 2) == 0) { if ((nd->key % 2) == 0) {
nd->key *= -1; nd->key *= -1;
@ -108,16 +151,107 @@ void test_nghttp2_pq_update(void) {
for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) { for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) {
nodes[i].key = i; nodes[i].key = i;
nodes[i].val = 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); nghttp2_pq_update(&pq, node_update, NULL);
for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) { 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); CU_ASSERT(ans[i] == nd->key);
nghttp2_pq_pop(&pq); nghttp2_pq_pop(&pq);
} }
nghttp2_pq_free(&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(void);
void test_nghttp2_pq_update(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 */ #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); 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, nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1,
16 * 1024); 16 * 1024);
@ -2696,9 +2696,8 @@ void test_nghttp2_session_on_window_update_received(void) {
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 == CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 ==
stream->remote_window_size); stream->remote_window_size);
CU_ASSERT(0 == CU_ASSERT(0 == nghttp2_stream_defer_item(
nghttp2_stream_defer_item( stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL));
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session));
CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame));
CU_ASSERT(2 == user_data.frame_recv_cb_called); 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); stream = nghttp2_session_get_stream(session, 1);
nghttp2_stream_detach_item(stream, session); nghttp2_stream_detach_item(stream);
nghttp2_outbound_item_free(item, mem); nghttp2_outbound_item_free(item, mem);
mem->free(item, NULL); mem->free(item, NULL);
@ -4887,7 +4886,7 @@ void test_nghttp2_session_defer_data(void) {
/* Resume deferred DATA */ /* Resume deferred DATA */
CU_ASSERT(0 == nghttp2_session_resume_data(session, 1)); 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 = item->aux_data.data.data_prd.read_callback =
fixed_length_data_source_read_callback; fixed_length_data_source_read_callback;
ud.block_count = 1; ud.block_count = 1;
@ -4905,7 +4904,6 @@ void test_nghttp2_session_defer_data(void) {
/* Resume deferred DATA */ /* Resume deferred DATA */
CU_ASSERT(0 == nghttp2_session_resume_data(session, 1)); 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 = item->aux_data.data.data_prd.read_callback =
fixed_length_data_source_read_callback; fixed_length_data_source_read_callback;
ud.block_count = 1; 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); CU_ASSERT(data_size - NGHTTP2_INITIAL_WINDOW_SIZE == ud.data_source_length);
stream = nghttp2_session_get_stream(session, 1); 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); CU_ASSERT(NGHTTP2_DATA == stream->item->frame.hd.type);
stream->item->aux_data.data.data_prd.read_callback = 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_remove_subtree(e);
nghttp2_stream_dep_add_subtree(a, e, session); nghttp2_stream_dep_add_subtree(a, e);
/* becomes /* becomes
* a * a
@ -5919,7 +5916,7 @@ void test_nghttp2_session_stream_dep_add_subtree(void) {
*/ */
nghttp2_stream_dep_remove_subtree(e); nghttp2_stream_dep_remove_subtree(e);
nghttp2_stream_dep_insert_subtree(a, e, session); nghttp2_stream_dep_insert_subtree(a, e);
/* becomes /* becomes
* a * 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); 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 * 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(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
CU_ASSERT(0 == b->sum_dep_weight); CU_ASSERT(0 == b->sum_dep_weight);
CU_ASSERT(0 == a->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&a->obq));
CU_ASSERT(0 == b->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(0 == c->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&c->obq));
check_stream_dep_sib(c, root, a, NULL, NULL); check_stream_dep_sib(c, root, a, NULL, NULL);
check_stream_dep_sib(a, c, b, 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); 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 * 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 == b->sum_dep_weight);
CU_ASSERT(0 == a->sum_dep_weight); CU_ASSERT(0 == a->sum_dep_weight);
CU_ASSERT(0 == a->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&a->obq));
CU_ASSERT(0 == b->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(0 == c->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&c->obq));
check_stream_dep_sib(c, root, b, NULL, NULL); check_stream_dep_sib(c, root, b, NULL, NULL);
check_stream_dep_sib(b, c, NULL, NULL, a); 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); 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 * 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(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
CU_ASSERT(0 == b->sum_dep_weight); CU_ASSERT(0 == b->sum_dep_weight);
CU_ASSERT(0 == a->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&a->obq));
CU_ASSERT(0 == b->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(0 == c->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&c->obq));
CU_ASSERT(0 == d->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&d->obq));
check_stream_dep_sib(c, root, d, NULL, NULL); check_stream_dep_sib(c, root, d, NULL, NULL);
check_stream_dep_sib(d, c, NULL, NULL, a); 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); 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); 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 * c
@ -6229,14 +6226,15 @@ void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
* | * |
* b * 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(c->queued);
CU_ASSERT(16 == c->sum_norest_weight); CU_ASSERT(a->queued);
CU_ASSERT(0 == d->sum_norest_weight); 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(c, root, d, NULL, NULL);
check_stream_dep_sib(d, c, NULL, NULL, a); 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); db = create_data_ob_item(mem);
dc = create_data_ob_item(mem); dc = create_data_ob_item(mem);
nghttp2_stream_attach_item(b, db, session); nghttp2_stream_attach_item(b, db);
nghttp2_stream_attach_item(c, dc, session); nghttp2_stream_attach_item(c, dc);
nghttp2_stream_dep_remove_subtree(c); 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 * c
@ -6277,10 +6275,10 @@ void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
* b * b
*/ */
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); CU_ASSERT(c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri); CU_ASSERT(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); CU_ASSERT(!d->queued);
check_stream_dep_sib(c, root, d, NULL, NULL); check_stream_dep_sib(c, root, d, NULL, NULL);
check_stream_dep_sib(d, c, NULL, NULL, a); 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); 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(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); CU_ASSERT(!c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); CU_ASSERT(!d->queued);
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(b)); CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(16 == a->sum_norest_weight);
CU_ASSERT(1 == db->queued);
/* Attach item to c */
dc = create_data_ob_item(mem); 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(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri); CU_ASSERT(c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); CU_ASSERT(!d->queued);
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(b)); CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(c));
CU_ASSERT(32 == a->sum_norest_weight);
CU_ASSERT(1 == dc->queued);
/* Attach item to a */
da = create_data_ob_item(mem); 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(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == c->dpri); CU_ASSERT(c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); 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(2 == nghttp2_pq_size(&a->obq));
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));
/* Attach item to d */
dd = create_data_ob_item(mem); 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(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri); CU_ASSERT(c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri); CU_ASSERT(d->queued);
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(b)); CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(c)); 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(2 == nghttp2_pq_size(&a->obq));
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri);
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(b)); /* Detach item from b */
CU_ASSERT(16 * 16 / 32 == nghttp2_stream_compute_effective_weight(d)); 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(1 == nghttp2_pq_size(&a->obq));
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);
/* exercises insertion */ /* exercises insertion */
e = open_stream_with_dep_excl(session, 9, a); e = open_stream_with_dep_excl(session, 9, a);
@ -6418,18 +6404,17 @@ void test_nghttp2_session_stream_attach_item(void) {
* d * d
*/ */
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == e->dpri); CU_ASSERT(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); CU_ASSERT(e->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == b->dpri); CU_ASSERT(!b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); CU_ASSERT(c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); 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(1 == nghttp2_pq_size(&e->obq));
CU_ASSERT(16 == a->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(16 == e->sum_norest_weight); CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(16 == c->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(0 == b->sum_norest_weight);
/* exercises deletion */ /* exercises deletion */
nghttp2_stream_dep_remove(e); nghttp2_stream_dep_remove(e);
@ -6441,18 +6426,28 @@ void test_nghttp2_session_stream_attach_item(void) {
* d * d
*/ */
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); CU_ASSERT(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == b->dpri); CU_ASSERT(!b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); CU_ASSERT(c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); 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 /* e's weight 16 is distributed equally among c and b, both now have
weight 8 each. */ weight 8 each. */
CU_ASSERT(8 == a->sum_norest_weight); CU_ASSERT(8 == b->weight);
CU_ASSERT(16 == c->sum_norest_weight); CU_ASSERT(8 == c->weight);
CU_ASSERT(0 == b->sum_norest_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); nghttp2_session_del(session);
@ -6474,25 +6469,36 @@ void test_nghttp2_session_stream_attach_item(void) {
db = create_data_ob_item(mem); db = create_data_ob_item(mem);
dc = create_data_ob_item(mem); dc = create_data_ob_item(mem);
nghttp2_stream_attach_item(a, da, session); nghttp2_stream_attach_item(a, da);
nghttp2_stream_attach_item(b, db, session); nghttp2_stream_attach_item(b, db);
nghttp2_stream_attach_item(c, dc, session); nghttp2_stream_attach_item(c, dc);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == a->dpri); CU_ASSERT(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == c->dpri); CU_ASSERT(c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); CU_ASSERT(!d->queued);
/* check that all children's item get queued */ CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
nghttp2_stream_detach_item(a, session); 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); /* Detach item from a */
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); nghttp2_stream_detach_item(a);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri);
CU_ASSERT(1 == db->queued); CU_ASSERT(a->queued);
CU_ASSERT(1 == dc->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); nghttp2_session_del(session);
} }
@ -6528,30 +6534,30 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
de = create_data_ob_item(mem); 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); 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(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); CU_ASSERT(!c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); CU_ASSERT(!d->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); CU_ASSERT(e->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); CU_ASSERT(!f->queued);
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(b)); CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(32 == nghttp2_stream_compute_effective_weight(e)); CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(nghttp2_pq_empty(&c->obq));
CU_ASSERT(16 == a->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(0 == c->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&e->obq));
CU_ASSERT(0 == d->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&f->obq));
/* Insert subtree e under a */ /* Insert subtree e under a */
nghttp2_stream_dep_remove_subtree(e); nghttp2_stream_dep_remove_subtree(e);
nghttp2_stream_dep_insert_subtree(a, e, session); nghttp2_stream_dep_insert_subtree(a, e);
/* /*
* a * a
@ -6563,22 +6569,25 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* d * d
*/ */
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); CU_ASSERT(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); CU_ASSERT(!c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); CU_ASSERT(!d->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); CU_ASSERT(e->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); CU_ASSERT(!f->queued);
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(32 == a->sum_norest_weight); 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 */ /* Remove subtree b */
nghttp2_stream_dep_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 * a b
@ -6590,34 +6599,45 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* d * d
*/ */
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); CU_ASSERT(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); CU_ASSERT(!c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); CU_ASSERT(!d->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); CU_ASSERT(e->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); CU_ASSERT(!f->queued);
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(b)); CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(e)); 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); 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(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); CU_ASSERT(!c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); CU_ASSERT(!d->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); CU_ASSERT(e->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); 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 */ /* Remove subtree c */
nghttp2_stream_dep_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 * a b c
@ -6627,26 +6647,28 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* f * f
*/ */
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); CU_ASSERT(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); CU_ASSERT(!c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); CU_ASSERT(!d->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); CU_ASSERT(e->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); CU_ASSERT(!f->queued);
CU_ASSERT(32 == a->sum_norest_weight); CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(0 == c->sum_norest_weight); 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); dd = create_data_ob_item(mem);
nghttp2_stream_attach_item(d, dd, session); nghttp2_stream_attach_item(d, dd);
CU_ASSERT(16 == c->sum_norest_weight);
/* Add subtree c to a */ /* Add subtree c to a */
nghttp2_stream_dep_remove_subtree(c); nghttp2_stream_dep_remove_subtree(c);
nghttp2_stream_dep_add_subtree(a, c, session); nghttp2_stream_dep_add_subtree(a, c);
/* /*
* a b * a b
@ -6656,50 +6678,53 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* d f * d f
*/ */
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); CU_ASSERT(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); CU_ASSERT(c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); CU_ASSERT(d->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); CU_ASSERT(e->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); CU_ASSERT(!f->queued);
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(b)); CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
CU_ASSERT(16 * 16 / 48 == nghttp2_stream_compute_effective_weight(d)); CU_ASSERT(nghttp2_pq_empty(&b->obq));
CU_ASSERT(16 * 32 / 48 == nghttp2_stream_compute_effective_weight(e)); CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
CU_ASSERT(nghttp2_pq_empty(&d->obq));
CU_ASSERT(48 == a->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&e->obq));
CU_ASSERT(16 == c->sum_norest_weight); CU_ASSERT(nghttp2_pq_empty(&f->obq));
/* Insert b under a */ /* Insert b under a */
nghttp2_stream_dep_remove_subtree(b); nghttp2_stream_dep_remove_subtree(b);
nghttp2_stream_dep_insert_subtree(a, b, session); nghttp2_stream_dep_insert_subtree(a, b);
/* /*
* a * a
* | * |
* b * b
* | * |
* e--c * c--e
* | | * | |
* f d * d f
*/ */
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); CU_ASSERT(a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); CU_ASSERT(c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri); CU_ASSERT(d->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == e->dpri); CU_ASSERT(e->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); CU_ASSERT(!f->queued);
CU_ASSERT(16 == nghttp2_stream_compute_effective_weight(b)); CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
CU_ASSERT(2 == nghttp2_pq_size(&b->obq));
CU_ASSERT(16 == a->sum_norest_weight); 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 */ /* Remove subtree b */
nghttp2_stream_dep_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 * b a
@ -6709,21 +6734,26 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* f d * f d
*/ */
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); CU_ASSERT(!a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); CU_ASSERT(c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri); CU_ASSERT(d->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == e->dpri); CU_ASSERT(e->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); 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 /* Remove subtree c, and detach item from b, and then re-add
subtree c under b */ subtree c under b */
nghttp2_stream_dep_remove_subtree(c); nghttp2_stream_dep_remove_subtree(c);
nghttp2_stream_detach_item(b, session); nghttp2_stream_detach_item(b);
nghttp2_stream_dep_add_subtree(b, c, session); nghttp2_stream_dep_add_subtree(b, c);
/* /*
* b a * b a
@ -6733,21 +6763,26 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* f d * f d
*/ */
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); CU_ASSERT(!a->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == b->dpri); CU_ASSERT(b->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); CU_ASSERT(c->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); CU_ASSERT(d->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); CU_ASSERT(e->queued);
CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); 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 */ /* Attach data to a, and add subtree a under b */
da = create_data_ob_item(mem); 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_remove_subtree(a);
nghttp2_stream_dep_add_subtree(b, a, session); nghttp2_stream_dep_add_subtree(b, a);
/* /*
* b * b
@ -6756,18 +6791,24 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* | | * | |
* f d * 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 */ /* Remove subtree c, and add under f */
nghttp2_stream_dep_remove_subtree(c); nghttp2_stream_dep_remove_subtree(c);
nghttp2_stream_dep_insert_subtree(f, c, session); nghttp2_stream_dep_insert_subtree(f, c);
/* /*
* b * b
@ -6780,14 +6821,24 @@ void test_nghttp2_session_stream_attach_item_subtree(void) {
* | * |
* d * 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); nghttp2_session_del(session);
} }
@ -6990,7 +7041,7 @@ void test_nghttp2_session_large_dep_tree(void) {
stream_id = 1; stream_id = 1;
for (i = 0; i < 250; ++i) { for (i = 0; i < 250; ++i) {
stream = nghttp2_session_get_stream(session, stream_id); 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)); CU_ASSERT(nghttp2_stream_in_dep_tree(stream));
} }