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