µOS++ IIIe Reference 7.0.0
The third edition of µOS++, a POSIX inspired open source framework, written in C++
Loading...
Searching...
No Matches
os-mempool.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
16#include <cmsis-plus/rtos/os.h>
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 rtos
30 {
31
32 // ------------------------------------------------------------------------
33
120 const memory_pool::attributes memory_pool::initializer;
121
122 // ------------------------------------------------------------------------
123
179 // ------------------------------------------------------------------------
184 // Protected internal constructor.
186 {
187#if defined(OS_TRACE_RTOS_MEMPOOL)
188 trace::printf ("%s() @%p %s\n", __func__, this, this->name ());
189#endif
190 }
191
192 memory_pool::memory_pool (const char* name) : object_named_system{ name }
193 {
194#if defined(OS_TRACE_RTOS_MEMPOOL)
195 trace::printf ("%s() @%p %s\n", __func__, this, this->name ());
196#endif
197 }
198
231 memory_pool::memory_pool (std::size_t blocks, std::size_t block_size_bytes,
232 const attributes& attr,
233 const allocator_type& allocator)
234 : memory_pool{ nullptr, blocks, block_size_bytes, attr, allocator }
235 {
236 }
237
266 memory_pool::memory_pool (const char* name, std::size_t blocks,
267 std::size_t block_size_bytes,
268 const attributes& attr,
269 const allocator_type& allocator)
270 : object_named_system{ name }
271 {
272#if defined(OS_TRACE_RTOS_MEMPOOL)
273 trace::printf ("%s() @%p %s %u %u\n", __func__, this, this->name (),
274 blocks, block_size_bytes);
275#endif
276
277 if (attr.mp_pool_address != nullptr)
278 {
279 // Do not use any allocator at all.
280 internal_construct_ (blocks, block_size_bytes, attr, nullptr, 0);
281 }
282 else
283 {
284 allocator_ = &allocator;
285
286 // If no user storage was provided via attributes,
287 // allocate it dynamically via the allocator.
288 allocated_pool_size_elements_
290 typename allocator_type::value_type> (blocks,
291 block_size_bytes)
292 + sizeof (typename allocator_type::value_type) - 1)
293 / sizeof (typename allocator_type::value_type);
294
295 allocated_pool_addr_
296 = const_cast<allocator_type&> (allocator).allocate (
297 allocated_pool_size_elements_);
298
299 internal_construct_ (
300 blocks, block_size_bytes, attr, allocated_pool_addr_,
301 allocated_pool_size_elements_
302 * sizeof (typename allocator_type::value_type));
303 }
304 }
305
310 void
311 memory_pool::internal_construct_ (std::size_t blocks,
312 std::size_t block_size_bytes,
313 const attributes& attr,
314 void* pool_address,
315 std::size_t pool_size_bytes)
316 {
317 // Don't call this from interrupt handlers.
319
320#if !defined(OS_USE_RTOS_PORT_MEMORY_POOL)
321 clock_ = attr.clock != nullptr ? attr.clock : &sysclock;
322#endif
323
324 blocks_ = static_cast<memory_pool::size_t> (blocks);
325 assert (blocks_ == blocks);
326 assert (blocks_ > 0);
327
328 // Adjust block size to multiple of pointer.
329 // Blocks must be large enough to store a pointer, used
330 // to construct the list of free blocks.
331 block_size_bytes_
332 = (static_cast<memory_pool::size_t> (block_size_bytes
333 + __SIZEOF_POINTER__ - 1))
334 & (static_cast<memory_pool::size_t> (~(__SIZEOF_POINTER__ - 1)));
335
336 // If the storage is given explicitly, override attributes.
337 if (pool_address != nullptr)
338 {
339 // The attributes should not define any storage in this case.
340 assert (attr.mp_pool_address == nullptr);
341
342 pool_addr_ = pool_address;
343 pool_size_bytes_ = pool_size_bytes;
344 }
345 else
346 {
347 pool_addr_ = attr.mp_pool_address;
348 pool_size_bytes_ = attr.mp_pool_size_bytes;
349 }
350
351 // Blocks must be pointer aligned.
352 void* p = pool_addr_;
353 std::size_t sz = pool_size_bytes_;
354 pool_addr_ = static_cast<char*> (std::align (
355 __SIZEOF_POINTER__,
356 static_cast<std::size_t> (blocks_ * block_size_bytes_), p, sz));
357
358#if defined(OS_TRACE_RTOS_MEMPOOL)
359 trace::printf ("%s() @%p %s %u %u %p %u\n", __func__, this, name (),
360 blocks_, block_size_bytes_, pool_addr_, pool_size_bytes_);
361#endif
362
363 std::size_t storage_size
364 = compute_allocated_size_bytes<void*> (blocks_, block_size_bytes_);
365
366 if (pool_addr_ != nullptr)
367 {
368 // The pool must be real, and have a non zero size.
369 os_assert_throw (pool_size_bytes_ > 0, EINVAL);
370 // The pool must fit the storage.
371 os_assert_throw (pool_size_bytes_ >= storage_size, EINVAL);
372 }
373
374 // The pool must have a real address.
375 os_assert_throw (pool_addr_ != nullptr, ENOMEM);
376
377 internal_init_ ();
378 }
379
401 {
402#if defined(OS_TRACE_RTOS_MEMPOOL)
403 trace::printf ("%s() @%p %s\n", __func__, this, name ());
404#endif
405
406 // There must be no threads waiting for this pool.
407 assert (list_.empty ());
408
409 typedef typename std::allocator_traits<allocator_type>::pointer pointer;
410
411 if (allocated_pool_addr_ != nullptr)
412 {
413 static_cast<allocator_type*> (const_cast<void*> (allocator_))
414 ->deallocate (static_cast<pointer> (allocated_pool_addr_),
415 allocated_pool_size_elements_);
416 }
417 }
418
423 /*
424 * Construct the linked list of blocks and initialise the
425 * internal pointers and counters.
426 */
427 void
428 memory_pool::internal_init_ (void)
429 {
430 // Construct a linked list of blocks. Store the pointer at
431 // the beginning of each block. Each block
432 // will hold the address of the next free block, or nullptr at the end.
433#pragma GCC diagnostic push
434#if defined(__clang__)
435#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
436#endif
437 char* p = static_cast<char*> (pool_addr_);
438 for (std::size_t i = 1; i < blocks_; ++i)
439 {
440 // Compute the address of the next block;
441 char* pn = p + block_size_bytes_;
442
443 // Make this block point to the next one.
444 *(static_cast<void**> (static_cast<void*> (p))) = pn;
445 // Advance pointer
446 p = pn;
447 }
448#pragma GCC diagnostic pop
449
450 // Mark end of list.
451 *(static_cast<void**> (static_cast<void*> (p))) = nullptr;
452
453 first_ = pool_addr_; // Pointer to first block.
454
455 count_ = 0; // No allocated blocks.
456 }
457
458 /*
459 * Internal function used to return the first block in the
460 * free list.
461 * Should be called from an interrupts critical section.
462 */
463 void*
464 memory_pool::internal_try_first_ (void)
465 {
466 if (first_ != nullptr)
467 {
468 void* p = static_cast<void*> (first_);
469 first_ = *(static_cast<void**> (first_));
470
471#pragma GCC diagnostic push
472#if defined(__clang__)
473#pragma clang diagnostic ignored "-Wdeprecated-volatile"
474#elif defined(__GNUC__)
475#pragma GCC diagnostic ignored "-Wvolatile"
476#endif
477 ++count_;
478#pragma GCC diagnostic pop
479
480 return p;
481 }
482
483 return nullptr;
484 }
485
509 void*
511 {
512#if defined(OS_TRACE_RTOS_MEMPOOL)
513 trace::printf ("%s() @%p %s\n", __func__, this, name ());
514#endif
515
516 // Don't call this from interrupt handlers.
518 // Don't call this from critical regions.
520
521 void* p;
522
523 // Extra test before entering the loop, with its inherent weight.
524 // Trade size for speed.
525 {
526 // ----- Enter critical section ---------------------------------------
528
529 p = internal_try_first_ ();
530 if (p != nullptr)
531 {
532#if defined(OS_TRACE_RTOS_MEMPOOL)
533 trace::printf ("%s()=%p @%p %s\n", __func__, p, this, name ());
534#endif
535 return p;
536 }
537 // ----- Exit critical section ----------------------------------------
538 }
539
540 thread& crt_thread = this_thread::thread ();
541
542 // Prepare a list node pointing to the current thread.
543 // Do not worry for being on stack, it is temporarily linked to the
544 // list and guaranteed to be removed before this function returns.
545 internal::waiting_thread_node node{ crt_thread };
546
547 for (;;)
548 {
549 {
550 // ----- Enter critical section -----------------------------------
552
553 p = internal_try_first_ ();
554 if (p != nullptr)
555 {
556#if defined(OS_TRACE_RTOS_MEMPOOL)
557 trace::printf ("%s()=%p @%p %s\n", __func__, p, this, name ());
558#endif
559 return p;
560 }
561
562 // Add this thread to the memory pool waiting list.
563 scheduler::internal_link_node (list_, node);
564 // state::suspended set in above link().
565 // ----- Exit critical section ------------------------------------
566 }
567
569
570 // Remove the thread from the memory pool waiting list,
571 // if not already removed by free().
572 scheduler::internal_unlink_node (node);
573
574 if (this_thread::thread ().interrupted ())
575 {
576#if defined(OS_TRACE_RTOS_MEMPOOL)
577 trace::printf ("%s() INTR @%p %s\n", __func__, this, name ());
578#endif
579 return nullptr;
580 }
581 }
582
583 /* NOTREACHED */
584 }
585
603 void*
605 {
606#if defined(OS_TRACE_RTOS_MEMPOOL)
607 trace::printf ("%s() @%p %s\n", __func__, this, name ());
608#endif
609
610 // Don't call this from high priority interrupts.
611 assert (port::interrupts::is_priority_valid ());
612
613 void* p;
614 {
615 // ----- Enter critical section ---------------------------------------
617
618 p = internal_try_first_ ();
619 // ----- Exit critical section ----------------------------------------
620 }
621
622#if defined(OS_TRACE_RTOS_MEMPOOL)
623 trace::printf ("%s()=%p @%p %s\n", __func__, p, this, name ());
624#endif
625 return p;
626 }
627
668 void*
670 {
671#if defined(OS_TRACE_RTOS_MEMPOOL)
672#pragma GCC diagnostic push
673#if defined(__clang__)
674#elif defined(__GNUC__)
675#pragma GCC diagnostic ignored "-Wuseless-cast"
676#endif
677 trace::printf ("%s(%u) @%p %s\n", __func__,
678 static_cast<unsigned int> (timeout), this, name ());
679#pragma GCC diagnostic pop
680#endif
681
682 // Don't call this from interrupt handlers.
684 // Don't call this from critical regions.
686
687 void* p;
688
689 // Extra test before entering the loop, with its inherent weight.
690 // Trade size for speed.
691 {
692 // ----- Enter critical section ---------------------------------------
694
695 p = internal_try_first_ ();
696 if (p != nullptr)
697 {
698#if defined(OS_TRACE_RTOS_MEMPOOL)
699 trace::printf ("%s()=%p @%p %s\n", __func__, p, this, name ());
700#endif
701 return p;
702 }
703 // ----- Exit critical section ----------------------------------------
704 }
705
706 thread& crt_thread = this_thread::thread ();
707
708 // Prepare a list node pointing to the current thread.
709 // Do not worry for being on stack, it is temporarily linked to the
710 // list and guaranteed to be removed before this function returns.
711 internal::waiting_thread_node node{ crt_thread };
712
713 internal::clock_timestamps_list& clock_list = clock_->steady_list ();
714 clock::timestamp_t timeout_timestamp = clock_->steady_now () + timeout;
715
716 // Prepare a timeout node pointing to the current thread.
717 internal::timeout_thread_node timeout_node{ timeout_timestamp,
718 crt_thread };
719
720 for (;;)
721 {
722 {
723 // ----- Enter critical section -----------------------------------
725
726 p = internal_try_first_ ();
727 if (p != nullptr)
728 {
729#if defined(OS_TRACE_RTOS_MEMPOOL)
730 trace::printf ("%s()=%p @%p %s\n", __func__, p, this, name ());
731#endif
732 return p;
733 }
734
735 // Add this thread to the memory pool waiting list,
736 // and the clock timeout list.
737 scheduler::internal_link_node (list_, node, clock_list,
738 timeout_node);
739 // state::suspended set in above link().
740 // ----- Exit critical section ------------------------------------
741 }
742
744
745 // Remove the thread from the memory pool waiting list,
746 // if not already removed by free() and from the clock
747 // timeout list, if not already removed by the timer.
748 scheduler::internal_unlink_node (node, timeout_node);
749
750 if (this_thread::thread ().interrupted ())
751 {
752#if defined(OS_TRACE_RTOS_MEMPOOL)
753 trace::printf ("%s() INTR @%p %s\n", __func__, this, name ());
754#endif
755 return nullptr;
756 }
757
758 if (clock_->steady_now () >= timeout_timestamp)
759 {
760#if defined(OS_TRACE_RTOS_MEMPOOL)
761 trace::printf ("%s() TMO @%p %s\n", __func__, this, name ());
762#endif
763 return nullptr;
764 }
765 }
766
767 /* NOTREACHED */
768 }
769
781 memory_pool::free (void* block)
782 {
783#if defined(OS_TRACE_RTOS_MEMPOOL)
784 trace::printf ("%s(%p) @%p %s\n", __func__, block, this, name ());
785#endif
786
787 // Don't call this from high priority interrupts.
788 assert (port::interrupts::is_priority_valid ());
789
790 // Validate pointer.
791#pragma GCC diagnostic push
792#if defined(__clang__)
793#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
794#endif
795 if ((block < pool_addr_)
796 || (block >= (static_cast<char*> (pool_addr_)
797 + blocks_ * block_size_bytes_)))
798 {
799#if defined(OS_TRACE_RTOS_MEMPOOL)
800 trace::printf ("%s(%p) EINVAL @%p %s\n", __func__, block, this,
801 name ());
802#endif
803 return EINVAL;
804 }
805#pragma GCC diagnostic pop
806
807 {
808 // ----- Enter critical section ---------------------------------------
810
811 // Perform a push_front() on the single linked LIFO list,
812 // i.e. add the block to the beginning of the list.
813
814 // Link previous list to this block; may be null, but it does
815 // not matter.
816 *(static_cast<void**> (block)) = first_;
817
818 // Now this block is the first one.
819 first_ = block;
820
821#pragma GCC diagnostic push
822#if defined(__clang__)
823#pragma clang diagnostic ignored "-Wdeprecated-volatile"
824#elif defined(__GNUC__)
825#pragma GCC diagnostic ignored "-Wvolatile"
826#endif
827 --count_;
828#pragma GCC diagnostic pop
829
830 // ----- Exit critical section ----------------------------------------
831 }
832
833 // Wake-up one thread, if any.
834 list_.resume_one ();
835
836 return result::ok;
837 }
838
847 {
848#if defined(OS_TRACE_RTOS_MEMPOOL)
849 trace::printf ("%s() @%p %s\n", __func__, this, name ());
850#endif
851
852 // Don't call this from interrupt handlers.
854
855 {
856 // ----- Enter critical section ---------------------------------------
858
859 internal_init_ ();
860 // ----- Exit critical section ----------------------------------------
861 }
862
863 // Wake-up all threads, if any.
864 // Need not be inside the critical section,
865 // the list is protected by inner `resume_one()`.
866 list_.resume_all ();
867
868 return result::ok;
869 }
870
871 // ------------------------------------------------------------------------
872
873 } /* namespace rtos */
874} /* namespace os */
875
876// ----------------------------------------------------------------------------
Ordered list of time stamp nodes.
Definition os-lists.h:666
const char * name(void) const
Get object name.
Definition os-decls.h:753
Double linked list node, with time stamp and thread.
Definition os-lists.h:220
Double linked list node, with thread reference.
Definition os-lists.h:59
Interrupts critical section RAII helper.
Definition os-sched.h:502
Standard allocator based on the RTOS system default memory manager.
Definition os-memory.h:538
void deallocate(value_type *addr, std::size_t elements) noexcept
Deallocate the number of memory blocks of type value_type.
T value_type
Type of elements to be allocated.
Definition os-memory.h:543
Memory pool attributes.
Definition os-mempool.h:99
void * mp_pool_address
Address of the user defined storage for the memory pool.
Definition os-mempool.h:143
Synchronised memory pool, using the default RTOS allocator.
Definition os-mempool.h:67
memory_pool(std::size_t blocks, std::size_t block_size_bytes, const attributes &attr=initializer, const allocator_type &allocator=allocator_type())
Construct a memory pool object instance.
void * timed_alloc(clock::duration_t timeout)
Allocate a memory block with timeout.
void * try_alloc(void)
Try to allocate a memory block.
void * alloc(void)
Allocate a memory block.
result_t free(void *block)
Free the memory block.
result_t reset(void)
Reset the memory pool.
constexpr std::size_t compute_allocated_size_bytes(std::size_t blocks, std::size_t block_size_bytes)
Calculator for pool storage requirements.
Definition os-mempool.h:188
virtual ~memory_pool()
Destruct the memory pool object instance.
POSIX compliant thread, using the default RTOS allocator.
Definition os-thread.h:251
int printf(const char *format,...)
Write a formatted string to the trace device.
Definition trace.cpp:59
port::clock::duration_t duration_t
Type of variables holding clock durations.
Definition os-clocks.h:78
port::clock::timestamp_t timestamp_t
Type of variables holding clock time stamps.
Definition os-clocks.h:88
clock_systick sysclock
The system clock object instance.
uint16_t size_t
Type of memory pool size storage.
Definition os-mempool.h:79
static const attributes initializer
Default memory pool initialiser.
Definition os-mempool.h:162
bool in_handler_mode(void)
Check if the CPU is in handler mode.
Definition os-sched.h:1101
@ ok
Function completed; no errors or events occurred.
Definition os-decls.h:179
bool locked(void)
Check if the scheduler is locked.
Definition os-sched.h:858
thread & thread(void)
Get the current running thread.
uint32_t result_t
Type of values returned by RTOS functions.
Definition os-decls.h:95
System namespace.
#define os_assert_throw(__e, __er)
Assert or throw a system error exception.
Definition os-decls.h:1122
#define os_assert_err(__e, __er)
Assert or return an error.
Definition os-decls.h:1101
Single file µOS++ RTOS definitions.