Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync fork as of June 3rd 2024 #17

Merged
merged 17 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 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.19.12
VERSION 1.1.20.0
DESCRIPTION "picoquic library"
LANGUAGES C CXX)

Expand Down Expand Up @@ -84,6 +84,7 @@ set(PICOQUIC_LIBRARY_FILES
picoquic/logwriter.c
picoquic/loss_recovery.c
picoquic/newreno.c
picoquic/pacing.c
picoquic/packet.c
picoquic/performance_log.c
picoquic/picohash.c
Expand Down Expand Up @@ -157,6 +158,7 @@ set(PICOQUIC_TEST_LIBRARY_FILES
picoquictest/multipath_test.c
picoquictest/netperf_test.c
picoquictest/openssl_test.c
picoquictest/pacing_test.c
picoquictest/parseheadertest.c
picoquictest/picoquic_lb_test.c
picoquictest/pn2pn64test.c
Expand Down
7 changes: 7 additions & 0 deletions UnitTest1/unittest1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,13 @@ namespace UnitTest1
{
int ret = pacing_test();

Assert::AreEqual(ret, 0);
}

TEST_METHOD(pacing_repeat)
{
int ret = pacing_repeat_test();

Assert::AreEqual(ret, 0);
}

Expand Down
2 changes: 1 addition & 1 deletion picoquic/bbr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1922,7 +1922,7 @@ void BBRCheckStartupLongRtt(picoquic_bbr_state_t* bbr_state, picoquic_path_t* pa
}

if (picoquic_hystart_test(&bbr_state->rtt_filter, rs->rtt_sample,
path_x->pacing_packet_time_microsec, current_time, 0)) {
path_x->pacing.packet_time_microsec, current_time, 0)) {
BBRExitStartupLongRtt(bbr_state, path_x, current_time);
}
else if (rs->ecn_alpha > BBRExcessiveEcnCE) {
Expand Down
4 changes: 2 additions & 2 deletions picoquic/bbr1.c
Original file line number Diff line number Diff line change
Expand Up @@ -1161,7 +1161,7 @@ static void picoquic_bbr1_notify(

if (bbr1_state->state == picoquic_bbr1_alg_startup_long_rtt) {
if (picoquic_hystart_test(&bbr1_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
cnx->path[0]->pacing.packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
BBR1ExitStartupLongRtt(bbr1_state, path_x, current_time);
}
}
Expand All @@ -1188,7 +1188,7 @@ static void picoquic_bbr1_notify(
path_x->cwin = min_win;
}
else if (path_x->smoothed_rtt > PICOQUIC_TARGET_RENO_RTT) {
path_x->pacing_bandwidth_pause = 1;
path_x->pacing.bandwidth_pause = 1;
}

picoquic_update_pacing_data(cnx, path_x, 1);
Expand Down
8 changes: 4 additions & 4 deletions picoquic/cubic.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ static void picoquic_cubic_notify(
/* Using RTT increases as signal to get out of initial slow start */
if (cubic_state->ssthresh == UINT64_MAX &&
picoquic_hystart_test(&cubic_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
cnx->path[0]->pacing.packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
/* RTT increased too much, get out of slow start! */
if (cubic_state->rtt_filter.rtt_filtered_min > PICOQUIC_TARGET_RENO_RTT){
double correction;
Expand Down Expand Up @@ -518,7 +518,7 @@ static void picoquic_dcubic_notify(
* for getting out of slow start, but also for ending a cycle
* during congestion avoidance */
if (picoquic_hystart_test(&cubic_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
cnx->path[0]->pacing.packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
dcubic_exit_slow_start(cnx, path_x, notification, cubic_state, current_time);
}
break;
Expand Down Expand Up @@ -578,7 +578,7 @@ static void picoquic_dcubic_notify(
}

if (picoquic_hystart_test(&cubic_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
cnx->path[0]->pacing.packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
if (current_time - cubic_state->start_of_epoch > path_x->smoothed_rtt ||
cubic_state->recovery_sequence <= picoquic_cc_get_ack_number(cnx, path_x)) {
/* re-enter recovery if this is a new event */
Expand Down Expand Up @@ -643,7 +643,7 @@ static void picoquic_dcubic_notify(
break;
case picoquic_congestion_notification_rtt_measurement:
if (picoquic_hystart_test(&cubic_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
cnx->path[0]->pacing.packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
if (current_time - cubic_state->start_of_epoch > path_x->smoothed_rtt ||
cubic_state->recovery_sequence <= picoquic_cc_get_ack_number(cnx, path_x)) {
/* re-enter recovery */
Expand Down
2 changes: 1 addition & 1 deletion picoquic/logwriter.c
Original file line number Diff line number Diff line change
Expand Up @@ -1176,7 +1176,7 @@ void binlog_cc_dump(picoquic_cnx_t* cnx, uint64_t current_time)
bytewrite_vint(ps_msg, path->bandwidth_estimate);
bytewrite_vint(ps_msg, path->receive_rate_estimate);
bytewrite_vint(ps_msg, path->send_mtu);
bytewrite_vint(ps_msg, path->pacing_packet_time_microsec);
bytewrite_vint(ps_msg, path->pacing.packet_time_microsec);
if (cnx->is_simple_multipath_enabled || cnx->is_multipath_enabled) {
bytewrite_vint(ps_msg, path->nb_losses_found);
bytewrite_vint(ps_msg, path->nb_spurious);
Expand Down
2 changes: 1 addition & 1 deletion picoquic/newreno.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ static void picoquic_newreno_notify(
}

if (picoquic_hystart_test(&nr_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
cnx->path[0]->pacing_packet_time_microsec, current_time,
cnx->path[0]->pacing.packet_time_microsec, current_time,
cnx->is_time_stamp_enabled)) {
/* RTT increased too much, get out of slow start! */
nr_state->nrss.ssthresh = nr_state->nrss.cwin;
Expand Down
270 changes: 270 additions & 0 deletions picoquic/pacing.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
/*
* Author: Christian Huitema
* Copyright (c) 2017, Private Octopus, Inc.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "picoquic_internal.h"
#include <stdlib.h>
#include <string.h>


/* Initialize pacing state to high speed default */
void picoquic_pacing_init(picoquic_pacing_t* pacing, uint64_t current_time)
{
pacing->evaluation_time = current_time;
pacing->bucket_nanosec = 16;
pacing->bucket_max = 16;
pacing->packet_time_nanosec = 1;
pacing->packet_time_microsec = 1;
}

/* Update the leaky bucket used for pacing.
*/
static void picoquic_update_pacing_bucket(picoquic_pacing_t* pacing, uint64_t current_time)
{
if (pacing->bucket_nanosec < -pacing->packet_time_nanosec) {
pacing->bucket_nanosec = -pacing->packet_time_nanosec;
}

if (current_time > pacing->evaluation_time) {
pacing->bucket_nanosec += (current_time - pacing->evaluation_time) * 1000;
pacing->evaluation_time = current_time;
if (pacing->bucket_nanosec > pacing->bucket_max) {
pacing->bucket_nanosec = pacing->bucket_max;
}
}
}

/* Check whether pacing authorizes immediate transmission,
* no not send any state
*/
int picoquic_is_pacing_blocked(picoquic_pacing_t* pacing)
{
return (pacing->bucket_nanosec < pacing->packet_time_nanosec);
}

/*
* Check pacing to see whether the next transmission is authorized.
* If if is not, update the next wait time to reflect pacing.
*
* In packet train mode, the wait will last until the bucket is completely full, or
* if at least N packets are received.
*/
int picoquic_is_authorized_by_pacing(picoquic_pacing_t * pacing, uint64_t current_time, uint64_t * next_time,
unsigned int packet_train_mode, picoquic_quic_t * quic)
{
int ret = 1;

picoquic_update_pacing_bucket(pacing, current_time);

if (pacing->bucket_nanosec < pacing->packet_time_nanosec) {
uint64_t next_pacing_time;
int64_t bucket_required;

if (packet_train_mode || pacing->bandwidth_pause) {
bucket_required = pacing->bucket_max;

if (bucket_required > 10 * pacing->packet_time_nanosec) {
bucket_required = 10 * pacing->packet_time_nanosec;
}

bucket_required -= pacing->bucket_nanosec;
}
else {
bucket_required = pacing->packet_time_nanosec - pacing->bucket_nanosec;
}

next_pacing_time = current_time + 1 + bucket_required / 1000;
if (next_pacing_time < *next_time) {
pacing->bandwidth_pause = 0;
*next_time = next_pacing_time;
if (quic != NULL) {
SET_LAST_WAKE(quic, PICOQUIC_SENDER);
}
}
ret = 0;
}

return ret;
}

/* Report pacing updates if required
*/
static void picoquic_report_pacing_update(picoquic_pacing_t* pacing, picoquic_path_t* path_x)
{
picoquic_cnx_t* cnx = path_x->cnx;

if (cnx->is_pacing_update_requested && path_x == cnx->path[0] &&
cnx->callback_fn != NULL) {
if ((pacing->rate > cnx->pacing_rate_signalled &&
(pacing->rate - cnx->pacing_rate_signalled >= cnx->pacing_increase_threshold)) ||
(pacing->rate < cnx->pacing_rate_signalled &&
(cnx->pacing_rate_signalled - pacing->rate > cnx->pacing_decrease_threshold))){
(void)cnx->callback_fn(cnx, pacing->rate, NULL, 0, picoquic_callback_pacing_changed, cnx->callback_ctx, NULL);
cnx->pacing_rate_signalled = pacing->rate;
}
}
if (cnx->is_path_quality_update_requested &&
cnx->callback_fn != NULL) {
/* TODO: add a function "export path quality" */
/* TODO: remember previous signalled value for change tests */
if (path_x->smoothed_rtt < path_x->rtt_threshold_low ||
path_x->smoothed_rtt > path_x->rtt_threshold_high ||
pacing->rate < path_x->pacing_rate_threshold_low ||
pacing->rate > path_x->pacing_rate_threshold_high) {
(void)cnx->callback_fn(cnx, path_x->unique_path_id, NULL, 0, picoquic_callback_path_quality_changed, cnx->callback_ctx, path_x->app_path_ctx);
picoquic_refresh_path_quality_thresholds(path_x);
}
}
}

/* Reset the pacing data after recomputing the pacing rate
*/
void picoquic_update_pacing_parameters(picoquic_pacing_t * pacing, double pacing_rate, uint64_t quantum, size_t send_mtu, uint64_t smoothed_rtt,
picoquic_path_t * signalled_path)
{
double packet_time = (double)send_mtu / pacing_rate;
double quantum_time = (double)quantum / pacing_rate;
uint64_t rtt_nanosec = smoothed_rtt * 1000;

pacing->rate = (uint64_t)pacing_rate;

if (quantum > pacing->quantum_max) {
pacing->quantum_max = quantum;
}
if (pacing->rate > pacing->rate_max) {
pacing->rate_max = pacing->rate;
}

pacing->packet_time_nanosec = (uint64_t)(packet_time * 1000000000.0);

if (pacing->packet_time_nanosec <= 0) {
pacing->packet_time_nanosec = 1;
pacing->packet_time_microsec = 1;
}
else {
if ((uint64_t)pacing->packet_time_nanosec > rtt_nanosec) {
pacing->packet_time_nanosec = rtt_nanosec;
}
pacing->packet_time_microsec = (pacing->packet_time_nanosec + 999ull) / 1000;
}

pacing->bucket_max = (uint64_t)(quantum_time * 1000000000.0);
if (pacing->bucket_max <= 0) {
pacing->bucket_max = 16 * pacing->packet_time_nanosec;
}

if (pacing->bucket_nanosec > pacing->bucket_max) {
pacing->bucket_nanosec = pacing->bucket_max;
}

if (signalled_path != NULL) {
picoquic_report_pacing_update(pacing, signalled_path);
}
}

/*
* Reset the pacing data after CWIN is updated.
* The max bucket is set to contain at least 2 packets more than 1/8th of the congestion window.
*/

void picoquic_update_pacing_window(picoquic_pacing_t * pacing, int slow_start, uint64_t cwin, size_t send_mtu, uint64_t smoothed_rtt,
picoquic_path_t * signalled_path)
{
uint64_t rtt_nanosec = smoothed_rtt * 1000;

if ((cwin < ((uint64_t)send_mtu) * 8) || rtt_nanosec <= 1000) {
/* Small windows, should only relie on ACK clocking */
pacing->bucket_max = rtt_nanosec;
pacing->packet_time_nanosec = 1;
pacing->packet_time_microsec = 1;

if (pacing->bucket_nanosec > pacing->bucket_max) {
pacing->bucket_nanosec = pacing->bucket_max;
}
}
else {
double pacing_rate = ((double)cwin / (double)rtt_nanosec) * 1000000000.0;
uint64_t quantum = cwin / 4;

if (quantum < 2ull * send_mtu) {
quantum = 2ull * send_mtu;
}
else {
if (slow_start && smoothed_rtt > 4*PICOQUIC_MAX_BANDWIDTH_TIME_INTERVAL_MAX) {
const uint64_t quantum_min = 0x8000;
if (quantum < quantum_min){
quantum = quantum_min;
}
else {
uint64_t quantum2 = (uint64_t)((pacing_rate * PICOQUIC_MAX_BANDWIDTH_TIME_INTERVAL_MAX) / 1000000.0);
if (quantum2 > quantum) {
quantum = quantum2;
}
}
}
else if (quantum > 16ull * send_mtu) {
quantum = 16ull * send_mtu;
}

}

if (slow_start) {
pacing_rate *= 1.25;
}
picoquic_update_pacing_parameters(pacing, pacing_rate, quantum, send_mtu, smoothed_rtt, signalled_path);
}
}

/*
* Update the pacing data after sending a packet.
*/
void picoquic_update_pacing_data_after_send(picoquic_pacing_t * pacing, size_t length, size_t send_mtu, uint64_t current_time)
{
uint64_t packet_time_nanosec;

picoquic_update_pacing_bucket(pacing, current_time);
packet_time_nanosec = ((pacing->packet_time_nanosec * (uint64_t)length) + (send_mtu - 1)) / send_mtu;
pacing->bucket_nanosec -= packet_time_nanosec;
}

/* Interface functions for compatibility with old implementation */
void picoquic_update_pacing_after_send(picoquic_path_t* path_x, size_t length, uint64_t current_time)
{
picoquic_update_pacing_data_after_send(&path_x->pacing, length, path_x->send_mtu, current_time);
}

int picoquic_is_sending_authorized_by_pacing(picoquic_cnx_t* cnx, picoquic_path_t* path_x, uint64_t current_time, uint64_t* next_time)
{
return picoquic_is_authorized_by_pacing(&path_x->pacing, current_time, next_time, cnx->quic->packet_train_mode,
cnx->quic);
}

/* Reset pacing data if congestion algorithm computes it directly */
void picoquic_update_pacing_rate(picoquic_cnx_t* cnx, picoquic_path_t* path_x, double pacing_rate, uint64_t quantum)
{
picoquic_update_pacing_parameters(&path_x->pacing, pacing_rate,
quantum, path_x->send_mtu, path_x->smoothed_rtt, path_x);
}
/* Reset pacing if expressed as CWIN and RTT */
void picoquic_update_pacing_data(picoquic_cnx_t* cnx, picoquic_path_t* path_x, int slow_start)
{
picoquic_update_pacing_window(&path_x->pacing, slow_start, path_x->cwin, path_x->send_mtu, path_x->smoothed_rtt,
path_x);
}
Loading
Loading