mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #79 from geky/thread-lifetime
Add lifetime management to rtos::Thread
commit
451b8c3a22
|
@ -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 (*F)(const void *)>
|
||||||
|
void test_single_thread() {
|
||||||
|
int var = 0;
|
||||||
|
Thread thread(F, &var);
|
||||||
|
thread.join();
|
||||||
|
TEST_ASSERT_EQUAL(var, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int N, void (*F)(const void *)>
|
||||||
|
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 <int N, void (*F)(const void *)>
|
||||||
|
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<increment>),
|
||||||
|
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<increment_with_yield>),
|
||||||
|
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<increment_with_wait>),
|
||||||
|
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<increment_with_child>),
|
||||||
|
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<increment_with_murder>),
|
||||||
|
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);
|
||||||
|
}
|
|
@ -26,34 +26,82 @@
|
||||||
|
|
||||||
namespace rtos {
|
namespace rtos {
|
||||||
|
|
||||||
Thread::Thread(void (*task)(void const *argument), void *argument,
|
Thread::Thread(osPriority priority,
|
||||||
osPriority priority, uint32_t stack_size, unsigned char *stack_pointer) {
|
uint32_t stack_size, unsigned char *stack_pointer):
|
||||||
|
_tid(NULL), _dynamic_stack(stack_pointer == NULL) {
|
||||||
#ifdef __MBED_CMSIS_RTOS_CM
|
#ifdef __MBED_CMSIS_RTOS_CM
|
||||||
_thread_def.pthread = task;
|
|
||||||
_thread_def.tpriority = priority;
|
_thread_def.tpriority = priority;
|
||||||
_thread_def.stacksize = stack_size;
|
_thread_def.stacksize = stack_size;
|
||||||
if (stack_pointer != NULL) {
|
_thread_def.stack_pointer = (uint32_t*)stack_pointer;
|
||||||
_thread_def.stack_pointer = (uint32_t*)stack_pointer;
|
#endif
|
||||||
_dynamic_stack = false;
|
}
|
||||||
} else {
|
|
||||||
_thread_def.stack_pointer = new uint32_t[stack_size/sizeof(uint32_t)];
|
Thread::Thread(void (*task)(void const *argument), void *argument,
|
||||||
if (_thread_def.stack_pointer == NULL)
|
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");
|
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
|
//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;
|
_thread_def.stack_pointer[i] = 0xE25A2EA5;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
_tid = osThreadCreate(&_thread_def, argument);
|
_tid = osThreadCreate(&_thread_def, argument);
|
||||||
|
if (_tid == NULL) {
|
||||||
|
if (_dynamic_stack) delete[] (_thread_def.stack_pointer);
|
||||||
|
return osErrorResource;
|
||||||
|
}
|
||||||
|
return osOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
osStatus Thread::terminate() {
|
osStatus Thread::terminate() {
|
||||||
return osThreadTerminate(_tid);
|
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) {
|
osStatus Thread::set_priority(osPriority priority) {
|
||||||
return osThreadSetPriority(_tid, priority);
|
return osThreadSetPriority(_tid, priority);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,15 @@ namespace rtos {
|
||||||
/** The Thread class allow defining, creating, and controlling thread functions in the system. */
|
/** The Thread class allow defining, creating, and controlling thread functions in the system. */
|
||||||
class Thread {
|
class Thread {
|
||||||
public:
|
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.
|
/** Create a new thread, and start it executing the specified function.
|
||||||
@param task function to be executed by this thread.
|
@param task function to be executed by this thread.
|
||||||
@param argument pointer that is passed to the thread function as start argument. (default: NULL).
|
@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,
|
uint32_t stack_size=DEFAULT_STACK_SIZE,
|
||||||
unsigned char *stack_pointer=NULL);
|
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
|
/** Terminate execution of a thread and remove it from Active Threads
|
||||||
@return status code that indicates the execution status of the function.
|
@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 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).
|
@param millisec timeout value or 0 in case of no time-out. (default: osWaitForever).
|
||||||
@return event flag information or error code.
|
@return event flag information or error code.
|
||||||
|
@note not callable from interrupt
|
||||||
*/
|
*/
|
||||||
static osEvent signal_wait(int32_t signals, uint32_t millisec=osWaitForever);
|
static osEvent signal_wait(int32_t signals, uint32_t millisec=osWaitForever);
|
||||||
|
|
||||||
/** Wait for a specified time period in millisec:
|
/** Wait for a specified time period in millisec:
|
||||||
@param millisec time delay value
|
@param millisec time delay value
|
||||||
@return status code that indicates the execution status of the function.
|
@return status code that indicates the execution status of the function.
|
||||||
|
@note not callable from interrupt
|
||||||
*/
|
*/
|
||||||
static osStatus wait(uint32_t millisec);
|
static osStatus wait(uint32_t millisec);
|
||||||
|
|
||||||
/** Pass control to next thread that is in state READY.
|
/** Pass control to next thread that is in state READY.
|
||||||
@return status code that indicates the execution status of the function.
|
@return status code that indicates the execution status of the function.
|
||||||
|
@note not callable from interrupt
|
||||||
*/
|
*/
|
||||||
static osStatus yield();
|
static osStatus yield();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue