µ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++ 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
17#include <cmsis-plus/rtos/os.h>
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 rtos
31 {
32
33 // ------------------------------------------------------------------------
34
117 const memory_pool::attributes memory_pool::initializer;
118
119 // ------------------------------------------------------------------------
120
175 // ------------------------------------------------------------------------
180 // Protected internal constructor.
182 {
183#if defined(OS_TRACE_RTOS_MEMPOOL)
184 trace::printf ("%s() @%p %s\n", __func__, this, this->name ());
185#endif
186 }
187
188 memory_pool::memory_pool (const char* name) :
189 object_named_system
190 { name }
191 {
192#if defined(OS_TRACE_RTOS_MEMPOOL)
193 trace::printf ("%s() @%p %s\n", __func__, this, this->name ());
194#endif
195 }
196
229 memory_pool::memory_pool (std::size_t blocks, std::size_t block_size_bytes,
230 const attributes& attr,
231 const allocator_type& allocator) :
233 { nullptr, blocks, block_size_bytes, attr, allocator }
234 {
235 }
236
265 memory_pool::memory_pool (const char* name, std::size_t blocks,
266 std::size_t block_size_bytes,
267 const attributes& attr,
268 const allocator_type& allocator) :
269 object_named_system
270 { 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_ = (compute_allocated_size_bytes<
289 typename allocator_type::value_type> (blocks, block_size_bytes)
290 + sizeof(typename allocator_type::value_type) - 1)
291 / sizeof(typename allocator_type::value_type);
292
293 allocated_pool_addr_ =
294 const_cast<allocator_type&> (allocator).allocate (
295 allocated_pool_size_elements_);
296
297 internal_construct_ (
298 blocks,
299 block_size_bytes,
300 attr,
301 allocated_pool_addr_,
302 allocated_pool_size_elements_
303 * sizeof(typename allocator_type::value_type));
304 }
305 }
306
311 void
312 memory_pool::internal_construct_ (std::size_t blocks,
313 std::size_t block_size_bytes,
314 const attributes& attr,
315 void* pool_address,
316 std::size_t pool_size_bytes)
317 {
318 // Don't call this from interrupt handlers.
320
321#if !defined(OS_USE_RTOS_PORT_MEMORY_POOL)
322 clock_ = attr.clock != nullptr ? attr.clock : &sysclock;
323#endif
324
325 blocks_ = static_cast<memory_pool::size_t> (blocks);
326 assert(blocks_ == blocks);
327 assert(blocks_ > 0);
328
329 // Adjust block size to multiple of pointer.
330 // Blocks must be large enough to store a pointer, used
331 // to construct the list of free blocks.
332 block_size_bytes_ = (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*> (
355 std::align (__SIZEOF_POINTER__,
356 static_cast<std::size_t>(blocks_ * block_size_bytes_),
357 p, sz)
358 );
359
360#if defined(OS_TRACE_RTOS_MEMPOOL)
361 trace::printf ("%s() @%p %s %u %u %p %u\n", __func__, this, name (),
362 blocks_, block_size_bytes_, pool_addr_, pool_size_bytes_);
363#endif
364
365 std::size_t storage_size = compute_allocated_size_bytes<void*> (
366 blocks_, block_size_bytes_);
367
368 if (pool_addr_ != nullptr)
369 {
370 // The pool must be real, and have a non zero size.
371 os_assert_throw(pool_size_bytes_ > 0, EINVAL);
372 // The pool must fit the storage.
373 os_assert_throw(pool_size_bytes_ >= storage_size, EINVAL);
374 }
375
376 // The pool must have a real address.
377 os_assert_throw(pool_addr_ != nullptr, ENOMEM);
378
379 internal_init_ ();
380
381 }
382
404 {
405#if defined(OS_TRACE_RTOS_MEMPOOL)
406 trace::printf ("%s() @%p %s\n", __func__, this, name ());
407#endif
408
409 // There must be no threads waiting for this pool.
410 assert(list_.empty ());
411
412 typedef typename std::allocator_traits<allocator_type>::pointer pointer;
413
414 if (allocated_pool_addr_ != nullptr)
415 {
416 static_cast<allocator_type*> (const_cast<void*> (allocator_))->deallocate (
417 static_cast<pointer> (allocated_pool_addr_),
418 allocated_pool_size_elements_);
419 }
420 }
421
426 /*
427 * Construct the linked list of blocks and initialise the
428 * internal pointers and counters.
429 */
430 void
431 memory_pool::internal_init_ (void)
432 {
433 // Construct a linked list of blocks. Store the pointer at
434 // the beginning of each block. Each block
435 // will hold the address of the next free block, or nullptr at the end.
436 char* p = static_cast<char*> (pool_addr_);
437 for (std::size_t i = 1; i < blocks_; ++i)
438 {
439 // Compute the address of the next block;
440 char* pn = p + block_size_bytes_;
441
442 // Make this block point to the next one.
443 *(static_cast<void**> (static_cast<void*> (p))) = pn;
444 // Advance pointer
445 p = pn;
446 }
447
448 // Mark end of list.
449 *(static_cast<void**> (static_cast<void*> (p))) = nullptr;
450
451 first_ = pool_addr_; // Pointer to first block.
452
453 count_ = 0; // No allocated blocks.
454 }
455
456 /*
457 * Internal function used to return the first block in the
458 * free list.
459 * Should be called from an interrupts critical section.
460 */
461 void*
462 memory_pool::internal_try_first_ (void)
463 {
464 if (first_ != nullptr)
465 {
466 void* p = static_cast<void*> (first_);
467 first_ = *(static_cast<void**> (first_));
468
469#pragma GCC diagnostic push
470#if defined(__clang__)
471#pragma clang diagnostic ignored "-Wdeprecated-volatile"
472#elif defined(__GNUC__)
473#pragma GCC diagnostic ignored "-Wvolatile"
474#endif
475 ++count_;
476#pragma GCC diagnostic pop
477
478 return p;
479 }
480
481 return nullptr;
482 }
483
507 void*
509 {
510#if defined(OS_TRACE_RTOS_MEMPOOL)
511 trace::printf ("%s() @%p %s\n", __func__, this, name ());
512#endif
513
514 // Don't call this from interrupt handlers.
516 // Don't call this from critical regions.
518
519 void* p;
520
521 // Extra test before entering the loop, with its inherent weight.
522 // Trade size for speed.
523 {
524 // ----- Enter critical section -------------------------------------
526
527 p = internal_try_first_ ();
528 if (p != nullptr)
529 {
530#if defined(OS_TRACE_RTOS_MEMPOOL)
531 trace::printf ("%s()=%p @%p %s\n", __func__, p, this, name ());
532#endif
533 return p;
534 }
535 // ----- Exit critical section --------------------------------------
536 }
537
538 thread& crt_thread = this_thread::thread ();
539
540 // Prepare a list node pointing to the current thread.
541 // Do not worry for being on stack, it is temporarily linked to the
542 // list and guaranteed to be removed before this function returns.
544 { crt_thread };
545
546 for (;;)
547 {
548 {
549 // ----- Enter critical section ---------------------------------
551
552 p = internal_try_first_ ();
553 if (p != nullptr)
554 {
555#if defined(OS_TRACE_RTOS_MEMPOOL)
556 trace::printf ("%s()=%p @%p %s\n", __func__, p, this,
557 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.
712 { crt_thread };
713
714 internal::clock_timestamps_list& clock_list = clock_->steady_list ();
715 clock::timestamp_t timeout_timestamp = clock_->steady_now () + timeout;
716
717 // Prepare a timeout node pointing to the current thread.
719 { timeout_timestamp, crt_thread };
720
721 for (;;)
722 {
723 {
724 // ----- Enter critical section ---------------------------------
726
727 p = internal_try_first_ ();
728 if (p != nullptr)
729 {
730#if defined(OS_TRACE_RTOS_MEMPOOL)
731 trace::printf ("%s()=%p @%p %s\n", __func__, p, this,
732 name ());
733#endif
734 return p;
735 }
736
737 // Add this thread to the memory pool waiting list,
738 // and the clock timeout list.
739 scheduler::internal_link_node (list_, node, clock_list,
740 timeout_node);
741 // state::suspended set in above link().
742 // ----- Exit critical section ----------------------------------
743 }
744
746
747 // Remove the thread from the memory pool waiting list,
748 // if not already removed by free() and from the clock
749 // timeout list, if not already removed by the timer.
750 scheduler::internal_unlink_node (node, timeout_node);
751
752 if (this_thread::thread ().interrupted ())
753 {
754#if defined(OS_TRACE_RTOS_MEMPOOL)
755 trace::printf ("%s() INTR @%p %s\n", __func__, this, name ());
756#endif
757 return nullptr;
758 }
759
760 if (clock_->steady_now () >= timeout_timestamp)
761 {
762#if defined(OS_TRACE_RTOS_MEMPOOL)
763 trace::printf ("%s() TMO @%p %s\n", __func__, this, name ());
764#endif
765 return nullptr;
766 }
767 }
768
769 /* NOTREACHED */
770 }
771
783 memory_pool::free (void* block)
784 {
785#if defined(OS_TRACE_RTOS_MEMPOOL)
786 trace::printf ("%s(%p) @%p %s\n", __func__, block, this, name ());
787#endif
788
789 // Don't call this from high priority interrupts.
790 assert(port::interrupts::is_priority_valid ());
791
792 // Validate pointer.
793 if ((block < pool_addr_)
794 || (block
795 >= (static_cast<char*> (pool_addr_) + blocks_ * block_size_bytes_)))
796 {
797#if defined(OS_TRACE_RTOS_MEMPOOL)
798 trace::printf ("%s(%p) EINVAL @%p %s\n", __func__, block, this,
799 name ());
800#endif
801 return EINVAL;
802 }
803
804 {
805 // ----- Enter critical section -------------------------------------
807
808 // Perform a push_front() on the single linked LIFO list,
809 // i.e. add the block to the beginning of the list.
810
811 // Link previous list to this block; may be null, but it does
812 // not matter.
813 *(static_cast<void**> (block)) = first_;
814
815 // Now this block is the first one.
816 first_ = block;
817
818#pragma GCC diagnostic push
819#if defined(__clang__)
820#pragma clang diagnostic ignored "-Wdeprecated-volatile"
821#elif defined(__GNUC__)
822#pragma GCC diagnostic ignored "-Wvolatile"
823#endif
824 --count_;
825#pragma GCC diagnostic pop
826
827 // ----- Exit critical section --------------------------------------
828 }
829
830 // Wake-up one thread, if any.
831 list_.resume_one ();
832
833 return result::ok;
834 }
835
844 {
845#if defined(OS_TRACE_RTOS_MEMPOOL)
846 trace::printf ("%s() @%p %s\n", __func__, this, name ());
847#endif
848
849 // Don't call this from interrupt handlers.
851
852 {
853 // ----- Enter critical section -------------------------------------
855
856 internal_init_ ();
857 // ----- Exit critical section --------------------------------------
858 }
859
860 // Wake-up all threads, if any.
861 // Need not be inside the critical section,
862 // the list is protected by inner `resume_one()`.
863 list_.resume_all ();
864
865 return result::ok;
866 }
867
868 // --------------------------------------------------------------------------
869
870 } /* namespace rtos */
871} /* namespace os */
872
873// ----------------------------------------------------------------------------
Ordered list of time stamp nodes.
Definition os-lists.h:671
const char * name(void) const
Get object name.
Definition os-decls.h:759
Double linked list node, with time stamp and thread.
Definition os-lists.h:223
Double linked list node, with thread reference.
Definition os-lists.h:60
Interrupts critical section RAII helper.
Definition os-sched.h:498
Standard allocator based on the RTOS system default memory manager.
Definition os-memory.h:540
void deallocate(value_type *addr, std::size_t elements) noexcept
Deallocate the number of memory blocks of type value_type.
Memory pool attributes.
Definition os-mempool.h:99
Synchronised memory pool, using the default RTOS allocator.
Definition os-mempool.h:68
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.
virtual ~memory_pool()
Destruct the memory pool object instance.
POSIX compliant thread, using the default RTOS allocator.
Definition os-thread.h:250
int printf(const char *format,...)
Write a formatted string to the trace device.
Definition trace.cpp:60
port::clock::duration_t duration_t
Type of variables holding clock durations.
Definition os-clocks.h:76
port::clock::timestamp_t timestamp_t
Type of variables holding clock time stamps.
Definition os-clocks.h:85
clock_systick sysclock
The system clock object instance.
uint16_t size_t
Type of memory pool size storage.
Definition os-mempool.h:80
static const attributes initializer
Default memory pool initialiser.
Definition os-mempool.h:163
bool in_handler_mode(void)
Check if the CPU is in handler mode.
Definition os-sched.h:1108
@ ok
Function completed; no errors or events occurred.
Definition os-decls.h:181
bool locked(void)
Check if the scheduler is locked.
Definition os-sched.h:856
thread & thread(void)
Get the current running thread.
uint32_t result_t
Type of values returned by RTOS functions.
Definition os-decls.h:96
System namespace.
#define os_assert_throw(__e, __er)
Assert or throw a system error exception.
Definition os-decls.h:1130
#define os_assert_err(__e, __er)
Assert or return an error.
Definition os-decls.h:1115
Single file µOS++ RTOS definitions.