lsquic_pacer.c revision 50aadb33
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_packet_scheduled (struct pacer *pacer, unsigned n_in_flight, 46 int in_recovery, tx_time_f tx_time, void *tx_ctx) 47{ 48 lsquic_time_t delay, sched_time; 49 int app_limited, making_up; 50 51 if (n_in_flight == 0 && !in_recovery) 52 { 53 pacer->pa_burst_tokens = 10; 54 LSQ_DEBUG("%s: replenish tokens: %u", __func__, pacer->pa_burst_tokens); 55 } 56 57 if (pacer->pa_burst_tokens > 0) 58 { 59 --pacer->pa_burst_tokens; 60 pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED; 61 pacer->pa_next_sched = 0; 62 pacer->pa_last_delayed = 0; 63 LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens); 64 return; 65 } 66 67 sched_time = pacer->pa_now; 68 delay = tx_time(tx_ctx); 69 if (pacer->pa_flags & PA_LAST_SCHED_DELAYED) 70 { 71 pacer->pa_next_sched += delay; 72 app_limited = pacer->pa_last_delayed != 0 73 && pacer->pa_last_delayed + delay <= sched_time; 74 making_up = pacer->pa_next_sched <= sched_time; 75 LSQ_DEBUG("making up: %d; app limited; %d", making_up, app_limited); 76 if (making_up && !app_limited) 77 pacer->pa_last_delayed = sched_time; 78 else 79 { 80 pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED; 81 pacer->pa_last_delayed = 0; 82 } 83 } 84 else 85 pacer->pa_next_sched = MAX(pacer->pa_next_sched + delay, 86 sched_time + delay); 87 LSQ_DEBUG("next_sched is set to %"PRIu64" usec from now", 88 pacer->pa_next_sched - lsquic_time_now()); 89} 90 91 92void 93pacer_loss_event (struct pacer *pacer) 94{ 95 pacer->pa_burst_tokens = 0; 96 LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens); 97} 98 99 100static unsigned 101clock_granularity (const struct pacer *pacer) 102{ 103#ifndef NDEBUG 104 if (pacer->pa_flags & PA_CONSTANT_INTERTICK) 105 return pacer->pa_max_intertick; 106#endif 107 108 return pacer->pa_intertick_var; 109} 110 111 112int 113pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight) 114{ 115 int can; 116 117 if (pacer->pa_burst_tokens > 0 || n_in_flight == 0) 118 can = 1; 119 else if (pacer->pa_next_sched > pacer->pa_now + clock_granularity(pacer)) 120 { 121 pacer->pa_flags |= PA_LAST_SCHED_DELAYED; 122 can = 0; 123 } 124 else 125 can = 1; 126 127 LSQ_DEBUG("%s: %d", __func__, can); 128 return can; 129} 130 131 132#define ALPHA_SHIFT 3 133#define BETA_SHIFT 2 134 135static void 136update_avg_intertick (struct pacer *pacer, unsigned intertick) 137{ 138 unsigned diff; 139 140#ifndef NDEBUG 141 if (pacer->pa_flags & PA_CONSTANT_INTERTICK) 142 return; 143#endif 144 145 if (pacer->pa_intertick_avg) 146 { 147 if (intertick > pacer->pa_intertick_avg) 148 diff = intertick - pacer->pa_intertick_avg; 149 else 150 diff = pacer->pa_intertick_avg - intertick; 151 pacer->pa_intertick_var -= pacer->pa_intertick_var >> BETA_SHIFT; 152 pacer->pa_intertick_var += diff >> BETA_SHIFT; 153 pacer->pa_intertick_avg -= pacer->pa_intertick_avg >> ALPHA_SHIFT; 154 pacer->pa_intertick_avg += intertick >> ALPHA_SHIFT; 155 } 156 else 157 { 158 pacer->pa_intertick_avg = intertick; 159 pacer->pa_intertick_var = intertick >> 1; 160 } 161} 162 163 164void 165pacer_tick (struct pacer *pacer, lsquic_time_t now) 166{ 167 unsigned intertick; 168 169 assert(now > pacer->pa_now); 170 if (pacer->pa_now) 171 { 172 assert(now - pacer->pa_now < (1ULL << sizeof(unsigned) * 8)); 173 intertick = now - pacer->pa_now; 174 LSQ_DEBUG("intertick estimate: %u; real value: %u; error: %d", 175 clock_granularity(pacer), intertick, 176 (int) clock_granularity(pacer) - (int) intertick); 177 update_avg_intertick(pacer, intertick); 178 } 179 pacer->pa_now = now; 180} 181