Coverage Report

Created: 2025-07-18 06:42

/src/h2o/deps/quicly/lib/loss.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2017-2020 Fastly, Kazuho Oku
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to
6
 * deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
 * sell copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in
12
 * all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20
 * IN THE SOFTWARE.
21
 */
22
#include "quicly/loss.h"
23
24
quicly_error_t quicly_loss_init_sentmap_iter(quicly_loss_t *loss, quicly_sentmap_iter_t *iter, int64_t now, uint32_t max_ack_delay,
25
                                             int is_closing)
26
0
{
27
0
    quicly_sentmap_init_iter(&loss->sentmap, iter);
28
29
0
    int64_t retire_before = now - quicly_loss_get_sentmap_expiration_time(loss, max_ack_delay);
30
31
    /* Retire entries older than the time specified, unless the connection is alive and the number of packets in the sentmap is
32
     * below 32 packets. This exception (the threshold of 32) exists to be capable of recognizing excessively late-ACKs when under
33
     * heavy loss; in such case, 32 is more than enough, yet small enough that the memory footprint does not matter. */
34
0
    const quicly_sent_packet_t *sent;
35
0
    while ((sent = quicly_sentmap_get(iter))->sent_at <= retire_before) {
36
0
        if (!is_closing && loss->sentmap.num_packets < 32)
37
0
            break;
38
0
        if (sent->cc_bytes_in_flight != 0) {
39
            /* cannot retire packets with cc_bytes_in_flight, but we may find retirable ones later in the map */
40
0
            quicly_sentmap_skip(iter);
41
0
            continue;
42
0
        }
43
0
        quicly_error_t ret;
44
0
        if ((ret = quicly_sentmap_update(&loss->sentmap, iter, QUICLY_SENTMAP_EVENT_EXPIRED)) != 0)
45
0
            return ret;
46
0
    }
47
48
    /* rewind iter to the head of the sentmap, before returning it to the caller */
49
0
    quicly_sentmap_init_iter(&loss->sentmap, iter);
50
51
0
    return 0;
52
0
}
53
54
quicly_error_t quicly_loss_detect_loss(quicly_loss_t *loss, int64_t now, uint32_t max_ack_delay, int is_1rtt_only,
55
                                       quicly_loss_on_detect_cb on_loss_detected)
56
0
{
57
    /* This function ensures that the value returned in loss_time is when the next application timer should be set for loss
58
     * detection. if no timer is required, loss_time is set to INT64_MAX. */
59
60
0
    const uint32_t delay_until_lost = ((loss->rtt.latest > loss->rtt.smoothed ? loss->rtt.latest : loss->rtt.smoothed) *
61
0
                                           (1024 + loss->thresholds.time_based_percentile) +
62
0
                                       1023) /
63
0
                                      1024;
64
0
    quicly_sentmap_iter_t iter;
65
0
    const quicly_sent_packet_t *sent;
66
0
    quicly_error_t ret;
67
68
0
#define CHECK_TIME_THRESHOLD(sent) ((sent)->sent_at <= now - delay_until_lost)
69
0
#define CHECK_PACKET_THRESHOLD(sent)                                                                                               \
70
0
    (loss->thresholds.use_packet_based &&                                                                                          \
71
0
     (int64_t)(sent)->packet_number <= largest_acked_signed - QUICLY_LOSS_DEFAULT_PACKET_THRESHOLD)
72
73
0
    loss->loss_time = INT64_MAX;
74
75
0
    if ((ret = quicly_loss_init_sentmap_iter(loss, &iter, now, max_ack_delay, 0)) != 0)
76
0
        return ret;
77
78
    /* Mark packets as lost if they are smaller than the largest_acked and outside either time-threshold or packet-threshold
79
     * windows. Once marked as lost, cc_bytes_in_flight becomes zero. */
80
0
    while ((sent = quicly_sentmap_get(&iter))->packet_number != UINT64_MAX) {
81
0
        int64_t largest_acked_signed = loss->largest_acked_packet_plus1[sent->ack_epoch] - 1;
82
0
        if ((int64_t)sent->packet_number < largest_acked_signed && (CHECK_TIME_THRESHOLD(sent) || CHECK_PACKET_THRESHOLD(sent))) {
83
0
            if (sent->cc_bytes_in_flight != 0) {
84
0
                on_loss_detected(loss, sent, !CHECK_PACKET_THRESHOLD(sent));
85
0
                if ((ret = quicly_sentmap_update(&loss->sentmap, &iter, QUICLY_SENTMAP_EVENT_LOST)) != 0)
86
0
                    return ret;
87
0
            } else {
88
0
                quicly_sentmap_skip(&iter);
89
0
            }
90
0
        } else {
91
            /* When only one PN space is active, it is possible to stop looking for packets that have to be considered lost and
92
             * continue on to calculating the loss time. Otherwise, iterate through the entire sentmap. */
93
0
            if (is_1rtt_only)
94
0
                break;
95
0
            quicly_sentmap_skip(&iter);
96
0
        }
97
0
    }
98
99
0
#undef CHECK_TIME_THRESHOLD
100
0
#undef CHECK_PACKET_THRESHOLD
101
102
0
    if (!is_1rtt_only) {
103
0
        if ((ret = quicly_loss_init_sentmap_iter(loss, &iter, now, max_ack_delay, 0)) != 0)
104
0
            return ret;
105
0
        sent = quicly_sentmap_get(&iter);
106
0
    }
107
108
    /* schedule time-threshold alarm if there is a packet outstanding that is smaller than largest_acked */
109
0
    while (sent->sent_at != INT64_MAX && sent->packet_number + 1 < loss->largest_acked_packet_plus1[sent->ack_epoch]) {
110
0
        if (sent->cc_bytes_in_flight != 0) {
111
0
            assert(now < sent->sent_at + delay_until_lost);
112
0
            loss->loss_time = sent->sent_at + delay_until_lost;
113
0
            break;
114
0
        }
115
0
        quicly_sentmap_skip(&iter);
116
0
        sent = quicly_sentmap_get(&iter);
117
0
    }
118
119
0
    return 0;
120
0
}