From 1dea4e154b3ae3633419b603f9e0ccec2964ddaf Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 5 Dec 2013 23:12:18 +0900 Subject: [PATCH] Reintroduce priority adjustment for DATA frame This mechanism existed but was deleted. We bring it back in order to prevent lower priority streams from starving. --- lib/nghttp2_outbound_item.h | 7 ++++++- lib/nghttp2_session.c | 27 +++++++++++++++++++++++++-- tests/nghttp2_session_test.c | 6 +++--- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/nghttp2_outbound_item.h b/lib/nghttp2_outbound_item.h index 8c367381..a6daec16 100644 --- a/lib/nghttp2_outbound_item.h +++ b/lib/nghttp2_outbound_item.h @@ -48,7 +48,12 @@ typedef struct { nghttp2_frame_category frame_cat; void *frame; void *aux_data; - int pri; + /* The priority used in priority comparion */ + int32_t pri; + /* The initial priority */ + int32_t inipri; + /* The amount of priority decrement in next time */ + uint32_t pri_decay; int64_t seq; } nghttp2_outbound_item; diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index e94299ac..46829138 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -424,7 +424,8 @@ static int outbound_item_update_pri return 0; } } - item->pri = stream->pri; + /* We only update initial priority */ + item->inipri = stream->pri; return 1; } @@ -442,10 +443,12 @@ void nghttp2_session_reprioritize_stream return; } stream->pri = pri; + /* For submitted frames, we only update initial priority, so the + structure of the queue will remain unchanged. */ nghttp2_pq_update(&session->ob_pq, update_stream_pri, stream); nghttp2_pq_update(&session->ob_ss_pq, update_stream_pri, stream); if(stream->deferred_data) { - stream->deferred_data->pri = pri; + stream->deferred_data->inipri = pri; } if(session->aob.item) { outbound_item_update_pri(session->aob.item, stream); @@ -471,6 +474,7 @@ int nghttp2_session_add_frame(nghttp2_session *session, item->seq = session->next_seq++; /* Set priority to the default value at the moment. */ item->pri = NGHTTP2_PRI_DEFAULT; + item->pri_decay = 1; if(frame_cat == NGHTTP2_CAT_CTRL) { nghttp2_frame *frame = (nghttp2_frame*)abs_frame; nghttp2_stream *stream = NULL; @@ -546,6 +550,7 @@ int nghttp2_session_add_frame(nghttp2_session *session, free(item); return r; } + item->inipri = item->pri; return 0; } @@ -1385,6 +1390,23 @@ nghttp2_outbound_item* nghttp2_session_pop_next_ob_item } } +/* + * Adjust priority of the |item|. In order to prevent the low priority + * streams from starving, lower the priority of the |item| by + * item->pri_decay. If the resulting priority exceeds + * NGHTTP2_PRI_DEFAULT, back to the original priority. + */ +static void adjust_pri(nghttp2_outbound_item *item) +{ + if(item->pri >= (int32_t)(NGHTTP2_PRI_LOWEST - (item->pri_decay - 1))) { + item->pri = item->inipri; + item->pri_decay = 1; + return; + } + item->pri += (int32_t)(item->pri_decay - 1); + item->pri_decay <<= 1; +} + /* * Called after a frame is sent. * @@ -1563,6 +1585,7 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) } else { nghttp2_outbound_item* next_item; next_item = nghttp2_session_get_next_ob_item(session); + adjust_pri(session->aob.item); /* 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. */ diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index e70bb9ee..180918da 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -1869,13 +1869,13 @@ void test_nghttp2_session_reprioritize_stream(void) nghttp2_session_reprioritize_stream(session, stream, 120); CU_ASSERT(session->aob.item != NULL); - CU_ASSERT(120 == session->aob.item->pri); + CU_ASSERT(120 == session->aob.item->inipri); CU_ASSERT(120 == stream->pri); CU_ASSERT(5000 == nghttp2_session_get_stream(session, 1)->pri); item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(120 == item->pri); + CU_ASSERT(5000 == item->inipri); CU_ASSERT(NGHTTP2_HEADERS == OB_CTRL_TYPE(item)); - CU_ASSERT(3 == OB_CTRL(item)->hd.stream_id); + CU_ASSERT(1 == OB_CTRL(item)->hd.stream_id); nghttp2_session_del(session);