161 lines
4.2 KiB
C
161 lines
4.2 KiB
C
/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
|
|
#include <assert.h>
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#ifdef WIN32
|
|
#include <vc_compat.h>
|
|
#endif
|
|
|
|
#include "lsquic_types.h"
|
|
#include "lsquic_int_types.h"
|
|
#include "lsquic_pacer.h"
|
|
#include "lsquic_packet_common.h"
|
|
#include "lsquic_packet_gquic.h"
|
|
#include "lsquic_packet_out.h"
|
|
#include "lsquic_util.h"
|
|
|
|
#define LSQUIC_LOGGER_MODULE LSQLM_PACER
|
|
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(pacer->pa_conn)
|
|
#include "lsquic_logger.h"
|
|
|
|
#ifndef MAX
|
|
# define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
#endif
|
|
|
|
|
|
void
|
|
lsquic_pacer_init (struct pacer *pacer, const struct lsquic_conn *conn,
|
|
unsigned clock_granularity)
|
|
{
|
|
memset(pacer, 0, sizeof(*pacer));
|
|
pacer->pa_burst_tokens = 10;
|
|
pacer->pa_conn = conn;
|
|
pacer->pa_clock_granularity = clock_granularity;
|
|
}
|
|
|
|
|
|
void
|
|
lsquic_pacer_cleanup (struct pacer *pacer)
|
|
{
|
|
#ifndef NDEBUG
|
|
LSQ_DEBUG("scheduled calls: %u", pacer->pa_stats.n_scheduled);
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
lsquic_pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight,
|
|
int in_recovery, tx_time_f tx_time, void *tx_ctx)
|
|
{
|
|
lsquic_time_t delay, sched_time;
|
|
int app_limited, making_up;
|
|
|
|
#ifndef NDEBUG
|
|
++pacer->pa_stats.n_scheduled;
|
|
#endif
|
|
++pacer->pa_n_scheduled;
|
|
|
|
if (n_in_flight == 0 && !in_recovery)
|
|
{
|
|
pacer->pa_burst_tokens = 10;
|
|
LSQ_DEBUG("%s: replenish tokens: %u", __func__, pacer->pa_burst_tokens);
|
|
}
|
|
|
|
if (pacer->pa_burst_tokens > 0)
|
|
{
|
|
--pacer->pa_burst_tokens;
|
|
pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
|
|
pacer->pa_next_sched = 0;
|
|
pacer->pa_last_delayed = 0;
|
|
LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens);
|
|
return;
|
|
}
|
|
|
|
sched_time = pacer->pa_now;
|
|
delay = tx_time(tx_ctx);
|
|
if (pacer->pa_flags & PA_LAST_SCHED_DELAYED)
|
|
{
|
|
pacer->pa_next_sched += delay;
|
|
app_limited = pacer->pa_last_delayed != 0
|
|
&& pacer->pa_last_delayed + delay <= sched_time;
|
|
making_up = pacer->pa_next_sched <= sched_time;
|
|
LSQ_DEBUG("making up: %d; app limited; %d", making_up, app_limited);
|
|
if (making_up && !app_limited)
|
|
pacer->pa_last_delayed = sched_time;
|
|
else
|
|
{
|
|
pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
|
|
pacer->pa_last_delayed = 0;
|
|
}
|
|
}
|
|
else
|
|
pacer->pa_next_sched = MAX(pacer->pa_next_sched + delay,
|
|
sched_time + delay);
|
|
LSQ_DEBUG("next_sched is set to %"PRIu64" usec from now",
|
|
pacer->pa_next_sched - pacer->pa_now);
|
|
}
|
|
|
|
|
|
void
|
|
lsquic_pacer_loss_event (struct pacer *pacer)
|
|
{
|
|
pacer->pa_burst_tokens = 0;
|
|
LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens);
|
|
}
|
|
|
|
|
|
int
|
|
lsquic_pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight)
|
|
{
|
|
int can;
|
|
|
|
if (pacer->pa_burst_tokens > 0 || n_in_flight == 0)
|
|
can = 1;
|
|
else if (pacer->pa_next_sched > pacer->pa_now + pacer->pa_clock_granularity)
|
|
{
|
|
pacer->pa_flags |= PA_LAST_SCHED_DELAYED;
|
|
can = 0;
|
|
}
|
|
else
|
|
can = 1;
|
|
|
|
LSQ_DEBUG("%s: %d", __func__, can);
|
|
return can;
|
|
}
|
|
|
|
|
|
int
|
|
lsquic_pacer_can_schedule_probe (const struct pacer *pacer,
|
|
unsigned n_in_flight, lsquic_time_t tx_time)
|
|
{
|
|
return pacer->pa_burst_tokens > 1 /* Double packet size, want two tokens */
|
|
|| n_in_flight == 0
|
|
|| pacer->pa_next_sched > pacer->pa_now + tx_time / 2;
|
|
}
|
|
|
|
|
|
void
|
|
lsquic_pacer_tick_in (struct pacer *pacer, lsquic_time_t now)
|
|
{
|
|
assert(now >= pacer->pa_now);
|
|
pacer->pa_now = now;
|
|
if (pacer->pa_flags & PA_LAST_SCHED_DELAYED)
|
|
pacer->pa_flags |= PA_DELAYED_ON_TICK_IN;
|
|
pacer->pa_n_scheduled = 0;
|
|
}
|
|
|
|
|
|
void
|
|
lsquic_pacer_tick_out (struct pacer *pacer)
|
|
{
|
|
if ((pacer->pa_flags & PA_DELAYED_ON_TICK_IN)
|
|
&& pacer->pa_n_scheduled == 0
|
|
&& pacer->pa_now > pacer->pa_next_sched)
|
|
{
|
|
LSQ_DEBUG("tick passed without scheduled packets: reset delayed flag");
|
|
pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
|
|
}
|
|
pacer->pa_flags &= ~PA_DELAYED_ON_TICK_IN;
|
|
}
|