Coverage Report

Created: 2026-07-25 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/Fast-DDS/src/cpp/rtps/history/CacheChangePool.cpp
Line
Count
Source
1
// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
 * @file CacheChangePool.cpp
17
 *
18
 */
19
20
#include <fastdds/rtps/common/CacheChange.hpp>
21
#include <fastdds/dds/log/Log.hpp>
22
23
#include <rtps/history/CacheChangePool.h>
24
25
#include <mutex>
26
#include <cstring>
27
#include <cassert>
28
#include <limits>
29
30
namespace eprosima {
31
namespace fastdds {
32
namespace rtps {
33
34
CacheChangePool::~CacheChangePool()
35
0
{
36
0
    EPROSIMA_LOG_INFO(RTPS_UTILS, "ChangePool destructor");
37
38
    // Deletion process does not depend on the memory management policy
39
0
    for (CacheChange_t* cache : all_caches_)
40
0
    {
41
0
        destroy_change(cache);
42
0
    }
43
0
}
44
45
void CacheChangePool::init(
46
        const PoolConfig& config)
47
0
{
48
0
    memory_mode_ = config.memory_policy;
49
50
    // Common for all modes: Set the pool size and size limit
51
0
    uint32_t pool_size = config.initial_size;
52
0
    uint32_t max_pool_size = config.maximum_size;
53
54
0
    EPROSIMA_LOG_INFO(RTPS_UTILS, "Creating CacheChangePool of size: " << pool_size);
55
56
0
    current_pool_size_ = 0;
57
0
    if (max_pool_size > 0)
58
0
    {
59
0
        if (pool_size > max_pool_size)
60
0
        {
61
0
            max_pool_size_ = pool_size;
62
0
        }
63
0
        else
64
0
        {
65
0
            max_pool_size_ = max_pool_size;
66
0
        }
67
0
    }
68
0
    else
69
0
    {
70
0
        max_pool_size_ = (std::numeric_limits<uint32_t>::max)();
71
0
    }
72
73
0
    switch (memory_mode_)
74
0
    {
75
0
        case PREALLOCATED_MEMORY_MODE:
76
0
            EPROSIMA_LOG_INFO(RTPS_UTILS, "Static Mode is active, preallocating memory for pool_size elements");
77
0
            allocateGroup(pool_size ? pool_size : 1);
78
0
            break;
79
0
        case PREALLOCATED_WITH_REALLOC_MEMORY_MODE:
80
0
            EPROSIMA_LOG_INFO(RTPS_UTILS,
81
0
                    "Semi-Static Mode is active, preallocating memory for pool_size. Size of the cachechanges can be increased");
82
0
            allocateGroup(pool_size ? pool_size : 1);
83
0
            break;
84
0
        case DYNAMIC_RESERVE_MEMORY_MODE:
85
0
            EPROSIMA_LOG_INFO(RTPS_UTILS, "Dynamic Mode is active, CacheChanges are allocated on request");
86
0
            break;
87
0
        case DYNAMIC_REUSABLE_MEMORY_MODE:
88
0
            EPROSIMA_LOG_INFO(RTPS_UTILS,
89
0
                    "Semi-Dynamic Mode is active, no preallocation but dynamically allocated CacheChanges are reused for future cachechanges");
90
0
            break;
91
0
    }
92
0
}
93
94
void CacheChangePool::return_cache_to_pool(
95
        CacheChange_t* ch)
96
0
{
97
0
    ch->kind = ALIVE;
98
0
    ch->writerGUID = c_Guid_Unknown;
99
0
    ch->instanceHandle.clear();
100
0
    ch->sequenceNumber.high = 0;
101
0
    ch->sequenceNumber.low = 0;
102
0
    ch->inline_qos.pos = 0;
103
0
    ch->inline_qos.length = 0;
104
0
    ch->isRead = false;
105
0
    ch->sourceTimestamp.seconds(0);
106
0
    ch->sourceTimestamp.fraction(0);
107
0
    ch->writer_info.num_sent_submessages = 0;
108
0
    ch->write_params.sample_identity(SampleIdentity::unknown());
109
0
    ch->write_params.related_sample_identity(SampleIdentity::unknown());
110
0
    ch->setFragmentSize(0);
111
0
    ch->vendor_id = c_VendorId_Unknown;
112
0
    assert(free_caches_.end() == std::find(free_caches_.begin(), free_caches_.end(), ch));
113
0
    free_caches_.push_back(ch);
114
0
}
115
116
bool CacheChangePool::allocateGroup(
117
        uint32_t group_size)
118
0
{
119
    // This method should only called from within PREALLOCATED_MEMORY_MODE or PREALLOCATED_WITH_REALLOC_MEMORY_MODE
120
0
    assert(memory_mode_ == PREALLOCATED_MEMORY_MODE ||
121
0
            memory_mode_ == PREALLOCATED_WITH_REALLOC_MEMORY_MODE);
122
123
0
    EPROSIMA_LOG_INFO(RTPS_UTILS, "Allocating group of cache changes of size: " << group_size);
124
125
0
    uint32_t desired_size = current_pool_size_ + group_size;
126
0
    if (desired_size > max_pool_size_)
127
0
    {
128
0
        desired_size = max_pool_size_;
129
0
        group_size = max_pool_size_ - current_pool_size_;
130
0
    }
131
132
0
    if (group_size <= 0)
133
0
    {
134
0
        EPROSIMA_LOG_WARNING(RTPS_HISTORY, "Maximum number of allowed reserved caches reached");
135
0
        return false;
136
0
    }
137
138
0
    all_caches_.reserve(desired_size);
139
0
    free_caches_.reserve(free_caches_.size() + group_size);
140
141
0
    while (current_pool_size_ < desired_size)
142
0
    {
143
0
        CacheChange_t* ch = create_change();
144
0
        all_caches_.push_back(ch);
145
0
        free_caches_.push_back(ch);
146
0
        ++current_pool_size_;
147
0
    }
148
149
0
    return true;
150
0
}
151
152
CacheChange_t* CacheChangePool::allocateSingle()
153
0
{
154
    /*
155
     *   In Dynamic Memory Mode CacheChanges are only allocated when they are needed.
156
     *   This means when the buffer of the message receiver is copied into this struct, the size is allocated.
157
     *   When the change is released and comes back to the pool, it is deallocated correspondingly.
158
     *
159
     *   In Preallocated mode, changes are allocated with a static maximum size and then they are dealt as
160
     *   they are needed. In Dynamic mode, they are only allocated when they are needed. In Dynamic mode only
161
     *   the all_caches_ vector is used, in order to keep track of all the changes that are dealt for destruction
162
     *   purposes.
163
     *
164
     */
165
0
    bool added = false;
166
0
    CacheChange_t* ch = nullptr;
167
168
    // This method should only be called from within DYNAMIC_RESERVE_MEMORY_MODE or DYNAMIC_REUSABLE_MEMORY_MODE
169
0
    assert(memory_mode_ == DYNAMIC_RESERVE_MEMORY_MODE ||
170
0
            memory_mode_ == DYNAMIC_REUSABLE_MEMORY_MODE);
171
172
0
    if (current_pool_size_ < max_pool_size_)
173
0
    {
174
0
        ++current_pool_size_;
175
0
        ch = create_change();
176
0
        all_caches_.push_back(ch);
177
0
        added = true;
178
0
    }
179
180
0
    if (!added)
181
0
    {
182
0
        EPROSIMA_LOG_WARNING(RTPS_HISTORY, "Maximum number of allowed reserved caches reached");
183
0
        return nullptr;
184
0
    }
185
186
0
    return ch;
187
0
}
188
189
bool CacheChangePool::reserve_cache(
190
        CacheChange_t*& cache_change)
191
0
{
192
0
    cache_change = nullptr;
193
194
0
    if (free_caches_.empty())
195
0
    {
196
0
        switch (memory_mode_)
197
0
        {
198
0
            case PREALLOCATED_MEMORY_MODE:
199
0
            case PREALLOCATED_WITH_REALLOC_MEMORY_MODE:
200
0
                if (!allocateGroup((uint16_t)(ceil((float)current_pool_size_ / 10) + 10)))
201
0
                {
202
0
                    return false;
203
0
                }
204
0
                break;
205
206
0
            case DYNAMIC_RESERVE_MEMORY_MODE:
207
0
            case DYNAMIC_REUSABLE_MEMORY_MODE:
208
0
                cache_change = allocateSingle(); //Allocates a single, empty CacheChange
209
0
                return cache_change != nullptr;
210
211
0
            default:
212
0
                return false;
213
0
        }
214
0
    }
215
216
0
    cache_change = free_caches_.back();
217
0
    free_caches_.pop_back();
218
0
    return true;
219
0
}
220
221
bool CacheChangePool::release_cache(
222
        CacheChange_t* cache_change)
223
0
{
224
0
    switch (memory_mode_)
225
0
    {
226
0
        case PREALLOCATED_MEMORY_MODE:
227
0
        case PREALLOCATED_WITH_REALLOC_MEMORY_MODE:
228
0
        case DYNAMIC_REUSABLE_MEMORY_MODE:
229
0
            return_cache_to_pool(cache_change);
230
0
            break;
231
232
0
        case DYNAMIC_RESERVE_MEMORY_MODE:
233
            // Find pointer in CacheChange vector, remove element, then delete it
234
0
            std::vector<CacheChange_t*>::iterator target =
235
0
                    std::find(all_caches_.begin(), all_caches_.end(), cache_change);
236
0
            if (target != all_caches_.end())
237
0
            {
238
                // Copy last element into the element being removed
239
0
                if (target != --all_caches_.end())
240
0
                {
241
0
                    *target = std::move(all_caches_.back());
242
0
                }
243
244
                // Then drop last element
245
0
                all_caches_.pop_back();
246
0
            }
247
0
            else
248
0
            {
249
0
                EPROSIMA_LOG_INFO(RTPS_UTILS, "Tried to release a CacheChange that is not logged in the Pool");
250
0
                return false;
251
0
            }
252
253
0
            destroy_change(cache_change);
254
0
            --current_pool_size_;
255
0
            break;
256
0
    }
257
258
0
    return true;
259
0
}
260
261
} /* namespace rtps */
262
} /* namespace fastdds */
263
} /* namespace eprosima */