µ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 Liviu Ionescu.
5 *
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or
11 * sell copies of the Software, and to permit persons to whom
12 * the Software is furnished to do so, subject to the following
13 * conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28#include <cmsis-plus/rtos/os.h>
29#include <memory>
30
31// ----------------------------------------------------------------------------
32
33#if defined(__clang__)
34#pragma clang diagnostic ignored "-Wc++98-compat"
35#endif
36
37// ----------------------------------------------------------------------------
38
39namespace os
40{
41 namespace rtos
42 {
43
44 // ------------------------------------------------------------------------
45
128 const memory_pool::attributes memory_pool::initializer;
129
130 // ------------------------------------------------------------------------
131
186 // ------------------------------------------------------------------------
191 // Protected internal constructor.
193 {
194#if defined(OS_TRACE_RTOS_MEMPOOL)
195 trace::printf ("%s() @%p %s\n", __func__, this, this->name ());
196#endif
197 }
198
199 memory_pool::memory_pool (const char* name) :
200 object_named_system
201 { name }
202 {
203#if defined(OS_TRACE_RTOS_MEMPOOL)
204 trace::printf ("%s() @%p %s\n", __func__, this, this->name ());
205#endif
206 }
207
240 memory_pool::memory_pool (std::size_t blocks, std::size_t block_size_bytes,
241 const attributes& attr,
242 const allocator_type& allocator) :
244 { nullptr, blocks, block_size_bytes, attr, allocator }
245 {
246 ;
247 }
248
277 memory_pool::memory_pool (const char* name, std::size_t blocks,
278 std::size_t block_size_bytes,
279 const attributes& attr,
280 const allocator_type& allocator) :
281 object_named_system
282 { name }
283 {
284#if defined(OS_TRACE_RTOS_MEMPOOL)
285 trace::printf ("%s() @%p %s %u %u\n", __func__, this, this->name (),
286 blocks, block_size_bytes);
287#endif
288
289 if (attr.mp_pool_address != nullptr)
290 {
291 // Do not use any allocator at all.
292 internal_construct_ (blocks, block_size_bytes, attr, nullptr, 0);
293 }
294 else
295 {
296 allocator_ = &allocator;
297
298 // If no user storage was provided via attributes,
299 // allocate it dynamically via the allocator.
300 allocated_pool_size_elements_ = (compute_allocated_size_bytes<
301 typename allocator_type::value_type> (blocks, block_size_bytes)
302 + sizeof(typename allocator_type::value_type) - 1)
303 / sizeof(typename allocator_type::value_type);
304
305 allocated_pool_addr_ =
306 const_cast<allocator_type&> (allocator).allocate (
307 allocated_pool_size_elements_);
308
309 internal_construct_ (
310 blocks,
311 block_size_bytes,
312 attr,
313 allocated_pool_addr_,
314 allocated_pool_size_elements_
315 * sizeof(typename allocator_type::value_type));
316 }
317 }
318
323 void
324 memory_pool::internal_construct_ (std::size_t blocks,
325 std::size_t block_size_bytes,
326 const attributes& attr,
327 void* pool_address,
328 std::size_t pool_size_bytes)
329 {
330 // Don't call this from interrupt handlers.
332
333#if !defined(OS_USE_RTOS_PORT_MEMORY_POOL)
334 clock_ = attr.clock != nullptr ? attr.clock : &sysclock;
335#endif
336
337 blocks_ = static_cast<memory_pool::size_t> (blocks);
338 assert(blocks_ == blocks);
339 assert(blocks_ > 0);
340
341 // Adjust block size to multiple of pointer.
342 // Blocks must be large enough to store a pointer, used
343 // to construct the list of free blocks.
344 block_size_bytes_ = (static_cast<memory_pool::size_t> (block_size_bytes
345 + __SIZEOF_POINTER__ - 1))
346 & (static_cast<memory_pool::size_t> (~(__SIZEOF_POINTER__ - 1)));
347
348 // If the storage is given explicitly, override attributes.
349 if (pool_address != nullptr)
350 {
351 // The attributes should not define any storage in this case.
352 assert(attr.mp_pool_address == nullptr);
353
354 pool_addr_ = pool_address;
355 pool_size_bytes_ = pool_size_bytes;
356 }
357 else
358 {
359 pool_addr_ = attr.mp_pool_address;
360 pool_size_bytes_ = attr.mp_pool_size_bytes;
361 }
362
363 // Blocks must be pointer aligned.
364 void* p = pool_addr_;
365 std::size_t sz = pool_size_bytes_;
366 pool_addr_ = static_cast<char*> (std::align (__SIZEOF_POINTER__,
367 blocks_ * block_size_bytes_,
368 p, sz));
369
370#if defined(OS_TRACE_RTOS_MEMPOOL)
371 trace::printf ("%s() @%p %s %u %u %p %u\n", __func__, this, name (),
372 blocks_, block_size_bytes_, pool_addr_, pool_size_bytes_);
373#endif
374
375 std::size_t storage_size = compute_allocated_size_bytes<void*> (
376 blocks_, block_size_bytes_);
377
378 if (pool_addr_ != nullptr)
379 {
380 // The pool must be real, and have a non zero size.
381 os_assert_throw(pool_size_bytes_ > 0, EINVAL);
382 // The pool must fit the storage.
383 os_assert_throw(pool_size_bytes_ >= storage_size, EINVAL);
384 }
385
386 // The pool must have a real address.
387 os_assert_throw(pool_addr_ != nullptr, ENOMEM);
388
389 internal_init_ ();
390
391 }
392
414 {
415#if defined(OS_TRACE_RTOS_MEMPOOL)
416 trace::printf ("%s() @%p %s\n", __func__, this, name ());
417#endif
418
419 // There must be no threads waiting for this pool.
420 assert(list_.empty ());
421
422 typedef typename std::allocator_traits<allocator_type>::pointer pointer;
423
424 if (allocated_pool_addr_ != nullptr)
425 {
426 static_cast<allocator_type*> (const_cast<void*> (allocator_))->deallocate (
427 static_cast<pointer> (allocated_pool_addr_),
428 allocated_pool_size_elements_);
429 }
430 }
431
436 /*
437 * Construct the linked list of blocks and initialise the
438 * internal pointers and counters.
439 */
440 void
441 memory_pool::internal_init_ (void)
442 {
443 // Construct a linked list of blocks. Store the pointer at
444 // the beginning of each block. Each block
445 // will hold the address of the next free block, or nullptr at the end.
446 char* p = static_cast<char*> (pool_addr_);
447 for (std::size_t i = 1; i < blocks_; ++i)
448 {
449 // Compute the address of the next block;
450 char* pn = p + block_size_bytes_;
451
452 // Make this block point to the next one.
453 *(static_cast<void**> (static_cast<void*> (p))) = pn;
454 // Advance pointer
455 p = pn;
456 }
457
458 // Mark end of list.
459 *(static_cast<void**> (static_cast<void*> (p))) = nullptr;
460
461 first_ = pool_addr_; // Pointer to first block.
462
463 count_ = 0; // No allocated blocks.
464 }
465
466 /*
467 * Internal function used to return the first block in the
468 * free list.
469 * Should be called from an interrupts critical section.
470 */
471 void*
472 memory_pool::internal_try_first_ (void)
473 {
474 if (first_ != nullptr)
475 {
476 void* p = static_cast<void*> (first_);
477 first_ = *(static_cast<void**> (first_));
478
479#pragma GCC diagnostic push
480#if defined(__clang__)
481#pragma clang diagnostic ignored "-Wdeprecated-volatile"
482#endif
483 ++count_;
484#pragma GCC diagnostic pop
485
486 return p;
487 }
488
489 return nullptr;
490 }
491
515 void*
517 {
518#if defined(OS_TRACE_RTOS_MEMPOOL)
519 trace::printf ("%s() @%p %s\n", __func__, this, name ());
520#endif
521
522 // Don't call this from interrupt handlers.
524 // Don't call this from critical regions.
526
527 void* p;
528
529 // Extra test before entering the loop, with its inherent weight.
530 // Trade size for speed.
531 {
532 // ----- Enter critical section -------------------------------------
534
535 p = internal_try_first_ ();
536 if (p != nullptr)
537 {
538#if defined(OS_TRACE_RTOS_MEMPOOL)
539 trace::printf ("%s()=%p @%p %s\n", __func__, p, this, name ());
540#endif
541 return p;
542 }
543 // ----- Exit critical section --------------------------------------
544 }
545
546 thread& crt_thread = this_thread::thread ();
547
548 // Prepare a list node pointing to the current thread.
549 // Do not worry for being on stack, it is temporarily linked to the
550 // list and guaranteed to be removed before this function returns.
552 { crt_thread };
553
554 for (;;)
555 {
556 {
557 // ----- Enter critical section ---------------------------------
559
560 p = internal_try_first_ ();
561 if (p != nullptr)
562 {
563#if defined(OS_TRACE_RTOS_MEMPOOL)
564 trace::printf ("%s()=%p @%p %s\n", __func__, p, this,
565 name ());
566#endif
567 return p;
568 }
569
570 // Add this thread to the memory pool waiting list.
571 scheduler::internal_link_node (list_, node);
572 // state::suspended set in above link().
573 // ----- Exit critical section ----------------------------------
574 }
575
577
578 // Remove the thread from the memory pool waiting list,
579 // if not already removed by free().
580 scheduler::internal_unlink_node (node);
581
582 if (this_thread::thread ().interrupted ())
583 {
584#if defined(OS_TRACE_RTOS_MEMPOOL)
585 trace::printf ("%s() INTR @%p %s\n", __func__, this, name ());
586#endif
587 return nullptr;
588 }
589 }
590
591 /* NOTREACHED */
592 }
593
611 void*
613 {
614#if defined(OS_TRACE_RTOS_MEMPOOL)
615 trace::printf ("%s() @%p %s\n", __func__, this, name ());
616#endif
617
618 // Don't call this from high priority interrupts.
619 assert(port::interrupts::is_priority_valid ());
620
621 void* p;
622 {
623 // ----- Enter critical section -------------------------------------
625
626 p = internal_try_first_ ();
627 // ----- Exit critical section --------------------------------------
628 }
629
630#if defined(OS_TRACE_RTOS_MEMPOOL)
631 trace::printf ("%s()=%p @%p %s\n", __func__, p, this, name ());
632#endif
633 return p;
634 }
635
676 void*
678 {
679#if defined(OS_TRACE_RTOS_MEMPOOL)
680 trace::printf ("%s(%u) @%p %s\n", __func__,
681 static_cast<unsigned int> (timeout), this, name ());
682#endif
683
684 // Don't call this from interrupt handlers.
686 // Don't call this from critical regions.
688
689 void* p;
690
691 // Extra test before entering the loop, with its inherent weight.
692 // Trade size for speed.
693 {
694 // ----- Enter critical section -------------------------------------
696
697 p = internal_try_first_ ();
698 if (p != nullptr)
699 {
700#if defined(OS_TRACE_RTOS_MEMPOOL)
701 trace::printf ("%s()=%p @%p %s\n", __func__, p, this, name ());
702#endif
703 return p;
704 }
705 // ----- Exit critical section --------------------------------------
706 }
707
708 thread& crt_thread = this_thread::thread ();
709
710 // Prepare a list node pointing to the current thread.
711 // Do not worry for being on stack, it is temporarily linked to the
712 // list and guaranteed to be removed before this function returns.
714 { crt_thread };
715
716 internal::clock_timestamps_list& clock_list = clock_->steady_list ();
717 clock::timestamp_t timeout_timestamp = clock_->steady_now () + timeout;
718
719 // Prepare a timeout node pointing to the current thread.
721 { timeout_timestamp, crt_thread };
722
723 for (;;)
724 {
725 {
726 // ----- Enter critical section ---------------------------------
728
729 p = internal_try_first_ ();
730 if (p != nullptr)
731 {
732#if defined(OS_TRACE_RTOS_MEMPOOL)
733 trace::printf ("%s()=%p @%p %s\n", __func__, p, this,
734 name ());
735#endif
736 return p;
737 }
738
739 // Add this thread to the memory pool waiting list,
740 // and the clock timeout list.
741 scheduler::internal_link_node (list_, node, clock_list,
742 timeout_node);
743 // state::suspended set in above link().
744 // ----- Exit critical section ----------------------------------
745 }
746
748
749 // Remove the thread from the memory pool waiting list,
750 // if not already removed by free() and from the clock
751 // timeout list, if not already removed by the timer.
752 scheduler::internal_unlink_node (node, timeout_node);
753
754 if (this_thread::thread ().interrupted ())
755 {
756#if defined(OS_TRACE_RTOS_MEMPOOL)
757 trace::printf ("%s() INTR @%p %s\n", __func__, this, name ());
758#endif
759 return nullptr;
760 }
761
762 if (clock_->steady_now () >= timeout_timestamp)
763 {
764#if defined(OS_TRACE_RTOS_MEMPOOL)
765 trace::printf ("%s() TMO @%p %s\n", __func__, this, name ());
766#endif
767 return nullptr;
768 }
769 }
770
771 /* NOTREACHED */
772 }
773
785 memory_pool::free (void* block)
786 {
787#if defined(OS_TRACE_RTOS_MEMPOOL)
788 trace::printf ("%s(%p) @%p %s\n", __func__, block, this, name ());
789#endif
790
791 // Don't call this from high priority interrupts.
792 assert(port::interrupts::is_priority_valid ());
793
794 // Validate pointer.
795 if ((block < pool_addr_)
796 || (block
797 >= (static_cast<char*> (pool_addr_) + 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
806 {
807 // ----- Enter critical section -------------------------------------
809
810 // Perform a push_front() on the single linked LIFO list,
811 // i.e. add the block to the beginning of the list.
812
813 // Link previous list to this block; may be null, but it does
814 // not matter.
815 *(static_cast<void**> (block)) = first_;
816
817 // Now this block is the first one.
818 first_ = block;
819
820#pragma GCC diagnostic push
821#if defined(__clang__)
822#pragma clang diagnostic ignored "-Wdeprecated-volatile"
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:774
Double linked list node, with time stamp and thread.
Definition os-lists.h:227
Double linked list node, with thread reference.
Definition os-lists.h:72
Interrupts critical section RAII helper.
Definition os-sched.h:524
Standard allocator based on the RTOS system default memory manager.
Definition os-memory.h:544
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:101
Synchronised memory pool, using the default RTOS allocator.
Definition os-mempool.h:70
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:247
int printf(const char *format,...)
Write a formatted string to the trace device.
Definition trace.cpp:74
port::clock::duration_t duration_t
Type of variables holding clock durations.
Definition os-clocks.h:83
port::clock::timestamp_t timestamp_t
Type of variables holding clock time stamps.
Definition os-clocks.h:92
clock_systick sysclock
The system clock object instance.
uint16_t size_t
Type of memory pool size storage.
Definition os-mempool.h:82
static const attributes initializer
Default memory pool initialiser.
Definition os-mempool.h:165
bool in_handler_mode(void)
Check if the CPU is in handler mode.
Definition os-sched.h:1136
@ ok
Function completed; no errors or events occurred.
Definition os-decls.h:195
bool locked(void)
Check if the scheduler is locked.
Definition os-sched.h:882
thread & thread(void)
Get the current running thread.
uint32_t result_t
Type of values returned by RTOS functions.
Definition os-decls.h:110
System namespace.
#define os_assert_throw(__e, __er)
Assert or throw a system error exception.
Definition os-decls.h:1141
#define os_assert_err(__e, __er)
Assert or return an error.
Definition os-decls.h:1126
Single file µOS++ RTOS definitions.