Test send queue bypass, retry priority, and bounded admission

This commit is contained in:
evgeny committed 2026-10-24 11:22:28 +02:00
1 parent 8167e0e5f5
commit acde6bda9d
1 file changed
+76
+76
View File
@@ -839,6 +839,81 @@ static void retry_alternative_lifecycle(void) {
memory_pool_free(inst.pkt_pool, pkt); ack_all(); teardown();
}
/* Заблокированный повтор не держит следующие DATA; среди готовых пакетов повтор имеет приоритет. */
static void send_queue_scan(void) {
setup(); fill(a, 1, 100);
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
struct INFLIGHT_PACKET* blocked = (struct INFLIGHT_PACKET*)conn->input_send_q->head; CHECK(blocked);
b->inflight_lim_bytes = 1;
struct INFLIGHT_PACKET* fresh = enqueue(80);
struct ETCP_DGRAM* pkt = request();
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "Send scan: blocked=%u fresh=%u selected=%u", blocked->seq, fresh->seq,
pkt && pkt->attempt ? pkt->attempt->seq : fresh->last_link ? fresh->seq : 0);
CHECK(pkt && pkt->link == a && fresh->state == INFLIGHT_STATE_WAIT_ACK && blocked->state == INFLIGHT_STATE_WAIT_SEND);
CHECK(blocked->attempt_count == 1 && !blocked->active_attempts);
memory_pool_free(inst.pkt_pool, pkt); ack_all(); teardown();
setup(); fill(a, 1, 100); fill(b, 1, 100);
a->data_rtt_us = 10000; b->data_rtt_us = 20000;
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
blocked = (struct INFLIGHT_PACKET*)queue_find_data_by_index(conn->input_send_q, &(uint32_t){1});
struct INFLIGHT_PACKET* later = (struct INFLIGHT_PACKET*)queue_find_data_by_index(conn->input_send_q, &(uint32_t){2});
CHECK(blocked && later && conn->input_send_q->head == &blocked->ll);
b->inflight_lim_bytes = 1;
pkt = request(); CHECK(pkt && pkt->link == a && later->last_link == a && blocked->state == INFLIGHT_STATE_WAIT_SEND);
memory_pool_free(inst.pkt_pool, pkt); ack_all(); teardown();
setup(); fill(a, 1, 100); fresh = enqueue(80);
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
CHECK(conn->input_send_q->head == &fresh->ll);
pkt = request(); CHECK(pkt && pkt->link == b && fresh->state == INFLIGHT_STATE_WAIT_SEND);
memory_pool_free(inst.pkt_pool, pkt);
pkt = request(); CHECK(pkt && fresh->state == INFLIGHT_STATE_WAIT_ACK);
memory_pool_free(inst.pkt_pool, pkt); ack_all(); teardown();
setup(); fill(a, 256, 100);
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
b->inflight_lim_bytes = 1; fresh = enqueue(80);
uint64_t started = get_time_us(); pkt = request();
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "Send scan: blocked_prefix=256 elapsed_us=%llu", (unsigned long long)(get_time_us() - started));
CHECK(pkt && pkt->link == a && fresh->state == INFLIGHT_STATE_WAIT_ACK && conn->input_send_q->count == 256);
memory_pool_free(inst.pkt_pool, pkt); ack_all(); teardown();
}
static void queued_input_fragment(unsigned len) {
struct ETCP_FRAGMENT* fragment = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(conn->io_pool); CHECK(fragment);
fragment->ll.dgram = memory_pool_alloc(inst.data_pool); CHECK(fragment->ll.dgram);
fragment->ll.dgram_pool = inst.data_pool; fragment->ll.len = len; memset(fragment->ll.dgram, 0x5a, len);
conn->input_queue->callback_suspended = 1;
CHECK(queue_data_put(conn->input_queue, &fragment->ll) == 0);
}
/* Полностью заблокированная send_q не мешает подгрузке, но общий лимит и смена эпохи сохраняются. */
static void send_queue_input_admission(void) {
setup(); fill(a, 1, 100);
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
b->inflight_lim_bytes = 1; queued_input_fragment(80);
CHECK(conn->input_queue->count == 1 && conn->input_send_q->count == 1);
struct ETCP_DGRAM* pkt = request(); CHECK(pkt && pkt->link == a && pkt->payload_len == 80);
CHECK(!conn->input_queue->count && conn->input_send_q->count == 1 && conn->input_wait_ack->count == 1);
memory_pool_free(inst.pkt_pool, pkt); ack_all(); teardown();
setup(); fill(a, 1, 100);
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
b->inflight_lim_bytes = 1; conn->optimal_inflight = 100; queued_input_fragment(80);
CHECK(!request() && conn->input_queue->count == 1 && conn->next_tx_id == 2 && conn->unacked_bytes == 100);
ack(1); pkt = request(); CHECK(pkt && pkt->payload_len == 80 && !conn->input_queue->count);
memory_pool_free(inst.pkt_pool, pkt); ack_all(); teardown();
setup(); fill(a, 1, 100);
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
b->inflight_lim_bytes = 1; queued_input_fragment(80); fail_tx_inflight = 1;
uint32_t reset = conn->reset_count;
CHECK(!request() && !fail_tx_inflight && conn->reset_count != reset && conn->reinit_pending);
CHECK(!conn->unacked_bytes && !conn->active_attempts && !conn->input_send_q->count && !conn->input_queue->count);
teardown();
}
/* Поздняя копия и общий till не освобождают окно/не обучают модель другого канала. */
static void independent_attempt_acks(void) {
setup(); enable_models(); fill(a, 1, 100);
@@ -1294,6 +1369,7 @@ int main(void) {
config_limits, timer_failure, immediate_send, ack_without_data_pacing, bbr_delivery_clock, bbr_probe_window, tcp_initial_window,
retransmission_timeout, pacing_rate_changes, pacing_timer_failure, retry_rotation, independent_attempt_acks,
retry_waits_for_alternative, retry_alternative_lifecycle,
send_queue_scan, send_queue_input_admission,
receive_admission_and_path_acks, peer_window_queries, pacing_continuity, pacing_data_units, attempt_memory_limits,
normalizer_rx_oom, normalizer_tx_oom, normalizer_callback_abort, reset_without_allocation, restart_allocation_failures,
receiver_bandwidth_bounds, receiver_point_lifecycle, receiver_point_wire, receiver_point_truncated,