Skip to content

Commit b1e48db

Browse files
author
Cruz Monrreal
authored
Merge pull request #10225 from kjbracey-arm/memorypool_blocking_alloc
Mail/MemoryPool: blocking alloc
2 parents b8410d8 + b8d90fb commit b1e48db

File tree

3 files changed

+222
-10
lines changed

3 files changed

+222
-10
lines changed

TESTS/mbedmicro-rtos-mbed/MemoryPool/main.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020

2121
using namespace utest::v1;
2222

23+
#define THREAD_STACK_SIZE 512
24+
#define TEST_TIMEOUT 50
25+
2326
/* Enum used to select block allocation method. */
2427
typedef enum {
2528
ALLOC, CALLOC
@@ -450,6 +453,80 @@ void test_mem_pool_free_realloc_first_complex(AllocType atype)
450453
}
451454
}
452455

456+
/* Test alloc timeout
457+
*
458+
* Given a pool with one slot for int data
459+
* When a thread tries to allocate two blocks with @ TEST_TIMEOUT timeout
460+
* Then first operation succeeds immediately and second fails at the correct time.
461+
*/
462+
void test_mem_pool_timeout()
463+
{
464+
MemoryPool<int, 1> mem_pool;
465+
466+
Timer timer;
467+
timer.start();
468+
469+
int *item = mem_pool.alloc_for(TEST_TIMEOUT);
470+
TEST_ASSERT_NOT_NULL(item);
471+
TEST_ASSERT_UINT32_WITHIN(TEST_TIMEOUT * 100, 0, timer.read_us());
472+
473+
item = mem_pool.alloc_for(TEST_TIMEOUT);
474+
TEST_ASSERT_NULL(item);
475+
TEST_ASSERT_UINT32_WITHIN(TEST_TIMEOUT * 100, TEST_TIMEOUT * 1000, timer.read_us());
476+
477+
uint64_t end_time = Kernel::get_ms_count() + TEST_TIMEOUT;
478+
item = mem_pool.alloc_until(end_time);
479+
TEST_ASSERT_NULL(item);
480+
TEST_ASSERT_UINT64_WITHIN(TEST_TIMEOUT * 100, end_time, Kernel::get_ms_count());
481+
}
482+
483+
namespace {
484+
struct free_capture {
485+
MemoryPool<int, 1> *pool;
486+
int *item;
487+
};
488+
}
489+
490+
static void free_int_item(free_capture *to_free)
491+
{
492+
ThisThread::sleep_for(TEST_TIMEOUT);
493+
494+
osStatus status = to_free->pool->free(to_free->item);
495+
TEST_ASSERT_EQUAL(osOK, status);
496+
}
497+
498+
/** Test alloc wait forever
499+
*
500+
* Given two threads A & B and a pool with one slot for int data
501+
* When thread A allocs a block from the pool and tries to alloc a second one with @a osWaitForever timeout
502+
* Then thread waits for a block to become free in the pool
503+
* When thread B frees the first block from the pool
504+
* Then thread A successfully allocs a block from the pool
505+
*/
506+
void test_mem_pool_waitforever()
507+
{
508+
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
509+
MemoryPool<int, 1> pool;
510+
511+
Timer timer;
512+
timer.start();
513+
514+
int *item = pool.alloc_for(osWaitForever);
515+
TEST_ASSERT_NOT_NULL(item);
516+
TEST_ASSERT_UINT32_WITHIN(TEST_TIMEOUT * 100, 0, timer.read_us());
517+
518+
struct free_capture to_free;
519+
to_free.pool = &pool;
520+
to_free.item = item;
521+
t.start(callback(free_int_item, &to_free));
522+
523+
item = pool.alloc_for(osWaitForever);
524+
TEST_ASSERT_EQUAL(item, to_free.item);
525+
TEST_ASSERT_UINT32_WITHIN(TEST_TIMEOUT * 100, TEST_TIMEOUT * 1000, timer.read_us());
526+
527+
t.join();
528+
}
529+
453530
/* Robustness checks for free() function.
454531
* Function under test is called with invalid parameters.
455532
*
@@ -569,6 +646,9 @@ Case cases[] = {
569646

570647
Case("Test: fail (out of free blocks).", test_mem_pool_alloc_fail_wrapper<int, 3>),
571648

649+
Case("Test: timeout", test_mem_pool_timeout),
650+
Case("Test: wait forever", test_mem_pool_waitforever),
651+
572652
Case("Test: free() - robust (free called with invalid param - NULL).", free_block_invalid_parameter_null),
573653
Case("Test: free() - robust (free called with invalid param).", free_block_invalid_parameter)
574654
};

rtos/Mail.h

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "mbed_rtos_storage.h"
3232
#include "mbed_rtos1_types.h"
3333

34+
#include "platform/mbed_toolchain.h"
3435
#include "platform/NonCopyable.h"
3536

3637
#ifndef MBED_NO_GLOBAL_USING_DIRECTIVE
@@ -90,32 +91,94 @@ class Mail : private mbed::NonCopyable<Mail<T, queue_sz> > {
9091
return _queue.full();
9192
}
9293

93-
/** Allocate a memory block of type T.
94+
/** Allocate a memory block of type T, without blocking.
9495
*
95-
* @param millisec Not used.
96+
* @param millisec Not used (see note).
9697
*
9798
* @return Pointer to memory block that you can fill with mail or NULL in case error.
9899
*
99100
* @note You may call this function from ISR context.
101+
* @note If blocking is required, use Mail::alloc_for or Mail::alloc_until
100102
*/
101-
T *alloc(uint32_t millisec = 0)
103+
T *alloc(MBED_UNUSED uint32_t millisec = 0)
102104
{
103105
return _pool.alloc();
104106
}
105107

108+
/** Allocate a memory block of type T, optionally blocking.
109+
*
110+
* @param millisec Timeout value, or osWaitForever.
111+
*
112+
* @return Pointer to memory block that you can fill with mail or NULL in case error.
113+
*
114+
* @note You may call this function from ISR context if the millisec parameter is set to 0.
115+
*/
116+
T *alloc_for(uint32_t millisec)
117+
{
118+
return _pool.alloc_for(millisec);
119+
}
120+
121+
/** Allocate a memory block of type T, blocking.
122+
*
123+
* @param millisec Absolute timeout time, referenced to Kernel::get_ms_count().
124+
*
125+
* @return Pointer to memory block that you can fill with mail or NULL in case error.
126+
*
127+
* @note You cannot call this function from ISR context.
128+
* @note the underlying RTOS may have a limit to the maximum wait time
129+
* due to internal 32-bit computations, but this is guaranteed to work if the
130+
* wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
131+
* the wait will time out earlier than specified.
132+
*/
133+
T *alloc_until(uint64_t millisec)
134+
{
135+
return _pool.alloc_until(millisec);
136+
}
137+
106138
/** Allocate a memory block of type T, and set memory block to zero.
107139
*
108-
* @param millisec Not used.
140+
* @param millisec Not used (see note).
109141
*
110142
* @return Pointer to memory block that you can fill with mail or NULL in case error.
111143
*
112-
* @note You may call this function from ISR context.
144+
* @note You may call this function from ISR context if the millisec parameter is set to 0.
145+
* @note If blocking is required, use Mail::calloc_for or Mail::calloc_until
113146
*/
114-
T *calloc(uint32_t millisec = 0)
147+
T *calloc(MBED_UNUSED uint32_t millisec = 0)
115148
{
116149
return _pool.calloc();
117150
}
118151

152+
/** Allocate a memory block of type T, optionally blocking, and set memory block to zero.
153+
*
154+
* @param millisec Timeout value, or osWaitForever.
155+
*
156+
* @return Pointer to memory block that you can fill with mail or NULL in case error.
157+
*
158+
* @note You may call this function from ISR context if the millisec parameter is set to 0.
159+
*/
160+
T *calloc_for(uint32_t millisec)
161+
{
162+
return _pool.calloc_for(millisec);
163+
}
164+
165+
/** Allocate a memory block of type T, blocking, and set memory block to zero.
166+
*
167+
* @param millisec Absolute timeout time, referenced to Kernel::get_ms_count().
168+
*
169+
* @return Pointer to memory block that you can fill with mail or NULL in case error.
170+
*
171+
* @note You cannot call this function from ISR context.
172+
* @note the underlying RTOS may have a limit to the maximum wait time
173+
* due to internal 32-bit computations, but this is guaranteed to work if the
174+
* wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
175+
* the wait will time out earlier than specified.
176+
*/
177+
T *calloc_until(uint64_t millisec)
178+
{
179+
return _pool.calloc_until(millisec);
180+
}
181+
119182
/** Put a mail in the queue.
120183
*
121184
* @param mptr Memory block previously allocated with Mail::alloc or Mail::calloc.

rtos/MemoryPool.h

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class MemoryPool : private mbed::NonCopyable<MemoryPool<T, pool_sz> > {
7575
osMemoryPoolDelete(_id);
7676
}
7777

78-
/** Allocate a memory block of type T from a memory pool.
78+
/** Allocate a memory block from a memory pool, without blocking.
7979
@return address of the allocated memory block or NULL in case of no memory available.
8080
8181
@note You may call this function from ISR context.
@@ -85,14 +85,83 @@ class MemoryPool : private mbed::NonCopyable<MemoryPool<T, pool_sz> > {
8585
return (T *)osMemoryPoolAlloc(_id, 0);
8686
}
8787

88-
/** Allocate a memory block of type T from a memory pool and set memory block to zero.
88+
/** Allocate a memory block from a memory pool, optionally blocking.
89+
@param millisec timeout value (osWaitForever to wait forever)
90+
@return address of the allocated memory block or NULL in case of no memory available.
91+
92+
@note You may call this function from ISR context if the millisec parameter is set to 0.
93+
*/
94+
T *alloc_for(uint32_t millisec)
95+
{
96+
return (T *)osMemoryPoolAlloc(_id, millisec);
97+
}
98+
99+
/** Allocate a memory block from a memory pool, blocking.
100+
@param millisec absolute timeout time, referenced to Kernel::get_ms_count().
101+
@return address of the allocated memory block or NULL in case of no memory available.
102+
103+
@note You cannot call this function from ISR context.
104+
@note the underlying RTOS may have a limit to the maximum wait time
105+
due to internal 32-bit computations, but this is guaranteed to work if the
106+
wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
107+
the wait will time out earlier than specified.
108+
*/
109+
T *alloc_until(uint64_t millisec)
110+
{
111+
uint64_t now = Kernel::get_ms_count();
112+
uint32_t delay;
113+
if (now >= millisec) {
114+
delay = 0;
115+
} else if (millisec - now >= osWaitForever) {
116+
delay = osWaitForever - 1;
117+
} else {
118+
delay = millisec - now;
119+
}
120+
return alloc_for(delay);
121+
}
122+
123+
/** Allocate a memory block from a memory pool, without blocking, and set memory block to zero.
89124
@return address of the allocated memory block or NULL in case of no memory available.
90125
91126
@note You may call this function from ISR context.
92127
*/
93128
T *calloc(void)
94129
{
95-
T *item = (T *)osMemoryPoolAlloc(_id, 0);
130+
T *item = alloc();
131+
if (item != NULL) {
132+
memset(item, 0, sizeof(T));
133+
}
134+
return item;
135+
}
136+
137+
/** Allocate a memory block from a memory pool, optionally blocking, and set memory block to zero.
138+
@param millisec timeout value (osWaitForever to wait forever)
139+
@return address of the allocated memory block or NULL in case of no memory available.
140+
141+
@note You may call this function from ISR context if the millisec parameter is set to 0.
142+
*/
143+
T *calloc_for(uint32_t millisec)
144+
{
145+
T *item = alloc_for(millisec);
146+
if (item != NULL) {
147+
memset(item, 0, sizeof(T));
148+
}
149+
return item;
150+
}
151+
152+
/** Allocate a memory block from a memory pool, blocking, and set memory block to zero.
153+
@param millisec absolute timeout time, referenced to Kernel::get_ms_count().
154+
@return address of the allocated memory block or NULL in case of no memory available.
155+
156+
@note You cannot call this function from ISR context.
157+
@note the underlying RTOS may have a limit to the maximum wait time
158+
due to internal 32-bit computations, but this is guaranteed to work if the
159+
wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
160+
the wait will time out earlier than specified.
161+
*/
162+
T *calloc_until(uint64_t millisec)
163+
{
164+
T *item = alloc_until(millisec);
96165
if (item != NULL) {
97166
memset(item, 0, sizeof(T));
98167
}
@@ -109,7 +178,7 @@ class MemoryPool : private mbed::NonCopyable<MemoryPool<T, pool_sz> > {
109178
*/
110179
osStatus free(T *block)
111180
{
112-
return osMemoryPoolFree(_id, (void *)block);
181+
return osMemoryPoolFree(_id, block);
113182
}
114183

115184
private:

0 commit comments

Comments
 (0)