mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #10225 from kjbracey-arm/memorypool_blocking_alloc
Mail/MemoryPool: blocking allocpull/10385/head
commit
b1e48db242
|
|
@ -20,6 +20,9 @@
|
||||||
|
|
||||||
using namespace utest::v1;
|
using namespace utest::v1;
|
||||||
|
|
||||||
|
#define THREAD_STACK_SIZE 512
|
||||||
|
#define TEST_TIMEOUT 50
|
||||||
|
|
||||||
/* Enum used to select block allocation method. */
|
/* Enum used to select block allocation method. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ALLOC, CALLOC
|
ALLOC, CALLOC
|
||||||
|
|
@ -450,6 +453,80 @@ void test_mem_pool_free_realloc_first_complex(AllocType atype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Test alloc timeout
|
||||||
|
*
|
||||||
|
* Given a pool with one slot for int data
|
||||||
|
* When a thread tries to allocate two blocks with @ TEST_TIMEOUT timeout
|
||||||
|
* Then first operation succeeds immediately and second fails at the correct time.
|
||||||
|
*/
|
||||||
|
void test_mem_pool_timeout()
|
||||||
|
{
|
||||||
|
MemoryPool<int, 1> mem_pool;
|
||||||
|
|
||||||
|
Timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
int *item = mem_pool.alloc_for(TEST_TIMEOUT);
|
||||||
|
TEST_ASSERT_NOT_NULL(item);
|
||||||
|
TEST_ASSERT_UINT32_WITHIN(TEST_TIMEOUT * 100, 0, timer.read_us());
|
||||||
|
|
||||||
|
item = mem_pool.alloc_for(TEST_TIMEOUT);
|
||||||
|
TEST_ASSERT_NULL(item);
|
||||||
|
TEST_ASSERT_UINT32_WITHIN(TEST_TIMEOUT * 100, TEST_TIMEOUT * 1000, timer.read_us());
|
||||||
|
|
||||||
|
uint64_t end_time = Kernel::get_ms_count() + TEST_TIMEOUT;
|
||||||
|
item = mem_pool.alloc_until(end_time);
|
||||||
|
TEST_ASSERT_NULL(item);
|
||||||
|
TEST_ASSERT_UINT64_WITHIN(TEST_TIMEOUT * 100, end_time, Kernel::get_ms_count());
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct free_capture {
|
||||||
|
MemoryPool<int, 1> *pool;
|
||||||
|
int *item;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_int_item(free_capture *to_free)
|
||||||
|
{
|
||||||
|
ThisThread::sleep_for(TEST_TIMEOUT);
|
||||||
|
|
||||||
|
osStatus status = to_free->pool->free(to_free->item);
|
||||||
|
TEST_ASSERT_EQUAL(osOK, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test alloc wait forever
|
||||||
|
*
|
||||||
|
* Given two threads A & B and a pool with one slot for int data
|
||||||
|
* When thread A allocs a block from the pool and tries to alloc a second one with @a osWaitForever timeout
|
||||||
|
* Then thread waits for a block to become free in the pool
|
||||||
|
* When thread B frees the first block from the pool
|
||||||
|
* Then thread A successfully allocs a block from the pool
|
||||||
|
*/
|
||||||
|
void test_mem_pool_waitforever()
|
||||||
|
{
|
||||||
|
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
|
||||||
|
MemoryPool<int, 1> pool;
|
||||||
|
|
||||||
|
Timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
int *item = pool.alloc_for(osWaitForever);
|
||||||
|
TEST_ASSERT_NOT_NULL(item);
|
||||||
|
TEST_ASSERT_UINT32_WITHIN(TEST_TIMEOUT * 100, 0, timer.read_us());
|
||||||
|
|
||||||
|
struct free_capture to_free;
|
||||||
|
to_free.pool = &pool;
|
||||||
|
to_free.item = item;
|
||||||
|
t.start(callback(free_int_item, &to_free));
|
||||||
|
|
||||||
|
item = pool.alloc_for(osWaitForever);
|
||||||
|
TEST_ASSERT_EQUAL(item, to_free.item);
|
||||||
|
TEST_ASSERT_UINT32_WITHIN(TEST_TIMEOUT * 100, TEST_TIMEOUT * 1000, timer.read_us());
|
||||||
|
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
/* Robustness checks for free() function.
|
/* Robustness checks for free() function.
|
||||||
* Function under test is called with invalid parameters.
|
* Function under test is called with invalid parameters.
|
||||||
*
|
*
|
||||||
|
|
@ -569,6 +646,9 @@ Case cases[] = {
|
||||||
|
|
||||||
Case("Test: fail (out of free blocks).", test_mem_pool_alloc_fail_wrapper<int, 3>),
|
Case("Test: fail (out of free blocks).", test_mem_pool_alloc_fail_wrapper<int, 3>),
|
||||||
|
|
||||||
|
Case("Test: timeout", test_mem_pool_timeout),
|
||||||
|
Case("Test: wait forever", test_mem_pool_waitforever),
|
||||||
|
|
||||||
Case("Test: free() - robust (free called with invalid param - NULL).", free_block_invalid_parameter_null),
|
Case("Test: free() - robust (free called with invalid param - NULL).", free_block_invalid_parameter_null),
|
||||||
Case("Test: free() - robust (free called with invalid param).", free_block_invalid_parameter)
|
Case("Test: free() - robust (free called with invalid param).", free_block_invalid_parameter)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
77
rtos/Mail.h
77
rtos/Mail.h
|
|
@ -31,6 +31,7 @@
|
||||||
#include "mbed_rtos_storage.h"
|
#include "mbed_rtos_storage.h"
|
||||||
#include "mbed_rtos1_types.h"
|
#include "mbed_rtos1_types.h"
|
||||||
|
|
||||||
|
#include "platform/mbed_toolchain.h"
|
||||||
#include "platform/NonCopyable.h"
|
#include "platform/NonCopyable.h"
|
||||||
|
|
||||||
#ifndef MBED_NO_GLOBAL_USING_DIRECTIVE
|
#ifndef MBED_NO_GLOBAL_USING_DIRECTIVE
|
||||||
|
|
@ -90,32 +91,94 @@ public:
|
||||||
return _queue.full();
|
return _queue.full();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Allocate a memory block of type T.
|
/** Allocate a memory block of type T, without blocking.
|
||||||
*
|
*
|
||||||
* @param millisec Not used.
|
* @param millisec Not used (see note).
|
||||||
*
|
*
|
||||||
* @return Pointer to memory block that you can fill with mail or NULL in case error.
|
* @return Pointer to memory block that you can fill with mail or NULL in case error.
|
||||||
*
|
*
|
||||||
* @note You may call this function from ISR context.
|
* @note You may call this function from ISR context.
|
||||||
|
* @note If blocking is required, use Mail::alloc_for or Mail::alloc_until
|
||||||
*/
|
*/
|
||||||
T *alloc(uint32_t millisec = 0)
|
T *alloc(MBED_UNUSED uint32_t millisec = 0)
|
||||||
{
|
{
|
||||||
return _pool.alloc();
|
return _pool.alloc();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Allocate a memory block of type T, and set memory block to zero.
|
/** Allocate a memory block of type T, optionally blocking.
|
||||||
*
|
*
|
||||||
* @param millisec Not used.
|
* @param millisec Timeout value, or osWaitForever.
|
||||||
*
|
*
|
||||||
* @return Pointer to memory block that you can fill with mail or NULL in case error.
|
* @return Pointer to memory block that you can fill with mail or NULL in case error.
|
||||||
*
|
*
|
||||||
* @note You may call this function from ISR context.
|
* @note You may call this function from ISR context if the millisec parameter is set to 0.
|
||||||
*/
|
*/
|
||||||
T *calloc(uint32_t millisec = 0)
|
T *alloc_for(uint32_t millisec)
|
||||||
|
{
|
||||||
|
return _pool.alloc_for(millisec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Allocate a memory block of type T, blocking.
|
||||||
|
*
|
||||||
|
* @param millisec Absolute timeout time, referenced to Kernel::get_ms_count().
|
||||||
|
*
|
||||||
|
* @return Pointer to memory block that you can fill with mail or NULL in case error.
|
||||||
|
*
|
||||||
|
* @note You cannot call this function from ISR context.
|
||||||
|
* @note the underlying RTOS may have a limit to the maximum wait time
|
||||||
|
* due to internal 32-bit computations, but this is guaranteed to work if the
|
||||||
|
* wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
|
||||||
|
* the wait will time out earlier than specified.
|
||||||
|
*/
|
||||||
|
T *alloc_until(uint64_t millisec)
|
||||||
|
{
|
||||||
|
return _pool.alloc_until(millisec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Allocate a memory block of type T, and set memory block to zero.
|
||||||
|
*
|
||||||
|
* @param millisec Not used (see note).
|
||||||
|
*
|
||||||
|
* @return Pointer to memory block that you can fill with mail or NULL in case error.
|
||||||
|
*
|
||||||
|
* @note You may call this function from ISR context if the millisec parameter is set to 0.
|
||||||
|
* @note If blocking is required, use Mail::calloc_for or Mail::calloc_until
|
||||||
|
*/
|
||||||
|
T *calloc(MBED_UNUSED uint32_t millisec = 0)
|
||||||
{
|
{
|
||||||
return _pool.calloc();
|
return _pool.calloc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Allocate a memory block of type T, optionally blocking, and set memory block to zero.
|
||||||
|
*
|
||||||
|
* @param millisec Timeout value, or osWaitForever.
|
||||||
|
*
|
||||||
|
* @return Pointer to memory block that you can fill with mail or NULL in case error.
|
||||||
|
*
|
||||||
|
* @note You may call this function from ISR context if the millisec parameter is set to 0.
|
||||||
|
*/
|
||||||
|
T *calloc_for(uint32_t millisec)
|
||||||
|
{
|
||||||
|
return _pool.calloc_for(millisec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Allocate a memory block of type T, blocking, and set memory block to zero.
|
||||||
|
*
|
||||||
|
* @param millisec Absolute timeout time, referenced to Kernel::get_ms_count().
|
||||||
|
*
|
||||||
|
* @return Pointer to memory block that you can fill with mail or NULL in case error.
|
||||||
|
*
|
||||||
|
* @note You cannot call this function from ISR context.
|
||||||
|
* @note the underlying RTOS may have a limit to the maximum wait time
|
||||||
|
* due to internal 32-bit computations, but this is guaranteed to work if the
|
||||||
|
* wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
|
||||||
|
* the wait will time out earlier than specified.
|
||||||
|
*/
|
||||||
|
T *calloc_until(uint64_t millisec)
|
||||||
|
{
|
||||||
|
return _pool.calloc_until(millisec);
|
||||||
|
}
|
||||||
|
|
||||||
/** Put a mail in the queue.
|
/** Put a mail in the queue.
|
||||||
*
|
*
|
||||||
* @param mptr Memory block previously allocated with Mail::alloc or Mail::calloc.
|
* @param mptr Memory block previously allocated with Mail::alloc or Mail::calloc.
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ public:
|
||||||
osMemoryPoolDelete(_id);
|
osMemoryPoolDelete(_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Allocate a memory block of type T from a memory pool.
|
/** Allocate a memory block from a memory pool, without blocking.
|
||||||
@return address of the allocated memory block or NULL in case of no memory available.
|
@return address of the allocated memory block or NULL in case of no memory available.
|
||||||
|
|
||||||
@note You may call this function from ISR context.
|
@note You may call this function from ISR context.
|
||||||
|
|
@ -85,14 +85,83 @@ public:
|
||||||
return (T *)osMemoryPoolAlloc(_id, 0);
|
return (T *)osMemoryPoolAlloc(_id, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Allocate a memory block of type T from a memory pool and set memory block to zero.
|
/** Allocate a memory block from a memory pool, optionally blocking.
|
||||||
|
@param millisec timeout value (osWaitForever to wait forever)
|
||||||
|
@return address of the allocated memory block or NULL in case of no memory available.
|
||||||
|
|
||||||
|
@note You may call this function from ISR context if the millisec parameter is set to 0.
|
||||||
|
*/
|
||||||
|
T *alloc_for(uint32_t millisec)
|
||||||
|
{
|
||||||
|
return (T *)osMemoryPoolAlloc(_id, millisec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Allocate a memory block from a memory pool, blocking.
|
||||||
|
@param millisec absolute timeout time, referenced to Kernel::get_ms_count().
|
||||||
|
@return address of the allocated memory block or NULL in case of no memory available.
|
||||||
|
|
||||||
|
@note You cannot call this function from ISR context.
|
||||||
|
@note the underlying RTOS may have a limit to the maximum wait time
|
||||||
|
due to internal 32-bit computations, but this is guaranteed to work if the
|
||||||
|
wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
|
||||||
|
the wait will time out earlier than specified.
|
||||||
|
*/
|
||||||
|
T *alloc_until(uint64_t millisec)
|
||||||
|
{
|
||||||
|
uint64_t now = Kernel::get_ms_count();
|
||||||
|
uint32_t delay;
|
||||||
|
if (now >= millisec) {
|
||||||
|
delay = 0;
|
||||||
|
} else if (millisec - now >= osWaitForever) {
|
||||||
|
delay = osWaitForever - 1;
|
||||||
|
} else {
|
||||||
|
delay = millisec - now;
|
||||||
|
}
|
||||||
|
return alloc_for(delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Allocate a memory block from a memory pool, without blocking, and set memory block to zero.
|
||||||
@return address of the allocated memory block or NULL in case of no memory available.
|
@return address of the allocated memory block or NULL in case of no memory available.
|
||||||
|
|
||||||
@note You may call this function from ISR context.
|
@note You may call this function from ISR context.
|
||||||
*/
|
*/
|
||||||
T *calloc(void)
|
T *calloc(void)
|
||||||
{
|
{
|
||||||
T *item = (T *)osMemoryPoolAlloc(_id, 0);
|
T *item = alloc();
|
||||||
|
if (item != NULL) {
|
||||||
|
memset(item, 0, sizeof(T));
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Allocate a memory block from a memory pool, optionally blocking, and set memory block to zero.
|
||||||
|
@param millisec timeout value (osWaitForever to wait forever)
|
||||||
|
@return address of the allocated memory block or NULL in case of no memory available.
|
||||||
|
|
||||||
|
@note You may call this function from ISR context if the millisec parameter is set to 0.
|
||||||
|
*/
|
||||||
|
T *calloc_for(uint32_t millisec)
|
||||||
|
{
|
||||||
|
T *item = alloc_for(millisec);
|
||||||
|
if (item != NULL) {
|
||||||
|
memset(item, 0, sizeof(T));
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Allocate a memory block from a memory pool, blocking, and set memory block to zero.
|
||||||
|
@param millisec absolute timeout time, referenced to Kernel::get_ms_count().
|
||||||
|
@return address of the allocated memory block or NULL in case of no memory available.
|
||||||
|
|
||||||
|
@note You cannot call this function from ISR context.
|
||||||
|
@note the underlying RTOS may have a limit to the maximum wait time
|
||||||
|
due to internal 32-bit computations, but this is guaranteed to work if the
|
||||||
|
wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
|
||||||
|
the wait will time out earlier than specified.
|
||||||
|
*/
|
||||||
|
T *calloc_until(uint64_t millisec)
|
||||||
|
{
|
||||||
|
T *item = alloc_until(millisec);
|
||||||
if (item != NULL) {
|
if (item != NULL) {
|
||||||
memset(item, 0, sizeof(T));
|
memset(item, 0, sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +178,7 @@ public:
|
||||||
*/
|
*/
|
||||||
osStatus free(T *block)
|
osStatus free(T *block)
|
||||||
{
|
{
|
||||||
return osMemoryPoolFree(_id, (void *)block);
|
return osMemoryPoolFree(_id, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue