µOS++ IIIe Reference 7.0.0
The third edition of µOS++, a POSIX inspired open source framework, written in C++
Loading...
Searching...
No Matches
first-fit-top.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the µOS++ distribution.
3 * (https://github.com/micro-os-plus)
4 * Copyright (c) 2016-2023 Liviu Ionescu. All rights reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software
7 * for any purpose is hereby granted, under the terms of the MIT license.
8 *
9 * If a copy of the license was not distributed with this file, it can
10 * be obtained from https://opensource.org/licenses/mit/.
11 */
12
13#if defined(OS_USE_OS_APP_CONFIG_H)
14#include <cmsis-plus/os-app-config.h>
15#endif
16
18#include <memory>
19
20// ----------------------------------------------------------------------------
21
22#if defined(__clang__)
23#pragma clang diagnostic ignored "-Wc++98-compat"
24#endif
25
26// ----------------------------------------------------------------------------
27
28namespace os
29{
30 namespace memory
31 {
32
33 // ========================================================================
34
36 {
37 trace::printf ("first_fit_top::%s() @%p %s\n", __func__, this, name ());
38 }
39
40 void
41 first_fit_top::internal_construct_ (void* addr, std::size_t bytes)
42 {
43 assert(bytes > chunk_minsize);
44
45 arena_addr_ = addr;
46 total_bytes_ = bytes;
47
48 // Align address for first chunk.
49 void* res;
50 // Possibly adjust the last two parameters.
51 res = std::align (chunk_align, chunk_minsize, arena_addr_, total_bytes_);
52 // std::align() will fail if it cannot fit the min chunk.
53 if (res == nullptr)
54 {
55 assert(res != nullptr);
56 }
57 assert((total_bytes_ % chunk_align) == 0);
58
60 }
61
62 void
64 {
65 // Fill it with the first chunk.
66 chunk_t* chunk = reinterpret_cast<chunk_t*> (arena_addr_);
67 // Entire arena is a big free chunk.
68 chunk->size = total_bytes_;
69 // Mark the end of the list with a null pointer.
70 chunk->next = nullptr;
71
72 allocated_bytes_ = 0;
73 max_allocated_bytes_ = 0;
74 free_bytes_ = total_bytes_;
75 allocated_chunks_ = 0;
76 free_chunks_ = 1;
77
78 // Remember first chunk as list head.
79 free_list_ = chunk;
80 }
81
82 void
84 {
85#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
86 trace::printf ("first_fit_top::%s() @%p %s\n", __func__, this, name ());
87#endif
88
89 internal_reset_ ();
90 }
91
92#pragma GCC diagnostic push
93// Needed because 'alignment' is used only in trace calls.
94#pragma GCC diagnostic ignored "-Wunused-parameter"
95
112 void*
113 first_fit_top::do_allocate (std::size_t bytes, std::size_t alignment)
114 {
115 std::size_t block_padding = calc_block_padding (alignment);
116 std::size_t alloc_size = rtos::memory::align_size (bytes, chunk_align);
117 alloc_size += block_padding;
118 alloc_size += chunk_offset;
119
120 std::size_t block_minchunk = calc_block_minchunk (block_padding);
121 alloc_size = os::rtos::memory::max (alloc_size, block_minchunk);
122
123 chunk_t* chunk;
124
125 while (true)
126 {
127 chunk_t* prev_chunk = free_list_;
128 chunk = prev_chunk;
129
130 while (chunk)
131 {
132 int rem = static_cast<int> (chunk->size - alloc_size);
133 if (rem >= 0)
134 {
135 if ((static_cast<std::size_t> (rem)) >= block_minchunk)
136 {
137 // Found a chunk that is much larger than required size
138 // (at least one more chunk is available);
139 // break it into two chunks and return the second one.
140
141 chunk->size = static_cast<std::size_t> (rem);
142#pragma GCC diagnostic push
143#pragma GCC diagnostic ignored "-Wcast-align"
144 chunk =
145 reinterpret_cast<chunk_t *> (reinterpret_cast<char *> (chunk)
146 + rem);
147#pragma GCC diagnostic pop
148 chunk->size = alloc_size;
149
150 // Splitting one chunk creates one more chunk.
151 ++free_chunks_;
152 }
153 else
154 {
155 // Found a chunk that is exactly the size or slightly
156 // larger than the requested size; return this chunk.
157
158 if (prev_chunk == chunk)
159 {
160 // This implies p==r==free_list, i.e. the list head.
161 // The next chunk becomes the first list element.
162 free_list_ = chunk->next;
163
164 // If this was the last chunk, the free list is empty.
165 }
166 else
167 {
168 // Normal case. Remove it from the free_list.
169 prev_chunk->next = chunk->next;
170 }
171 }
172 break;
173 }
174 prev_chunk = chunk;
175 chunk = chunk->next;
176 }
177
178 if (chunk != nullptr)
179 {
180 break;
181 }
182
183 if (out_of_memory_handler_ == nullptr)
184 {
185#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
186 trace::printf ("first_fit_top::%s(%u,%u)=0 @%p %s\n", __func__,
187 bytes, alignment, this, name ());
188#endif
189
190 return nullptr;
191 }
192
193#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
194 trace::printf ("first_fit_top::%s(%u,%u) @%p %s out of memory\n",
195 __func__, bytes, alignment, this, name ());
196#endif
197 out_of_memory_handler_ ();
198
199 // If the handler returned, assume it freed some memory
200 // and try again to allocate.
201 }
202
203 void* aligned_payload = internal_align_ (chunk, bytes, alignment);
204
205#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
206 trace::printf ("first_fit_top::%s(%u,%u)=%p,%u @%p %s\n", __func__, bytes,
207 alignment, aligned_payload, alloc_size, this, name ());
208#endif
209
210 return aligned_payload;
211 }
212
227 void
228 first_fit_top::do_deallocate (void* addr, std::size_t bytes,
229 std::size_t alignment) noexcept
230 {
231#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
232 trace::printf ("first_fit_top::%s(%p,%u,%u) @%p %s\n", __func__, addr,
233 bytes, alignment, this, name ());
234#endif
235
236 // The address must be inside the arena; no exceptions.
237 if ((addr < arena_addr_)
238 || (addr > (static_cast<char*> (arena_addr_) + total_bytes_)))
239 {
240 assert(false);
241 return;
242 }
243
244#pragma GCC diagnostic push
245#pragma GCC diagnostic ignored "-Wcast-align"
246 // Compute the chunk address from the user address.
247 chunk_t* chunk = reinterpret_cast<chunk_t *> (static_cast<char *> (addr)
248 - chunk_offset);
249
250 // If the block was aligned, the offset appears as size; adjust back.
251 if (static_cast<std::ptrdiff_t> (chunk->size) < 0)
252 {
253 chunk = reinterpret_cast<chunk_t *> (reinterpret_cast<char *> (chunk)
254 + static_cast<std::ptrdiff_t> (chunk->size));
255 }
256#pragma GCC diagnostic pop
257
258 if (bytes)
259 {
260 // If size is known, validate.
261 // (when called from free(), the size is not known).
262 if (bytes + chunk_offset > chunk->size)
263 {
264 assert(false);
265 return;
266 }
267 }
268
269 // Update statistics.
270 // What is subtracted from allocated is added to free.
271 internal_decrease_allocated_statistics (chunk->size);
272
273 // If the free list is empty, create it with the current chunk, alone.
274 if (free_list_ == nullptr)
275 {
276 // Mark the end of the list with a null pointer.
277 chunk->next = nullptr;
278
279 // The chunk becomes the first list element.
280 free_list_ = chunk;
281 assert(free_chunks_ == 1);
282
283 return;
284 }
285
286 // The free list exists; is the chunk before the list head?
287 if (chunk < free_list_)
288 {
289 // Is the chunk *right* before the list head?
290 if (reinterpret_cast<char *> (chunk) + chunk->size
291 == reinterpret_cast<char *> (free_list_))
292 {
293 // Coalesce chunk to the list head.
294 chunk->size += free_list_->size;
295 chunk->next = free_list_->next; // May be nullptr
296
297 // Coalescing means one less chunk.
298 --free_chunks_;
299 }
300 else
301 {
302 // Insert before the list head.
303 chunk->next = free_list_;
304 }
305 // The chunk becomes the new list head.
306 free_list_ = chunk;
307
308 return;
309 }
310
311 // Walk the free list to find the place to insert,
312 // (the list must be ordered by addresses).
313 // Warning: not deterministic!
314
315 chunk_t* next_chunk = free_list_;
316 chunk_t* prev_chunk;
317 do
318 {
319 prev_chunk = next_chunk;
320 next_chunk = next_chunk->next;
321 }
322 while (next_chunk != nullptr && next_chunk <= chunk);
323
324 // Now prev_chunk <= chunk and either next_chunk == nullptr or
325 // next_chunk > chunk.
326 // Try to merge with chunks immediately before/after it.
327
328 if (reinterpret_cast<char *> (prev_chunk) + prev_chunk->size
329 == reinterpret_cast<char *> (chunk))
330 {
331 // Chunk to be freed is adjacent to a free chunk before it.
332 prev_chunk->size += chunk->size;
333
334 // Coalescing means one less chunk.
335 --free_chunks_;
336
337 // If the merged chunk is also adjacent to the chunk after it,
338 // merge again. Does not match if next_chunk == nullptr.
339 if (reinterpret_cast<char *> (prev_chunk) + prev_chunk->size
340 == reinterpret_cast<char *> (next_chunk))
341 {
342 prev_chunk->size += next_chunk->size;
343 prev_chunk->next = next_chunk->next;
344
345 // Coalescing means one less chunk.
346 --free_chunks_;
347 }
348 }
349 else if (reinterpret_cast<char *> (prev_chunk) + prev_chunk->size
350 > reinterpret_cast<char *> (chunk))
351 {
352 // Already freed.
353
354 // Revert statistics.
355 // What is subtracted from free is added to allocated.
356 allocated_bytes_ += chunk->size;
357 free_bytes_ -= chunk->size;
358 ++allocated_chunks_;
359 --free_chunks_;
360
361 trace::printf ("first_fit_top::%s(%p,%u,%u) @%p %s already freed\n",
362 __func__, addr, bytes, alignment, this, name ());
363
364 return;
365 }
366 // Does not match if next_chunk == nullptr.
367 else if (reinterpret_cast<char *> (chunk) + chunk->size
368 == reinterpret_cast<char *> (next_chunk))
369 {
370 // The chunk to be freed is adjacent to a free chunk after it.
371 chunk->size += next_chunk->size;
372 chunk->next = next_chunk->next; // May be nullptr.
373 prev_chunk->next = chunk;
374
375 // Coalescing means one less chunk.
376 --free_chunks_;
377 }
378 else
379 {
380 // Not adjacent to any chunk. Just insert it.
381 // The result is a new fragment. Not great...
382 chunk->next = next_chunk; // May be nullptr.
383 prev_chunk->next = chunk;
384 }
385 }
386
387 std::size_t
388 first_fit_top::do_max_size (void) const noexcept
389 {
390 return total_bytes_;
391 }
392
393 void*
394 first_fit_top::internal_align_ (chunk_t* chunk, std::size_t bytes,
395 std::size_t alignment)
396 {
397 // Update statistics.
398 // The value subtracted from free is added to allocated.
400
401 // Compute pointer to payload area.
402 char* payload = reinterpret_cast<char *> (chunk) + chunk_offset;
403
404 // Align it to user provided alignment.
405 void* aligned_payload = payload;
406 std::size_t aligned_size = chunk->size - chunk_offset;
407
408 void* res;
409 res = std::align (alignment, bytes, aligned_payload, aligned_size);
410 if (res == nullptr)
411 {
412 assert(res != nullptr);
413 }
414
415 // Compute the possible alignment offset.
416 std::ptrdiff_t offset = static_cast<char *> (aligned_payload) - payload;
417 if (offset)
418 {
419 // If non-zero, store it in the gap left by alignment in the
420 // chunk header.
421
422#pragma GCC diagnostic push
423#pragma GCC diagnostic ignored "-Wcast-align"
424 chunk_t* adj_chunk =
425 reinterpret_cast<chunk_t *> (static_cast<char *> (aligned_payload)
426 - chunk_offset);
427 adj_chunk->size = static_cast<std::size_t> (-offset);
428#pragma GCC diagnostic pop
429 }
430
431 assert(
432 (reinterpret_cast<uintptr_t> (aligned_payload) & (alignment - 1))
433 == 0);
434
435 return aligned_payload;
436 }
437
438#pragma GCC diagnostic pop
439
440 // --------------------------------------------------------------------------
441 } /* namespace memory */
442} /* namespace os */
443
444// ----------------------------------------------------------------------------
virtual ~first_fit_top() override
Destruct the memory resource object instance.
virtual void do_reset(void) noexcept override
Implementation of the function to reset the memory manager.
void * internal_align_(chunk_t *chunk, std::size_t bytes, std::size_t alignment)
Internal function to align a chunk.
virtual void do_deallocate(void *addr, std::size_t bytes, std::size_t alignment) noexcept override
Implementation of the memory deallocator.
void internal_reset_(void) noexcept
Internal function to reset the memory resource.
virtual void * do_allocate(std::size_t bytes, std::size_t alignment) override
Implementation of the memory allocator.
virtual std::size_t do_max_size(void) const noexcept override
Implementation of the function to get max size.
void internal_construct_(void *addr, std::size_t bytes)
Internal function to construct the memory resource.
const char * name(void) const
Get object name.
Definition os-decls.h:759
void internal_increase_allocated_statistics(std::size_t bytes) noexcept
Update statistics after allocation.
int printf(const char *format,...)
Write a formatted string to the trace device.
Definition trace.cpp:60
constexpr std::size_t align_size(std::size_t size, std::size_t align) noexcept
Helper function to align size values.
Definition os-memory.h:86
constexpr std::size_t max(std::size_t a, std::size_t b)
Definition os-memory.h:74
System namespace.