void nghttp2_stream_reschedule(nghttp2_stream *stream) { nghttp2_stream *dep_stream; assert(stream->queued); dep_stream = stream->dep_prev; for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) { if (nghttp2_pq_size(&dep_stream->obq) == 1) { dep_stream->descendant_last_cycle = 0; stream->cycle = 0; } else { dep_stream->descendant_last_cycle = nghttp2_max(dep_stream->descendant_last_cycle, stream->cycle); stream->cycle = stream_next_cycle(stream, dep_stream->descendant_last_cycle); nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); } DEBUGF(fprintf(stderr, "stream: stream=%d obq resched cycle=%ld\n", stream->stream_id, stream->cycle)); dep_stream->last_writelen = stream->last_writelen; } }
static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) { /* This is required for Android NDK r10d */ int rv = 0; nghttp2_outbound_item *item; assert(stream->item); assert(stream->item->queued == 0); item = stream->item; /* If item is now sent, don't push it to the queue. Otherwise, we may push same item twice. */ if (session->aob.item == item) { return 0; } /* Penalize item by delaying scheduling according to effective weight. This will delay low priority stream, which is good. OTOH, this may incur delay for high priority item. Will see. */ item->cycle = session->last_cycle + NGHTTP2_DATA_PAYLOADLEN * NGHTTP2_MAX_WEIGHT / stream->effective_weight; switch (item->frame.hd.type) { case NGHTTP2_DATA: rv = nghttp2_pq_push(&session->ob_da_pq, item); break; case NGHTTP2_HEADERS: if (stream->state == NGHTTP2_STREAM_RESERVED) { rv = nghttp2_pq_push(&session->ob_ss_pq, item); } else { rv = nghttp2_pq_push(&session->ob_pq, item); } break; default: /* should not reach here */ assert(0); } if (rv != 0) { return rv; } item->queued = 1; return 0; }
static int stream_push_data(nghttp2_stream *stream, nghttp2_session *session) { int rv; nghttp2_outbound_item *item; assert(stream->data_item); assert(stream->data_item->queued == 0); item = stream->data_item; /* If item is now sent, don't push it to the queue. Otherwise, we may push same item twice. */ if (session->aob.item == item) { return 0; } if (item->weight > stream->effective_weight) { item->weight = stream->effective_weight; } item->cycle = session->last_cycle; switch (item->frame.hd.type) { case NGHTTP2_DATA: rv = nghttp2_pq_push(&session->ob_da_pq, item); break; case NGHTTP2_HEADERS: if (stream->state == NGHTTP2_STREAM_RESERVED) { rv = nghttp2_pq_push(&session->ob_ss_pq, item); } else { rv = nghttp2_pq_push(&session->ob_pq, item); } break; default: /* should not reach here */ assert(0); } if (rv != 0) { return rv; } item->queued = 1; return 0; }
void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) { nghttp2_stream *dep_stream; uint32_t last_cycle; int32_t old_weight; uint32_t wlen_penalty; if (stream->weight == weight) { return; } old_weight = stream->weight; stream->weight = weight; dep_stream = stream->dep_prev; if (!dep_stream) { return; } dep_stream->sum_dep_weight += weight - old_weight; if (!stream->queued) { return; } nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); wlen_penalty = (uint32_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT; /* Compute old stream->pending_penalty we used to calculate stream->cycle */ stream->pending_penalty = (uint32_t)((stream->pending_penalty + (uint32_t)old_weight - (wlen_penalty % (uint32_t)old_weight)) % (uint32_t)old_weight); last_cycle = stream->cycle - (wlen_penalty + stream->pending_penalty) / (uint32_t)old_weight; /* Now we have old stream->pending_penalty and new stream->weight in place */ stream_next_cycle(stream, last_cycle); if (stream->cycle < dep_stream->descendant_last_cycle && (dep_stream->descendant_last_cycle - stream->cycle) <= NGHTTP2_MAX_CYCLE_DISTANCE) { stream->cycle = dep_stream->descendant_last_cycle; } /* Continue to use same stream->seq */ nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); DEBUGF("stream: stream=%d obq resched cycle=%d\n", stream->stream_id, stream->cycle); }
void test_nghttp2_pq(void) { int i; nghttp2_pq pq; nghttp2_pq_init(&pq, pq_compar); CU_ASSERT(nghttp2_pq_empty(&pq)); CU_ASSERT(0 == nghttp2_pq_size(&pq)); CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo")); CU_ASSERT(0 == nghttp2_pq_empty(&pq)); CU_ASSERT(1 == nghttp2_pq_size(&pq)); CU_ASSERT(strcmp("foo", nghttp2_pq_top(&pq)) == 0); CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"bar")); CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"baz")); CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"C")); CU_ASSERT(4 == nghttp2_pq_size(&pq)); CU_ASSERT(strcmp("C", nghttp2_pq_top(&pq)) == 0); nghttp2_pq_pop(&pq); CU_ASSERT(3 == nghttp2_pq_size(&pq)); CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); nghttp2_pq_pop(&pq); CU_ASSERT(strcmp("baz", nghttp2_pq_top(&pq)) == 0); nghttp2_pq_pop(&pq); CU_ASSERT(strcmp("foo", nghttp2_pq_top(&pq)) == 0); nghttp2_pq_pop(&pq); CU_ASSERT(nghttp2_pq_empty(&pq)); CU_ASSERT(0 == nghttp2_pq_size(&pq)); CU_ASSERT(NULL == nghttp2_pq_top(&pq)); /* Add bunch of entry to see realloc works */ for (i = 0; i < 10000; ++i) { CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo")); CU_ASSERT((size_t)(i + 1) == nghttp2_pq_size(&pq)); } for (i = 10000; i > 0; --i) { CU_ASSERT(NULL != nghttp2_pq_top(&pq)); nghttp2_pq_pop(&pq); CU_ASSERT((size_t)(i - 1) == nghttp2_pq_size(&pq)); } nghttp2_pq_free(&pq); }
void nghttp2_stream_reschedule(nghttp2_stream *stream) { nghttp2_stream *dep_stream; assert(stream->queued); dep_stream = stream->dep_prev; for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) { nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); stream_next_cycle(stream, dep_stream->descendant_last_cycle); stream->seq = dep_stream->descendant_next_seq++; nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); DEBUGF("stream: stream=%d obq resched cycle=%d\n", stream->stream_id, stream->cycle); dep_stream->last_writelen = stream->last_writelen; } }
static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) { int rv; for (; dep_stream && !stream->queued; stream = dep_stream, dep_stream = dep_stream->dep_prev) { stream->cycle = stream_next_cycle(stream, dep_stream->descendant_last_cycle); DEBUGF(fprintf(stderr, "stream: stream=%d obq push cycle=%ld\n", stream->stream_id, stream->cycle)); DEBUGF(fprintf(stderr, "stream: push stream %d to stream %d\n", stream->stream_id, dep_stream->stream_id)); rv = nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); if (rv != 0) { return rv; } stream->queued = 1; } return 0; }
void test_nghttp2_pq(void) { int i; nghttp2_pq pq; string_entry *top; nghttp2_pq_init(&pq, pq_less, nghttp2_mem_default()); CU_ASSERT(nghttp2_pq_empty(&pq)); CU_ASSERT(0 == nghttp2_pq_size(&pq)); CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("foo")->ent)); CU_ASSERT(0 == nghttp2_pq_empty(&pq)); CU_ASSERT(1 == nghttp2_pq_size(&pq)); top = (string_entry *)nghttp2_pq_top(&pq); CU_ASSERT(strcmp("foo", top->s) == 0); CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("bar")->ent)); top = (string_entry *)nghttp2_pq_top(&pq); CU_ASSERT(strcmp("bar", top->s) == 0); CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("baz")->ent)); top = (string_entry *)nghttp2_pq_top(&pq); CU_ASSERT(strcmp("bar", top->s) == 0); CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("C")->ent)); CU_ASSERT(4 == nghttp2_pq_size(&pq)); top = (string_entry *)nghttp2_pq_top(&pq); CU_ASSERT(strcmp("C", top->s) == 0); string_entry_del(top); nghttp2_pq_pop(&pq); CU_ASSERT(3 == nghttp2_pq_size(&pq)); top = (string_entry *)nghttp2_pq_top(&pq); CU_ASSERT(strcmp("bar", top->s) == 0); nghttp2_pq_pop(&pq); string_entry_del(top); top = (string_entry *)nghttp2_pq_top(&pq); CU_ASSERT(strcmp("baz", top->s) == 0); nghttp2_pq_pop(&pq); string_entry_del(top); top = (string_entry *)nghttp2_pq_top(&pq); CU_ASSERT(strcmp("foo", top->s) == 0); nghttp2_pq_pop(&pq); string_entry_del(top); CU_ASSERT(nghttp2_pq_empty(&pq)); CU_ASSERT(0 == nghttp2_pq_size(&pq)); CU_ASSERT(NULL == nghttp2_pq_top(&pq)); /* Add bunch of entry to see realloc works */ for (i = 0; i < 10000; ++i) { CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("foo")->ent)); CU_ASSERT((size_t)(i + 1) == nghttp2_pq_size(&pq)); } for (i = 10000; i > 0; --i) { top = (string_entry *)nghttp2_pq_top(&pq); CU_ASSERT(NULL != top); nghttp2_pq_pop(&pq); string_entry_del(top); CU_ASSERT((size_t)(i - 1) == nghttp2_pq_size(&pq)); } nghttp2_pq_free(&pq); }