diff --git a/core/mbed-rtos/TESTS/mbed-rtos/threads/main.cpp b/core/mbed-rtos/TESTS/mbed-rtos/threads/main.cpp new file mode 100644 index 0000000000..030beec2da --- /dev/null +++ b/core/mbed-rtos/TESTS/mbed-rtos/threads/main.cpp @@ -0,0 +1,110 @@ +#include "mbed.h" +#include "test_env.h" +#include "unity.h" +#include "utest.h" +#include "rtos.h" + + +using namespace utest::v1; + + +// Tasks with different functions to test on threads +void increment(const void *var) { + (*(int *)var)++; +} + +void increment_with_yield(const void *var) { + Thread::yield(); + (*(int *)var)++; +} + +void increment_with_wait(const void *var) { + Thread::wait(100); + (*(int *)var)++; +} + +void increment_with_child(const void *var) { + Thread child(increment, (void*)var); + child.join(); +} + +void increment_with_murder(const void *var) { + Thread child(increment_with_wait, (void*)var); + // Kill child before it can increment var + child.terminate(); + (*(int *)var)++; +} + + +// Tests that spawn tasks in different configurations +template +void test_single_thread() { + int var = 0; + Thread thread(F, &var); + thread.join(); + TEST_ASSERT_EQUAL(var, 1); +} + +template +void test_parallel_threads() { + int var = 0; + Thread *threads[N]; + + for (int i = 0; i < N; i++) { + threads[i] = new Thread(F, &var); + } + + for (int i = 0; i < N; i++) { + threads[i]->join(); + delete threads[i]; + } + + TEST_ASSERT_EQUAL(var, N); +} + +template +void test_serial_threads() { + int var = 0; + + for (int i = 0; i < N; i++) { + Thread thread(F, &var); + thread.join(); + } + + TEST_ASSERT_EQUAL(var, N); +} + + +status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(40, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +// Test cases +Case cases[] = { + Case("Testing single thread", test_single_thread), + Case("Testing parallel threads", test_parallel_threads<3, increment>), + Case("Testing serial threads", test_serial_threads<10, increment>), + + Case("Testing single thread with yield", test_single_thread), + Case("Testing parallel threads with yield", test_parallel_threads<3, increment_with_yield>), + Case("Testing serial threads with yield", test_serial_threads<10, increment_with_yield>), + + Case("Testing single thread with wait", test_single_thread), + Case("Testing parallel threads with wait", test_parallel_threads<3, increment_with_wait>), + Case("Testing serial threads with wait", test_serial_threads<10, increment_with_wait>), + + Case("Testing single thread with child", test_single_thread), + Case("Testing parallel threads with child", test_parallel_threads<3, increment_with_child>), + Case("Testing serial threads with child", test_serial_threads<10, increment_with_child>), + + Case("Testing single thread with murder", test_single_thread), + Case("Testing parallel threads with murder", test_parallel_threads<3, increment_with_murder>), + Case("Testing serial threads with murder", test_serial_threads<10, increment_with_murder>), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} diff --git a/core/mbed-rtos/rtos/Thread.cpp b/core/mbed-rtos/rtos/Thread.cpp index ed8270a36a..402295e8d7 100644 --- a/core/mbed-rtos/rtos/Thread.cpp +++ b/core/mbed-rtos/rtos/Thread.cpp @@ -26,34 +26,82 @@ namespace rtos { -Thread::Thread(void (*task)(void const *argument), void *argument, - osPriority priority, uint32_t stack_size, unsigned char *stack_pointer) { +Thread::Thread(osPriority priority, + uint32_t stack_size, unsigned char *stack_pointer): + _tid(NULL), _dynamic_stack(stack_pointer == NULL) { #ifdef __MBED_CMSIS_RTOS_CM - _thread_def.pthread = task; _thread_def.tpriority = priority; _thread_def.stacksize = stack_size; - if (stack_pointer != NULL) { - _thread_def.stack_pointer = (uint32_t*)stack_pointer; - _dynamic_stack = false; - } else { - _thread_def.stack_pointer = new uint32_t[stack_size/sizeof(uint32_t)]; - if (_thread_def.stack_pointer == NULL) + _thread_def.stack_pointer = (uint32_t*)stack_pointer; +#endif +} + +Thread::Thread(void (*task)(void const *argument), void *argument, + osPriority priority, uint32_t stack_size, unsigned char *stack_pointer): + _tid(NULL), _dynamic_stack(stack_pointer == NULL) { +#ifdef __MBED_CMSIS_RTOS_CM + _thread_def.tpriority = priority; + _thread_def.stacksize = stack_size; + _thread_def.stack_pointer = (uint32_t*)stack_pointer; +#endif + switch(start(task, argument)) { + case osErrorResource: + error("OS ran out of threads!\n"); + break; + case osErrorParameter: + error("Thread already running!\n"); + break; + case osErrorNoMemory: error("Error allocating the stack memory\n"); - _dynamic_stack = true; + default: + break; } - +} + +osStatus Thread::start(void (*task)(void const *argument), void *argument) { + if (_tid != NULL) { + return osErrorParameter; + } + +#ifdef __MBED_CMSIS_RTOS_CM + _thread_def.pthread = task; + if (_thread_def.stack_pointer == NULL) { + _thread_def.stack_pointer = new uint32_t[_thread_def.stacksize/sizeof(uint32_t)]; + if (_thread_def.stack_pointer == NULL) + return osErrorNoMemory; + } + //Fill the stack with a magic word for maximum usage checking - for (uint32_t i = 0; i < (stack_size / sizeof(uint32_t)); i++) { + for (uint32_t i = 0; i < (_thread_def.stacksize / sizeof(uint32_t)); i++) { _thread_def.stack_pointer[i] = 0xE25A2EA5; } #endif _tid = osThreadCreate(&_thread_def, argument); + if (_tid == NULL) { + if (_dynamic_stack) delete[] (_thread_def.stack_pointer); + return osErrorResource; + } + return osOK; } osStatus Thread::terminate() { return osThreadTerminate(_tid); } +osStatus Thread::join() { + while (true) { + uint8_t state = get_state(); + if (state == Thread::Inactive || state == osErrorParameter) { + return osOK; + } + + osStatus status = yield(); + if (status != osOK) { + return status; + } + } +} + osStatus Thread::set_priority(osPriority priority) { return osThreadSetPriority(_tid, priority); } diff --git a/core/mbed-rtos/rtos/Thread.h b/core/mbed-rtos/rtos/Thread.h index 3e787983ab..275f6fe5c9 100644 --- a/core/mbed-rtos/rtos/Thread.h +++ b/core/mbed-rtos/rtos/Thread.h @@ -30,6 +30,15 @@ namespace rtos { /** The Thread class allow defining, creating, and controlling thread functions in the system. */ class Thread { 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: DEFAULT_STACK_SIZE). + @param stack_pointer pointer to the stack area to be used by this thread (default: NULL). + */ + Thread(osPriority priority=osPriorityNormal, + uint32_t stack_size=DEFAULT_STACK_SIZE, + unsigned char *stack_pointer=NULL); + /** Create a new thread, and start it executing the specified function. @param task function to be executed by this thread. @param argument pointer that is passed to the thread function as start argument. (default: NULL). @@ -42,6 +51,19 @@ public: uint32_t stack_size=DEFAULT_STACK_SIZE, unsigned char *stack_pointer=NULL); + /** Starts a thread executing the specified function. + @param task function to be executed by this thread. + @param argument pointer that is passed to the thread function as start argument. (default: NULL). + @return status code that indicates the execution status of the function. + */ + osStatus start(void (*task)(void const *argument), void *argument=NULL); + + /** Wait for thread to terminate + @return status code that indicates the execution status of the function. + @note not callable from interrupt + */ + osStatus join(); + /** Terminate execution of a thread and remove it from Active Threads @return status code that indicates the execution status of the function. */ @@ -113,17 +135,20 @@ public: @param signals wait until all specified signal flags set or 0 for any single signal flag. @param millisec timeout value or 0 in case of no time-out. (default: osWaitForever). @return event flag information or error code. + @note not callable from interrupt */ static osEvent signal_wait(int32_t signals, uint32_t millisec=osWaitForever); /** Wait for a specified time period in millisec: @param millisec time delay value @return status code that indicates the execution status of the function. + @note not callable from interrupt */ static osStatus wait(uint32_t millisec); /** Pass control to next thread that is in state READY. @return status code that indicates the execution status of the function. + @note not callable from interrupt */ static osStatus yield();