Skip to content

Commit 825d603

Browse files
committed
Isolate pacing code in module
1 parent fe150dd commit 825d603

15 files changed

+359
-36
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ set(PICOQUIC_LIBRARY_FILES
8484
picoquic/logwriter.c
8585
picoquic/loss_recovery.c
8686
picoquic/newreno.c
87+
picoquic/pacing.c
8788
picoquic/packet.c
8889
picoquic/performance_log.c
8990
picoquic/picohash.c

picoquic/bbr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1922,7 +1922,7 @@ void BBRCheckStartupLongRtt(picoquic_bbr_state_t* bbr_state, picoquic_path_t* pa
19221922
}
19231923

19241924
if (picoquic_hystart_test(&bbr_state->rtt_filter, rs->rtt_sample,
1925-
path_x->pacing_packet_time_microsec, current_time, 0)) {
1925+
path_x->pacing.packet_time_microsec, current_time, 0)) {
19261926
BBRExitStartupLongRtt(bbr_state, path_x, current_time);
19271927
}
19281928
else if (rs->ecn_alpha > BBRExcessiveEcnCE) {

picoquic/bbr1.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,7 +1161,7 @@ static void picoquic_bbr1_notify(
11611161

11621162
if (bbr1_state->state == picoquic_bbr1_alg_startup_long_rtt) {
11631163
if (picoquic_hystart_test(&bbr1_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
1164-
cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
1164+
cnx->path[0]->pacing.packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
11651165
BBR1ExitStartupLongRtt(bbr1_state, path_x, current_time);
11661166
}
11671167
}
@@ -1188,7 +1188,7 @@ static void picoquic_bbr1_notify(
11881188
path_x->cwin = min_win;
11891189
}
11901190
else if (path_x->smoothed_rtt > PICOQUIC_TARGET_RENO_RTT) {
1191-
path_x->pacing_bandwidth_pause = 1;
1191+
path_x->pacing.bandwidth_pause = 1;
11921192
}
11931193

11941194
picoquic_update_pacing_data(cnx, path_x, 1);

picoquic/cubic.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ static void picoquic_cubic_notify(
266266
/* Using RTT increases as signal to get out of initial slow start */
267267
if (cubic_state->ssthresh == UINT64_MAX &&
268268
picoquic_hystart_test(&cubic_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
269-
cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
269+
cnx->path[0]->pacing.packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
270270
/* RTT increased too much, get out of slow start! */
271271
if (cubic_state->rtt_filter.rtt_filtered_min > PICOQUIC_TARGET_RENO_RTT){
272272
double correction;
@@ -518,7 +518,7 @@ static void picoquic_dcubic_notify(
518518
* for getting out of slow start, but also for ending a cycle
519519
* during congestion avoidance */
520520
if (picoquic_hystart_test(&cubic_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
521-
cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
521+
cnx->path[0]->pacing.packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
522522
dcubic_exit_slow_start(cnx, path_x, notification, cubic_state, current_time);
523523
}
524524
break;
@@ -578,7 +578,7 @@ static void picoquic_dcubic_notify(
578578
}
579579

580580
if (picoquic_hystart_test(&cubic_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
581-
cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
581+
cnx->path[0]->pacing.packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
582582
if (current_time - cubic_state->start_of_epoch > path_x->smoothed_rtt ||
583583
cubic_state->recovery_sequence <= picoquic_cc_get_ack_number(cnx, path_x)) {
584584
/* re-enter recovery if this is a new event */
@@ -643,7 +643,7 @@ static void picoquic_dcubic_notify(
643643
break;
644644
case picoquic_congestion_notification_rtt_measurement:
645645
if (picoquic_hystart_test(&cubic_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
646-
cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
646+
cnx->path[0]->pacing.packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
647647
if (current_time - cubic_state->start_of_epoch > path_x->smoothed_rtt ||
648648
cubic_state->recovery_sequence <= picoquic_cc_get_ack_number(cnx, path_x)) {
649649
/* re-enter recovery */

picoquic/logwriter.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1176,7 +1176,7 @@ void binlog_cc_dump(picoquic_cnx_t* cnx, uint64_t current_time)
11761176
bytewrite_vint(ps_msg, path->bandwidth_estimate);
11771177
bytewrite_vint(ps_msg, path->receive_rate_estimate);
11781178
bytewrite_vint(ps_msg, path->send_mtu);
1179-
bytewrite_vint(ps_msg, path->pacing_packet_time_microsec);
1179+
bytewrite_vint(ps_msg, path->pacing.packet_time_microsec);
11801180
if (cnx->is_simple_multipath_enabled || cnx->is_multipath_enabled) {
11811181
bytewrite_vint(ps_msg, path->nb_losses_found);
11821182
bytewrite_vint(ps_msg, path->nb_spurious);

picoquic/newreno.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ static void picoquic_newreno_notify(
268268
}
269269

270270
if (picoquic_hystart_test(&nr_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
271-
cnx->path[0]->pacing_packet_time_microsec, current_time,
271+
cnx->path[0]->pacing.packet_time_microsec, current_time,
272272
cnx->is_time_stamp_enabled)) {
273273
/* RTT increased too much, get out of slow start! */
274274
nr_state->nrss.ssthresh = nr_state->nrss.cwin;

picoquic/pacing.c

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
/*
2+
* Author: Christian Huitema
3+
* Copyright (c) 2017, Private Octopus, Inc.
4+
* All rights reserved.
5+
*
6+
* Permission to use, copy, modify, and distribute this software for any
7+
* purpose with or without fee is hereby granted, provided that the above
8+
* copyright notice and this permission notice appear in all copies.
9+
*
10+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
11+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
12+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
13+
* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY
14+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
15+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
16+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
17+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
18+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
19+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20+
*/
21+
22+
#include "picoquic_internal.h"
23+
#include <stdlib.h>
24+
#include <string.h>
25+
26+
/* Compute nanosec per packet */
27+
static uint64_t picoquic_pacing_time_nanosec(picoquic_pacing_t* pacing, size_t length)
28+
{
29+
const uint64_t nanosec_per_sec = 1000000000ull;
30+
uint64_t packet_time_nanosec = 1;
31+
if (pacing->rate > 0) {
32+
packet_time_nanosec = (nanosec_per_sec * length + (pacing->rate - 1)) / pacing->rate;
33+
}
34+
35+
return packet_time_nanosec;
36+
}
37+
38+
/* Update the leaky bucket used for pacing.
39+
*/
40+
static void picoquic_update_pacing_bucket(picoquic_pacing_t* pacing, uint64_t current_time)
41+
{
42+
if (pacing->bucket_nanosec < -pacing->packet_time_nanosec) {
43+
pacing->bucket_nanosec = -pacing->packet_time_nanosec;
44+
}
45+
46+
if (current_time > pacing->evaluation_time) {
47+
pacing->bucket_nanosec += (current_time - pacing->evaluation_time) * 1000;
48+
pacing->evaluation_time = current_time;
49+
if (pacing->bucket_nanosec > pacing->bucket_max) {
50+
pacing->bucket_nanosec = pacing->bucket_max;
51+
}
52+
}
53+
}
54+
55+
/*
56+
* Check pacing to see whether the next transmission is authorized.
57+
* If if is not, update the next wait time to reflect pacing.
58+
*
59+
* In packet train mode, the wait will last until the bucket is completely full, or
60+
* if at least N packets are received.
61+
*/
62+
int picoquic_is_authorized_by_pacing(picoquic_pacing_t * pacing, uint64_t current_time, uint64_t * next_time,
63+
unsigned int packet_train_mode, picoquic_quic_t * quic)
64+
{
65+
int ret = 1;
66+
67+
picoquic_update_pacing_bucket(pacing, current_time);
68+
69+
if (pacing->bucket_nanosec < pacing->packet_time_nanosec) {
70+
uint64_t next_pacing_time;
71+
int64_t bucket_required;
72+
73+
if (packet_train_mode || pacing->bandwidth_pause) {
74+
bucket_required = pacing->bucket_max;
75+
76+
if (bucket_required > 10 * pacing->packet_time_nanosec) {
77+
bucket_required = 10 * pacing->packet_time_nanosec;
78+
}
79+
80+
bucket_required -= pacing->bucket_nanosec;
81+
}
82+
else {
83+
bucket_required = pacing->packet_time_nanosec - pacing->bucket_nanosec;
84+
}
85+
86+
next_pacing_time = current_time + 1 + bucket_required / 1000;
87+
if (next_pacing_time < *next_time) {
88+
pacing->bandwidth_pause = 0;
89+
*next_time = next_pacing_time;
90+
SET_LAST_WAKE(quic, PICOQUIC_SENDER);
91+
}
92+
ret = 0;
93+
}
94+
95+
return ret;
96+
}
97+
98+
/* Report pacing updates if required
99+
*/
100+
static void picoquic_report_pacing_update(picoquic_pacing_t* pacing, picoquic_path_t* path_x)
101+
{
102+
picoquic_cnx_t* cnx = path_x->cnx;
103+
104+
if (cnx->is_pacing_update_requested && path_x == cnx->path[0] &&
105+
cnx->callback_fn != NULL) {
106+
if ((pacing->rate > cnx->pacing_rate_signalled &&
107+
(pacing->rate - cnx->pacing_rate_signalled >= cnx->pacing_increase_threshold)) ||
108+
(pacing->rate < cnx->pacing_rate_signalled &&
109+
(cnx->pacing_rate_signalled - pacing->rate > cnx->pacing_decrease_threshold))){
110+
(void)cnx->callback_fn(cnx, pacing->rate, NULL, 0, picoquic_callback_pacing_changed, cnx->callback_ctx, NULL);
111+
cnx->pacing_rate_signalled = pacing->rate;
112+
}
113+
}
114+
if (cnx->is_path_quality_update_requested &&
115+
cnx->callback_fn != NULL) {
116+
/* TODO: add a function "export path quality" */
117+
/* TODO: remember previous signalled value for change tests */
118+
if (path_x->smoothed_rtt < path_x->rtt_threshold_low ||
119+
path_x->smoothed_rtt > path_x->rtt_threshold_high ||
120+
pacing->rate < path_x->pacing_rate_threshold_low ||
121+
pacing->rate > path_x->pacing_rate_threshold_high) {
122+
(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);
123+
picoquic_refresh_path_quality_thresholds(path_x);
124+
}
125+
}
126+
}
127+
128+
/* Reset the pacing data after recomputing the pacing rate
129+
*/
130+
void picoquic_update_pacing_parameters(picoquic_pacing_t * pacing, double pacing_rate, uint64_t quantum, size_t send_mtu, uint64_t smoothed_rtt,
131+
picoquic_path_t * signalled_path)
132+
{
133+
#if 0
134+
const uint64_t nanosec_per_sec = 1000000000ull;
135+
136+
pacing->rate = (uint64_t)pacing_rate;
137+
138+
if (quantum > pacing->quantum_max) {
139+
pacing->quantum_max = quantum;
140+
}
141+
if (pacing->rate > pacing->rate_max) {
142+
pacing->rate_max = pacing->rate;
143+
}
144+
145+
pacing->packet_time_nanosec = picoquic_packet_time_nanosec(pacing, send_mtu);
146+
147+
pacing->bucket_max = (nanosec_per_sec * quantum) / pacing->rate;
148+
if (pacing->bucket_max <= 0) {
149+
pacing->bucket_max = 16 * pacing->packet_time_nanosec;
150+
}
151+
152+
#else
153+
double packet_time = (double)send_mtu / pacing_rate;
154+
double quantum_time = (double)quantum / pacing_rate;
155+
uint64_t rtt_nanosec = smoothed_rtt * 1000;
156+
157+
pacing->rate = (uint64_t)pacing_rate;
158+
159+
if (quantum > pacing->quantum_max) {
160+
pacing->quantum_max = quantum;
161+
}
162+
if (pacing->rate > pacing->rate_max) {
163+
pacing->rate_max = pacing->rate;
164+
}
165+
166+
pacing->packet_time_nanosec = (uint64_t)(packet_time * 1000000000.0);
167+
168+
if (pacing->packet_time_nanosec <= 0) {
169+
pacing->packet_time_nanosec = 1;
170+
pacing->packet_time_microsec = 1;
171+
}
172+
else {
173+
if ((uint64_t)pacing->packet_time_nanosec > rtt_nanosec) {
174+
pacing->packet_time_nanosec = rtt_nanosec;
175+
}
176+
pacing->packet_time_microsec = (pacing->packet_time_nanosec + 999ull) / 1000;
177+
}
178+
179+
pacing->bucket_max = (uint64_t)(quantum_time * 1000000000.0);
180+
if (pacing->bucket_max <= 0) {
181+
pacing->bucket_max = 16 * pacing->packet_time_nanosec;
182+
}
183+
#endif
184+
185+
if (pacing->bucket_nanosec > pacing->bucket_max) {
186+
pacing->bucket_nanosec = pacing->bucket_max;
187+
}
188+
189+
if (signalled_path != NULL) {
190+
picoquic_report_pacing_update(pacing, signalled_path);
191+
}
192+
}
193+
194+
/*
195+
* Reset the pacing data after CWIN is updated.
196+
* The max bucket is set to contain at least 2 packets more than 1/8th of the congestion window.
197+
*/
198+
199+
void picoquic_update_pacing_window(picoquic_pacing_t * pacing, int slow_start, uint64_t cwin, size_t send_mtu, uint64_t smoothed_rtt,
200+
picoquic_path_t * signalled_path)
201+
{
202+
uint64_t rtt_nanosec = smoothed_rtt * 1000;
203+
204+
if ((cwin < ((uint64_t)send_mtu) * 8) || rtt_nanosec <= 1000) {
205+
/* Small windows, should only relie on ACK clocking */
206+
pacing->bucket_max = rtt_nanosec;
207+
pacing->packet_time_nanosec = 1;
208+
pacing->packet_time_microsec = 1;
209+
210+
if (pacing->bucket_nanosec > pacing->bucket_max) {
211+
pacing->bucket_nanosec = pacing->bucket_max;
212+
}
213+
}
214+
else {
215+
double pacing_rate = ((double)cwin / (double)rtt_nanosec) * 1000000000.0;
216+
uint64_t quantum = cwin / 4;
217+
218+
if (quantum < 2ull * send_mtu) {
219+
quantum = 2ull * send_mtu;
220+
}
221+
else {
222+
if (slow_start && smoothed_rtt > 4*PICOQUIC_MAX_BANDWIDTH_TIME_INTERVAL_MAX) {
223+
const uint64_t quantum_min = 0x8000;
224+
if (quantum < quantum_min){
225+
quantum = quantum_min;
226+
}
227+
else {
228+
uint64_t quantum2 = (uint64_t)((pacing_rate * PICOQUIC_MAX_BANDWIDTH_TIME_INTERVAL_MAX) / 1000000.0);
229+
if (quantum2 > quantum) {
230+
quantum = quantum2;
231+
}
232+
}
233+
}
234+
else if (quantum > 16ull * send_mtu) {
235+
quantum = 16ull * send_mtu;
236+
}
237+
238+
}
239+
240+
if (slow_start) {
241+
pacing_rate *= 1.25;
242+
}
243+
picoquic_update_pacing_parameters(pacing, pacing_rate, quantum, send_mtu, smoothed_rtt, signalled_path);
244+
}
245+
}
246+
247+
/*
248+
* Update the pacing data after sending a packet.
249+
*/
250+
void picoquic_update_pacing_data_after_send(picoquic_pacing_t * pacing, size_t length, size_t send_mtu, uint64_t current_time)
251+
{
252+
uint64_t packet_time_nanosec;
253+
254+
picoquic_update_pacing_bucket(pacing, current_time);
255+
packet_time_nanosec = ((pacing->packet_time_nanosec * (uint64_t)length) + (send_mtu - 1)) / send_mtu;
256+
pacing->bucket_nanosec -= packet_time_nanosec;
257+
}
258+
259+
/* Interface functions for compatibility with old implementation */
260+
void picoquic_update_pacing_after_send(picoquic_path_t* path_x, size_t length, uint64_t current_time)
261+
{
262+
picoquic_update_pacing_data_after_send(&path_x->pacing, length, path_x->send_mtu, current_time);
263+
}
264+
265+
int picoquic_is_sending_authorized_by_pacing(picoquic_cnx_t* cnx, picoquic_path_t* path_x, uint64_t current_time, uint64_t* next_time)
266+
{
267+
return picoquic_is_authorized_by_pacing(&path_x->pacing, current_time, next_time, cnx->quic->packet_train_mode,
268+
cnx->quic);
269+
}
270+
271+
/* Reset pacing data if congestion algorithm computes it directly */
272+
void picoquic_update_pacing_rate(picoquic_cnx_t* cnx, picoquic_path_t* path_x, double pacing_rate, uint64_t quantum)
273+
{
274+
picoquic_update_pacing_parameters(&path_x->pacing, pacing_rate,
275+
quantum, path_x->send_mtu, path_x->smoothed_rtt, path_x);
276+
}
277+
/* Reset pacing if expressed as CWIN and RTT */
278+
void picoquic_update_pacing_data(picoquic_cnx_t* cnx, picoquic_path_t* path_x, int slow_start)
279+
{
280+
picoquic_update_pacing_window(&path_x->pacing, slow_start, path_x->cwin, path_x->send_mtu, path_x->smoothed_rtt,
281+
path_x);
282+
}

picoquic/performance_log.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ int picoquic_perflog_record(picoquic_cnx_t* cnx, picoquic_performance_log_ctx_t*
183183
perflog_item->v[picoquic_perflog_minrtt] = cnx->path[0]->rtt_min;
184184
perflog_item->v[picoquic_perflog_cwin] = cnx->path[0]->cwin;
185185
perflog_item->v[picoquic_perflog_bwe_max] = cnx->path[0]->bandwidth_estimate_max;
186-
perflog_item->v[picoquic_perflog_pacing_quantum_max] = cnx->path[0]->pacing_quantum_max;
187-
perflog_item->v[picoquic_perflog_pacing_rate] = cnx->path[0]->pacing_rate_max;
186+
perflog_item->v[picoquic_perflog_pacing_quantum_max] = cnx->path[0]->pacing.quantum_max;
187+
perflog_item->v[picoquic_perflog_pacing_rate] = cnx->path[0]->pacing.rate_max;
188188
}
189189
if (cnx->congestion_alg != NULL) {
190190
perflog_item->v[picoquic_perflog_ccalgo] = cnx->congestion_alg->congestion_algorithm_number;

picoquic/picoquic.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@
157157
<ClCompile Include="logwriter.c" />
158158
<ClCompile Include="loss_recovery.c" />
159159
<ClCompile Include="newreno.c" />
160+
<ClCompile Include="pacing.c" />
160161
<ClCompile Include="performance_log.c" />
161162
<ClCompile Include="picoquic_lb.c" />
162163
<ClCompile Include="picoquic_mbedtls.c" />

0 commit comments

Comments
 (0)