Skip to content

Commit

Permalink
Merge pull request #1582 from private-octopus/heavy-loss
Browse files Browse the repository at this point in the history
Only disconnect on idle timeout, not on packet losses
  • Loading branch information
huitema authored Nov 26, 2023
2 parents e86ef9f + 4178263 commit 70aaa41
Show file tree
Hide file tree
Showing 10 changed files with 202 additions and 16 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ else()
endif()

project(picoquic
VERSION 1.1.14.0
VERSION 1.1.15.0
DESCRIPTION "picoquic library"
LANGUAGES C CXX)

Expand Down
21 changes: 21 additions & 0 deletions UnitTest1/unittest1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,27 @@ namespace UnitTest1
Assert::AreEqual(ret, 0);
}

TEST_METHOD(heavy_loss)
{
int ret = heavy_loss_test();

Assert::AreEqual(ret, 0);
}

TEST_METHOD(heavy_loss_inter)
{
int ret = heavy_loss_inter_test();

Assert::AreEqual(ret, 0);
}

TEST_METHOD(heavy_loss_total)
{
int ret = heavy_loss_total_test();

Assert::AreEqual(ret, 0);
}

TEST_METHOD(test_spurious_retransmit)
{
int ret = spurious_retransmit_test();
Expand Down
6 changes: 2 additions & 4 deletions picoquic/loss_recovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -515,12 +515,10 @@ static size_t picoquic_retransmit_needed_packet(picoquic_cnx_t* cnx, picoquic_pa
}
if (all_paths_bad) {
/*
* Max retransmission count was exceeded. Disconnect.
* Max retransmission count was exceeded. Log.
*/
DBG_PRINTF("Too many retransmits of packet number %d, disconnect", (int)old_p->sequence_number);
cnx->local_error = PICOQUIC_ERROR_REPEAT_TIMEOUT;
picoquic_connection_disconnect(cnx);
length = 0;

*continue_next = 0;
}
}
Expand Down
2 changes: 1 addition & 1 deletion picoquic/picoquic.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
extern "C" {
#endif

#define PICOQUIC_VERSION "1.1.14.0"
#define PICOQUIC_VERSION "1.1.15.0"
#define PICOQUIC_ERROR_CLASS 0x400
#define PICOQUIC_ERROR_DUPLICATE (PICOQUIC_ERROR_CLASS + 1)
#define PICOQUIC_ERROR_AEAD_CHECK (PICOQUIC_ERROR_CLASS + 3)
Expand Down
20 changes: 19 additions & 1 deletion picoquic/timing.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,25 @@ uint64_t picoquic_current_retransmit_timer(picoquic_cnx_t* cnx, picoquic_path_t
{
uint64_t rto = path_x->retransmit_timer;

rto <<= (path_x->nb_retransmit < 3) ? path_x->nb_retransmit : 2;
if (path_x->nb_retransmit > 0) {
if (path_x->nb_retransmit < 3) {
rto <<= path_x->nb_retransmit;
}
else {
uint64_t n1 = path_x->nb_retransmit - 2;
if (n1 > 18) {
n1 = 18;
}
rto <<= (2 + (n1 / 4));
n1 &= 3;
rto += (n1*rto) >> 2;
}
if (cnx->idle_timeout > 15) {
if (rto > (cnx->idle_timeout >> 4)) {
rto = cnx->idle_timeout >> 4;
}
}
}

if (cnx->cnx_state < picoquic_state_client_ready_start) {
if (PICOQUIC_MICROSEC_HANDSHAKE_MAX / 1000 < cnx->local_parameters.idle_timeout) {
Expand Down
3 changes: 3 additions & 0 deletions picoquic_t/picoquic_t.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ static const picoquic_test_def_t test_table[] = {
{ "red_cc", red_cc_test },
{ "multi_segment", multi_segment_test },
{ "pacing_cc", pacing_cc_test },
{ "heavy_loss", heavy_loss_test },
{ "heavy_loss_inter", heavy_loss_inter_test },
{ "heavy_loss_total", heavy_loss_total_test },
{ "spurious_retransmit", spurious_retransmit_test },
{ "tls_zero_share", tls_zero_share_test },
{ "transport_param_log", transport_param_log_test },
Expand Down
16 changes: 11 additions & 5 deletions picoquictest/edge_cases.c
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,8 @@ int ec9a_preemptive_amok_test()
picoquic_test_tls_api_ctx_t* test_ctx = NULL;
uint64_t initial_losses = 0b100000000000;
uint8_t test_case_id = 0x9a;
uint64_t cnx_server_idle_timeout = 0;
uint64_t cnx_server_nb_preemptive_repeat = 0;
int ret = edge_case_prepare(&test_ctx, test_case_id, 0, &simulated_time, initial_losses, 12);

if (ret == 0) {
Expand Down Expand Up @@ -566,14 +568,18 @@ int ec9a_preemptive_amok_test()
picoquic_cnx_t * last_cnx;
int loop_count = 0;
int send_count = 0;
const int send_count_max = 30;
const int send_count_max = 50;
uint64_t repeat_begin = simulated_time;
uint64_t repeat_duration = 0;

cnx_server_idle_timeout = test_ctx->cnx_server->idle_timeout;
cnx_server_nb_preemptive_repeat = test_ctx->cnx_server->nb_preemptive_repeat;

picoquic_reinsert_by_wake_time(test_ctx->qserver, test_ctx->cnx_server, simulated_time);

while (test_ctx->cnx_server->cnx_state == picoquic_state_ready && loop_count < 10000 && ret == 0) {
while (test_ctx->qserver->current_number_connections > 0 && test_ctx->cnx_server->cnx_state == picoquic_state_ready && loop_count < 10000 && ret == 0) {
loop_count++;
cnx_server_nb_preemptive_repeat = test_ctx->cnx_server->nb_preemptive_repeat;
simulated_time = picoquic_get_next_wake_time(test_ctx->qserver, simulated_time);
ret = picoquic_prepare_next_packet_ex(test_ctx->qserver, simulated_time, buffer,
sizeof(buffer), &send_length, &addr_to, &addr_from, &if_index, &log_id,
Expand All @@ -593,12 +599,12 @@ int ec9a_preemptive_amok_test()
send_count, send_count_max);
ret = -1;
}
else if (repeat_duration > test_ctx->cnx_server->idle_timeout) {
else if (repeat_duration > cnx_server_idle_timeout) {
DBG_PRINTF("End at t=%" PRIu64 ", later than %" PRIu64,
simulated_time, test_ctx->cnx_server->idle_timeout);
simulated_time, cnx_server_idle_timeout);
ret = -1;
}
else if (test_ctx->cnx_server->nb_preemptive_repeat == 0) {
else if (cnx_server_nb_preemptive_repeat == 0) {
DBG_PRINTF("%s", "No preemptive repeat");
ret = -1;
}
Expand Down
4 changes: 2 additions & 2 deletions picoquictest/multipath_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1080,7 +1080,7 @@ int multipath_abandon_test()
*/
int multipath_back1_test()
{
uint64_t max_completion_microsec = 3000000;
uint64_t max_completion_microsec = 3050000;

return multipath_test_one(max_completion_microsec, multipath_test_back1, 0);
}
Expand Down Expand Up @@ -1137,7 +1137,7 @@ int multipath_standby_test()

int multipath_standup_test()
{
uint64_t max_completion_microsec = 4500000;
uint64_t max_completion_microsec = 4750000;

return multipath_test_one(max_completion_microsec, multipath_test_standup, 0);
}
Expand Down
3 changes: 3 additions & 0 deletions picoquictest/picoquictest.h
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,9 @@ int port_blocked_test();
int red_cc_test();
int multi_segment_test();
int pacing_cc_test();
int heavy_loss_test();
int heavy_loss_inter_test();
int heavy_loss_total_test();
int integrity_limit_test();
int excess_repeat_test();
int netperf_basic_test();
Expand Down
141 changes: 139 additions & 2 deletions picoquictest/tls_api_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -3404,6 +3404,7 @@ int immediate_close_test()
ret = tls_api_one_sim_round(test_ctx, &simulated_time, 0, &was_active);
if (test_ctx->cnx_client->cnx_state >= picoquic_state_disconnected) {
/* Client has noticed the disconnect */
ret = 0;
break;
}
}
Expand Down Expand Up @@ -11492,6 +11493,140 @@ int pacing_cc_test()
return ret;
}

/* heavy loss test:
* Simulate a connection experiencing heavy packet
* loss, such as 50% packet loss, for a duration of
* 5 seconds. The test succeeds if the connection stays
* up and the transfer completes.
*/

test_api_stream_desc_t* heavy_loss_inter_scenario(size_t* scenario_size)
{
size_t nb_rounds = 100;
size_t sc_z = sizeof(test_api_stream_desc_t) * nb_rounds;
test_api_stream_desc_t* sc;
uint64_t previous_stream_id = 0;
uint64_t next_stream_id = 4;

sc = (test_api_stream_desc_t*)malloc(sc_z);
if (sc != NULL) {
memset(sc, 0, sc_z);

for (size_t i = 0; i < nb_rounds; i++) {
sc[i].previous_stream_id = previous_stream_id;
sc[i].stream_id = next_stream_id;
sc[i].q_len = 255;
sc[i].r_len = 1000;
previous_stream_id = next_stream_id;
next_stream_id += 4;
}
*scenario_size = sc_z;
}
return sc;
}

int heavy_loss_test_one(int scenario_id, uint64_t completion_target)
{
uint64_t simulated_time = 0;
uint64_t loss_mask = 0;
test_api_stream_desc_t* scenario = test_scenario_sustained;
size_t scenario_size = sizeof(test_scenario_sustained);
test_api_stream_desc_t* allocated_scenario = NULL;
picoquic_test_tls_api_ctx_t* test_ctx = NULL;
picoquic_connection_id_t initial_cid = { {0x8e, 0xfe, 0x10, 0x55, 0, 0, 0, 0}, 8 };
int ret;

ret = tls_api_init_ctx_ex(&test_ctx, PICOQUIC_INTERNAL_TEST_VERSION_1,
PICOQUIC_TEST_SNI, PICOQUIC_TEST_ALPN, &simulated_time, NULL, NULL, 0, 1, 0, &initial_cid);

if (ret == 0) {
/* Set the CC algorithm to selected value */
picoquic_set_default_congestion_algorithm(test_ctx->qserver, picoquic_bbr_algorithm);
picoquic_set_binlog(test_ctx->qserver, ".");
test_ctx->qserver->use_long_log = 1;
}

if (ret == 0 && scenario_id == 1) {
allocated_scenario = heavy_loss_inter_scenario(&scenario_size);
if (allocated_scenario == NULL) {
DBG_PRINTF("%s", "Could not allocate interactive scenario");
ret = -1;
}
else {
scenario = allocated_scenario;
}
}

if (ret == 0) {
ret = picoquic_start_client_cnx(test_ctx->cnx_client);
}

if (ret == 0) {
ret = tls_api_connection_loop(test_ctx, &loss_mask, 0, &simulated_time);
}

/* Prepare to send data */
if (ret == 0) {
ret = test_api_init_send_recv_scenario(test_ctx, scenario, scenario_size);
}

/* Send for 0.1 second, in order to ramp up transfer speed */
if (ret == 0) {
ret = tls_api_wait_for_timeout(test_ctx, &simulated_time, 100000);
}

/* Send for up to 30 seconds, with 50% packet loss rate.
* Stop earlier if something breaks, or if all the required
* data is sent.
* In scenario 2, simulate "total loss".
*/
if (ret == 0) {
loss_mask = (scenario_id == 2)?UINT64_MAX:0x13596ac77ca69531ull;
for (int i = 0; ret == 0 && !test_ctx->test_finished && i < 20; i++) {
ret = tls_api_wait_for_timeout(test_ctx, &simulated_time, 1000000);
}
}

/* Stop losing packets, try to complete the data sending loop */
if (ret == 0) {
loss_mask = 0;
ret = tls_api_data_sending_loop(test_ctx, &loss_mask, &simulated_time, 0);
}

/* verify that the transmission was complete */
if (ret == 0) {
ret = tls_api_one_scenario_body_verify(test_ctx, &simulated_time, completion_target);
}

if (test_ctx != NULL) {
tls_api_delete_ctx(test_ctx);
test_ctx = NULL;
}

if (allocated_scenario != NULL) {
free(allocated_scenario);
}

return ret;
}

int heavy_loss_test()
{
return heavy_loss_test_one(0, 23000000);
}

int heavy_loss_inter_test()
{
return heavy_loss_test_one(1, 21000000);
}

int heavy_loss_total_test()
{
return heavy_loss_test_one(2, 25000000);
}



int integrity_limit_test()
{
uint64_t simulated_time = 0;
Expand Down Expand Up @@ -11637,7 +11772,7 @@ int excess_repeat_test_one(picoquic_congestion_algorithm_t* cc_algo, int repeat_
int if_index = 0;
picoquic_connection_id_t log_cid;
picoquic_cnx_t* last_cnx;
uint64_t max_disconnected_time = simulated_time + 20000000;
uint64_t max_disconnected_time = simulated_time + 30000000;
int nb_loops = 0;

if (cc_algo->congestion_algorithm_number == PICOQUIC_CC_ALGO_NUMBER_DCUBIC ||
Expand All @@ -11649,7 +11784,9 @@ int excess_repeat_test_one(picoquic_congestion_algorithm_t* cc_algo, int repeat_
ret = -1;
}
else {
while (ret == 0 && test_ctx->cnx_server != NULL &&
while (ret == 0 &&
test_ctx->qserver->current_number_connections > 0 &&
test_ctx->cnx_server != NULL &&
test_ctx->cnx_server->cnx_state != picoquic_state_disconnected) {
uint64_t old_time = simulated_time;
uint64_t delta_t;
Expand Down

0 comments on commit 70aaa41

Please sign in to comment.