diff --git a/TESTS/mbedmicro-rtos-mbed/MemoryPool/main.cpp b/TESTS/mbedmicro-rtos-mbed/MemoryPool/main.cpp deleted file mode 100644 index 58fdf1d..0000000 --- a/TESTS/mbedmicro-rtos-mbed/MemoryPool/main.cpp +++ /dev/null @@ -1,688 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity.h" -#include "utest.h" - -#if !defined(MBED_CONF_RTOS_PRESENT) -#error [NOT_SUPPORTED] MemoryPool test cases require a RTOS to run. -#else - -#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ - do { \ - using ct = std::common_type_t; \ - TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ - } while (0) - -#define TEST_ASSERT_TIME_POINT_WITHIN(delta, expected, actual) \ - do { \ - using ct_tp = std::common_type_t; \ - using ct = std::common_type_t; \ - TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected.time_since_epoch()).count(), ct(actual.time_since_epoch()).count()); \ - } while (0) - -using namespace utest::v1; -using namespace std::chrono; - -#define THREAD_STACK_SIZE 512 -#define TEST_TIMEOUT 50ms - -/* Enum used to select block allocation method. */ -typedef enum { - ALLOC, CALLOC -} AllocType; - -/* Structure for complex block type. */ -typedef struct { - int a; - char b; - int c; -} COMPLEX_TYPE; - -/* Function to check if complex type object is cleared.*/ -bool comp_is_cleared(COMPLEX_TYPE *object) -{ - if (object->a == 0 && object->b == 0 && object->c == 0) { - return true; - } - - return false; -} - -/* Function to check if complex type object holds specified values.*/ -bool comp_is_equal(COMPLEX_TYPE *object, int a, char b, int c) -{ - if (object->a == a && object->b == b && object->c == c) { - return true; - } - - return false; -} - -/* Function to set complex type object fields.*/ -void comp_set(COMPLEX_TYPE *object, int a, char b, int c) -{ - object->a = a; - object->b = b; - object->c = c; -} - -/* Template for functional tests for try_alloc(), try_calloc() functions - * of MemoryPool object. - * - * Given MemoryPool object of the specified type and queue size has - * been successfully created. - * When max number of blocks is allocated from the pool. - * Then all allocations are successful. - * - * */ -template -void test_mem_pool_alloc_success(AllocType atype) -{ - MemoryPool mem_pool; - T *p_blocks[numOfEntries]; - uint32_t i; - - /* Test alloc()/calloc() methods - try to allocate max number of - blocks. All allocations should be successful. */ - for (i = 0; i < numOfEntries; i++) { - /* Allocate memory block. */ - if (atype == ALLOC) { - p_blocks[i] = mem_pool.try_alloc(); - } else { - p_blocks[i] = mem_pool.try_calloc(); - } - - /* Show that memory pool block has been allocated. */ - TEST_ASSERT_NOT_NULL(p_blocks[i]); - - /* Check if Calloc clears the block. */ - if (atype == CALLOC) { - TEST_ASSERT_EQUAL(0, *p_blocks[i]); - } - - /* Init fields. */ - *p_blocks[i] = (i + 5); - } - - /* Check if blocks holds valid values. */ - for (i = 0; i < numOfEntries; i++) { - TEST_ASSERT_EQUAL((i + 5), *p_blocks[i]); - } -} - -/* Template for functional tests for try_alloc(), try_calloc() functions - * of MemoryPool object. - * - * Complex memory pool block type is used. - * - * Given MemoryPool object of the specified type and queue size has - * been successfully created. - * When max number of blocks is allocated from the pool. - * Then all allocations are successful. - * - * */ -template -void test_mem_pool_alloc_success_complex(AllocType atype) -{ - MemoryPool mem_pool; - T *p_blocks[numOfEntries]; - uint32_t i; - - /* Test alloc()/calloc() methods - try to allocate max number of - blocks. All allocations should be successful. */ - for (i = 0; i < numOfEntries; i++) { - /* Allocate memory block. */ - if (atype == ALLOC) { - p_blocks[i] = mem_pool.try_alloc(); - } else { - p_blocks[i] = mem_pool.try_calloc(); - } - - /* Show that memory pool block has been allocated. */ - TEST_ASSERT_NOT_NULL(p_blocks[i]); - - /* Check if Calloc clears the block. */ - if (atype == CALLOC) { - TEST_ASSERT_EQUAL(true, comp_is_cleared(p_blocks[i])); - } - - /* Init fields. */ - comp_set(p_blocks[i], i + 1, i + 2, i + 3); - } - - /* Check if blocks holds valid values. */ - for (i = 0; i < numOfEntries; i++) { - TEST_ASSERT_EQUAL(true, comp_is_equal(p_blocks[i], i + 1, i + 2, i + 3)); - } -} - -/* Template for functional tests for try_alloc(), try_calloc() functions - * of MemoryPool object. - * - * Given MemoryPool has already max number of blocks allocated from the pool. - * When next block is allocated. - * Then allocation fails. - * - * */ -template -void test_mem_pool_alloc_fail(AllocType atype) -{ - MemoryPool mem_pool; - T *p_blocks[numOfEntries]; - T *p_extra_block; - uint32_t i; - - /* Allocate all available blocks. */ - for (i = 0; i < numOfEntries; i++) { - if (atype == ALLOC) { - p_blocks[i] = mem_pool.try_alloc(); - } else { - p_blocks[i] = mem_pool.try_calloc(); - } - - /* Show that memory pool block has been allocated. */ - TEST_ASSERT_NOT_NULL(p_blocks[i]); - } - - /* There are no more blocks available. Try to allocate another block. */ - if (atype == ALLOC) { - p_extra_block = mem_pool.try_alloc(); - } else { - p_extra_block = mem_pool.try_calloc(); - } - - /* Show that memory pool block has NOT been allocated. */ - TEST_ASSERT_NULL(p_extra_block); -} - -/* Template for functional tests for free() function - * of MemoryPool object. - * - * Given MemoryPool has all blocks allocated. - * When free operation is executed on the each allocated block. - * Then each deallocation is successfully performed. - * - * */ -template -void test_mem_pool_free_success(AllocType atype) -{ - MemoryPool mem_pool; - T *p_blocks[numOfEntries]; - uint32_t i; - osStatus status; - - /* Allocate all available blocks. */ - for (i = 0; i < numOfEntries; i++) { - if (atype == ALLOC) { - p_blocks[i] = mem_pool.try_alloc(); - } else { - p_blocks[i] = mem_pool.try_calloc(); - } - - /* Show that memory pool block has been allocated. */ - TEST_ASSERT_NOT_NULL(p_blocks[i]); - } - - /* Free all memory blocks. */ - for (i = 0; i < numOfEntries; i++) { - status = mem_pool.free(p_blocks[i]); - - /* Check operation status. */ - TEST_ASSERT_EQUAL(osOK, status); - } -} - -/* Template for functional tests for try_alloc(), try_calloc() functions - * of MemoryPool object. - * - * Basic memory pool block type is used. - * - * Given MemoryPool had all blocks allocated and one block has - * been freed (last). - * When next block is allocated. - * Then allocation is successful. - * - * */ -template -void test_mem_pool_free_realloc_last(AllocType atype) -{ - MemoryPool mem_pool; - T *p_blocks[numOfEntries]; - uint32_t i; - osStatus status; - - /* Allocate all available blocks. */ - for (i = 0; i < numOfEntries; i++) { - if (atype == ALLOC) { - p_blocks[i] = mem_pool.try_alloc(); - } else { - p_blocks[i] = mem_pool.try_calloc(); - } - - /* Init block. */ - *p_blocks[i] = 0xAB; - - /* Show that memory pool block has been allocated. */ - TEST_ASSERT_NOT_NULL(p_blocks[i]); - } - - /* Free the last block. */ - status = mem_pool.free(p_blocks[numOfEntries - 1]); - - /* Check status. */ - TEST_ASSERT_EQUAL(osOK, status); - - /* Try to allocate another block (one block is now available). */ - if (atype == ALLOC) { - p_blocks[numOfEntries - 1] = mem_pool.try_alloc(); - } else { - p_blocks[numOfEntries - 1] = mem_pool.try_calloc(); - } - - /* Show that memory pool block has been now allocated. */ - TEST_ASSERT_NOT_NULL(p_blocks[numOfEntries - 1]); - - /* Check if Calloc clears the block. */ - if (atype == CALLOC) { - TEST_ASSERT_EQUAL(0, *p_blocks[numOfEntries - 1]); - } -} - -/* Template for functional tests for try_alloc(), try_calloc() functions - * of MemoryPool object. - * - * Complex memory pool block type is used. - * - * Given MemoryPool had all blocks allocated and one block has - * been freed (last). - * When next block is allocated. - * Then allocation is successful. - * - * */ -template -void test_mem_pool_free_realloc_last_complex(AllocType atype) -{ - MemoryPool mem_pool; - T *p_blocks[numOfEntries]; - uint32_t i; - osStatus status; - - /* Allocate all available blocks. */ - for (i = 0; i < numOfEntries; i++) { - if (atype == ALLOC) { - p_blocks[i] = mem_pool.try_alloc(); - } else { - p_blocks[i] = mem_pool.try_calloc(); - } - - /* Init block. */ - comp_set(p_blocks[i], i + 1, i + 2, i + 3); - - /* Show that memory pool block has been allocated. */ - TEST_ASSERT_NOT_NULL(p_blocks[i]); - } - - /* Free the last block. */ - status = mem_pool.free(p_blocks[numOfEntries - 1]); - - /* Check status. */ - TEST_ASSERT_EQUAL(osOK, status); - - /* Try to allocate another block (one block is now available). */ - if (atype == ALLOC) { - p_blocks[numOfEntries - 1] = mem_pool.try_alloc(); - } else { - p_blocks[numOfEntries - 1] = mem_pool.try_calloc(); - } - - /* Show that memory pool block has been now allocated. */ - TEST_ASSERT_NOT_NULL(p_blocks[numOfEntries - 1]); - - /* Check if Calloc clears the block. */ - if (atype == CALLOC) { - TEST_ASSERT_EQUAL(true, comp_is_cleared(p_blocks[numOfEntries - 1])); - } -} - -/* Template for functional tests for try_alloc(), try_calloc() functions - * of MemoryPool object. - * - * Basic memory pool block type is used. - * - * Given MemoryPool had all blocks allocated and one block has - * been freed (first). - * When next block is allocated. - * Then allocation is successful. - * - * */ -template -void test_mem_pool_free_realloc_first(AllocType atype) -{ - MemoryPool mem_pool; - T *p_blocks[numOfEntries]; - uint32_t i; - osStatus status; - - /* Allocate all available blocks. */ - for (i = 0; i < numOfEntries; i++) { - if (atype == ALLOC) { - p_blocks[i] = mem_pool.try_alloc(); - } else { - p_blocks[i] = mem_pool.try_calloc(); - } - - /* Init block. */ - *p_blocks[i] = 0xAB; - - /* Show that memory pool block has been allocated. */ - TEST_ASSERT_NOT_NULL(p_blocks[i]); - } - - /* Free the last block. */ - status = mem_pool.free(p_blocks[0]); - - /* Check status. */ - TEST_ASSERT_EQUAL(osOK, status); - - /* Try to allocate another block (one block is now available). */ - if (atype == ALLOC) { - p_blocks[0] = mem_pool.try_alloc(); - } else { - p_blocks[0] = mem_pool.try_calloc(); - } - - /* Show that memory pool block has been now allocated. */ - TEST_ASSERT_NOT_NULL(p_blocks[0]); - - /* Check if Calloc clears the block. */ - if (atype == CALLOC) { - TEST_ASSERT_EQUAL(0, *p_blocks[0]); - } -} - -/* Template for functional tests for try_alloc(), try_calloc() functions - * of MemoryPool object. - * - * Complex memory pool block type is used. - * - * Given MemoryPool had all blocks allocated and one block has - * been freed (first). - * When next block is allocated. - * Then allocation is successful. - * - * */ -template -void test_mem_pool_free_realloc_first_complex(AllocType atype) -{ - MemoryPool mem_pool; - T *p_blocks[numOfEntries]; - uint32_t i; - osStatus status; - - /* Allocate all available blocks. */ - for (i = 0; i < numOfEntries; i++) { - if (atype == ALLOC) { - p_blocks[i] = mem_pool.try_alloc(); - } else { - p_blocks[i] = mem_pool.try_calloc(); - } - - /* Init block. */ - comp_set(p_blocks[i], i + 1, i + 2, i + 3); - - /* Show that memory pool block has been allocated. */ - TEST_ASSERT_NOT_NULL(p_blocks[i]); - } - - /* Free the last block. */ - status = mem_pool.free(p_blocks[0]); - - /* Check status. */ - TEST_ASSERT_EQUAL(osOK, status); - - /* Try to allocate another block (one block is now available). */ - if (atype == ALLOC) { - p_blocks[0] = mem_pool.try_alloc(); - } else { - p_blocks[0] = mem_pool.try_calloc(); - } - - /* Show that memory pool block has been now allocated. */ - TEST_ASSERT_NOT_NULL(p_blocks[0]); - - /* Check if Calloc clears the block. */ - if (atype == CALLOC) { - TEST_ASSERT_EQUAL(true, comp_is_cleared(p_blocks[0])); - } -} - -/* Test try_alloc_for/try_alloc_until 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 mem_pool; - - Timer timer; - timer.start(); - - int *item = mem_pool.try_alloc_for(TEST_TIMEOUT); - TEST_ASSERT_NOT_NULL(item); - TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, 0ms, timer.elapsed_time()); - - item = mem_pool.try_alloc_for(TEST_TIMEOUT); - TEST_ASSERT_NULL(item); - TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time()); - - auto end_time = Kernel::Clock::now() + TEST_TIMEOUT; - item = mem_pool.try_alloc_until(end_time); - TEST_ASSERT_NULL(item); - TEST_ASSERT_TIME_POINT_WITHIN(TEST_TIMEOUT / 10, end_time, Kernel::Clock::now()); -} - -namespace { -struct free_capture { - MemoryPool *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 pool; - - Timer timer; - timer.start(); - - int *item = pool.try_alloc_for(Kernel::wait_for_u32_forever); - TEST_ASSERT_NOT_NULL(item); - TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, 0ms, timer.elapsed_time()); - - struct free_capture to_free; - to_free.pool = &pool; - to_free.item = item; - t.start(callback(free_int_item, &to_free)); - - item = pool.try_alloc_for(Kernel::wait_for_u32_forever); - TEST_ASSERT_EQUAL(item, to_free.item); - TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time()); - - t.join(); -} - -/* Robustness checks for free() function. - * Function under test is called with invalid parameters. - * - * Given MemoryPool object has been successfully created. - * When free operation is performed on NULL address. - * Then deallocation fails with osErrorParameter error. - * - */ -void free_block_invalid_parameter_null() -{ - MemoryPool mem_pool; - osStatus status; - - /* Try to free block passing invalid parameter (NULL). */ - status = mem_pool.free(NULL); - - /* Check operation status. */ - TEST_ASSERT_EQUAL(osErrorParameter, status); -} - -/* Robustness checks for free() function. - * Function under test is called with invalid parameters. - * - * Given MemoryPool object has been successfully created. - * When free operation is performed on invalid address. - * Then deallocation fails with osErrorParameter error. - * - */ -void free_block_invalid_parameter() -{ - MemoryPool mem_pool; - osStatus status; - - /* Try to free block passing invalid parameter (variable address). */ - status = mem_pool.free(reinterpret_cast(&status)); - - /* Check operation status. */ - TEST_ASSERT_EQUAL(osErrorParameter, status); -} - -/* Use wrapper functions to reduce memory usage. */ - -template -void test_mem_pool_alloc_success_wrapper() -{ - test_mem_pool_alloc_success(ALLOC); - test_mem_pool_alloc_success(CALLOC); -} - -template -void test_mem_pool_alloc_success_complex_wrapper() -{ - test_mem_pool_alloc_success_complex(ALLOC); - test_mem_pool_alloc_success_complex(CALLOC); -} - -template -void test_mem_pool_free_success_wrapper() -{ - test_mem_pool_free_success(ALLOC); - test_mem_pool_free_success(CALLOC); -} - -template -void test_mem_pool_free_realloc_last_wrapper() -{ - test_mem_pool_free_realloc_last(ALLOC); - test_mem_pool_free_realloc_last(CALLOC); - -} - -template -void test_mem_pool_free_realloc_first_wrapper() -{ - test_mem_pool_free_realloc_first(ALLOC); - test_mem_pool_free_realloc_first(CALLOC); -} - -template -void test_mem_pool_free_realloc_first_complex_wrapper() -{ - test_mem_pool_free_realloc_first_complex(ALLOC); - test_mem_pool_free_realloc_first_complex(CALLOC); -} - -template -void test_mem_pool_free_realloc_last_complex_wrapper() -{ - test_mem_pool_free_realloc_last_complex(ALLOC); - test_mem_pool_free_realloc_last_complex(CALLOC); -} - -template -void test_mem_pool_alloc_fail_wrapper() -{ - test_mem_pool_alloc_fail(ALLOC); - test_mem_pool_alloc_fail(CALLOC); -} - -Case cases[] = { - Case("Test: try_alloc()/try_calloc() - success, 4 bytes b_type, q_size equal to 1.", test_mem_pool_alloc_success_wrapper), - Case("Test: try_alloc()/try_calloc() - success, 4 bytes b_type, q_size equal to 3.", test_mem_pool_alloc_success_wrapper), - Case("Test: try_alloc()/try_calloc() - success, 1 bytes b_type, q_size equal to 1.", test_mem_pool_alloc_success_wrapper), - Case("Test: try_alloc()/try_calloc() - success, 1 bytes b_type, q_size equal to 3.", test_mem_pool_alloc_success_wrapper), - Case("Test: try_alloc()/try_calloc() - success, complex b_type, q_size equal to 1.", test_mem_pool_alloc_success_complex_wrapper), - Case("Test: try_alloc()/try_calloc() - success, complex b_type, q_size equal to 3.", test_mem_pool_alloc_success_complex_wrapper), - - Case("Test: free() - success, 4 bytes b_type, q_size equal to 1.", test_mem_pool_free_success_wrapper), - Case("Test: free() - success, 4 bytes b_type, q_size equal to 3.", test_mem_pool_free_success_wrapper), - Case("Test: free() - success, complex b_type, q_size equal to 1.", test_mem_pool_free_success_wrapper), - Case("Test: free() - success, complex b_type, q_size equal to 3.", test_mem_pool_free_success_wrapper), - - Case("Test: re-allocation of the last block, basic type.", test_mem_pool_free_realloc_last_wrapper), - Case("Test: re-allocation of the first block, basic type.", test_mem_pool_free_realloc_first_wrapper), - Case("Test: re-allocation of the first block, complex type.", test_mem_pool_free_realloc_first_complex_wrapper), - Case("Test: re-allocation of the last block, complex type.", test_mem_pool_free_realloc_last_complex_wrapper), - - Case("Test: fail (out of free blocks).", test_mem_pool_alloc_fail_wrapper), - - 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).", free_block_invalid_parameter) -}; - -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(20, "default_auto"); - return greentea_test_setup_handler(number_of_cases); -} - -Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); - -int main() -{ - Harness::run(specification); -} - -#endif // !defined(MBED_CONF_RTOS_PRESENT) diff --git a/TESTS/mbedmicro-rtos-mbed/basic/main.cpp b/TESTS/mbedmicro-rtos-mbed/basic/main.cpp deleted file mode 100644 index a405ca7..0000000 --- a/TESTS/mbedmicro-rtos-mbed/basic/main.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2013-2017, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) -#error [NOT_SUPPORTED] RTOS basic test cases require RTOS with multithread to run -#else - -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "utest/utest.h" -#include "unity/unity.h" - -#if defined(SKIP_TIME_DRIFT_TESTS) || !DEVICE_USTICKER -#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. -#else - -using utest::v1::Case; -using std::milli; -using std::micro; -using namespace std::chrono; - -#if defined(__CORTEX_M23) || defined(__CORTEX_M33) -#define TEST_STACK_SIZE 512 -#else -#define TEST_STACK_SIZE 256 -#endif - -static duration elapsed_time_ms; -static const int test_timeout = 40; - - -void update_tick_thread(Mutex *mutex) -{ - while (true) { - ThisThread::sleep_for(1ms); - mutex->lock(); - ++elapsed_time_ms; - mutex->unlock(); - } -} - - -/** Tests is to measure the accuracy of ThisThread::sleep_for() over a period of time - - Given - a thread updating elapsed_time_ms every milli sec - and host script for time measurement accuracy check (More details on tests can be found in timing_drift_auto.py) - When host query what is current count base_time - Then Device responds by the elapsed_time_ms - When host query what is current count final_time - Then Device responds by the elapsed_time_ms - When host computes the drift considering base_time, final_time, transport delay and measurement stretch - Then host send the results back to device pass/fail based on tolerance - */ -void test(void) -{ - char _key[11] = { }; - char _value[128] = { }; - int expected_key = 1; - Mutex mutex; - duration elapsed_time; - - Thread tick_thread(osPriorityHigh, TEST_STACK_SIZE); - tick_thread.start(callback(update_tick_thread, &mutex)); - - greentea_send_kv("timing_drift_check_start", 0); - - // wait for 1st signal from host - do { - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - expected_key = strcmp(_key, "base_time"); - } while (expected_key); - - mutex.lock(); - elapsed_time = elapsed_time_ms; - mutex.unlock(); - // send base_time - greentea_send_kv(_key, elapsed_time.count()); - - // wait for 2nd signal from host - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - - mutex.lock(); - elapsed_time = elapsed_time_ms; - mutex.unlock(); - // send final_time - greentea_send_kv(_key, elapsed_time.count()); - - //get the results from host - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - - TEST_ASSERT_EQUAL_STRING_MESSAGE("pass", _key, "Host side script reported a fail..."); -} - -Case cases[] = { - Case("Test ThisThread::sleep_for accuracy", test) -}; - -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(test_timeout, "timing_drift_auto"); - return utest::v1::greentea_test_setup_handler(number_of_cases); -} - -utest::v1::Specification specification(greentea_test_setup, cases); - -int main() -{ - utest::v1::Harness::run(specification); -} - -#endif // defined(SKIP_TIME_DRIFT_TESTS) || !DEVICE_USTICKER -#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) diff --git a/TESTS/mbedmicro-rtos-mbed/condition_variable/main.cpp b/TESTS/mbedmicro-rtos-mbed/condition_variable/main.cpp deleted file mode 100644 index 5424ffc..0000000 --- a/TESTS/mbedmicro-rtos-mbed/condition_variable/main.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) -#error [NOT_SUPPORTED] Condition variable test cases require RTOS with multithread to run -#else - -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. -#else - -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity.h" -#include "utest.h" -#include "rtos.h" - -using namespace utest::v1; -using namespace std::chrono_literals; - -#define TEST_STACK_SIZE 512 -#define TEST_DELAY 10ms - -static int change_counter = 0; -static Mutex mutex; -static ConditionVariable cond(mutex); - -void increment_on_signal() -{ - mutex.lock(); - - cond.wait(); - change_counter++; - - mutex.unlock(); -} - -void test_notify_one() -{ - Thread t1(osPriorityNormal, TEST_STACK_SIZE); - Thread t2(osPriorityNormal, TEST_STACK_SIZE); - - change_counter = 0; - t1.start(increment_on_signal); - t2.start(increment_on_signal); - - ThisThread::sleep_for(TEST_DELAY); - TEST_ASSERT_EQUAL(0, change_counter); - - mutex.lock(); - cond.notify_one(); - mutex.unlock(); - - ThisThread::sleep_for(TEST_DELAY); - TEST_ASSERT_EQUAL(1, change_counter); - - mutex.lock(); - cond.notify_one(); - mutex.unlock(); - - t1.join(); - t2.join(); -} - -void test_notify_all() -{ - Thread t1(osPriorityNormal, TEST_STACK_SIZE); - Thread t2(osPriorityNormal, TEST_STACK_SIZE); - - change_counter = 0; - t1.start(increment_on_signal); - t2.start(increment_on_signal); - - ThisThread::sleep_for(TEST_DELAY); - TEST_ASSERT_EQUAL(0, change_counter); - - mutex.lock(); - cond.notify_all(); - mutex.unlock(); - - ThisThread::sleep_for(TEST_DELAY); - TEST_ASSERT_EQUAL(2, change_counter); - - t1.join(); - t2.join(); -} - - -class TestConditionVariable : public ConditionVariable { - -public: - static void test_linked_list(void) - { - Waiter *list = NULL; - Waiter w1; - Waiter w2; - Waiter w3; - Waiter w4; - - TEST_ASSERT_EQUAL(0, validate_and_get_size(&list)); - - // Add 4 nodes - _add_wait_list(&list, &w1); - TEST_ASSERT_EQUAL(1, validate_and_get_size(&list)); - _add_wait_list(&list, &w2); - TEST_ASSERT_EQUAL(2, validate_and_get_size(&list)); - _add_wait_list(&list, &w3); - TEST_ASSERT_EQUAL(3, validate_and_get_size(&list)); - _add_wait_list(&list, &w4); - TEST_ASSERT_EQUAL(4, validate_and_get_size(&list)); - - // Remove a middle node - _remove_wait_list(&list, &w2); - TEST_ASSERT_EQUAL(3, validate_and_get_size(&list)); - - // Remove front node - _remove_wait_list(&list, &w1); - TEST_ASSERT_EQUAL(2, validate_and_get_size(&list)); - - // remove back node - _remove_wait_list(&list, &w4); - TEST_ASSERT_EQUAL(1, validate_and_get_size(&list)); - - // remove last node - _remove_wait_list(&list, &w3); - TEST_ASSERT_EQUAL(0, validate_and_get_size(&list)); - - TEST_ASSERT_EQUAL_PTR(NULL, list); - } - - /** - * Validate the linked list an return the number of elements - * - * If this list is invalid then this function asserts and does not - * return. - * - * Every node in a valid linked list has the properties: - * 1. node->prev->next == node - * 2. node->next->prev == node - */ - static int validate_and_get_size(Waiter **list) - { - Waiter *first = *list; - if (NULL == first) { - // List is empty - return 0; - } - - int size = 0; - Waiter *current = first; - do { - TEST_ASSERT_EQUAL_PTR(current, current->prev->next); - TEST_ASSERT_EQUAL_PTR(current, current->next->prev); - current = current->next; - size++; - } while (current != first); - return size; - } - -}; - -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(10, "default_auto"); - return verbose_test_setup_handler(number_of_cases); -} - -Case cases[] = { - Case("Test notify one", test_notify_one), - Case("Test notify all", test_notify_all), - Case("Test linked list", TestConditionVariable::test_linked_list), -}; - -Specification specification(test_setup, cases); - -int main() -{ - return !Harness::run(specification); -} - -#endif // !DEVICE_USTICKER -#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) diff --git a/TESTS/mbedmicro-rtos-mbed/event_flags/main.cpp b/TESTS/mbedmicro-rtos-mbed/event_flags/main.cpp deleted file mode 100644 index 0d906b3..0000000 --- a/TESTS/mbedmicro-rtos-mbed/event_flags/main.cpp +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (c) 2013-2017, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity/unity.h" -#include "utest/utest.h" - -using utest::v1::Case; -using namespace std::chrono; - -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. -#else - -#if defined(MBED_CONF_RTOS_PRESENT) -#if defined(__CORTEX_M23) || defined(__CORTEX_M33) -#define THREAD_STACK_SIZE 512 -#else -#define THREAD_STACK_SIZE 320 /* 512B stack on GCC_ARM compiler cause out of memory on some 16kB RAM boards e.g. NUCLEO_F070RB */ -#endif -#endif - -#define MAX_FLAG_POS 30 -#define PROHIBITED_FLAG_POS 31 - -/* flags */ -#define FLAG01 0x1FFF /* 00000000000000000001111111111111 */ -#define FLAG02 0x3FFE000 /* 00000011111111111110000000000000 */ -#define FLAG03 0x7C000000 /* 01111100000000000000000000000000 */ -#define PROHIBITED_FLAG 0x80000000 /* 10000000000000000000000000000000 */ -#define NO_FLAGS 0x0 - -void send_thread(EventFlags *ef, uint32_t flags, milliseconds wait) -{ - for (uint32_t i = 0; i <= MAX_FLAG_POS; i++) { - const uint32_t flag = flags & (1 << i); - if (flag) { - ef->set(flag); - if (wait != 0ms) { - ThisThread::sleep_for(wait); - } - } - } -} - -#if defined(MBED_CONF_RTOS_PRESENT) -Semaphore sync_sem(0, 1); - -void send_thread_sync(EventFlags *ef, uint32_t flags, milliseconds wait) -{ - for (uint32_t i = 0; i <= MAX_FLAG_POS; i++) { - const uint32_t flag = flags & (1 << i); - if (flag) { - sync_sem.acquire(); - ef->set(flag); - ThisThread::sleep_for(wait); - } - } -} - -void wait_thread_all(EventFlags *ef, uint32_t flags) -{ - uint32_t ret, flags_after_clear; - ret = ef->wait_all(flags); - flags_after_clear = ef->get(); - TEST_ASSERT(flags | ret); - TEST_ASSERT(flags | ~flags_after_clear); -} -#endif - - -/** Test if get on empty EventFlags object return NO_FLAGS - - Given a empty EventFlags object - When call @a get - Then @a get return status is NO_FLAGS - */ -void test_empty_get(void) -{ - EventFlags ev; - uint32_t flags; - - flags = ev.get(); - TEST_ASSERT_EQUAL(NO_FLAGS, flags); -} - -/** Test if clear on empty EventFlags object return NO_FLAGS - - Given a empty EventFlags object - When call @a clear(NO_FLAGS) - Then @a clear return status is NO_FLAGS - */ -void test_empty_clear(void) -{ - EventFlags ev; - uint32_t flags; - - flags = ev.clear(NO_FLAGS); - TEST_ASSERT_EQUAL(NO_FLAGS, flags); -} - -/** Test if set on empty EventFlags object return NO_FLAGS - - Given a empty EventFlags object - When call @a set(NO_FLAGS) - Then @a set return status is NO_FLAGS - */ -void test_empty_set(void) -{ - EventFlags ev; - uint32_t flags; - - flags = ev.set(NO_FLAGS); - TEST_ASSERT_EQUAL(NO_FLAGS, flags); -} - -/** Test if call of set/clean with PROHIBITED_FLAG doesn't invalidates object flags - - Given a EventFlags object with all flags already set - When call @a clear(PROHIBITED_FLAG) with prohibited flag - Then @a clear return status is osFlagsErrorParameter and object flags stays unchanged - When call @a set(PROHIBITED_FLAG) with prohibited flag - Then @a set return status is osFlagsErrorParameter and object flags stays unchanged - - @note Each signal has up to 31 event flags 0x1, 0x2, 0x4, 0x8, ..., 0x40000000 - Most significant bit is reserved and thereby flag 0x80000000 is prohibited - */ -void test_prohibited(void) -{ - EventFlags ev; - uint32_t flags; - - ev.set(FLAG01 | FLAG02 | FLAG03); - -#if !MBED_TRAP_ERRORS_ENABLED - flags = ev.clear(PROHIBITED_FLAG); - TEST_ASSERT_EQUAL(osFlagsErrorParameter, flags); -#endif - - flags = ev.get(); - TEST_ASSERT_EQUAL(FLAG01 | FLAG02 | FLAG03, flags); - -#if !MBED_TRAP_ERRORS_ENABLED - flags = ev.set(PROHIBITED_FLAG); - TEST_ASSERT_EQUAL(osFlagsErrorParameter, flags); -#endif - - flags = ev.get(); - TEST_ASSERT_EQUAL(FLAG01 | FLAG02 | FLAG03, flags); -} - -/** Test set/get/clear for full flag range - - Given a EventFlags object - When call @a clear - Then @a clear return status is already set flags - When call @a set with specified flag - Then @a set return status is flags after setting - When call @a get - Then @a get return status is set flags - */ -void test_set_get_clear_full_flag_range(void) -{ - EventFlags ev; - uint32_t flag, flags, ret; - - flags = NO_FLAGS; - for (int i = 0; i <= MAX_FLAG_POS; i++) { - ret = ev.clear(); - TEST_ASSERT_EQUAL(flags, ret); - flags = 1 << i; - ret = ev.set(flags); - TEST_ASSERT_EQUAL(flags, ret); - ret = ev.get(); - TEST_ASSERT_EQUAL(flags, ret); - } - - ev.clear(); - flags = NO_FLAGS; - for (int i = 0; i <= MAX_FLAG_POS; i++) { - ret = ev.clear(NO_FLAGS); - TEST_ASSERT_EQUAL(flags, ret); - flag = 1 << i; - flags |= flag; - ret = ev.set(flag); - TEST_ASSERT_EQUAL(flags, ret); - ret = ev.get(); - TEST_ASSERT_EQUAL(flags, ret); - } -} - -#if defined(MBED_CONF_RTOS_PRESENT) -/** Test if multi-threaded flag set cause wait_all to return - - Given a EventFlags object and three threads are started in parallel - When threads set specified flags - Then main thread waits until receive all of them - */ -void test_multi_thread_all(void) -{ - EventFlags ef; - Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); - thread1.start([&] { send_thread(&ef, FLAG01, 1ms); }); - thread2.start([&] { send_thread(&ef, FLAG02, 2ms); }); - thread3.start([&] { send_thread(&ef, FLAG03, 3ms); }); - - uint32_t ret = ef.wait_all(FLAG01 | FLAG02 | FLAG03); - TEST_ASSERT_EQUAL(FLAG01 | FLAG02 | FLAG03, ret); -} - -/** Test if multi-threaded flag set cause wait_any to return - - Given a EventFlags object and three threads are started in parallel - When threads set specified flags - Then main thread waits until receive all of them - */ -void test_multi_thread_any(void) -{ - EventFlags ef; - uint32_t ret; - Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); - thread1.start([&] { send_thread(&ef, FLAG01, 1ms); }); - thread2.start([&] { send_thread(&ef, FLAG02, 1ms); }); - thread3.start([&] { send_thread(&ef, FLAG03, 1ms); }); - - for (int i = 0; i <= MAX_FLAG_POS; i++) { - uint32_t flag = 1 << i; - ret = ef.wait_any(flag); - TEST_ASSERT(flag | ret); - } - ret = ef.get(); - TEST_ASSERT_EQUAL(NO_FLAGS, ret); -} - -/** Test if multi-threaded flag set cause wait_any(with timeout) to return - - Given a EventFlags object and thread is running - When main thread call @ wait_any with timeout - Then when timeout expires @ wait_any return status is osFlagsErrorTimeout - When main thread call @ wait_any with timeout and thread set specified flags - Then main thread waits until receive all of them and @ wait_any return status is wait flag - */ -void test_multi_thread_any_timeout(void) -{ - EventFlags ef; - uint32_t ret; - Thread thread(osPriorityNormal, THREAD_STACK_SIZE); - thread.start([&] { send_thread_sync(&ef, FLAG01 | FLAG02 | FLAG03, 1ms); }); - - for (int i = 0; i <= MAX_FLAG_POS; i++) { - uint32_t flag = 1 << i; - - ret = ef.wait_any_for(flag, 10ms); - TEST_ASSERT_EQUAL(osFlagsErrorTimeout, ret); - - sync_sem.release(); - ret = ef.wait_any_for(flag, 10ms); - TEST_ASSERT_EQUAL(flag, ret); - } - ret = ef.get(); - TEST_ASSERT_EQUAL(NO_FLAGS, ret); -} - -/** Test if multi-threaded flag set cause wait_any(without clear) to return - - Given a EventFlags object and three threads are started in parallel - When threads set specified flags - Then main thread waits until receive all of them - */ -void test_multi_thread_any_no_clear(void) -{ - EventFlags ef; - uint32_t ret; - Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); - thread1.start([&] { send_thread(&ef, FLAG01, 1ms); }); - thread2.start([&] { send_thread(&ef, FLAG02, 1ms); }); - thread3.start([&] { send_thread(&ef, FLAG03, 1ms); }); - - for (int i = 0; i <= MAX_FLAG_POS; i++) { - uint32_t flag = 1 << i; - ret = ef.wait_any_for(flag, Kernel::wait_for_u32_forever, false); - TEST_ASSERT(flag | ret); - ret = ef.clear(flag); - TEST_ASSERT(ret < osFlagsError); - } - ret = ef.get(); - TEST_ASSERT_EQUAL(NO_FLAGS, ret); -} - -/** Test multi-threaded wait_any - - Given a EventFlags object and three threads are started in parallel - When flags are set in main thread - Then other threads waits until receive all of them - */ -void test_multi_thread_all_many_wait(void) -{ - EventFlags ef; - { - Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); - thread1.start([&] { wait_thread_all(&ef, FLAG01); }); - thread2.start([&] { wait_thread_all(&ef, FLAG02); }); - thread3.start([&] { wait_thread_all(&ef, FLAG03); }); - - ef.set(FLAG01 | FLAG02 | FLAG03); - thread1.join(); - thread2.join(); - thread3.join(); - TEST_ASSERT_EQUAL(NO_FLAGS, ef.get()); - } - - { - Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); - thread1.start([&] { wait_thread_all(&ef, FLAG01); }); - thread2.start([&] { wait_thread_all(&ef, FLAG02); }); - thread3.start([&] { wait_thread_all(&ef, FLAG03); }); - - ef.set(FLAG01); - thread1.join(); - ef.set(FLAG02); - thread2.join(); - ef.set(FLAG03); - thread3.join(); - TEST_ASSERT_EQUAL(NO_FLAGS, ef.get()); - } -} -#endif - -/** Test if multi-event flag set cause wait_all to return - - Given a EventFlags object and three ticker instance with the callback registered - When callbacks set specified flags - Then main thread waits until receive all of them - */ -void test_multi_eventflags_all(void) -{ - EventFlags ef; - Ticker t1, t2, t3; - t1.attach([&] { send_thread(&ef, FLAG01, 0ms); }, 3ms); - t2.attach([&] { send_thread(&ef, FLAG02, 0ms); }, 4ms); - t3.attach([&] { send_thread(&ef, FLAG03, 0ms); }, 5ms); - uint32_t ret = ef.wait_all_for(FLAG01 | FLAG02 | FLAG03, 20ms, false); - TEST_ASSERT_EQUAL(FLAG01 | FLAG02 | FLAG03, ret); -} - -/** Test if multi-event flag set cause wait_any to return - - Given a EventFlags object and three ticker instance with the callback registered - When callbacks set specified flags - Then main thread waits until receive all of them - */ -void test_multi_eventflags_any(void) -{ - EventFlags ef; - uint32_t ret; - Ticker t1, t2, t3; - t1.attach([&] { send_thread(&ef, FLAG01, 0ms); }, 3ms); - t2.attach([&] { send_thread(&ef, FLAG02, 0ms); }, 4ms); - t3.attach([&] { send_thread(&ef, FLAG03, 0ms); }, 5ms); - - for (int i = 0; i <= MAX_FLAG_POS; i++) { - uint32_t flag = 1 << i; - ret = ef.wait_any(flag); - TEST_ASSERT(flag | ret); - } - ret = ef.get(); - TEST_ASSERT_EQUAL(NO_FLAGS, ret); -} - -/** Test if multi-event flag set cause wait_any(without clear) to return - - Given a EventFlags object and three ticker instance with the callback registered - When callbacks set specified flags - Then main thread waits until receive all of them - */ -void test_multi_eventflags_any_no_clear(void) -{ - EventFlags ef; - uint32_t ret; - Ticker t1, t2, t3; - t1.attach([&] { send_thread(&ef, FLAG01, 0ms); }, 3ms); - t2.attach([&] { send_thread(&ef, FLAG02, 0ms); }, 4ms); - t3.attach([&] { send_thread(&ef, FLAG03, 0ms); }, 5ms); - - for (int i = 0; i <= MAX_FLAG_POS; i++) { - uint32_t flag = 1 << i; - ret = ef.wait_any(flag, osWaitForever, false); - TEST_ASSERT(flag | ret); - ret = ef.clear(flag); - TEST_ASSERT(ret < osFlagsError); - } - ret = ef.get(); - TEST_ASSERT_EQUAL(NO_FLAGS, ret); -} - -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(10, "default_auto"); - return utest::v1::verbose_test_setup_handler(number_of_cases); -} - -Case cases[] = { - Case("Test empty clear", test_empty_clear), - Case("Test empty get", test_empty_get), - Case("Test empty set", test_empty_set), - Case("Test clear/set with prohibited flag", test_prohibited), - Case("Test set/get/clear for full flag range", test_set_get_clear_full_flag_range), -#if defined(MBED_CONF_RTOS_PRESENT) - Case("Test multi-threaded wait_all", test_multi_thread_all), - Case("Test multi-threaded wait_any", test_multi_thread_any), - Case("Test multi-threaded wait_all many wait", test_multi_thread_all_many_wait), - Case("Test multi-threaded wait_any timeout", test_multi_thread_any_timeout), - Case("Test multi-threaded wait_any no clear", test_multi_thread_any_no_clear), -#endif - Case("Test multi-eventflags wait_all", test_multi_eventflags_all), - Case("Test multi-eventflags wait_any", test_multi_eventflags_any), - Case("Test multi-eventflags wait_any no clear", test_multi_eventflags_any_no_clear) -}; - -utest::v1::Specification specification(test_setup, cases); - -int main() -{ - return !utest::v1::Harness::run(specification); -} - -#endif // !DEVICE_USTICKER diff --git a/TESTS/mbedmicro-rtos-mbed/heap_and_stack/main.cpp b/TESTS/mbedmicro-rtos-mbed/heap_and_stack/main.cpp deleted file mode 100644 index e4c88a3..0000000 --- a/TESTS/mbedmicro-rtos-mbed/heap_and_stack/main.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (c) 2016-2017, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if defined(TARGET_CORTEX_A) || !DEVICE_USTICKER -#error [NOT_SUPPORTED] test not supported. -#else - -#include -#include -#include - -#include "mbed.h" -#include "cmsis.h" -#include "greentea-client/test_env.h" -#include "utest/utest.h" -#include "unity/unity.h" - -using utest::v1::Case; - -static const int test_timeout = 30; - - -// Amount to malloc for each iteration -#define MALLOC_TEST_SIZE 256 -// Malloc fill pattern -#define MALLOC_FILL 0x55 - -extern unsigned char *mbed_heap_start; -extern uint32_t mbed_heap_size; -extern unsigned char *mbed_stack_isr_start; -extern uint32_t mbed_stack_isr_size; - -#if defined(TOOLCHAIN_GCC_ARM) && defined(MBED_SPLIT_HEAP) -extern uint32_t __mbed_sbrk_start_0; -extern uint32_t __mbed_krbs_start_0; -unsigned char *mbed_heap_start_0 = (unsigned char *) &__mbed_sbrk_start_0;; -uint32_t mbed_heap_size_0 = (uint32_t) &__mbed_krbs_start_0 - (uint32_t) &__mbed_sbrk_start_0; -#endif - -struct linked_list { - linked_list *next; - uint8_t data[MALLOC_TEST_SIZE]; -}; - - -// Global test variables -#define TEST_VALUE 789 -static struct Test { - Test() : val(TEST_VALUE) {} - ~Test() {} - int val; -} t; - -int test_function() -{ - return TEST_VALUE; -} -static int global_int = test_function(); - -/** Test global variables initialisation - */ -void test_global_variables_initialisation(void) -{ - TEST_ASSERT_EQUAL(TEST_VALUE, global_int); - TEST_ASSERT_EQUAL(TEST_VALUE, t.val); -} - -/* TODO: add memory layout test. - * - * The test was skipped for now since not all devices seems to comply with Mbed OS memory. - * - * @note Mbed OS memory model: https://os.mbed.com/docs/latest/reference/memory.html - * - */ - - -/* - * Return true if addr is in range [start:start+size) - */ -static bool inrange(uint32_t addr, uint32_t start, uint32_t size) -{ - return (addr >= start) && (addr < (start + size)); -} - -/* - * Return true if [addr:addr+size] is inside [start:start+len] - */ -static bool rangeinrange(uint32_t addr, uint32_t size, uint32_t start, uint32_t len) -{ - if ((addr + size) > (start + len)) { - return false; - } - if (addr < start) { - return false; - } - return true; -} - -/* - * Return true if the region is filled only with the specified value - */ -static bool valid_fill(uint8_t *data, uint32_t size, uint8_t fill) -{ - for (uint32_t i = 0; i < size; i++) { - if (data[i] != fill) { - return false; - } - } - return true; -} - -static void allocate_and_fill_heap(linked_list *&head) -{ - linked_list *current; - - current = (linked_list *) malloc(sizeof(linked_list)); - TEST_ASSERT_NOT_NULL(current); - - current->next = NULL; - memset((void *) current->data, MALLOC_FILL, sizeof(current->data)); - - // Allocate until malloc returns NULL - head = current; - while (true) { - - // Allocate - linked_list *temp = (linked_list *) malloc(sizeof(linked_list)); - - if (NULL == temp) { - break; - } - bool result = rangeinrange((uint32_t) temp, sizeof(linked_list), (uint32_t)mbed_heap_start, mbed_heap_size); -#if defined(TOOLCHAIN_GCC_ARM) && defined(MBED_SPLIT_HEAP) - if (false == result) { - result = rangeinrange((uint32_t) temp, sizeof(linked_list), (uint32_t)mbed_heap_start_0, mbed_heap_size_0); - } -#endif - TEST_ASSERT_TRUE_MESSAGE(result, "Memory allocation out of range"); - - // Init - temp->next = NULL; - memset((void *) temp->data, MALLOC_FILL, sizeof(current->data)); - - // Add to list - current->next = temp; - current = temp; - } -} - -static void check_and_free_heap(linked_list *head, uint32_t &max_allocation_size) -{ - uint32_t total_size = 0; - linked_list *current = head; - - while (current != NULL) { - total_size += sizeof(linked_list); - bool result = valid_fill(current->data, sizeof(current->data), MALLOC_FILL); - - TEST_ASSERT_TRUE_MESSAGE(result, "Memory fill check failed"); - - linked_list *next = current->next; - free(current); - current = next; - } - - max_allocation_size = total_size; -} - -/** Test heap allocation - - Given a heap - When memory is allocated from heap - Then the memory is within heap boundary - - */ -void test_heap_in_range(void) -{ - char *initial_heap; - - // Sanity check malloc - initial_heap = (char *) malloc(1); - TEST_ASSERT_NOT_NULL(initial_heap); - - bool result = inrange((uint32_t) initial_heap, (uint32_t)mbed_heap_start, mbed_heap_size); - -#if defined(TOOLCHAIN_GCC_ARM) && defined(MBED_SPLIT_HEAP) - if (false == result) { - result = inrange((uint32_t) initial_heap, (uint32_t)mbed_heap_start_0, mbed_heap_size_0); - } -#endif - TEST_ASSERT_TRUE_MESSAGE(result, "Heap in wrong location"); - free(initial_heap); -} - -#if MBED_CONF_RTOS_PRESENT -/** Test for Main thread stack - - Given a Main thread and its stack - When check Main thread stack pointer - Then the SP is within Main stack boundary - */ -void test_main_stack_in_range(void) -{ - mbed_rtos_storage_thread_t *thread = (mbed_rtos_storage_thread_t *) osThreadGetId(); - - uint32_t psp = __get_PSP(); - uint8_t *stack_mem = (uint8_t *) thread->stack_mem; - uint32_t stack_size = thread->stack_size; - - // PSP stack should be somewhere in the middle - bool result = inrange(psp, (uint32_t) stack_mem, stack_size); - - TEST_ASSERT_TRUE_MESSAGE(result, "Main stack in wrong location"); -} -#endif // #if MBED_CONF_RTOS_PRESENT - -/** Test for Scheduler/ISR thread stack - - Given a Scheduler/ISR thread and its stack - When check Scheduler/ISR thread stack pointer - Then the SP is within Scheduler/ISR stack boundary - */ -void test_isr_stack_in_range(void) -{ - // MSP stack should be very near end (test using within 128 bytes) - uint32_t msp = __get_MSP(); - bool result = inrange(msp, (uint32_t)mbed_stack_isr_start + mbed_stack_isr_size - 0x400, 0x400); - - TEST_ASSERT_TRUE_MESSAGE(result, "Interrupt stack in wrong location"); -} - -/** Test full heap allocation - - Given a heap and linked_list data structure - When linked_list is filled till run out of heap memory - Then the memory is properly initialised and freed - */ -void test_heap_allocation_free(void) -{ - linked_list *head = NULL; - uint32_t max_allocation_size = 0; - - // Fully allocate the heap and stack - allocate_and_fill_heap(head); - - check_and_free_heap(head, max_allocation_size); - - // Force a task switch so a stack check is performed - ThisThread::sleep_for(10); - - printf("Total size dynamically allocated: %luB\n", max_allocation_size); -} - - -// Test cases -Case cases[] = { - Case("Test global variables initialisation", test_global_variables_initialisation), - Case("Test heap in range", test_heap_in_range), -#if MBED_CONF_RTOS_PRESENT - Case("Test main stack in range", test_main_stack_in_range), -#endif - Case("Test isr stack in range", test_isr_stack_in_range), - Case("Test heap allocation and free", test_heap_allocation_free) -}; - -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(test_timeout, "default_auto"); - return utest::v1::greentea_test_setup_handler(number_of_cases); -} - -utest::v1::Specification specification(greentea_test_setup, cases); - -int main() -{ - return !utest::v1::Harness::run(specification); -} - -#endif // defined(TARGET_CORTEX_A) || !DEVICE_USTICKER diff --git a/TESTS/mbedmicro-rtos-mbed/kernel_tick_count/main.cpp b/TESTS/mbedmicro-rtos-mbed/kernel_tick_count/main.cpp deleted file mode 100644 index 0b72099..0000000 --- a/TESTS/mbedmicro-rtos-mbed/kernel_tick_count/main.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2018-2018 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "greentea-client/test_env.h" -#include "utest/utest.h" -#include "unity/unity.h" - -#include "rtos/Kernel.h" -#include "mbed.h" - -#include - -using namespace std::chrono; - -using utest::v1::Case; - -#define TEST_REPEAT_COUNT 1000 - -#define ACCURACY_DURATION 1s -#if defined(NO_SYSTICK) || defined(MBED_TICKLESS) -// On targets with NO_SYSTICK/MBED_TICKLESS enabled, systick is emulated by lp_ticker what makes it less accurate -// for more details https://os.mbed.com/docs/latest/reference/tickless.html -#define ACCURACY_DELTA 15000us // 1.5% -#else -#define ACCURACY_DELTA 1500us // 0.15% -#endif - - -#define TEST_ASSERT_EQUAL_DURATION(expected, actual) \ - do { \ - using ct = std::common_type_t; \ - TEST_ASSERT_EQUAL(ct(expected).count(), ct(actual).count()); \ - } while (0) - -#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ - do { \ - using ct = std::common_type_t; \ - TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ - } while (0) - -/** Test if declared kernel ticker frequency is 1kHz - - Given a RTOS kernel ticker - When check it frequency - Then the the frequency is 1kHz - */ -void test_frequency() -{ -#if defined(MBED_CONF_RTOS_PRESENT) - uint32_t freq = osKernelGetTickFreq(); - TEST_ASSERT_EQUAL_UINT32_MESSAGE(1000, freq, "Expected SysTick frequency is 1kHz"); -#endif - TEST_ASSERT_TRUE_MESSAGE((std::ratio_equal::value), "Expected Kernel::Clock frequency is 1kHz"); -} - -/** Test if kernel ticker increments by one - - Given a RTOS kernel ticker - When perform subsequent calls of @a rtos::Kernel::Clock::now - Then subsequent reads should not differ by more than one - */ -void test_increment(void) -{ - for (uint32_t i = 0; i < TEST_REPEAT_COUNT; i++) { - auto start = rtos::Kernel::Clock::now(); - while (true) { - rtos::Kernel::Clock::duration diff = rtos::Kernel::Clock::now() - start; - if (diff.count() != 0) { - TEST_ASSERT_EQUAL_INT64(1, diff.count()); - break; - } - } - } -} - -/** Test if kernel ticker rate is correct - - Given a RTOS kernel ticker - When perform subsequent calls of @a rtos::Kernel::Clock::now - Then when it reports 1 second elapsed, the time measured using a high-res Timer corresponds. - */ -void test_accuracy() -{ - Kernel::Clock::time_point start, stop; - Timer timer; - - start = rtos::Kernel::Clock::now(); - // wait for tick - do { - stop = rtos::Kernel::Clock::now(); - } while (stop == start); - timer.start(); - start = stop; - - // wait for 1 second to elapse according to kernel - do { - stop = rtos::Kernel::Clock::now(); - } while ((stop - start) < ACCURACY_DURATION); - timer.stop(); - - TEST_ASSERT_DURATION_WITHIN(ACCURACY_DELTA, ACCURACY_DURATION, timer.elapsed_time()); -} - -// Test cases -Case cases[] = { - Case("Test kernel ticker declared frequency", test_frequency), - Case("Test if kernel ticker increments by one", test_increment), - Case("Test kernel ticker accuracy", test_accuracy) -}; - -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(10, "default_auto"); - return utest::v1::greentea_test_setup_handler(number_of_cases); -} - -utest::v1::Specification specification(greentea_test_setup, cases); - -int main() -{ - return !utest::v1::Harness::run(specification); -} diff --git a/TESTS/mbedmicro-rtos-mbed/mail/main.cpp b/TESTS/mbedmicro-rtos-mbed/mail/main.cpp deleted file mode 100644 index 1c22f61..0000000 --- a/TESTS/mbedmicro-rtos-mbed/mail/main.cpp +++ /dev/null @@ -1,510 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) -#error [NOT_SUPPORTED] mail test cases require RTOS with multithread to run -#else - -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. -#else - -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity.h" -#include "utest.h" -#include "rtos.h" - -using namespace utest::v1; -using namespace std::chrono; - -#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ - do { \ - using ct = std::common_type_t; \ - TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ - } while (0) - -#if defined(__CORTEX_M23) || defined(__CORTEX_M33) -#define THREAD_STACK_SIZE 512 -#elif defined(TARGET_ARM_FM) -#define THREAD_STACK_SIZE 512 -#elif defined(TARGET_CY8CKIT_062_WIFI_BT_PSA) -#define THREAD_STACK_SIZE 512 -#else -#define THREAD_STACK_SIZE 320 /* larger stack cause out of heap memory on some 16kB RAM boards in multi thread test*/ -#endif -#define QUEUE_SIZE 16 -#define THREAD_1_ID 1 -#define THREAD_2_ID 2 -#define THREAD_3_ID 3 -#define QUEUE_PUT_DELAY_1 5ms -#define QUEUE_PUT_DELAY_2 50ms -#define QUEUE_PUT_DELAY_3 100ms -#define DATA_BASE 100 - - -typedef struct { - uint16_t data; - uint8_t thread_id; -} mail_t; - - -void send_thread(Mail *m, uint8_t thread_id, milliseconds wait, uint32_t send_count) -{ - uint32_t data = thread_id * DATA_BASE; - - for (uint32_t i = 0; i < send_count; i++) { - mail_t *mail = m->try_alloc(); - mail->thread_id = thread_id; - mail->data = data++; - m->put(mail); - ThisThread::sleep_for(wait); - } -} - -template -void receive_thread(Mail *m, uint8_t thread_id, milliseconds wait) -{ - int result_counter = 0; - uint32_t data = thread_id * DATA_BASE; - - ThisThread::sleep_for(wait); - for (uint32_t i = 0; i < queue_size; i++) { - mail_t *mail = m->try_get_for(Kernel::wait_for_u32_forever); - if (mail) { - const uint8_t id = mail->thread_id; - - // verify thread id - TEST_ASSERT_TRUE(id == thread_id); - // verify sent data - TEST_ASSERT_TRUE(mail->data == data++); - - m->free(mail); - result_counter++; - } - } - TEST_ASSERT_EQUAL(queue_size, result_counter); -} - -/** Test single thread Mail usage and order - - Given mailbox and one additional thread - When messages are put in to the Mail box by this thread - Then messages are received in main thread in the same order as was sent and the data sent is valid - */ -void test_single_thread_order(void) -{ - uint16_t data = DATA_BASE; - int result_counter = 0; - Mail mail_box; - - // mail send thread creation - Thread thread(osPriorityNormal, THREAD_STACK_SIZE); - thread.start([&] { send_thread(&mail_box, THREAD_1_ID, QUEUE_PUT_DELAY_1, QUEUE_SIZE); }); - - // wait for some mail to be collected - ThisThread::sleep_for(10ms); - - for (uint32_t i = 0; i < QUEUE_SIZE; i++) { - // mail receive (main thread) - mail_t *mail = mail_box.try_get_for(Kernel::wait_for_u32_forever); - if (mail) { - const uint8_t id = mail->thread_id; - - // verify thread id - TEST_ASSERT_TRUE(id == THREAD_1_ID); - // verify sent data - TEST_ASSERT_TRUE(mail->data == data++); - mail_box.free(mail); - - result_counter++; - } - } - TEST_ASSERT_EQUAL(QUEUE_SIZE, result_counter); -} - -/** Test multi thread Mail usage and order - - Given mailbox and three additional threads - When messages are put in to the Mail box by these threads - Then messages are received in main thread in the same per thread order as was sent and the data sent is valid - */ -void test_multi_thread_order(void) -{ - uint16_t data[4] = { 0, DATA_BASE, DATA_BASE * 2, DATA_BASE * 3 }; - int result_counter = 0; - Mail mail_box; - - // mail send threads creation - Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); - thread1.start([&] { send_thread(&mail_box, THREAD_1_ID, QUEUE_PUT_DELAY_1, 7); }); - thread2.start([&] { send_thread(&mail_box, THREAD_2_ID, QUEUE_PUT_DELAY_2, 5); }); - thread3.start([&] { send_thread(&mail_box, THREAD_3_ID, QUEUE_PUT_DELAY_3, 4); }); - - // wait for some mail to be collected - ThisThread::sleep_for(10ms); - - for (uint32_t i = 0; i < QUEUE_SIZE; i++) { - // mail receive (main thread) - mail_t *mail = mail_box.try_get_for(Kernel::wait_for_u32_forever); - if (mail) { - const uint8_t id = mail->thread_id; - - // verify thread id - TEST_ASSERT_TRUE((id == THREAD_1_ID) || (id == THREAD_2_ID) || (id == THREAD_3_ID)); - // verify sent data - TEST_ASSERT_TRUE(mail->data == data[id]++); - mail_box.free(mail); - - result_counter++; - } - } - TEST_ASSERT_EQUAL(QUEUE_SIZE, result_counter); -} - -/** Test multi thread multi Mail usage and order - - Given 3 mailbox and three additional threads - When messages are put in to the mail boxes by main thread - Then messages are received by threads in the same per mail box order as was sent and the data sent is valid - */ -void test_multi_thread_multi_mail_order(void) -{ - Mail mail_box[4]; /* mail_box[0] not used */ - uint16_t data[4] = { 0, DATA_BASE, DATA_BASE * 2, DATA_BASE * 3 }; - mail_t *mail; - uint8_t id; - - // mail receive threads creation - Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); - Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); - thread1.start([&] { receive_thread<4>(mail_box + 1, THREAD_1_ID, 0ms); }); - thread2.start([&] { receive_thread<4>(mail_box + 2, THREAD_2_ID, 10ms); }); - thread3.start([&] { receive_thread<4>(mail_box + 3, THREAD_3_ID, 100ms); }); - - for (uint32_t i = 0; i < 4; i++) { - id = THREAD_1_ID; - mail = mail_box[id].try_alloc(); - mail->thread_id = id; - mail->data = data[id]++; - mail_box[id].put(mail); - - id = THREAD_2_ID; - mail = mail_box[id].try_alloc(); - mail->thread_id = id; - mail->data = data[id]++; - mail_box[id].put(mail); - - id = THREAD_3_ID; - mail = mail_box[id].try_alloc(); - mail->thread_id = id; - mail->data = data[id]++; - mail_box[id].put(mail); - - ThisThread::sleep_for(i * 10ms); - } - - thread1.join(); - thread2.join(); - thread3.join(); -} - -/** Test message memory deallocation with block out of the scope - - Given an empty mailbox - When try to free out of the scope memory block - Then it return appropriate error code - */ -void test_free_wrong() -{ - osStatus status; - Mail mail_box; - uint32_t *mail, data; - - mail = &data; - status = mail_box.free(mail); - TEST_ASSERT_EQUAL(osErrorParameter, status); - - mail = mail_box.try_alloc(); - TEST_ASSERT_NOT_EQUAL(NULL, mail); - - mail = &data; - status = mail_box.free(mail); - TEST_ASSERT_EQUAL(osErrorParameter, status); -} - -/** Test message memory deallocation with null block - - Given an empty mailbox - When try to free null ptr - Then it return appropriate error code - */ -void test_free_null() -{ - osStatus status; - Mail mail_box; - uint32_t *mail; - - mail = NULL; - status = mail_box.free(mail); - TEST_ASSERT_EQUAL(osErrorParameter, status); - - mail = mail_box.try_alloc(); - TEST_ASSERT_NOT_EQUAL(NULL, mail); - - mail = NULL; - status = mail_box.free(mail); - TEST_ASSERT_EQUAL(osErrorParameter, status); -} - -/** Test get from empty mailbox with timeout set - - Given an empty mailbox - When @a try_get_for is called on the mailbox with timeout of 50ms - Then mailbox returns no data - */ -void test_get_empty_timeout() -{ - Mail mail_box; - Timer timer; - - timer.start(); - uint32_t *mail = mail_box.try_get_for(50ms); - TEST_ASSERT_DURATION_WITHIN(5ms, 50ms, timer.elapsed_time()); - TEST_ASSERT_NULL(mail); -} - -/** Test get from empty mailbox with 0 timeout - - Given an empty mailbox - When @a try_get is called on the mailbox - Then mailbox returns no data - */ -void test_get_empty_no_timeout() -{ - Mail mail_box; - - uint32_t *mail = mail_box.try_get(); - TEST_ASSERT_NULL(mail); -} - -/** Test mail order - - Given an mailbox for uint32_t values - Then allocate two mails and put them in to mailbox - When call @a get it returns previously put mails - Then mails should be in the same order as put - */ -void test_order(void) -{ - osStatus status; - Mail mail_box; - const int32_t TEST_VAL1 = 123; - const int32_t TEST_VAL2 = 456; - - int32_t *mail1 = mail_box.try_alloc(); - TEST_ASSERT_NOT_EQUAL(NULL, mail1); - - *mail1 = TEST_VAL1; - mail_box.put(mail1); - - int32_t *mail2 = mail_box.try_alloc(); - TEST_ASSERT_NOT_EQUAL(NULL, mail2); - - *mail2 = TEST_VAL2; - mail_box.put(mail2); - - - mail1 = mail_box.try_get_for(Kernel::wait_for_u32_forever); - TEST_ASSERT_NOT_NULL(mail1); - TEST_ASSERT_EQUAL(TEST_VAL1, *mail1); - - mail2 = mail_box.try_get_for(Kernel::wait_for_u32_forever); - TEST_ASSERT_NOT_NULL(mail2); - TEST_ASSERT_EQUAL(TEST_VAL2, *mail2); - - - status = mail_box.free(mail1); - TEST_ASSERT_EQUAL(osOK, status); - - status = mail_box.free(mail2); - TEST_ASSERT_EQUAL(osOK, status); -} - -/** Test Mail box max size limit - - Given an Mail box with max size of 4 elements - When call @a try_alloc four times it returns memory blocks - Then the memory blocks should be valid - When call @a try_alloc one more time it returns memory blocks - Then the memory blocks should be not valid (NULL - no memory available) - */ -void test_max_size() -{ - osStatus status; - Mail mail_box; - - // 1 OK - uint32_t *mail1 = mail_box.try_alloc(); - TEST_ASSERT_NOT_EQUAL(NULL, mail1); - - // 2 OK - uint32_t *mail2 = mail_box.try_alloc(); - TEST_ASSERT_NOT_EQUAL(NULL, mail2); - - // 3 OK - uint32_t *mail3 = mail_box.try_alloc(); - TEST_ASSERT_NOT_EQUAL(NULL, mail3); - - // 4 OK - uint32_t *mail4 = mail_box.try_alloc(); - TEST_ASSERT_NOT_EQUAL(NULL, mail4); - - // 5 KO - uint32_t *mail5 = mail_box.try_alloc(); - TEST_ASSERT_EQUAL(NULL, mail5); - - - status = mail_box.free(mail1); - TEST_ASSERT_EQUAL(osOK, status); - - status = mail_box.free(mail2); - TEST_ASSERT_EQUAL(osOK, status); - - status = mail_box.free(mail3); - TEST_ASSERT_EQUAL(osOK, status); - - status = mail_box.free(mail4); - TEST_ASSERT_EQUAL(osOK, status); -} - -/** Test mailbox of T type data - - Given an mailbox with T memory block type - When allocate/put/get/free memory block - Then all operations should succeed - */ -template -void test_data_type(void) -{ - osStatus status; - Mail mail_box; - const T TEST_VAL = 123; - - T *mail = mail_box.try_alloc(); - TEST_ASSERT_NOT_EQUAL(NULL, mail); - - *mail = TEST_VAL; - mail_box.put(mail); - - mail = mail_box.try_get_for(Kernel::wait_for_u32_forever); - TEST_ASSERT_NOT_NULL(mail); - TEST_ASSERT_EQUAL(TEST_VAL, *mail); - - - status = mail_box.free(mail); - TEST_ASSERT_EQUAL(osOK, status); -} - -/** Test try_calloc - memory block allocation with resetting - - Given an empty Mail box - When call @a try_calloc it returns allocated memory block - Then the memory block should be valid and filled with zeros - */ -void test_calloc() -{ - Mail mail_box; - - uint32_t *mail = mail_box.try_calloc(); - TEST_ASSERT_NOT_EQUAL(NULL, mail); - TEST_ASSERT_EQUAL(0, *mail); -} - -/** Test mail empty - - Given a mail of uint32_t data - before data is inserted the mail should be empty - after data is inserted the mail shouldn't be empty - */ -void test_mail_empty() -{ - Mail m; - - mail_t *mail = m.try_alloc(); - - TEST_ASSERT_EQUAL(true, m.empty()); - - m.put(mail); - - TEST_ASSERT_EQUAL(false, m.empty()); -} - -/** Test mail empty - - Given a mail of uint32_t data with size of 1 - before data is inserted the mail shouldn't be full - after data is inserted the mail should be full - */ -void test_mail_full() -{ - Mail m; - - mail_t *mail = m.try_alloc(); - - TEST_ASSERT_EQUAL(false, m.full()); - - m.put(mail); - - TEST_ASSERT_EQUAL(true, m.full()); -} - -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(10, "default_auto"); - return verbose_test_setup_handler(number_of_cases); -} - -Case cases[] = { - Case("Test try_calloc", test_calloc), - Case("Test message type uint8", test_data_type), - Case("Test message type uint16", test_data_type), - Case("Test message type uint32", test_data_type), - Case("Test mailbox max size", test_max_size), - Case("Test message send order", test_order), - Case("Test get with timeout on empty mailbox", test_get_empty_timeout), - Case("Test get without timeout on empty mailbox", test_get_empty_no_timeout), - Case("Test null message free", test_free_null), - Case("Test invalid message free", test_free_wrong), - Case("Test message send/receive single thread and order", test_single_thread_order), - Case("Test message send/receive multi-thread and per thread order", test_multi_thread_order), - Case("Test message send/receive multi-thread, multi-Mail and per thread order", test_multi_thread_multi_mail_order), - Case("Test mail empty", test_mail_empty), - Case("Test mail full", test_mail_full) -}; - -Specification specification(test_setup, cases); - -int main() -{ - return !Harness::run(specification); -} - -#endif // !DEVICE_USTICKER -#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) diff --git a/TESTS/mbedmicro-rtos-mbed/malloc/main.cpp b/TESTS/mbedmicro-rtos-mbed/malloc/main.cpp deleted file mode 100644 index bc8678f..0000000 --- a/TESTS/mbedmicro-rtos-mbed/malloc/main.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "utest/utest.h" -#include "unity/unity.h" - - -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] test not supported -#else - -using utest::v1::Case; - -extern uint32_t mbed_heap_size; -static const int test_timeout = 25; -volatile bool thread_should_continue = true; -#define NUM_THREADS 4 -#define THREAD_MALLOC_SIZE 100 - -#if defined(__CORTEX_A9) -#define THREAD_STACK_SIZE 512 -#elif defined(__CORTEX_M23) || defined(__CORTEX_M33) -#define THREAD_STACK_SIZE 512 -#elif defined(TARGET_ARM_FM) -#define THREAD_STACK_SIZE 512 -#elif defined(TARGET_CY8CKIT_062_WIFI_BT_PSA) -#define THREAD_STACK_SIZE 512 -#else -#define THREAD_STACK_SIZE 256 -#endif - - -void task_using_malloc(void) -{ - void *data = NULL; - - while (thread_should_continue) { - // Repeatedly allocate and free memory - data = malloc(THREAD_MALLOC_SIZE); - TEST_ASSERT_NOT_NULL(data); - - // test whole allocated memory - memset(data, 0, THREAD_MALLOC_SIZE); - - free(data); - } -} -/** Test for multithreaded heap allocations - - Given multiple threads are started in parallel - When each of the threads allocate memory - Then the memory allocation succeed and @a malloc return valid memory - */ -#if defined(MBED_CONF_RTOS_PRESENT) -void test_multithread_allocation(void) -{ - // static stack for threads to reduce heap usage on devices with small RAM - // and eliminate run out of heap memory problem - uint8_t stack[NUM_THREADS][THREAD_STACK_SIZE]; - - bool thread_alloc_failure = false; - Thread *thread_list[NUM_THREADS]; - int test_time = 20; - - // Allocate threads for the test - for (int i = 0; i < NUM_THREADS; i++) { - thread_list[i] = new Thread(osPriorityNormal, THREAD_STACK_SIZE, stack[i]); - if (NULL == thread_list[i]) { - thread_alloc_failure = true; - } else { - thread_list[i]->start(task_using_malloc); - } - } - - // Give the test time to run - while (test_time--) { - ThisThread::sleep_for(1000); - } - - // Join and delete all threads - thread_should_continue = false; - for (int i = 0; i < NUM_THREADS; i++) { - if (NULL != thread_list[i]) { - thread_list[i]->join(); - delete thread_list[i]; - thread_list[i] = NULL; - } - } - TEST_ASSERT_FALSE(thread_alloc_failure); -} -#endif - -/** Test for multiple heap alloc and free calls */ -#define ALLOC_ARRAY_SIZE 100 -#define ALLOC_LOOP 20 -#define SIZE_INCREMENTS 1023 -#define SIZE_MODULO 31 - -void test_alloc_and_free(void) -{ - void *array[ALLOC_ARRAY_SIZE]; - void *data = NULL; - long total_allocated = 0; - int count = 0; - int size = SIZE_INCREMENTS; - int loop = ALLOC_LOOP; - while (loop) { - data = count < ALLOC_ARRAY_SIZE ? malloc(size) : NULL; - if (NULL != data) { - array[count++] = data; - memset((void *)data, 0xdeadbeef, size); - total_allocated += size; - size += SIZE_INCREMENTS; - if (size > 10000) { - size %= SIZE_MODULO; - } - } else { - for (int i = 0; i < count; i++) { - free(array[i]); - array[i] = NULL; - } - loop--; - printf("Total size dynamically allocated: %luB\n", total_allocated); - total_allocated = 0; - count = 0; - continue; - } - } -} - -/** Test for large heap allocation - - Given a heap of size mbed_heap_size - When try to allocate memory of size mbed_heap_size/5 (20% of whole heap) - Then the memory is allocated and @a malloc return valid memory - */ -void test_big_allocation(void) -{ - const uint32_t alloc_size = mbed_heap_size / 5; - void *data = NULL; - - data = malloc(alloc_size); - TEST_ASSERT_NOT_NULL(data); - - // test whole allocated memory - memset(data, 0, alloc_size); - - free(data); -} - -/** Test if allocation of zero size does not cause any undefined behaviour - - Given a heap - When try to allocate memory of size 0 - Then the return value of @a malloc depends on the particular library implementation - (NULL or smallest possible allocation) and no undefined behaviour happens - - @note If allocation size is zero, the return value depends on the particular library implementation - (it may or may not be a null pointer), but the returned pointer shall not be dereferenced - */ -void test_zero_allocation(void) -{ - void *data = NULL; - - data = malloc(0); - if (data != NULL) { - free(data); - } - TEST_ASSERT_MESSAGE(true, "malloc(0) succeed - no undefined behaviour happens"); -} - -/** Test if free on NULL pointer does not cause any undefined behaviour - - Given a NULL pointer - When try to free it - Then the function @a free does nothing and no undefined behaviour happens - */ -void test_null_free(void) -{ - void *data = NULL; - free(data); - - TEST_ASSERT_MESSAGE(true, "free(NULL) succeed - no undefined behaviour happens"); -} - -// Test cases -Case cases[] = { - Case("Test 0 size allocation", test_zero_allocation), - Case("Test NULL pointer free", test_null_free), -#if defined(MBED_CONF_RTOS_PRESENT) - Case("Test multithreaded allocations", test_multithread_allocation), -#endif - Case("Test large allocation", test_big_allocation), - Case("Test multiple alloc and free calls", test_alloc_and_free) -}; - -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(test_timeout, "default_auto"); - return utest::v1::greentea_test_setup_handler(number_of_cases); -} - -utest::v1::Specification specification(greentea_test_setup, cases); - -int main() -{ - return !utest::v1::Harness::run(specification); -} - -#endif // defined(MBED_RTOS_SINGLE_THREAD) || !DEVICE_USTICKER diff --git a/TESTS/mbedmicro-rtos-mbed/mutex/main.cpp b/TESTS/mbedmicro-rtos-mbed/mutex/main.cpp deleted file mode 100644 index 52705ed..0000000 --- a/TESTS/mbedmicro-rtos-mbed/mutex/main.cpp +++ /dev/null @@ -1,323 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) -#error [NOT_SUPPORTED] Mutex test cases require RTOS with multithread to run -#else - -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. -#else - -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity.h" -#include "utest.h" -#include "rtos.h" -#include - -#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ - do { \ - using ct = std::common_type_t; \ - TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ - } while (0) - - -using namespace utest::v1; -using namespace std::chrono; - -#if defined(__CORTEX_M23) || defined(__CORTEX_M33) -#define TEST_STACK_SIZE 768 -#else -#define TEST_STACK_SIZE 512 -#endif - -#define TEST_LONG_DELAY 20ms -#define TEST_DELAY 10ms -#define SIGNALS_TO_EMIT 100 - -Mutex stdio_mutex; - -volatile int change_counter = 0; -volatile bool changing_counter = false; -volatile bool mutex_defect = false; - -bool manipulate_protected_zone(const Kernel::Clock::duration thread_delay) -{ - bool result = true; - osStatus stat; - - stdio_mutex.lock(); - - core_util_critical_section_enter(); - if (changing_counter == true) { - result = false; - mutex_defect = true; - } - changing_counter = true; - - change_counter++; - core_util_critical_section_exit(); - - ThisThread::sleep_for(thread_delay); - - core_util_critical_section_enter(); - changing_counter = false; - core_util_critical_section_exit(); - - stdio_mutex.unlock(); - - return result; -} - -void test_thread(Kernel::Clock::duration const *thread_delay) -{ - while (true) { - manipulate_protected_zone(*thread_delay); - } -} - -/** Test multiple thread - - Given 3 threads started with different delays and a section protected with a mutex - when each thread runs it tries to lock the mutex - then no more than one thread should be able to access protected region -*/ -void test_multiple_threads(void) -{ - const Kernel::Clock::duration t1_delay = TEST_DELAY * 1; - const Kernel::Clock::duration t2_delay = TEST_DELAY * 2; - const Kernel::Clock::duration t3_delay = TEST_DELAY * 3; - - Thread t2(osPriorityNormal, TEST_STACK_SIZE); - Thread t3(osPriorityNormal, TEST_STACK_SIZE); - - t2.start(callback(test_thread, &t2_delay)); - t3.start(callback(test_thread, &t3_delay)); - - while (true) { - // Thread 1 action - ThisThread::sleep_for(t1_delay); - manipulate_protected_zone(t1_delay); - - core_util_critical_section_enter(); - if (change_counter >= SIGNALS_TO_EMIT or mutex_defect == true) { - core_util_critical_section_exit(); - t2.terminate(); - t3.terminate(); - break; - } - core_util_critical_section_exit(); - } - - TEST_ASSERT_EQUAL(false, mutex_defect); -} - -void test_dual_thread_nolock_lock_thread(Mutex *mutex) -{ - osStatus stat; - mutex->lock(); - - mutex->unlock(); -} - -void test_dual_thread_nolock_trylock_thread(Mutex *mutex) -{ - bool stat_b = mutex->trylock(); - TEST_ASSERT_EQUAL(true, stat_b); - - mutex->unlock(); -} - -/** Test dual thread no-lock - - Test dual thread second thread lock - Given two threads A & B and a mutex - When thread A creates a mutex and starts thread B - and thread B calls @a lock and @a unlock - Then @a lock and @a unlock operations are successfully performed. - - Test dual thread second thread trylock - Given two threads A & B and a mutex - When thread A creates a mutex and starts thread B - and thread B calls @a trylock and @a unlock - Then @a trylock and @a unlock operations are successfully performed. -*/ -template -void test_dual_thread_nolock(void) -{ - Mutex mutex; - Thread thread(osPriorityNormal, TEST_STACK_SIZE); - - thread.start(callback(F, &mutex)); - - ThisThread::sleep_for(TEST_DELAY); -} - -void test_dual_thread_lock_unlock_thread(Mutex *mutex) -{ - mutex->lock(); -} - -/** Test dual thread lock unlock - - Given two threads and a lock - When thread A locks the lock and starts thread B - and thread B calls @a lock on the mutex - Then thread B waits for thread A to unlock the lock - When thread A calls @a unlock on the mutex - Then thread B acquires the lock -*/ -void test_dual_thread_lock_unlock(void) -{ - Mutex mutex; - osStatus stat; - Thread thread(osPriorityNormal, TEST_STACK_SIZE); - - mutex.lock(); - - thread.start(callback(test_dual_thread_lock_unlock_thread, &mutex)); - - mutex.unlock(); - - ThisThread::sleep_for(TEST_DELAY); -} - -void test_dual_thread_lock_trylock_thread(Mutex *mutex) -{ - bool stat = mutex->trylock(); - TEST_ASSERT_EQUAL(false, stat); -} - -void test_dual_thread_lock_lock_thread(Mutex *mutex) -{ - Timer timer; - timer.start(); - - bool stat = mutex->trylock_for(TEST_DELAY); - TEST_ASSERT_EQUAL(false, stat); - TEST_ASSERT_DURATION_WITHIN(5ms, TEST_DELAY, timer.elapsed_time()); -} - -/** Test dual thread lock - - Test dual thread lock locked - Given a mutex and two threads A & B - When thread A calls @a lock and starts thread B - and thread B calls @a lock with 500ms timeout - Then thread B waits 500ms and timeouts - - Test dual thread trylock locked - Given a mutex and two threads A & B - When thread A calls @a lock and starts thread B - Then thread B calls @a trylock - and thread B fails to acquire the lock -*/ -template -void test_dual_thread_lock(void) -{ - Mutex mutex; - osStatus stat; - Thread thread(osPriorityNormal, TEST_STACK_SIZE); - - mutex.lock(); - - thread.start(callback(F, &mutex)); - - ThisThread::sleep_for(TEST_LONG_DELAY); - - mutex.unlock(); -} - -/** Test single thread lock recursive - - Given a mutex and a single running thread - When thread calls @a lock twice and @a unlock twice on the mutex - Then @a lock and @a unlock operations are successfully performed. -*/ -void test_single_thread_lock_recursive(void) -{ - Mutex mutex; - osStatus stat; - - mutex.lock(); - - mutex.lock(); - - mutex.unlock(); - - mutex.unlock(); -} - -/** Test single thread trylock - - Given a mutex and a single running thread - When thread calls @a trylock and @a unlock on the mutex - Then @a trylock and @a unlock operations are successfully performed. -*/ -void test_single_thread_trylock(void) -{ - Mutex mutex; - - bool stat_b = mutex.trylock(); - TEST_ASSERT_EQUAL(true, stat_b); - - mutex.unlock(); -} - -/** Test single thread lock - - Given a mutex and a single running thread - When thread calls @a lock and @a unlock on the mutex - Then @a lock and @a unlock operations are successfully performed. -*/ -void test_single_thread_lock(void) -{ - Mutex mutex; - osStatus stat; - - mutex.lock(); - - mutex.unlock(); -} - -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(10, "default_auto"); - return verbose_test_setup_handler(number_of_cases); -} - -Case cases[] = { - Case("Test single thread lock", test_single_thread_lock), - Case("Test single thread trylock", test_single_thread_trylock), - Case("Test single thread lock recursive", test_single_thread_lock_recursive), - Case("Test dual thread lock locked", test_dual_thread_lock), - Case("Test dual thread trylock locked", test_dual_thread_lock), - Case("Test dual thread lock unlock", test_dual_thread_lock_unlock), - Case("Test dual thread second thread lock", test_dual_thread_nolock), - Case("Test dual thread second thread trylock", test_dual_thread_nolock), - Case("Test multiple thread", test_multiple_threads), -}; - -Specification specification(test_setup, cases); - -int main() -{ - return !Harness::run(specification); -} - -#endif // !DEVICE_USTICKER -#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) diff --git a/TESTS/mbedmicro-rtos-mbed/queue/main.cpp b/TESTS/mbedmicro-rtos-mbed/queue/main.cpp deleted file mode 100644 index 822fa48..0000000 --- a/TESTS/mbedmicro-rtos-mbed/queue/main.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) -#error [NOT_SUPPORTED] Queue test cases require RTOS with multithread to run -#else - -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. -#else - -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity.h" -#include "utest.h" -#include "rtos.h" - -using namespace utest::v1; -using namespace std::chrono; - -#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ - do { \ - using ct = std::common_type_t; \ - TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ - } while (0) - -#define THREAD_STACK_SIZE 512 -#define TEST_TIMEOUT 50ms - -static uint32_t msg; -static uint32_t msg2; - -void thread_put_uint_msg(Queue *q) -{ - ThisThread::sleep_for(TEST_TIMEOUT); - bool stat = q->try_put(&msg); - TEST_ASSERT_TRUE(stat); -} - -void thread_get_uint_msg(Queue *q) -{ - ThisThread::sleep_for(TEST_TIMEOUT); - uint32_t *v; - bool stat = q->try_get_for(Kernel::wait_for_u32_forever, &v); - TEST_ASSERT_TRUE(stat) - TEST_ASSERT_EQUAL(&msg, v); -} - -/** Test pass msg - - Given a queue for uint32_t messages with one slot - When a uin32_t value is inserted into the queue - and a message is extracted from the queue - Then the extracted message is the same as previously inserted message - */ -void test_pass() -{ - Queue q; - bool stat = q.try_put(&msg); - TEST_ASSERT_TRUE(stat) - - uint32_t *v; - stat = q.try_get_for(Kernel::wait_for_u32_forever, &v); - TEST_ASSERT_TRUE(stat) - TEST_ASSERT_EQUAL(&msg, v); -} - -/** Test pass msg twice - - Given a queue for uint32_t messages with one slot - When a uin32_t value is inserted into the queue - and a message is extracted from the queue - and the procedure is repeated with different message - Then the extracted message is the same as previously inserted message for both iterations - - */ -void test_pass_twice() -{ - Queue q; - bool stat = q.try_put(&msg); - TEST_ASSERT_TRUE(stat); - - uint32_t *v; - stat = q.try_get_for(Kernel::wait_for_u32_forever, &v); - TEST_ASSERT_TRUE(stat); - TEST_ASSERT_EQUAL(&msg, v); - - stat = q.try_put(&msg2); - TEST_ASSERT_TRUE(stat); - - stat = q.try_get_for(Kernel::wait_for_u32_forever, &v); - TEST_ASSERT_TRUE(stat); - TEST_ASSERT_EQUAL(&msg2, v); -} - -/** Test get from empty queue - - Given an empty queue for uint32_t values - When @a get is called on the queue with timeout of 0 - Then queue returns status of false - */ -void test_get_empty_no_timeout() -{ - Queue q; - - uint32_t *v; - bool stat = q.try_get(&v); - TEST_ASSERT_FALSE(stat); -} - -/** Test get from empty queue with timeout - - Given an empty queue for uint32_t values - When @a get is called on the queue with timeout of 50ms - Then queue returns status of osEventTimeout after about 50ms wait - */ -void test_get_empty_timeout() -{ - Queue q; - Timer timer; - timer.start(); - - uint32_t *v; - bool stat = q.try_get_for(50ms, &v); - TEST_ASSERT_FALSE(stat); - TEST_ASSERT_DURATION_WITHIN(5ms, 50ms, timer.elapsed_time()); -} - -/** Test get empty wait forever - - Given a two threads A & B and a queue for uint32_t values - When thread A calls @a get on an empty queue with osWaitForever - Then the thread A waits for a message to appear in the queue - When thread B puts a message in the queue - Then thread A wakes up and receives it - */ -void test_get_empty_wait_forever() -{ - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - Queue q; - - t.start(callback(thread_put_uint_msg, &q)); - - Timer timer; - timer.start(); - - uint32_t *v; - bool stat = q.try_get_for(Kernel::wait_for_u32_forever, &v); - TEST_ASSERT_TRUE(stat); - TEST_ASSERT_EQUAL(&msg, v); - TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time()); -} - -/** Test put full no timeout - * - * Given a queue with one slot for uint32_t data - * When a thread tries to insert two messages - * Then first operation succeeds and second fails - */ -void test_put_full_no_timeout() -{ - Queue q; - - bool stat = q.try_put(&msg); - TEST_ASSERT_TRUE(stat); - - stat = q.try_put(&msg); - TEST_ASSERT_FALSE(stat); -} - -/** Test put full timeout - * - * Given a queue with one slot for uint32_t data - * When a thread tries to insert two messages with @ TEST_TIMEOUT timeout - * Then first operation succeeds and second fails - */ -void test_put_full_timeout() -{ - Queue q; - - bool stat = q.try_put_for(TEST_TIMEOUT, &msg); - TEST_ASSERT_TRUE(stat); - - Timer timer; - timer.start(); - - stat = q.try_put_for(TEST_TIMEOUT, &msg); - TEST_ASSERT_FALSE(stat); - TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time()); -} - -/** Test put full wait forever - * - * Given two threads A & B and a queue with one slot for uint32_t data - * When thread A puts a message to the queue and tries to put second one with @a Kernel::wait_for_u32_forever timeout - * Then thread waits for a slot to become empty in the queue - * When thread B takes one message out of the queue - * Then thread A successfully inserts message into the queue - */ -void test_put_full_waitforever() -{ - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - Queue q; - - t.start(callback(thread_get_uint_msg, &q)); - - bool stat = q.try_put(&msg); - TEST_ASSERT_TRUE(stat); - - Timer timer; - timer.start(); - stat = q.try_put_for(Kernel::wait_for_u32_forever, &msg); - TEST_ASSERT_TRUE(stat); - TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time()); - - t.join(); -} - -/** Test message ordering - - Given a queue of uint32_t data - When two messages are inserted with equal priority - Then messages should be returned in the exact order they were inserted - */ -void test_msg_order() -{ - Queue q; - - bool stat = q.try_put_for(TEST_TIMEOUT, &msg); - TEST_ASSERT_TRUE(stat); - - stat = q.try_put_for(TEST_TIMEOUT, &msg2); - TEST_ASSERT_TRUE(stat); - - uint32_t *v; - stat = q.try_get(&v); - TEST_ASSERT_TRUE(stat); - TEST_ASSERT_EQUAL(&msg, v); - - stat = q.try_get(&v); - TEST_ASSERT_TRUE(stat); - TEST_ASSERT_EQUAL(&msg2, v); -} - -/** Test message priority - - Given a queue of uint32_t data - When two messages are inserted with ascending priority - Then messages should be returned in descending priority order - */ -void test_msg_prio() -{ - Queue q; - - bool stat = q.try_put_for(TEST_TIMEOUT, &msg, 0); - TEST_ASSERT_TRUE(stat); - - stat = q.try_put_for(TEST_TIMEOUT, &msg2, 1); - TEST_ASSERT_TRUE(stat); - - uint32_t *v; - stat = q.try_get(&v); - TEST_ASSERT_TRUE(stat); - TEST_ASSERT_EQUAL(&msg2, v); - - stat = q.try_get(&v); - TEST_ASSERT_TRUE(stat); - TEST_ASSERT_EQUAL(&msg, v); -} - -/** Test queue empty - - Given a queue of uint32_t data - before data is inserted the queue should be empty - after data is inserted the queue shouldn't be empty - */ -void test_queue_empty() -{ - Queue q; - - TEST_ASSERT_TRUE(q.empty()); - - q.try_put_for(TEST_TIMEOUT, &msg, 1); - - TEST_ASSERT_FALSE(q.empty()); -} - -/** Test queue empty - - Given a queue of uint32_t data with size of 1 - before data is inserted the queue shouldn't be full - after data is inserted the queue should be full - */ -void test_queue_full() -{ - Queue q; - - TEST_ASSERT_FALSE(q.full()); - - q.try_put_for(TEST_TIMEOUT, &msg, 1); - - TEST_ASSERT_TRUE(q.full()); -} - -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(5, "default_auto"); - return verbose_test_setup_handler(number_of_cases); -} - -Case cases[] = { - Case("Test pass msg", test_pass), - Case("Test pass msg twice", test_pass_twice), - Case("Test get from empty queue no timeout", test_get_empty_no_timeout), - Case("Test get from empty queue timeout", test_get_empty_timeout), - Case("Test get empty wait forever", test_get_empty_wait_forever), - Case("Test put full no timeout", test_put_full_no_timeout), - Case("Test put full timeout", test_put_full_timeout), - Case("Test put full wait forever", test_put_full_waitforever), - Case("Test message ordering", test_msg_order), - Case("Test message priority", test_msg_prio), - Case("Test queue empty", test_queue_empty), - Case("Test queue full", test_queue_full) -}; - -Specification specification(test_setup, cases); - -int main() -{ - return !Harness::run(specification); -} - -#endif // !DEVICE_USTICKER -#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) diff --git a/TESTS/mbedmicro-rtos-mbed/semaphore/main.cpp b/TESTS/mbedmicro-rtos-mbed/semaphore/main.cpp deleted file mode 100644 index efa02be..0000000 --- a/TESTS/mbedmicro-rtos-mbed/semaphore/main.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. -#else - -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity.h" -#include "utest.h" -#include "rtos.h" - -using namespace utest::v1; -using namespace std::chrono; - -struct test_data { - Semaphore *sem; - uint32_t data; -}; - -#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ - do { \ - using ct = std::common_type_t; \ - TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ - } while (0) - -#if defined(MBED_CONF_RTOS_PRESENT) -#define THREAD_DELAY 30ms -#define SEMAPHORE_SLOTS 2 -#define SEM_CHANGES 100 -#define SHORT_WAIT 5ms - -#define THREAD_STACK_SIZE 320 /* larger stack cause out of heap memory on some 16kB RAM boards in multi thread test*/ - -Semaphore two_slots(SEMAPHORE_SLOTS); - -volatile int change_counter = 0; -volatile int sem_counter = 0; -volatile bool sem_defect = false; - -void test_thread(rtos::Kernel::Clock::duration const *delay) -{ - const auto thread_delay = *delay; - while (true) { - two_slots.acquire(); - sem_counter++; - const bool sem_lock_failed = sem_counter > SEMAPHORE_SLOTS; - if (sem_lock_failed) { - sem_defect = true; - } - ThisThread::sleep_for(thread_delay); - sem_counter--; - change_counter++; - two_slots.release(); - } -} - -/* Test multiple threads - - Given 3 threads started with different delays and a semaphore with 2 tokens - when each thread runs it tries to acquire a token - then no more than two threads should be able to access protected region -*/ -void test_multi() -{ - const rtos::Kernel::Clock::duration t1_delay = THREAD_DELAY * 1; - const rtos::Kernel::Clock::duration t2_delay = THREAD_DELAY * 2; - const rtos::Kernel::Clock::duration t3_delay = THREAD_DELAY * 3; - - Thread t1(osPriorityNormal, THREAD_STACK_SIZE); - Thread t2(osPriorityNormal, THREAD_STACK_SIZE); - Thread t3(osPriorityNormal, THREAD_STACK_SIZE); - - t1.start(callback(test_thread, &t1_delay)); - t2.start(callback(test_thread, &t2_delay)); - t3.start(callback(test_thread, &t3_delay)); - - while (true) { - if (change_counter >= SEM_CHANGES or sem_defect == true) { - t1.terminate(); - t2.terminate(); - t3.terminate(); - break; - } - } -} - -void single_thread(struct test_data *data) -{ - data->sem->acquire(); - data->data++; -} - -/** Test single thread - - Given a two threads A & B and a semaphore (with count of 0) and a counter (equals to 0) - when thread B calls @a acquire - then thread B waits for a token to become available - then the counter is equal to 0 - when thread A calls @a release on the semaphore - then thread B acquires a token and increments the counter - then the counter equals to 1 - */ -void test_single_thread() -{ - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - Semaphore sem(0); - struct test_data data; - osStatus res; - - data.sem = &sem; - data.data = 0; - - res = t.start(callback(single_thread, &data)); - TEST_ASSERT_EQUAL(osOK, res); - ThisThread::sleep_for(SHORT_WAIT); - - TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state()); - TEST_ASSERT_EQUAL(0, data.data); - - res = sem.release(); - TEST_ASSERT_EQUAL(osOK, res); - - ThisThread::sleep_for(SHORT_WAIT); - - TEST_ASSERT_EQUAL(1, data.data); - - t.join(); -} - -void timeout_thread(Semaphore *sem) -{ - bool acquired = sem->try_acquire_for(30ms); - TEST_ASSERT_FALSE(acquired); -} - -/** Test timeout - - Given thread and a semaphore with no tokens available - when a thread calls @a try_acquire_for with a timeout of 30ms - then the thread is blocked for up to 30ms and timeouts after. - */ -void test_timeout() -{ - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - Semaphore sem(0); - osStatus res; - - Timer timer; - timer.start(); - res = t.start(callback(timeout_thread, &sem)); - TEST_ASSERT_EQUAL(osOK, res); - ThisThread::sleep_for(SHORT_WAIT); - - TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state()); - - t.join(); - TEST_ASSERT_DURATION_WITHIN(5ms, 30ms, timer.elapsed_time()); -} -#endif - -void test_ticker_release(struct test_data *data) -{ - osStatus res; - data->data++; - res = data->sem->release(); - TEST_ASSERT_EQUAL(osOK, res); -} - -/** Test semaphore acquire - - Given a semaphore with no tokens available and ticker with the callback registered - when the main thread calls @a acquire - then the main thread is blocked - when callback calls @a release on the semaphore - then the main thread is unblocked - */ -void test_semaphore_acquire() -{ - Semaphore sem(0); - struct test_data data; - - data.sem = &sem; - data.data = 0; - Ticker t1; - t1.attach(callback(test_ticker_release, &data), 3ms); - sem.acquire(); - t1.detach(); - - TEST_ASSERT_EQUAL(1, data.data); -} - -void test_ticker_try_acquire(Semaphore *sem) -{ - osStatus res; - res = sem->try_acquire(); - TEST_ASSERT_FALSE(res); -} - -/** Test semaphore try acquire - - Given a semaphore with no tokens available and ticker with the callback registered - when callback tries to acquire the semaphore, it fails. - */ -void test_semaphore_try_acquire() -{ - Semaphore sem(0); - Ticker t1; - t1.attach(callback(test_ticker_try_acquire, &sem), 3ms); - ThisThread::sleep_for(4ms); - t1.detach(); -} - - -/** Test semaphore try timeout - - Given a semaphore with no tokens available - when the main thread calls @a try_acquire_for with 3ms timeout - then the main thread is blocked for 3ms and timeouts after - */ -void test_semaphore_try_timeout() -{ - Semaphore sem(0); - bool res; - res = sem.try_acquire_for(3ms); - TEST_ASSERT_FALSE(res); -} - - -void test_ticker_semaphore_release(struct Semaphore *sem) -{ - osStatus res; - res = sem->release(); - TEST_ASSERT_EQUAL(osOK, res); -} - -/** Test semaphore try acquire timeout - - Given a semaphore with no tokens available and ticker with the callback registered - when the main thread calls @a try_acquire_for with 10ms timeout - then the main thread is blocked for up to 10ms - when callback call @a release on the semaphore after 3ms - then the main thread is unblocked. - */ -void test_semaphore_try_acquire_timeout() -{ - Semaphore sem(0); - bool res; - Ticker t1; - t1.attach(callback(test_ticker_semaphore_release, &sem), 3ms); - res = sem.try_acquire_for(10ms); - t1.detach(); - TEST_ASSERT_TRUE(res); -} - -/** Test no timeouts - -Test 1 token no timeout -Given thread and a semaphore with one token available -when thread calls @a try_acquire with timeout of 0ms -then the thread acquires the token immediately - -Test 0 tokens no timeout -Given thread and a semaphore with no tokens available -when thread calls @a try_acquire with timeout of 0ms -then the thread returns immediately without acquiring a token - */ -template -void test_no_timeout() -{ - Semaphore sem(T); - - Timer timer; - timer.start(); - - bool acquired = sem.try_acquire(); - TEST_ASSERT_EQUAL(T > 0, acquired); - - TEST_ASSERT_DURATION_WITHIN(5ms, 0ms, timer.elapsed_time()); -} - -/** Test multiple tokens wait - - Given a thread and a semaphore initialized with 5 tokens - when thread calls @a try_acquire 6 times in a loop - then the token counts goes to zero - */ -void test_multiple_tokens_wait() -{ - Semaphore sem(5); - - for (int i = 5; i >= 0; i--) { - bool acquired = sem.try_acquire(); - TEST_ASSERT_EQUAL(i > 0, acquired); - } -} - -/** Test multiple tokens release - - Given a thread and a semaphore initialized with zero tokens and max of 5 - when thread calls @a release 6 times on the semaphore - then the token count should be equal to 5 and last release call should fail - */ -void test_multiple_tokens_release() -{ - Semaphore sem(0, 5); - - for (int i = 5; i > 0; i--) { - osStatus stat = sem.release(); - TEST_ASSERT_EQUAL(osOK, stat); - } - osStatus stat = sem.release(); - TEST_ASSERT_EQUAL(osErrorResource, stat); -} - -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(10, "default_auto"); - return verbose_test_setup_handler(number_of_cases); -} - -Case cases[] = { - Case("Test 1 token no timeout", test_no_timeout<1>), - Case("Test 0 tokens no timeout", test_no_timeout<0>), - Case("Test multiple tokens wait", test_multiple_tokens_wait), - Case("Test multiple tokens release", test_multiple_tokens_release), - Case("Test semaphore acquire", test_semaphore_acquire), - Case("Test semaphore try acquire", test_semaphore_try_acquire), - Case("Test semaphore try timeout", test_semaphore_try_timeout), - Case("Test semaphore try acquire timeout", test_semaphore_try_acquire_timeout), -#if defined(MBED_CONF_RTOS_PRESENT) - Case("Test single thread", test_single_thread), - Case("Test timeout", test_timeout), - Case("Test multiple threads", test_multi) -#endif -}; - -Specification specification(test_setup, cases); - -int main() -{ - return !Harness::run(specification); -} - -#endif // !DEVICE_USTICKER diff --git a/TESTS/mbedmicro-rtos-mbed/signals/main.cpp b/TESTS/mbedmicro-rtos-mbed/signals/main.cpp deleted file mode 100644 index 0a4d7ca..0000000 --- a/TESTS/mbedmicro-rtos-mbed/signals/main.cpp +++ /dev/null @@ -1,544 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "utest/utest.h" -#include "unity/unity.h" - -using utest::v1::Case; - -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. -#else - -#define ALL_SIGNALS 0x7fffffff -#define NO_SIGNALS 0x0 - -#define SIGNAL1 0x1 -#define SIGNAL2 0x2 -#define SIGNAL3 0x4 - -template -void run_signal_wait(void) -{ - uint32_t ret = ThisThread::flags_wait_all_for(signals, timeout); - TEST_ASSERT_EQUAL(test_val, ret); -} - -template -void run_clear(void) -{ - int32_t ret = ThisThread::flags_clear(signals); - TEST_ASSERT_EQUAL(test_val, ret); -} - -void run_multiple_wait_clear(int32_t signals1, int32_t signals2, int32_t test_val1, int32_t test_val2) -{ - int32_t ret; - ret = ThisThread::flags_clear(signals1); - TEST_ASSERT_EQUAL(test_val1, ret); - ret = ThisThread::flags_clear(signals2); - TEST_ASSERT_EQUAL(test_val2, ret); -} -/** Validate that ticker callback to flags_clr(NO_SIGNALS) doesn't change main thread signals and return actual signals - - Given main thread and ticker instance with callback registered - When callback calls @a flags_clear(NO_SIGNALS) - then callback @a flags_clear return status should be ALL_SIGNALS indicating that the signal is unchanged - */ -void test_clear_no_signals_with_ticker(void) -{ - Ticker t; - osThreadFlagsSet(ThisThread::get_id(), ALL_SIGNALS); - t.attach_us([&] { run_multiple_wait_clear(NO_SIGNALS, NO_SIGNALS, ALL_SIGNALS, ALL_SIGNALS); }, 3000); -} - -/** Validate all flags_clr clears the signal in one shot - - Given main thread and ticker instance with callback registered - When callback calls @a flags_clear(ALL_SIGNALS) with all possible signals - then callback @a flags_clear(NO_SIGNALS) return status should be NO_SIGNALS(0) indicating all signals cleared correctly - */ -void test_clear_all_with_ticker(void) -{ - Ticker t; - osThreadFlagsSet(ThisThread::get_id(), ALL_SIGNALS); - t.attach_us([&] { run_multiple_wait_clear(ALL_SIGNALS, NO_SIGNALS, ALL_SIGNALS, NO_SIGNALS); }, 3000); -} - -/** Validate if any signals are set on ticker callback - - Given main thread and ticker instance with callback registered - When callback calls @a flags_clear(NO_SIGNALS) - then callback @a flags_clear return status should be NO_SIGNALS(0) indicating no signals set - */ -void test_init_state_with_ticker(void) -{ - Ticker t; - t.attach_us(callback(run_clear), 3000); -} - -/** Validate signal_wait return status if timeout specified - - Given main thread and ticker instance with callback registered - When callback calls @a flags_wait_all_for(signals, timeout) with specified signals and timeout - then callback @a flags_wait_all_for timeout and return 0 indicating no signals set - */ -template -void test_wait_timeout_with_ticker(void) -{ - Ticker t; - t.attach_us(callback(run_signal_wait), 3000); -} - -void run_release_wait_signal_wait_callback() -{ - osThreadFlagsSet(ThisThread::get_id(), ALL_SIGNALS); -} - -/** Validate that call of signal_wait return correctly when thread has all signals already set - - Given main thread and ticker instance with callback registered - When main thread @a flags_wait_all_for(ALL_SIGNALS, osWaitForever), - then main thread is blocked - when a callback calls @a osThreadFlagsSet set ALL_SIGNALS - then the main thread is unblocked from @ flags_wait_all_for with the return of ALL_SIGNALS set - */ -void test_wait_all_already_set_with_ticker(void) -{ - Ticker t; - t.attach_us([&] { run_release_wait_signal_wait_callback(); }, 3000); - uint32_t ret = ThisThread::flags_wait_all_for(ALL_SIGNALS, osWaitForever); - TEST_ASSERT_EQUAL(ALL_SIGNALS, ret); -} - -void run_release_wait_signal_set_callback(int32_t signal, osThreadId_t id) -{ - int32_t ret; - if (signal == 0) { - for (int i = 0; i < 16; i++) { - int32_t signal = 1 << i; - ret = osThreadFlagsSet(id, signal); - } - } else { - ret = osThreadFlagsSet(id, signal); - } -} - -/** Validate if wait_signal accumulate signals and return correctly when all signals set - - Given the main thread and ticker instance with callback registered - When main thread @a flags_wait_all_for, - then main thread is blocked - when a callback calls @a osThreadFlagsSet in a loop to set 16 different signal - then the main thread is unblocked from @ flags_wait_all_for with the return of expected different signals set - */ -void test_wait_all_loop_with_ticker(void) -{ - int32_t ret; - Semaphore sem(0, 1); - Ticker t; - osThreadId_t id = ThisThread::get_id(); - t.attach_us([&] { run_release_wait_signal_set_callback(0, id); }, 4000); - ret = ThisThread::flags_wait_all_for((ALL_SIGNALS & 0xFFFF), osWaitForever, true); - TEST_ASSERT_EQUAL((ALL_SIGNALS & 0xFFFF), ret); -} - -/** Validate if setting same signal twice cause any unwanted behaviour - - Given the main thread and two ticker instance with callback registered - When main thread @a flags_wait_all_for, - then main thread is blocked - when a first callback calls @a osThreadFlagsSet set SIGNAL2 and - second callback calls @a osThreadFlagsSet set SIGNAL1 | SIGNAL2 | SIGNAL3 with SIGNAL2 set again - then the main thread is unblocked from @ flags_wait_all_for with return of expected signals set - */ -void test_set_double_with_ticker(void) -{ - int32_t ret; - Ticker t1, t2; - osThreadId_t id = ThisThread::get_id(); - t1.attach_us([&] { run_release_wait_signal_set_callback(SIGNAL2, id); }, 3000); - t2.attach_us([&] { run_release_wait_signal_set_callback(SIGNAL1 | SIGNAL2 | SIGNAL3, id); }, 4000); - - ret = ThisThread::flags_wait_all_for((SIGNAL1 | SIGNAL2 | SIGNAL3), osWaitForever, true); - TEST_ASSERT_EQUAL(SIGNAL1 | SIGNAL2 | SIGNAL3, ret); -} - -#if defined(MBED_CONF_RTOS_PRESENT) - -#define TEST_STACK_SIZE 512 -#define PROHIBITED_SIGNAL 0x80000000 -#define MAX_FLAG_POS 30 - -struct Sync { - Sync(Semaphore &parent, Semaphore &child): sem_parent(parent), sem_child(child) - {} - - Semaphore &sem_parent; - Semaphore &sem_child; -}; - -template -void run_release_signal_wait(Semaphore *sem) -{ - sem->release(); - uint32_t ret = ThisThread::flags_wait_all_for(signals, timeout); - TEST_ASSERT_EQUAL(test_val, ret); -} - -template -void run_release_wait_signal_wait(Sync *sync) -{ - sync->sem_parent.release(); - sync->sem_child.acquire(); - uint32_t ret = ThisThread::flags_wait_all_for(signals, timeout); - TEST_ASSERT_EQUAL(test_val, ret); -} - -template -void run_wait_clear(Sync *sync) -{ - sync->sem_parent.release(); - sync->sem_child.acquire(); - int32_t ret = ThisThread::flags_clear(signals); - TEST_ASSERT_EQUAL(test_val, ret); -} - -void run_loop_wait_clear(Sync *sync) -{ - int32_t signals = NO_SIGNALS; - for (int i = 0; i <= MAX_FLAG_POS; i++) { - int32_t signal = 1 << i; - signals |= signal; - sync->sem_child.acquire(); - int32_t ret = ThisThread::flags_clear(NO_SIGNALS); - TEST_ASSERT_EQUAL(signals, ret); - sync->sem_parent.release(); - } -} - -template -void run_double_wait_clear(Sync *sync) -{ - int32_t ret; - - sync->sem_parent.release(); - sync->sem_child.acquire(); - ret = ThisThread::flags_clear(signals1); - TEST_ASSERT_EQUAL(test_val1, ret); - - ret = ThisThread::flags_clear(signals2); - TEST_ASSERT_EQUAL(test_val2, ret); -} - -/** Validate that call signal_clr(NO_SIGNALS) doesn't change thread signals and return actual signals - - Given two threads A & B are started, B with all signals already set - When thread B calls @a signal_clr(NO_SIGNALS) - Then thread B @a signal_clr status should be ALL_SIGNALS indicating that thread B state is unchanged - */ -void test_clear_no_signals(void) -{ - Semaphore sem_parent(0, 1); - Semaphore sem_child(0, 1); - Sync sync(sem_parent, sem_child); - - Thread t(osPriorityNormal, TEST_STACK_SIZE); - t.start(callback(run_double_wait_clear, &sync)); - sem_parent.acquire(); - t.flags_set(ALL_SIGNALS); - sem_child.release(); - t.join(); -} - -/** Validate if any signals are set on just created thread - - Given the thread is running - When thread execute @a signal_clr(NO_SIGNALS) - Then thread @a signal_clr return status should be NO_SIGNALS(0) indicating no signals set - */ -void test_init_state(void) -{ - Thread t(osPriorityNormal, TEST_STACK_SIZE); - t.start(callback(run_clear)); - t.join(); -} - -/** Validate all signals set in one shot - - Given two threads A & B are started - When thread A call @a flags_set(ALL_SIGNALS) with all possible signals - Then thread B @a signal_clr(NO_SIGNALS) status should be ALL_SIGNALS indicating all signals set correctly - */ -void test_set_all(void) -{ - int32_t ret; - Semaphore sem_parent(0, 1); - Semaphore sem_child(0, 1); - Sync sync(sem_parent, sem_child); - - Thread t(osPriorityNormal, TEST_STACK_SIZE); - t.start(callback(run_wait_clear, &sync)); - - sem_parent.acquire(); - ret = t.flags_set(ALL_SIGNALS); - TEST_ASSERT_EQUAL(ALL_SIGNALS, ret); - - sem_child.release(); - t.join(); -} - -/** Validate that call flags_set with prohibited signal doesn't change thread signals - - Given two threads A & B are started, B with all signals set - When thread A executes @a flags_set(PROHIBITED_SIGNAL) with prohibited signal - Then thread B @a signal_clr(NO_SIGNALS) status should be ALL_SIGNALS indicating that thread B signals are unchanged - - @note Each signal has up to 31 event flags 0x1, 0x2, 0x4, 0x8, ..., 0x40000000 - Most significant bit is reserved and thereby flag 0x80000000 is prohibited - */ -void test_set_prohibited(void) -{ - int32_t ret; - Semaphore sem_parent(0, 1); - Semaphore sem_child(0, 1); - Sync sync(sem_parent, sem_child); - - Thread t(osPriorityNormal, TEST_STACK_SIZE); - t.start(callback(run_wait_clear, &sync)); - - sem_parent.acquire(); - t.flags_set(ALL_SIGNALS); - -#if !MBED_TRAP_ERRORS_ENABLED - ret = t.flags_set(PROHIBITED_SIGNAL); - TEST_ASSERT_EQUAL(osErrorParameter, ret); -#endif - - sem_child.release(); - t.join(); -} - -/** Validate all signals clear in one shot - - Given two threads A & B are started, B with all signals set - When thread B execute @a signal_clr(ALL_SIGNALS) with all possible signals - Then thread B @a signal_clr(NO_SIGNALS) status should be NO_SIGNALS(0) indicating all signals cleared correctly - */ -void test_clear_all(void) -{ - Semaphore sem_parent(0, 1); - Semaphore sem_child(0, 1); - Sync sync(sem_parent, sem_child); - - Thread t(osPriorityNormal, TEST_STACK_SIZE); - t.start(callback(run_double_wait_clear, &sync)); - sem_parent.acquire(); - t.flags_set(ALL_SIGNALS); - sem_child.release(); - t.join(); -} - -/** Validate all signals set one by one in loop - - Given two threads A & B are started - When thread A executes @a flags_set(signal) in loop with all possible signals - */ -void test_set_all_loop(void) -{ - int32_t ret; - Semaphore sem_parent(0, 1); - Semaphore sem_child(0, 1); - Sync sync(sem_parent, sem_child); - - Thread t(osPriorityNormal, TEST_STACK_SIZE); - t.start(callback(run_loop_wait_clear, &sync)); - - int32_t signals = 0; - for (int i = 0; i <= MAX_FLAG_POS; i++) { - int32_t signal = 1 << i; - - ret = t.flags_set(signal); - signals |= signal; - TEST_ASSERT_EQUAL(signals, ret); - sem_child.release(); - sem_parent.acquire(); - } - t.join(); -} - -/** Validate signal_wait return status if timeout specified - - Given the thread is running - When thread executes @a flags_wait_all_for(signals, timeout) with specified signals and timeout - Then thread @a flags_wait_all_for return should be 0 indicating no flags set - */ -template -void test_wait_timeout(void) -{ - Thread t(osPriorityNormal, TEST_STACK_SIZE); - t.start(callback(run_signal_wait)); - t.join(); -} - -/** Validate that call of signal_wait return correctly when thread has all signals already set - - Given two threads A & B are started, B with all signals already set - When thread B executes @a signal_wait(ALL_SIGNALS, osWaitForever), - Then thread B @a flags_wait_all_for return immediately with ALL_SIGNALS indicating all wait signals was already set - */ -void test_wait_all_already_set(void) -{ - Semaphore sem_parent(0, 1); - Semaphore sem_child(0, 1); - Sync sync(sem_parent, sem_child); - - Thread t(osPriorityNormal, TEST_STACK_SIZE); - t.start(callback(run_release_wait_signal_wait, &sync)); - - sem_parent.acquire(); - TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state()); - t.flags_set(ALL_SIGNALS); - sem_child.release(); - t.join(); -} - -/** Validate if signal_wait return correctly when all signals set - - Given two threads A & B are started and B waiting for a thread flag to be set - When thread A executes @a flags_set(ALL_SIGNALS) with all possible signals - Then thread B @a flags_wait_all_for return is ALL_SIGNALS indicating all wait signals was set - */ -void test_wait_all(void) -{ - Semaphore sem(0, 1); - - Thread t(osPriorityNormal, TEST_STACK_SIZE); - t.start(callback(run_release_signal_wait, &sem)); - - sem.acquire(); - TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); - - t.flags_set(ALL_SIGNALS); - t.join(); -} - -/** Validate if signal_wait accumulate signals and return correctly when all signals set - - Given two threads A & B are started and B waiting for a thread signals to be set - When thread A executes @a flags_set setting all signals in loop - Then thread B @a flags_wait_all_for return is ALL_SIGNALS indicating that all wait signals was set - */ -void test_wait_all_loop(void) -{ - int32_t ret; - Semaphore sem(0, 1); - - Thread t(osPriorityNormal, TEST_STACK_SIZE); - t.start(callback(run_release_signal_wait, &sem)); - - sem.acquire(); - TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); - - for (int i = 0; i < MAX_FLAG_POS; i++) { - int32_t signal = 1 << i; - ret = t.flags_set(signal); - } - ret = t.flags_set(1 << MAX_FLAG_POS); - TEST_ASSERT_EQUAL(NO_SIGNALS, ret); - t.join(); -} - -/** Validate if setting same signal twice cause any unwanted behaviour - - Given two threads A & B are started and B waiting for a thread signals to be set - When thread A executes @a flags_set twice for the same signal - Then thread A @a flags_set status is current signal set - thread B @a flags_wait_all_for return indicates that all wait signals was set - */ -void test_set_double(void) -{ - int32_t ret; - Semaphore sem(0, 1); - - Thread t(osPriorityNormal, TEST_STACK_SIZE); - t.start(callback(run_release_signal_wait < SIGNAL1 | SIGNAL2 | SIGNAL3, osWaitForever, SIGNAL1 | SIGNAL2 | SIGNAL3 >, &sem)); - - sem.acquire(); - TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); - - ret = t.flags_set(SIGNAL1); - TEST_ASSERT_EQUAL(SIGNAL1, ret); - - ret = t.flags_set(SIGNAL2); - TEST_ASSERT_EQUAL(SIGNAL1 | SIGNAL2, ret); - - ret = t.flags_set(SIGNAL2); - TEST_ASSERT_EQUAL(SIGNAL1 | SIGNAL2, ret); - TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); - - ret = t.flags_set(SIGNAL3); - TEST_ASSERT_EQUAL(NO_SIGNALS, ret); - t.join(); -} -#endif - -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(20, "default_auto"); - return utest::v1::verbose_test_setup_handler(number_of_cases); -} - -Case cases[] = { - Case("Validate that ticker callback flags_clear(NO_SIGNALS) doesn't change main thread flags and return actual flags", test_clear_no_signals_with_ticker), - Case("Validate if any flags are set on ticker callback", test_init_state_with_ticker), - Case("Validate all flags clear in one shot using ticker callback", test_clear_all_with_ticker), - Case("Validate ticker callback flags_wait return status if timeout specified: 0[ms] no flags", test_wait_timeout_with_ticker<0, 0, 0>), - Case("Validate ticker callback flags_wait return status if timeout specified: 0[ms] all flags", test_wait_timeout_with_ticker), - Case("Validate ticker callback flags_wait return status if timeout specified: 1[ms] no flags", test_wait_timeout_with_ticker<0, 1, 0>), - Case("Validate ticker callback flags_wait return status if timeout specified: 1[ms] all flags", test_wait_timeout_with_ticker), - Case("Validate that main thread call of flags_wait_all_for return correctly when ticker callback set all flags", test_wait_all_already_set_with_ticker), - Case("Validate if setting same flag twice cause any unwanted behaviour when ticker callbacks set", test_set_double_with_ticker), - Case("Validate if main thread flags_wait_all_for accumulate flags and return correctly when all flags set by ticker callback", test_wait_all_loop_with_ticker), -#if defined(MBED_CONF_RTOS_PRESENT) - Case("Validate that call flags_clear(NO_SIGNALS) doesn't change thread flags and return actual flags", test_clear_no_signals), - Case("Validate if any flags are set on just created thread", test_init_state), - Case("Validate all flags set in one shot", test_set_all), - Case("Validate that call flags_set with prohibited flag doesn't change thread flags", test_set_prohibited), - Case("Validate all flags clear in one shot", test_clear_all), - Case("Validate all flags set one by one in loop", test_set_all_loop), - Case("Validate flags_wait return status if timeout specified: 0[ms] no flags", test_wait_timeout<0, 0, 0>), - Case("Validate flags_wait return status if timeout specified: 0[ms] all flags", test_wait_timeout), - Case("Validate flags_wait return status if timeout specified: 1[ms] no flags", test_wait_timeout<0, 1, 0>), - Case("Validate flags_wait return status if timeout specified: 1[ms] all flags", test_wait_timeout), - Case("Validate that call of flags_wait_all_for return correctly when thread has all flags already set", test_wait_all_already_set), - Case("Validate if flags_wait_all_for return correctly when all flags set", test_wait_all), - Case("Validate if flags_wait_all_for accumulate flags and return correctly when all flags set", test_wait_all_loop), - Case("Validate if setting same flag twice cause any unwanted behaviour", test_set_double), - -#endif -}; - -utest::v1::Specification specification(test_setup, cases); - -int main() -{ - return !utest::v1::Harness::run(specification); -} - -#endif // !DEVICE_USTICKER diff --git a/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp b/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp deleted file mode 100644 index 6999f01..0000000 --- a/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity.h" -#include "utest.h" -#include "ticker_api.h" - -#include "platform/source/SysTimer.h" - -using namespace std::chrono; - -#define TEST_TICKS 42 -#define TEST_TICK_PERIOD std::ratio::type -#define TEST_TICK_DURATION duration -#define DELAY TEST_TICK_DURATION(1) -#define DELAY_DELTA 2500us - -#define TEST_ASSERT_EQUAL_DURATION(expected, actual) \ - do { \ - using ct = std::common_type_t; \ - TEST_ASSERT_EQUAL(ct(expected).count(), ct(actual).count()); \ - } while (0) - -#define TEST_ASSERT_EQUAL_TIME_POINT(expected, actual) \ - do { \ - using ct = std::common_type_t; \ - TEST_ASSERT_EQUAL(ct(expected).time_since_epoch().count(), ct(actual).time_since_epoch().count()); \ - } while (0) - -#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ - do { \ - using ct = std::common_type_t; \ - TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ - } while (0) - - -/* Use a specific delta value for deep sleep, as entry/exit adds up extra latency. - * Use deep sleep latency if defined and add 1ms extra delta */ -#if defined MBED_CONF_TARGET_DEEP_SLEEP_LATENCY -#define DEEP_SLEEP_DELAY_DELTA milliseconds(MBED_CONF_TARGET_DEEP_SLEEP_LATENCY + 1) -#else -#define DEEP_SLEEP_DELAY_DELTA 2500us -#endif - -using namespace utest::v1; -using mbed::internal::SysTimer; - -// The SysTick interrupt must not be set as pending by the test code. -template -class SysTimerTest: public SysTimer { -private: - Semaphore _sem; - virtual void handler() - { - _sem.release(); - SysTimer::handler(); - } - -public: - SysTimerTest() : - SysTimer(), _sem(0, 1) - { - } - - SysTimerTest(const ticker_data_t *data) : - SysTimer(data), _sem(0, 1) - { - } - - virtual ~SysTimerTest() - { - } - - bool sem_try_acquire(rtos::Kernel::Clock::duration_u32 millisec) - { - return _sem.try_acquire_for(millisec); - } - - void sem_acquire() - { - _sem.acquire(); - } -}; - -duration mock_ticker_timestamp; - -void mock_ticker_init() -{ -} - -uint32_t mock_ticker_read() -{ - return mock_ticker_timestamp.count(); -} - -void mock_ticker_disable_interrupt() -{ -} - -void mock_ticker_clear_interrupt() -{ -} - -void mock_ticker_set_interrupt(timestamp_t timestamp) -{ -} - -void mock_ticker_fire_interrupt() -{ -} - -void mock_ticker_free() -{ -} - -const ticker_info_t *mock_ticker_get_info() -{ - static const ticker_info_t mock_ticker_info = { - .frequency = 1000000, - .bits = 32 - }; - return &mock_ticker_info; -} - -ticker_interface_t mock_ticker_interface = { - .init = mock_ticker_init, - .read = mock_ticker_read, - .disable_interrupt = mock_ticker_disable_interrupt, - .clear_interrupt = mock_ticker_clear_interrupt, - .set_interrupt = mock_ticker_set_interrupt, - .fire_interrupt = mock_ticker_fire_interrupt, - .free = mock_ticker_free, - .get_info = mock_ticker_get_info, -}; - -ticker_event_queue_t mock_ticker_event_queue; - -const ticker_data_t mock_ticker_data = { - .interface = &mock_ticker_interface, - .queue = &mock_ticker_event_queue -}; - -void mock_ticker_reset() -{ - mock_ticker_timestamp = 0s; - memset(&mock_ticker_event_queue, 0, sizeof mock_ticker_event_queue); -} - -/** Test tick count is zero upon creation - * - * Given a SysTimer - * When the timer is created - * Then tick count is zero - */ -void test_created_with_zero_tick_count(void) -{ - SysTimerTest st; - using time_point = decltype(st)::time_point; - TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(0)), st.get_tick()); -} - -/** Test tick count is updated correctly - * - * Given a SysTimer - * When the @a suspend and @a resume methods are called immediately after creation - * Then the tick count is not updated - * When @a suspend and @a resume methods are called again after a delay - * Then the tick count is updated - * and the number of ticks incremented is equal TEST_TICKS - * When @a suspend and @a resume methods are called again without a delay - * Then the tick count is not updated - */ -void test_update_tick(void) -{ - mock_ticker_reset(); - SysTimerTest st(&mock_ticker_data); - using time_point = decltype(st)::time_point; - st.set_wake_time(st.get_tick() + TEST_TICK_DURATION(2)); - st.cancel_wake(); - TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(0)), st.get_tick()); - - st.set_wake_time(st.get_tick() + TEST_TICK_DURATION(2)); - mock_ticker_timestamp = TEST_TICK_DURATION(1); - st.cancel_wake(); - TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(1)), st.update_and_get_tick()); - TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(1)), st.get_tick()); - - st.set_wake_time(st.get_tick() + TEST_TICK_DURATION(2)); - st.cancel_wake(); - TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(1)), st.get_tick()); -} - -/** Test get_time returns correct time - * - * Given a SysTimer - * When @a get_time method is called before and after a delay - * Then time difference is equal the delay - */ -void test_get_time(void) -{ - mock_ticker_reset(); - SysTimerTest st(&mock_ticker_data); - auto t1 = st.get_time(); - - mock_ticker_timestamp = TEST_TICK_DURATION(1); - auto t2 = st.get_time(); - TEST_ASSERT_EQUAL_DURATION(TEST_TICK_DURATION(1), t2 - t1); -} - -/** Test cancel_tick - * - * Given a SysTimer with a scheduled tick - * When @a cancel_tick is called before the given number of ticks elapse - * Then the handler is never called - * and the tick count is not incremented - */ -void test_cancel_tick(void) -{ - SysTimerTest st; - using time_point = decltype(st)::time_point; - st.cancel_tick(); - st.start_tick(); - - st.cancel_tick(); - bool acquired = st.sem_try_acquire(duration_cast(TEST_TICK_DURATION(1) + DELAY_DELTA)); - TEST_ASSERT_FALSE(acquired); - TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(0)), st.get_tick()); -} - -/** Test handler called twice - * - * Given a SysTimer with a tick scheduled with delta = TEST_TICKS - * When the handler is called - * Then the tick count is incremented by 1 - * and elapsed time is equal 1000000ULL * TEST_TICKS / OS_TICK_FREQ; - * When more time elapses - * Repeat a second time. - */ -void test_handler_called_twice(void) -{ - SysTimerTest st; - using time_point = decltype(st)::time_point; - auto t1 = st.get_time(); - bool acquired = st.sem_try_acquire(0s); - TEST_ASSERT_FALSE(acquired); - - st.start_tick(); - // Wait in a busy loop to prevent entering sleep or deepsleep modes. - do { - acquired = st.sem_try_acquire(0s); - } while (!acquired); - auto t2 = st.get_time(); - TEST_ASSERT_TRUE(acquired); - TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(1)), st.get_tick()); - TEST_ASSERT_DURATION_WITHIN(DELAY_DELTA, TEST_TICK_DURATION(1), t2 - t1); - - // Wait in a busy loop to prevent entering sleep or deepsleep modes. - do { - acquired = st.sem_try_acquire(0s); - } while (!acquired); - t2 = st.get_time(); - TEST_ASSERT_TRUE(acquired); - TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(2)), st.get_tick()); - TEST_ASSERT_DURATION_WITHIN(DELAY_DELTA, TEST_TICK_DURATION(2), t2 - t1); - st.cancel_tick(); -} - -#if DEVICE_SLEEP -/** Test wake up from sleep - * - * Given a SysTimer with a tick scheduled in the future - * and a core in sleep mode - * When given time elapses - * Then the uC is woken up from sleep - * and the tick handler is called - * and measured time matches requested delay - */ -void test_sleep(void) -{ - Timer timer; - SysTimerTest st; - - sleep_manager_lock_deep_sleep(); - timer.start(); - st.start_tick(); - - TEST_ASSERT_FALSE_MESSAGE(sleep_manager_can_deep_sleep(), "Deep sleep should be disallowed"); - st.sem_acquire(); - - timer.stop(); - st.cancel_tick(); - sleep_manager_unlock_deep_sleep(); - - TEST_ASSERT_DURATION_WITHIN(DELAY_DELTA, TEST_TICK_DURATION(1), timer.elapsed_time()); -} - -#if DEVICE_LPTICKER && !MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER -/** Test wake up from deepsleep - * - * Given a SysTimer with a tick scheduled in the future - * and a core in deepsleep mode - * When given time elapses - * Then the uC is woken up from deepsleep - * and the tick handler is called - * and measured time matches requested delay - */ -void test_deepsleep(void) -{ - /* - * Since deepsleep() may shut down the UART peripheral, we wait for 10ms - * to allow for hardware serial buffers to completely flush. - - * This should be replaced with a better function that checks if the - * hardware buffers are empty. However, such an API does not exist now, - * so we'll use the ThisThread::sleep_for() function for now. - */ - ThisThread::sleep_for(10ms); - // Regular Timer might be disabled during deepsleep. - LowPowerTimer lptimer; - SysTimerTest st; - - lptimer.start(); - st.start_tick(); - TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep_test_check(), "Deep sleep should be allowed"); - st.sem_acquire(); - lptimer.stop(); - st.cancel_tick(); - - TEST_ASSERT_DURATION_WITHIN(DEEP_SLEEP_DELAY_DELTA, TEST_TICK_DURATION(1), lptimer.elapsed_time()); -} -#endif -#endif - -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(15, "default_auto"); - return verbose_test_setup_handler(number_of_cases); -} - -Case cases[] = { - Case("Tick count is zero upon creation", test_created_with_zero_tick_count), - Case("Tick count is updated correctly", test_update_tick), - Case("Time is updated correctly", test_get_time), - Case("Tick can be cancelled", test_cancel_tick), - Case("Handler called twice", test_handler_called_twice), -#if DEVICE_SLEEP - Case("Wake up from sleep", test_sleep), -#if DEVICE_LPTICKER && !MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER - Case("Wake up from deep sleep", test_deepsleep), -#endif -#endif - -}; - -Specification specification(test_setup, cases); - -int main() -{ - return !Harness::run(specification); -} diff --git a/TESTS/mbedmicro-rtos-mbed/threads/SynchronizedIntegral.h b/TESTS/mbedmicro-rtos-mbed/threads/SynchronizedIntegral.h deleted file mode 100644 index 148f189..0000000 --- a/TESTS/mbedmicro-rtos-mbed/threads/SynchronizedIntegral.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2013-2016, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MBEDMICRO_RTOS_MBED_THREADS_SYNCHRONIZED_INTEGRAL -#define MBEDMICRO_RTOS_MBED_THREADS_SYNCHRONIZED_INTEGRAL - -#include -#include - -using mstd::lock_guard; - -/** - * Thread safe wrapper for integral types. - * @tparam T type of the integral - */ -template -class SynchronizedIntegral { -public: - SynchronizedIntegral(T value) : _mutex(), _value(value) { } - - // preincrement operator - T operator++() - { - lock_guard lock(_mutex); - return ++_value; - } - - // predecrement operator - T operator--() - { - lock_guard lock(_mutex); - return --_value; - } - - // post increment operator - T operator++(int) - { - lock_guard lock(_mutex); - return _value++; - } - - // post decrement operator - T operator--(int) - { - lock_guard lock(_mutex); - return _value--; - } - - // conversion operator, used also for <,>,<=,>=,== and != - operator T() const - { - lock_guard lock(_mutex); - return _value; - } - - // access to the internal mutex - rtos::Mutex &internal_mutex() - { - return _mutex; - } - -private: - mutable rtos::Mutex _mutex; - T _value; -}; -#endif /* MBEDMICRO_RTOS_MBED_THREADS_SYNCHRONIZED_INTEGRAL */ diff --git a/TESTS/mbedmicro-rtos-mbed/threads/main.cpp b/TESTS/mbedmicro-rtos-mbed/threads/main.cpp deleted file mode 100644 index 6cd9f3b..0000000 --- a/TESTS/mbedmicro-rtos-mbed/threads/main.cpp +++ /dev/null @@ -1,863 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * - * SPDX-License-Identifier: Apache-2.0 - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) -#error [NOT_SUPPORTED] Threads test cases require RTOS with multithread to run -#else - -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. -#else - -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity.h" -#include "utest.h" -#include "rtos.h" -#include "SynchronizedIntegral.h" -#include -#include - -#define THREAD_STACK_SIZE 512 -#if defined(__CORTEX_A9) || defined(__CORTEX_M23) || defined(__CORTEX_M33) || defined(TARGET_ARM_FM) || defined(TARGET_CY8CKIT_062_WIFI_BT_PSA) -#define PARALLEL_THREAD_STACK_SIZE 512 -#define CHILD_THREAD_STACK_SIZE 512 -#else -#define PARALLEL_THREAD_STACK_SIZE 384 -#define CHILD_THREAD_STACK_SIZE 384 -#endif - -#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ - do { \ - using ct = std::common_type_t; \ - TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ - } while (0) - - -using namespace utest::v1; -using mstd::lock_guard; - -// The counter type used accross all the tests -// It is internall ysynchronized so read -typedef SynchronizedIntegral counter_t; - -template -class ParallelThread : public Thread { -public: - ParallelThread() : Thread(P, S) { } -}; - -// Tasks with different functions to test on threads -void increment(counter_t *counter) -{ - (*counter)++; -} - -void increment_with_yield(counter_t *counter) -{ - ThisThread::yield(); - (*counter)++; -} - -void increment_with_wait(counter_t *counter) -{ - ThisThread::sleep_for(100ms); - (*counter)++; -} - -void increment_with_child(counter_t *counter) -{ - Thread *child = new (std::nothrow) Thread(osPriorityNormal, CHILD_THREAD_STACK_SIZE); - char *dummy = new (std::nothrow) char[CHILD_THREAD_STACK_SIZE]; - delete[] dummy; - - // Don't fail test due to lack of memory. Call function directly instead - if (!child || !dummy) { - increment(counter); - delete child; - return; - } - child->start(callback(increment, counter)); - child->join(); - delete child; -} - -void increment_with_murder(counter_t *counter) -{ - { - // take ownership of the counter mutex so it prevent the child to - // modify counter. - lock_guard lock(counter->internal_mutex()); - Thread *child = new (std::nothrow) Thread(osPriorityNormal, CHILD_THREAD_STACK_SIZE); - char *dummy = new (std::nothrow) char[CHILD_THREAD_STACK_SIZE]; - delete[] dummy; - - // Don't fail test due to lack of memory. - if (!child || !dummy) { - delete child; - goto end; - } - child->start(callback(increment, counter)); - child->terminate(); - delete child; - } - -end: - (*counter)++; -} - -void self_terminate(Thread *self) -{ - self->terminate(); - // Code should not get here - TEST_ASSERT(0); -} - -// Tests that spawn tasks in different configurations - -/** Template for tests: single thread, with yield, with wait, with child, with murder - - Testing single thread - Given single thread is started - when the thread increments the counter - then the final value of the counter is equal to 1 - - Testing single thread with yield - Given single thread is started - when the thread yields and then increments the counter - then the final value of the counter is equal to 1 - - Testing single thread with wait - Given single thread is started - when the thread waits for 100ms and then increments the counter - then the final value of the counter is equal to 1 - - Testing single thread with child - Given single thread is started - when the thread spawns another thread that increments the counter - then the final value of the counter is equal to 1 - - Testing serial threads with murder - Given single thread is started - when the parent thread is holding a lock - and the parent thread spawns a child thread that waits for the lock before incrementing the counter - and the parent terminates the child before releasing the lock - and the parent increments the counter - then the final value of the counter is equal to 1 -*/ -template -void test_single_thread() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - counter_t counter(0); - Thread thread(osPriorityNormal, THREAD_STACK_SIZE); - thread.start(callback(F, &counter)); - thread.join(); - TEST_ASSERT_EQUAL(counter, 1); -} - -/** Template for tests: parallel threads, with yield, with wait, with child, with murder - - Testing parallel threads - Given multiple threads are started in parallel - when each of the threads increments the counter - then the final value of the counter is equal to number of threads - - Testing parallel threads with yield - Given multiple threads are started in parallel - when each of the threads yields and then increments the counter - then the final value of the counter is equal to number of threads - - Testing parallel threads with wait - Given multiple threads are started in parallel - when each of the threads waits for 100ms and then increments the counter - then the final value of the counter is equal to number of threads - - Testing parallel threads with child - Given multiple threads are started in parallel - when each of the threads spawns another thread that increments the counter - then the final value of the counter is equal to number of parallel threads - - Testing parallel threads with murder - Given multiple threads are started in parallel - when the parent thread is holding a lock - and the parent thread spawns a child thread that waits for the lock before incrementing the counter - and the parent terminates the child before releasing the lock - and the parent increments the counter - then the final value of the counter is equal to number of parallel threads -*/ -template -void test_parallel_threads() -{ - char *dummy = new (std::nothrow) char[PARALLEL_THREAD_STACK_SIZE * N]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - counter_t counter(0); - ParallelThread threads[N]; - - for (int i = 0; i < N; i++) { - threads[i].start(callback(F, &counter)); - } - - for (int i = 0; i < N; i++) { - threads[i].join(); - } - - TEST_ASSERT_EQUAL(counter, N); -} - -/** Template for tests: serial threads, with yield, with wait, with child, with murder - - Testing serial threads - Given multiple threads are started serially - when each of the threads increments the counter - then the final value of the counter is equal to number of threads - - Testing serial threads with yield - Given multiple threads are started serially - when each of the threads yields and then increments the counter - then the final value of the counter is equal to number of threads - - Testing serial threads with wait - Given multiple threads are started serially - when each of the threads waits for 100ms and then increments the counter - then the final value of the counter is equal to number of threads - - Testing serial threads with child - Given multiple threads are started serially - when each of the threads spawns another thread that increments the counter - then the final value of the counter is equal to number of serial threads - - Testing serial threads with murder - Given multiple threads are started serially - when the parent thread is holding a lock - and the parent thread spawns a child thread that waits for the lock before incrementing the counter - and the parent terminates the child before releasing the lock - and the parent increments the counter - then the final value of the counter is equal to number of serial threads -*/ -template -void test_serial_threads() -{ - counter_t counter(0); - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - for (int i = 0; i < N; i++) { - Thread thread(osPriorityNormal, THREAD_STACK_SIZE); - thread.start(callback(F, &counter)); - thread.join(); - } - - TEST_ASSERT_EQUAL(counter, N); -} - -/** Testing thread self terminate - - Given the thread is running - when the thread calls @a terminate on its self - then the thread terminates execution cleanly - */ -void test_self_terminate() -{ - Thread *thread = new (std::nothrow) Thread(osPriorityNormal, THREAD_STACK_SIZE); - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - - // Don't fail test due to lack of memory. - if (!thread || !dummy) { - goto end; - } - thread->start(callback(self_terminate, thread)); - thread->join(); - -end: - delete thread; -} - -void flags_wait() -{ - uint32_t flags = ThisThread::flags_wait_all(0x1); - TEST_ASSERT_EQUAL(0x1, flags); -} - -void flags_wait_tout() -{ - uint32_t flags = ThisThread::flags_wait_all_for(0x2, 50ms); - TEST_ASSERT_EQUAL(0x1, flags); -} - -void flags_wait_multibit() -{ - uint32_t flags = ThisThread::flags_wait_all(0x1 | 0x2); - TEST_ASSERT_EQUAL(0x3, flags); -} - -void flags_wait_multibit_any() -{ - uint32_t flags = ThisThread::flags_wait_any(0x1 | 0x2); - TEST_ASSERT_NOT_EQUAL(0x0, flags); -} - -void flags_wait_multibit_tout() -{ - uint32_t flags = ThisThread::flags_wait_all_for(0x1 | 0x2, 50ms); - TEST_ASSERT_NOT_EQUAL(0x3, flags); -} - -/** - Testing thread flags: wait - Given two threads (A & B) are started - when thread A executes @a flags_wait_all(0x1) - and thread B execute @a flags_set(0x1) - then thread A exits the wait and continues execution - - Testing thread flags: timeout - Given two threads (A & B) are started - when thread A executes @a flags_wait_all_for(0x1 | 0x2, 50) with a timeout of 50ms - and thread B execute @a flags_set(0x2) - then thread A keeps waiting for correct flags until it timeouts - - Testing thread flags: multi-bit - Given two threads (A & B) are started - when thread A executes @a flags_wait_all(0x1 | 0x2) - and thread B execute @a flags_set(0x1 | 0x2) - then thread A exits the wait and continues execution - - Testing thread flags: multi-bit any - Given two threads (A & B) are started - when thread A executes @a flags_wait_any(0x1 | 0x2) - and thread B execute @a flags_set(0x1) - then thread A exits the wait and continues execution - - Testing thread flags: multi-bit timeout - Given two threads (A & B) are started - when thread A executes @a flags_wait_all_for(0x1, 50) with a timeout of 50ms - and thread B execute @a flags_set(0x2) - then thread A keeps waiting for correct flags until it timeouts -*/ -template -void test_thread_flags_set() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - Thread t_wait(osPriorityNormal, THREAD_STACK_SIZE); - - t_wait.start(callback(F)); - - ThisThread::yield(); - - Thread::State state = t_wait.get_state(); - TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, state); - - int32_t res = t_wait.flags_set(S); - - t_wait.join(); -} - -void flags_clear() -{ - ThisThread::yield(); - - int32_t sig = ThisThread::flags_clear(0x1); - TEST_ASSERT_EQUAL(0x1, sig); - - /* Flags cleared we should get timeout */ - uint32_t flags = ThisThread::flags_wait_all_for(0x1, 0s); - TEST_ASSERT_EQUAL(0, flags); -} - -/** Testing thread flags: flags clear - - Given two threads (A & B) are started - when thread A executes @a flags_set(0x1) - and thread B execute @a flags_clear(0x1) - and thread B execute @a flags_wait_all_for(0x1, 0) - then thread B @a flags_wait_all_for return should be 0 indicating no flags set - */ -void test_thread_flags_clear() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - Thread t_wait(osPriorityNormal, THREAD_STACK_SIZE); - - t_wait.start(callback(flags_clear)); - - int32_t res = t_wait.flags_set(0x1); - TEST_ASSERT_EQUAL(0x1, res); - - t_wait.join(); -} - -void thread_wait_flags() -{ - ThisThread::flags_wait_all(0x1); -} - -void stack_info() -{ - ThisThread::flags_wait_all(0x1); - - thread_wait_flags(); - - ThisThread::flags_wait_all(0x1); -} - -/** Testing thread stack info - - Given the thread is running - when a function is called from the thread context - then the stack usage goes up - and the reported stack size is as requested in the constructor - and the sum of free and used stack sizes is equal to the total stack size - when the function returns - then the stack usage goes down - and the reported stack size is as requested in the constructor - and the sum of free and used stack sizes is equal to the total stack size - */ -void test_thread_stack_info() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - t.start(callback(stack_info)); - - ThisThread::yield(); - - TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.stack_size()); - TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.free_stack() + t.used_stack()); - uint32_t last_stack = t.used_stack(); - - t.flags_set(0x1); - ThisThread::yield(); - - TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.free_stack() + t.used_stack()); - TEST_ASSERT(last_stack <= t.used_stack()); - last_stack = t.used_stack(); - - t.flags_set(0x1); - ThisThread::yield(); - - TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.free_stack() + t.used_stack()); - TEST_ASSERT(last_stack >= t.used_stack()); - - t.flags_set(0x1); - - t.join(); -} - -/** Testing thread wait aka delay - - Given the thread is running - when the @a wait function is called - then the thread sleeps for given amount of time - */ -void test_thread_wait() -{ - Timer timer; - timer.start(); - - ThisThread::sleep_for(150ms); - - TEST_ASSERT_DURATION_WITHIN(50ms, 150ms, timer.elapsed_time()); -} - -/** Testing thread name - - Given a thread is started with a specified name - when the name is queried using @a get_name - then the returned name is as set -*/ -void test_thread_name() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - const char tname[] = "Amazing thread"; - Thread t(osPriorityNormal, THREAD_STACK_SIZE, NULL, tname); - TEST_ASSERT_EQUAL(strcmp(tname, t.get_name()), 0); - t.start([&] { TEST_ASSERT_EQUAL(strcmp(tname, ThisThread::get_name()), 0); }); - t.join(); -} - -void test_deleted_thread() -{ -} - -/** Testing thread states: deleted - - Given the thread is not started - then its state, as reported by @a get_state, is @a Deleted - when the thread is started and finishes executing - then its state, as reported by @a get_state, is @a Deleted - */ -void test_deleted() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - - TEST_ASSERT_EQUAL(Thread::Deleted, t.get_state()); - - t.start(callback(test_deleted_thread)); - - t.join(); - TEST_ASSERT_EQUAL(Thread::Deleted, t.get_state()); -} - -void test_delay_thread() -{ - ThisThread::sleep_for(50ms); -} - -/** Testing thread states: wait delay - - Given the thread is running - when thread calls @a wait - then its state, as reported by @a get_state, is @a WaitingDelay - */ -void test_delay() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - - t.start(callback(test_delay_thread)); - - ThisThread::yield(); - - TEST_ASSERT_EQUAL(Thread::WaitingDelay, t.get_state()); - - t.join(); - TEST_ASSERT_EQUAL(Thread::Deleted, t.get_state()); -} - -void test_thread_flags_thread() -{ - ThisThread::flags_wait_all(0x1); -} - -/** Testing thread states: wait flags - - Given the thread is running - when thread waits for flags - then its state, as reported by @a get_state, is @a WaitingThreadFlag - */ -void test_thread_flags() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - - t.start(callback(test_thread_flags_thread)); - - ThisThread::yield(); - - TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); - - t.flags_set(0x1); -} - -void test_evt_flag_thread(EventFlags *evtflg) -{ - evtflg->wait_any(0x1); -} - -/** Testing thread states: wait evt flag - - Given the thread is running - when thread waits for an event flag - then its state, as reported by @a get_state, is @a WaitingEventFlag - */ -void test_evt_flag() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - EventFlags evtflg; - - t.start(callback(test_evt_flag_thread, &evtflg)); - - ThisThread::yield(); - - TEST_ASSERT_EQUAL(Thread::WaitingEventFlag, t.get_state()); - - evtflg.set(0x1); -} - -void test_mutex_thread(Mutex *mutex) -{ - mutex->lock(); -} - -/** Testing thread states: wait mutex - - Given the thread is running - when thread waits for a mutex - then its state, as reported by @a get_state, is @a WaitingMutex - */ -void test_mutex() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - Mutex mutex; - - mutex.lock(); - - t.start(callback(test_mutex_thread, &mutex)); - - ThisThread::yield(); - - TEST_ASSERT_EQUAL(Thread::WaitingMutex, t.get_state()); - - mutex.unlock(); -} - -void test_semaphore_thread(Semaphore *sem) -{ - sem->acquire(); -} - -/** Testing thread states: wait semaphore - - Given the thread is running - when thread waits for a semaphore - then its state, as reported by @a get_state, is @a WaitingSemaphore - */ -void test_semaphore() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - Semaphore sem; - - t.start(callback(test_semaphore_thread, &sem)); - - ThisThread::yield(); - - TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state()); - - sem.release(); -} - -void test_msg_get_thread(Queue *queue) -{ - queue->get(); -} - -/** Testing thread states: wait message get - - Given the thread is running - when thread tries to get a message from an empty queue - then its state, as reported by @a get_state, is @a WaitingMessageGet - */ -void test_msg_get() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - Queue queue; - - t.start(callback(test_msg_get_thread, &queue)); - - ThisThread::yield(); - - TEST_ASSERT_EQUAL(Thread::WaitingMessageGet, t.get_state()); - - queue.try_put((int32_t *)0xE1EE7); -} - -void test_msg_put_thread(Queue *queue) -{ - queue->try_put_for(Kernel::wait_for_u32_forever, (int32_t *)0xDEADBEEF); - -} - -/** Testing thread states: wait message put - - Given the thread is running - when thread tries to put a message into a full queue - then its state, as reported by @a get_state, is @a WaitingMessagePut - */ -void test_msg_put() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - Queue queue; - - queue.try_put((int32_t *)0xE1EE7); - - t.start(callback(test_msg_put_thread, &queue)); - - ThisThread::yield(); - - TEST_ASSERT_EQUAL(Thread::WaitingMessagePut, t.get_state()); - queue.get(); -} - -/** Utility function that places some date on the stack */ -void use_some_stack() -{ - volatile uint32_t stack_filler[10] = {0xDEADBEEF}; -} - -/** Testing thread with external stack memory - - Given external buffer is supplied as stack to a thread - when the thread executes - then the supplies buffer is used as a stack - */ -void test_thread_ext_stack() -{ - char stack[512]; - Thread t(osPriorityNormal, sizeof(stack), (unsigned char *)stack); - - memset(&stack, 0, sizeof(stack)); - t.start(callback(use_some_stack)); - t.join(); - - /* If buffer was used as a stack it was cleared with pattern and some data were placed in it */ - for (unsigned i = 0; i < sizeof(stack); i++) { - if (stack[i] != 0) { - return; - } - } - - TEST_FAIL_MESSAGE("External stack was not used."); -} - -/** Testing thread priority operations - - Given thread running with osPriorityNormal - when new priority is set using @a set_priority - then priority is changed and can be retrieved using @a get_priority - */ -void test_thread_prio() -{ - char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; - delete[] dummy; - TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); - - Thread t(osPriorityNormal, THREAD_STACK_SIZE); - t.start(callback(thread_wait_flags)); - - TEST_ASSERT_EQUAL(osPriorityNormal, t.get_priority()); - - t.set_priority(osPriorityHigh); - - TEST_ASSERT_EQUAL(osPriorityHigh, t.get_priority()); - - t.flags_set(0x1); - t.join(); -} - -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(25, "default_auto"); - return verbose_test_setup_handler(number_of_cases); -} - -#define DEFAULT_HANDLERS NULL,NULL,greentea_case_setup_handler,greentea_case_teardown_handler,greentea_case_failure_abort_handler - -// Test cases. It's spelled out rather than constructed with macro because -// macros don't play nicely with the templates (extra comma). -static const case_t cases[] = { - {"Testing single thread", test_single_thread, DEFAULT_HANDLERS}, - {"Testing parallel threads", test_parallel_threads<3, increment>, DEFAULT_HANDLERS}, - {"Testing serial threads", test_serial_threads<10, increment>, DEFAULT_HANDLERS}, - - {"Testing single thread with yield", test_single_thread, DEFAULT_HANDLERS}, - {"Testing parallel threads with yield", test_parallel_threads<3, increment_with_yield>, DEFAULT_HANDLERS}, - {"Testing serial threads with yield", test_serial_threads<10, increment_with_yield>, DEFAULT_HANDLERS}, - - {"Testing single thread with wait", test_single_thread, DEFAULT_HANDLERS}, - {"Testing parallel threads with wait", test_parallel_threads<3, increment_with_wait>, DEFAULT_HANDLERS}, - {"Testing serial threads with wait", test_serial_threads<10, increment_with_wait>, DEFAULT_HANDLERS}, - - {"Testing single thread with child", test_single_thread, DEFAULT_HANDLERS}, - {"Testing parallel threads with child", test_parallel_threads<2, increment_with_child>, DEFAULT_HANDLERS}, - {"Testing serial threads with child", test_serial_threads<10, increment_with_child>, DEFAULT_HANDLERS}, - - {"Testing single thread with murder", test_single_thread, DEFAULT_HANDLERS}, - {"Testing parallel threads with murder", test_parallel_threads<2, increment_with_murder>, DEFAULT_HANDLERS}, - {"Testing serial threads with murder", test_serial_threads<10, increment_with_murder>, DEFAULT_HANDLERS}, - - {"Testing thread self terminate", test_self_terminate, DEFAULT_HANDLERS}, - - {"Testing thread flags: wait", test_thread_flags_set<0x1, flags_wait>, DEFAULT_HANDLERS}, - {"Testing thread flags: timeout", test_thread_flags_set<0x1, flags_wait_tout>, DEFAULT_HANDLERS}, - {"Testing thread flags: multi-bit all", test_thread_flags_set<0x3, flags_wait_multibit>, DEFAULT_HANDLERS}, - {"Testing thread flags: multi-bit all timeout", test_thread_flags_set<0x1, flags_wait_multibit_tout>, DEFAULT_HANDLERS}, - {"Testing thread flags: multi-bit any", test_thread_flags_set<0x1, flags_wait_multibit_any>, DEFAULT_HANDLERS}, - {"Testing thread flags: flags clear", test_thread_flags_clear, DEFAULT_HANDLERS}, - - {"Testing thread stack info", test_thread_stack_info, DEFAULT_HANDLERS}, - {"Testing thread wait", test_thread_wait, DEFAULT_HANDLERS}, - {"Testing thread name", test_thread_name, DEFAULT_HANDLERS}, - - {"Testing thread states: deleted", test_deleted, DEFAULT_HANDLERS}, - {"Testing thread states: wait delay", test_delay, DEFAULT_HANDLERS}, - {"Testing thread states: wait thread flags", test_thread_flags, DEFAULT_HANDLERS}, - {"Testing thread states: wait event flag", test_evt_flag, DEFAULT_HANDLERS}, - {"Testing thread states: wait mutex", test_mutex, DEFAULT_HANDLERS}, - {"Testing thread states: wait semaphore", test_semaphore, DEFAULT_HANDLERS}, - {"Testing thread states: wait message get", test_msg_get, DEFAULT_HANDLERS}, - {"Testing thread states: wait message put", test_msg_put, DEFAULT_HANDLERS}, - - {"Testing thread with external stack memory", test_thread_ext_stack, DEFAULT_HANDLERS}, - {"Testing thread priority ops", test_thread_prio, DEFAULT_HANDLERS} - -}; - -Specification specification(test_setup, cases); - -int main() -{ - return !Harness::run(specification); -} - -#endif // !DEVICE_USTICKER -#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) diff --git a/rtos/ConditionVariable.h b/rtos/ConditionVariable.h deleted file mode 100644 index d602d7c..0000000 --- a/rtos/ConditionVariable.h +++ /dev/null @@ -1,550 +0,0 @@ -/* Mbed Microcontroller Library - * Copyright (c) 2017-2019 ARM Limited - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef CONDITIONVARIABLE_H -#define CONDITIONVARIABLE_H - -#include -#include -#include "rtos/mbed_rtos_types.h" -#include "rtos/Mutex.h" -#include "rtos/Semaphore.h" -#include "rtos/Kernel.h" - -#include "platform/NonCopyable.h" - -#if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY) - -namespace rtos { -/** \addtogroup rtos-public-api */ -/** @{*/ - -enum class cv_status { - no_timeout, - timeout -}; - -struct Waiter; -/** - * \defgroup rtos_ConditionVariable ConditionVariable class - * @{ - */ - -/** The ConditionVariable class is a synchronization primitive that allows - * threads to wait until a particular condition occurs. - * - * Use the condition variable in conjunction with a mutex to safely wait for - * or notify waiters of condition changes to a resource accessible by multiple - * threads. - * - * The thread that intends to wait on a ConditionVariable must: - * - Acquire a lock on a mutex. - * - Execute `wait`, `wait_for` or `wait_until`. While the thread is waiting, - * the mutex is unlocked. - * - When the condition variable has been notified, or in the case of `wait_for` - * and `wait_until` the timeout expires, the thread is awakened. - * - * The thread that intends to notify a ConditionVariable must: - * - Acquire a lock on the mutex used to construct the condition variable. - * - Execute `notify_one` or `notify_all` on the condition variable. - * - * All threads waiting on the condition variable wake when - * `ConditionVariable::notify_all` is called. - * At least one thread waiting on the condition variable wakes - * when `ConditionVariable::notify_one` is called. - * - * While a thread is waiting for notification of a - * ConditionVariable, it releases the lock held on the mutex. - * The ConditionVariable reacquires the mutex lock before exiting the wait - * function. - * - * #### Unspecified behavior - * - The thread that is unblocked on `ConditionVariable::notify_one` is - * unspecified if there are multiple waiters. - * - When `ConditionVariable::notify_one` or `ConditionVariable::notify_all` is - * called and there are one or more waiters, and one or more threads - * attempting to acquire the condition variable's mutex, the order in which the mutex is - * acquired is unspecified. - * - Spurious notifications (not triggered by the application) can occur. - * - * #### Undefined behavior - * - Calling wait if the mutex is not locked by the current thread is undefined - * behavior. - * - The order in which waiting threads acquire the condition variable's - * mutex after `ConditionVariable::notify_all` is called is undefined. - * - The behavior of `ConditionVariable::wait` and `ConditionVariable::wait_for` - * is undefined if the condition variable's mutex is locked more than once by - * the calling thread. - * - * @note Synchronization level: Thread safe - * - * @note Bare metal profile: This class is not supported. - * - * Example: - * - * @code - * #include "mbed.h" - * - * Mutex mutex; - * ConditionVariable cv(mutex); - * - * // These variables are protected by locking the mutex. - * uint32_t work_count = 0; - * bool done = false; - * - * void worker_thread() - * { - * // Acquire lock on mutex before accessing protected variables and waiting. - * mutex.lock(); - * - * while (done == false) { - * printf("Worker thread: Count: %lu\r\n", work_count); - * - * // Wait for main thread to notify the condition variable. - * printf("Worker thread: Waiting\r\n"); - * cv.wait(); - * } - * - * printf("Worker: Exiting\r\n"); - * - * // The condition variable acquires the lock when exiting the `wait` function. - * // Unlock mutex when exiting the thread. - * mutex.unlock(); - * } - * - * int main() - * { - * Thread thread; - * thread.start(worker_thread); - * - * for (int i = 0; i < 5; i++) { - * // Acquire lock on mutex before modifying variables and notifying. - * mutex.lock(); - * - * // Change count and notify waiters. - * work_count++; - * printf("Main thread: Set count to: %lu\r\n", work_count); - * printf("Main thread: Notifying worker thread\r\n"); - * cv.notify_all(); - * - * // Mutex must be unlocked before the worker thread can acquire it. - * mutex.unlock(); - * - * ThisThread::sleep_for(1000); - * } - * - * // Change done and notify waiters of this. - * mutex.lock(); - * done = true; - * cv.notify_all(); - * mutex.unlock(); - * - * thread.join(); - * - * printf("Main: Exiting\r\n"); - * } - * @endcode - */ - -class ConditionVariable : private mbed::NonCopyable { -public: - /** Create and initialize a ConditionVariable object. - * - * @note You cannot call this function from ISR context. - */ - ConditionVariable(Mutex &mutex); - - /** Wait for a notification. - * - * Wait causes the current thread to block until the condition variable - * receives a notification from another thread. - * - * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex, and it must be locked exactly once. - * - * @note - Spurious notifications can occur, so the caller of this API - * should check to make sure the condition the caller is waiting on has - * been met. - * - * @note - The current thread releases the mutex while inside the wait - * function and reacquires it upon exiting the function. - * - * Example: - * @code - * mutex.lock(); - * - * while (!condition_met) { - * cond.wait(); - * } - * - * function_to_handle_condition(); - * - * mutex.unlock(); - * @endcode - * - * @note You cannot call this function from ISR context. - */ - void wait(); - - /** Wait for a predicate. - * - * Wait causes the current thread to block until the predicate is - * true. - * - * @param pred A function-like object such that `pred()` is convertible to bool - * - * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex, and it must be locked exactly once. - * - * @note - The current thread releases the mutex while inside the wait - * function and reacquires it upon exiting the function. - * - * Example: - * @code - * extern bool data_available(); - * - * mutex.lock(); - * - * cond.wait(data_available); - * - * function_to_handle_data(); - * - * mutex.unlock(); - * @endcode - * - * @note You cannot call this function from ISR context. - */ - template - void wait(Predicate pred) - { - while (!pred()) { - wait(); - } - } - - - /** Wait for a notification until the specified time. - * - * Wait until causes the current thread to block until the condition - * variable is notified, or a specific time given by millisec parameter is - * reached. - * - * @param millisec Absolute end time referenced to `Kernel::get_ms_count()` - * @return `true` if a timeout occurred, `false` otherwise. - * - * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex, and it must be locked exactly once. - * - * @note - Spurious notifications can occur, so the caller of this API - * should check to make sure the condition the caller is waiting on has - * been met. - * - * @note - The current thread releases the lock while inside the wait - * function and reacquires it upon exiting the function. - * - * Example: - * @code - * mutex.lock(); - * uint64_t end_time = Kernel::get_ms_count() + COND_WAIT_TIMEOUT; - * - * while (!condition_met) { - * if (cond.wait_until(end_time)) { - * break; - * } - * } - * - * if (condition_met) { - * function_to_handle_condition(); - * } - * - * mutex.unlock(); - * @endcode - * - * @note You cannot call this function from ISR context. - * @deprecated Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` - * rather than `Kernel::get_ms_count() + 5000`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") - bool wait_until(uint64_t millisec); - - /** Wait for a notification until the specified time. - * - * Wait until causes the current thread to block until the condition - * variable is notified, or a specific time given by millisec parameter is - * reached. - * - * @param abs_time Absolute end time referenced to `Kernel::Clock` - * @return `cv_status::timeout` if a timeout occurred, `cv_status::no_timeout` otherwise. - * - * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex, and it must be locked exactly once. - * - * @note - Spurious notifications can occur, so the caller of this API - * should check to make sure the condition the caller is waiting on has - * been met. - * - * @note - The current thread releases the lock while inside the wait - * function and reacquires it upon exiting the function. - * - * Example: - * @code - * mutex.lock(); - * Kernel::Clock::time_point end_time = Kernel::Clock::now() + 2s; - * - * while (!condition_met) { - * if (cond.wait_until(end_time) == cv_status::timeout) { - * break; - * } - * } - * - * if (condition_met) { - * function_to_handle_condition(); - * } - * - * mutex.unlock(); - * @endcode - * - * @note You cannot call this function from ISR context. - */ - cv_status wait_until(Kernel::Clock::time_point abs_time); - - /** Wait for a predicate until the specified time. - * - * Wait until causes the current thread to block until the predicate is true, - * or a specific time given by abs_time parameter is reached. - * - * @param abs_time Absolute end time referenced to `Kernel::Clock` - * @param pred A function-like object such that `pred()` is convertible to bool - * @return The state of the predicate - * - * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex, and it must be locked exactly once. - * - * @note - The current thread releases the mutex while inside the wait - * function and reacquires it upon exiting the function. - * - * Example: - * @code - * extern bool data_available(); - * - * mutex.lock(); - * - * if (cond.wait_until(Kernel::Clock::now() + 2s, data_available)) { - * function_to_handle_data(); - * } - * - * mutex.unlock(); - * @endcode - * - * @note You cannot call this function from ISR context. - */ - template - bool wait_until(Kernel::Clock::time_point abs_time, Predicate pred) - { - while (!pred()) { - if (wait_until(abs_time) == cv_status::timeout) { - return pred(); - } - } - return true; - } - - /** Wait for a notification or timeout. - * - * `Wait for` causes the current thread to block until the condition - * variable receives a notification from another thread, or the timeout - * specified by the millisec parameter is reached. - * - * @param millisec Timeout value or osWaitForever in case of no timeout. - * @return `true` if a timeout occurred, `false` otherwise. - * - * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex, and it must be locked exactly once. - * - * @note - Spurious notifications can occur, so the caller of this API - * should check to make sure the condition the caller is waiting on has - * been met. - * - * @note - The current thread releases the lock while inside the wait - * function and reacquire it upon exiting the function. - * - * Example: - * @code - * mutex.lock(); - * - * while (!condition_met) { - * cond.wait_for(MAX_SLEEP_TIME); - * if (!condition_met) { - * do_other_work_while_condition_false(); - * } - * } - * - * if (condition_met) { - * function_to_handle_condition(); - * } - * - * mutex.unlock(); - * @endcode - * - * @note You cannot call this function from ISR context. - * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - bool wait_for(uint32_t millisec); - - /** Wait for a notification or timeout. - * - * `Wait for` causes the current thread to block until the condition - * variable receives a notification from another thread, or the timeout - * specified by the millisec parameter is reached. - * - * @param rel_time Timeout value. - * @return `cv_status::timeout` if a timeout occurred, `cv_status::no_timeout` otherwise. - * - * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex, and it must be locked exactly once. - * - * @note - Spurious notifications can occur, so the caller of this API - * should check to make sure the condition the caller is waiting on has - * been met. - * - * @note - The current thread releases the lock while inside the wait - * function and reacquire it upon exiting the function. - * - * Example: - * @code - * mutex.lock(); - * - * while (!condition_met) { - * cond.wait_for(MAX_SLEEP_TIME); - * if (!condition_met) { - * do_other_work_while_condition_false(); - * } - * } - * - * if (condition_met) { - * function_to_handle_condition(); - * } - * - * mutex.unlock(); - * @endcode - * - * @note You cannot call this function from ISR context. - */ - cv_status wait_for(Kernel::Clock::duration_u32 rel_time); - - /** Wait for a predicate or timeout. - * - * `Wait for` causes the current thread to block until the predicate - * is true, or the timeout specified by the rel_time parameter is reached. - * - * @param rel_time Timeout value. - * @param pred a function-like object such that `pred()` is convertible to bool - * @return The state of the predicate - * - * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex, and it must be locked exactly once. - * - * @note - The current thread releases the mutex while inside the wait - * function and reacquire it upon exiting the function. - * - * Example: - * @code - * extern bool data_available(); - * - * mutex.lock(); - * - * if (cond.wait_for(2s, data_available)) { - * function_to_handle_data(); - * } - * - * mutex.unlock(); - * @endcode - * - * @note You cannot call this function from ISR context. - */ - template - bool wait_for(Kernel::Clock::duration rel_time, Predicate pred) - { - return wait_until(Kernel::Clock::now() + rel_time, std::move(pred)); - } - - /** Notify one waiter on this condition variable that a condition changed. - * - * This function unblocks one of the threads waiting for the condition - * variable. - * - * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex. - * - * @note - The thread that is unblocked on ConditionVariable::notify_one is - * undefined if there are multiple waiters. - * - * @note You cannot call this function from ISR context. - */ - void notify_one(); - - /** Notify all waiters on this condition variable that a condition changed. - * - * This function unblocks all of the threads waiting for the condition - * variable. - * - * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex. - * - * @note - If there are one or more waiters and one or more threads - * attempting to acquire the condition variable's mutex the order in which - * the mutex is acquired is undefined. - * - * @note You cannot call this function from ISR context. - */ - void notify_all(); - - /** ConditionVariable destructor. - * - * @note You cannot call this function from ISR context. - */ - ~ConditionVariable(); - -#if !defined(DOXYGEN_ONLY) -protected: - struct Waiter { - Waiter(); - Semaphore sem; - Waiter *prev; - Waiter *next; - bool in_list; - }; - - static void _add_wait_list(Waiter **wait_list, Waiter *waiter); - static void _remove_wait_list(Waiter **wait_list, Waiter *waiter); - Mutex &_mutex; - Waiter *_wait_list; -#endif // !defined(DOXYGEN_ONLY) -}; - -/** @}*/ -/** @}*/ -} // namespace rtos -#endif - -#endif diff --git a/rtos/EventFlags.h b/rtos/EventFlags.h deleted file mode 100644 index 6769142..0000000 --- a/rtos/EventFlags.h +++ /dev/null @@ -1,175 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019 ARM Limited - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef EVENT_FLAG_H -#define EVENT_FLAG_H - -#include -#include -#include "rtos/Kernel.h" -#include "rtos/mbed_rtos_types.h" -#include "rtos/mbed_rtos1_types.h" -#include "rtos/mbed_rtos_storage.h" - -#include "platform/NonCopyable.h" - -namespace rtos { -/** \addtogroup rtos-public-api */ -/** @{*/ - -/** - * \defgroup rtos_EventFlags EventFlags class - * @{ - */ - -/** The EventFlags class is used to control event flags or wait for event flags other threads control. - - @note - EventFlags support 31 flags. The MSB flag is ignored. It is used to return an error code (@a osFlagsError). - - @note - Memory considerations: The EventFlags control structures will be created on the current thread's stack, both for the Mbed OS - and underlying RTOS objects (static or dynamic RTOS memory pools are not being used). -*/ -class EventFlags : private mbed::NonCopyable { -public: - /** Create and initialize an EventFlags object. - * - * @note You cannot call this function from ISR context. - */ - EventFlags(); - - /** Create and initialize an EventFlags object. - - @param name name to be used for this EventFlags. It has to stay allocated for the lifetime of the thread. - - @note You cannot call this function from ISR context. - */ - EventFlags(const char *name); - - /** Set the specified event flags. - @param flags the flags that will be set. - @return event flags after setting or error code if highest bit set (see @a osFlagsError for details). - - @note This function may be called from ISR context. - */ - uint32_t set(uint32_t flags); - - /** Clear the specified event flags. - @param flags the flags that will be cleared (default: 0x7fffffff -- all flags). - @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). - - @note You may call this function from ISR context. - */ - uint32_t clear(uint32_t flags = 0x7fffffff); - - /** Get the currently set event flags. - @return current event flags. - - @note You may call this function from ISR context. - */ - uint32_t get() const; - - /** Wait for all of the specified event flags to become signaled. - @param flags the flags to wait for (default: 0 -- no flags). - @param millisec timeout value (default: osWaitForever). - @param clear clear specified event flags after waiting for them (default: true). - @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). - - @note You may call this function from ISR context if the millisec parameter is set to 0. - */ - uint32_t wait_all(uint32_t flags = 0, uint32_t millisec = osWaitForever, bool clear = true); - - /** Wait for all of the specified event flags to become signaled. - @param flags the flags to wait for. - @param rel_time timeout value. - @param clear clear specified event flags after waiting for them (default: true). - @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). - - @note You may call this function from ISR context if the rel_time parameter is set to 0. - */ - uint32_t wait_all_for(uint32_t flags, Kernel::Clock::duration_u32 rel_time, bool clear = true); - - /** Wait for all of the specified event flags to become signaled. - @param flags the flags to wait for. - @param abs_time timeout value. - @param clear clear specified event flags after waiting for them (default: true). - @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). - - @note You cannot call this function from ISR context. - */ - uint32_t wait_all_until(uint32_t flags, Kernel::Clock::time_point abs_time, bool clear = true); - - /** Wait for any of the specified event flags to become signaled. - @param flags the flags to wait for (default: 0 -- no flags). - @param millisec timeout value (default: osWaitForever). - @param clear clear specified event flags after waiting for them (default: true). - @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). - - @note This function may be called from ISR context if the millisec parameter is set to 0. - */ - uint32_t wait_any(uint32_t flags = 0, uint32_t millisec = osWaitForever, bool clear = true); - - /** Wait for any of the specified event flags to become signaled. - @param flags the flags to wait for. - @param rel_time timeout value. - @param clear clear specified event flags after waiting for them (default: true). - @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). - - @note This function may be called from ISR context if the millisec parameter is set to 0. - */ - uint32_t wait_any_for(uint32_t flags, Kernel::Clock::duration_u32 rel_time, bool clear = true); - - /** Wait for any of the specified event flags to become signaled. - @param flags the flags to wait for. - @param abs_time timeout value. - @param clear clear specified event flags after waiting for them (default: true). - @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). - - @note You cannot call this function from ISR context. - */ - uint32_t wait_any_until(uint32_t flags, Kernel::Clock::time_point abs_time, bool clear = true); - - /** EventFlags destructor. - - @note You cannot call this function from ISR context. - */ - ~EventFlags(); - -private: - void constructor(const char *name = nullptr); - uint32_t wait_for(uint32_t flags, uint32_t opt, Kernel::Clock::duration_u32 rel_time, bool clear); - uint32_t wait_until(uint32_t flags, uint32_t opt, Kernel::Clock::time_point abs_time, bool clear); - -#if MBED_CONF_RTOS_PRESENT - osEventFlagsId_t _id; - mbed_rtos_storage_event_flags_t _obj_mem; -#else - uint32_t _flags; -#endif -}; - -/** @}*/ -/** @}*/ - -} -#endif diff --git a/rtos/Kernel.h b/rtos/Kernel.h deleted file mode 100644 index c653b16..0000000 --- a/rtos/Kernel.h +++ /dev/null @@ -1,143 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017-2019 ARM Limited - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef KERNEL_H -#define KERNEL_H - -#include -#include -#include "rtos/mbed_rtos_types.h" -#include "platform/mbed_toolchain.h" -#if !MBED_CONF_RTOS_PRESENT -#include "platform/source/mbed_os_timer.h" -#endif - - -namespace rtos { - -/** \addtogroup rtos-public-api */ -/** @{*/ - -/** Functions in the Kernel namespace control RTOS kernel information. */ -namespace Kernel { - -namespace impl { -/* Internal integer-returning function. - * - * ARM EABI means that `time_point`s do not get returned in registers, so - * it's worth having the actual exteernal definition return an integer, and only - * convert to `time_point` via the inline function `now()`. - */ -uint64_t get_tick_count(); -} - -/** Read the current RTOS kernel millisecond tick count. - The tick count corresponds to the tick count the RTOS uses for timing - purposes. It increments monotonically from 0 at boot, so it effectively - never wraps. If the underlying RTOS only provides a 32-bit tick count, - this method expands it to 64 bits. - @return RTOS kernel current tick count - @note Mbed OS always uses millisecond RTOS ticks, and this could only wrap - after half a billion years. - @note You cannot call this function from ISR context. - @deprecated Use `Kernel::Clock::now()` to get a chrono time_point instead of an integer millisecond count. - */ -MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Use `Kernel::Clock::now()` to get a chrono time_point instead of an integer millisecond count.") -uint64_t get_ms_count(); - -/** A C++11 chrono TrivialClock for the kernel millisecond tick count - * - * @note To fit better into the chrono framework, Kernel::Clock uses - * std::chrono::milliseconds as its representation, which makes it signed - * and at least 45 bits (so it will be int64_t or equivalent). - */ -struct Clock { - Clock() = delete; - /* Standard TrivialClock fields */ - using duration = std::chrono::milliseconds; - using rep = duration::rep; - using period = duration::period; -#if MBED_CONF_RTOS_PRESENT - using time_point = std::chrono::time_point; -#else - /* In non-RTOS builds, the clock maps directly to the underlying clock, and must - * indicate that here, so we can do implicit conversion internally. - */ - using time_point = std::chrono::time_point; -#endif - static constexpr bool is_steady = true; - static time_point now() - { - return time_point(duration(impl::get_tick_count())); - } - /* Extension to make it easy to use 32-bit durations for some APIs, as we historically have, - * for efficiency. - */ - using duration_u32 = std::chrono::duration; - - /** Lock the clock to ensure it stays running; dummy for API compatibility with HighResClock */ - static void lock() - { - } - - /** Unlock the clock, allowing it to stop during power saving; dummy for API compatibility with HighResClock */ - static void unlock() - { - } -}; - -/** Maximum duration for Kernel::Clock::duration_u32-based APIs - * - * @note As duration_u32-based APIs pass through straight to CMSIS-RTOS, they will - * interpret duration_u32(0xFFFFFFFF) as "wait forever". Indicate maximum - * wait time of 0xFFFFFFFE for these calls (which is ~49 days). - */ -constexpr Clock::duration_u32 wait_for_u32_max{osWaitForever - 1}; - -/** Magic "wait forever" constant for Kernel::Clock::duration_u32-based APIs - * - * Many duration_u32-based APIs treat duration_u32(0xFFFFFFFF) as "wait forever". - */ -constexpr Clock::duration_u32 wait_for_u32_forever{osWaitForever}; - -/** Attach a function to be called by the RTOS idle task. - @param fptr pointer to the function to be called - - @note You may call this function from ISR context. - @note Bare metal profile: This API is not supported. -*/ -void attach_idle_hook(void (*fptr)(void)); - -/** Attach a function to be called when a thread terminates. - @param fptr pointer to the function to be called - - @note You may call this function from ISR context. - @note Bare metal profile: This API is not supported. -*/ -void attach_thread_terminate_hook(void (*fptr)(osThreadId_t id)); - -} // namespace Kernel - -/** @}*/ - -} // namespace rtos -#endif diff --git a/rtos/Mail.h b/rtos/Mail.h deleted file mode 100644 index 7929a17..0000000 --- a/rtos/Mail.h +++ /dev/null @@ -1,386 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019 ARM Limited - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef MAIL_H -#define MAIL_H - -#include -#include - -#include "rtos/Queue.h" -#include "rtos/MemoryPool.h" -#include "rtos/mbed_rtos_types.h" -#include "rtos/mbed_rtos_storage.h" -#include "rtos/mbed_rtos1_types.h" - -#include "platform/mbed_toolchain.h" -#include "platform/mbed_assert.h" -#include "platform/NonCopyable.h" - -#ifndef MBED_NO_GLOBAL_USING_DIRECTIVE -using namespace rtos; -#endif - -#if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY) - -namespace rtos { -/** \addtogroup rtos-public-api */ -/** @{*/ - -/** - * \defgroup rtos_Mail Mail class - * @{ - */ - -/** The Mail class allows you to control, send, receive or wait for mail. - * A mail is a memory block that is sent to a thread or interrupt service routine (ISR). - * @tparam T Data type of a single mail message element. - * @tparam queue_sz Maximum number of mail messages in queue. - * - * @note - * Memory considerations: The mail data store and control structures are part of this class - they do not (themselves) - * allocate memory on the heap, both for the Mbed OS and underlying RTOS objects (static or dynamic RTOS memory - * pools are not being used). - * - * @note - * Bare metal profile: This class is not supported. - */ -template -class Mail : private mbed::NonCopyable > { -public: - /** Create and initialize Mail queue. - * - * @note You cannot call this function from ISR context. - */ - Mail() = default; - - /** Check if the mail queue is empty. - * - * @return State of queue. - * @retval true Mail queue is empty. - * @retval false Mail queue contains mail. - * - * @note You may call this function from ISR context. - */ - bool empty() const - { - return _queue.empty(); - } - - /** Check if the mail queue is full. - * - * @return State of queue. - * @retval true Mail queue is full. - * @retval false Mail queue is not full. - * - * @note You may call this function from ISR context. - */ - bool full() const - { - return _queue.full(); - } - - /** Allocate a memory block of type T, without blocking. - * - * @param millisec Not used (see note). - * - * @return Pointer to memory block that you can fill with mail or nullptr in case error. - * - * @note You may call this function from ISR context. - * @note If blocking is required, use Mail::try_alloc_for or Mail::try_alloc_until - * @deprecated Replaced with try_alloc. In future alloc() will be an untimed blocking call. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_alloc. In future alloc() will be an untimed blocking call.") - T *alloc(MBED_UNUSED uint32_t millisec = 0) - { - return try_alloc(); - } - - /** Allocate a memory block of type T, without blocking. - * - * @return Pointer to memory block that you can fill with mail or nullptr in case error. - * - * @note You may call this function from ISR context. - * @note If blocking is required, use Mail::try_alloc_for or Mail::try_alloc_until - */ - T *try_alloc() - { - return _pool.try_alloc(); - } - - /** Allocate a memory block of type T, optionally blocking. - * - * @param rel_time Timeout value, or Kernel::wait_for_u32_forever. - * - * @return Pointer to memory block that you can fill with mail or nullptr in case error. - * - * @note You may call this function from ISR context if the millisec parameter is set to 0. - */ - T *try_alloc_for(Kernel::Clock::duration_u32 rel_time) - { - return _pool.try_alloc_for(rel_time); - } - - /** Allocate a memory block of type T, optionally blocking. - * - * @param millisec Timeout value, or osWaitForever. - * - * @return Pointer to memory block that you can fill with mail or nullptr in case error. - * - * @note You may call this function from ISR context if the millisec parameter is set to 0. - * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - T *alloc_for(uint32_t millisec) - { - return try_alloc_for(std::chrono::duration(millisec)); - } - - /** Allocate a memory block of type T, blocking. - * - * @param abs_time Absolute timeout time, referenced to Kernel::Clock. - * - * @return Pointer to memory block that you can fill with mail or nullptr 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 *try_alloc_until(Kernel::Clock::time_point abs_time) - { - return _pool.try_alloc_until(abs_time); - } - - /** 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 nullptr 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. - * @deprecated Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` - * rather than `Kernel::get_ms_count() + 5000`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") - T *alloc_until(uint64_t millisec) - { - return try_alloc_until(Kernel::Clock::time_point(std::chrono::duration(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 nullptr in case error. - * - * @note You may call this function from ISR context. - * @note If blocking is required, use Mail::try_calloc_for or Mail::try_calloc_until - * @deprecated Replaced with try_calloc. In future calloc() will be an untimed blocking call. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_calloc. In future calloc() will be an untimed blocking call.") - T *calloc(MBED_UNUSED uint32_t millisec = 0) - { - return try_calloc(); - } - - /** Allocate a memory block of type T, and set memory block to zero. - * - * @return Pointer to memory block that you can fill with mail or nullptr in case error. - * - * @note You may call this function from ISR context. - * @note If blocking is required, use Mail::try_calloc_for or Mail::try_calloc_until - */ - T *try_calloc() - { - return _pool.try_calloc(); - } - - /** Allocate a memory block of type T, optionally blocking, and set memory block to zero. - * - * @param rel_time Timeout value, or Kernel::wait_for_u32_forever. - * - * @return Pointer to memory block that you can fill with mail or nullptr in case error. - * - * @note You may call this function from ISR context if the rel_time parameter is set to 0. - */ - T *try_calloc_for(Kernel::Clock::duration_u32 rel_time) - { - return _pool.try_calloc_for(rel_time); - } - - /** 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 nullptr in case error. - * - * @note You may call this function from ISR context if the millisec parameter is set to 0. - * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - T *calloc_for(uint32_t millisec) - { - return try_calloc_for(std::chrono::duration(millisec)); - } - - /** Allocate a memory block of type T, blocking, and set memory block to zero. - * - * @param abs_time Absolute timeout time, referenced to Kernel::Clock. - * - * @return Pointer to memory block that you can fill with mail or nullptr 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 *try_calloc_until(Kernel::Clock::time_point abs_time) - { - return _pool.try_calloc_until(abs_time); - } - - /** 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 nullptr 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. - * @deprecated Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` - * rather than `Kernel::get_ms_count() + 5000`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") - T *calloc_until(uint64_t millisec) - { - return try_calloc_until(Kernel::Clock::time_point(std::chrono::duration(millisec))); - } - - /** Put a mail in the queue. - * - * @param mptr Memory block previously allocated with Mail::alloc or Mail::calloc. - * - * @return Status code that indicates the execution status of the function (osOK on success). - * See note. - * - * @note You may call this function from ISR context. - * @note As the mail should have already been allocated, and the memory pool is the same size - * as the queue, the put operation should always succeed, despite being implemented with - * Queue::try_put - there is room in the queue for every mail from the pool. Therefore - * use of the return value is deprecated, and the function will return void in future. - */ - osStatus put(T *mptr) - { - bool ok = _queue.try_put(mptr); - MBED_ASSERT(ok); - return ok ? osOK : osErrorResource; - } - - /** Get a mail from the queue. - * - * @param rel_time Timeout value (default: Kernel::wait_for_u32_forever). - * - * @return Event that contains mail information and status code. The status code - * is stored in the status member: - * @a osEventMail Mail successfully received. - * @a osOK No mail is available (and no timeout was specified). - * @a osEventTimeout No mail has arrived during the given timeout period. - * @a osErrorParameter A parameter is invalid or outside of a permitted range. - * - * @note You may call this function from ISR context if the millisec parameter is set to 0. - * @deprecated Replaced with try_get and try_get_for. In future get will be an untimed blocking call. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_get and try_get_for. In future get will be an untimed blocking call.") - osEvent get(uint32_t millisec = osWaitForever) - { - osEvent evt = _queue.get(millisec); - if (evt.status == osEventMessage) { - evt.status = osEventMail; - } - return evt; - } - - /** Get a mail from the queue. - * - * @return Pointer to received mail, or nullptr if none was received. - * - * @note You may call this function from ISR context. - */ - T *try_get() - { - T *mptr = nullptr; - _queue.try_get(&mptr); - return mptr; - } - - /** Get a mail from the queue. - * - * @param rel_time Timeout value or Kernel::wait_for_u32_forever. - * - * @return Pointer to received mail, or nullptr if none was received. - * - * @note You may call this function from ISR context if the millisec parameter is set to 0. - */ - T *try_get_for(Kernel::Clock::duration_u32 rel_time) - { - T *mptr = nullptr; - _queue.try_get_for(rel_time, &mptr); - return mptr; - } - - /** Free a memory block from a mail. - * - * @param mptr Pointer to the memory block that was obtained with Mail::get. - * - * @return Status code that indicates the execution status of the function (osOK on success). - * - * @note You may call this function from ISR context. - */ - osStatus free(T *mptr) - { - return _pool.free(mptr); - } - -private: - Queue _queue; - MemoryPool _pool; -}; - -/** @}*/ -/** @}*/ - -} - -#endif - -#endif - diff --git a/rtos/MemoryPool.h b/rtos/MemoryPool.h deleted file mode 100644 index 9d1c6de..0000000 --- a/rtos/MemoryPool.h +++ /dev/null @@ -1,288 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019 ARM Limited - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef MEMORYPOOL_H -#define MEMORYPOOL_H - -#include -#include - -#include "rtos/mbed_rtos_types.h" -#include "rtos/mbed_rtos1_types.h" -#include "rtos/mbed_rtos_storage.h" -#include "platform/NonCopyable.h" -#include "platform/mbed_assert.h" -#include "Kernel.h" - - -#if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY) -namespace rtos { -/** \addtogroup rtos-public-api */ -/** @{*/ - -/** - * \defgroup rtos_MemoryPool MemoryPool class - * @{ - */ - -/** Define and manage fixed-size memory pools of objects of a given type. - @tparam T data type of a single object (element). - @tparam queue_sz maximum number of objects (elements) in the memory pool. - - @note - Memory considerations: The memory pool data store and control structures will be created on current thread's stack, - both for the mbed OS and underlying RTOS objects (static or dynamic RTOS memory pools are not being used). - - @note - Bare metal profile: This class is not supported. -*/ -template -class MemoryPool : private mbed::NonCopyable > { - MBED_STATIC_ASSERT(pool_sz > 0, "Invalid memory pool size. Must be greater than 0."); -public: - /** Create and Initialize a memory pool. - * - * @note You cannot call this function from ISR context. - */ - MemoryPool() - { - memset(_pool_mem, 0, sizeof(_pool_mem)); - osMemoryPoolAttr_t attr = { 0 }; - attr.mp_mem = _pool_mem; - attr.mp_size = sizeof(_pool_mem); - attr.cb_mem = &_obj_mem; - attr.cb_size = sizeof(_obj_mem); - _id = osMemoryPoolNew(pool_sz, sizeof(T), &attr); - MBED_ASSERT(_id); - } - - /** Destroy a memory pool - * - * @note You cannot call this function from ISR context. - */ - ~MemoryPool() - { - osMemoryPoolDelete(_id); - } - - /** Allocate a memory block from a memory pool, without blocking. - @return address of the allocated memory block or nullptr in case of no memory available. - - @note You may call this function from ISR context. - @deprecated Replaced with try_alloc. In future alloc() will be an untimed blocking call. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_alloc. In future alloc() will be an untimed blocking call.") - T *alloc() - { - return try_alloc(); - } - - /** Allocate a memory block from a memory pool, without blocking. - @return address of the allocated memory block or nullptr in case of no memory available. - - @note You may call this function from ISR context. - */ - T *try_alloc() - { - return (T *)osMemoryPoolAlloc(_id, 0); - } - - /** 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 nullptr in case of no memory available. - - @note You may call this function from ISR context if the millisec parameter is set to 0. - @deprecated Replaced with `try_alloc_for`. For example use `try_alloc_for(5s)` rather than `alloc_for(5000)`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with `try_alloc_for`. For example use `try_alloc_for(5s)` rather than `alloc_for(5000)`.") - T *alloc_for(uint32_t millisec) - { - return try_alloc_for(std::chrono::duration(millisec)); - } - - /** Allocate a memory block from a memory pool, optionally blocking. - @param rel_time timeout value (Kernel::wait_for_u32_forever to wait forever) - @return address of the allocated memory block or nullptr in case of no memory available. - - @note You may call this function from ISR context if the rel_time parameter is set to 0. - */ - T *try_alloc_for(Kernel::Clock::duration_u32 rel_time) - { - return (T *)osMemoryPoolAlloc(_id, rel_time.count()); - } - - /** 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 nullptr 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. - @deprecated Replaced with `try_alloc_until`. For example use `try_alloc_until(Kernel::Clock::now() + 5s)` - rather than `alloc_until(Kernel::get_ms_count() + 5000)`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with `try_alloc_until`. For example use `try_alloc_until(Kernel::Clock::now() + 5s)` rather than `alloc_until(Kernel::get_ms_count() + 5000)`.") - T *alloc_until(uint64_t millisec) - { - return try_alloc_until(Kernel::Clock::time_point(std::chrono::duration(millisec))); - } - - /** Allocate a memory block from a memory pool, blocking. - @param abs_time absolute timeout time, referenced to Kernel::Clock. - @return address of the allocated memory block or nullptr 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 *try_alloc_until(Kernel::Clock::time_point abs_time) - { - Kernel::Clock::time_point now = Kernel::Clock::now(); - Kernel::Clock::duration_u32 rel_time; - if (now >= abs_time) { - rel_time = rel_time.zero(); - } else if (abs_time - now > Kernel::wait_for_u32_max) { - rel_time = Kernel::wait_for_u32_max; - } else { - rel_time = abs_time - now; - } - return try_alloc_for(rel_time); - } - - /** Allocate a memory block from a memory pool, without blocking, and set memory block to zero. - @return address of the allocated memory block or nullptr in case of no memory available. - - @note You may call this function from ISR context. - @deprecated Replaced with try_calloc. In future calloc() will be an untimed blocking call. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_calloc. In future calloc() will be an untimed blocking call.") - T *calloc() - { - return try_calloc(); - } - - /** Allocate a memory block from a memory pool, without blocking, and set memory block to zero. - @return address of the allocated memory block or nullptr in case of no memory available. - - @note You may call this function from ISR context. - */ - T *try_calloc() - { - T *item = try_alloc(); - if (item != nullptr) { - 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 nullptr in case of no memory available. - - @note You may call this function from ISR context if the millisec parameter is set to 0. - @deprecated Replaced with `try_calloc_for`. For example use `try_calloc_for(5s)` rather than `calloc_for(5000)`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with `try_calloc_for`. For example use `try_calloc_for(5s)` rather than `calloc_for(5000)`.") - T *calloc_for(uint32_t millisec) - { - return try_calloc_for(std::chrono::duration(millisec)); - } - - /** Allocate a memory block from a memory pool, optionally blocking, and set memory block to zero. - @param rel_time timeout value (Kernel::wait_for_u32_forever to wait forever) - @return address of the allocated memory block or nullptr in case of no memory available. - - @note You may call this function from ISR context if the rel_time parameter is set to 0. - */ - T *try_calloc_for(Kernel::Clock::duration_u32 rel_time) - { - T *item = try_alloc_for(rel_time); - if (item != nullptr) { - 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 nullptr 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. - @deprecated Replaced with `try_calloc_until`. For example use `try_calloc_until(Kernel::Clock::now() + 5s)` - rather than `calloc_until(Kernel::get_ms_count() + 5000)`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with `try_calloc_until`. For example use `try_calloc_until(Kernel::Clock::now() + 5s)` rather than `calloc_until(Kernel::get_ms_count() + 5000)`.") - T *calloc_until(uint64_t millisec) - { - return try_calloc_until(Kernel::Clock::time_point(std::chrono::duration(millisec))); - } - - /** Allocate a memory block from a memory pool, blocking, and set memory block to zero. - @param abs_time absolute timeout time, referenced to Kernel::Clock. - @return address of the allocated memory block or nullptr 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 *try_calloc_until(Kernel::Clock::time_point abs_time) - { - T *item = try_alloc_until(abs_time); - if (item != nullptr) { - memset(item, 0, sizeof(T)); - } - return item; - } - - /** Free a memory block. - @param block address of the allocated memory block to be freed. - @return osOK on successful deallocation, osErrorParameter if given memory block id - is nullptr or invalid, or osErrorResource if given memory block is in an - invalid memory pool state. - - @note You may call this function from ISR context. - */ - osStatus free(T *block) - { - return osMemoryPoolFree(_id, block); - } - -private: - osMemoryPoolId_t _id; - char _pool_mem[MBED_RTOS_STORAGE_MEM_POOL_MEM_SIZE(pool_sz, sizeof(T))]; - mbed_rtos_storage_mem_pool_t _obj_mem; -}; -/** @}*/ -/** @}*/ -} -#endif -#endif diff --git a/rtos/Mutex.h b/rtos/Mutex.h deleted file mode 100644 index 4996330..0000000 --- a/rtos/Mutex.h +++ /dev/null @@ -1,234 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019 ARM Limited - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef MUTEX_H -#define MUTEX_H - -#include -#include "rtos/mbed_rtos_types.h" -#include "rtos/mbed_rtos1_types.h" -#include "rtos/mbed_rtos_storage.h" -#include "rtos/Kernel.h" - -#include "platform/NonCopyable.h" -#include "platform/ScopedLock.h" -#include "platform/mbed_toolchain.h" - -namespace rtos { -/** \addtogroup rtos-public-api */ -/** @{*/ - -class Mutex; -/** Typedef for the mutex lock - * - * Usage: - * @code - * void foo(Mutex &m) { - * ScopedMutexLock lock(m); - * // Mutex lock protects code in this block - * } - * @endcode - */ -typedef mbed::ScopedLock ScopedMutexLock; - -/** - * \defgroup rtos_Mutex Mutex class - * @{ - */ - -/** The Mutex class is used to synchronize the execution of threads. - This is, for example, used to protect access to a shared resource. - - In bare-metal builds, the Mutex class is a dummy, so lock() and unlock() are no-ops. - - @note You cannot use member functions of this class in ISR context. If you require Mutex functionality within - ISR handler, consider using @a Semaphore. - - @note - Memory considerations: The mutex control structures are created on the current thread's stack, both for the Mbed OS - and underlying RTOS objects (static or dynamic RTOS memory pools are not being used). -*/ -class Mutex : private mbed::NonCopyable { -public: - /** Create and Initialize a Mutex object - * - * @note You cannot call this function from ISR context. - */ - Mutex(); - - /** Create and Initialize a Mutex object - - @param name name to be used for this mutex. It has to stay allocated for the lifetime of the thread. - @note You cannot call this function from ISR context. - */ - Mutex(const char *name); - - /** - Wait until a Mutex becomes available. - - @note You cannot call this function from ISR context. - */ - void lock(); - - /** Try to lock the mutex, and return immediately - @return true if the mutex was acquired, false otherwise. - @note equivalent to trylock_for(0) - - @note You cannot call this function from ISR context. - */ - bool trylock(); - - /** Try to lock the mutex for a specified time - @param millisec timeout value. - @return true if the mutex was acquired, false otherwise. - @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 lock attempt will time out earlier than specified. - - @note You cannot call this function from ISR context. - @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - bool trylock_for(uint32_t millisec); - - /** Try to lock the mutex for a specified time - @param rel_time timeout value. - @return true if the mutex was acquired, false otherwise. - @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 lock attempt will time out earlier than specified. - - @note You cannot call this function from ISR context. - */ - bool trylock_for(Kernel::Clock::duration_u32 rel_time); - - /** Try to lock the mutex until specified time - @param millisec absolute timeout time, referenced to Kernel::get_ms_count() - @return true if the mutex was acquired, false otherwise. - @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 lock attempt will time out earlier than specified. - - @note You cannot call this function from ISR context. - @deprecated Pass a chrono time_point, not an integer millisecond count. For example use - `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") - bool trylock_until(uint64_t millisec); - - /** Try to lock the mutex until specified time - @param abs_time absolute timeout time, referenced to Kernel::get_ms_count() - @return true if the mutex was acquired, false otherwise. - @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 lock attempt will time out earlier than specified. - - @note You cannot call this function from ISR context. - */ - bool trylock_until(Kernel::Clock::time_point abs_time); - - /** - Unlock the mutex that has previously been locked by the same thread - - @note You cannot call this function from ISR context. - */ - void unlock(); - - /** Get the owner the this mutex - @return the current owner of this mutex. - - @note You cannot call this function from ISR context. - */ - osThreadId_t get_owner(); - - /** Mutex destructor - * - * @note You cannot call this function from ISR context. - */ - ~Mutex(); - -private: -#if MBED_CONF_RTOS_PRESENT - void constructor(const char *name = nullptr); - friend class ConditionVariable; - - osMutexId_t _id; - mbed_rtos_storage_mutex_t _obj_mem; - uint32_t _count; -#endif -}; - -#if !MBED_CONF_RTOS_PRESENT -inline Mutex::Mutex() -{ -} - -inline Mutex::Mutex(const char *) -{ -} - -inline Mutex::~Mutex() -{ -} - -inline void Mutex::lock() -{ -} - -inline bool Mutex::trylock() -{ - return true; -} - -inline bool Mutex::trylock_for(uint32_t) -{ - return true; -} - -inline bool Mutex::trylock_for(Kernel::Clock::duration_u32) -{ - return true; -} - -inline bool Mutex::trylock_until(uint64_t) -{ - return true; -} - -inline bool Mutex::trylock_until(Kernel::Clock::time_point) -{ - return true; -} - -inline void Mutex::unlock() -{ -} -#endif - -/** @}*/ -/** @}*/ -} -#endif diff --git a/rtos/Queue.h b/rtos/Queue.h deleted file mode 100644 index 203d122..0000000 --- a/rtos/Queue.h +++ /dev/null @@ -1,348 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019 ARM Limited - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef QUEUE_H -#define QUEUE_H - -#include "rtos/mbed_rtos_types.h" -#include "rtos/mbed_rtos1_types.h" -#include "rtos/mbed_rtos_storage.h" -#include "rtos/Kernel.h" -#include "platform/mbed_error.h" -#include "platform/NonCopyable.h" - -#if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY) - -namespace rtos { -/** \addtogroup rtos-public-api */ -/** @{*/ - -/** - * \defgroup rtos_Queue Queue class - * @{ - */ - -/** The Queue class represents a collection of objects that are stored first by - * order of priority, and then in first-in, first-out (FIFO) order. - * - * You can use a queue when you need to store data and then access it in the same - * order that it has been stored. The order in which you retrieve the data is in - * order of descending priority. If multiple elements have the same priority, - * they are retrieved in FIFO order. - * - * The object type stored in the queue can be an integer, pointer or a generic - * type given by the template parameter T. - * - * @tparam T Specifies the type of elements stored in the queue. - * @tparam queue_sz Maximum number of messages that you can store in the queue. - * - * @note Memory considerations: The queue control structures are created on the - * current thread's stack, both for the Mbed OS and underlying RTOS - * objects (static or dynamic RTOS memory pools are not being used). - * - * @note Bare metal profile: This class is not supported. - */ -template -class Queue : private mbed::NonCopyable > { -public: - /** Create and initialize a message Queue of objects of the parameterized - * type `T` and maximum capacity specified by `queue_sz`. - * - * @note You cannot call this function from ISR context. - */ - Queue() - { - osMessageQueueAttr_t attr = { 0 }; - attr.mq_mem = _queue_mem; - attr.mq_size = sizeof(_queue_mem); - attr.cb_mem = &_obj_mem; - attr.cb_size = sizeof(_obj_mem); - _id = osMessageQueueNew(queue_sz, sizeof(T *), &attr); - MBED_ASSERT(_id); - } - - /** Queue destructor - * - * @note You cannot call this function from ISR context. - */ - ~Queue() - { - osMessageQueueDelete(_id); - } - - /** Check if the queue is empty. - * - * @return True if the queue is empty, false if not - * - * @note You may call this function from ISR context. - */ - bool empty() const - { - return osMessageQueueGetCount(_id) == 0; - } - - /** Check if the queue is full. - * - * @return True if the queue is full, false if not - * - * @note You may call this function from ISR context. - */ - bool full() const - { - return osMessageQueueGetSpace(_id) == 0; - } - - /** Get number of queued messages in the queue. - * - * @return Number of items in the queue - * - * @note You may call this function from ISR context. - */ - uint32_t count() const - { - return osMessageQueueGetCount(_id); - } - - /** Inserts the given element to the end of the queue. - * - * This function puts the message pointed to by `data` into the queue. The - * parameter `prio` is used to sort the message according to their priority - * (higher numbers indicate higher priority) on insertion. - * - * The function does not block, and returns immediately if the queue is full. - * - * @param data Pointer to the element to insert into the queue. - * @param prio Priority of the operation or 0 in case of default. - * (default: 0) - * - * @return true if the element was inserted, false otherwise. - * - * @note You may call this function from ISR context. - */ - bool try_put(T *data, uint8_t prio = 0) - { - return try_put_for(Kernel::Clock::duration_u32::zero(), data, prio); - } - - /** Inserts the given element to the end of the queue. - * - * This function puts the message pointed to by `data` into the queue. The - * parameter `prio` is used to sort the message according to their priority - * (higher numbers indicate higher priority) on insertion. - * - * The timeout indicated by the parameter `rel_time` specifies how long the - * function blocks waiting for the message to be inserted into the - * queue. - * - * The parameter `rel_time` can have the following values: - * - When the duration is 0, the function returns instantly. You could use - * `try_put` instead. - * - When the duration is Kernel::wait_for_u32_forever, the function waits for an - * infinite time. - * - For all other values, the function waits for the given duration. - * - * @param rel_time Timeout for the operation to be executed. - * @param data Pointer to the element to insert into the queue. - * @param prio Priority of the operation or 0 in case of default. - * (default: 0) - * - * @return true if the element was inserted, false otherwise. - * - * @note You may call this function from ISR context if the rel_time - * parameter is set to 0. - * - */ - bool try_put_for(Kernel::Clock::duration_u32 rel_time, T *data, uint8_t prio = 0) - { - osStatus status = osMessageQueuePut(_id, &data, prio, rel_time.count()); - return status == osOK; - } - - /** Inserts the given element to the end of the queue. - * - * This function puts the message pointed to by `data` into the queue. The - * parameter `prio` is used to sort the message according to their priority - * (higher numbers indicate higher priority) on insertion. - * - * The timeout indicated by the parameter `millisec` specifies how long the - * function blocks waiting for the message to be inserted into the - * queue. - * - * The parameter `millisec` can have the following values: - * - When the timeout is 0, the function returns instantly. - * - When the timeout is osWaitForever, the function waits for an - * infinite time. - * - For all other values, the function waits for the given number of - * milliseconds. - * - * @param data Pointer to the element to insert into the queue. - * @param millisec Timeout for the operation to be executed, or 0 in case - * of no timeout. - * @param prio Priority of the operation or 0 in case of default. - * (default: 0) - * - * @return Status code that indicates the execution status of the function: - * @a osOK The message has been successfully inserted - * into the queue. - * @a osErrorTimeout The message could not be inserted into the - * queue in the given time. - * @a osErrorResource The message could not be inserted because - * the queue is full. - * @a osErrorParameter Internal error or nonzero timeout specified - * in an ISR. - * - * @note You may call this function from ISR context if the millisec - * parameter is set to 0. - * @deprecated Replaced with try_put and try_put_for. In future put will be an untimed blocking call. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_put and try_put_for. In future put will be an untimed blocking call.") - osStatus put(T *data, uint32_t millisec = 0, uint8_t prio = 0) - { - return osMessageQueuePut(_id, &data, prio, millisec); - } - - /** Get a message from the queue. - * - * This function retrieves a message from the queue. The message is stored - * in the location pointed to be the parameter `data_out`. - * - * The function does not block, and returns immediately if the queue is empty. - * - * @param[out] data_out Pointer to location to write the element retrieved from the queue. - * - * @return true if an element was received and written to data_out. - * - * @note You may call this function from ISR context. - */ - bool try_get(T **data_out) - { - return try_get_for(Kernel::Clock::duration_u32::zero(), data_out); - } - - /** Get a message or wait for a message from the queue. - * - * This function retrieves a message from the queue. The message is stored - * in the location pointed to be the parameter `data_out`. - * - * The timeout specified by the parameter `rel_time` specifies how long the - * function waits to retrieve the message from the queue. - * - * The timeout parameter can have the following values: - * - When the timeout is 0, the function returns instantly. - * - When the timeout is Kernel::wait_for_u32_forever, the function waits - * infinite time until the message is retrieved. - * - When the timeout is any other value, the function waits for the - * specified time before returning a timeout error. - * - * Messages are retrieved in descending priority order. If two messages - * share the same priority level, they are retrieved in first-in, first-out - * (FIFO) order. - * - * @param rel_time Timeout value. - * @param[out] data_out Pointer to location to write the element retrieved from the queue. - * - * @return true if an element was received and written to data_out. - * - * @note You may call this function from ISR context if the rel_time - * parameter is set to 0. - */ - bool try_get_for(Kernel::Clock::duration_u32 rel_time, T **data_out) - { - osStatus status = osMessageQueueGet(_id, data_out, nullptr, rel_time.count()); - return status == osOK; - } - - /** Get a message or wait for a message from the queue. - * - * This function retrieves a message from the queue. The message is stored - * in the value field of the returned `osEvent` object. - * - * The timeout specified by the parameter `millisec` specifies how long the - * function waits to retrieve the message from the queue. - * - * The timeout parameter can have the following values: - * - When the timeout is 0, the function returns instantly. - * - When the timeout is osWaitForever (default), the function waits - * infinite time until the message is retrieved. - * - When the timeout is any other value, the function waits for the - * specified time before returning a timeout error. - * - * Messages are retrieved in descending priority order. If two messages - * share the same priority level, they are retrieved in first-in, first-out - * (FIFO) order. - * - * @param millisec Timeout value. - * - * @return Event information that includes the message in event. Message - * value and the status code in event.status: - * @a osEventMessage Message successfully received. - * @a osOK No message is available in the queue, and no - * timeout was specified. - * @a osEventTimeout No message was received before a timeout - * event occurred. - * @a osErrorParameter A parameter is invalid or outside of a - * permitted range. - * - * @note You may call this function from ISR context if the millisec - * parameter is set to 0. - * @deprecated Replaced with try_get and try_get_for. In future get will be an untimed blocking call. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_get and try_get_for. In future get will be an untimed blocking call.") - osEvent get(uint32_t millisec = osWaitForever) - { - osEvent event; - T *data = nullptr; - osStatus_t res = osMessageQueueGet(_id, &data, nullptr, millisec); - - switch (res) { - case osOK: - event.status = (osStatus)osEventMessage; - event.value.p = data; - break; - case osErrorResource: - event.status = osOK; - break; - case osErrorTimeout: - event.status = (osStatus)osEventTimeout; - break; - case osErrorParameter: - default: - event.status = osErrorParameter; - break; - } - event.def.message_id = _id; - - return event; - } -private: - osMessageQueueId_t _id; - char _queue_mem[queue_sz * (sizeof(T *) + sizeof(mbed_rtos_storage_message_t))]; - mbed_rtos_storage_msg_queue_t _obj_mem; -}; -/** @}*/ -/** @}*/ - -} // namespace rtos - -#endif - -#endif // QUEUE_H diff --git a/rtos/Semaphore.h b/rtos/Semaphore.h deleted file mode 100644 index c97f568..0000000 --- a/rtos/Semaphore.h +++ /dev/null @@ -1,159 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019 ARM Limited - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef SEMAPHORE_H -#define SEMAPHORE_H - -#include -#include -#include "rtos/mbed_rtos_types.h" -#include "rtos/mbed_rtos1_types.h" -#include "rtos/mbed_rtos_storage.h" -#include "rtos/Kernel.h" -#include "platform/mbed_toolchain.h" -#include "platform/NonCopyable.h" - -namespace rtos { -/** \addtogroup rtos-public-api */ -/** @{*/ - -/** - * \defgroup rtos_Semaphore Semaphore class - * @{ - */ - -/** The Semaphore class is used to manage and protect access to a set of shared resources. - * - * @note - * Memory considerations: The semaphore control structures will be created on current thread's stack, both for the mbed OS - * and underlying RTOS objects (static or dynamic RTOS memory pools are not being used). - */ -class Semaphore : private mbed::NonCopyable { -public: - /** Create and Initialize a Semaphore object used for managing resources. - @param count number of available resources; maximum index value is (count-1). (default: 0). - - @note You cannot call this function from ISR context. - */ - Semaphore(int32_t count = 0); - - /** Create and Initialize a Semaphore object used for managing resources. - @param count number of available resources - @param max_count maximum number of available resources - - @note You cannot call this function from ISR context. - */ - Semaphore(int32_t count, uint16_t max_count); - - /** Wait until a Semaphore resource becomes available. - - @note You cannot call this function from ISR context. - */ - void acquire(); - - /** Try to acquire a Semaphore resource, and return immediately - @return true if a resource was acquired, false otherwise. - @note equivalent to try_acquire_for(0) - - @note You may call this function from ISR context. - */ - bool try_acquire(); - - /** Wait until a Semaphore resource becomes available. - @param millisec timeout value. - @return true if a resource was acquired, false otherwise. - - @note You may call this function from ISR context if the millisec parameter is set to 0. - @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - bool try_acquire_for(uint32_t millisec); - - /** Wait until a Semaphore resource becomes available. - @param rel_time timeout value. - @return true if a resource was acquired, false otherwise. - - @note You may call this function from ISR context if the rel_time parameter is set to 0. - */ - bool try_acquire_for(Kernel::Clock::duration_u32 rel_time); - - /** Wait until a Semaphore resource becomes available. - @param millisec absolute timeout time, referenced to Kernel::get_ms_count() - @return true if a resource was acquired, false otherwise. - @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 acquire attempt will time out earlier than specified. - - @note You cannot call this function from ISR context. - @deprecated Pass a chrono time_point, not an integer millisecond count. For example use - `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`. - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") - bool try_acquire_until(uint64_t millisec); - - /** Wait until a Semaphore resource becomes available. - @param millisec absolute timeout time, referenced to Kernel::get_ms_count() - @return true if a resource was acquired, false otherwise. - @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 acquire attempt will time out earlier than specified. - - @note You cannot call this function from ISR context. - */ - bool try_acquire_until(Kernel::Clock::time_point abs_time); - - /** Release a Semaphore resource that was obtain with Semaphore::acquire. - @return status code that indicates the execution status of the function: - @a osOK the token has been correctly released. - @a osErrorResource the maximum token count has been reached. - @a osErrorParameter internal error. - - @note You may call this function from ISR context. - */ - osStatus release(void); - - /** Semaphore destructor - * - * @note You cannot call this function from ISR context. - */ - ~Semaphore(); - -private: - void constructor(int32_t count, uint16_t max_count); - -#if MBED_CONF_RTOS_PRESENT - int32_t _wait(uint32_t millisec); - - osSemaphoreId_t _id; - mbed_rtos_storage_semaphore_t _obj_mem; -#else - static bool semaphore_available(void *); - int32_t _count; - uint16_t _max_count; -#endif -}; -/** @}*/ -/** @}*/ -} -#endif diff --git a/rtos/ThisThread.h b/rtos/ThisThread.h deleted file mode 100644 index 76c1387..0000000 --- a/rtos/ThisThread.h +++ /dev/null @@ -1,290 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019 ARM Limited - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef THIS_THREAD_H -#define THIS_THREAD_H - -#include -#include "platform/mbed_toolchain.h" -#include "rtos/Kernel.h" -#include "rtos/mbed_rtos_types.h" - -namespace rtos { -/** \addtogroup rtos-public-api */ -/** @{*/ - -/** - * \defgroup rtos_ThisThread ThisThread namespace - * @{ - */ - -/** The ThisThread namespace allows controlling the current thread. - * - * Example: - * @code - * #include "mbed.h" - * #include "rtos.h" - * - * Thread thread; - * DigitalOut led1(LED1); - * - * #define STOP_FLAG 1 - * - * // Blink function toggles the led in a long running loop - * void blink(DigitalOut *led) { - * while (!ThisThread::flags_wait_any_for(STOP_FLAG, 1000)) { - * *led = !*led; - * } - * } - * - * // Spawns a thread to run blink for 5 seconds - * int main() { - * thread.start(callback(blink, &led1)); - * ThisThread::sleep_for(5000); - * thread.signal_set(STOP_FLAG); - * thread.join(); - * } - * @endcode - * - */ -namespace ThisThread { -/** Clears the specified Thread Flags of the currently running thread. - @param flags specifies the flags of the thread that should be cleared. - @return thread flags before clearing. - @note You cannot call this function from ISR context. - @see Thread::flags_set -*/ -uint32_t flags_clear(uint32_t flags); - -/** Returns the Thread Flags currently set for the currently running thread. - @return current thread flags or 0 if not in a valid thread. - @note You cannot call this function from ISR context. - @see Thread::flags_set -*/ -uint32_t flags_get(); - -/** Wait for all of the specified Thread Flags to become signaled for the current thread. - @param flags specifies the flags to wait for - @param clear whether to clear the specified flags after waiting for them. (default: true) - @return actual thread flags before clearing, which will satisfy the wait - @note You cannot call this function from ISR context. - @see Thread::flags_set -*/ -uint32_t flags_wait_all(uint32_t flags, bool clear = true); - -/** Wait for any of the specified Thread Flags to become signaled for the current thread. - @param flags specifies the flags to wait for - @param clear whether to clear the specified flags after waiting for them. (default: true) - @return actual thread flags before clearing, which will satisfy the wait - @note You cannot call this function from ISR context. - @see Thread::flags_set -*/ -uint32_t flags_wait_any(uint32_t flags, bool clear = true); - -/** Wait for all of the specified Thread Flags to become signaled for the current thread. - @param flags specifies the flags to wait for - @param millisec timeout value. - @param clear whether to clear the specified flags after waiting for them. (default: true) - @return actual thread flags before clearing, which may not satisfy the wait - @note You cannot call this function from ISR context. - @see Thread::flags_set - @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. -*/ -MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") -uint32_t flags_wait_all_for(uint32_t flags, uint32_t millisec, bool clear = true); - -/** Wait for all of the specified Thread Flags to become signaled for the current thread. - @param flags specifies the flags to wait for - @param rel_time timeout value. - @param clear whether to clear the specified flags after waiting for them. (default: true) - @return actual thread flags before clearing, which may not satisfy the wait - @note You cannot call this function from ISR context. - @see Thread::flags_set -*/ -uint32_t flags_wait_all_for(uint32_t flags, Kernel::Clock::duration_u32 rel_time, bool clear = true); - -/** Wait for all of the specified Thread Flags to become signaled for the current thread. - @param flags specifies the flags to wait for - @param millisec absolute timeout time, referenced to Kernel::get_ms_count() - @param clear whether to clear the specified flags after waiting for them. (default: true) - @return actual thread flags before clearing, which may not satisfy the wait - @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. - @see Thread::flags_set - @deprecated Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` - rather than `Kernel::get_ms_count() + 5000`. -*/ -MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") -uint32_t flags_wait_all_until(uint32_t flags, uint64_t millisec, bool clear = true); - -/** Wait for all of the specified Thread Flags to become signaled for the current thread. - @param flags specifies the flags to wait for - @param abs_time absolute timeout time, referenced to Kernel::Clock - @param clear whether to clear the specified flags after waiting for them. (default: true) - @return actual thread flags before clearing, which may not satisfy the wait - @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. - @see Thread::flags_set -*/ -uint32_t flags_wait_all_until(uint32_t flags, Kernel::Clock::time_point abs_time, bool clear = true); - -/** Wait for any of the specified Thread Flags to become signaled for the current thread. - @param flags specifies the flags to wait for - @param millisec timeout value. - @param clear whether to clear the specified flags after waiting for them. (default: true) - @return actual thread flags before clearing, which may not satisfy the wait - @note You cannot call this function from ISR context. - @see Thread::flags_set - @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. -*/ -MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") -uint32_t flags_wait_any_for(uint32_t flags, uint32_t millisec, bool clear = true); - -/** Wait for any of the specified Thread Flags to become signaled for the current thread. - @param flags specifies the flags to wait for - @param rel_time timeout value. - @param clear whether to clear the specified flags after waiting for them. (default: true) - @return actual thread flags before clearing, which may not satisfy the wait - @note You cannot call this function from ISR context. - @see Thread::flags_set -*/ -uint32_t flags_wait_any_for(uint32_t flags, Kernel::Clock::duration_u32 rel_time, bool clear = true); - -/** Wait for any of the specified Thread Flags to become signaled for the current thread. - @param flags specifies the flags to wait for - @param millisec absolute timeout time, referenced to Kernel::get_ms_count() - @param clear whether to clear the specified flags after waiting for them. (default: true) - @return actual thread flags before clearing, which may not satisfy the wait - @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. - @see Thread::flags_set - @deprecated Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` - rather than `Kernel::get_ms_count() + 5000`. -*/ -MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") -uint32_t flags_wait_any_until(uint32_t flags, uint64_t millisec, bool clear = true); - -/** Wait for any of the specified Thread Flags to become signaled for the current thread. - @param flags specifies the flags to wait for - @param abs_time absolute timeout time, referenced to Kernel::Clock - @param clear whether to clear the specified flags after waiting for them. (default: true) - @return actual thread flags before clearing, which may not satisfy the wait - @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. - @see Thread::flags_set -*/ -uint32_t flags_wait_any_until(uint32_t flags, Kernel::Clock::time_point abs_time, bool clear = true); - -/** Sleep for a specified time period in millisec: - @param millisec time delay value - @note You cannot call this function from ISR context. - @note The equivalent functionality is accessible in C via thread_sleep_for. - @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. -*/ -MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") -void sleep_for(uint32_t millisec); - -/** Sleep for a specified time period: - @param rel_time time delay value - @note You cannot call this function from ISR context. - @note The equivalent functionality is accessible in C via thread_sleep_for. -*/ -void sleep_for(Kernel::Clock::duration_u32 rel_time); - - -/** Sleep until a specified time in millisec - The specified time is according to Kernel::get_ms_count(). - @param millisec absolute time in millisec - @note You cannot call this function from ISR context. - @note if millisec is equal to or lower than the current tick count, this - returns immediately. - @note The equivalent functionality is accessible in C via thread_sleep_until. - @deprecated Pass a chrono time_point, not an integer millisecond count. For example use - `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`. -*/ -MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") -void sleep_until(uint64_t millisec); - -/** Sleep until a specified time in millisec - The specified time is according to Kernel::Clock. - @param abs_time absolute time - @note You cannot call this function from ISR context. - @note if abs_time is equal to or lower than Kernel::Clock::now(), this - returns immediately. - @note The equivalent functionality is accessible in C via thread_sleep_until. -*/ -void sleep_until(Kernel::Clock::time_point abs_time); - -/** Pass control to next equal-priority thread that is in state READY. - (Higher-priority READY threads would prevent us from running; this - will not enable lower-priority threads to run, as we remain READY). - @note You cannot call this function from ISR context. -*/ -void yield(); - -/** Get the thread id of the current running thread. - @return thread ID for reference by other functions or nullptr in case of error or in ISR context. - @note You may call this function from ISR context. -*/ -osThreadId_t get_id(); - -/** Get the thread name of the current running thread. - @return thread name pointer or nullptr if thread has no name or in case of error. - @note You cannot call this function from ISR context. -*/ -const char *get_name(); - -}; -/** @}*/ -/** @}*/ - - -namespace internal { -/** \addtogroup rtos-internal-api */ -/** @{*/ - -struct flags_check_capture { - uint32_t *flags; - uint32_t options; - uint32_t flags_wanted; - uint32_t result; - bool match; -}; - -bool non_rtos_check_flags(void *handle); - -} -/** @}*/ -} -#endif diff --git a/rtos/Thread.h b/rtos/Thread.h deleted file mode 100644 index 576bb9c..0000000 --- a/rtos/Thread.h +++ /dev/null @@ -1,284 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019 ARM Limited - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef THREAD_H -#define THREAD_H - -#include -#include "rtos/mbed_rtos_types.h" -#include "rtos/mbed_rtos1_types.h" -#include "rtos/mbed_rtos_storage.h" -#include "platform/Callback.h" -#include "platform/mbed_toolchain.h" -#include "platform/NonCopyable.h" -#include "rtos/Semaphore.h" -#include "rtos/Mutex.h" - -#if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY) || defined(UNITTEST) - -namespace rtos { -/** \addtogroup rtos-public-api */ -/** @{*/ - -/** - * \defgroup rtos_Thread Thread class - * @{ - */ - -/** The Thread class allow defining, creating, and controlling thread functions in the system. - * - * Example: - * @code - * #include "mbed.h" - * #include "rtos.h" - * - * Thread thread; - * DigitalOut led1(LED1); - * volatile bool running = true; - * - * // Blink function toggles the led in a long running loop - * void blink(DigitalOut *led) { - * while (running) { - * *led = !*led; - * ThisThread::sleep_for(1000); - * } - * } - * - * // Spawns a thread to run blink for 5 seconds - * int main() { - * thread.start(callback(blink, &led1)); - * ThisThread::sleep_for(5000); - * running = false; - * thread.join(); - * } - * @endcode - * - * @note - * Memory considerations: The thread control structures will be created on current thread's stack, both for the mbed OS - * and underlying RTOS objects (static or dynamic RTOS memory pools are not being used). - * Additionally the stack memory for this thread will be allocated on the heap, if it wasn't supplied to the constructor. - * - * @note - * MBED_TZ_DEFAULT_ACCESS (default:0) flag can be used to change the default access of all user threads in non-secure mode. - * MBED_TZ_DEFAULT_ACCESS set to 1, means all non-secure user threads have access to call secure functions. - * MBED_TZ_DEFAULT_ACCESS set to 0, means none of the non-secure user thread have access to call secure functions, - * to give access to particular thread used overloaded constructor with `tz_module` as argument during thread creation. - * - * MBED_TZ_DEFAULT_ACCESS is target specific define, should be set in targets.json file for Cortex-M23/M33 devices. - * - * @note - * Bare metal profile: This class is not supported. - */ - -class Thread : private mbed::NonCopyable { -public: - /** Allocate a new thread without starting execution - @param priority initial priority of the thread function. (default: osPriorityNormal). - @param stack_size stack size (in bytes) requirements for the thread function. (default: OS_STACK_SIZE). - @param stack_mem pointer to the stack area to be used by this thread (default: nullptr). - @param name name to be used for this thread. It has to stay allocated for the lifetime of the thread (default: nullptr) - - @note Default value of tz_module will be MBED_TZ_DEFAULT_ACCESS - @note You cannot call this function from ISR context. - */ - - Thread(osPriority priority = osPriorityNormal, - uint32_t stack_size = OS_STACK_SIZE, - unsigned char *stack_mem = nullptr, const char *name = nullptr) - { - constructor(priority, stack_size, stack_mem, name); - } - - /** Allocate a new thread without starting execution - @param tz_module trustzone thread identifier (osThreadAttr_t::tz_module) - Context of RTOS threads in non-secure state must be saved when calling secure functions. - tz_module ID is used to allocate context memory for threads, and it can be safely set to zero for - threads not using secure calls at all. See "TrustZone RTOS Context Management" for more details. - @param priority initial priority of the thread function. (default: osPriorityNormal). - @param stack_size stack size (in bytes) requirements for the thread function. (default: OS_STACK_SIZE). - @param stack_mem pointer to the stack area to be used by this thread (default: nullptr). - @param name name to be used for this thread. It has to stay allocated for the lifetime of the thread (default: nullptr) - - @note You cannot call this function from ISR context. - */ - - Thread(uint32_t tz_module, osPriority priority = osPriorityNormal, - uint32_t stack_size = OS_STACK_SIZE, - unsigned char *stack_mem = nullptr, const char *name = nullptr) - { - constructor(tz_module, priority, stack_size, stack_mem, name); - } - - - /** Starts a thread executing the specified function. - @param task function to be executed by this thread. - @return status code that indicates the execution status of the function. - @note a thread can only be started once - - @note You cannot call this function ISR context. - */ - osStatus start(mbed::Callback task); - - /** Wait for thread to terminate - @return status code that indicates the execution status of the function. - - @note You cannot call this function from ISR context. - */ - osStatus join(); - - /** Terminate execution of a thread and remove it from Active Threads - @return status code that indicates the execution status of the function. - - @note You cannot call this function from ISR context. - */ - osStatus terminate(); - - /** Set priority of an active thread - @param priority new priority value for the thread function. - @return status code that indicates the execution status of the function. - - @note You cannot call this function from ISR context. - */ - osStatus set_priority(osPriority priority); - - /** Get priority of an active thread - @return current priority value of the thread function. - - @note You cannot call this function from ISR context. - */ - osPriority get_priority() const; - - /** Set the specified Thread Flags for the thread. - @param flags specifies the flags of the thread that should be set. - @return thread flags after setting or osFlagsError in case of incorrect parameters. - - @note You may call this function from ISR context. - */ - uint32_t flags_set(uint32_t flags); - - /** State of the Thread */ - enum State { - Inactive, /**< NOT USED */ - Ready, /**< Ready to run */ - Running, /**< Running */ - WaitingDelay, /**< Waiting for a delay to occur */ - WaitingJoin, /**< Waiting for thread to join. Only happens when using RTX directly. */ - WaitingThreadFlag, /**< Waiting for a thread flag to be set */ - WaitingEventFlag, /**< Waiting for a event flag to be set */ - WaitingMutex, /**< Waiting for a mutex event to occur */ - WaitingSemaphore, /**< Waiting for a semaphore event to occur */ - WaitingMemoryPool, /**< Waiting for a memory pool */ - WaitingMessageGet, /**< Waiting for message to arrive */ - WaitingMessagePut, /**< Waiting for message to be send */ - WaitingInterval, /**< NOT USED */ - WaitingOr, /**< NOT USED */ - WaitingAnd, /**< NOT USED */ - WaitingMailbox, /**< NOT USED (Mail is implemented as MemoryPool and Queue) */ - - /* Not in sync with RTX below here */ - Deleted, /**< The task has been deleted or not started */ - }; - - /** State of this Thread - @return the State of this Thread - - @note You cannot call this function from ISR context. - */ - State get_state() const; - - /** Get the total stack memory size for this Thread - @return the total stack memory size in bytes - - @note You cannot call this function from ISR context. - */ - uint32_t stack_size() const; - - /** Get the currently unused stack memory for this Thread - @return the currently unused stack memory in bytes - - @note You cannot call this function from ISR context. - */ - uint32_t free_stack() const; - - /** Get the currently used stack memory for this Thread - @return the currently used stack memory in bytes - - @note You cannot call this function from ISR context. - */ - uint32_t used_stack() const; - - /** Get the maximum stack memory usage to date for this Thread - @return the maximum stack memory usage to date in bytes - - @note You cannot call this function from ISR context. - */ - uint32_t max_stack() const; - - /** Get thread name - @return thread name or nullptr if the name was not set. - - @note You may call this function from ISR context. - */ - const char *get_name() const; - - /** Get thread id - @return thread ID for reference by other functions. - - @note You may call this function from ISR context. - */ - osThreadId_t get_id() const; - - /** Thread destructor - * - * @note You cannot call this function from ISR context. - */ - virtual ~Thread(); - -private: - // Required to share definitions without - // delegated constructors - void constructor(osPriority priority = osPriorityNormal, - uint32_t stack_size = OS_STACK_SIZE, - unsigned char *stack_mem = nullptr, - const char *name = nullptr); - void constructor(uint32_t tz_module, - osPriority priority = osPriorityNormal, - uint32_t stack_size = OS_STACK_SIZE, - unsigned char *stack_mem = nullptr, - const char *name = nullptr); - static void _thunk(void *thread_ptr); - - mbed::Callback _task; - osThreadId_t _tid; - osThreadAttr_t _attr; - bool _dynamic_stack; - bool _finished; - Semaphore _join_sem; - mutable Mutex _mutex; - mbed_rtos_storage_thread_t _obj_mem; -}; -/** @}*/ -/** @}*/ -} -#endif - -#endif diff --git a/rtos/include/rtos/ConditionVariable.h b/rtos/include/rtos/ConditionVariable.h new file mode 100644 index 0000000..c90b297 --- /dev/null +++ b/rtos/include/rtos/ConditionVariable.h @@ -0,0 +1,550 @@ +/* Mbed Microcontroller Library + * Copyright (c) 2017-2019 ARM Limited + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef CONDITIONVARIABLE_H +#define CONDITIONVARIABLE_H + +#include +#include +#include "rtos/internal/mbed_rtos_types.h" +#include "rtos/Mutex.h" +#include "rtos/Semaphore.h" +#include "rtos/Kernel.h" + +#include "platform/NonCopyable.h" + +#if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY) + +namespace rtos { +/** \addtogroup rtos-public-api */ +/** @{*/ + +enum class cv_status { + no_timeout, + timeout +}; + +struct Waiter; +/** + * \defgroup rtos_ConditionVariable ConditionVariable class + * @{ + */ + +/** The ConditionVariable class is a synchronization primitive that allows + * threads to wait until a particular condition occurs. + * + * Use the condition variable in conjunction with a mutex to safely wait for + * or notify waiters of condition changes to a resource accessible by multiple + * threads. + * + * The thread that intends to wait on a ConditionVariable must: + * - Acquire a lock on a mutex. + * - Execute `wait`, `wait_for` or `wait_until`. While the thread is waiting, + * the mutex is unlocked. + * - When the condition variable has been notified, or in the case of `wait_for` + * and `wait_until` the timeout expires, the thread is awakened. + * + * The thread that intends to notify a ConditionVariable must: + * - Acquire a lock on the mutex used to construct the condition variable. + * - Execute `notify_one` or `notify_all` on the condition variable. + * + * All threads waiting on the condition variable wake when + * `ConditionVariable::notify_all` is called. + * At least one thread waiting on the condition variable wakes + * when `ConditionVariable::notify_one` is called. + * + * While a thread is waiting for notification of a + * ConditionVariable, it releases the lock held on the mutex. + * The ConditionVariable reacquires the mutex lock before exiting the wait + * function. + * + * #### Unspecified behavior + * - The thread that is unblocked on `ConditionVariable::notify_one` is + * unspecified if there are multiple waiters. + * - When `ConditionVariable::notify_one` or `ConditionVariable::notify_all` is + * called and there are one or more waiters, and one or more threads + * attempting to acquire the condition variable's mutex, the order in which the mutex is + * acquired is unspecified. + * - Spurious notifications (not triggered by the application) can occur. + * + * #### Undefined behavior + * - Calling wait if the mutex is not locked by the current thread is undefined + * behavior. + * - The order in which waiting threads acquire the condition variable's + * mutex after `ConditionVariable::notify_all` is called is undefined. + * - The behavior of `ConditionVariable::wait` and `ConditionVariable::wait_for` + * is undefined if the condition variable's mutex is locked more than once by + * the calling thread. + * + * @note Synchronization level: Thread safe + * + * @note Bare metal profile: This class is not supported. + * + * Example: + * + * @code + * #include "mbed.h" + * + * Mutex mutex; + * ConditionVariable cv(mutex); + * + * // These variables are protected by locking the mutex. + * uint32_t work_count = 0; + * bool done = false; + * + * void worker_thread() + * { + * // Acquire lock on mutex before accessing protected variables and waiting. + * mutex.lock(); + * + * while (done == false) { + * printf("Worker thread: Count: %lu\r\n", work_count); + * + * // Wait for main thread to notify the condition variable. + * printf("Worker thread: Waiting\r\n"); + * cv.wait(); + * } + * + * printf("Worker: Exiting\r\n"); + * + * // The condition variable acquires the lock when exiting the `wait` function. + * // Unlock mutex when exiting the thread. + * mutex.unlock(); + * } + * + * int main() + * { + * Thread thread; + * thread.start(worker_thread); + * + * for (int i = 0; i < 5; i++) { + * // Acquire lock on mutex before modifying variables and notifying. + * mutex.lock(); + * + * // Change count and notify waiters. + * work_count++; + * printf("Main thread: Set count to: %lu\r\n", work_count); + * printf("Main thread: Notifying worker thread\r\n"); + * cv.notify_all(); + * + * // Mutex must be unlocked before the worker thread can acquire it. + * mutex.unlock(); + * + * ThisThread::sleep_for(1000); + * } + * + * // Change done and notify waiters of this. + * mutex.lock(); + * done = true; + * cv.notify_all(); + * mutex.unlock(); + * + * thread.join(); + * + * printf("Main: Exiting\r\n"); + * } + * @endcode + */ + +class ConditionVariable : private mbed::NonCopyable { +public: + /** Create and initialize a ConditionVariable object. + * + * @note You cannot call this function from ISR context. + */ + ConditionVariable(Mutex &mutex); + + /** Wait for a notification. + * + * Wait causes the current thread to block until the condition variable + * receives a notification from another thread. + * + * @note - The thread calling this function must be the owner of the + * ConditionVariable's mutex, and it must be locked exactly once. + * + * @note - Spurious notifications can occur, so the caller of this API + * should check to make sure the condition the caller is waiting on has + * been met. + * + * @note - The current thread releases the mutex while inside the wait + * function and reacquires it upon exiting the function. + * + * Example: + * @code + * mutex.lock(); + * + * while (!condition_met) { + * cond.wait(); + * } + * + * function_to_handle_condition(); + * + * mutex.unlock(); + * @endcode + * + * @note You cannot call this function from ISR context. + */ + void wait(); + + /** Wait for a predicate. + * + * Wait causes the current thread to block until the predicate is + * true. + * + * @param pred A function-like object such that `pred()` is convertible to bool + * + * @note - The thread calling this function must be the owner of the + * ConditionVariable's mutex, and it must be locked exactly once. + * + * @note - The current thread releases the mutex while inside the wait + * function and reacquires it upon exiting the function. + * + * Example: + * @code + * extern bool data_available(); + * + * mutex.lock(); + * + * cond.wait(data_available); + * + * function_to_handle_data(); + * + * mutex.unlock(); + * @endcode + * + * @note You cannot call this function from ISR context. + */ + template + void wait(Predicate pred) + { + while (!pred()) { + wait(); + } + } + + + /** Wait for a notification until the specified time. + * + * Wait until causes the current thread to block until the condition + * variable is notified, or a specific time given by millisec parameter is + * reached. + * + * @param millisec Absolute end time referenced to `Kernel::get_ms_count()` + * @return `true` if a timeout occurred, `false` otherwise. + * + * @note - The thread calling this function must be the owner of the + * ConditionVariable's mutex, and it must be locked exactly once. + * + * @note - Spurious notifications can occur, so the caller of this API + * should check to make sure the condition the caller is waiting on has + * been met. + * + * @note - The current thread releases the lock while inside the wait + * function and reacquires it upon exiting the function. + * + * Example: + * @code + * mutex.lock(); + * uint64_t end_time = Kernel::get_ms_count() + COND_WAIT_TIMEOUT; + * + * while (!condition_met) { + * if (cond.wait_until(end_time)) { + * break; + * } + * } + * + * if (condition_met) { + * function_to_handle_condition(); + * } + * + * mutex.unlock(); + * @endcode + * + * @note You cannot call this function from ISR context. + * @deprecated Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` + * rather than `Kernel::get_ms_count() + 5000`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") + bool wait_until(uint64_t millisec); + + /** Wait for a notification until the specified time. + * + * Wait until causes the current thread to block until the condition + * variable is notified, or a specific time given by millisec parameter is + * reached. + * + * @param abs_time Absolute end time referenced to `Kernel::Clock` + * @return `cv_status::timeout` if a timeout occurred, `cv_status::no_timeout` otherwise. + * + * @note - The thread calling this function must be the owner of the + * ConditionVariable's mutex, and it must be locked exactly once. + * + * @note - Spurious notifications can occur, so the caller of this API + * should check to make sure the condition the caller is waiting on has + * been met. + * + * @note - The current thread releases the lock while inside the wait + * function and reacquires it upon exiting the function. + * + * Example: + * @code + * mutex.lock(); + * Kernel::Clock::time_point end_time = Kernel::Clock::now() + 2s; + * + * while (!condition_met) { + * if (cond.wait_until(end_time) == cv_status::timeout) { + * break; + * } + * } + * + * if (condition_met) { + * function_to_handle_condition(); + * } + * + * mutex.unlock(); + * @endcode + * + * @note You cannot call this function from ISR context. + */ + cv_status wait_until(Kernel::Clock::time_point abs_time); + + /** Wait for a predicate until the specified time. + * + * Wait until causes the current thread to block until the predicate is true, + * or a specific time given by abs_time parameter is reached. + * + * @param abs_time Absolute end time referenced to `Kernel::Clock` + * @param pred A function-like object such that `pred()` is convertible to bool + * @return The state of the predicate + * + * @note - The thread calling this function must be the owner of the + * ConditionVariable's mutex, and it must be locked exactly once. + * + * @note - The current thread releases the mutex while inside the wait + * function and reacquires it upon exiting the function. + * + * Example: + * @code + * extern bool data_available(); + * + * mutex.lock(); + * + * if (cond.wait_until(Kernel::Clock::now() + 2s, data_available)) { + * function_to_handle_data(); + * } + * + * mutex.unlock(); + * @endcode + * + * @note You cannot call this function from ISR context. + */ + template + bool wait_until(Kernel::Clock::time_point abs_time, Predicate pred) + { + while (!pred()) { + if (wait_until(abs_time) == cv_status::timeout) { + return pred(); + } + } + return true; + } + + /** Wait for a notification or timeout. + * + * `Wait for` causes the current thread to block until the condition + * variable receives a notification from another thread, or the timeout + * specified by the millisec parameter is reached. + * + * @param millisec Timeout value or osWaitForever in case of no timeout. + * @return `true` if a timeout occurred, `false` otherwise. + * + * @note - The thread calling this function must be the owner of the + * ConditionVariable's mutex, and it must be locked exactly once. + * + * @note - Spurious notifications can occur, so the caller of this API + * should check to make sure the condition the caller is waiting on has + * been met. + * + * @note - The current thread releases the lock while inside the wait + * function and reacquire it upon exiting the function. + * + * Example: + * @code + * mutex.lock(); + * + * while (!condition_met) { + * cond.wait_for(MAX_SLEEP_TIME); + * if (!condition_met) { + * do_other_work_while_condition_false(); + * } + * } + * + * if (condition_met) { + * function_to_handle_condition(); + * } + * + * mutex.unlock(); + * @endcode + * + * @note You cannot call this function from ISR context. + * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + bool wait_for(uint32_t millisec); + + /** Wait for a notification or timeout. + * + * `Wait for` causes the current thread to block until the condition + * variable receives a notification from another thread, or the timeout + * specified by the millisec parameter is reached. + * + * @param rel_time Timeout value. + * @return `cv_status::timeout` if a timeout occurred, `cv_status::no_timeout` otherwise. + * + * @note - The thread calling this function must be the owner of the + * ConditionVariable's mutex, and it must be locked exactly once. + * + * @note - Spurious notifications can occur, so the caller of this API + * should check to make sure the condition the caller is waiting on has + * been met. + * + * @note - The current thread releases the lock while inside the wait + * function and reacquire it upon exiting the function. + * + * Example: + * @code + * mutex.lock(); + * + * while (!condition_met) { + * cond.wait_for(MAX_SLEEP_TIME); + * if (!condition_met) { + * do_other_work_while_condition_false(); + * } + * } + * + * if (condition_met) { + * function_to_handle_condition(); + * } + * + * mutex.unlock(); + * @endcode + * + * @note You cannot call this function from ISR context. + */ + cv_status wait_for(Kernel::Clock::duration_u32 rel_time); + + /** Wait for a predicate or timeout. + * + * `Wait for` causes the current thread to block until the predicate + * is true, or the timeout specified by the rel_time parameter is reached. + * + * @param rel_time Timeout value. + * @param pred a function-like object such that `pred()` is convertible to bool + * @return The state of the predicate + * + * @note - The thread calling this function must be the owner of the + * ConditionVariable's mutex, and it must be locked exactly once. + * + * @note - The current thread releases the mutex while inside the wait + * function and reacquire it upon exiting the function. + * + * Example: + * @code + * extern bool data_available(); + * + * mutex.lock(); + * + * if (cond.wait_for(2s, data_available)) { + * function_to_handle_data(); + * } + * + * mutex.unlock(); + * @endcode + * + * @note You cannot call this function from ISR context. + */ + template + bool wait_for(Kernel::Clock::duration rel_time, Predicate pred) + { + return wait_until(Kernel::Clock::now() + rel_time, std::move(pred)); + } + + /** Notify one waiter on this condition variable that a condition changed. + * + * This function unblocks one of the threads waiting for the condition + * variable. + * + * @note - The thread calling this function must be the owner of the + * ConditionVariable's mutex. + * + * @note - The thread that is unblocked on ConditionVariable::notify_one is + * undefined if there are multiple waiters. + * + * @note You cannot call this function from ISR context. + */ + void notify_one(); + + /** Notify all waiters on this condition variable that a condition changed. + * + * This function unblocks all of the threads waiting for the condition + * variable. + * + * @note - The thread calling this function must be the owner of the + * ConditionVariable's mutex. + * + * @note - If there are one or more waiters and one or more threads + * attempting to acquire the condition variable's mutex the order in which + * the mutex is acquired is undefined. + * + * @note You cannot call this function from ISR context. + */ + void notify_all(); + + /** ConditionVariable destructor. + * + * @note You cannot call this function from ISR context. + */ + ~ConditionVariable(); + +#if !defined(DOXYGEN_ONLY) +protected: + struct Waiter { + Waiter(); + Semaphore sem; + Waiter *prev; + Waiter *next; + bool in_list; + }; + + static void _add_wait_list(Waiter **wait_list, Waiter *waiter); + static void _remove_wait_list(Waiter **wait_list, Waiter *waiter); + Mutex &_mutex; + Waiter *_wait_list; +#endif // !defined(DOXYGEN_ONLY) +}; + +/** @}*/ +/** @}*/ +} // namespace rtos +#endif + +#endif diff --git a/rtos/include/rtos/EventFlags.h b/rtos/include/rtos/EventFlags.h new file mode 100644 index 0000000..1869702 --- /dev/null +++ b/rtos/include/rtos/EventFlags.h @@ -0,0 +1,175 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EVENT_FLAG_H +#define EVENT_FLAG_H + +#include +#include +#include "rtos/Kernel.h" +#include "rtos/internal/mbed_rtos_types.h" +#include "rtos/internal/mbed_rtos1_types.h" +#include "rtos/internal/mbed_rtos_storage.h" + +#include "platform/NonCopyable.h" + +namespace rtos { +/** \addtogroup rtos-public-api */ +/** @{*/ + +/** + * \defgroup rtos_EventFlags EventFlags class + * @{ + */ + +/** The EventFlags class is used to control event flags or wait for event flags other threads control. + + @note + EventFlags support 31 flags. The MSB flag is ignored. It is used to return an error code (@a osFlagsError). + + @note + Memory considerations: The EventFlags control structures will be created on the current thread's stack, both for the Mbed OS + and underlying RTOS objects (static or dynamic RTOS memory pools are not being used). +*/ +class EventFlags : private mbed::NonCopyable { +public: + /** Create and initialize an EventFlags object. + * + * @note You cannot call this function from ISR context. + */ + EventFlags(); + + /** Create and initialize an EventFlags object. + + @param name name to be used for this EventFlags. It has to stay allocated for the lifetime of the thread. + + @note You cannot call this function from ISR context. + */ + EventFlags(const char *name); + + /** Set the specified event flags. + @param flags the flags that will be set. + @return event flags after setting or error code if highest bit set (see @a osFlagsError for details). + + @note This function may be called from ISR context. + */ + uint32_t set(uint32_t flags); + + /** Clear the specified event flags. + @param flags the flags that will be cleared (default: 0x7fffffff -- all flags). + @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). + + @note You may call this function from ISR context. + */ + uint32_t clear(uint32_t flags = 0x7fffffff); + + /** Get the currently set event flags. + @return current event flags. + + @note You may call this function from ISR context. + */ + uint32_t get() const; + + /** Wait for all of the specified event flags to become signaled. + @param flags the flags to wait for (default: 0 -- no flags). + @param millisec timeout value (default: osWaitForever). + @param clear clear specified event flags after waiting for them (default: true). + @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). + + @note You may call this function from ISR context if the millisec parameter is set to 0. + */ + uint32_t wait_all(uint32_t flags = 0, uint32_t millisec = osWaitForever, bool clear = true); + + /** Wait for all of the specified event flags to become signaled. + @param flags the flags to wait for. + @param rel_time timeout value. + @param clear clear specified event flags after waiting for them (default: true). + @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). + + @note You may call this function from ISR context if the rel_time parameter is set to 0. + */ + uint32_t wait_all_for(uint32_t flags, Kernel::Clock::duration_u32 rel_time, bool clear = true); + + /** Wait for all of the specified event flags to become signaled. + @param flags the flags to wait for. + @param abs_time timeout value. + @param clear clear specified event flags after waiting for them (default: true). + @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). + + @note You cannot call this function from ISR context. + */ + uint32_t wait_all_until(uint32_t flags, Kernel::Clock::time_point abs_time, bool clear = true); + + /** Wait for any of the specified event flags to become signaled. + @param flags the flags to wait for (default: 0 -- no flags). + @param millisec timeout value (default: osWaitForever). + @param clear clear specified event flags after waiting for them (default: true). + @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). + + @note This function may be called from ISR context if the millisec parameter is set to 0. + */ + uint32_t wait_any(uint32_t flags = 0, uint32_t millisec = osWaitForever, bool clear = true); + + /** Wait for any of the specified event flags to become signaled. + @param flags the flags to wait for. + @param rel_time timeout value. + @param clear clear specified event flags after waiting for them (default: true). + @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). + + @note This function may be called from ISR context if the millisec parameter is set to 0. + */ + uint32_t wait_any_for(uint32_t flags, Kernel::Clock::duration_u32 rel_time, bool clear = true); + + /** Wait for any of the specified event flags to become signaled. + @param flags the flags to wait for. + @param abs_time timeout value. + @param clear clear specified event flags after waiting for them (default: true). + @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). + + @note You cannot call this function from ISR context. + */ + uint32_t wait_any_until(uint32_t flags, Kernel::Clock::time_point abs_time, bool clear = true); + + /** EventFlags destructor. + + @note You cannot call this function from ISR context. + */ + ~EventFlags(); + +private: + void constructor(const char *name = nullptr); + uint32_t wait_for(uint32_t flags, uint32_t opt, Kernel::Clock::duration_u32 rel_time, bool clear); + uint32_t wait_until(uint32_t flags, uint32_t opt, Kernel::Clock::time_point abs_time, bool clear); + +#if MBED_CONF_RTOS_PRESENT + osEventFlagsId_t _id; + mbed_rtos_storage_event_flags_t _obj_mem; +#else + uint32_t _flags; +#endif +}; + +/** @}*/ +/** @}*/ + +} +#endif diff --git a/rtos/include/rtos/Kernel.h b/rtos/include/rtos/Kernel.h new file mode 100644 index 0000000..63d4dad --- /dev/null +++ b/rtos/include/rtos/Kernel.h @@ -0,0 +1,143 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2019 ARM Limited + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef KERNEL_H +#define KERNEL_H + +#include +#include +#include "rtos/internal/mbed_rtos_types.h" +#include "platform/mbed_toolchain.h" +#if !MBED_CONF_RTOS_PRESENT +#include "platform/source/mbed_os_timer.h" +#endif + + +namespace rtos { + +/** \addtogroup rtos-public-api */ +/** @{*/ + +/** Functions in the Kernel namespace control RTOS kernel information. */ +namespace Kernel { + +namespace impl { +/* Internal integer-returning function. + * + * ARM EABI means that `time_point`s do not get returned in registers, so + * it's worth having the actual exteernal definition return an integer, and only + * convert to `time_point` via the inline function `now()`. + */ +uint64_t get_tick_count(); +} + +/** Read the current RTOS kernel millisecond tick count. + The tick count corresponds to the tick count the RTOS uses for timing + purposes. It increments monotonically from 0 at boot, so it effectively + never wraps. If the underlying RTOS only provides a 32-bit tick count, + this method expands it to 64 bits. + @return RTOS kernel current tick count + @note Mbed OS always uses millisecond RTOS ticks, and this could only wrap + after half a billion years. + @note You cannot call this function from ISR context. + @deprecated Use `Kernel::Clock::now()` to get a chrono time_point instead of an integer millisecond count. + */ +MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Use `Kernel::Clock::now()` to get a chrono time_point instead of an integer millisecond count.") +uint64_t get_ms_count(); + +/** A C++11 chrono TrivialClock for the kernel millisecond tick count + * + * @note To fit better into the chrono framework, Kernel::Clock uses + * std::chrono::milliseconds as its representation, which makes it signed + * and at least 45 bits (so it will be int64_t or equivalent). + */ +struct Clock { + Clock() = delete; + /* Standard TrivialClock fields */ + using duration = std::chrono::milliseconds; + using rep = duration::rep; + using period = duration::period; +#if MBED_CONF_RTOS_PRESENT + using time_point = std::chrono::time_point; +#else + /* In non-RTOS builds, the clock maps directly to the underlying clock, and must + * indicate that here, so we can do implicit conversion internally. + */ + using time_point = std::chrono::time_point; +#endif + static constexpr bool is_steady = true; + static time_point now() + { + return time_point(duration(impl::get_tick_count())); + } + /* Extension to make it easy to use 32-bit durations for some APIs, as we historically have, + * for efficiency. + */ + using duration_u32 = std::chrono::duration; + + /** Lock the clock to ensure it stays running; dummy for API compatibility with HighResClock */ + static void lock() + { + } + + /** Unlock the clock, allowing it to stop during power saving; dummy for API compatibility with HighResClock */ + static void unlock() + { + } +}; + +/** Maximum duration for Kernel::Clock::duration_u32-based APIs + * + * @note As duration_u32-based APIs pass through straight to CMSIS-RTOS, they will + * interpret duration_u32(0xFFFFFFFF) as "wait forever". Indicate maximum + * wait time of 0xFFFFFFFE for these calls (which is ~49 days). + */ +constexpr Clock::duration_u32 wait_for_u32_max{osWaitForever - 1}; + +/** Magic "wait forever" constant for Kernel::Clock::duration_u32-based APIs + * + * Many duration_u32-based APIs treat duration_u32(0xFFFFFFFF) as "wait forever". + */ +constexpr Clock::duration_u32 wait_for_u32_forever{osWaitForever}; + +/** Attach a function to be called by the RTOS idle task. + @param fptr pointer to the function to be called + + @note You may call this function from ISR context. + @note Bare metal profile: This API is not supported. +*/ +void attach_idle_hook(void (*fptr)(void)); + +/** Attach a function to be called when a thread terminates. + @param fptr pointer to the function to be called + + @note You may call this function from ISR context. + @note Bare metal profile: This API is not supported. +*/ +void attach_thread_terminate_hook(void (*fptr)(osThreadId_t id)); + +} // namespace Kernel + +/** @}*/ + +} // namespace rtos +#endif diff --git a/rtos/include/rtos/Mail.h b/rtos/include/rtos/Mail.h new file mode 100644 index 0000000..65d40e5 --- /dev/null +++ b/rtos/include/rtos/Mail.h @@ -0,0 +1,386 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef MAIL_H +#define MAIL_H + +#include +#include + +#include "rtos/Queue.h" +#include "rtos/MemoryPool.h" +#include "rtos/internal/mbed_rtos_types.h" +#include "rtos/internal/mbed_rtos_storage.h" +#include "rtos/internal/mbed_rtos1_types.h" + +#include "platform/mbed_toolchain.h" +#include "platform/mbed_assert.h" +#include "platform/NonCopyable.h" + +#ifndef MBED_NO_GLOBAL_USING_DIRECTIVE +using namespace rtos; +#endif + +#if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY) + +namespace rtos { +/** \addtogroup rtos-public-api */ +/** @{*/ + +/** + * \defgroup rtos_Mail Mail class + * @{ + */ + +/** The Mail class allows you to control, send, receive or wait for mail. + * A mail is a memory block that is sent to a thread or interrupt service routine (ISR). + * @tparam T Data type of a single mail message element. + * @tparam queue_sz Maximum number of mail messages in queue. + * + * @note + * Memory considerations: The mail data store and control structures are part of this class - they do not (themselves) + * allocate memory on the heap, both for the Mbed OS and underlying RTOS objects (static or dynamic RTOS memory + * pools are not being used). + * + * @note + * Bare metal profile: This class is not supported. + */ +template +class Mail : private mbed::NonCopyable > { +public: + /** Create and initialize Mail queue. + * + * @note You cannot call this function from ISR context. + */ + Mail() = default; + + /** Check if the mail queue is empty. + * + * @return State of queue. + * @retval true Mail queue is empty. + * @retval false Mail queue contains mail. + * + * @note You may call this function from ISR context. + */ + bool empty() const + { + return _queue.empty(); + } + + /** Check if the mail queue is full. + * + * @return State of queue. + * @retval true Mail queue is full. + * @retval false Mail queue is not full. + * + * @note You may call this function from ISR context. + */ + bool full() const + { + return _queue.full(); + } + + /** Allocate a memory block of type T, without blocking. + * + * @param millisec Not used (see note). + * + * @return Pointer to memory block that you can fill with mail or nullptr in case error. + * + * @note You may call this function from ISR context. + * @note If blocking is required, use Mail::try_alloc_for or Mail::try_alloc_until + * @deprecated Replaced with try_alloc. In future alloc() will be an untimed blocking call. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_alloc. In future alloc() will be an untimed blocking call.") + T *alloc(MBED_UNUSED uint32_t millisec = 0) + { + return try_alloc(); + } + + /** Allocate a memory block of type T, without blocking. + * + * @return Pointer to memory block that you can fill with mail or nullptr in case error. + * + * @note You may call this function from ISR context. + * @note If blocking is required, use Mail::try_alloc_for or Mail::try_alloc_until + */ + T *try_alloc() + { + return _pool.try_alloc(); + } + + /** Allocate a memory block of type T, optionally blocking. + * + * @param rel_time Timeout value, or Kernel::wait_for_u32_forever. + * + * @return Pointer to memory block that you can fill with mail or nullptr in case error. + * + * @note You may call this function from ISR context if the millisec parameter is set to 0. + */ + T *try_alloc_for(Kernel::Clock::duration_u32 rel_time) + { + return _pool.try_alloc_for(rel_time); + } + + /** Allocate a memory block of type T, optionally blocking. + * + * @param millisec Timeout value, or osWaitForever. + * + * @return Pointer to memory block that you can fill with mail or nullptr in case error. + * + * @note You may call this function from ISR context if the millisec parameter is set to 0. + * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + T *alloc_for(uint32_t millisec) + { + return try_alloc_for(std::chrono::duration(millisec)); + } + + /** Allocate a memory block of type T, blocking. + * + * @param abs_time Absolute timeout time, referenced to Kernel::Clock. + * + * @return Pointer to memory block that you can fill with mail or nullptr 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 *try_alloc_until(Kernel::Clock::time_point abs_time) + { + return _pool.try_alloc_until(abs_time); + } + + /** 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 nullptr 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. + * @deprecated Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` + * rather than `Kernel::get_ms_count() + 5000`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") + T *alloc_until(uint64_t millisec) + { + return try_alloc_until(Kernel::Clock::time_point(std::chrono::duration(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 nullptr in case error. + * + * @note You may call this function from ISR context. + * @note If blocking is required, use Mail::try_calloc_for or Mail::try_calloc_until + * @deprecated Replaced with try_calloc. In future calloc() will be an untimed blocking call. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_calloc. In future calloc() will be an untimed blocking call.") + T *calloc(MBED_UNUSED uint32_t millisec = 0) + { + return try_calloc(); + } + + /** Allocate a memory block of type T, and set memory block to zero. + * + * @return Pointer to memory block that you can fill with mail or nullptr in case error. + * + * @note You may call this function from ISR context. + * @note If blocking is required, use Mail::try_calloc_for or Mail::try_calloc_until + */ + T *try_calloc() + { + return _pool.try_calloc(); + } + + /** Allocate a memory block of type T, optionally blocking, and set memory block to zero. + * + * @param rel_time Timeout value, or Kernel::wait_for_u32_forever. + * + * @return Pointer to memory block that you can fill with mail or nullptr in case error. + * + * @note You may call this function from ISR context if the rel_time parameter is set to 0. + */ + T *try_calloc_for(Kernel::Clock::duration_u32 rel_time) + { + return _pool.try_calloc_for(rel_time); + } + + /** 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 nullptr in case error. + * + * @note You may call this function from ISR context if the millisec parameter is set to 0. + * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + T *calloc_for(uint32_t millisec) + { + return try_calloc_for(std::chrono::duration(millisec)); + } + + /** Allocate a memory block of type T, blocking, and set memory block to zero. + * + * @param abs_time Absolute timeout time, referenced to Kernel::Clock. + * + * @return Pointer to memory block that you can fill with mail or nullptr 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 *try_calloc_until(Kernel::Clock::time_point abs_time) + { + return _pool.try_calloc_until(abs_time); + } + + /** 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 nullptr 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. + * @deprecated Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` + * rather than `Kernel::get_ms_count() + 5000`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") + T *calloc_until(uint64_t millisec) + { + return try_calloc_until(Kernel::Clock::time_point(std::chrono::duration(millisec))); + } + + /** Put a mail in the queue. + * + * @param mptr Memory block previously allocated with Mail::alloc or Mail::calloc. + * + * @return Status code that indicates the execution status of the function (osOK on success). + * See note. + * + * @note You may call this function from ISR context. + * @note As the mail should have already been allocated, and the memory pool is the same size + * as the queue, the put operation should always succeed, despite being implemented with + * Queue::try_put - there is room in the queue for every mail from the pool. Therefore + * use of the return value is deprecated, and the function will return void in future. + */ + osStatus put(T *mptr) + { + bool ok = _queue.try_put(mptr); + MBED_ASSERT(ok); + return ok ? osOK : osErrorResource; + } + + /** Get a mail from the queue. + * + * @param rel_time Timeout value (default: Kernel::wait_for_u32_forever). + * + * @return Event that contains mail information and status code. The status code + * is stored in the status member: + * @a osEventMail Mail successfully received. + * @a osOK No mail is available (and no timeout was specified). + * @a osEventTimeout No mail has arrived during the given timeout period. + * @a osErrorParameter A parameter is invalid or outside of a permitted range. + * + * @note You may call this function from ISR context if the millisec parameter is set to 0. + * @deprecated Replaced with try_get and try_get_for. In future get will be an untimed blocking call. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_get and try_get_for. In future get will be an untimed blocking call.") + osEvent get(uint32_t millisec = osWaitForever) + { + osEvent evt = _queue.get(millisec); + if (evt.status == osEventMessage) { + evt.status = osEventMail; + } + return evt; + } + + /** Get a mail from the queue. + * + * @return Pointer to received mail, or nullptr if none was received. + * + * @note You may call this function from ISR context. + */ + T *try_get() + { + T *mptr = nullptr; + _queue.try_get(&mptr); + return mptr; + } + + /** Get a mail from the queue. + * + * @param rel_time Timeout value or Kernel::wait_for_u32_forever. + * + * @return Pointer to received mail, or nullptr if none was received. + * + * @note You may call this function from ISR context if the millisec parameter is set to 0. + */ + T *try_get_for(Kernel::Clock::duration_u32 rel_time) + { + T *mptr = nullptr; + _queue.try_get_for(rel_time, &mptr); + return mptr; + } + + /** Free a memory block from a mail. + * + * @param mptr Pointer to the memory block that was obtained with Mail::get. + * + * @return Status code that indicates the execution status of the function (osOK on success). + * + * @note You may call this function from ISR context. + */ + osStatus free(T *mptr) + { + return _pool.free(mptr); + } + +private: + Queue _queue; + MemoryPool _pool; +}; + +/** @}*/ +/** @}*/ + +} + +#endif + +#endif + diff --git a/rtos/include/rtos/MemoryPool.h b/rtos/include/rtos/MemoryPool.h new file mode 100644 index 0000000..6ed7804 --- /dev/null +++ b/rtos/include/rtos/MemoryPool.h @@ -0,0 +1,288 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef MEMORYPOOL_H +#define MEMORYPOOL_H + +#include +#include + +#include "rtos/internal/mbed_rtos_types.h" +#include "rtos/internal/mbed_rtos1_types.h" +#include "rtos/internal/mbed_rtos_storage.h" +#include "platform/NonCopyable.h" +#include "platform/mbed_assert.h" +#include "rtos/Kernel.h" + + +#if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY) +namespace rtos { +/** \addtogroup rtos-public-api */ +/** @{*/ + +/** + * \defgroup rtos_MemoryPool MemoryPool class + * @{ + */ + +/** Define and manage fixed-size memory pools of objects of a given type. + @tparam T data type of a single object (element). + @tparam queue_sz maximum number of objects (elements) in the memory pool. + + @note + Memory considerations: The memory pool data store and control structures will be created on current thread's stack, + both for the mbed OS and underlying RTOS objects (static or dynamic RTOS memory pools are not being used). + + @note + Bare metal profile: This class is not supported. +*/ +template +class MemoryPool : private mbed::NonCopyable > { + MBED_STATIC_ASSERT(pool_sz > 0, "Invalid memory pool size. Must be greater than 0."); +public: + /** Create and Initialize a memory pool. + * + * @note You cannot call this function from ISR context. + */ + MemoryPool() + { + memset(_pool_mem, 0, sizeof(_pool_mem)); + osMemoryPoolAttr_t attr = { 0 }; + attr.mp_mem = _pool_mem; + attr.mp_size = sizeof(_pool_mem); + attr.cb_mem = &_obj_mem; + attr.cb_size = sizeof(_obj_mem); + _id = osMemoryPoolNew(pool_sz, sizeof(T), &attr); + MBED_ASSERT(_id); + } + + /** Destroy a memory pool + * + * @note You cannot call this function from ISR context. + */ + ~MemoryPool() + { + osMemoryPoolDelete(_id); + } + + /** Allocate a memory block from a memory pool, without blocking. + @return address of the allocated memory block or nullptr in case of no memory available. + + @note You may call this function from ISR context. + @deprecated Replaced with try_alloc. In future alloc() will be an untimed blocking call. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_alloc. In future alloc() will be an untimed blocking call.") + T *alloc() + { + return try_alloc(); + } + + /** Allocate a memory block from a memory pool, without blocking. + @return address of the allocated memory block or nullptr in case of no memory available. + + @note You may call this function from ISR context. + */ + T *try_alloc() + { + return (T *)osMemoryPoolAlloc(_id, 0); + } + + /** 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 nullptr in case of no memory available. + + @note You may call this function from ISR context if the millisec parameter is set to 0. + @deprecated Replaced with `try_alloc_for`. For example use `try_alloc_for(5s)` rather than `alloc_for(5000)`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with `try_alloc_for`. For example use `try_alloc_for(5s)` rather than `alloc_for(5000)`.") + T *alloc_for(uint32_t millisec) + { + return try_alloc_for(std::chrono::duration(millisec)); + } + + /** Allocate a memory block from a memory pool, optionally blocking. + @param rel_time timeout value (Kernel::wait_for_u32_forever to wait forever) + @return address of the allocated memory block or nullptr in case of no memory available. + + @note You may call this function from ISR context if the rel_time parameter is set to 0. + */ + T *try_alloc_for(Kernel::Clock::duration_u32 rel_time) + { + return (T *)osMemoryPoolAlloc(_id, rel_time.count()); + } + + /** 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 nullptr 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. + @deprecated Replaced with `try_alloc_until`. For example use `try_alloc_until(Kernel::Clock::now() + 5s)` + rather than `alloc_until(Kernel::get_ms_count() + 5000)`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with `try_alloc_until`. For example use `try_alloc_until(Kernel::Clock::now() + 5s)` rather than `alloc_until(Kernel::get_ms_count() + 5000)`.") + T *alloc_until(uint64_t millisec) + { + return try_alloc_until(Kernel::Clock::time_point(std::chrono::duration(millisec))); + } + + /** Allocate a memory block from a memory pool, blocking. + @param abs_time absolute timeout time, referenced to Kernel::Clock. + @return address of the allocated memory block or nullptr 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 *try_alloc_until(Kernel::Clock::time_point abs_time) + { + Kernel::Clock::time_point now = Kernel::Clock::now(); + Kernel::Clock::duration_u32 rel_time; + if (now >= abs_time) { + rel_time = rel_time.zero(); + } else if (abs_time - now > Kernel::wait_for_u32_max) { + rel_time = Kernel::wait_for_u32_max; + } else { + rel_time = abs_time - now; + } + return try_alloc_for(rel_time); + } + + /** Allocate a memory block from a memory pool, without blocking, and set memory block to zero. + @return address of the allocated memory block or nullptr in case of no memory available. + + @note You may call this function from ISR context. + @deprecated Replaced with try_calloc. In future calloc() will be an untimed blocking call. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_calloc. In future calloc() will be an untimed blocking call.") + T *calloc() + { + return try_calloc(); + } + + /** Allocate a memory block from a memory pool, without blocking, and set memory block to zero. + @return address of the allocated memory block or nullptr in case of no memory available. + + @note You may call this function from ISR context. + */ + T *try_calloc() + { + T *item = try_alloc(); + if (item != nullptr) { + 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 nullptr in case of no memory available. + + @note You may call this function from ISR context if the millisec parameter is set to 0. + @deprecated Replaced with `try_calloc_for`. For example use `try_calloc_for(5s)` rather than `calloc_for(5000)`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with `try_calloc_for`. For example use `try_calloc_for(5s)` rather than `calloc_for(5000)`.") + T *calloc_for(uint32_t millisec) + { + return try_calloc_for(std::chrono::duration(millisec)); + } + + /** Allocate a memory block from a memory pool, optionally blocking, and set memory block to zero. + @param rel_time timeout value (Kernel::wait_for_u32_forever to wait forever) + @return address of the allocated memory block or nullptr in case of no memory available. + + @note You may call this function from ISR context if the rel_time parameter is set to 0. + */ + T *try_calloc_for(Kernel::Clock::duration_u32 rel_time) + { + T *item = try_alloc_for(rel_time); + if (item != nullptr) { + 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 nullptr 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. + @deprecated Replaced with `try_calloc_until`. For example use `try_calloc_until(Kernel::Clock::now() + 5s)` + rather than `calloc_until(Kernel::get_ms_count() + 5000)`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with `try_calloc_until`. For example use `try_calloc_until(Kernel::Clock::now() + 5s)` rather than `calloc_until(Kernel::get_ms_count() + 5000)`.") + T *calloc_until(uint64_t millisec) + { + return try_calloc_until(Kernel::Clock::time_point(std::chrono::duration(millisec))); + } + + /** Allocate a memory block from a memory pool, blocking, and set memory block to zero. + @param abs_time absolute timeout time, referenced to Kernel::Clock. + @return address of the allocated memory block or nullptr 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 *try_calloc_until(Kernel::Clock::time_point abs_time) + { + T *item = try_alloc_until(abs_time); + if (item != nullptr) { + memset(item, 0, sizeof(T)); + } + return item; + } + + /** Free a memory block. + @param block address of the allocated memory block to be freed. + @return osOK on successful deallocation, osErrorParameter if given memory block id + is nullptr or invalid, or osErrorResource if given memory block is in an + invalid memory pool state. + + @note You may call this function from ISR context. + */ + osStatus free(T *block) + { + return osMemoryPoolFree(_id, block); + } + +private: + osMemoryPoolId_t _id; + char _pool_mem[MBED_RTOS_STORAGE_MEM_POOL_MEM_SIZE(pool_sz, sizeof(T))]; + mbed_rtos_storage_mem_pool_t _obj_mem; +}; +/** @}*/ +/** @}*/ +} +#endif +#endif diff --git a/rtos/include/rtos/Mutex.h b/rtos/include/rtos/Mutex.h new file mode 100644 index 0000000..163b671 --- /dev/null +++ b/rtos/include/rtos/Mutex.h @@ -0,0 +1,234 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef MUTEX_H +#define MUTEX_H + +#include +#include "rtos/internal/mbed_rtos_types.h" +#include "rtos/internal/mbed_rtos1_types.h" +#include "rtos/internal/mbed_rtos_storage.h" +#include "rtos/Kernel.h" + +#include "platform/NonCopyable.h" +#include "platform/ScopedLock.h" +#include "platform/mbed_toolchain.h" + +namespace rtos { +/** \addtogroup rtos-public-api */ +/** @{*/ + +class Mutex; +/** Typedef for the mutex lock + * + * Usage: + * @code + * void foo(Mutex &m) { + * ScopedMutexLock lock(m); + * // Mutex lock protects code in this block + * } + * @endcode + */ +typedef mbed::ScopedLock ScopedMutexLock; + +/** + * \defgroup rtos_Mutex Mutex class + * @{ + */ + +/** The Mutex class is used to synchronize the execution of threads. + This is, for example, used to protect access to a shared resource. + + In bare-metal builds, the Mutex class is a dummy, so lock() and unlock() are no-ops. + + @note You cannot use member functions of this class in ISR context. If you require Mutex functionality within + ISR handler, consider using @a Semaphore. + + @note + Memory considerations: The mutex control structures are created on the current thread's stack, both for the Mbed OS + and underlying RTOS objects (static or dynamic RTOS memory pools are not being used). +*/ +class Mutex : private mbed::NonCopyable { +public: + /** Create and Initialize a Mutex object + * + * @note You cannot call this function from ISR context. + */ + Mutex(); + + /** Create and Initialize a Mutex object + + @param name name to be used for this mutex. It has to stay allocated for the lifetime of the thread. + @note You cannot call this function from ISR context. + */ + Mutex(const char *name); + + /** + Wait until a Mutex becomes available. + + @note You cannot call this function from ISR context. + */ + void lock(); + + /** Try to lock the mutex, and return immediately + @return true if the mutex was acquired, false otherwise. + @note equivalent to trylock_for(0) + + @note You cannot call this function from ISR context. + */ + bool trylock(); + + /** Try to lock the mutex for a specified time + @param millisec timeout value. + @return true if the mutex was acquired, false otherwise. + @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 lock attempt will time out earlier than specified. + + @note You cannot call this function from ISR context. + @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + bool trylock_for(uint32_t millisec); + + /** Try to lock the mutex for a specified time + @param rel_time timeout value. + @return true if the mutex was acquired, false otherwise. + @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 lock attempt will time out earlier than specified. + + @note You cannot call this function from ISR context. + */ + bool trylock_for(Kernel::Clock::duration_u32 rel_time); + + /** Try to lock the mutex until specified time + @param millisec absolute timeout time, referenced to Kernel::get_ms_count() + @return true if the mutex was acquired, false otherwise. + @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 lock attempt will time out earlier than specified. + + @note You cannot call this function from ISR context. + @deprecated Pass a chrono time_point, not an integer millisecond count. For example use + `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") + bool trylock_until(uint64_t millisec); + + /** Try to lock the mutex until specified time + @param abs_time absolute timeout time, referenced to Kernel::get_ms_count() + @return true if the mutex was acquired, false otherwise. + @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 lock attempt will time out earlier than specified. + + @note You cannot call this function from ISR context. + */ + bool trylock_until(Kernel::Clock::time_point abs_time); + + /** + Unlock the mutex that has previously been locked by the same thread + + @note You cannot call this function from ISR context. + */ + void unlock(); + + /** Get the owner the this mutex + @return the current owner of this mutex. + + @note You cannot call this function from ISR context. + */ + osThreadId_t get_owner(); + + /** Mutex destructor + * + * @note You cannot call this function from ISR context. + */ + ~Mutex(); + +private: +#if MBED_CONF_RTOS_PRESENT + void constructor(const char *name = nullptr); + friend class ConditionVariable; + + osMutexId_t _id; + mbed_rtos_storage_mutex_t _obj_mem; + uint32_t _count; +#endif +}; + +#if !MBED_CONF_RTOS_PRESENT +inline Mutex::Mutex() +{ +} + +inline Mutex::Mutex(const char *) +{ +} + +inline Mutex::~Mutex() +{ +} + +inline void Mutex::lock() +{ +} + +inline bool Mutex::trylock() +{ + return true; +} + +inline bool Mutex::trylock_for(uint32_t) +{ + return true; +} + +inline bool Mutex::trylock_for(Kernel::Clock::duration_u32) +{ + return true; +} + +inline bool Mutex::trylock_until(uint64_t) +{ + return true; +} + +inline bool Mutex::trylock_until(Kernel::Clock::time_point) +{ + return true; +} + +inline void Mutex::unlock() +{ +} +#endif + +/** @}*/ +/** @}*/ +} +#endif diff --git a/rtos/include/rtos/Queue.h b/rtos/include/rtos/Queue.h new file mode 100644 index 0000000..a8e8828 --- /dev/null +++ b/rtos/include/rtos/Queue.h @@ -0,0 +1,348 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef QUEUE_H +#define QUEUE_H + +#include "rtos/internal/mbed_rtos_types.h" +#include "rtos/internal/mbed_rtos1_types.h" +#include "rtos/internal/mbed_rtos_storage.h" +#include "rtos/Kernel.h" +#include "platform/mbed_error.h" +#include "platform/NonCopyable.h" + +#if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY) + +namespace rtos { +/** \addtogroup rtos-public-api */ +/** @{*/ + +/** + * \defgroup rtos_Queue Queue class + * @{ + */ + +/** The Queue class represents a collection of objects that are stored first by + * order of priority, and then in first-in, first-out (FIFO) order. + * + * You can use a queue when you need to store data and then access it in the same + * order that it has been stored. The order in which you retrieve the data is in + * order of descending priority. If multiple elements have the same priority, + * they are retrieved in FIFO order. + * + * The object type stored in the queue can be an integer, pointer or a generic + * type given by the template parameter T. + * + * @tparam T Specifies the type of elements stored in the queue. + * @tparam queue_sz Maximum number of messages that you can store in the queue. + * + * @note Memory considerations: The queue control structures are created on the + * current thread's stack, both for the Mbed OS and underlying RTOS + * objects (static or dynamic RTOS memory pools are not being used). + * + * @note Bare metal profile: This class is not supported. + */ +template +class Queue : private mbed::NonCopyable > { +public: + /** Create and initialize a message Queue of objects of the parameterized + * type `T` and maximum capacity specified by `queue_sz`. + * + * @note You cannot call this function from ISR context. + */ + Queue() + { + osMessageQueueAttr_t attr = { 0 }; + attr.mq_mem = _queue_mem; + attr.mq_size = sizeof(_queue_mem); + attr.cb_mem = &_obj_mem; + attr.cb_size = sizeof(_obj_mem); + _id = osMessageQueueNew(queue_sz, sizeof(T *), &attr); + MBED_ASSERT(_id); + } + + /** Queue destructor + * + * @note You cannot call this function from ISR context. + */ + ~Queue() + { + osMessageQueueDelete(_id); + } + + /** Check if the queue is empty. + * + * @return True if the queue is empty, false if not + * + * @note You may call this function from ISR context. + */ + bool empty() const + { + return osMessageQueueGetCount(_id) == 0; + } + + /** Check if the queue is full. + * + * @return True if the queue is full, false if not + * + * @note You may call this function from ISR context. + */ + bool full() const + { + return osMessageQueueGetSpace(_id) == 0; + } + + /** Get number of queued messages in the queue. + * + * @return Number of items in the queue + * + * @note You may call this function from ISR context. + */ + uint32_t count() const + { + return osMessageQueueGetCount(_id); + } + + /** Inserts the given element to the end of the queue. + * + * This function puts the message pointed to by `data` into the queue. The + * parameter `prio` is used to sort the message according to their priority + * (higher numbers indicate higher priority) on insertion. + * + * The function does not block, and returns immediately if the queue is full. + * + * @param data Pointer to the element to insert into the queue. + * @param prio Priority of the operation or 0 in case of default. + * (default: 0) + * + * @return true if the element was inserted, false otherwise. + * + * @note You may call this function from ISR context. + */ + bool try_put(T *data, uint8_t prio = 0) + { + return try_put_for(Kernel::Clock::duration_u32::zero(), data, prio); + } + + /** Inserts the given element to the end of the queue. + * + * This function puts the message pointed to by `data` into the queue. The + * parameter `prio` is used to sort the message according to their priority + * (higher numbers indicate higher priority) on insertion. + * + * The timeout indicated by the parameter `rel_time` specifies how long the + * function blocks waiting for the message to be inserted into the + * queue. + * + * The parameter `rel_time` can have the following values: + * - When the duration is 0, the function returns instantly. You could use + * `try_put` instead. + * - When the duration is Kernel::wait_for_u32_forever, the function waits for an + * infinite time. + * - For all other values, the function waits for the given duration. + * + * @param rel_time Timeout for the operation to be executed. + * @param data Pointer to the element to insert into the queue. + * @param prio Priority of the operation or 0 in case of default. + * (default: 0) + * + * @return true if the element was inserted, false otherwise. + * + * @note You may call this function from ISR context if the rel_time + * parameter is set to 0. + * + */ + bool try_put_for(Kernel::Clock::duration_u32 rel_time, T *data, uint8_t prio = 0) + { + osStatus status = osMessageQueuePut(_id, &data, prio, rel_time.count()); + return status == osOK; + } + + /** Inserts the given element to the end of the queue. + * + * This function puts the message pointed to by `data` into the queue. The + * parameter `prio` is used to sort the message according to their priority + * (higher numbers indicate higher priority) on insertion. + * + * The timeout indicated by the parameter `millisec` specifies how long the + * function blocks waiting for the message to be inserted into the + * queue. + * + * The parameter `millisec` can have the following values: + * - When the timeout is 0, the function returns instantly. + * - When the timeout is osWaitForever, the function waits for an + * infinite time. + * - For all other values, the function waits for the given number of + * milliseconds. + * + * @param data Pointer to the element to insert into the queue. + * @param millisec Timeout for the operation to be executed, or 0 in case + * of no timeout. + * @param prio Priority of the operation or 0 in case of default. + * (default: 0) + * + * @return Status code that indicates the execution status of the function: + * @a osOK The message has been successfully inserted + * into the queue. + * @a osErrorTimeout The message could not be inserted into the + * queue in the given time. + * @a osErrorResource The message could not be inserted because + * the queue is full. + * @a osErrorParameter Internal error or nonzero timeout specified + * in an ISR. + * + * @note You may call this function from ISR context if the millisec + * parameter is set to 0. + * @deprecated Replaced with try_put and try_put_for. In future put will be an untimed blocking call. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_put and try_put_for. In future put will be an untimed blocking call.") + osStatus put(T *data, uint32_t millisec = 0, uint8_t prio = 0) + { + return osMessageQueuePut(_id, &data, prio, millisec); + } + + /** Get a message from the queue. + * + * This function retrieves a message from the queue. The message is stored + * in the location pointed to be the parameter `data_out`. + * + * The function does not block, and returns immediately if the queue is empty. + * + * @param[out] data_out Pointer to location to write the element retrieved from the queue. + * + * @return true if an element was received and written to data_out. + * + * @note You may call this function from ISR context. + */ + bool try_get(T **data_out) + { + return try_get_for(Kernel::Clock::duration_u32::zero(), data_out); + } + + /** Get a message or wait for a message from the queue. + * + * This function retrieves a message from the queue. The message is stored + * in the location pointed to be the parameter `data_out`. + * + * The timeout specified by the parameter `rel_time` specifies how long the + * function waits to retrieve the message from the queue. + * + * The timeout parameter can have the following values: + * - When the timeout is 0, the function returns instantly. + * - When the timeout is Kernel::wait_for_u32_forever, the function waits + * infinite time until the message is retrieved. + * - When the timeout is any other value, the function waits for the + * specified time before returning a timeout error. + * + * Messages are retrieved in descending priority order. If two messages + * share the same priority level, they are retrieved in first-in, first-out + * (FIFO) order. + * + * @param rel_time Timeout value. + * @param[out] data_out Pointer to location to write the element retrieved from the queue. + * + * @return true if an element was received and written to data_out. + * + * @note You may call this function from ISR context if the rel_time + * parameter is set to 0. + */ + bool try_get_for(Kernel::Clock::duration_u32 rel_time, T **data_out) + { + osStatus status = osMessageQueueGet(_id, data_out, nullptr, rel_time.count()); + return status == osOK; + } + + /** Get a message or wait for a message from the queue. + * + * This function retrieves a message from the queue. The message is stored + * in the value field of the returned `osEvent` object. + * + * The timeout specified by the parameter `millisec` specifies how long the + * function waits to retrieve the message from the queue. + * + * The timeout parameter can have the following values: + * - When the timeout is 0, the function returns instantly. + * - When the timeout is osWaitForever (default), the function waits + * infinite time until the message is retrieved. + * - When the timeout is any other value, the function waits for the + * specified time before returning a timeout error. + * + * Messages are retrieved in descending priority order. If two messages + * share the same priority level, they are retrieved in first-in, first-out + * (FIFO) order. + * + * @param millisec Timeout value. + * + * @return Event information that includes the message in event. Message + * value and the status code in event.status: + * @a osEventMessage Message successfully received. + * @a osOK No message is available in the queue, and no + * timeout was specified. + * @a osEventTimeout No message was received before a timeout + * event occurred. + * @a osErrorParameter A parameter is invalid or outside of a + * permitted range. + * + * @note You may call this function from ISR context if the millisec + * parameter is set to 0. + * @deprecated Replaced with try_get and try_get_for. In future get will be an untimed blocking call. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Replaced with try_get and try_get_for. In future get will be an untimed blocking call.") + osEvent get(uint32_t millisec = osWaitForever) + { + osEvent event; + T *data = nullptr; + osStatus_t res = osMessageQueueGet(_id, &data, nullptr, millisec); + + switch (res) { + case osOK: + event.status = (osStatus)osEventMessage; + event.value.p = data; + break; + case osErrorResource: + event.status = osOK; + break; + case osErrorTimeout: + event.status = (osStatus)osEventTimeout; + break; + case osErrorParameter: + default: + event.status = osErrorParameter; + break; + } + event.def.message_id = _id; + + return event; + } +private: + osMessageQueueId_t _id; + char _queue_mem[queue_sz * (sizeof(T *) + sizeof(mbed_rtos_storage_message_t))]; + mbed_rtos_storage_msg_queue_t _obj_mem; +}; +/** @}*/ +/** @}*/ + +} // namespace rtos + +#endif + +#endif // QUEUE_H diff --git a/rtos/include/rtos/Semaphore.h b/rtos/include/rtos/Semaphore.h new file mode 100644 index 0000000..be35754 --- /dev/null +++ b/rtos/include/rtos/Semaphore.h @@ -0,0 +1,159 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef SEMAPHORE_H +#define SEMAPHORE_H + +#include +#include +#include "rtos/internal/mbed_rtos_types.h" +#include "rtos/internal/mbed_rtos1_types.h" +#include "rtos/internal/mbed_rtos_storage.h" +#include "rtos/Kernel.h" +#include "platform/mbed_toolchain.h" +#include "platform/NonCopyable.h" + +namespace rtos { +/** \addtogroup rtos-public-api */ +/** @{*/ + +/** + * \defgroup rtos_Semaphore Semaphore class + * @{ + */ + +/** The Semaphore class is used to manage and protect access to a set of shared resources. + * + * @note + * Memory considerations: The semaphore control structures will be created on current thread's stack, both for the mbed OS + * and underlying RTOS objects (static or dynamic RTOS memory pools are not being used). + */ +class Semaphore : private mbed::NonCopyable { +public: + /** Create and Initialize a Semaphore object used for managing resources. + @param count number of available resources; maximum index value is (count-1). (default: 0). + + @note You cannot call this function from ISR context. + */ + Semaphore(int32_t count = 0); + + /** Create and Initialize a Semaphore object used for managing resources. + @param count number of available resources + @param max_count maximum number of available resources + + @note You cannot call this function from ISR context. + */ + Semaphore(int32_t count, uint16_t max_count); + + /** Wait until a Semaphore resource becomes available. + + @note You cannot call this function from ISR context. + */ + void acquire(); + + /** Try to acquire a Semaphore resource, and return immediately + @return true if a resource was acquired, false otherwise. + @note equivalent to try_acquire_for(0) + + @note You may call this function from ISR context. + */ + bool try_acquire(); + + /** Wait until a Semaphore resource becomes available. + @param millisec timeout value. + @return true if a resource was acquired, false otherwise. + + @note You may call this function from ISR context if the millisec parameter is set to 0. + @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + bool try_acquire_for(uint32_t millisec); + + /** Wait until a Semaphore resource becomes available. + @param rel_time timeout value. + @return true if a resource was acquired, false otherwise. + + @note You may call this function from ISR context if the rel_time parameter is set to 0. + */ + bool try_acquire_for(Kernel::Clock::duration_u32 rel_time); + + /** Wait until a Semaphore resource becomes available. + @param millisec absolute timeout time, referenced to Kernel::get_ms_count() + @return true if a resource was acquired, false otherwise. + @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 acquire attempt will time out earlier than specified. + + @note You cannot call this function from ISR context. + @deprecated Pass a chrono time_point, not an integer millisecond count. For example use + `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`. + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") + bool try_acquire_until(uint64_t millisec); + + /** Wait until a Semaphore resource becomes available. + @param millisec absolute timeout time, referenced to Kernel::get_ms_count() + @return true if a resource was acquired, false otherwise. + @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 acquire attempt will time out earlier than specified. + + @note You cannot call this function from ISR context. + */ + bool try_acquire_until(Kernel::Clock::time_point abs_time); + + /** Release a Semaphore resource that was obtain with Semaphore::acquire. + @return status code that indicates the execution status of the function: + @a osOK the token has been correctly released. + @a osErrorResource the maximum token count has been reached. + @a osErrorParameter internal error. + + @note You may call this function from ISR context. + */ + osStatus release(void); + + /** Semaphore destructor + * + * @note You cannot call this function from ISR context. + */ + ~Semaphore(); + +private: + void constructor(int32_t count, uint16_t max_count); + +#if MBED_CONF_RTOS_PRESENT + int32_t _wait(uint32_t millisec); + + osSemaphoreId_t _id; + mbed_rtos_storage_semaphore_t _obj_mem; +#else + static bool semaphore_available(void *); + int32_t _count; + uint16_t _max_count; +#endif +}; +/** @}*/ +/** @}*/ +} +#endif diff --git a/rtos/include/rtos/ThisThread.h b/rtos/include/rtos/ThisThread.h new file mode 100644 index 0000000..7a5aedd --- /dev/null +++ b/rtos/include/rtos/ThisThread.h @@ -0,0 +1,290 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef THIS_THREAD_H +#define THIS_THREAD_H + +#include +#include "platform/mbed_toolchain.h" +#include "rtos/Kernel.h" +#include "rtos/internal/mbed_rtos_types.h" + +namespace rtos { +/** \addtogroup rtos-public-api */ +/** @{*/ + +/** + * \defgroup rtos_ThisThread ThisThread namespace + * @{ + */ + +/** The ThisThread namespace allows controlling the current thread. + * + * Example: + * @code + * #include "mbed.h" + * #include "rtos.h" + * + * Thread thread; + * DigitalOut led1(LED1); + * + * #define STOP_FLAG 1 + * + * // Blink function toggles the led in a long running loop + * void blink(DigitalOut *led) { + * while (!ThisThread::flags_wait_any_for(STOP_FLAG, 1000)) { + * *led = !*led; + * } + * } + * + * // Spawns a thread to run blink for 5 seconds + * int main() { + * thread.start(callback(blink, &led1)); + * ThisThread::sleep_for(5000); + * thread.signal_set(STOP_FLAG); + * thread.join(); + * } + * @endcode + * + */ +namespace ThisThread { +/** Clears the specified Thread Flags of the currently running thread. + @param flags specifies the flags of the thread that should be cleared. + @return thread flags before clearing. + @note You cannot call this function from ISR context. + @see Thread::flags_set +*/ +uint32_t flags_clear(uint32_t flags); + +/** Returns the Thread Flags currently set for the currently running thread. + @return current thread flags or 0 if not in a valid thread. + @note You cannot call this function from ISR context. + @see Thread::flags_set +*/ +uint32_t flags_get(); + +/** Wait for all of the specified Thread Flags to become signaled for the current thread. + @param flags specifies the flags to wait for + @param clear whether to clear the specified flags after waiting for them. (default: true) + @return actual thread flags before clearing, which will satisfy the wait + @note You cannot call this function from ISR context. + @see Thread::flags_set +*/ +uint32_t flags_wait_all(uint32_t flags, bool clear = true); + +/** Wait for any of the specified Thread Flags to become signaled for the current thread. + @param flags specifies the flags to wait for + @param clear whether to clear the specified flags after waiting for them. (default: true) + @return actual thread flags before clearing, which will satisfy the wait + @note You cannot call this function from ISR context. + @see Thread::flags_set +*/ +uint32_t flags_wait_any(uint32_t flags, bool clear = true); + +/** Wait for all of the specified Thread Flags to become signaled for the current thread. + @param flags specifies the flags to wait for + @param millisec timeout value. + @param clear whether to clear the specified flags after waiting for them. (default: true) + @return actual thread flags before clearing, which may not satisfy the wait + @note You cannot call this function from ISR context. + @see Thread::flags_set + @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. +*/ +MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") +uint32_t flags_wait_all_for(uint32_t flags, uint32_t millisec, bool clear = true); + +/** Wait for all of the specified Thread Flags to become signaled for the current thread. + @param flags specifies the flags to wait for + @param rel_time timeout value. + @param clear whether to clear the specified flags after waiting for them. (default: true) + @return actual thread flags before clearing, which may not satisfy the wait + @note You cannot call this function from ISR context. + @see Thread::flags_set +*/ +uint32_t flags_wait_all_for(uint32_t flags, Kernel::Clock::duration_u32 rel_time, bool clear = true); + +/** Wait for all of the specified Thread Flags to become signaled for the current thread. + @param flags specifies the flags to wait for + @param millisec absolute timeout time, referenced to Kernel::get_ms_count() + @param clear whether to clear the specified flags after waiting for them. (default: true) + @return actual thread flags before clearing, which may not satisfy the wait + @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. + @see Thread::flags_set + @deprecated Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` + rather than `Kernel::get_ms_count() + 5000`. +*/ +MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") +uint32_t flags_wait_all_until(uint32_t flags, uint64_t millisec, bool clear = true); + +/** Wait for all of the specified Thread Flags to become signaled for the current thread. + @param flags specifies the flags to wait for + @param abs_time absolute timeout time, referenced to Kernel::Clock + @param clear whether to clear the specified flags after waiting for them. (default: true) + @return actual thread flags before clearing, which may not satisfy the wait + @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. + @see Thread::flags_set +*/ +uint32_t flags_wait_all_until(uint32_t flags, Kernel::Clock::time_point abs_time, bool clear = true); + +/** Wait for any of the specified Thread Flags to become signaled for the current thread. + @param flags specifies the flags to wait for + @param millisec timeout value. + @param clear whether to clear the specified flags after waiting for them. (default: true) + @return actual thread flags before clearing, which may not satisfy the wait + @note You cannot call this function from ISR context. + @see Thread::flags_set + @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. +*/ +MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") +uint32_t flags_wait_any_for(uint32_t flags, uint32_t millisec, bool clear = true); + +/** Wait for any of the specified Thread Flags to become signaled for the current thread. + @param flags specifies the flags to wait for + @param rel_time timeout value. + @param clear whether to clear the specified flags after waiting for them. (default: true) + @return actual thread flags before clearing, which may not satisfy the wait + @note You cannot call this function from ISR context. + @see Thread::flags_set +*/ +uint32_t flags_wait_any_for(uint32_t flags, Kernel::Clock::duration_u32 rel_time, bool clear = true); + +/** Wait for any of the specified Thread Flags to become signaled for the current thread. + @param flags specifies the flags to wait for + @param millisec absolute timeout time, referenced to Kernel::get_ms_count() + @param clear whether to clear the specified flags after waiting for them. (default: true) + @return actual thread flags before clearing, which may not satisfy the wait + @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. + @see Thread::flags_set + @deprecated Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` + rather than `Kernel::get_ms_count() + 5000`. +*/ +MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") +uint32_t flags_wait_any_until(uint32_t flags, uint64_t millisec, bool clear = true); + +/** Wait for any of the specified Thread Flags to become signaled for the current thread. + @param flags specifies the flags to wait for + @param abs_time absolute timeout time, referenced to Kernel::Clock + @param clear whether to clear the specified flags after waiting for them. (default: true) + @return actual thread flags before clearing, which may not satisfy the wait + @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. + @see Thread::flags_set +*/ +uint32_t flags_wait_any_until(uint32_t flags, Kernel::Clock::time_point abs_time, bool clear = true); + +/** Sleep for a specified time period in millisec: + @param millisec time delay value + @note You cannot call this function from ISR context. + @note The equivalent functionality is accessible in C via thread_sleep_for. + @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. +*/ +MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") +void sleep_for(uint32_t millisec); + +/** Sleep for a specified time period: + @param rel_time time delay value + @note You cannot call this function from ISR context. + @note The equivalent functionality is accessible in C via thread_sleep_for. +*/ +void sleep_for(Kernel::Clock::duration_u32 rel_time); + + +/** Sleep until a specified time in millisec + The specified time is according to Kernel::get_ms_count(). + @param millisec absolute time in millisec + @note You cannot call this function from ISR context. + @note if millisec is equal to or lower than the current tick count, this + returns immediately. + @note The equivalent functionality is accessible in C via thread_sleep_until. + @deprecated Pass a chrono time_point, not an integer millisecond count. For example use + `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`. +*/ +MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.") +void sleep_until(uint64_t millisec); + +/** Sleep until a specified time in millisec + The specified time is according to Kernel::Clock. + @param abs_time absolute time + @note You cannot call this function from ISR context. + @note if abs_time is equal to or lower than Kernel::Clock::now(), this + returns immediately. + @note The equivalent functionality is accessible in C via thread_sleep_until. +*/ +void sleep_until(Kernel::Clock::time_point abs_time); + +/** Pass control to next equal-priority thread that is in state READY. + (Higher-priority READY threads would prevent us from running; this + will not enable lower-priority threads to run, as we remain READY). + @note You cannot call this function from ISR context. +*/ +void yield(); + +/** Get the thread id of the current running thread. + @return thread ID for reference by other functions or nullptr in case of error or in ISR context. + @note You may call this function from ISR context. +*/ +osThreadId_t get_id(); + +/** Get the thread name of the current running thread. + @return thread name pointer or nullptr if thread has no name or in case of error. + @note You cannot call this function from ISR context. +*/ +const char *get_name(); + +}; +/** @}*/ +/** @}*/ + + +namespace internal { +/** \addtogroup rtos-internal-api */ +/** @{*/ + +struct flags_check_capture { + uint32_t *flags; + uint32_t options; + uint32_t flags_wanted; + uint32_t result; + bool match; +}; + +bool non_rtos_check_flags(void *handle); + +} +/** @}*/ +} +#endif diff --git a/rtos/include/rtos/Thread.h b/rtos/include/rtos/Thread.h new file mode 100644 index 0000000..a2bfbf0 --- /dev/null +++ b/rtos/include/rtos/Thread.h @@ -0,0 +1,284 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef THREAD_H +#define THREAD_H + +#include +#include "rtos/internal/mbed_rtos_types.h" +#include "rtos/internal/mbed_rtos1_types.h" +#include "rtos/internal/mbed_rtos_storage.h" +#include "platform/Callback.h" +#include "platform/mbed_toolchain.h" +#include "platform/NonCopyable.h" +#include "rtos/Semaphore.h" +#include "rtos/Mutex.h" + +#if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY) || defined(UNITTEST) + +namespace rtos { +/** \addtogroup rtos-public-api */ +/** @{*/ + +/** + * \defgroup rtos_Thread Thread class + * @{ + */ + +/** The Thread class allow defining, creating, and controlling thread functions in the system. + * + * Example: + * @code + * #include "mbed.h" + * #include "rtos.h" + * + * Thread thread; + * DigitalOut led1(LED1); + * volatile bool running = true; + * + * // Blink function toggles the led in a long running loop + * void blink(DigitalOut *led) { + * while (running) { + * *led = !*led; + * ThisThread::sleep_for(1000); + * } + * } + * + * // Spawns a thread to run blink for 5 seconds + * int main() { + * thread.start(callback(blink, &led1)); + * ThisThread::sleep_for(5000); + * running = false; + * thread.join(); + * } + * @endcode + * + * @note + * Memory considerations: The thread control structures will be created on current thread's stack, both for the mbed OS + * and underlying RTOS objects (static or dynamic RTOS memory pools are not being used). + * Additionally the stack memory for this thread will be allocated on the heap, if it wasn't supplied to the constructor. + * + * @note + * MBED_TZ_DEFAULT_ACCESS (default:0) flag can be used to change the default access of all user threads in non-secure mode. + * MBED_TZ_DEFAULT_ACCESS set to 1, means all non-secure user threads have access to call secure functions. + * MBED_TZ_DEFAULT_ACCESS set to 0, means none of the non-secure user thread have access to call secure functions, + * to give access to particular thread used overloaded constructor with `tz_module` as argument during thread creation. + * + * MBED_TZ_DEFAULT_ACCESS is target specific define, should be set in targets.json file for Cortex-M23/M33 devices. + * + * @note + * Bare metal profile: This class is not supported. + */ + +class Thread : private mbed::NonCopyable { +public: + /** Allocate a new thread without starting execution + @param priority initial priority of the thread function. (default: osPriorityNormal). + @param stack_size stack size (in bytes) requirements for the thread function. (default: OS_STACK_SIZE). + @param stack_mem pointer to the stack area to be used by this thread (default: nullptr). + @param name name to be used for this thread. It has to stay allocated for the lifetime of the thread (default: nullptr) + + @note Default value of tz_module will be MBED_TZ_DEFAULT_ACCESS + @note You cannot call this function from ISR context. + */ + + Thread(osPriority priority = osPriorityNormal, + uint32_t stack_size = OS_STACK_SIZE, + unsigned char *stack_mem = nullptr, const char *name = nullptr) + { + constructor(priority, stack_size, stack_mem, name); + } + + /** Allocate a new thread without starting execution + @param tz_module trustzone thread identifier (osThreadAttr_t::tz_module) + Context of RTOS threads in non-secure state must be saved when calling secure functions. + tz_module ID is used to allocate context memory for threads, and it can be safely set to zero for + threads not using secure calls at all. See "TrustZone RTOS Context Management" for more details. + @param priority initial priority of the thread function. (default: osPriorityNormal). + @param stack_size stack size (in bytes) requirements for the thread function. (default: OS_STACK_SIZE). + @param stack_mem pointer to the stack area to be used by this thread (default: nullptr). + @param name name to be used for this thread. It has to stay allocated for the lifetime of the thread (default: nullptr) + + @note You cannot call this function from ISR context. + */ + + Thread(uint32_t tz_module, osPriority priority = osPriorityNormal, + uint32_t stack_size = OS_STACK_SIZE, + unsigned char *stack_mem = nullptr, const char *name = nullptr) + { + constructor(tz_module, priority, stack_size, stack_mem, name); + } + + + /** Starts a thread executing the specified function. + @param task function to be executed by this thread. + @return status code that indicates the execution status of the function. + @note a thread can only be started once + + @note You cannot call this function ISR context. + */ + osStatus start(mbed::Callback task); + + /** Wait for thread to terminate + @return status code that indicates the execution status of the function. + + @note You cannot call this function from ISR context. + */ + osStatus join(); + + /** Terminate execution of a thread and remove it from Active Threads + @return status code that indicates the execution status of the function. + + @note You cannot call this function from ISR context. + */ + osStatus terminate(); + + /** Set priority of an active thread + @param priority new priority value for the thread function. + @return status code that indicates the execution status of the function. + + @note You cannot call this function from ISR context. + */ + osStatus set_priority(osPriority priority); + + /** Get priority of an active thread + @return current priority value of the thread function. + + @note You cannot call this function from ISR context. + */ + osPriority get_priority() const; + + /** Set the specified Thread Flags for the thread. + @param flags specifies the flags of the thread that should be set. + @return thread flags after setting or osFlagsError in case of incorrect parameters. + + @note You may call this function from ISR context. + */ + uint32_t flags_set(uint32_t flags); + + /** State of the Thread */ + enum State { + Inactive, /**< NOT USED */ + Ready, /**< Ready to run */ + Running, /**< Running */ + WaitingDelay, /**< Waiting for a delay to occur */ + WaitingJoin, /**< Waiting for thread to join. Only happens when using RTX directly. */ + WaitingThreadFlag, /**< Waiting for a thread flag to be set */ + WaitingEventFlag, /**< Waiting for a event flag to be set */ + WaitingMutex, /**< Waiting for a mutex event to occur */ + WaitingSemaphore, /**< Waiting for a semaphore event to occur */ + WaitingMemoryPool, /**< Waiting for a memory pool */ + WaitingMessageGet, /**< Waiting for message to arrive */ + WaitingMessagePut, /**< Waiting for message to be send */ + WaitingInterval, /**< NOT USED */ + WaitingOr, /**< NOT USED */ + WaitingAnd, /**< NOT USED */ + WaitingMailbox, /**< NOT USED (Mail is implemented as MemoryPool and Queue) */ + + /* Not in sync with RTX below here */ + Deleted, /**< The task has been deleted or not started */ + }; + + /** State of this Thread + @return the State of this Thread + + @note You cannot call this function from ISR context. + */ + State get_state() const; + + /** Get the total stack memory size for this Thread + @return the total stack memory size in bytes + + @note You cannot call this function from ISR context. + */ + uint32_t stack_size() const; + + /** Get the currently unused stack memory for this Thread + @return the currently unused stack memory in bytes + + @note You cannot call this function from ISR context. + */ + uint32_t free_stack() const; + + /** Get the currently used stack memory for this Thread + @return the currently used stack memory in bytes + + @note You cannot call this function from ISR context. + */ + uint32_t used_stack() const; + + /** Get the maximum stack memory usage to date for this Thread + @return the maximum stack memory usage to date in bytes + + @note You cannot call this function from ISR context. + */ + uint32_t max_stack() const; + + /** Get thread name + @return thread name or nullptr if the name was not set. + + @note You may call this function from ISR context. + */ + const char *get_name() const; + + /** Get thread id + @return thread ID for reference by other functions. + + @note You may call this function from ISR context. + */ + osThreadId_t get_id() const; + + /** Thread destructor + * + * @note You cannot call this function from ISR context. + */ + virtual ~Thread(); + +private: + // Required to share definitions without + // delegated constructors + void constructor(osPriority priority = osPriorityNormal, + uint32_t stack_size = OS_STACK_SIZE, + unsigned char *stack_mem = nullptr, + const char *name = nullptr); + void constructor(uint32_t tz_module, + osPriority priority = osPriorityNormal, + uint32_t stack_size = OS_STACK_SIZE, + unsigned char *stack_mem = nullptr, + const char *name = nullptr); + static void _thunk(void *thread_ptr); + + mbed::Callback _task; + osThreadId_t _tid; + osThreadAttr_t _attr; + bool _dynamic_stack; + bool _finished; + Semaphore _join_sem; + mutable Mutex _mutex; + mbed_rtos_storage_thread_t _obj_mem; +}; +/** @}*/ +/** @}*/ +} +#endif + +#endif diff --git a/rtos/include/rtos/internal/mbed_rtos1_types.h b/rtos/include/rtos/internal/mbed_rtos1_types.h new file mode 100644 index 0000000..e178b27 --- /dev/null +++ b/rtos/include/rtos/internal/mbed_rtos1_types.h @@ -0,0 +1,30 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_RTOS_RTX1_TYPES_H +#define MBED_RTOS_RTX1_TYPES_H + +#if MBED_CONF_RTOS_PRESENT || defined(UNITTEST) + +#include "cmsis_os.h" + +#else + +typedef int32_t osStatus; + +#endif + +#endif diff --git a/rtos/include/rtos/internal/mbed_rtos_storage.h b/rtos/include/rtos/internal/mbed_rtos_storage.h new file mode 100644 index 0000000..750725d --- /dev/null +++ b/rtos/include/rtos/internal/mbed_rtos_storage.h @@ -0,0 +1,26 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_RTOS_STORAGE_H +#define MBED_RTOS_STORAGE_H + +#if MBED_CONF_RTOS_PRESENT || defined(UNITTEST) + +#include "mbed_rtx_storage.h" + +#endif + +#endif diff --git a/rtos/include/rtos/internal/mbed_rtos_types.h b/rtos/include/rtos/internal/mbed_rtos_types.h new file mode 100644 index 0000000..eaf5da0 --- /dev/null +++ b/rtos/include/rtos/internal/mbed_rtos_types.h @@ -0,0 +1,80 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RTOS_TYPES_H_ +#define RTOS_TYPES_H_ + +#if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY) || defined(UNITTEST) +#include "cmsis_os2.h" +#else + +#ifdef __cplusplus +extern "C" { +#endif + +/** \addtogroup rtos-public-api */ +/** @{*/ + +/* Minimal definitions for bare metal form of RTOS */ + +// Timeout value. +#define osWaitForever 0xFFFFFFFFU ///< Wait forever timeout value. + +// Flags options (\ref osThreadFlagsWait and \ref osEventFlagsWait). +#define osFlagsWaitAny 0x00000000U ///< Wait for any flag (default). +#define osFlagsWaitAll 0x00000001U ///< Wait for all flags. +#define osFlagsNoClear 0x00000002U ///< Do not clear flags which have been specified to wait for. + +// Flags errors (returned by osThreadFlagsXxxx and osEventFlagsXxxx). +#define osFlagsError 0x80000000U ///< Error indicator. +#define osFlagsErrorUnknown 0xFFFFFFFFU ///< osError (-1). +#define osFlagsErrorTimeout 0xFFFFFFFEU ///< osErrorTimeout (-2). +#define osFlagsErrorResource 0xFFFFFFFDU ///< osErrorResource (-3). +#define osFlagsErrorParameter 0xFFFFFFFCU ///< osErrorParameter (-4). +#define osFlagsErrorISR 0xFFFFFFFAU ///< osErrorISR (-6). + +// Status code values returned by CMSIS-RTOS functions. +typedef enum { + osOK = 0, ///< Operation completed successfully. + osError = -1, ///< Unspecified RTOS error: run-time error but no other error message fits. + osErrorTimeout = -2, ///< Operation not completed within the timeout period. + osErrorResource = -3, ///< Resource not available. + osErrorParameter = -4, ///< Parameter error. + osErrorNoMemory = -5, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation. + osErrorISR = -6, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines. + osStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization. +} osStatus_t; + + +// \details Thread ID identifies the thread. +typedef void *osThreadId_t; + +// Set the specified Thread Flags of a thread. +// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId. +// \param[in] flags specifies the flags of the thread that shall be set. +// \return thread flags after setting or error code if highest bit set. +uint32_t osThreadFlagsSet(osThreadId_t thread_id, uint32_t flags); + +/** @}*/ + +#ifdef __cplusplus +} +#endif + +#endif + + +#endif /* RTOS_TYPES_H_ */ diff --git a/rtos/include/rtos/rtos.h b/rtos/include/rtos/rtos.h new file mode 100644 index 0000000..ec85d5c --- /dev/null +++ b/rtos/include/rtos/rtos.h @@ -0,0 +1,48 @@ + +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef RTOS_H +#define RTOS_H + +#include "rtos/internal/mbed_rtos_storage.h" +#include "rtos/Kernel.h" +#include "rtos/Thread.h" +#include "rtos/ThisThread.h" +#include "rtos/Mutex.h" +#include "rtos/Semaphore.h" +#include "rtos/Mail.h" +#include "rtos/MemoryPool.h" +#include "rtos/Queue.h" +#include "rtos/EventFlags.h" +#include "rtos/ConditionVariable.h" + + +/** \defgroup rtos-public-api RTOS + * \ingroup mbed-os-public + */ + +#ifndef MBED_NO_GLOBAL_USING_DIRECTIVE +using namespace rtos; +#endif + +#endif diff --git a/rtos/mbed_rtos1_types.h b/rtos/mbed_rtos1_types.h deleted file mode 100644 index e178b27..0000000 --- a/rtos/mbed_rtos1_types.h +++ /dev/null @@ -1,30 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MBED_RTOS_RTX1_TYPES_H -#define MBED_RTOS_RTX1_TYPES_H - -#if MBED_CONF_RTOS_PRESENT || defined(UNITTEST) - -#include "cmsis_os.h" - -#else - -typedef int32_t osStatus; - -#endif - -#endif diff --git a/rtos/mbed_rtos_storage.h b/rtos/mbed_rtos_storage.h deleted file mode 100644 index 750725d..0000000 --- a/rtos/mbed_rtos_storage.h +++ /dev/null @@ -1,26 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MBED_RTOS_STORAGE_H -#define MBED_RTOS_STORAGE_H - -#if MBED_CONF_RTOS_PRESENT || defined(UNITTEST) - -#include "mbed_rtx_storage.h" - -#endif - -#endif diff --git a/rtos/mbed_rtos_types.h b/rtos/mbed_rtos_types.h deleted file mode 100644 index eaf5da0..0000000 --- a/rtos/mbed_rtos_types.h +++ /dev/null @@ -1,80 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef RTOS_TYPES_H_ -#define RTOS_TYPES_H_ - -#if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY) || defined(UNITTEST) -#include "cmsis_os2.h" -#else - -#ifdef __cplusplus -extern "C" { -#endif - -/** \addtogroup rtos-public-api */ -/** @{*/ - -/* Minimal definitions for bare metal form of RTOS */ - -// Timeout value. -#define osWaitForever 0xFFFFFFFFU ///< Wait forever timeout value. - -// Flags options (\ref osThreadFlagsWait and \ref osEventFlagsWait). -#define osFlagsWaitAny 0x00000000U ///< Wait for any flag (default). -#define osFlagsWaitAll 0x00000001U ///< Wait for all flags. -#define osFlagsNoClear 0x00000002U ///< Do not clear flags which have been specified to wait for. - -// Flags errors (returned by osThreadFlagsXxxx and osEventFlagsXxxx). -#define osFlagsError 0x80000000U ///< Error indicator. -#define osFlagsErrorUnknown 0xFFFFFFFFU ///< osError (-1). -#define osFlagsErrorTimeout 0xFFFFFFFEU ///< osErrorTimeout (-2). -#define osFlagsErrorResource 0xFFFFFFFDU ///< osErrorResource (-3). -#define osFlagsErrorParameter 0xFFFFFFFCU ///< osErrorParameter (-4). -#define osFlagsErrorISR 0xFFFFFFFAU ///< osErrorISR (-6). - -// Status code values returned by CMSIS-RTOS functions. -typedef enum { - osOK = 0, ///< Operation completed successfully. - osError = -1, ///< Unspecified RTOS error: run-time error but no other error message fits. - osErrorTimeout = -2, ///< Operation not completed within the timeout period. - osErrorResource = -3, ///< Resource not available. - osErrorParameter = -4, ///< Parameter error. - osErrorNoMemory = -5, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation. - osErrorISR = -6, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines. - osStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization. -} osStatus_t; - - -// \details Thread ID identifies the thread. -typedef void *osThreadId_t; - -// Set the specified Thread Flags of a thread. -// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId. -// \param[in] flags specifies the flags of the thread that shall be set. -// \return thread flags after setting or error code if highest bit set. -uint32_t osThreadFlagsSet(osThreadId_t thread_id, uint32_t flags); - -/** @}*/ - -#ifdef __cplusplus -} -#endif - -#endif - - -#endif /* RTOS_TYPES_H_ */ diff --git a/rtos/rtos.h b/rtos/rtos.h deleted file mode 100644 index 1cb7819..0000000 --- a/rtos/rtos.h +++ /dev/null @@ -1,48 +0,0 @@ - -/* mbed Microcontroller Library - * Copyright (c) 2006-2012 ARM Limited - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RTOS_H -#define RTOS_H - -#include "rtos/mbed_rtos_storage.h" -#include "rtos/Kernel.h" -#include "rtos/Thread.h" -#include "rtos/ThisThread.h" -#include "rtos/Mutex.h" -#include "rtos/Semaphore.h" -#include "rtos/Mail.h" -#include "rtos/MemoryPool.h" -#include "rtos/Queue.h" -#include "rtos/EventFlags.h" -#include "rtos/ConditionVariable.h" - - -/** \defgroup rtos-public-api RTOS - * \ingroup mbed-os-public - */ - -#ifndef MBED_NO_GLOBAL_USING_DIRECTIVE -using namespace rtos; -#endif - -#endif diff --git a/rtos/source/rtos_handlers.h b/rtos/source/rtos_handlers.h index c87b6b7..c2a0835 100644 --- a/rtos/source/rtos_handlers.h +++ b/rtos/source/rtos_handlers.h @@ -23,7 +23,7 @@ #ifndef RTOS_HANDLERS_H #define RTOS_HANDLERS_H -#include "rtos/mbed_rtos_types.h" +#include "rtos/internal/mbed_rtos_types.h" #ifdef __cplusplus extern "C" { diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/MemoryPool/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/MemoryPool/main.cpp new file mode 100644 index 0000000..58fdf1d --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/MemoryPool/main.cpp @@ -0,0 +1,688 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#if !defined(MBED_CONF_RTOS_PRESENT) +#error [NOT_SUPPORTED] MemoryPool test cases require a RTOS to run. +#else + +#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ + do { \ + using ct = std::common_type_t; \ + TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ + } while (0) + +#define TEST_ASSERT_TIME_POINT_WITHIN(delta, expected, actual) \ + do { \ + using ct_tp = std::common_type_t; \ + using ct = std::common_type_t; \ + TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected.time_since_epoch()).count(), ct(actual.time_since_epoch()).count()); \ + } while (0) + +using namespace utest::v1; +using namespace std::chrono; + +#define THREAD_STACK_SIZE 512 +#define TEST_TIMEOUT 50ms + +/* Enum used to select block allocation method. */ +typedef enum { + ALLOC, CALLOC +} AllocType; + +/* Structure for complex block type. */ +typedef struct { + int a; + char b; + int c; +} COMPLEX_TYPE; + +/* Function to check if complex type object is cleared.*/ +bool comp_is_cleared(COMPLEX_TYPE *object) +{ + if (object->a == 0 && object->b == 0 && object->c == 0) { + return true; + } + + return false; +} + +/* Function to check if complex type object holds specified values.*/ +bool comp_is_equal(COMPLEX_TYPE *object, int a, char b, int c) +{ + if (object->a == a && object->b == b && object->c == c) { + return true; + } + + return false; +} + +/* Function to set complex type object fields.*/ +void comp_set(COMPLEX_TYPE *object, int a, char b, int c) +{ + object->a = a; + object->b = b; + object->c = c; +} + +/* Template for functional tests for try_alloc(), try_calloc() functions + * of MemoryPool object. + * + * Given MemoryPool object of the specified type and queue size has + * been successfully created. + * When max number of blocks is allocated from the pool. + * Then all allocations are successful. + * + * */ +template +void test_mem_pool_alloc_success(AllocType atype) +{ + MemoryPool mem_pool; + T *p_blocks[numOfEntries]; + uint32_t i; + + /* Test alloc()/calloc() methods - try to allocate max number of + blocks. All allocations should be successful. */ + for (i = 0; i < numOfEntries; i++) { + /* Allocate memory block. */ + if (atype == ALLOC) { + p_blocks[i] = mem_pool.try_alloc(); + } else { + p_blocks[i] = mem_pool.try_calloc(); + } + + /* Show that memory pool block has been allocated. */ + TEST_ASSERT_NOT_NULL(p_blocks[i]); + + /* Check if Calloc clears the block. */ + if (atype == CALLOC) { + TEST_ASSERT_EQUAL(0, *p_blocks[i]); + } + + /* Init fields. */ + *p_blocks[i] = (i + 5); + } + + /* Check if blocks holds valid values. */ + for (i = 0; i < numOfEntries; i++) { + TEST_ASSERT_EQUAL((i + 5), *p_blocks[i]); + } +} + +/* Template for functional tests for try_alloc(), try_calloc() functions + * of MemoryPool object. + * + * Complex memory pool block type is used. + * + * Given MemoryPool object of the specified type and queue size has + * been successfully created. + * When max number of blocks is allocated from the pool. + * Then all allocations are successful. + * + * */ +template +void test_mem_pool_alloc_success_complex(AllocType atype) +{ + MemoryPool mem_pool; + T *p_blocks[numOfEntries]; + uint32_t i; + + /* Test alloc()/calloc() methods - try to allocate max number of + blocks. All allocations should be successful. */ + for (i = 0; i < numOfEntries; i++) { + /* Allocate memory block. */ + if (atype == ALLOC) { + p_blocks[i] = mem_pool.try_alloc(); + } else { + p_blocks[i] = mem_pool.try_calloc(); + } + + /* Show that memory pool block has been allocated. */ + TEST_ASSERT_NOT_NULL(p_blocks[i]); + + /* Check if Calloc clears the block. */ + if (atype == CALLOC) { + TEST_ASSERT_EQUAL(true, comp_is_cleared(p_blocks[i])); + } + + /* Init fields. */ + comp_set(p_blocks[i], i + 1, i + 2, i + 3); + } + + /* Check if blocks holds valid values. */ + for (i = 0; i < numOfEntries; i++) { + TEST_ASSERT_EQUAL(true, comp_is_equal(p_blocks[i], i + 1, i + 2, i + 3)); + } +} + +/* Template for functional tests for try_alloc(), try_calloc() functions + * of MemoryPool object. + * + * Given MemoryPool has already max number of blocks allocated from the pool. + * When next block is allocated. + * Then allocation fails. + * + * */ +template +void test_mem_pool_alloc_fail(AllocType atype) +{ + MemoryPool mem_pool; + T *p_blocks[numOfEntries]; + T *p_extra_block; + uint32_t i; + + /* Allocate all available blocks. */ + for (i = 0; i < numOfEntries; i++) { + if (atype == ALLOC) { + p_blocks[i] = mem_pool.try_alloc(); + } else { + p_blocks[i] = mem_pool.try_calloc(); + } + + /* Show that memory pool block has been allocated. */ + TEST_ASSERT_NOT_NULL(p_blocks[i]); + } + + /* There are no more blocks available. Try to allocate another block. */ + if (atype == ALLOC) { + p_extra_block = mem_pool.try_alloc(); + } else { + p_extra_block = mem_pool.try_calloc(); + } + + /* Show that memory pool block has NOT been allocated. */ + TEST_ASSERT_NULL(p_extra_block); +} + +/* Template for functional tests for free() function + * of MemoryPool object. + * + * Given MemoryPool has all blocks allocated. + * When free operation is executed on the each allocated block. + * Then each deallocation is successfully performed. + * + * */ +template +void test_mem_pool_free_success(AllocType atype) +{ + MemoryPool mem_pool; + T *p_blocks[numOfEntries]; + uint32_t i; + osStatus status; + + /* Allocate all available blocks. */ + for (i = 0; i < numOfEntries; i++) { + if (atype == ALLOC) { + p_blocks[i] = mem_pool.try_alloc(); + } else { + p_blocks[i] = mem_pool.try_calloc(); + } + + /* Show that memory pool block has been allocated. */ + TEST_ASSERT_NOT_NULL(p_blocks[i]); + } + + /* Free all memory blocks. */ + for (i = 0; i < numOfEntries; i++) { + status = mem_pool.free(p_blocks[i]); + + /* Check operation status. */ + TEST_ASSERT_EQUAL(osOK, status); + } +} + +/* Template for functional tests for try_alloc(), try_calloc() functions + * of MemoryPool object. + * + * Basic memory pool block type is used. + * + * Given MemoryPool had all blocks allocated and one block has + * been freed (last). + * When next block is allocated. + * Then allocation is successful. + * + * */ +template +void test_mem_pool_free_realloc_last(AllocType atype) +{ + MemoryPool mem_pool; + T *p_blocks[numOfEntries]; + uint32_t i; + osStatus status; + + /* Allocate all available blocks. */ + for (i = 0; i < numOfEntries; i++) { + if (atype == ALLOC) { + p_blocks[i] = mem_pool.try_alloc(); + } else { + p_blocks[i] = mem_pool.try_calloc(); + } + + /* Init block. */ + *p_blocks[i] = 0xAB; + + /* Show that memory pool block has been allocated. */ + TEST_ASSERT_NOT_NULL(p_blocks[i]); + } + + /* Free the last block. */ + status = mem_pool.free(p_blocks[numOfEntries - 1]); + + /* Check status. */ + TEST_ASSERT_EQUAL(osOK, status); + + /* Try to allocate another block (one block is now available). */ + if (atype == ALLOC) { + p_blocks[numOfEntries - 1] = mem_pool.try_alloc(); + } else { + p_blocks[numOfEntries - 1] = mem_pool.try_calloc(); + } + + /* Show that memory pool block has been now allocated. */ + TEST_ASSERT_NOT_NULL(p_blocks[numOfEntries - 1]); + + /* Check if Calloc clears the block. */ + if (atype == CALLOC) { + TEST_ASSERT_EQUAL(0, *p_blocks[numOfEntries - 1]); + } +} + +/* Template for functional tests for try_alloc(), try_calloc() functions + * of MemoryPool object. + * + * Complex memory pool block type is used. + * + * Given MemoryPool had all blocks allocated and one block has + * been freed (last). + * When next block is allocated. + * Then allocation is successful. + * + * */ +template +void test_mem_pool_free_realloc_last_complex(AllocType atype) +{ + MemoryPool mem_pool; + T *p_blocks[numOfEntries]; + uint32_t i; + osStatus status; + + /* Allocate all available blocks. */ + for (i = 0; i < numOfEntries; i++) { + if (atype == ALLOC) { + p_blocks[i] = mem_pool.try_alloc(); + } else { + p_blocks[i] = mem_pool.try_calloc(); + } + + /* Init block. */ + comp_set(p_blocks[i], i + 1, i + 2, i + 3); + + /* Show that memory pool block has been allocated. */ + TEST_ASSERT_NOT_NULL(p_blocks[i]); + } + + /* Free the last block. */ + status = mem_pool.free(p_blocks[numOfEntries - 1]); + + /* Check status. */ + TEST_ASSERT_EQUAL(osOK, status); + + /* Try to allocate another block (one block is now available). */ + if (atype == ALLOC) { + p_blocks[numOfEntries - 1] = mem_pool.try_alloc(); + } else { + p_blocks[numOfEntries - 1] = mem_pool.try_calloc(); + } + + /* Show that memory pool block has been now allocated. */ + TEST_ASSERT_NOT_NULL(p_blocks[numOfEntries - 1]); + + /* Check if Calloc clears the block. */ + if (atype == CALLOC) { + TEST_ASSERT_EQUAL(true, comp_is_cleared(p_blocks[numOfEntries - 1])); + } +} + +/* Template for functional tests for try_alloc(), try_calloc() functions + * of MemoryPool object. + * + * Basic memory pool block type is used. + * + * Given MemoryPool had all blocks allocated and one block has + * been freed (first). + * When next block is allocated. + * Then allocation is successful. + * + * */ +template +void test_mem_pool_free_realloc_first(AllocType atype) +{ + MemoryPool mem_pool; + T *p_blocks[numOfEntries]; + uint32_t i; + osStatus status; + + /* Allocate all available blocks. */ + for (i = 0; i < numOfEntries; i++) { + if (atype == ALLOC) { + p_blocks[i] = mem_pool.try_alloc(); + } else { + p_blocks[i] = mem_pool.try_calloc(); + } + + /* Init block. */ + *p_blocks[i] = 0xAB; + + /* Show that memory pool block has been allocated. */ + TEST_ASSERT_NOT_NULL(p_blocks[i]); + } + + /* Free the last block. */ + status = mem_pool.free(p_blocks[0]); + + /* Check status. */ + TEST_ASSERT_EQUAL(osOK, status); + + /* Try to allocate another block (one block is now available). */ + if (atype == ALLOC) { + p_blocks[0] = mem_pool.try_alloc(); + } else { + p_blocks[0] = mem_pool.try_calloc(); + } + + /* Show that memory pool block has been now allocated. */ + TEST_ASSERT_NOT_NULL(p_blocks[0]); + + /* Check if Calloc clears the block. */ + if (atype == CALLOC) { + TEST_ASSERT_EQUAL(0, *p_blocks[0]); + } +} + +/* Template for functional tests for try_alloc(), try_calloc() functions + * of MemoryPool object. + * + * Complex memory pool block type is used. + * + * Given MemoryPool had all blocks allocated and one block has + * been freed (first). + * When next block is allocated. + * Then allocation is successful. + * + * */ +template +void test_mem_pool_free_realloc_first_complex(AllocType atype) +{ + MemoryPool mem_pool; + T *p_blocks[numOfEntries]; + uint32_t i; + osStatus status; + + /* Allocate all available blocks. */ + for (i = 0; i < numOfEntries; i++) { + if (atype == ALLOC) { + p_blocks[i] = mem_pool.try_alloc(); + } else { + p_blocks[i] = mem_pool.try_calloc(); + } + + /* Init block. */ + comp_set(p_blocks[i], i + 1, i + 2, i + 3); + + /* Show that memory pool block has been allocated. */ + TEST_ASSERT_NOT_NULL(p_blocks[i]); + } + + /* Free the last block. */ + status = mem_pool.free(p_blocks[0]); + + /* Check status. */ + TEST_ASSERT_EQUAL(osOK, status); + + /* Try to allocate another block (one block is now available). */ + if (atype == ALLOC) { + p_blocks[0] = mem_pool.try_alloc(); + } else { + p_blocks[0] = mem_pool.try_calloc(); + } + + /* Show that memory pool block has been now allocated. */ + TEST_ASSERT_NOT_NULL(p_blocks[0]); + + /* Check if Calloc clears the block. */ + if (atype == CALLOC) { + TEST_ASSERT_EQUAL(true, comp_is_cleared(p_blocks[0])); + } +} + +/* Test try_alloc_for/try_alloc_until 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 mem_pool; + + Timer timer; + timer.start(); + + int *item = mem_pool.try_alloc_for(TEST_TIMEOUT); + TEST_ASSERT_NOT_NULL(item); + TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, 0ms, timer.elapsed_time()); + + item = mem_pool.try_alloc_for(TEST_TIMEOUT); + TEST_ASSERT_NULL(item); + TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time()); + + auto end_time = Kernel::Clock::now() + TEST_TIMEOUT; + item = mem_pool.try_alloc_until(end_time); + TEST_ASSERT_NULL(item); + TEST_ASSERT_TIME_POINT_WITHIN(TEST_TIMEOUT / 10, end_time, Kernel::Clock::now()); +} + +namespace { +struct free_capture { + MemoryPool *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 pool; + + Timer timer; + timer.start(); + + int *item = pool.try_alloc_for(Kernel::wait_for_u32_forever); + TEST_ASSERT_NOT_NULL(item); + TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, 0ms, timer.elapsed_time()); + + struct free_capture to_free; + to_free.pool = &pool; + to_free.item = item; + t.start(callback(free_int_item, &to_free)); + + item = pool.try_alloc_for(Kernel::wait_for_u32_forever); + TEST_ASSERT_EQUAL(item, to_free.item); + TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time()); + + t.join(); +} + +/* Robustness checks for free() function. + * Function under test is called with invalid parameters. + * + * Given MemoryPool object has been successfully created. + * When free operation is performed on NULL address. + * Then deallocation fails with osErrorParameter error. + * + */ +void free_block_invalid_parameter_null() +{ + MemoryPool mem_pool; + osStatus status; + + /* Try to free block passing invalid parameter (NULL). */ + status = mem_pool.free(NULL); + + /* Check operation status. */ + TEST_ASSERT_EQUAL(osErrorParameter, status); +} + +/* Robustness checks for free() function. + * Function under test is called with invalid parameters. + * + * Given MemoryPool object has been successfully created. + * When free operation is performed on invalid address. + * Then deallocation fails with osErrorParameter error. + * + */ +void free_block_invalid_parameter() +{ + MemoryPool mem_pool; + osStatus status; + + /* Try to free block passing invalid parameter (variable address). */ + status = mem_pool.free(reinterpret_cast(&status)); + + /* Check operation status. */ + TEST_ASSERT_EQUAL(osErrorParameter, status); +} + +/* Use wrapper functions to reduce memory usage. */ + +template +void test_mem_pool_alloc_success_wrapper() +{ + test_mem_pool_alloc_success(ALLOC); + test_mem_pool_alloc_success(CALLOC); +} + +template +void test_mem_pool_alloc_success_complex_wrapper() +{ + test_mem_pool_alloc_success_complex(ALLOC); + test_mem_pool_alloc_success_complex(CALLOC); +} + +template +void test_mem_pool_free_success_wrapper() +{ + test_mem_pool_free_success(ALLOC); + test_mem_pool_free_success(CALLOC); +} + +template +void test_mem_pool_free_realloc_last_wrapper() +{ + test_mem_pool_free_realloc_last(ALLOC); + test_mem_pool_free_realloc_last(CALLOC); + +} + +template +void test_mem_pool_free_realloc_first_wrapper() +{ + test_mem_pool_free_realloc_first(ALLOC); + test_mem_pool_free_realloc_first(CALLOC); +} + +template +void test_mem_pool_free_realloc_first_complex_wrapper() +{ + test_mem_pool_free_realloc_first_complex(ALLOC); + test_mem_pool_free_realloc_first_complex(CALLOC); +} + +template +void test_mem_pool_free_realloc_last_complex_wrapper() +{ + test_mem_pool_free_realloc_last_complex(ALLOC); + test_mem_pool_free_realloc_last_complex(CALLOC); +} + +template +void test_mem_pool_alloc_fail_wrapper() +{ + test_mem_pool_alloc_fail(ALLOC); + test_mem_pool_alloc_fail(CALLOC); +} + +Case cases[] = { + Case("Test: try_alloc()/try_calloc() - success, 4 bytes b_type, q_size equal to 1.", test_mem_pool_alloc_success_wrapper), + Case("Test: try_alloc()/try_calloc() - success, 4 bytes b_type, q_size equal to 3.", test_mem_pool_alloc_success_wrapper), + Case("Test: try_alloc()/try_calloc() - success, 1 bytes b_type, q_size equal to 1.", test_mem_pool_alloc_success_wrapper), + Case("Test: try_alloc()/try_calloc() - success, 1 bytes b_type, q_size equal to 3.", test_mem_pool_alloc_success_wrapper), + Case("Test: try_alloc()/try_calloc() - success, complex b_type, q_size equal to 1.", test_mem_pool_alloc_success_complex_wrapper), + Case("Test: try_alloc()/try_calloc() - success, complex b_type, q_size equal to 3.", test_mem_pool_alloc_success_complex_wrapper), + + Case("Test: free() - success, 4 bytes b_type, q_size equal to 1.", test_mem_pool_free_success_wrapper), + Case("Test: free() - success, 4 bytes b_type, q_size equal to 3.", test_mem_pool_free_success_wrapper), + Case("Test: free() - success, complex b_type, q_size equal to 1.", test_mem_pool_free_success_wrapper), + Case("Test: free() - success, complex b_type, q_size equal to 3.", test_mem_pool_free_success_wrapper), + + Case("Test: re-allocation of the last block, basic type.", test_mem_pool_free_realloc_last_wrapper), + Case("Test: re-allocation of the first block, basic type.", test_mem_pool_free_realloc_first_wrapper), + Case("Test: re-allocation of the first block, complex type.", test_mem_pool_free_realloc_first_complex_wrapper), + Case("Test: re-allocation of the last block, complex type.", test_mem_pool_free_realloc_last_complex_wrapper), + + Case("Test: fail (out of free blocks).", test_mem_pool_alloc_fail_wrapper), + + 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).", free_block_invalid_parameter) +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(20, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif // !defined(MBED_CONF_RTOS_PRESENT) diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/basic/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/basic/main.cpp new file mode 100644 index 0000000..a405ca7 --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/basic/main.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2013-2017, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) +#error [NOT_SUPPORTED] RTOS basic test cases require RTOS with multithread to run +#else + +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "utest/utest.h" +#include "unity/unity.h" + +#if defined(SKIP_TIME_DRIFT_TESTS) || !DEVICE_USTICKER +#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. +#else + +using utest::v1::Case; +using std::milli; +using std::micro; +using namespace std::chrono; + +#if defined(__CORTEX_M23) || defined(__CORTEX_M33) +#define TEST_STACK_SIZE 512 +#else +#define TEST_STACK_SIZE 256 +#endif + +static duration elapsed_time_ms; +static const int test_timeout = 40; + + +void update_tick_thread(Mutex *mutex) +{ + while (true) { + ThisThread::sleep_for(1ms); + mutex->lock(); + ++elapsed_time_ms; + mutex->unlock(); + } +} + + +/** Tests is to measure the accuracy of ThisThread::sleep_for() over a period of time + + Given + a thread updating elapsed_time_ms every milli sec + and host script for time measurement accuracy check (More details on tests can be found in timing_drift_auto.py) + When host query what is current count base_time + Then Device responds by the elapsed_time_ms + When host query what is current count final_time + Then Device responds by the elapsed_time_ms + When host computes the drift considering base_time, final_time, transport delay and measurement stretch + Then host send the results back to device pass/fail based on tolerance + */ +void test(void) +{ + char _key[11] = { }; + char _value[128] = { }; + int expected_key = 1; + Mutex mutex; + duration elapsed_time; + + Thread tick_thread(osPriorityHigh, TEST_STACK_SIZE); + tick_thread.start(callback(update_tick_thread, &mutex)); + + greentea_send_kv("timing_drift_check_start", 0); + + // wait for 1st signal from host + do { + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + expected_key = strcmp(_key, "base_time"); + } while (expected_key); + + mutex.lock(); + elapsed_time = elapsed_time_ms; + mutex.unlock(); + // send base_time + greentea_send_kv(_key, elapsed_time.count()); + + // wait for 2nd signal from host + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + + mutex.lock(); + elapsed_time = elapsed_time_ms; + mutex.unlock(); + // send final_time + greentea_send_kv(_key, elapsed_time.count()); + + //get the results from host + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + + TEST_ASSERT_EQUAL_STRING_MESSAGE("pass", _key, "Host side script reported a fail..."); +} + +Case cases[] = { + Case("Test ThisThread::sleep_for accuracy", test) +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(test_timeout, "timing_drift_auto"); + return utest::v1::greentea_test_setup_handler(number_of_cases); +} + +utest::v1::Specification specification(greentea_test_setup, cases); + +int main() +{ + utest::v1::Harness::run(specification); +} + +#endif // defined(SKIP_TIME_DRIFT_TESTS) || !DEVICE_USTICKER +#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/condition_variable/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/condition_variable/main.cpp new file mode 100644 index 0000000..5424ffc --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/condition_variable/main.cpp @@ -0,0 +1,195 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) +#error [NOT_SUPPORTED] Condition variable test cases require RTOS with multithread to run +#else + +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. +#else + +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "rtos.h" + +using namespace utest::v1; +using namespace std::chrono_literals; + +#define TEST_STACK_SIZE 512 +#define TEST_DELAY 10ms + +static int change_counter = 0; +static Mutex mutex; +static ConditionVariable cond(mutex); + +void increment_on_signal() +{ + mutex.lock(); + + cond.wait(); + change_counter++; + + mutex.unlock(); +} + +void test_notify_one() +{ + Thread t1(osPriorityNormal, TEST_STACK_SIZE); + Thread t2(osPriorityNormal, TEST_STACK_SIZE); + + change_counter = 0; + t1.start(increment_on_signal); + t2.start(increment_on_signal); + + ThisThread::sleep_for(TEST_DELAY); + TEST_ASSERT_EQUAL(0, change_counter); + + mutex.lock(); + cond.notify_one(); + mutex.unlock(); + + ThisThread::sleep_for(TEST_DELAY); + TEST_ASSERT_EQUAL(1, change_counter); + + mutex.lock(); + cond.notify_one(); + mutex.unlock(); + + t1.join(); + t2.join(); +} + +void test_notify_all() +{ + Thread t1(osPriorityNormal, TEST_STACK_SIZE); + Thread t2(osPriorityNormal, TEST_STACK_SIZE); + + change_counter = 0; + t1.start(increment_on_signal); + t2.start(increment_on_signal); + + ThisThread::sleep_for(TEST_DELAY); + TEST_ASSERT_EQUAL(0, change_counter); + + mutex.lock(); + cond.notify_all(); + mutex.unlock(); + + ThisThread::sleep_for(TEST_DELAY); + TEST_ASSERT_EQUAL(2, change_counter); + + t1.join(); + t2.join(); +} + + +class TestConditionVariable : public ConditionVariable { + +public: + static void test_linked_list(void) + { + Waiter *list = NULL; + Waiter w1; + Waiter w2; + Waiter w3; + Waiter w4; + + TEST_ASSERT_EQUAL(0, validate_and_get_size(&list)); + + // Add 4 nodes + _add_wait_list(&list, &w1); + TEST_ASSERT_EQUAL(1, validate_and_get_size(&list)); + _add_wait_list(&list, &w2); + TEST_ASSERT_EQUAL(2, validate_and_get_size(&list)); + _add_wait_list(&list, &w3); + TEST_ASSERT_EQUAL(3, validate_and_get_size(&list)); + _add_wait_list(&list, &w4); + TEST_ASSERT_EQUAL(4, validate_and_get_size(&list)); + + // Remove a middle node + _remove_wait_list(&list, &w2); + TEST_ASSERT_EQUAL(3, validate_and_get_size(&list)); + + // Remove front node + _remove_wait_list(&list, &w1); + TEST_ASSERT_EQUAL(2, validate_and_get_size(&list)); + + // remove back node + _remove_wait_list(&list, &w4); + TEST_ASSERT_EQUAL(1, validate_and_get_size(&list)); + + // remove last node + _remove_wait_list(&list, &w3); + TEST_ASSERT_EQUAL(0, validate_and_get_size(&list)); + + TEST_ASSERT_EQUAL_PTR(NULL, list); + } + + /** + * Validate the linked list an return the number of elements + * + * If this list is invalid then this function asserts and does not + * return. + * + * Every node in a valid linked list has the properties: + * 1. node->prev->next == node + * 2. node->next->prev == node + */ + static int validate_and_get_size(Waiter **list) + { + Waiter *first = *list; + if (NULL == first) { + // List is empty + return 0; + } + + int size = 0; + Waiter *current = first; + do { + TEST_ASSERT_EQUAL_PTR(current, current->prev->next); + TEST_ASSERT_EQUAL_PTR(current, current->next->prev); + current = current->next; + size++; + } while (current != first); + return size; + } + +}; + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(10, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Test notify one", test_notify_one), + Case("Test notify all", test_notify_all), + Case("Test linked list", TestConditionVariable::test_linked_list), +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + +#endif // !DEVICE_USTICKER +#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/event_flags/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/event_flags/main.cpp new file mode 100644 index 0000000..0d906b3 --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/event_flags/main.cpp @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2013-2017, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest/utest.h" + +using utest::v1::Case; +using namespace std::chrono; + +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. +#else + +#if defined(MBED_CONF_RTOS_PRESENT) +#if defined(__CORTEX_M23) || defined(__CORTEX_M33) +#define THREAD_STACK_SIZE 512 +#else +#define THREAD_STACK_SIZE 320 /* 512B stack on GCC_ARM compiler cause out of memory on some 16kB RAM boards e.g. NUCLEO_F070RB */ +#endif +#endif + +#define MAX_FLAG_POS 30 +#define PROHIBITED_FLAG_POS 31 + +/* flags */ +#define FLAG01 0x1FFF /* 00000000000000000001111111111111 */ +#define FLAG02 0x3FFE000 /* 00000011111111111110000000000000 */ +#define FLAG03 0x7C000000 /* 01111100000000000000000000000000 */ +#define PROHIBITED_FLAG 0x80000000 /* 10000000000000000000000000000000 */ +#define NO_FLAGS 0x0 + +void send_thread(EventFlags *ef, uint32_t flags, milliseconds wait) +{ + for (uint32_t i = 0; i <= MAX_FLAG_POS; i++) { + const uint32_t flag = flags & (1 << i); + if (flag) { + ef->set(flag); + if (wait != 0ms) { + ThisThread::sleep_for(wait); + } + } + } +} + +#if defined(MBED_CONF_RTOS_PRESENT) +Semaphore sync_sem(0, 1); + +void send_thread_sync(EventFlags *ef, uint32_t flags, milliseconds wait) +{ + for (uint32_t i = 0; i <= MAX_FLAG_POS; i++) { + const uint32_t flag = flags & (1 << i); + if (flag) { + sync_sem.acquire(); + ef->set(flag); + ThisThread::sleep_for(wait); + } + } +} + +void wait_thread_all(EventFlags *ef, uint32_t flags) +{ + uint32_t ret, flags_after_clear; + ret = ef->wait_all(flags); + flags_after_clear = ef->get(); + TEST_ASSERT(flags | ret); + TEST_ASSERT(flags | ~flags_after_clear); +} +#endif + + +/** Test if get on empty EventFlags object return NO_FLAGS + + Given a empty EventFlags object + When call @a get + Then @a get return status is NO_FLAGS + */ +void test_empty_get(void) +{ + EventFlags ev; + uint32_t flags; + + flags = ev.get(); + TEST_ASSERT_EQUAL(NO_FLAGS, flags); +} + +/** Test if clear on empty EventFlags object return NO_FLAGS + + Given a empty EventFlags object + When call @a clear(NO_FLAGS) + Then @a clear return status is NO_FLAGS + */ +void test_empty_clear(void) +{ + EventFlags ev; + uint32_t flags; + + flags = ev.clear(NO_FLAGS); + TEST_ASSERT_EQUAL(NO_FLAGS, flags); +} + +/** Test if set on empty EventFlags object return NO_FLAGS + + Given a empty EventFlags object + When call @a set(NO_FLAGS) + Then @a set return status is NO_FLAGS + */ +void test_empty_set(void) +{ + EventFlags ev; + uint32_t flags; + + flags = ev.set(NO_FLAGS); + TEST_ASSERT_EQUAL(NO_FLAGS, flags); +} + +/** Test if call of set/clean with PROHIBITED_FLAG doesn't invalidates object flags + + Given a EventFlags object with all flags already set + When call @a clear(PROHIBITED_FLAG) with prohibited flag + Then @a clear return status is osFlagsErrorParameter and object flags stays unchanged + When call @a set(PROHIBITED_FLAG) with prohibited flag + Then @a set return status is osFlagsErrorParameter and object flags stays unchanged + + @note Each signal has up to 31 event flags 0x1, 0x2, 0x4, 0x8, ..., 0x40000000 + Most significant bit is reserved and thereby flag 0x80000000 is prohibited + */ +void test_prohibited(void) +{ + EventFlags ev; + uint32_t flags; + + ev.set(FLAG01 | FLAG02 | FLAG03); + +#if !MBED_TRAP_ERRORS_ENABLED + flags = ev.clear(PROHIBITED_FLAG); + TEST_ASSERT_EQUAL(osFlagsErrorParameter, flags); +#endif + + flags = ev.get(); + TEST_ASSERT_EQUAL(FLAG01 | FLAG02 | FLAG03, flags); + +#if !MBED_TRAP_ERRORS_ENABLED + flags = ev.set(PROHIBITED_FLAG); + TEST_ASSERT_EQUAL(osFlagsErrorParameter, flags); +#endif + + flags = ev.get(); + TEST_ASSERT_EQUAL(FLAG01 | FLAG02 | FLAG03, flags); +} + +/** Test set/get/clear for full flag range + + Given a EventFlags object + When call @a clear + Then @a clear return status is already set flags + When call @a set with specified flag + Then @a set return status is flags after setting + When call @a get + Then @a get return status is set flags + */ +void test_set_get_clear_full_flag_range(void) +{ + EventFlags ev; + uint32_t flag, flags, ret; + + flags = NO_FLAGS; + for (int i = 0; i <= MAX_FLAG_POS; i++) { + ret = ev.clear(); + TEST_ASSERT_EQUAL(flags, ret); + flags = 1 << i; + ret = ev.set(flags); + TEST_ASSERT_EQUAL(flags, ret); + ret = ev.get(); + TEST_ASSERT_EQUAL(flags, ret); + } + + ev.clear(); + flags = NO_FLAGS; + for (int i = 0; i <= MAX_FLAG_POS; i++) { + ret = ev.clear(NO_FLAGS); + TEST_ASSERT_EQUAL(flags, ret); + flag = 1 << i; + flags |= flag; + ret = ev.set(flag); + TEST_ASSERT_EQUAL(flags, ret); + ret = ev.get(); + TEST_ASSERT_EQUAL(flags, ret); + } +} + +#if defined(MBED_CONF_RTOS_PRESENT) +/** Test if multi-threaded flag set cause wait_all to return + + Given a EventFlags object and three threads are started in parallel + When threads set specified flags + Then main thread waits until receive all of them + */ +void test_multi_thread_all(void) +{ + EventFlags ef; + Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); + thread1.start([&] { send_thread(&ef, FLAG01, 1ms); }); + thread2.start([&] { send_thread(&ef, FLAG02, 2ms); }); + thread3.start([&] { send_thread(&ef, FLAG03, 3ms); }); + + uint32_t ret = ef.wait_all(FLAG01 | FLAG02 | FLAG03); + TEST_ASSERT_EQUAL(FLAG01 | FLAG02 | FLAG03, ret); +} + +/** Test if multi-threaded flag set cause wait_any to return + + Given a EventFlags object and three threads are started in parallel + When threads set specified flags + Then main thread waits until receive all of them + */ +void test_multi_thread_any(void) +{ + EventFlags ef; + uint32_t ret; + Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); + thread1.start([&] { send_thread(&ef, FLAG01, 1ms); }); + thread2.start([&] { send_thread(&ef, FLAG02, 1ms); }); + thread3.start([&] { send_thread(&ef, FLAG03, 1ms); }); + + for (int i = 0; i <= MAX_FLAG_POS; i++) { + uint32_t flag = 1 << i; + ret = ef.wait_any(flag); + TEST_ASSERT(flag | ret); + } + ret = ef.get(); + TEST_ASSERT_EQUAL(NO_FLAGS, ret); +} + +/** Test if multi-threaded flag set cause wait_any(with timeout) to return + + Given a EventFlags object and thread is running + When main thread call @ wait_any with timeout + Then when timeout expires @ wait_any return status is osFlagsErrorTimeout + When main thread call @ wait_any with timeout and thread set specified flags + Then main thread waits until receive all of them and @ wait_any return status is wait flag + */ +void test_multi_thread_any_timeout(void) +{ + EventFlags ef; + uint32_t ret; + Thread thread(osPriorityNormal, THREAD_STACK_SIZE); + thread.start([&] { send_thread_sync(&ef, FLAG01 | FLAG02 | FLAG03, 1ms); }); + + for (int i = 0; i <= MAX_FLAG_POS; i++) { + uint32_t flag = 1 << i; + + ret = ef.wait_any_for(flag, 10ms); + TEST_ASSERT_EQUAL(osFlagsErrorTimeout, ret); + + sync_sem.release(); + ret = ef.wait_any_for(flag, 10ms); + TEST_ASSERT_EQUAL(flag, ret); + } + ret = ef.get(); + TEST_ASSERT_EQUAL(NO_FLAGS, ret); +} + +/** Test if multi-threaded flag set cause wait_any(without clear) to return + + Given a EventFlags object and three threads are started in parallel + When threads set specified flags + Then main thread waits until receive all of them + */ +void test_multi_thread_any_no_clear(void) +{ + EventFlags ef; + uint32_t ret; + Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); + thread1.start([&] { send_thread(&ef, FLAG01, 1ms); }); + thread2.start([&] { send_thread(&ef, FLAG02, 1ms); }); + thread3.start([&] { send_thread(&ef, FLAG03, 1ms); }); + + for (int i = 0; i <= MAX_FLAG_POS; i++) { + uint32_t flag = 1 << i; + ret = ef.wait_any_for(flag, Kernel::wait_for_u32_forever, false); + TEST_ASSERT(flag | ret); + ret = ef.clear(flag); + TEST_ASSERT(ret < osFlagsError); + } + ret = ef.get(); + TEST_ASSERT_EQUAL(NO_FLAGS, ret); +} + +/** Test multi-threaded wait_any + + Given a EventFlags object and three threads are started in parallel + When flags are set in main thread + Then other threads waits until receive all of them + */ +void test_multi_thread_all_many_wait(void) +{ + EventFlags ef; + { + Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); + thread1.start([&] { wait_thread_all(&ef, FLAG01); }); + thread2.start([&] { wait_thread_all(&ef, FLAG02); }); + thread3.start([&] { wait_thread_all(&ef, FLAG03); }); + + ef.set(FLAG01 | FLAG02 | FLAG03); + thread1.join(); + thread2.join(); + thread3.join(); + TEST_ASSERT_EQUAL(NO_FLAGS, ef.get()); + } + + { + Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); + thread1.start([&] { wait_thread_all(&ef, FLAG01); }); + thread2.start([&] { wait_thread_all(&ef, FLAG02); }); + thread3.start([&] { wait_thread_all(&ef, FLAG03); }); + + ef.set(FLAG01); + thread1.join(); + ef.set(FLAG02); + thread2.join(); + ef.set(FLAG03); + thread3.join(); + TEST_ASSERT_EQUAL(NO_FLAGS, ef.get()); + } +} +#endif + +/** Test if multi-event flag set cause wait_all to return + + Given a EventFlags object and three ticker instance with the callback registered + When callbacks set specified flags + Then main thread waits until receive all of them + */ +void test_multi_eventflags_all(void) +{ + EventFlags ef; + Ticker t1, t2, t3; + t1.attach([&] { send_thread(&ef, FLAG01, 0ms); }, 3ms); + t2.attach([&] { send_thread(&ef, FLAG02, 0ms); }, 4ms); + t3.attach([&] { send_thread(&ef, FLAG03, 0ms); }, 5ms); + uint32_t ret = ef.wait_all_for(FLAG01 | FLAG02 | FLAG03, 20ms, false); + TEST_ASSERT_EQUAL(FLAG01 | FLAG02 | FLAG03, ret); +} + +/** Test if multi-event flag set cause wait_any to return + + Given a EventFlags object and three ticker instance with the callback registered + When callbacks set specified flags + Then main thread waits until receive all of them + */ +void test_multi_eventflags_any(void) +{ + EventFlags ef; + uint32_t ret; + Ticker t1, t2, t3; + t1.attach([&] { send_thread(&ef, FLAG01, 0ms); }, 3ms); + t2.attach([&] { send_thread(&ef, FLAG02, 0ms); }, 4ms); + t3.attach([&] { send_thread(&ef, FLAG03, 0ms); }, 5ms); + + for (int i = 0; i <= MAX_FLAG_POS; i++) { + uint32_t flag = 1 << i; + ret = ef.wait_any(flag); + TEST_ASSERT(flag | ret); + } + ret = ef.get(); + TEST_ASSERT_EQUAL(NO_FLAGS, ret); +} + +/** Test if multi-event flag set cause wait_any(without clear) to return + + Given a EventFlags object and three ticker instance with the callback registered + When callbacks set specified flags + Then main thread waits until receive all of them + */ +void test_multi_eventflags_any_no_clear(void) +{ + EventFlags ef; + uint32_t ret; + Ticker t1, t2, t3; + t1.attach([&] { send_thread(&ef, FLAG01, 0ms); }, 3ms); + t2.attach([&] { send_thread(&ef, FLAG02, 0ms); }, 4ms); + t3.attach([&] { send_thread(&ef, FLAG03, 0ms); }, 5ms); + + for (int i = 0; i <= MAX_FLAG_POS; i++) { + uint32_t flag = 1 << i; + ret = ef.wait_any(flag, osWaitForever, false); + TEST_ASSERT(flag | ret); + ret = ef.clear(flag); + TEST_ASSERT(ret < osFlagsError); + } + ret = ef.get(); + TEST_ASSERT_EQUAL(NO_FLAGS, ret); +} + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(10, "default_auto"); + return utest::v1::verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Test empty clear", test_empty_clear), + Case("Test empty get", test_empty_get), + Case("Test empty set", test_empty_set), + Case("Test clear/set with prohibited flag", test_prohibited), + Case("Test set/get/clear for full flag range", test_set_get_clear_full_flag_range), +#if defined(MBED_CONF_RTOS_PRESENT) + Case("Test multi-threaded wait_all", test_multi_thread_all), + Case("Test multi-threaded wait_any", test_multi_thread_any), + Case("Test multi-threaded wait_all many wait", test_multi_thread_all_many_wait), + Case("Test multi-threaded wait_any timeout", test_multi_thread_any_timeout), + Case("Test multi-threaded wait_any no clear", test_multi_thread_any_no_clear), +#endif + Case("Test multi-eventflags wait_all", test_multi_eventflags_all), + Case("Test multi-eventflags wait_any", test_multi_eventflags_any), + Case("Test multi-eventflags wait_any no clear", test_multi_eventflags_any_no_clear) +}; + +utest::v1::Specification specification(test_setup, cases); + +int main() +{ + return !utest::v1::Harness::run(specification); +} + +#endif // !DEVICE_USTICKER diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/heap_and_stack/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/heap_and_stack/main.cpp new file mode 100644 index 0000000..e4c88a3 --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/heap_and_stack/main.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2016-2017, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined(TARGET_CORTEX_A) || !DEVICE_USTICKER +#error [NOT_SUPPORTED] test not supported. +#else + +#include +#include +#include + +#include "mbed.h" +#include "cmsis.h" +#include "greentea-client/test_env.h" +#include "utest/utest.h" +#include "unity/unity.h" + +using utest::v1::Case; + +static const int test_timeout = 30; + + +// Amount to malloc for each iteration +#define MALLOC_TEST_SIZE 256 +// Malloc fill pattern +#define MALLOC_FILL 0x55 + +extern unsigned char *mbed_heap_start; +extern uint32_t mbed_heap_size; +extern unsigned char *mbed_stack_isr_start; +extern uint32_t mbed_stack_isr_size; + +#if defined(TOOLCHAIN_GCC_ARM) && defined(MBED_SPLIT_HEAP) +extern uint32_t __mbed_sbrk_start_0; +extern uint32_t __mbed_krbs_start_0; +unsigned char *mbed_heap_start_0 = (unsigned char *) &__mbed_sbrk_start_0;; +uint32_t mbed_heap_size_0 = (uint32_t) &__mbed_krbs_start_0 - (uint32_t) &__mbed_sbrk_start_0; +#endif + +struct linked_list { + linked_list *next; + uint8_t data[MALLOC_TEST_SIZE]; +}; + + +// Global test variables +#define TEST_VALUE 789 +static struct Test { + Test() : val(TEST_VALUE) {} + ~Test() {} + int val; +} t; + +int test_function() +{ + return TEST_VALUE; +} +static int global_int = test_function(); + +/** Test global variables initialisation + */ +void test_global_variables_initialisation(void) +{ + TEST_ASSERT_EQUAL(TEST_VALUE, global_int); + TEST_ASSERT_EQUAL(TEST_VALUE, t.val); +} + +/* TODO: add memory layout test. + * + * The test was skipped for now since not all devices seems to comply with Mbed OS memory. + * + * @note Mbed OS memory model: https://os.mbed.com/docs/latest/reference/memory.html + * + */ + + +/* + * Return true if addr is in range [start:start+size) + */ +static bool inrange(uint32_t addr, uint32_t start, uint32_t size) +{ + return (addr >= start) && (addr < (start + size)); +} + +/* + * Return true if [addr:addr+size] is inside [start:start+len] + */ +static bool rangeinrange(uint32_t addr, uint32_t size, uint32_t start, uint32_t len) +{ + if ((addr + size) > (start + len)) { + return false; + } + if (addr < start) { + return false; + } + return true; +} + +/* + * Return true if the region is filled only with the specified value + */ +static bool valid_fill(uint8_t *data, uint32_t size, uint8_t fill) +{ + for (uint32_t i = 0; i < size; i++) { + if (data[i] != fill) { + return false; + } + } + return true; +} + +static void allocate_and_fill_heap(linked_list *&head) +{ + linked_list *current; + + current = (linked_list *) malloc(sizeof(linked_list)); + TEST_ASSERT_NOT_NULL(current); + + current->next = NULL; + memset((void *) current->data, MALLOC_FILL, sizeof(current->data)); + + // Allocate until malloc returns NULL + head = current; + while (true) { + + // Allocate + linked_list *temp = (linked_list *) malloc(sizeof(linked_list)); + + if (NULL == temp) { + break; + } + bool result = rangeinrange((uint32_t) temp, sizeof(linked_list), (uint32_t)mbed_heap_start, mbed_heap_size); +#if defined(TOOLCHAIN_GCC_ARM) && defined(MBED_SPLIT_HEAP) + if (false == result) { + result = rangeinrange((uint32_t) temp, sizeof(linked_list), (uint32_t)mbed_heap_start_0, mbed_heap_size_0); + } +#endif + TEST_ASSERT_TRUE_MESSAGE(result, "Memory allocation out of range"); + + // Init + temp->next = NULL; + memset((void *) temp->data, MALLOC_FILL, sizeof(current->data)); + + // Add to list + current->next = temp; + current = temp; + } +} + +static void check_and_free_heap(linked_list *head, uint32_t &max_allocation_size) +{ + uint32_t total_size = 0; + linked_list *current = head; + + while (current != NULL) { + total_size += sizeof(linked_list); + bool result = valid_fill(current->data, sizeof(current->data), MALLOC_FILL); + + TEST_ASSERT_TRUE_MESSAGE(result, "Memory fill check failed"); + + linked_list *next = current->next; + free(current); + current = next; + } + + max_allocation_size = total_size; +} + +/** Test heap allocation + + Given a heap + When memory is allocated from heap + Then the memory is within heap boundary + + */ +void test_heap_in_range(void) +{ + char *initial_heap; + + // Sanity check malloc + initial_heap = (char *) malloc(1); + TEST_ASSERT_NOT_NULL(initial_heap); + + bool result = inrange((uint32_t) initial_heap, (uint32_t)mbed_heap_start, mbed_heap_size); + +#if defined(TOOLCHAIN_GCC_ARM) && defined(MBED_SPLIT_HEAP) + if (false == result) { + result = inrange((uint32_t) initial_heap, (uint32_t)mbed_heap_start_0, mbed_heap_size_0); + } +#endif + TEST_ASSERT_TRUE_MESSAGE(result, "Heap in wrong location"); + free(initial_heap); +} + +#if MBED_CONF_RTOS_PRESENT +/** Test for Main thread stack + + Given a Main thread and its stack + When check Main thread stack pointer + Then the SP is within Main stack boundary + */ +void test_main_stack_in_range(void) +{ + mbed_rtos_storage_thread_t *thread = (mbed_rtos_storage_thread_t *) osThreadGetId(); + + uint32_t psp = __get_PSP(); + uint8_t *stack_mem = (uint8_t *) thread->stack_mem; + uint32_t stack_size = thread->stack_size; + + // PSP stack should be somewhere in the middle + bool result = inrange(psp, (uint32_t) stack_mem, stack_size); + + TEST_ASSERT_TRUE_MESSAGE(result, "Main stack in wrong location"); +} +#endif // #if MBED_CONF_RTOS_PRESENT + +/** Test for Scheduler/ISR thread stack + + Given a Scheduler/ISR thread and its stack + When check Scheduler/ISR thread stack pointer + Then the SP is within Scheduler/ISR stack boundary + */ +void test_isr_stack_in_range(void) +{ + // MSP stack should be very near end (test using within 128 bytes) + uint32_t msp = __get_MSP(); + bool result = inrange(msp, (uint32_t)mbed_stack_isr_start + mbed_stack_isr_size - 0x400, 0x400); + + TEST_ASSERT_TRUE_MESSAGE(result, "Interrupt stack in wrong location"); +} + +/** Test full heap allocation + + Given a heap and linked_list data structure + When linked_list is filled till run out of heap memory + Then the memory is properly initialised and freed + */ +void test_heap_allocation_free(void) +{ + linked_list *head = NULL; + uint32_t max_allocation_size = 0; + + // Fully allocate the heap and stack + allocate_and_fill_heap(head); + + check_and_free_heap(head, max_allocation_size); + + // Force a task switch so a stack check is performed + ThisThread::sleep_for(10); + + printf("Total size dynamically allocated: %luB\n", max_allocation_size); +} + + +// Test cases +Case cases[] = { + Case("Test global variables initialisation", test_global_variables_initialisation), + Case("Test heap in range", test_heap_in_range), +#if MBED_CONF_RTOS_PRESENT + Case("Test main stack in range", test_main_stack_in_range), +#endif + Case("Test isr stack in range", test_isr_stack_in_range), + Case("Test heap allocation and free", test_heap_allocation_free) +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(test_timeout, "default_auto"); + return utest::v1::greentea_test_setup_handler(number_of_cases); +} + +utest::v1::Specification specification(greentea_test_setup, cases); + +int main() +{ + return !utest::v1::Harness::run(specification); +} + +#endif // defined(TARGET_CORTEX_A) || !DEVICE_USTICKER diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/kernel_tick_count/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/kernel_tick_count/main.cpp new file mode 100644 index 0000000..0b72099 --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/kernel_tick_count/main.cpp @@ -0,0 +1,135 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "greentea-client/test_env.h" +#include "utest/utest.h" +#include "unity/unity.h" + +#include "rtos/Kernel.h" +#include "mbed.h" + +#include + +using namespace std::chrono; + +using utest::v1::Case; + +#define TEST_REPEAT_COUNT 1000 + +#define ACCURACY_DURATION 1s +#if defined(NO_SYSTICK) || defined(MBED_TICKLESS) +// On targets with NO_SYSTICK/MBED_TICKLESS enabled, systick is emulated by lp_ticker what makes it less accurate +// for more details https://os.mbed.com/docs/latest/reference/tickless.html +#define ACCURACY_DELTA 15000us // 1.5% +#else +#define ACCURACY_DELTA 1500us // 0.15% +#endif + + +#define TEST_ASSERT_EQUAL_DURATION(expected, actual) \ + do { \ + using ct = std::common_type_t; \ + TEST_ASSERT_EQUAL(ct(expected).count(), ct(actual).count()); \ + } while (0) + +#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ + do { \ + using ct = std::common_type_t; \ + TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ + } while (0) + +/** Test if declared kernel ticker frequency is 1kHz + + Given a RTOS kernel ticker + When check it frequency + Then the the frequency is 1kHz + */ +void test_frequency() +{ +#if defined(MBED_CONF_RTOS_PRESENT) + uint32_t freq = osKernelGetTickFreq(); + TEST_ASSERT_EQUAL_UINT32_MESSAGE(1000, freq, "Expected SysTick frequency is 1kHz"); +#endif + TEST_ASSERT_TRUE_MESSAGE((std::ratio_equal::value), "Expected Kernel::Clock frequency is 1kHz"); +} + +/** Test if kernel ticker increments by one + + Given a RTOS kernel ticker + When perform subsequent calls of @a rtos::Kernel::Clock::now + Then subsequent reads should not differ by more than one + */ +void test_increment(void) +{ + for (uint32_t i = 0; i < TEST_REPEAT_COUNT; i++) { + auto start = rtos::Kernel::Clock::now(); + while (true) { + rtos::Kernel::Clock::duration diff = rtos::Kernel::Clock::now() - start; + if (diff.count() != 0) { + TEST_ASSERT_EQUAL_INT64(1, diff.count()); + break; + } + } + } +} + +/** Test if kernel ticker rate is correct + + Given a RTOS kernel ticker + When perform subsequent calls of @a rtos::Kernel::Clock::now + Then when it reports 1 second elapsed, the time measured using a high-res Timer corresponds. + */ +void test_accuracy() +{ + Kernel::Clock::time_point start, stop; + Timer timer; + + start = rtos::Kernel::Clock::now(); + // wait for tick + do { + stop = rtos::Kernel::Clock::now(); + } while (stop == start); + timer.start(); + start = stop; + + // wait for 1 second to elapse according to kernel + do { + stop = rtos::Kernel::Clock::now(); + } while ((stop - start) < ACCURACY_DURATION); + timer.stop(); + + TEST_ASSERT_DURATION_WITHIN(ACCURACY_DELTA, ACCURACY_DURATION, timer.elapsed_time()); +} + +// Test cases +Case cases[] = { + Case("Test kernel ticker declared frequency", test_frequency), + Case("Test if kernel ticker increments by one", test_increment), + Case("Test kernel ticker accuracy", test_accuracy) +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(10, "default_auto"); + return utest::v1::greentea_test_setup_handler(number_of_cases); +} + +utest::v1::Specification specification(greentea_test_setup, cases); + +int main() +{ + return !utest::v1::Harness::run(specification); +} diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/mail/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/mail/main.cpp new file mode 100644 index 0000000..1c22f61 --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/mail/main.cpp @@ -0,0 +1,510 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) +#error [NOT_SUPPORTED] mail test cases require RTOS with multithread to run +#else + +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. +#else + +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "rtos.h" + +using namespace utest::v1; +using namespace std::chrono; + +#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ + do { \ + using ct = std::common_type_t; \ + TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ + } while (0) + +#if defined(__CORTEX_M23) || defined(__CORTEX_M33) +#define THREAD_STACK_SIZE 512 +#elif defined(TARGET_ARM_FM) +#define THREAD_STACK_SIZE 512 +#elif defined(TARGET_CY8CKIT_062_WIFI_BT_PSA) +#define THREAD_STACK_SIZE 512 +#else +#define THREAD_STACK_SIZE 320 /* larger stack cause out of heap memory on some 16kB RAM boards in multi thread test*/ +#endif +#define QUEUE_SIZE 16 +#define THREAD_1_ID 1 +#define THREAD_2_ID 2 +#define THREAD_3_ID 3 +#define QUEUE_PUT_DELAY_1 5ms +#define QUEUE_PUT_DELAY_2 50ms +#define QUEUE_PUT_DELAY_3 100ms +#define DATA_BASE 100 + + +typedef struct { + uint16_t data; + uint8_t thread_id; +} mail_t; + + +void send_thread(Mail *m, uint8_t thread_id, milliseconds wait, uint32_t send_count) +{ + uint32_t data = thread_id * DATA_BASE; + + for (uint32_t i = 0; i < send_count; i++) { + mail_t *mail = m->try_alloc(); + mail->thread_id = thread_id; + mail->data = data++; + m->put(mail); + ThisThread::sleep_for(wait); + } +} + +template +void receive_thread(Mail *m, uint8_t thread_id, milliseconds wait) +{ + int result_counter = 0; + uint32_t data = thread_id * DATA_BASE; + + ThisThread::sleep_for(wait); + for (uint32_t i = 0; i < queue_size; i++) { + mail_t *mail = m->try_get_for(Kernel::wait_for_u32_forever); + if (mail) { + const uint8_t id = mail->thread_id; + + // verify thread id + TEST_ASSERT_TRUE(id == thread_id); + // verify sent data + TEST_ASSERT_TRUE(mail->data == data++); + + m->free(mail); + result_counter++; + } + } + TEST_ASSERT_EQUAL(queue_size, result_counter); +} + +/** Test single thread Mail usage and order + + Given mailbox and one additional thread + When messages are put in to the Mail box by this thread + Then messages are received in main thread in the same order as was sent and the data sent is valid + */ +void test_single_thread_order(void) +{ + uint16_t data = DATA_BASE; + int result_counter = 0; + Mail mail_box; + + // mail send thread creation + Thread thread(osPriorityNormal, THREAD_STACK_SIZE); + thread.start([&] { send_thread(&mail_box, THREAD_1_ID, QUEUE_PUT_DELAY_1, QUEUE_SIZE); }); + + // wait for some mail to be collected + ThisThread::sleep_for(10ms); + + for (uint32_t i = 0; i < QUEUE_SIZE; i++) { + // mail receive (main thread) + mail_t *mail = mail_box.try_get_for(Kernel::wait_for_u32_forever); + if (mail) { + const uint8_t id = mail->thread_id; + + // verify thread id + TEST_ASSERT_TRUE(id == THREAD_1_ID); + // verify sent data + TEST_ASSERT_TRUE(mail->data == data++); + mail_box.free(mail); + + result_counter++; + } + } + TEST_ASSERT_EQUAL(QUEUE_SIZE, result_counter); +} + +/** Test multi thread Mail usage and order + + Given mailbox and three additional threads + When messages are put in to the Mail box by these threads + Then messages are received in main thread in the same per thread order as was sent and the data sent is valid + */ +void test_multi_thread_order(void) +{ + uint16_t data[4] = { 0, DATA_BASE, DATA_BASE * 2, DATA_BASE * 3 }; + int result_counter = 0; + Mail mail_box; + + // mail send threads creation + Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); + thread1.start([&] { send_thread(&mail_box, THREAD_1_ID, QUEUE_PUT_DELAY_1, 7); }); + thread2.start([&] { send_thread(&mail_box, THREAD_2_ID, QUEUE_PUT_DELAY_2, 5); }); + thread3.start([&] { send_thread(&mail_box, THREAD_3_ID, QUEUE_PUT_DELAY_3, 4); }); + + // wait for some mail to be collected + ThisThread::sleep_for(10ms); + + for (uint32_t i = 0; i < QUEUE_SIZE; i++) { + // mail receive (main thread) + mail_t *mail = mail_box.try_get_for(Kernel::wait_for_u32_forever); + if (mail) { + const uint8_t id = mail->thread_id; + + // verify thread id + TEST_ASSERT_TRUE((id == THREAD_1_ID) || (id == THREAD_2_ID) || (id == THREAD_3_ID)); + // verify sent data + TEST_ASSERT_TRUE(mail->data == data[id]++); + mail_box.free(mail); + + result_counter++; + } + } + TEST_ASSERT_EQUAL(QUEUE_SIZE, result_counter); +} + +/** Test multi thread multi Mail usage and order + + Given 3 mailbox and three additional threads + When messages are put in to the mail boxes by main thread + Then messages are received by threads in the same per mail box order as was sent and the data sent is valid + */ +void test_multi_thread_multi_mail_order(void) +{ + Mail mail_box[4]; /* mail_box[0] not used */ + uint16_t data[4] = { 0, DATA_BASE, DATA_BASE * 2, DATA_BASE * 3 }; + mail_t *mail; + uint8_t id; + + // mail receive threads creation + Thread thread1(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread2(osPriorityNormal, THREAD_STACK_SIZE); + Thread thread3(osPriorityNormal, THREAD_STACK_SIZE); + thread1.start([&] { receive_thread<4>(mail_box + 1, THREAD_1_ID, 0ms); }); + thread2.start([&] { receive_thread<4>(mail_box + 2, THREAD_2_ID, 10ms); }); + thread3.start([&] { receive_thread<4>(mail_box + 3, THREAD_3_ID, 100ms); }); + + for (uint32_t i = 0; i < 4; i++) { + id = THREAD_1_ID; + mail = mail_box[id].try_alloc(); + mail->thread_id = id; + mail->data = data[id]++; + mail_box[id].put(mail); + + id = THREAD_2_ID; + mail = mail_box[id].try_alloc(); + mail->thread_id = id; + mail->data = data[id]++; + mail_box[id].put(mail); + + id = THREAD_3_ID; + mail = mail_box[id].try_alloc(); + mail->thread_id = id; + mail->data = data[id]++; + mail_box[id].put(mail); + + ThisThread::sleep_for(i * 10ms); + } + + thread1.join(); + thread2.join(); + thread3.join(); +} + +/** Test message memory deallocation with block out of the scope + + Given an empty mailbox + When try to free out of the scope memory block + Then it return appropriate error code + */ +void test_free_wrong() +{ + osStatus status; + Mail mail_box; + uint32_t *mail, data; + + mail = &data; + status = mail_box.free(mail); + TEST_ASSERT_EQUAL(osErrorParameter, status); + + mail = mail_box.try_alloc(); + TEST_ASSERT_NOT_EQUAL(NULL, mail); + + mail = &data; + status = mail_box.free(mail); + TEST_ASSERT_EQUAL(osErrorParameter, status); +} + +/** Test message memory deallocation with null block + + Given an empty mailbox + When try to free null ptr + Then it return appropriate error code + */ +void test_free_null() +{ + osStatus status; + Mail mail_box; + uint32_t *mail; + + mail = NULL; + status = mail_box.free(mail); + TEST_ASSERT_EQUAL(osErrorParameter, status); + + mail = mail_box.try_alloc(); + TEST_ASSERT_NOT_EQUAL(NULL, mail); + + mail = NULL; + status = mail_box.free(mail); + TEST_ASSERT_EQUAL(osErrorParameter, status); +} + +/** Test get from empty mailbox with timeout set + + Given an empty mailbox + When @a try_get_for is called on the mailbox with timeout of 50ms + Then mailbox returns no data + */ +void test_get_empty_timeout() +{ + Mail mail_box; + Timer timer; + + timer.start(); + uint32_t *mail = mail_box.try_get_for(50ms); + TEST_ASSERT_DURATION_WITHIN(5ms, 50ms, timer.elapsed_time()); + TEST_ASSERT_NULL(mail); +} + +/** Test get from empty mailbox with 0 timeout + + Given an empty mailbox + When @a try_get is called on the mailbox + Then mailbox returns no data + */ +void test_get_empty_no_timeout() +{ + Mail mail_box; + + uint32_t *mail = mail_box.try_get(); + TEST_ASSERT_NULL(mail); +} + +/** Test mail order + + Given an mailbox for uint32_t values + Then allocate two mails and put them in to mailbox + When call @a get it returns previously put mails + Then mails should be in the same order as put + */ +void test_order(void) +{ + osStatus status; + Mail mail_box; + const int32_t TEST_VAL1 = 123; + const int32_t TEST_VAL2 = 456; + + int32_t *mail1 = mail_box.try_alloc(); + TEST_ASSERT_NOT_EQUAL(NULL, mail1); + + *mail1 = TEST_VAL1; + mail_box.put(mail1); + + int32_t *mail2 = mail_box.try_alloc(); + TEST_ASSERT_NOT_EQUAL(NULL, mail2); + + *mail2 = TEST_VAL2; + mail_box.put(mail2); + + + mail1 = mail_box.try_get_for(Kernel::wait_for_u32_forever); + TEST_ASSERT_NOT_NULL(mail1); + TEST_ASSERT_EQUAL(TEST_VAL1, *mail1); + + mail2 = mail_box.try_get_for(Kernel::wait_for_u32_forever); + TEST_ASSERT_NOT_NULL(mail2); + TEST_ASSERT_EQUAL(TEST_VAL2, *mail2); + + + status = mail_box.free(mail1); + TEST_ASSERT_EQUAL(osOK, status); + + status = mail_box.free(mail2); + TEST_ASSERT_EQUAL(osOK, status); +} + +/** Test Mail box max size limit + + Given an Mail box with max size of 4 elements + When call @a try_alloc four times it returns memory blocks + Then the memory blocks should be valid + When call @a try_alloc one more time it returns memory blocks + Then the memory blocks should be not valid (NULL - no memory available) + */ +void test_max_size() +{ + osStatus status; + Mail mail_box; + + // 1 OK + uint32_t *mail1 = mail_box.try_alloc(); + TEST_ASSERT_NOT_EQUAL(NULL, mail1); + + // 2 OK + uint32_t *mail2 = mail_box.try_alloc(); + TEST_ASSERT_NOT_EQUAL(NULL, mail2); + + // 3 OK + uint32_t *mail3 = mail_box.try_alloc(); + TEST_ASSERT_NOT_EQUAL(NULL, mail3); + + // 4 OK + uint32_t *mail4 = mail_box.try_alloc(); + TEST_ASSERT_NOT_EQUAL(NULL, mail4); + + // 5 KO + uint32_t *mail5 = mail_box.try_alloc(); + TEST_ASSERT_EQUAL(NULL, mail5); + + + status = mail_box.free(mail1); + TEST_ASSERT_EQUAL(osOK, status); + + status = mail_box.free(mail2); + TEST_ASSERT_EQUAL(osOK, status); + + status = mail_box.free(mail3); + TEST_ASSERT_EQUAL(osOK, status); + + status = mail_box.free(mail4); + TEST_ASSERT_EQUAL(osOK, status); +} + +/** Test mailbox of T type data + + Given an mailbox with T memory block type + When allocate/put/get/free memory block + Then all operations should succeed + */ +template +void test_data_type(void) +{ + osStatus status; + Mail mail_box; + const T TEST_VAL = 123; + + T *mail = mail_box.try_alloc(); + TEST_ASSERT_NOT_EQUAL(NULL, mail); + + *mail = TEST_VAL; + mail_box.put(mail); + + mail = mail_box.try_get_for(Kernel::wait_for_u32_forever); + TEST_ASSERT_NOT_NULL(mail); + TEST_ASSERT_EQUAL(TEST_VAL, *mail); + + + status = mail_box.free(mail); + TEST_ASSERT_EQUAL(osOK, status); +} + +/** Test try_calloc - memory block allocation with resetting + + Given an empty Mail box + When call @a try_calloc it returns allocated memory block + Then the memory block should be valid and filled with zeros + */ +void test_calloc() +{ + Mail mail_box; + + uint32_t *mail = mail_box.try_calloc(); + TEST_ASSERT_NOT_EQUAL(NULL, mail); + TEST_ASSERT_EQUAL(0, *mail); +} + +/** Test mail empty + + Given a mail of uint32_t data + before data is inserted the mail should be empty + after data is inserted the mail shouldn't be empty + */ +void test_mail_empty() +{ + Mail m; + + mail_t *mail = m.try_alloc(); + + TEST_ASSERT_EQUAL(true, m.empty()); + + m.put(mail); + + TEST_ASSERT_EQUAL(false, m.empty()); +} + +/** Test mail empty + + Given a mail of uint32_t data with size of 1 + before data is inserted the mail shouldn't be full + after data is inserted the mail should be full + */ +void test_mail_full() +{ + Mail m; + + mail_t *mail = m.try_alloc(); + + TEST_ASSERT_EQUAL(false, m.full()); + + m.put(mail); + + TEST_ASSERT_EQUAL(true, m.full()); +} + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(10, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Test try_calloc", test_calloc), + Case("Test message type uint8", test_data_type), + Case("Test message type uint16", test_data_type), + Case("Test message type uint32", test_data_type), + Case("Test mailbox max size", test_max_size), + Case("Test message send order", test_order), + Case("Test get with timeout on empty mailbox", test_get_empty_timeout), + Case("Test get without timeout on empty mailbox", test_get_empty_no_timeout), + Case("Test null message free", test_free_null), + Case("Test invalid message free", test_free_wrong), + Case("Test message send/receive single thread and order", test_single_thread_order), + Case("Test message send/receive multi-thread and per thread order", test_multi_thread_order), + Case("Test message send/receive multi-thread, multi-Mail and per thread order", test_multi_thread_multi_mail_order), + Case("Test mail empty", test_mail_empty), + Case("Test mail full", test_mail_full) +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + +#endif // !DEVICE_USTICKER +#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/malloc/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/malloc/main.cpp new file mode 100644 index 0000000..bc8678f --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/malloc/main.cpp @@ -0,0 +1,225 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "utest/utest.h" +#include "unity/unity.h" + + +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] test not supported +#else + +using utest::v1::Case; + +extern uint32_t mbed_heap_size; +static const int test_timeout = 25; +volatile bool thread_should_continue = true; +#define NUM_THREADS 4 +#define THREAD_MALLOC_SIZE 100 + +#if defined(__CORTEX_A9) +#define THREAD_STACK_SIZE 512 +#elif defined(__CORTEX_M23) || defined(__CORTEX_M33) +#define THREAD_STACK_SIZE 512 +#elif defined(TARGET_ARM_FM) +#define THREAD_STACK_SIZE 512 +#elif defined(TARGET_CY8CKIT_062_WIFI_BT_PSA) +#define THREAD_STACK_SIZE 512 +#else +#define THREAD_STACK_SIZE 256 +#endif + + +void task_using_malloc(void) +{ + void *data = NULL; + + while (thread_should_continue) { + // Repeatedly allocate and free memory + data = malloc(THREAD_MALLOC_SIZE); + TEST_ASSERT_NOT_NULL(data); + + // test whole allocated memory + memset(data, 0, THREAD_MALLOC_SIZE); + + free(data); + } +} +/** Test for multithreaded heap allocations + + Given multiple threads are started in parallel + When each of the threads allocate memory + Then the memory allocation succeed and @a malloc return valid memory + */ +#if defined(MBED_CONF_RTOS_PRESENT) +void test_multithread_allocation(void) +{ + // static stack for threads to reduce heap usage on devices with small RAM + // and eliminate run out of heap memory problem + uint8_t stack[NUM_THREADS][THREAD_STACK_SIZE]; + + bool thread_alloc_failure = false; + Thread *thread_list[NUM_THREADS]; + int test_time = 20; + + // Allocate threads for the test + for (int i = 0; i < NUM_THREADS; i++) { + thread_list[i] = new Thread(osPriorityNormal, THREAD_STACK_SIZE, stack[i]); + if (NULL == thread_list[i]) { + thread_alloc_failure = true; + } else { + thread_list[i]->start(task_using_malloc); + } + } + + // Give the test time to run + while (test_time--) { + ThisThread::sleep_for(1000); + } + + // Join and delete all threads + thread_should_continue = false; + for (int i = 0; i < NUM_THREADS; i++) { + if (NULL != thread_list[i]) { + thread_list[i]->join(); + delete thread_list[i]; + thread_list[i] = NULL; + } + } + TEST_ASSERT_FALSE(thread_alloc_failure); +} +#endif + +/** Test for multiple heap alloc and free calls */ +#define ALLOC_ARRAY_SIZE 100 +#define ALLOC_LOOP 20 +#define SIZE_INCREMENTS 1023 +#define SIZE_MODULO 31 + +void test_alloc_and_free(void) +{ + void *array[ALLOC_ARRAY_SIZE]; + void *data = NULL; + long total_allocated = 0; + int count = 0; + int size = SIZE_INCREMENTS; + int loop = ALLOC_LOOP; + while (loop) { + data = count < ALLOC_ARRAY_SIZE ? malloc(size) : NULL; + if (NULL != data) { + array[count++] = data; + memset((void *)data, 0xdeadbeef, size); + total_allocated += size; + size += SIZE_INCREMENTS; + if (size > 10000) { + size %= SIZE_MODULO; + } + } else { + for (int i = 0; i < count; i++) { + free(array[i]); + array[i] = NULL; + } + loop--; + printf("Total size dynamically allocated: %luB\n", total_allocated); + total_allocated = 0; + count = 0; + continue; + } + } +} + +/** Test for large heap allocation + + Given a heap of size mbed_heap_size + When try to allocate memory of size mbed_heap_size/5 (20% of whole heap) + Then the memory is allocated and @a malloc return valid memory + */ +void test_big_allocation(void) +{ + const uint32_t alloc_size = mbed_heap_size / 5; + void *data = NULL; + + data = malloc(alloc_size); + TEST_ASSERT_NOT_NULL(data); + + // test whole allocated memory + memset(data, 0, alloc_size); + + free(data); +} + +/** Test if allocation of zero size does not cause any undefined behaviour + + Given a heap + When try to allocate memory of size 0 + Then the return value of @a malloc depends on the particular library implementation + (NULL or smallest possible allocation) and no undefined behaviour happens + + @note If allocation size is zero, the return value depends on the particular library implementation + (it may or may not be a null pointer), but the returned pointer shall not be dereferenced + */ +void test_zero_allocation(void) +{ + void *data = NULL; + + data = malloc(0); + if (data != NULL) { + free(data); + } + TEST_ASSERT_MESSAGE(true, "malloc(0) succeed - no undefined behaviour happens"); +} + +/** Test if free on NULL pointer does not cause any undefined behaviour + + Given a NULL pointer + When try to free it + Then the function @a free does nothing and no undefined behaviour happens + */ +void test_null_free(void) +{ + void *data = NULL; + free(data); + + TEST_ASSERT_MESSAGE(true, "free(NULL) succeed - no undefined behaviour happens"); +} + +// Test cases +Case cases[] = { + Case("Test 0 size allocation", test_zero_allocation), + Case("Test NULL pointer free", test_null_free), +#if defined(MBED_CONF_RTOS_PRESENT) + Case("Test multithreaded allocations", test_multithread_allocation), +#endif + Case("Test large allocation", test_big_allocation), + Case("Test multiple alloc and free calls", test_alloc_and_free) +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(test_timeout, "default_auto"); + return utest::v1::greentea_test_setup_handler(number_of_cases); +} + +utest::v1::Specification specification(greentea_test_setup, cases); + +int main() +{ + return !utest::v1::Harness::run(specification); +} + +#endif // defined(MBED_RTOS_SINGLE_THREAD) || !DEVICE_USTICKER diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/mutex/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/mutex/main.cpp new file mode 100644 index 0000000..52705ed --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/mutex/main.cpp @@ -0,0 +1,323 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) +#error [NOT_SUPPORTED] Mutex test cases require RTOS with multithread to run +#else + +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. +#else + +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "rtos.h" +#include + +#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ + do { \ + using ct = std::common_type_t; \ + TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ + } while (0) + + +using namespace utest::v1; +using namespace std::chrono; + +#if defined(__CORTEX_M23) || defined(__CORTEX_M33) +#define TEST_STACK_SIZE 768 +#else +#define TEST_STACK_SIZE 512 +#endif + +#define TEST_LONG_DELAY 20ms +#define TEST_DELAY 10ms +#define SIGNALS_TO_EMIT 100 + +Mutex stdio_mutex; + +volatile int change_counter = 0; +volatile bool changing_counter = false; +volatile bool mutex_defect = false; + +bool manipulate_protected_zone(const Kernel::Clock::duration thread_delay) +{ + bool result = true; + osStatus stat; + + stdio_mutex.lock(); + + core_util_critical_section_enter(); + if (changing_counter == true) { + result = false; + mutex_defect = true; + } + changing_counter = true; + + change_counter++; + core_util_critical_section_exit(); + + ThisThread::sleep_for(thread_delay); + + core_util_critical_section_enter(); + changing_counter = false; + core_util_critical_section_exit(); + + stdio_mutex.unlock(); + + return result; +} + +void test_thread(Kernel::Clock::duration const *thread_delay) +{ + while (true) { + manipulate_protected_zone(*thread_delay); + } +} + +/** Test multiple thread + + Given 3 threads started with different delays and a section protected with a mutex + when each thread runs it tries to lock the mutex + then no more than one thread should be able to access protected region +*/ +void test_multiple_threads(void) +{ + const Kernel::Clock::duration t1_delay = TEST_DELAY * 1; + const Kernel::Clock::duration t2_delay = TEST_DELAY * 2; + const Kernel::Clock::duration t3_delay = TEST_DELAY * 3; + + Thread t2(osPriorityNormal, TEST_STACK_SIZE); + Thread t3(osPriorityNormal, TEST_STACK_SIZE); + + t2.start(callback(test_thread, &t2_delay)); + t3.start(callback(test_thread, &t3_delay)); + + while (true) { + // Thread 1 action + ThisThread::sleep_for(t1_delay); + manipulate_protected_zone(t1_delay); + + core_util_critical_section_enter(); + if (change_counter >= SIGNALS_TO_EMIT or mutex_defect == true) { + core_util_critical_section_exit(); + t2.terminate(); + t3.terminate(); + break; + } + core_util_critical_section_exit(); + } + + TEST_ASSERT_EQUAL(false, mutex_defect); +} + +void test_dual_thread_nolock_lock_thread(Mutex *mutex) +{ + osStatus stat; + mutex->lock(); + + mutex->unlock(); +} + +void test_dual_thread_nolock_trylock_thread(Mutex *mutex) +{ + bool stat_b = mutex->trylock(); + TEST_ASSERT_EQUAL(true, stat_b); + + mutex->unlock(); +} + +/** Test dual thread no-lock + + Test dual thread second thread lock + Given two threads A & B and a mutex + When thread A creates a mutex and starts thread B + and thread B calls @a lock and @a unlock + Then @a lock and @a unlock operations are successfully performed. + + Test dual thread second thread trylock + Given two threads A & B and a mutex + When thread A creates a mutex and starts thread B + and thread B calls @a trylock and @a unlock + Then @a trylock and @a unlock operations are successfully performed. +*/ +template +void test_dual_thread_nolock(void) +{ + Mutex mutex; + Thread thread(osPriorityNormal, TEST_STACK_SIZE); + + thread.start(callback(F, &mutex)); + + ThisThread::sleep_for(TEST_DELAY); +} + +void test_dual_thread_lock_unlock_thread(Mutex *mutex) +{ + mutex->lock(); +} + +/** Test dual thread lock unlock + + Given two threads and a lock + When thread A locks the lock and starts thread B + and thread B calls @a lock on the mutex + Then thread B waits for thread A to unlock the lock + When thread A calls @a unlock on the mutex + Then thread B acquires the lock +*/ +void test_dual_thread_lock_unlock(void) +{ + Mutex mutex; + osStatus stat; + Thread thread(osPriorityNormal, TEST_STACK_SIZE); + + mutex.lock(); + + thread.start(callback(test_dual_thread_lock_unlock_thread, &mutex)); + + mutex.unlock(); + + ThisThread::sleep_for(TEST_DELAY); +} + +void test_dual_thread_lock_trylock_thread(Mutex *mutex) +{ + bool stat = mutex->trylock(); + TEST_ASSERT_EQUAL(false, stat); +} + +void test_dual_thread_lock_lock_thread(Mutex *mutex) +{ + Timer timer; + timer.start(); + + bool stat = mutex->trylock_for(TEST_DELAY); + TEST_ASSERT_EQUAL(false, stat); + TEST_ASSERT_DURATION_WITHIN(5ms, TEST_DELAY, timer.elapsed_time()); +} + +/** Test dual thread lock + + Test dual thread lock locked + Given a mutex and two threads A & B + When thread A calls @a lock and starts thread B + and thread B calls @a lock with 500ms timeout + Then thread B waits 500ms and timeouts + + Test dual thread trylock locked + Given a mutex and two threads A & B + When thread A calls @a lock and starts thread B + Then thread B calls @a trylock + and thread B fails to acquire the lock +*/ +template +void test_dual_thread_lock(void) +{ + Mutex mutex; + osStatus stat; + Thread thread(osPriorityNormal, TEST_STACK_SIZE); + + mutex.lock(); + + thread.start(callback(F, &mutex)); + + ThisThread::sleep_for(TEST_LONG_DELAY); + + mutex.unlock(); +} + +/** Test single thread lock recursive + + Given a mutex and a single running thread + When thread calls @a lock twice and @a unlock twice on the mutex + Then @a lock and @a unlock operations are successfully performed. +*/ +void test_single_thread_lock_recursive(void) +{ + Mutex mutex; + osStatus stat; + + mutex.lock(); + + mutex.lock(); + + mutex.unlock(); + + mutex.unlock(); +} + +/** Test single thread trylock + + Given a mutex and a single running thread + When thread calls @a trylock and @a unlock on the mutex + Then @a trylock and @a unlock operations are successfully performed. +*/ +void test_single_thread_trylock(void) +{ + Mutex mutex; + + bool stat_b = mutex.trylock(); + TEST_ASSERT_EQUAL(true, stat_b); + + mutex.unlock(); +} + +/** Test single thread lock + + Given a mutex and a single running thread + When thread calls @a lock and @a unlock on the mutex + Then @a lock and @a unlock operations are successfully performed. +*/ +void test_single_thread_lock(void) +{ + Mutex mutex; + osStatus stat; + + mutex.lock(); + + mutex.unlock(); +} + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(10, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Test single thread lock", test_single_thread_lock), + Case("Test single thread trylock", test_single_thread_trylock), + Case("Test single thread lock recursive", test_single_thread_lock_recursive), + Case("Test dual thread lock locked", test_dual_thread_lock), + Case("Test dual thread trylock locked", test_dual_thread_lock), + Case("Test dual thread lock unlock", test_dual_thread_lock_unlock), + Case("Test dual thread second thread lock", test_dual_thread_nolock), + Case("Test dual thread second thread trylock", test_dual_thread_nolock), + Case("Test multiple thread", test_multiple_threads), +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + +#endif // !DEVICE_USTICKER +#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/queue/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/queue/main.cpp new file mode 100644 index 0000000..822fa48 --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/queue/main.cpp @@ -0,0 +1,347 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) +#error [NOT_SUPPORTED] Queue test cases require RTOS with multithread to run +#else + +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. +#else + +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "rtos.h" + +using namespace utest::v1; +using namespace std::chrono; + +#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ + do { \ + using ct = std::common_type_t; \ + TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ + } while (0) + +#define THREAD_STACK_SIZE 512 +#define TEST_TIMEOUT 50ms + +static uint32_t msg; +static uint32_t msg2; + +void thread_put_uint_msg(Queue *q) +{ + ThisThread::sleep_for(TEST_TIMEOUT); + bool stat = q->try_put(&msg); + TEST_ASSERT_TRUE(stat); +} + +void thread_get_uint_msg(Queue *q) +{ + ThisThread::sleep_for(TEST_TIMEOUT); + uint32_t *v; + bool stat = q->try_get_for(Kernel::wait_for_u32_forever, &v); + TEST_ASSERT_TRUE(stat) + TEST_ASSERT_EQUAL(&msg, v); +} + +/** Test pass msg + + Given a queue for uint32_t messages with one slot + When a uin32_t value is inserted into the queue + and a message is extracted from the queue + Then the extracted message is the same as previously inserted message + */ +void test_pass() +{ + Queue q; + bool stat = q.try_put(&msg); + TEST_ASSERT_TRUE(stat) + + uint32_t *v; + stat = q.try_get_for(Kernel::wait_for_u32_forever, &v); + TEST_ASSERT_TRUE(stat) + TEST_ASSERT_EQUAL(&msg, v); +} + +/** Test pass msg twice + + Given a queue for uint32_t messages with one slot + When a uin32_t value is inserted into the queue + and a message is extracted from the queue + and the procedure is repeated with different message + Then the extracted message is the same as previously inserted message for both iterations + + */ +void test_pass_twice() +{ + Queue q; + bool stat = q.try_put(&msg); + TEST_ASSERT_TRUE(stat); + + uint32_t *v; + stat = q.try_get_for(Kernel::wait_for_u32_forever, &v); + TEST_ASSERT_TRUE(stat); + TEST_ASSERT_EQUAL(&msg, v); + + stat = q.try_put(&msg2); + TEST_ASSERT_TRUE(stat); + + stat = q.try_get_for(Kernel::wait_for_u32_forever, &v); + TEST_ASSERT_TRUE(stat); + TEST_ASSERT_EQUAL(&msg2, v); +} + +/** Test get from empty queue + + Given an empty queue for uint32_t values + When @a get is called on the queue with timeout of 0 + Then queue returns status of false + */ +void test_get_empty_no_timeout() +{ + Queue q; + + uint32_t *v; + bool stat = q.try_get(&v); + TEST_ASSERT_FALSE(stat); +} + +/** Test get from empty queue with timeout + + Given an empty queue for uint32_t values + When @a get is called on the queue with timeout of 50ms + Then queue returns status of osEventTimeout after about 50ms wait + */ +void test_get_empty_timeout() +{ + Queue q; + Timer timer; + timer.start(); + + uint32_t *v; + bool stat = q.try_get_for(50ms, &v); + TEST_ASSERT_FALSE(stat); + TEST_ASSERT_DURATION_WITHIN(5ms, 50ms, timer.elapsed_time()); +} + +/** Test get empty wait forever + + Given a two threads A & B and a queue for uint32_t values + When thread A calls @a get on an empty queue with osWaitForever + Then the thread A waits for a message to appear in the queue + When thread B puts a message in the queue + Then thread A wakes up and receives it + */ +void test_get_empty_wait_forever() +{ + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + Queue q; + + t.start(callback(thread_put_uint_msg, &q)); + + Timer timer; + timer.start(); + + uint32_t *v; + bool stat = q.try_get_for(Kernel::wait_for_u32_forever, &v); + TEST_ASSERT_TRUE(stat); + TEST_ASSERT_EQUAL(&msg, v); + TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time()); +} + +/** Test put full no timeout + * + * Given a queue with one slot for uint32_t data + * When a thread tries to insert two messages + * Then first operation succeeds and second fails + */ +void test_put_full_no_timeout() +{ + Queue q; + + bool stat = q.try_put(&msg); + TEST_ASSERT_TRUE(stat); + + stat = q.try_put(&msg); + TEST_ASSERT_FALSE(stat); +} + +/** Test put full timeout + * + * Given a queue with one slot for uint32_t data + * When a thread tries to insert two messages with @ TEST_TIMEOUT timeout + * Then first operation succeeds and second fails + */ +void test_put_full_timeout() +{ + Queue q; + + bool stat = q.try_put_for(TEST_TIMEOUT, &msg); + TEST_ASSERT_TRUE(stat); + + Timer timer; + timer.start(); + + stat = q.try_put_for(TEST_TIMEOUT, &msg); + TEST_ASSERT_FALSE(stat); + TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time()); +} + +/** Test put full wait forever + * + * Given two threads A & B and a queue with one slot for uint32_t data + * When thread A puts a message to the queue and tries to put second one with @a Kernel::wait_for_u32_forever timeout + * Then thread waits for a slot to become empty in the queue + * When thread B takes one message out of the queue + * Then thread A successfully inserts message into the queue + */ +void test_put_full_waitforever() +{ + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + Queue q; + + t.start(callback(thread_get_uint_msg, &q)); + + bool stat = q.try_put(&msg); + TEST_ASSERT_TRUE(stat); + + Timer timer; + timer.start(); + stat = q.try_put_for(Kernel::wait_for_u32_forever, &msg); + TEST_ASSERT_TRUE(stat); + TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time()); + + t.join(); +} + +/** Test message ordering + + Given a queue of uint32_t data + When two messages are inserted with equal priority + Then messages should be returned in the exact order they were inserted + */ +void test_msg_order() +{ + Queue q; + + bool stat = q.try_put_for(TEST_TIMEOUT, &msg); + TEST_ASSERT_TRUE(stat); + + stat = q.try_put_for(TEST_TIMEOUT, &msg2); + TEST_ASSERT_TRUE(stat); + + uint32_t *v; + stat = q.try_get(&v); + TEST_ASSERT_TRUE(stat); + TEST_ASSERT_EQUAL(&msg, v); + + stat = q.try_get(&v); + TEST_ASSERT_TRUE(stat); + TEST_ASSERT_EQUAL(&msg2, v); +} + +/** Test message priority + + Given a queue of uint32_t data + When two messages are inserted with ascending priority + Then messages should be returned in descending priority order + */ +void test_msg_prio() +{ + Queue q; + + bool stat = q.try_put_for(TEST_TIMEOUT, &msg, 0); + TEST_ASSERT_TRUE(stat); + + stat = q.try_put_for(TEST_TIMEOUT, &msg2, 1); + TEST_ASSERT_TRUE(stat); + + uint32_t *v; + stat = q.try_get(&v); + TEST_ASSERT_TRUE(stat); + TEST_ASSERT_EQUAL(&msg2, v); + + stat = q.try_get(&v); + TEST_ASSERT_TRUE(stat); + TEST_ASSERT_EQUAL(&msg, v); +} + +/** Test queue empty + + Given a queue of uint32_t data + before data is inserted the queue should be empty + after data is inserted the queue shouldn't be empty + */ +void test_queue_empty() +{ + Queue q; + + TEST_ASSERT_TRUE(q.empty()); + + q.try_put_for(TEST_TIMEOUT, &msg, 1); + + TEST_ASSERT_FALSE(q.empty()); +} + +/** Test queue empty + + Given a queue of uint32_t data with size of 1 + before data is inserted the queue shouldn't be full + after data is inserted the queue should be full + */ +void test_queue_full() +{ + Queue q; + + TEST_ASSERT_FALSE(q.full()); + + q.try_put_for(TEST_TIMEOUT, &msg, 1); + + TEST_ASSERT_TRUE(q.full()); +} + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(5, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Test pass msg", test_pass), + Case("Test pass msg twice", test_pass_twice), + Case("Test get from empty queue no timeout", test_get_empty_no_timeout), + Case("Test get from empty queue timeout", test_get_empty_timeout), + Case("Test get empty wait forever", test_get_empty_wait_forever), + Case("Test put full no timeout", test_put_full_no_timeout), + Case("Test put full timeout", test_put_full_timeout), + Case("Test put full wait forever", test_put_full_waitforever), + Case("Test message ordering", test_msg_order), + Case("Test message priority", test_msg_prio), + Case("Test queue empty", test_queue_empty), + Case("Test queue full", test_queue_full) +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + +#endif // !DEVICE_USTICKER +#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/semaphore/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/semaphore/main.cpp new file mode 100644 index 0000000..efa02be --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/semaphore/main.cpp @@ -0,0 +1,359 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. +#else + +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "rtos.h" + +using namespace utest::v1; +using namespace std::chrono; + +struct test_data { + Semaphore *sem; + uint32_t data; +}; + +#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ + do { \ + using ct = std::common_type_t; \ + TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ + } while (0) + +#if defined(MBED_CONF_RTOS_PRESENT) +#define THREAD_DELAY 30ms +#define SEMAPHORE_SLOTS 2 +#define SEM_CHANGES 100 +#define SHORT_WAIT 5ms + +#define THREAD_STACK_SIZE 320 /* larger stack cause out of heap memory on some 16kB RAM boards in multi thread test*/ + +Semaphore two_slots(SEMAPHORE_SLOTS); + +volatile int change_counter = 0; +volatile int sem_counter = 0; +volatile bool sem_defect = false; + +void test_thread(rtos::Kernel::Clock::duration const *delay) +{ + const auto thread_delay = *delay; + while (true) { + two_slots.acquire(); + sem_counter++; + const bool sem_lock_failed = sem_counter > SEMAPHORE_SLOTS; + if (sem_lock_failed) { + sem_defect = true; + } + ThisThread::sleep_for(thread_delay); + sem_counter--; + change_counter++; + two_slots.release(); + } +} + +/* Test multiple threads + + Given 3 threads started with different delays and a semaphore with 2 tokens + when each thread runs it tries to acquire a token + then no more than two threads should be able to access protected region +*/ +void test_multi() +{ + const rtos::Kernel::Clock::duration t1_delay = THREAD_DELAY * 1; + const rtos::Kernel::Clock::duration t2_delay = THREAD_DELAY * 2; + const rtos::Kernel::Clock::duration t3_delay = THREAD_DELAY * 3; + + Thread t1(osPriorityNormal, THREAD_STACK_SIZE); + Thread t2(osPriorityNormal, THREAD_STACK_SIZE); + Thread t3(osPriorityNormal, THREAD_STACK_SIZE); + + t1.start(callback(test_thread, &t1_delay)); + t2.start(callback(test_thread, &t2_delay)); + t3.start(callback(test_thread, &t3_delay)); + + while (true) { + if (change_counter >= SEM_CHANGES or sem_defect == true) { + t1.terminate(); + t2.terminate(); + t3.terminate(); + break; + } + } +} + +void single_thread(struct test_data *data) +{ + data->sem->acquire(); + data->data++; +} + +/** Test single thread + + Given a two threads A & B and a semaphore (with count of 0) and a counter (equals to 0) + when thread B calls @a acquire + then thread B waits for a token to become available + then the counter is equal to 0 + when thread A calls @a release on the semaphore + then thread B acquires a token and increments the counter + then the counter equals to 1 + */ +void test_single_thread() +{ + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + Semaphore sem(0); + struct test_data data; + osStatus res; + + data.sem = &sem; + data.data = 0; + + res = t.start(callback(single_thread, &data)); + TEST_ASSERT_EQUAL(osOK, res); + ThisThread::sleep_for(SHORT_WAIT); + + TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state()); + TEST_ASSERT_EQUAL(0, data.data); + + res = sem.release(); + TEST_ASSERT_EQUAL(osOK, res); + + ThisThread::sleep_for(SHORT_WAIT); + + TEST_ASSERT_EQUAL(1, data.data); + + t.join(); +} + +void timeout_thread(Semaphore *sem) +{ + bool acquired = sem->try_acquire_for(30ms); + TEST_ASSERT_FALSE(acquired); +} + +/** Test timeout + + Given thread and a semaphore with no tokens available + when a thread calls @a try_acquire_for with a timeout of 30ms + then the thread is blocked for up to 30ms and timeouts after. + */ +void test_timeout() +{ + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + Semaphore sem(0); + osStatus res; + + Timer timer; + timer.start(); + res = t.start(callback(timeout_thread, &sem)); + TEST_ASSERT_EQUAL(osOK, res); + ThisThread::sleep_for(SHORT_WAIT); + + TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state()); + + t.join(); + TEST_ASSERT_DURATION_WITHIN(5ms, 30ms, timer.elapsed_time()); +} +#endif + +void test_ticker_release(struct test_data *data) +{ + osStatus res; + data->data++; + res = data->sem->release(); + TEST_ASSERT_EQUAL(osOK, res); +} + +/** Test semaphore acquire + + Given a semaphore with no tokens available and ticker with the callback registered + when the main thread calls @a acquire + then the main thread is blocked + when callback calls @a release on the semaphore + then the main thread is unblocked + */ +void test_semaphore_acquire() +{ + Semaphore sem(0); + struct test_data data; + + data.sem = &sem; + data.data = 0; + Ticker t1; + t1.attach(callback(test_ticker_release, &data), 3ms); + sem.acquire(); + t1.detach(); + + TEST_ASSERT_EQUAL(1, data.data); +} + +void test_ticker_try_acquire(Semaphore *sem) +{ + osStatus res; + res = sem->try_acquire(); + TEST_ASSERT_FALSE(res); +} + +/** Test semaphore try acquire + + Given a semaphore with no tokens available and ticker with the callback registered + when callback tries to acquire the semaphore, it fails. + */ +void test_semaphore_try_acquire() +{ + Semaphore sem(0); + Ticker t1; + t1.attach(callback(test_ticker_try_acquire, &sem), 3ms); + ThisThread::sleep_for(4ms); + t1.detach(); +} + + +/** Test semaphore try timeout + + Given a semaphore with no tokens available + when the main thread calls @a try_acquire_for with 3ms timeout + then the main thread is blocked for 3ms and timeouts after + */ +void test_semaphore_try_timeout() +{ + Semaphore sem(0); + bool res; + res = sem.try_acquire_for(3ms); + TEST_ASSERT_FALSE(res); +} + + +void test_ticker_semaphore_release(struct Semaphore *sem) +{ + osStatus res; + res = sem->release(); + TEST_ASSERT_EQUAL(osOK, res); +} + +/** Test semaphore try acquire timeout + + Given a semaphore with no tokens available and ticker with the callback registered + when the main thread calls @a try_acquire_for with 10ms timeout + then the main thread is blocked for up to 10ms + when callback call @a release on the semaphore after 3ms + then the main thread is unblocked. + */ +void test_semaphore_try_acquire_timeout() +{ + Semaphore sem(0); + bool res; + Ticker t1; + t1.attach(callback(test_ticker_semaphore_release, &sem), 3ms); + res = sem.try_acquire_for(10ms); + t1.detach(); + TEST_ASSERT_TRUE(res); +} + +/** Test no timeouts + +Test 1 token no timeout +Given thread and a semaphore with one token available +when thread calls @a try_acquire with timeout of 0ms +then the thread acquires the token immediately + +Test 0 tokens no timeout +Given thread and a semaphore with no tokens available +when thread calls @a try_acquire with timeout of 0ms +then the thread returns immediately without acquiring a token + */ +template +void test_no_timeout() +{ + Semaphore sem(T); + + Timer timer; + timer.start(); + + bool acquired = sem.try_acquire(); + TEST_ASSERT_EQUAL(T > 0, acquired); + + TEST_ASSERT_DURATION_WITHIN(5ms, 0ms, timer.elapsed_time()); +} + +/** Test multiple tokens wait + + Given a thread and a semaphore initialized with 5 tokens + when thread calls @a try_acquire 6 times in a loop + then the token counts goes to zero + */ +void test_multiple_tokens_wait() +{ + Semaphore sem(5); + + for (int i = 5; i >= 0; i--) { + bool acquired = sem.try_acquire(); + TEST_ASSERT_EQUAL(i > 0, acquired); + } +} + +/** Test multiple tokens release + + Given a thread and a semaphore initialized with zero tokens and max of 5 + when thread calls @a release 6 times on the semaphore + then the token count should be equal to 5 and last release call should fail + */ +void test_multiple_tokens_release() +{ + Semaphore sem(0, 5); + + for (int i = 5; i > 0; i--) { + osStatus stat = sem.release(); + TEST_ASSERT_EQUAL(osOK, stat); + } + osStatus stat = sem.release(); + TEST_ASSERT_EQUAL(osErrorResource, stat); +} + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(10, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Test 1 token no timeout", test_no_timeout<1>), + Case("Test 0 tokens no timeout", test_no_timeout<0>), + Case("Test multiple tokens wait", test_multiple_tokens_wait), + Case("Test multiple tokens release", test_multiple_tokens_release), + Case("Test semaphore acquire", test_semaphore_acquire), + Case("Test semaphore try acquire", test_semaphore_try_acquire), + Case("Test semaphore try timeout", test_semaphore_try_timeout), + Case("Test semaphore try acquire timeout", test_semaphore_try_acquire_timeout), +#if defined(MBED_CONF_RTOS_PRESENT) + Case("Test single thread", test_single_thread), + Case("Test timeout", test_timeout), + Case("Test multiple threads", test_multi) +#endif +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + +#endif // !DEVICE_USTICKER diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/signals/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/signals/main.cpp new file mode 100644 index 0000000..0a4d7ca --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/signals/main.cpp @@ -0,0 +1,544 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "utest/utest.h" +#include "unity/unity.h" + +using utest::v1::Case; + +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. +#else + +#define ALL_SIGNALS 0x7fffffff +#define NO_SIGNALS 0x0 + +#define SIGNAL1 0x1 +#define SIGNAL2 0x2 +#define SIGNAL3 0x4 + +template +void run_signal_wait(void) +{ + uint32_t ret = ThisThread::flags_wait_all_for(signals, timeout); + TEST_ASSERT_EQUAL(test_val, ret); +} + +template +void run_clear(void) +{ + int32_t ret = ThisThread::flags_clear(signals); + TEST_ASSERT_EQUAL(test_val, ret); +} + +void run_multiple_wait_clear(int32_t signals1, int32_t signals2, int32_t test_val1, int32_t test_val2) +{ + int32_t ret; + ret = ThisThread::flags_clear(signals1); + TEST_ASSERT_EQUAL(test_val1, ret); + ret = ThisThread::flags_clear(signals2); + TEST_ASSERT_EQUAL(test_val2, ret); +} +/** Validate that ticker callback to flags_clr(NO_SIGNALS) doesn't change main thread signals and return actual signals + + Given main thread and ticker instance with callback registered + When callback calls @a flags_clear(NO_SIGNALS) + then callback @a flags_clear return status should be ALL_SIGNALS indicating that the signal is unchanged + */ +void test_clear_no_signals_with_ticker(void) +{ + Ticker t; + osThreadFlagsSet(ThisThread::get_id(), ALL_SIGNALS); + t.attach_us([&] { run_multiple_wait_clear(NO_SIGNALS, NO_SIGNALS, ALL_SIGNALS, ALL_SIGNALS); }, 3000); +} + +/** Validate all flags_clr clears the signal in one shot + + Given main thread and ticker instance with callback registered + When callback calls @a flags_clear(ALL_SIGNALS) with all possible signals + then callback @a flags_clear(NO_SIGNALS) return status should be NO_SIGNALS(0) indicating all signals cleared correctly + */ +void test_clear_all_with_ticker(void) +{ + Ticker t; + osThreadFlagsSet(ThisThread::get_id(), ALL_SIGNALS); + t.attach_us([&] { run_multiple_wait_clear(ALL_SIGNALS, NO_SIGNALS, ALL_SIGNALS, NO_SIGNALS); }, 3000); +} + +/** Validate if any signals are set on ticker callback + + Given main thread and ticker instance with callback registered + When callback calls @a flags_clear(NO_SIGNALS) + then callback @a flags_clear return status should be NO_SIGNALS(0) indicating no signals set + */ +void test_init_state_with_ticker(void) +{ + Ticker t; + t.attach_us(callback(run_clear), 3000); +} + +/** Validate signal_wait return status if timeout specified + + Given main thread and ticker instance with callback registered + When callback calls @a flags_wait_all_for(signals, timeout) with specified signals and timeout + then callback @a flags_wait_all_for timeout and return 0 indicating no signals set + */ +template +void test_wait_timeout_with_ticker(void) +{ + Ticker t; + t.attach_us(callback(run_signal_wait), 3000); +} + +void run_release_wait_signal_wait_callback() +{ + osThreadFlagsSet(ThisThread::get_id(), ALL_SIGNALS); +} + +/** Validate that call of signal_wait return correctly when thread has all signals already set + + Given main thread and ticker instance with callback registered + When main thread @a flags_wait_all_for(ALL_SIGNALS, osWaitForever), + then main thread is blocked + when a callback calls @a osThreadFlagsSet set ALL_SIGNALS + then the main thread is unblocked from @ flags_wait_all_for with the return of ALL_SIGNALS set + */ +void test_wait_all_already_set_with_ticker(void) +{ + Ticker t; + t.attach_us([&] { run_release_wait_signal_wait_callback(); }, 3000); + uint32_t ret = ThisThread::flags_wait_all_for(ALL_SIGNALS, osWaitForever); + TEST_ASSERT_EQUAL(ALL_SIGNALS, ret); +} + +void run_release_wait_signal_set_callback(int32_t signal, osThreadId_t id) +{ + int32_t ret; + if (signal == 0) { + for (int i = 0; i < 16; i++) { + int32_t signal = 1 << i; + ret = osThreadFlagsSet(id, signal); + } + } else { + ret = osThreadFlagsSet(id, signal); + } +} + +/** Validate if wait_signal accumulate signals and return correctly when all signals set + + Given the main thread and ticker instance with callback registered + When main thread @a flags_wait_all_for, + then main thread is blocked + when a callback calls @a osThreadFlagsSet in a loop to set 16 different signal + then the main thread is unblocked from @ flags_wait_all_for with the return of expected different signals set + */ +void test_wait_all_loop_with_ticker(void) +{ + int32_t ret; + Semaphore sem(0, 1); + Ticker t; + osThreadId_t id = ThisThread::get_id(); + t.attach_us([&] { run_release_wait_signal_set_callback(0, id); }, 4000); + ret = ThisThread::flags_wait_all_for((ALL_SIGNALS & 0xFFFF), osWaitForever, true); + TEST_ASSERT_EQUAL((ALL_SIGNALS & 0xFFFF), ret); +} + +/** Validate if setting same signal twice cause any unwanted behaviour + + Given the main thread and two ticker instance with callback registered + When main thread @a flags_wait_all_for, + then main thread is blocked + when a first callback calls @a osThreadFlagsSet set SIGNAL2 and + second callback calls @a osThreadFlagsSet set SIGNAL1 | SIGNAL2 | SIGNAL3 with SIGNAL2 set again + then the main thread is unblocked from @ flags_wait_all_for with return of expected signals set + */ +void test_set_double_with_ticker(void) +{ + int32_t ret; + Ticker t1, t2; + osThreadId_t id = ThisThread::get_id(); + t1.attach_us([&] { run_release_wait_signal_set_callback(SIGNAL2, id); }, 3000); + t2.attach_us([&] { run_release_wait_signal_set_callback(SIGNAL1 | SIGNAL2 | SIGNAL3, id); }, 4000); + + ret = ThisThread::flags_wait_all_for((SIGNAL1 | SIGNAL2 | SIGNAL3), osWaitForever, true); + TEST_ASSERT_EQUAL(SIGNAL1 | SIGNAL2 | SIGNAL3, ret); +} + +#if defined(MBED_CONF_RTOS_PRESENT) + +#define TEST_STACK_SIZE 512 +#define PROHIBITED_SIGNAL 0x80000000 +#define MAX_FLAG_POS 30 + +struct Sync { + Sync(Semaphore &parent, Semaphore &child): sem_parent(parent), sem_child(child) + {} + + Semaphore &sem_parent; + Semaphore &sem_child; +}; + +template +void run_release_signal_wait(Semaphore *sem) +{ + sem->release(); + uint32_t ret = ThisThread::flags_wait_all_for(signals, timeout); + TEST_ASSERT_EQUAL(test_val, ret); +} + +template +void run_release_wait_signal_wait(Sync *sync) +{ + sync->sem_parent.release(); + sync->sem_child.acquire(); + uint32_t ret = ThisThread::flags_wait_all_for(signals, timeout); + TEST_ASSERT_EQUAL(test_val, ret); +} + +template +void run_wait_clear(Sync *sync) +{ + sync->sem_parent.release(); + sync->sem_child.acquire(); + int32_t ret = ThisThread::flags_clear(signals); + TEST_ASSERT_EQUAL(test_val, ret); +} + +void run_loop_wait_clear(Sync *sync) +{ + int32_t signals = NO_SIGNALS; + for (int i = 0; i <= MAX_FLAG_POS; i++) { + int32_t signal = 1 << i; + signals |= signal; + sync->sem_child.acquire(); + int32_t ret = ThisThread::flags_clear(NO_SIGNALS); + TEST_ASSERT_EQUAL(signals, ret); + sync->sem_parent.release(); + } +} + +template +void run_double_wait_clear(Sync *sync) +{ + int32_t ret; + + sync->sem_parent.release(); + sync->sem_child.acquire(); + ret = ThisThread::flags_clear(signals1); + TEST_ASSERT_EQUAL(test_val1, ret); + + ret = ThisThread::flags_clear(signals2); + TEST_ASSERT_EQUAL(test_val2, ret); +} + +/** Validate that call signal_clr(NO_SIGNALS) doesn't change thread signals and return actual signals + + Given two threads A & B are started, B with all signals already set + When thread B calls @a signal_clr(NO_SIGNALS) + Then thread B @a signal_clr status should be ALL_SIGNALS indicating that thread B state is unchanged + */ +void test_clear_no_signals(void) +{ + Semaphore sem_parent(0, 1); + Semaphore sem_child(0, 1); + Sync sync(sem_parent, sem_child); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_double_wait_clear, &sync)); + sem_parent.acquire(); + t.flags_set(ALL_SIGNALS); + sem_child.release(); + t.join(); +} + +/** Validate if any signals are set on just created thread + + Given the thread is running + When thread execute @a signal_clr(NO_SIGNALS) + Then thread @a signal_clr return status should be NO_SIGNALS(0) indicating no signals set + */ +void test_init_state(void) +{ + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_clear)); + t.join(); +} + +/** Validate all signals set in one shot + + Given two threads A & B are started + When thread A call @a flags_set(ALL_SIGNALS) with all possible signals + Then thread B @a signal_clr(NO_SIGNALS) status should be ALL_SIGNALS indicating all signals set correctly + */ +void test_set_all(void) +{ + int32_t ret; + Semaphore sem_parent(0, 1); + Semaphore sem_child(0, 1); + Sync sync(sem_parent, sem_child); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_wait_clear, &sync)); + + sem_parent.acquire(); + ret = t.flags_set(ALL_SIGNALS); + TEST_ASSERT_EQUAL(ALL_SIGNALS, ret); + + sem_child.release(); + t.join(); +} + +/** Validate that call flags_set with prohibited signal doesn't change thread signals + + Given two threads A & B are started, B with all signals set + When thread A executes @a flags_set(PROHIBITED_SIGNAL) with prohibited signal + Then thread B @a signal_clr(NO_SIGNALS) status should be ALL_SIGNALS indicating that thread B signals are unchanged + + @note Each signal has up to 31 event flags 0x1, 0x2, 0x4, 0x8, ..., 0x40000000 + Most significant bit is reserved and thereby flag 0x80000000 is prohibited + */ +void test_set_prohibited(void) +{ + int32_t ret; + Semaphore sem_parent(0, 1); + Semaphore sem_child(0, 1); + Sync sync(sem_parent, sem_child); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_wait_clear, &sync)); + + sem_parent.acquire(); + t.flags_set(ALL_SIGNALS); + +#if !MBED_TRAP_ERRORS_ENABLED + ret = t.flags_set(PROHIBITED_SIGNAL); + TEST_ASSERT_EQUAL(osErrorParameter, ret); +#endif + + sem_child.release(); + t.join(); +} + +/** Validate all signals clear in one shot + + Given two threads A & B are started, B with all signals set + When thread B execute @a signal_clr(ALL_SIGNALS) with all possible signals + Then thread B @a signal_clr(NO_SIGNALS) status should be NO_SIGNALS(0) indicating all signals cleared correctly + */ +void test_clear_all(void) +{ + Semaphore sem_parent(0, 1); + Semaphore sem_child(0, 1); + Sync sync(sem_parent, sem_child); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_double_wait_clear, &sync)); + sem_parent.acquire(); + t.flags_set(ALL_SIGNALS); + sem_child.release(); + t.join(); +} + +/** Validate all signals set one by one in loop + + Given two threads A & B are started + When thread A executes @a flags_set(signal) in loop with all possible signals + */ +void test_set_all_loop(void) +{ + int32_t ret; + Semaphore sem_parent(0, 1); + Semaphore sem_child(0, 1); + Sync sync(sem_parent, sem_child); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_loop_wait_clear, &sync)); + + int32_t signals = 0; + for (int i = 0; i <= MAX_FLAG_POS; i++) { + int32_t signal = 1 << i; + + ret = t.flags_set(signal); + signals |= signal; + TEST_ASSERT_EQUAL(signals, ret); + sem_child.release(); + sem_parent.acquire(); + } + t.join(); +} + +/** Validate signal_wait return status if timeout specified + + Given the thread is running + When thread executes @a flags_wait_all_for(signals, timeout) with specified signals and timeout + Then thread @a flags_wait_all_for return should be 0 indicating no flags set + */ +template +void test_wait_timeout(void) +{ + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_signal_wait)); + t.join(); +} + +/** Validate that call of signal_wait return correctly when thread has all signals already set + + Given two threads A & B are started, B with all signals already set + When thread B executes @a signal_wait(ALL_SIGNALS, osWaitForever), + Then thread B @a flags_wait_all_for return immediately with ALL_SIGNALS indicating all wait signals was already set + */ +void test_wait_all_already_set(void) +{ + Semaphore sem_parent(0, 1); + Semaphore sem_child(0, 1); + Sync sync(sem_parent, sem_child); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_release_wait_signal_wait, &sync)); + + sem_parent.acquire(); + TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state()); + t.flags_set(ALL_SIGNALS); + sem_child.release(); + t.join(); +} + +/** Validate if signal_wait return correctly when all signals set + + Given two threads A & B are started and B waiting for a thread flag to be set + When thread A executes @a flags_set(ALL_SIGNALS) with all possible signals + Then thread B @a flags_wait_all_for return is ALL_SIGNALS indicating all wait signals was set + */ +void test_wait_all(void) +{ + Semaphore sem(0, 1); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_release_signal_wait, &sem)); + + sem.acquire(); + TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); + + t.flags_set(ALL_SIGNALS); + t.join(); +} + +/** Validate if signal_wait accumulate signals and return correctly when all signals set + + Given two threads A & B are started and B waiting for a thread signals to be set + When thread A executes @a flags_set setting all signals in loop + Then thread B @a flags_wait_all_for return is ALL_SIGNALS indicating that all wait signals was set + */ +void test_wait_all_loop(void) +{ + int32_t ret; + Semaphore sem(0, 1); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_release_signal_wait, &sem)); + + sem.acquire(); + TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); + + for (int i = 0; i < MAX_FLAG_POS; i++) { + int32_t signal = 1 << i; + ret = t.flags_set(signal); + } + ret = t.flags_set(1 << MAX_FLAG_POS); + TEST_ASSERT_EQUAL(NO_SIGNALS, ret); + t.join(); +} + +/** Validate if setting same signal twice cause any unwanted behaviour + + Given two threads A & B are started and B waiting for a thread signals to be set + When thread A executes @a flags_set twice for the same signal + Then thread A @a flags_set status is current signal set + thread B @a flags_wait_all_for return indicates that all wait signals was set + */ +void test_set_double(void) +{ + int32_t ret; + Semaphore sem(0, 1); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_release_signal_wait < SIGNAL1 | SIGNAL2 | SIGNAL3, osWaitForever, SIGNAL1 | SIGNAL2 | SIGNAL3 >, &sem)); + + sem.acquire(); + TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); + + ret = t.flags_set(SIGNAL1); + TEST_ASSERT_EQUAL(SIGNAL1, ret); + + ret = t.flags_set(SIGNAL2); + TEST_ASSERT_EQUAL(SIGNAL1 | SIGNAL2, ret); + + ret = t.flags_set(SIGNAL2); + TEST_ASSERT_EQUAL(SIGNAL1 | SIGNAL2, ret); + TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); + + ret = t.flags_set(SIGNAL3); + TEST_ASSERT_EQUAL(NO_SIGNALS, ret); + t.join(); +} +#endif + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(20, "default_auto"); + return utest::v1::verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Validate that ticker callback flags_clear(NO_SIGNALS) doesn't change main thread flags and return actual flags", test_clear_no_signals_with_ticker), + Case("Validate if any flags are set on ticker callback", test_init_state_with_ticker), + Case("Validate all flags clear in one shot using ticker callback", test_clear_all_with_ticker), + Case("Validate ticker callback flags_wait return status if timeout specified: 0[ms] no flags", test_wait_timeout_with_ticker<0, 0, 0>), + Case("Validate ticker callback flags_wait return status if timeout specified: 0[ms] all flags", test_wait_timeout_with_ticker), + Case("Validate ticker callback flags_wait return status if timeout specified: 1[ms] no flags", test_wait_timeout_with_ticker<0, 1, 0>), + Case("Validate ticker callback flags_wait return status if timeout specified: 1[ms] all flags", test_wait_timeout_with_ticker), + Case("Validate that main thread call of flags_wait_all_for return correctly when ticker callback set all flags", test_wait_all_already_set_with_ticker), + Case("Validate if setting same flag twice cause any unwanted behaviour when ticker callbacks set", test_set_double_with_ticker), + Case("Validate if main thread flags_wait_all_for accumulate flags and return correctly when all flags set by ticker callback", test_wait_all_loop_with_ticker), +#if defined(MBED_CONF_RTOS_PRESENT) + Case("Validate that call flags_clear(NO_SIGNALS) doesn't change thread flags and return actual flags", test_clear_no_signals), + Case("Validate if any flags are set on just created thread", test_init_state), + Case("Validate all flags set in one shot", test_set_all), + Case("Validate that call flags_set with prohibited flag doesn't change thread flags", test_set_prohibited), + Case("Validate all flags clear in one shot", test_clear_all), + Case("Validate all flags set one by one in loop", test_set_all_loop), + Case("Validate flags_wait return status if timeout specified: 0[ms] no flags", test_wait_timeout<0, 0, 0>), + Case("Validate flags_wait return status if timeout specified: 0[ms] all flags", test_wait_timeout), + Case("Validate flags_wait return status if timeout specified: 1[ms] no flags", test_wait_timeout<0, 1, 0>), + Case("Validate flags_wait return status if timeout specified: 1[ms] all flags", test_wait_timeout), + Case("Validate that call of flags_wait_all_for return correctly when thread has all flags already set", test_wait_all_already_set), + Case("Validate if flags_wait_all_for return correctly when all flags set", test_wait_all), + Case("Validate if flags_wait_all_for accumulate flags and return correctly when all flags set", test_wait_all_loop), + Case("Validate if setting same flag twice cause any unwanted behaviour", test_set_double), + +#endif +}; + +utest::v1::Specification specification(test_setup, cases); + +int main() +{ + return !utest::v1::Harness::run(specification); +} + +#endif // !DEVICE_USTICKER diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp new file mode 100644 index 0000000..6999f01 --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp @@ -0,0 +1,376 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "ticker_api.h" + +#include "platform/source/SysTimer.h" + +using namespace std::chrono; + +#define TEST_TICKS 42 +#define TEST_TICK_PERIOD std::ratio::type +#define TEST_TICK_DURATION duration +#define DELAY TEST_TICK_DURATION(1) +#define DELAY_DELTA 2500us + +#define TEST_ASSERT_EQUAL_DURATION(expected, actual) \ + do { \ + using ct = std::common_type_t; \ + TEST_ASSERT_EQUAL(ct(expected).count(), ct(actual).count()); \ + } while (0) + +#define TEST_ASSERT_EQUAL_TIME_POINT(expected, actual) \ + do { \ + using ct = std::common_type_t; \ + TEST_ASSERT_EQUAL(ct(expected).time_since_epoch().count(), ct(actual).time_since_epoch().count()); \ + } while (0) + +#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ + do { \ + using ct = std::common_type_t; \ + TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ + } while (0) + + +/* Use a specific delta value for deep sleep, as entry/exit adds up extra latency. + * Use deep sleep latency if defined and add 1ms extra delta */ +#if defined MBED_CONF_TARGET_DEEP_SLEEP_LATENCY +#define DEEP_SLEEP_DELAY_DELTA milliseconds(MBED_CONF_TARGET_DEEP_SLEEP_LATENCY + 1) +#else +#define DEEP_SLEEP_DELAY_DELTA 2500us +#endif + +using namespace utest::v1; +using mbed::internal::SysTimer; + +// The SysTick interrupt must not be set as pending by the test code. +template +class SysTimerTest: public SysTimer { +private: + Semaphore _sem; + virtual void handler() + { + _sem.release(); + SysTimer::handler(); + } + +public: + SysTimerTest() : + SysTimer(), _sem(0, 1) + { + } + + SysTimerTest(const ticker_data_t *data) : + SysTimer(data), _sem(0, 1) + { + } + + virtual ~SysTimerTest() + { + } + + bool sem_try_acquire(rtos::Kernel::Clock::duration_u32 millisec) + { + return _sem.try_acquire_for(millisec); + } + + void sem_acquire() + { + _sem.acquire(); + } +}; + +duration mock_ticker_timestamp; + +void mock_ticker_init() +{ +} + +uint32_t mock_ticker_read() +{ + return mock_ticker_timestamp.count(); +} + +void mock_ticker_disable_interrupt() +{ +} + +void mock_ticker_clear_interrupt() +{ +} + +void mock_ticker_set_interrupt(timestamp_t timestamp) +{ +} + +void mock_ticker_fire_interrupt() +{ +} + +void mock_ticker_free() +{ +} + +const ticker_info_t *mock_ticker_get_info() +{ + static const ticker_info_t mock_ticker_info = { + .frequency = 1000000, + .bits = 32 + }; + return &mock_ticker_info; +} + +ticker_interface_t mock_ticker_interface = { + .init = mock_ticker_init, + .read = mock_ticker_read, + .disable_interrupt = mock_ticker_disable_interrupt, + .clear_interrupt = mock_ticker_clear_interrupt, + .set_interrupt = mock_ticker_set_interrupt, + .fire_interrupt = mock_ticker_fire_interrupt, + .free = mock_ticker_free, + .get_info = mock_ticker_get_info, +}; + +ticker_event_queue_t mock_ticker_event_queue; + +const ticker_data_t mock_ticker_data = { + .interface = &mock_ticker_interface, + .queue = &mock_ticker_event_queue +}; + +void mock_ticker_reset() +{ + mock_ticker_timestamp = 0s; + memset(&mock_ticker_event_queue, 0, sizeof mock_ticker_event_queue); +} + +/** Test tick count is zero upon creation + * + * Given a SysTimer + * When the timer is created + * Then tick count is zero + */ +void test_created_with_zero_tick_count(void) +{ + SysTimerTest st; + using time_point = decltype(st)::time_point; + TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(0)), st.get_tick()); +} + +/** Test tick count is updated correctly + * + * Given a SysTimer + * When the @a suspend and @a resume methods are called immediately after creation + * Then the tick count is not updated + * When @a suspend and @a resume methods are called again after a delay + * Then the tick count is updated + * and the number of ticks incremented is equal TEST_TICKS + * When @a suspend and @a resume methods are called again without a delay + * Then the tick count is not updated + */ +void test_update_tick(void) +{ + mock_ticker_reset(); + SysTimerTest st(&mock_ticker_data); + using time_point = decltype(st)::time_point; + st.set_wake_time(st.get_tick() + TEST_TICK_DURATION(2)); + st.cancel_wake(); + TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(0)), st.get_tick()); + + st.set_wake_time(st.get_tick() + TEST_TICK_DURATION(2)); + mock_ticker_timestamp = TEST_TICK_DURATION(1); + st.cancel_wake(); + TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(1)), st.update_and_get_tick()); + TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(1)), st.get_tick()); + + st.set_wake_time(st.get_tick() + TEST_TICK_DURATION(2)); + st.cancel_wake(); + TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(1)), st.get_tick()); +} + +/** Test get_time returns correct time + * + * Given a SysTimer + * When @a get_time method is called before and after a delay + * Then time difference is equal the delay + */ +void test_get_time(void) +{ + mock_ticker_reset(); + SysTimerTest st(&mock_ticker_data); + auto t1 = st.get_time(); + + mock_ticker_timestamp = TEST_TICK_DURATION(1); + auto t2 = st.get_time(); + TEST_ASSERT_EQUAL_DURATION(TEST_TICK_DURATION(1), t2 - t1); +} + +/** Test cancel_tick + * + * Given a SysTimer with a scheduled tick + * When @a cancel_tick is called before the given number of ticks elapse + * Then the handler is never called + * and the tick count is not incremented + */ +void test_cancel_tick(void) +{ + SysTimerTest st; + using time_point = decltype(st)::time_point; + st.cancel_tick(); + st.start_tick(); + + st.cancel_tick(); + bool acquired = st.sem_try_acquire(duration_cast(TEST_TICK_DURATION(1) + DELAY_DELTA)); + TEST_ASSERT_FALSE(acquired); + TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(0)), st.get_tick()); +} + +/** Test handler called twice + * + * Given a SysTimer with a tick scheduled with delta = TEST_TICKS + * When the handler is called + * Then the tick count is incremented by 1 + * and elapsed time is equal 1000000ULL * TEST_TICKS / OS_TICK_FREQ; + * When more time elapses + * Repeat a second time. + */ +void test_handler_called_twice(void) +{ + SysTimerTest st; + using time_point = decltype(st)::time_point; + auto t1 = st.get_time(); + bool acquired = st.sem_try_acquire(0s); + TEST_ASSERT_FALSE(acquired); + + st.start_tick(); + // Wait in a busy loop to prevent entering sleep or deepsleep modes. + do { + acquired = st.sem_try_acquire(0s); + } while (!acquired); + auto t2 = st.get_time(); + TEST_ASSERT_TRUE(acquired); + TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(1)), st.get_tick()); + TEST_ASSERT_DURATION_WITHIN(DELAY_DELTA, TEST_TICK_DURATION(1), t2 - t1); + + // Wait in a busy loop to prevent entering sleep or deepsleep modes. + do { + acquired = st.sem_try_acquire(0s); + } while (!acquired); + t2 = st.get_time(); + TEST_ASSERT_TRUE(acquired); + TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(2)), st.get_tick()); + TEST_ASSERT_DURATION_WITHIN(DELAY_DELTA, TEST_TICK_DURATION(2), t2 - t1); + st.cancel_tick(); +} + +#if DEVICE_SLEEP +/** Test wake up from sleep + * + * Given a SysTimer with a tick scheduled in the future + * and a core in sleep mode + * When given time elapses + * Then the uC is woken up from sleep + * and the tick handler is called + * and measured time matches requested delay + */ +void test_sleep(void) +{ + Timer timer; + SysTimerTest st; + + sleep_manager_lock_deep_sleep(); + timer.start(); + st.start_tick(); + + TEST_ASSERT_FALSE_MESSAGE(sleep_manager_can_deep_sleep(), "Deep sleep should be disallowed"); + st.sem_acquire(); + + timer.stop(); + st.cancel_tick(); + sleep_manager_unlock_deep_sleep(); + + TEST_ASSERT_DURATION_WITHIN(DELAY_DELTA, TEST_TICK_DURATION(1), timer.elapsed_time()); +} + +#if DEVICE_LPTICKER && !MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER +/** Test wake up from deepsleep + * + * Given a SysTimer with a tick scheduled in the future + * and a core in deepsleep mode + * When given time elapses + * Then the uC is woken up from deepsleep + * and the tick handler is called + * and measured time matches requested delay + */ +void test_deepsleep(void) +{ + /* + * Since deepsleep() may shut down the UART peripheral, we wait for 10ms + * to allow for hardware serial buffers to completely flush. + + * This should be replaced with a better function that checks if the + * hardware buffers are empty. However, such an API does not exist now, + * so we'll use the ThisThread::sleep_for() function for now. + */ + ThisThread::sleep_for(10ms); + // Regular Timer might be disabled during deepsleep. + LowPowerTimer lptimer; + SysTimerTest st; + + lptimer.start(); + st.start_tick(); + TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep_test_check(), "Deep sleep should be allowed"); + st.sem_acquire(); + lptimer.stop(); + st.cancel_tick(); + + TEST_ASSERT_DURATION_WITHIN(DEEP_SLEEP_DELAY_DELTA, TEST_TICK_DURATION(1), lptimer.elapsed_time()); +} +#endif +#endif + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(15, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Tick count is zero upon creation", test_created_with_zero_tick_count), + Case("Tick count is updated correctly", test_update_tick), + Case("Time is updated correctly", test_get_time), + Case("Tick can be cancelled", test_cancel_tick), + Case("Handler called twice", test_handler_called_twice), +#if DEVICE_SLEEP + Case("Wake up from sleep", test_sleep), +#if DEVICE_LPTICKER && !MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER + Case("Wake up from deep sleep", test_deepsleep), +#endif +#endif + +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/threads/SynchronizedIntegral.h b/rtos/tests/TESTS/mbedmicro-rtos-mbed/threads/SynchronizedIntegral.h new file mode 100644 index 0000000..148f189 --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/threads/SynchronizedIntegral.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2013-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDMICRO_RTOS_MBED_THREADS_SYNCHRONIZED_INTEGRAL +#define MBEDMICRO_RTOS_MBED_THREADS_SYNCHRONIZED_INTEGRAL + +#include +#include + +using mstd::lock_guard; + +/** + * Thread safe wrapper for integral types. + * @tparam T type of the integral + */ +template +class SynchronizedIntegral { +public: + SynchronizedIntegral(T value) : _mutex(), _value(value) { } + + // preincrement operator + T operator++() + { + lock_guard lock(_mutex); + return ++_value; + } + + // predecrement operator + T operator--() + { + lock_guard lock(_mutex); + return --_value; + } + + // post increment operator + T operator++(int) + { + lock_guard lock(_mutex); + return _value++; + } + + // post decrement operator + T operator--(int) + { + lock_guard lock(_mutex); + return _value--; + } + + // conversion operator, used also for <,>,<=,>=,== and != + operator T() const + { + lock_guard lock(_mutex); + return _value; + } + + // access to the internal mutex + rtos::Mutex &internal_mutex() + { + return _mutex; + } + +private: + mutable rtos::Mutex _mutex; + T _value; +}; +#endif /* MBEDMICRO_RTOS_MBED_THREADS_SYNCHRONIZED_INTEGRAL */ diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/threads/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/threads/main.cpp new file mode 100644 index 0000000..6cd9f3b --- /dev/null +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/threads/main.cpp @@ -0,0 +1,863 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) +#error [NOT_SUPPORTED] Threads test cases require RTOS with multithread to run +#else + +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] UsTicker need to be enabled for this test. +#else + +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "rtos.h" +#include "SynchronizedIntegral.h" +#include +#include + +#define THREAD_STACK_SIZE 512 +#if defined(__CORTEX_A9) || defined(__CORTEX_M23) || defined(__CORTEX_M33) || defined(TARGET_ARM_FM) || defined(TARGET_CY8CKIT_062_WIFI_BT_PSA) +#define PARALLEL_THREAD_STACK_SIZE 512 +#define CHILD_THREAD_STACK_SIZE 512 +#else +#define PARALLEL_THREAD_STACK_SIZE 384 +#define CHILD_THREAD_STACK_SIZE 384 +#endif + +#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \ + do { \ + using ct = std::common_type_t; \ + TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \ + } while (0) + + +using namespace utest::v1; +using mstd::lock_guard; + +// The counter type used accross all the tests +// It is internall ysynchronized so read +typedef SynchronizedIntegral counter_t; + +template +class ParallelThread : public Thread { +public: + ParallelThread() : Thread(P, S) { } +}; + +// Tasks with different functions to test on threads +void increment(counter_t *counter) +{ + (*counter)++; +} + +void increment_with_yield(counter_t *counter) +{ + ThisThread::yield(); + (*counter)++; +} + +void increment_with_wait(counter_t *counter) +{ + ThisThread::sleep_for(100ms); + (*counter)++; +} + +void increment_with_child(counter_t *counter) +{ + Thread *child = new (std::nothrow) Thread(osPriorityNormal, CHILD_THREAD_STACK_SIZE); + char *dummy = new (std::nothrow) char[CHILD_THREAD_STACK_SIZE]; + delete[] dummy; + + // Don't fail test due to lack of memory. Call function directly instead + if (!child || !dummy) { + increment(counter); + delete child; + return; + } + child->start(callback(increment, counter)); + child->join(); + delete child; +} + +void increment_with_murder(counter_t *counter) +{ + { + // take ownership of the counter mutex so it prevent the child to + // modify counter. + lock_guard lock(counter->internal_mutex()); + Thread *child = new (std::nothrow) Thread(osPriorityNormal, CHILD_THREAD_STACK_SIZE); + char *dummy = new (std::nothrow) char[CHILD_THREAD_STACK_SIZE]; + delete[] dummy; + + // Don't fail test due to lack of memory. + if (!child || !dummy) { + delete child; + goto end; + } + child->start(callback(increment, counter)); + child->terminate(); + delete child; + } + +end: + (*counter)++; +} + +void self_terminate(Thread *self) +{ + self->terminate(); + // Code should not get here + TEST_ASSERT(0); +} + +// Tests that spawn tasks in different configurations + +/** Template for tests: single thread, with yield, with wait, with child, with murder + + Testing single thread + Given single thread is started + when the thread increments the counter + then the final value of the counter is equal to 1 + + Testing single thread with yield + Given single thread is started + when the thread yields and then increments the counter + then the final value of the counter is equal to 1 + + Testing single thread with wait + Given single thread is started + when the thread waits for 100ms and then increments the counter + then the final value of the counter is equal to 1 + + Testing single thread with child + Given single thread is started + when the thread spawns another thread that increments the counter + then the final value of the counter is equal to 1 + + Testing serial threads with murder + Given single thread is started + when the parent thread is holding a lock + and the parent thread spawns a child thread that waits for the lock before incrementing the counter + and the parent terminates the child before releasing the lock + and the parent increments the counter + then the final value of the counter is equal to 1 +*/ +template +void test_single_thread() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + counter_t counter(0); + Thread thread(osPriorityNormal, THREAD_STACK_SIZE); + thread.start(callback(F, &counter)); + thread.join(); + TEST_ASSERT_EQUAL(counter, 1); +} + +/** Template for tests: parallel threads, with yield, with wait, with child, with murder + + Testing parallel threads + Given multiple threads are started in parallel + when each of the threads increments the counter + then the final value of the counter is equal to number of threads + + Testing parallel threads with yield + Given multiple threads are started in parallel + when each of the threads yields and then increments the counter + then the final value of the counter is equal to number of threads + + Testing parallel threads with wait + Given multiple threads are started in parallel + when each of the threads waits for 100ms and then increments the counter + then the final value of the counter is equal to number of threads + + Testing parallel threads with child + Given multiple threads are started in parallel + when each of the threads spawns another thread that increments the counter + then the final value of the counter is equal to number of parallel threads + + Testing parallel threads with murder + Given multiple threads are started in parallel + when the parent thread is holding a lock + and the parent thread spawns a child thread that waits for the lock before incrementing the counter + and the parent terminates the child before releasing the lock + and the parent increments the counter + then the final value of the counter is equal to number of parallel threads +*/ +template +void test_parallel_threads() +{ + char *dummy = new (std::nothrow) char[PARALLEL_THREAD_STACK_SIZE * N]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + counter_t counter(0); + ParallelThread threads[N]; + + for (int i = 0; i < N; i++) { + threads[i].start(callback(F, &counter)); + } + + for (int i = 0; i < N; i++) { + threads[i].join(); + } + + TEST_ASSERT_EQUAL(counter, N); +} + +/** Template for tests: serial threads, with yield, with wait, with child, with murder + + Testing serial threads + Given multiple threads are started serially + when each of the threads increments the counter + then the final value of the counter is equal to number of threads + + Testing serial threads with yield + Given multiple threads are started serially + when each of the threads yields and then increments the counter + then the final value of the counter is equal to number of threads + + Testing serial threads with wait + Given multiple threads are started serially + when each of the threads waits for 100ms and then increments the counter + then the final value of the counter is equal to number of threads + + Testing serial threads with child + Given multiple threads are started serially + when each of the threads spawns another thread that increments the counter + then the final value of the counter is equal to number of serial threads + + Testing serial threads with murder + Given multiple threads are started serially + when the parent thread is holding a lock + and the parent thread spawns a child thread that waits for the lock before incrementing the counter + and the parent terminates the child before releasing the lock + and the parent increments the counter + then the final value of the counter is equal to number of serial threads +*/ +template +void test_serial_threads() +{ + counter_t counter(0); + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + for (int i = 0; i < N; i++) { + Thread thread(osPriorityNormal, THREAD_STACK_SIZE); + thread.start(callback(F, &counter)); + thread.join(); + } + + TEST_ASSERT_EQUAL(counter, N); +} + +/** Testing thread self terminate + + Given the thread is running + when the thread calls @a terminate on its self + then the thread terminates execution cleanly + */ +void test_self_terminate() +{ + Thread *thread = new (std::nothrow) Thread(osPriorityNormal, THREAD_STACK_SIZE); + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + + // Don't fail test due to lack of memory. + if (!thread || !dummy) { + goto end; + } + thread->start(callback(self_terminate, thread)); + thread->join(); + +end: + delete thread; +} + +void flags_wait() +{ + uint32_t flags = ThisThread::flags_wait_all(0x1); + TEST_ASSERT_EQUAL(0x1, flags); +} + +void flags_wait_tout() +{ + uint32_t flags = ThisThread::flags_wait_all_for(0x2, 50ms); + TEST_ASSERT_EQUAL(0x1, flags); +} + +void flags_wait_multibit() +{ + uint32_t flags = ThisThread::flags_wait_all(0x1 | 0x2); + TEST_ASSERT_EQUAL(0x3, flags); +} + +void flags_wait_multibit_any() +{ + uint32_t flags = ThisThread::flags_wait_any(0x1 | 0x2); + TEST_ASSERT_NOT_EQUAL(0x0, flags); +} + +void flags_wait_multibit_tout() +{ + uint32_t flags = ThisThread::flags_wait_all_for(0x1 | 0x2, 50ms); + TEST_ASSERT_NOT_EQUAL(0x3, flags); +} + +/** + Testing thread flags: wait + Given two threads (A & B) are started + when thread A executes @a flags_wait_all(0x1) + and thread B execute @a flags_set(0x1) + then thread A exits the wait and continues execution + + Testing thread flags: timeout + Given two threads (A & B) are started + when thread A executes @a flags_wait_all_for(0x1 | 0x2, 50) with a timeout of 50ms + and thread B execute @a flags_set(0x2) + then thread A keeps waiting for correct flags until it timeouts + + Testing thread flags: multi-bit + Given two threads (A & B) are started + when thread A executes @a flags_wait_all(0x1 | 0x2) + and thread B execute @a flags_set(0x1 | 0x2) + then thread A exits the wait and continues execution + + Testing thread flags: multi-bit any + Given two threads (A & B) are started + when thread A executes @a flags_wait_any(0x1 | 0x2) + and thread B execute @a flags_set(0x1) + then thread A exits the wait and continues execution + + Testing thread flags: multi-bit timeout + Given two threads (A & B) are started + when thread A executes @a flags_wait_all_for(0x1, 50) with a timeout of 50ms + and thread B execute @a flags_set(0x2) + then thread A keeps waiting for correct flags until it timeouts +*/ +template +void test_thread_flags_set() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + Thread t_wait(osPriorityNormal, THREAD_STACK_SIZE); + + t_wait.start(callback(F)); + + ThisThread::yield(); + + Thread::State state = t_wait.get_state(); + TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, state); + + int32_t res = t_wait.flags_set(S); + + t_wait.join(); +} + +void flags_clear() +{ + ThisThread::yield(); + + int32_t sig = ThisThread::flags_clear(0x1); + TEST_ASSERT_EQUAL(0x1, sig); + + /* Flags cleared we should get timeout */ + uint32_t flags = ThisThread::flags_wait_all_for(0x1, 0s); + TEST_ASSERT_EQUAL(0, flags); +} + +/** Testing thread flags: flags clear + + Given two threads (A & B) are started + when thread A executes @a flags_set(0x1) + and thread B execute @a flags_clear(0x1) + and thread B execute @a flags_wait_all_for(0x1, 0) + then thread B @a flags_wait_all_for return should be 0 indicating no flags set + */ +void test_thread_flags_clear() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + Thread t_wait(osPriorityNormal, THREAD_STACK_SIZE); + + t_wait.start(callback(flags_clear)); + + int32_t res = t_wait.flags_set(0x1); + TEST_ASSERT_EQUAL(0x1, res); + + t_wait.join(); +} + +void thread_wait_flags() +{ + ThisThread::flags_wait_all(0x1); +} + +void stack_info() +{ + ThisThread::flags_wait_all(0x1); + + thread_wait_flags(); + + ThisThread::flags_wait_all(0x1); +} + +/** Testing thread stack info + + Given the thread is running + when a function is called from the thread context + then the stack usage goes up + and the reported stack size is as requested in the constructor + and the sum of free and used stack sizes is equal to the total stack size + when the function returns + then the stack usage goes down + and the reported stack size is as requested in the constructor + and the sum of free and used stack sizes is equal to the total stack size + */ +void test_thread_stack_info() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + t.start(callback(stack_info)); + + ThisThread::yield(); + + TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.stack_size()); + TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.free_stack() + t.used_stack()); + uint32_t last_stack = t.used_stack(); + + t.flags_set(0x1); + ThisThread::yield(); + + TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.free_stack() + t.used_stack()); + TEST_ASSERT(last_stack <= t.used_stack()); + last_stack = t.used_stack(); + + t.flags_set(0x1); + ThisThread::yield(); + + TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.free_stack() + t.used_stack()); + TEST_ASSERT(last_stack >= t.used_stack()); + + t.flags_set(0x1); + + t.join(); +} + +/** Testing thread wait aka delay + + Given the thread is running + when the @a wait function is called + then the thread sleeps for given amount of time + */ +void test_thread_wait() +{ + Timer timer; + timer.start(); + + ThisThread::sleep_for(150ms); + + TEST_ASSERT_DURATION_WITHIN(50ms, 150ms, timer.elapsed_time()); +} + +/** Testing thread name + + Given a thread is started with a specified name + when the name is queried using @a get_name + then the returned name is as set +*/ +void test_thread_name() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + const char tname[] = "Amazing thread"; + Thread t(osPriorityNormal, THREAD_STACK_SIZE, NULL, tname); + TEST_ASSERT_EQUAL(strcmp(tname, t.get_name()), 0); + t.start([&] { TEST_ASSERT_EQUAL(strcmp(tname, ThisThread::get_name()), 0); }); + t.join(); +} + +void test_deleted_thread() +{ +} + +/** Testing thread states: deleted + + Given the thread is not started + then its state, as reported by @a get_state, is @a Deleted + when the thread is started and finishes executing + then its state, as reported by @a get_state, is @a Deleted + */ +void test_deleted() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + + TEST_ASSERT_EQUAL(Thread::Deleted, t.get_state()); + + t.start(callback(test_deleted_thread)); + + t.join(); + TEST_ASSERT_EQUAL(Thread::Deleted, t.get_state()); +} + +void test_delay_thread() +{ + ThisThread::sleep_for(50ms); +} + +/** Testing thread states: wait delay + + Given the thread is running + when thread calls @a wait + then its state, as reported by @a get_state, is @a WaitingDelay + */ +void test_delay() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + + t.start(callback(test_delay_thread)); + + ThisThread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingDelay, t.get_state()); + + t.join(); + TEST_ASSERT_EQUAL(Thread::Deleted, t.get_state()); +} + +void test_thread_flags_thread() +{ + ThisThread::flags_wait_all(0x1); +} + +/** Testing thread states: wait flags + + Given the thread is running + when thread waits for flags + then its state, as reported by @a get_state, is @a WaitingThreadFlag + */ +void test_thread_flags() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + + t.start(callback(test_thread_flags_thread)); + + ThisThread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); + + t.flags_set(0x1); +} + +void test_evt_flag_thread(EventFlags *evtflg) +{ + evtflg->wait_any(0x1); +} + +/** Testing thread states: wait evt flag + + Given the thread is running + when thread waits for an event flag + then its state, as reported by @a get_state, is @a WaitingEventFlag + */ +void test_evt_flag() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + EventFlags evtflg; + + t.start(callback(test_evt_flag_thread, &evtflg)); + + ThisThread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingEventFlag, t.get_state()); + + evtflg.set(0x1); +} + +void test_mutex_thread(Mutex *mutex) +{ + mutex->lock(); +} + +/** Testing thread states: wait mutex + + Given the thread is running + when thread waits for a mutex + then its state, as reported by @a get_state, is @a WaitingMutex + */ +void test_mutex() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + Mutex mutex; + + mutex.lock(); + + t.start(callback(test_mutex_thread, &mutex)); + + ThisThread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingMutex, t.get_state()); + + mutex.unlock(); +} + +void test_semaphore_thread(Semaphore *sem) +{ + sem->acquire(); +} + +/** Testing thread states: wait semaphore + + Given the thread is running + when thread waits for a semaphore + then its state, as reported by @a get_state, is @a WaitingSemaphore + */ +void test_semaphore() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + Semaphore sem; + + t.start(callback(test_semaphore_thread, &sem)); + + ThisThread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state()); + + sem.release(); +} + +void test_msg_get_thread(Queue *queue) +{ + queue->get(); +} + +/** Testing thread states: wait message get + + Given the thread is running + when thread tries to get a message from an empty queue + then its state, as reported by @a get_state, is @a WaitingMessageGet + */ +void test_msg_get() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + Queue queue; + + t.start(callback(test_msg_get_thread, &queue)); + + ThisThread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingMessageGet, t.get_state()); + + queue.try_put((int32_t *)0xE1EE7); +} + +void test_msg_put_thread(Queue *queue) +{ + queue->try_put_for(Kernel::wait_for_u32_forever, (int32_t *)0xDEADBEEF); + +} + +/** Testing thread states: wait message put + + Given the thread is running + when thread tries to put a message into a full queue + then its state, as reported by @a get_state, is @a WaitingMessagePut + */ +void test_msg_put() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + Queue queue; + + queue.try_put((int32_t *)0xE1EE7); + + t.start(callback(test_msg_put_thread, &queue)); + + ThisThread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingMessagePut, t.get_state()); + queue.get(); +} + +/** Utility function that places some date on the stack */ +void use_some_stack() +{ + volatile uint32_t stack_filler[10] = {0xDEADBEEF}; +} + +/** Testing thread with external stack memory + + Given external buffer is supplied as stack to a thread + when the thread executes + then the supplies buffer is used as a stack + */ +void test_thread_ext_stack() +{ + char stack[512]; + Thread t(osPriorityNormal, sizeof(stack), (unsigned char *)stack); + + memset(&stack, 0, sizeof(stack)); + t.start(callback(use_some_stack)); + t.join(); + + /* If buffer was used as a stack it was cleared with pattern and some data were placed in it */ + for (unsigned i = 0; i < sizeof(stack); i++) { + if (stack[i] != 0) { + return; + } + } + + TEST_FAIL_MESSAGE("External stack was not used."); +} + +/** Testing thread priority operations + + Given thread running with osPriorityNormal + when new priority is set using @a set_priority + then priority is changed and can be retrieved using @a get_priority + */ +void test_thread_prio() +{ + char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE]; + delete[] dummy; + TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test"); + + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + t.start(callback(thread_wait_flags)); + + TEST_ASSERT_EQUAL(osPriorityNormal, t.get_priority()); + + t.set_priority(osPriorityHigh); + + TEST_ASSERT_EQUAL(osPriorityHigh, t.get_priority()); + + t.flags_set(0x1); + t.join(); +} + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(25, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +#define DEFAULT_HANDLERS NULL,NULL,greentea_case_setup_handler,greentea_case_teardown_handler,greentea_case_failure_abort_handler + +// Test cases. It's spelled out rather than constructed with macro because +// macros don't play nicely with the templates (extra comma). +static const case_t cases[] = { + {"Testing single thread", test_single_thread, DEFAULT_HANDLERS}, + {"Testing parallel threads", test_parallel_threads<3, increment>, DEFAULT_HANDLERS}, + {"Testing serial threads", test_serial_threads<10, increment>, DEFAULT_HANDLERS}, + + {"Testing single thread with yield", test_single_thread, DEFAULT_HANDLERS}, + {"Testing parallel threads with yield", test_parallel_threads<3, increment_with_yield>, DEFAULT_HANDLERS}, + {"Testing serial threads with yield", test_serial_threads<10, increment_with_yield>, DEFAULT_HANDLERS}, + + {"Testing single thread with wait", test_single_thread, DEFAULT_HANDLERS}, + {"Testing parallel threads with wait", test_parallel_threads<3, increment_with_wait>, DEFAULT_HANDLERS}, + {"Testing serial threads with wait", test_serial_threads<10, increment_with_wait>, DEFAULT_HANDLERS}, + + {"Testing single thread with child", test_single_thread, DEFAULT_HANDLERS}, + {"Testing parallel threads with child", test_parallel_threads<2, increment_with_child>, DEFAULT_HANDLERS}, + {"Testing serial threads with child", test_serial_threads<10, increment_with_child>, DEFAULT_HANDLERS}, + + {"Testing single thread with murder", test_single_thread, DEFAULT_HANDLERS}, + {"Testing parallel threads with murder", test_parallel_threads<2, increment_with_murder>, DEFAULT_HANDLERS}, + {"Testing serial threads with murder", test_serial_threads<10, increment_with_murder>, DEFAULT_HANDLERS}, + + {"Testing thread self terminate", test_self_terminate, DEFAULT_HANDLERS}, + + {"Testing thread flags: wait", test_thread_flags_set<0x1, flags_wait>, DEFAULT_HANDLERS}, + {"Testing thread flags: timeout", test_thread_flags_set<0x1, flags_wait_tout>, DEFAULT_HANDLERS}, + {"Testing thread flags: multi-bit all", test_thread_flags_set<0x3, flags_wait_multibit>, DEFAULT_HANDLERS}, + {"Testing thread flags: multi-bit all timeout", test_thread_flags_set<0x1, flags_wait_multibit_tout>, DEFAULT_HANDLERS}, + {"Testing thread flags: multi-bit any", test_thread_flags_set<0x1, flags_wait_multibit_any>, DEFAULT_HANDLERS}, + {"Testing thread flags: flags clear", test_thread_flags_clear, DEFAULT_HANDLERS}, + + {"Testing thread stack info", test_thread_stack_info, DEFAULT_HANDLERS}, + {"Testing thread wait", test_thread_wait, DEFAULT_HANDLERS}, + {"Testing thread name", test_thread_name, DEFAULT_HANDLERS}, + + {"Testing thread states: deleted", test_deleted, DEFAULT_HANDLERS}, + {"Testing thread states: wait delay", test_delay, DEFAULT_HANDLERS}, + {"Testing thread states: wait thread flags", test_thread_flags, DEFAULT_HANDLERS}, + {"Testing thread states: wait event flag", test_evt_flag, DEFAULT_HANDLERS}, + {"Testing thread states: wait mutex", test_mutex, DEFAULT_HANDLERS}, + {"Testing thread states: wait semaphore", test_semaphore, DEFAULT_HANDLERS}, + {"Testing thread states: wait message get", test_msg_get, DEFAULT_HANDLERS}, + {"Testing thread states: wait message put", test_msg_put, DEFAULT_HANDLERS}, + + {"Testing thread with external stack memory", test_thread_ext_stack, DEFAULT_HANDLERS}, + {"Testing thread priority ops", test_thread_prio, DEFAULT_HANDLERS} + +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + +#endif // !DEVICE_USTICKER +#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)