lsquic_pacer.c revision bfc7bfd8
1/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */ 2#include <assert.h> 3#include <inttypes.h> 4#include <stdint.h> 5#ifndef NDEBUG 6#include <stdlib.h> /* getenv */ 7#endif 8#include <string.h> 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_out.h" 15#include "lsquic_util.h" 16 17#define LSQUIC_LOGGER_MODULE LSQLM_PACER 18#define LSQUIC_LOG_CONN_ID pacer->pa_cid 19#include "lsquic_logger.h" 20 21#ifndef MAX 22# define MAX(a, b) ((a) > (b) ? (a) : (b)) 23#endif 24 25 26void 27pacer_init (struct pacer *pacer, lsquic_cid_t cid, unsigned max_intertick) 28{ 29 memset(pacer, 0, sizeof(*pacer)); 30 pacer->pa_burst_tokens = 10; 31 pacer->pa_cid = cid; 32 pacer->pa_max_intertick = max_intertick; 33#ifndef NDEBUG 34 const char *val; 35 if ((val = getenv("LSQUIC_PACER_INTERTICK"))) 36 { 37 pacer->pa_flags |= PA_CONSTANT_INTERTICK; 38 pacer->pa_intertick_avg = atoi(val); 39 } 40#endif 41} 42 43 44void 45pacer_cleanup (struct pacer *pacer) 46{ 47#ifndef NDEBUG 48 LSQ_NOTICE("scheduled calls: %u", pacer->pa_stats.n_scheduled); 49#endif 50} 51 52 53void 54pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight, 55 int in_recovery, tx_time_f tx_time, void *tx_ctx) 56{ 57 lsquic_time_t delay, sched_time; 58 int app_limited, making_up; 59 60#ifndef NDEBUG 61 ++pacer->pa_stats.n_scheduled; 62#endif 63 64 if (n_in_flight == 0 && !in_recovery) 65 { 66 pacer->pa_burst_tokens = 10; 67 LSQ_DEBUG("%s: replenish tokens: %u", __func__, pacer->pa_burst_tokens); 68 } 69 70 if (pacer->pa_burst_tokens > 0) 71 { 72 --pacer->pa_burst_tokens; 73 pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED; 74 pacer->pa_next_sched = 0; 75 pacer->pa_last_delayed = 0; 76 LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens); 77 return; 78 } 79 80 sched_time = pacer->pa_now; 81 delay = tx_time(tx_ctx); 82 if (pacer->pa_flags & PA_LAST_SCHED_DELAYED) 83 { 84 pacer->pa_next_sched += delay; 85 app_limited = pacer->pa_last_delayed != 0 86 && pacer->pa_last_delayed + delay <= sched_time; 87 making_up = pacer->pa_next_sched <= sched_time; 88 LSQ_DEBUG("making up: %d; app limited; %d", making_up, app_limited); 89 if (making_up && !app_limited) 90 pacer->pa_last_delayed = sched_time; 91 else 92 { 93 pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED; 94 pacer->pa_last_delayed = 0; 95 } 96 } 97 else 98 pacer->pa_next_sched = MAX(pacer->pa_next_sched + delay, 99 sched_time + delay); 100 LSQ_DEBUG("next_sched is set to %"PRIu64" usec from now", 101 pacer->pa_next_sched - lsquic_time_now()); 102} 103 104 105void 106pacer_loss_event (struct pacer *pacer) 107{ 108 pacer->pa_burst_tokens = 0; 109 LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens); 110} 111 112 113static unsigned 114clock_granularity (const struct pacer *pacer) 115{ 116 return pacer->pa_intertick_avg; 117} 118 119 120int 121pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight) 122{ 123 int can; 124 125 if (pacer->pa_burst_tokens > 0 || n_in_flight == 0) 126 can = 1; 127 else if (pacer->pa_next_sched > pacer->pa_now + clock_granularity(pacer)) 128 { 129 pacer->pa_flags |= PA_LAST_SCHED_DELAYED; 130 can = 0; 131 } 132 else 133 can = 1; 134 135 LSQ_DEBUG("%s: %d", __func__, can); 136 return can; 137} 138 139 140#define ALPHA_SHIFT 3 141#define BETA_SHIFT 2 142 143static void 144update_avg_intertick (struct pacer *pacer, unsigned intertick) 145{ 146 unsigned diff; 147 148#ifndef NDEBUG 149 if (pacer->pa_flags & PA_CONSTANT_INTERTICK) 150 return; 151#endif 152 153 if (pacer->pa_intertick_avg) 154 { 155 if (intertick > pacer->pa_intertick_avg) 156 diff = intertick - pacer->pa_intertick_avg; 157 else 158 diff = pacer->pa_intertick_avg - intertick; 159 pacer->pa_intertick_var -= pacer->pa_intertick_var >> BETA_SHIFT; 160 pacer->pa_intertick_var += diff >> BETA_SHIFT; 161 pacer->pa_intertick_avg -= pacer->pa_intertick_avg >> ALPHA_SHIFT; 162 pacer->pa_intertick_avg += intertick >> ALPHA_SHIFT; 163 } 164 else 165 { 166 pacer->pa_intertick_avg = intertick; 167 pacer->pa_intertick_var = intertick >> 1; 168 } 169} 170 171 172void 173pacer_tick (struct pacer *pacer, lsquic_time_t now) 174{ 175 unsigned intertick; 176 177 assert(now > pacer->pa_now); 178 if (pacer->pa_now) 179 { 180 assert(now - pacer->pa_now < (1ULL << sizeof(unsigned) * 8)); 181 intertick = now - pacer->pa_now; 182 LSQ_DEBUG("intertick estimate: %u; real value: %u; error: %d", 183 clock_granularity(pacer), intertick, 184 (int) clock_granularity(pacer) - (int) intertick); 185 update_avg_intertick(pacer, intertick); 186 } 187 pacer->pa_now = now; 188} 189