1/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */ 2#include <assert.h> 3#include <inttypes.h> 4#include <stdint.h> 5#include <string.h> 6#ifdef WIN32 7#include <vc_compat.h> 8#endif 9 10#include "lsquic_types.h" 11#include "lsquic_int_types.h" 12#include "lsquic_pacer.h" 13#include "lsquic_packet_common.h" 14#include "lsquic_packet_gquic.h" 15#include "lsquic_packet_out.h" 16#include "lsquic_util.h" 17 18#define LSQUIC_LOGGER_MODULE LSQLM_PACER 19#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(pacer->pa_conn) 20#include "lsquic_logger.h" 21 22#ifndef MAX 23# define MAX(a, b) ((a) > (b) ? (a) : (b)) 24#endif 25 26 27void 28lsquic_pacer_init (struct pacer *pacer, const struct lsquic_conn *conn, 29 unsigned clock_granularity) 30{ 31 memset(pacer, 0, sizeof(*pacer)); 32 pacer->pa_burst_tokens = 10; 33 pacer->pa_conn = conn; 34 pacer->pa_clock_granularity = clock_granularity; 35} 36 37 38void 39lsquic_pacer_cleanup (struct pacer *pacer) 40{ 41#ifndef NDEBUG 42 LSQ_DEBUG("scheduled calls: %u", pacer->pa_stats.n_scheduled); 43#endif 44} 45 46 47void 48lsquic_pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight, 49 int in_recovery, tx_time_f tx_time, void *tx_ctx) 50{ 51 lsquic_time_t delay, sched_time; 52 int app_limited, making_up; 53 54#ifndef NDEBUG 55 ++pacer->pa_stats.n_scheduled; 56#endif 57 ++pacer->pa_n_scheduled; 58 59 if (n_in_flight == 0 && !in_recovery) 60 { 61 pacer->pa_burst_tokens = 10; 62 LSQ_DEBUG("%s: replenish tokens: %u", __func__, pacer->pa_burst_tokens); 63 } 64 65 if (pacer->pa_burst_tokens > 0) 66 { 67 --pacer->pa_burst_tokens; 68 pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED; 69 pacer->pa_next_sched = 0; 70 pacer->pa_last_delayed = 0; 71 LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens); 72 return; 73 } 74 75 sched_time = pacer->pa_now; 76 delay = tx_time(tx_ctx); 77 if (pacer->pa_flags & PA_LAST_SCHED_DELAYED) 78 { 79 pacer->pa_next_sched += delay; 80 app_limited = pacer->pa_last_delayed != 0 81 && pacer->pa_last_delayed + delay <= sched_time; 82 making_up = pacer->pa_next_sched <= sched_time; 83 LSQ_DEBUG("making up: %d; app limited; %d", making_up, app_limited); 84 if (making_up && !app_limited) 85 pacer->pa_last_delayed = sched_time; 86 else 87 { 88 pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED; 89 pacer->pa_last_delayed = 0; 90 } 91 } 92 else 93 pacer->pa_next_sched = MAX(pacer->pa_next_sched + delay, 94 sched_time + delay); 95 LSQ_DEBUG("next_sched is set to %"PRIu64" usec from now", 96 pacer->pa_next_sched - pacer->pa_now); 97} 98 99 100void 101lsquic_pacer_loss_event (struct pacer *pacer) 102{ 103 pacer->pa_burst_tokens = 0; 104 LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens); 105} 106 107 108int 109lsquic_pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight) 110{ 111 int can; 112 113 if (pacer->pa_burst_tokens > 0 || n_in_flight == 0) 114 can = 1; 115 else if (pacer->pa_next_sched > pacer->pa_now + pacer->pa_clock_granularity) 116 { 117 pacer->pa_flags |= PA_LAST_SCHED_DELAYED; 118 can = 0; 119 } 120 else 121 can = 1; 122 123 LSQ_DEBUG("%s: %d", __func__, can); 124 return can; 125} 126 127 128int 129lsquic_pacer_can_schedule_probe (const struct pacer *pacer, 130 unsigned n_in_flight, lsquic_time_t tx_time) 131{ 132 return pacer->pa_burst_tokens > 1 /* Double packet size, want two tokens */ 133 || n_in_flight == 0 134 || pacer->pa_next_sched > pacer->pa_now + tx_time / 2; 135} 136 137 138void 139lsquic_pacer_tick_in (struct pacer *pacer, lsquic_time_t now) 140{ 141 assert(now >= pacer->pa_now); 142 pacer->pa_now = now; 143 if (pacer->pa_flags & PA_LAST_SCHED_DELAYED) 144 pacer->pa_flags |= PA_DELAYED_ON_TICK_IN; 145 pacer->pa_n_scheduled = 0; 146} 147 148 149void 150lsquic_pacer_tick_out (struct pacer *pacer) 151{ 152 if ((pacer->pa_flags & PA_DELAYED_ON_TICK_IN) 153 && pacer->pa_n_scheduled == 0 154 && pacer->pa_now > pacer->pa_next_sched) 155 { 156 LSQ_DEBUG("tick passed without scheduled packets: reset delayed flag"); 157 pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED; 158 } 159 pacer->pa_flags &= ~PA_DELAYED_ON_TICK_IN; 160} 161