lsquic_pacer.c revision 6aba801d
1/* Copyright (c) 2017 - 2019 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_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 clock_granularity)
28{
29    memset(pacer, 0, sizeof(*pacer));
30    pacer->pa_burst_tokens = 10;
31    pacer->pa_cid = cid;
32    pacer->pa_clock_granularity = clock_granularity;
33}
34
35
36void
37pacer_cleanup (struct pacer *pacer)
38{
39#ifndef NDEBUG
40    LSQ_NOTICE("scheduled calls: %u", pacer->pa_stats.n_scheduled);
41#endif
42}
43
44
45void
46pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight,
47                            int in_recovery, tx_time_f tx_time, void *tx_ctx)
48{
49    lsquic_time_t delay, sched_time;
50    int app_limited, making_up;
51
52#ifndef NDEBUG
53    ++pacer->pa_stats.n_scheduled;
54#endif
55
56    if (n_in_flight == 0 && !in_recovery)
57    {
58        pacer->pa_burst_tokens = 10;
59        LSQ_DEBUG("%s: replenish tokens: %u", __func__, pacer->pa_burst_tokens);
60    }
61
62    if (pacer->pa_burst_tokens > 0)
63    {
64        --pacer->pa_burst_tokens;
65        pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
66        pacer->pa_next_sched = 0;
67        pacer->pa_last_delayed = 0;
68        LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens);
69        return;
70    }
71
72    sched_time = pacer->pa_now;
73    delay = tx_time(tx_ctx);
74    if (pacer->pa_flags & PA_LAST_SCHED_DELAYED)
75    {
76        pacer->pa_next_sched += delay;
77        app_limited = pacer->pa_last_delayed != 0
78            && pacer->pa_last_delayed + delay <= sched_time;
79        making_up = pacer->pa_next_sched <= sched_time;
80        LSQ_DEBUG("making up: %d; app limited; %d", making_up, app_limited);
81        if (making_up && !app_limited)
82            pacer->pa_last_delayed = sched_time;
83        else
84        {
85            pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
86            pacer->pa_last_delayed = 0;
87        }
88    }
89    else
90        pacer->pa_next_sched = MAX(pacer->pa_next_sched + delay,
91                                                    sched_time + delay);
92    LSQ_DEBUG("next_sched is set to %"PRIu64" usec from now",
93                                pacer->pa_next_sched - lsquic_time_now());
94}
95
96
97void
98pacer_loss_event (struct pacer *pacer)
99{
100    pacer->pa_burst_tokens = 0;
101    LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens);
102}
103
104
105int
106pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight)
107{
108    int can;
109
110    if (pacer->pa_burst_tokens > 0 || n_in_flight == 0)
111        can = 1;
112    else if (pacer->pa_next_sched > pacer->pa_now + pacer->pa_clock_granularity)
113    {
114        pacer->pa_flags |= PA_LAST_SCHED_DELAYED;
115        can = 0;
116    }
117    else
118        can = 1;
119
120    LSQ_DEBUG("%s: %d", __func__, can);
121    return can;
122}
123
124
125void
126pacer_tick (struct pacer *pacer, lsquic_time_t now)
127{
128    assert(now >= pacer->pa_now);
129    pacer->pa_now = now;
130}
131