lsquic_pacer.c revision a5fa05f9
1/* Copyright (c) 2017 - 2020 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
128void
129lsquic_pacer_tick_in (struct pacer *pacer, lsquic_time_t now)
130{
131    assert(now >= pacer->pa_now);
132    pacer->pa_now = now;
133    if (pacer->pa_flags & PA_LAST_SCHED_DELAYED)
134        pacer->pa_flags |= PA_DELAYED_ON_TICK_IN;
135    pacer->pa_n_scheduled = 0;
136}
137
138
139void
140lsquic_pacer_tick_out (struct pacer *pacer)
141{
142    if ((pacer->pa_flags & PA_DELAYED_ON_TICK_IN)
143            && pacer->pa_n_scheduled == 0
144                && pacer->pa_now > pacer->pa_next_sched)
145    {
146        LSQ_DEBUG("tick passed without scheduled packets: reset delayed flag");
147        pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
148    }
149    pacer->pa_flags &= ~PA_DELAYED_ON_TICK_IN;
150}
151