/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 */ |