Skip to content

Commit 1e45db3

Browse files
authored
Merge pull request #1680 from private-octopus/media_test_check_reset
Notification of loss of feedback
2 parents 2ccf08f + 302c689 commit 1e45db3

File tree

11 files changed

+159
-13
lines changed

11 files changed

+159
-13
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ else()
88
endif()
99

1010
project(picoquic
11-
VERSION 1.1.19.10
11+
VERSION 1.1.19.11
1212
DESCRIPTION "picoquic library"
1313
LANGUAGES C CXX)
1414

UnitTest1/unittest1.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2273,6 +2273,12 @@ namespace UnitTest1
22732273
Assert::AreEqual(ret, 0);
22742274
}
22752275

2276+
TEST_METHOD(mediatest_suspension) {
2277+
int ret = mediatest_suspension_test();
2278+
2279+
Assert::AreEqual(ret, 0);
2280+
}
2281+
22762282
TEST_METHOD(warptest_video) {
22772283
int ret = warptest_video_test();
22782284

picoquic/bbr.c

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ typedef struct st_picoquic_bbr_state_t {
230230
uint64_t recovery_packet_number;
231231
uint64_t recovery_delivered;
232232

233+
/* Management of lost feedback */
234+
unsigned int is_handling_lost_feedback;
235+
uint64_t cwin_before_lost_feedback;
236+
233237
/* Management of ECN marks */
234238
uint64_t ecn_ect1_last_round;
235239
uint64_t ecn_ce_last_round;
@@ -607,6 +611,31 @@ static void BBROnEnterFastRecovery(picoquic_bbr_state_t* bbr_state, picoquic_pat
607611
bbr_state->recovery_delivered = path_x->delivered;
608612
}
609613

614+
/* Handling of the "lost feedback" state.
615+
*/
616+
static void BBREnterLostFeedback(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x)
617+
{
618+
if ((IsInAProbeBWState(bbr_state) || bbr_state->state == picoquic_bbr_alg_drain) &&
619+
!bbr_state->is_handling_lost_feedback &&
620+
path_x->cnx->cnx_state == picoquic_state_ready) {
621+
/* Remembering the old cwin, so the state can be restored when the
622+
* condition is lifted. */
623+
bbr_state->cwin_before_lost_feedback = path_x->cwin;
624+
/* setting the congestion window to exactly the bytes in transit, thus
625+
* preventing any further transmission until the condition is lifted */
626+
path_x->cwin = path_x->bytes_in_transit;
627+
bbr_state->is_handling_lost_feedback = 1;
628+
}
629+
}
630+
631+
static void BBRExitLostFeedback(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x)
632+
{
633+
if (bbr_state->is_handling_lost_feedback) {
634+
path_x->cwin = bbr_state->cwin_before_lost_feedback;
635+
bbr_state->is_handling_lost_feedback = 0;
636+
}
637+
}
638+
610639
/* In picoquic, the arrival of an RTO maps to a "timer based" packet loss.
611640
* Question: do we want this on a loss signal, or simply when observing
612641
* loss data in ack notification?
@@ -2177,18 +2206,24 @@ static void picoquic_bbr_notify(
21772206
BBRUpdateRecoveryOnLoss(bbr_state, path_x, ack_state->nb_bytes_newly_lost);
21782207
break;
21792208
case picoquic_congestion_notification_timeout:
2209+
BBRExitLostFeedback(bbr_state, path_x);
21802210
/* if loss is PTO, we should start the OnPto processing */
21812211
BBROnEnterRTO(bbr_state, path_x, ack_state->lost_packet_number);
21822212
break;
21832213
case picoquic_congestion_notification_spurious_repeat:
21842214
/* handling of suspension */
21852215
BBROnSpuriousLoss(bbr_state, path_x, ack_state->lost_packet_number, current_time);
21862216
break;
2217+
case picoquic_congestion_notification_lost_feedback:
2218+
/* Feedback has been lost. It will be restored at the next notification. */
2219+
BBREnterLostFeedback(bbr_state, path_x);
2220+
break;
21872221
case picoquic_congestion_notification_rtt_measurement:
21882222
/* TODO: this call is subsumed by the acknowledgement notification.
21892223
* Consider removing it from the API once other CC algorithms are updated. */
21902224
break;
2191-
case picoquic_congestion_notification_acknowledgement:
2225+
case picoquic_congestion_notification_acknowledgement:
2226+
BBRExitLostFeedback(bbr_state, path_x);
21922227
picoquic_bbr_notify_ack(bbr_state, path_x, ack_state, current_time);
21932228
if (bbr_state->state == picoquic_bbr_alg_startup_long_rtt) {
21942229
picoquic_update_pacing_data(cnx, path_x, 1);

picoquic/frames.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2885,6 +2885,7 @@ void process_decoded_packet_data(picoquic_cnx_t* cnx, picoquic_path_t * path_x,
28852885
ack_state.inflight_prior = packet_data->path_ack[i].inflight_prior;
28862886
ack_state.is_app_limited = packet_data->path_ack[i].rs_is_path_limited;
28872887
ack_state.is_cwnd_limited = packet_data->path_ack[i].rs_is_cwnd_limited;
2888+
packet_data->path_ack[i].acked_path->is_lost_feedback_notified = 0;
28882889
cnx->congestion_alg->alg_notify(cnx, packet_data->path_ack[i].acked_path,
28892890
picoquic_congestion_notification_acknowledgement,
28902891
&ack_state, current_time);

picoquic/picoquic.h

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
extern "C" {
4141
#endif
4242

43-
#define PICOQUIC_VERSION "1.1.19.10"
43+
#define PICOQUIC_VERSION "1.1.19.11"
4444
#define PICOQUIC_ERROR_CLASS 0x400
4545
#define PICOQUIC_ERROR_DUPLICATE (PICOQUIC_ERROR_CLASS + 1)
4646
#define PICOQUIC_ERROR_AEAD_CHECK (PICOQUIC_ERROR_CLASS + 3)
@@ -1406,7 +1406,8 @@ typedef enum {
14061406
picoquic_congestion_notification_ecn_ec,
14071407
picoquic_congestion_notification_cwin_blocked,
14081408
picoquic_congestion_notification_seed_cwin,
1409-
picoquic_congestion_notification_reset
1409+
picoquic_congestion_notification_reset,
1410+
picoquic_congestion_notification_lost_feedback /* notification of lost feedback */
14101411
} picoquic_congestion_notification_t;
14111412

14121413
typedef struct st_picoquic_per_ack_state_t {
@@ -1485,6 +1486,32 @@ void picoquic_set_default_wifi_shadow_rtt(picoquic_quic_t* quic, uint64_t wifi_s
14851486
*/
14861487
void picoquic_set_default_bbr_quantum_ratio(picoquic_quic_t* quic, double quantum_ratio);
14871488

1489+
/* The experimental API `picoquic_set_feedback_loss_notification` allow applications
1490+
* to turn on the "feedback lost" event notification. These events are
1491+
* passed to the congestion control algorithm, allowing it to react
1492+
* quickly to a temporary loss of connectivity, instead of waiting
1493+
* for retransmission timers. Delay sensitive applications use this
1494+
* feature to stop queuing more data when connectivity is lost,
1495+
* and thus avoid the queues of less urgent data to delay
1496+
* arrival of urgent real time frames when connectivity is restored.
1497+
* On the other hand, this feature may lower the performance of
1498+
* applications sending lots of data, and thus should only be
1499+
* used when applications require it.
1500+
*
1501+
* The lost control events fires if there is more that 2 "ack delay max" between
1502+
* the last ACK received and the next one. In practice, that means 1 RTT + 2 ack delays
1503+
* after the first non acked packet was sent. In contrast, the RTO fires
1504+
* 1 RTT + 4 STDEV + 1 ack delay after the last packet was sent. Given congestion
1505+
* control and CWIN, this "last packet" is typically sent 1 RTT after the "first
1506+
* packet not acknowledged". Thus, the "lost control" event will typically
1507+
* happen 1 RTT before the RTO event.
1508+
*
1509+
* The `should_notify` should be set 1 to enable the feature, or to 0
1510+
* to stop notifications. It is set by default to zero when a connection
1511+
* is created.
1512+
*/
1513+
void picoquic_set_feedback_loss_notification(picoquic_cnx_t* cnx, unsigned int should_notify);
1514+
14881515
/* Bandwidth update and congestion control parameters value.
14891516
* Congestion control in picoquic is characterized by three values:
14901517
* - pacing rate, expressed in bytes per second (for example, 10Mbps would be noted as 1250000)

picoquic/picoquic_internal.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,7 +1079,8 @@ typedef struct st_picoquic_path_t {
10791079
unsigned int is_datagram_ready : 1;
10801080
unsigned int is_pto_required : 1; /* Should send PTO probe */
10811081
unsigned int is_probing_nat : 1; /* When path transmission is scheduled only for NAT probing */
1082-
1082+
unsigned int is_lost_feedback_notified : 1; /* Lost feedback has been notified */
1083+
10831084
/* Management of retransmissions in a path.
10841085
* The "path_packet" variables are used for the RACK algorithm, per path, to avoid
10851086
* declaring packets lost just because another path is delivering them faster.
@@ -1292,7 +1293,8 @@ typedef struct st_picoquic_cnx_t {
12921293
unsigned int is_datagram_ready : 1; /* Active polling for datagrams */
12931294
unsigned int is_immediate_ack_required : 1; /* Should send an ACK asap */
12941295
unsigned int is_multipath_enabled : 1; /* Unique path ID extension has been negotiated */
1295-
1296+
unsigned int is_lost_feedback_notification_required : 1; /* CC algorithm requests lost feedback notification */
1297+
12961298
/* PMTUD policy */
12971299
picoquic_pmtud_policy_enum pmtud_policy;
12981300
/* Spin bit policy */

picoquic/quicctx.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4782,6 +4782,11 @@ void picoquic_set_default_bbr_quantum_ratio(picoquic_quic_t* quic, double quantu
47824782
quic->bbr_quantum_ratio = quantum_ratio;
47834783
}
47844784

4785+
void picoquic_set_feedback_loss_notification(picoquic_cnx_t* cnx, unsigned int should_notify)
4786+
{
4787+
cnx->is_lost_feedback_notification_required = should_notify;
4788+
}
4789+
47854790
void picoquic_subscribe_pacing_rate_updates(picoquic_cnx_t* cnx, uint64_t decrease_threshold, uint64_t increase_threshold)
47864791
{
47874792
cnx->pacing_decrease_threshold = decrease_threshold;

picoquic/sender.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4433,6 +4433,39 @@ static int picoquic_select_next_path(picoquic_cnx_t * cnx, uint64_t current_time
44334433
return path_id;
44344434
}
44354435

4436+
/* manage the CC timer, if any */
4437+
static int picoquic_check_cc_feedback_timer(picoquic_cnx_t* cnx, uint64_t* next_wake_time, uint64_t current_time)
4438+
{
4439+
int ret = 0;
4440+
4441+
if (cnx->is_lost_feedback_notification_required && cnx->congestion_alg != NULL) {
4442+
for (int i = 0; i < cnx->nb_paths; i++) {
4443+
picoquic_path_t* path_x = cnx->path[i];
4444+
if (!path_x->is_lost_feedback_notified){
4445+
picoquic_packet_context_t* pkt_ctx = (cnx->is_multipath_enabled)?
4446+
&path_x->pkt_ctx:&cnx->pkt_ctx[picoquic_packet_context_application];
4447+
if (pkt_ctx->pending_first != NULL) {
4448+
uint64_t delta_sent = (pkt_ctx->pending_first->send_time <= path_x->last_time_acked_data_frame_sent) ? 0 :
4449+
(pkt_ctx->pending_first->send_time - path_x->last_time_acked_data_frame_sent);
4450+
uint64_t lost_feedback_time = pkt_ctx->highest_acknowledged_time + delta_sent + 2 * cnx->ack_frequency_delay_local;
4451+
4452+
if (lost_feedback_time <= current_time) {
4453+
path_x->is_lost_feedback_notified = 1;
4454+
cnx->congestion_alg->alg_notify(cnx, path_x,
4455+
picoquic_congestion_notification_lost_feedback,
4456+
NULL, current_time);
4457+
}
4458+
else if (lost_feedback_time < *next_wake_time) {
4459+
*next_wake_time = lost_feedback_time;
4460+
SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
4461+
}
4462+
}
4463+
}
4464+
}
4465+
}
4466+
return ret;
4467+
}
4468+
44364469
/* Prepare next packet to send, or nothing.. */
44374470
int picoquic_prepare_packet_ex(picoquic_cnx_t* cnx,
44384471
uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length,
@@ -4458,6 +4491,10 @@ int picoquic_prepare_packet_ex(picoquic_cnx_t* cnx,
44584491

44594492
ret = picoquic_check_idle_timer(cnx, &next_wake_time, current_time);
44604493

4494+
if (ret == 0) {
4495+
ret = picoquic_check_cc_feedback_timer(cnx, &next_wake_time, current_time);
4496+
}
4497+
44614498
if (send_buffer_max < PICOQUIC_ENFORCED_INITIAL_MTU) {
44624499
DBG_PRINTF("Invalid buffer size: %zu", send_buffer_max);
44634500
ret = -1;

picoquic_t/picoquic_t.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ static const picoquic_test_def_t test_table[] = {
397397
{ "mediatest_video2_back", mediatest_video2_back_test },
398398
{ "mediatest_wifi", mediatest_wifi_test },
399399
{ "mediatest_worst", mediatest_worst_test },
400+
{ "mediatest_suspension", mediatest_suspension_test },
400401
{ "warptest_video", warptest_video_test },
401402
{ "warptest_video_audio", warptest_video_audio_test },
402403
{ "warptest_video_data_audio", warptest_video_data_audio_test },

picoquictest/mediatest.c

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ typedef enum {
6868
mediatest_worst = 4,
6969
mediatest_video2_down = 5,
7070
mediatest_wifi = 6,
71-
mediatest_video2_back = 7
71+
mediatest_video2_back = 7,
72+
mediatest_suspension = 8
7273
} mediatest_id_enum;
7374

7475
typedef enum {
@@ -1121,6 +1122,7 @@ mediatest_ctx_t * mediatest_configure(int media_test_id, mediatest_spec_t * spe
11211122
picoquic_tp_t client_parameters;
11221123
mediatest_init_transport_parameters(&client_parameters, 1);
11231124
picoquic_set_transport_parameters(cnx, &client_parameters);
1125+
picoquic_set_feedback_loss_notification(cnx, 1);
11241126

11251127
if (picoquic_start_client_cnx(cnx) != 0) {
11261128
picoquic_delete_cnx(cnx);
@@ -1197,6 +1199,7 @@ int mediatest_one(mediatest_id_enum media_test_id, mediatest_spec_t * spec)
11971199
if (mt_ctx == NULL) {
11981200
ret = -1;
11991201
}
1202+
12001203
/* Three special cases in which we manipulate the configuration
12011204
* to simulate various downgrade or suspension patterns.
12021205
*/
@@ -1210,15 +1213,14 @@ int mediatest_one(mediatest_id_enum media_test_id, mediatest_spec_t * spec)
12101213
ret = mediatest_loop(mt_ctx, 2000000, 1, &is_finished);
12111214
}
12121215
}
1213-
1214-
if (media_test_id == mediatest_video2_down ||
1216+
else if (media_test_id == mediatest_video2_down ||
12151217
media_test_id == mediatest_video2_back) {
12161218
uint64_t picosec_per_byte_ref[2];
12171219
uint64_t latency_ref[2];
12181220
uint64_t down_time = (media_test_id == mediatest_video2_down) ? 4000000 : 2000000;
12191221
uint64_t back_time = (media_test_id == mediatest_video2_down) ? 24000000 : 4000000;
12201222

1221-
/* Run the simulation for 2 second. */
1223+
/* Run the simulation for the first period. */
12221224
ret = mediatest_loop(mt_ctx, down_time, 0, &is_finished);
12231225
/* Drop the bandwidth and increase latency for specified down time */
12241226
for (int i = 0; i < 2; i++) {
@@ -1235,8 +1237,19 @@ int mediatest_one(mediatest_id_enum media_test_id, mediatest_spec_t * spec)
12351237
mt_ctx->link[i]->microsec_latency = latency_ref[i];
12361238
}
12371239
}
1238-
1239-
if (media_test_id == mediatest_wifi) {
1240+
else if (media_test_id == mediatest_suspension) {
1241+
/* Simulate one single wifi suspension, for 150 ms at
1242+
* t = 4000000 */
1243+
uint64_t sim_time = 4000000;
1244+
/* Run the simulation for the first period. */
1245+
ret = mediatest_loop(mt_ctx, sim_time, 0, &is_finished);
1246+
/* Simulate the suspension for 150 ms */
1247+
sim_time += 150000;
1248+
for (int i = 0; i < 2; i++) {
1249+
picoquic_test_simlink_suspend(mt_ctx->link[i], sim_time, 0);
1250+
}
1251+
}
1252+
else if (media_test_id == mediatest_wifi) {
12401253
/* For 10 seconds, run a test loop in which the simulated Wi-Fi
12411254
* link alternates between 100Mbps, low delay and just blocked,
12421255
* 0 bandwidth. The duration of the block unblock phase varies.
@@ -1412,6 +1425,24 @@ int mediatest_worst_test()
14121425
}
14131426

14141427
int mediatest_wifi_test()
1428+
{
1429+
int ret;
1430+
mediatest_spec_t spec = { 0 };
1431+
spec.ccalgo = picoquic_bbr_algorithm;
1432+
spec.bandwidth = 0.1;
1433+
spec.do_video = 1;
1434+
spec.do_video2 = 1;
1435+
spec.do_audio = 1;
1436+
spec.data_size = 0;
1437+
spec.latency_average = 60000;
1438+
spec.latency_max = 350000;
1439+
spec.do_not_check_video2 = 1;
1440+
ret = mediatest_one(mediatest_wifi, &spec);
1441+
1442+
return ret;
1443+
}
1444+
1445+
int mediatest_suspension_test()
14151446
{
14161447
int ret;
14171448
mediatest_spec_t spec = { 0 };
@@ -1424,7 +1455,7 @@ int mediatest_wifi_test()
14241455
spec.latency_average = 50000;
14251456
spec.latency_max = 300000;
14261457
spec.do_not_check_video2 = 1;
1427-
ret = mediatest_one(mediatest_wifi, &spec);
1458+
ret = mediatest_one(mediatest_suspension, &spec);
14281459

14291460
return ret;
14301461
}

0 commit comments

Comments
 (0)