|
|
Threads. More...
Defines | |
#define | OS_INVALID_THREAD 0xFF |
Invalid thread identifier is coded by this value. | |
#define | OS_MAX_THREADS CFG_OS_MAX_THREADS |
Maximum number of threads. | |
#define | OS_MAX_PRIO 16 |
Maximum numbers of priorities. | |
Typedefs | |
typedef unsigned char | os_thread_prio_t |
Thread identification and priority. | |
typedef enum os_thread_state_e | os_thread_state_t |
Thread state type. | |
typedef os_result_t(* | os_thread_func_t )(void *args) |
Main thread function type. | |
Enumerations | |
enum | os_thread_state_e { OS_THREAD_WAIT = 1, OS_THREAD_READY = 2, OS_THREAD_RUNNING = 3 } |
Thread state. More... | |
Functions | |
void | os_thread_init (void) |
Initialization of system threads structures. | |
os_result_t | os_thread_create (os_thread_prio_t priority, os_thread_func_t fun, void *args, unsigned short stack_size) |
Thread create function. | |
void | os_thread_terminated (uni_cpu_reg_t retcode) |
Thread termination function. |
Threads.
Thread in the DioneOS is a separate part of program that can run semi-parallel to other threads. A priority is assigned to each thread. The priority is an ordering norm that defines thread importance and helps to determine which of the threads should be run first. Because performance and compactness features that were main requirements for the system, it has been assumed that priorities are unique. This means that two threads cannot have the same priority. This made simpler to optimize the system code and allowed using the priority as the thread identifier.
The CPU time is assigned to threads by scheduler - the core item of the system. The scheduler always selects the thread with the highest priority from the set of threads that are not waiting. When a function of synchronization object (e.g. semaphore) is called the list of ready threads can be altered and CPU may be redirected to execute another thread. This switch from one thread to another is done by changing a context (stack, CPU registers contents, PC counter) and is handled by the system. Note that the preemption (and as a consequence context switch) may be also caused by ISR that have been triggered by hardware interrupt. In this case the switch happens in the time and code location that the thread is not aware of.
The thread in DioneOS can be in one of following states:
. OS_THREAD_RUNNING - the thread is currently running and owns CPU time. Only one thread can be in running state.
. OS_THREAD_READY - thread is ready to be run, but right now is preempted by higher priority thread.
. OS_THREAD_WAIT - thread is waiting on some synchronization object (semaphore, mutex etc.)
The state change is automatic and is done by the system during context switch.
At the first step threads module should be initialized by os_thread_init() call. After that, but before the scheduler is started, threads should be created. At that point thread properties should be specified (i.e. priority, stack size, main function).
Main function of the thread is C function of specific declaration:
os_result_t my_thread_fun( void * args )
The function is entered when corresponding thread is run for the first time. The thread creator can pass arguments to the main thread function. The arguments are passed by universal pointer that can be assigned and then casted to a structure type, hence it does not limit possible options. This pointer is one of the arguments of thread create function. The function should loop forever when it is in regular mode. Nevertheless, there is protection that when it exits it will behaves like a call of os_thread_terminated() with the input argument equal to return code from main thread function. Current implementation throws os_bug() exception when thread is terminated in this way. It may be altered by the user.
In order to implement true independent* thread context (i.e. some kind of isolated state proper for each thread), each thread has its own stack. The size of the stack is declared during thread creation and cannot be changed later. Reserved size should take into account a space required for image of all registers. This space is used during context switch and in ISR, because ISR uses running thread stack. Also, regular stack usage must be included in that. The stack stores return addresses (so number of nested calls is important), local variables (in each function in call tree) and saved registers. Note that change of the compilation option (e.g. optimization level) may affect stack requirement.
*) - some solutions provide semi-parallel items (e.g. "co-routines", "protothreads", etc.) that are light version of multi-job programming. Unfortunatelly, they cannot provide preemption and require cooperative programming of the jobs. This affects event processing latency and adds some constrains for such jobs (e.g. global data).
#define OS_MAX_THREADS CFG_OS_MAX_THREADS |
Maximum number of threads.
Threads information s stored in an internal table that contains thread controls blocks. This number is used to limit size of tcb table. Set it to number of threads that are created in your program. Use corresponding variable in config.h to configure its value.
typedef os_result_t(* os_thread_func_t)(void *args) |
Main thread function type.
The function is called at the first context switch to the thread. It should contain infinite loop. The function should not exit. Nevertheless if execution exits the function it is redirected to os_thread_terminated() function.
struct my_struct { int field1, char field2 };//example structure for arguments passing os_result_t my_thread_1( void * args ) { my_struct * a = (args_struct*)args;//conversion to user type allows access to passed items while(1) { // ... do thread job here... // ... in infinite loop ... // arguments can be accessed by: // a->field1 } //never return in regular conditions }
[in] | args | pointer passed to the function, the pointer is defined during thread create function os_thread_create() |
typedef unsigned char os_thread_prio_t |
Thread identification and priority.
Priorities of the threads must be unique, hence the priority can be used to identify the thread. Lower value represents higher priority (more significant thread). Priority 15 (the least important thread) should be assigned to Idle Thread.
enum os_thread_state_e |
Thread state.
{ OS_THREAD_WAIT = 1, OS_THREAD_READY = 2, OS_THREAD_RUNNING = 3 };
os_result_t os_thread_create | ( | os_thread_prio_t | priority, |
os_thread_func_t | fun, | ||
void * | args, | ||
unsigned short | stack_size | ||
) |
Thread create function.
It registers the thread in system structures. Threads should be created before the scheduler is run. Threads cannot be deleted.
Size of the thread stack depends on number of nested calls, saved space in each function (incl. local variables). The requirements may change when compilation options are altered (e.g. optimization level). The space must be reserved for all registers save, first part is needed for context switch, second for ISR that uses thread stack.
[in] | priority | thread priority (0..OS_MAX_THREADS-1), 0 represents the highest priority |
[in] | fun | main thread function |
[in] | args | arguments passed to thread function by the pointer. May be NULL if thread function is aware of that |
[in] | stack_size | size of thread stack |
void os_thread_init | ( | void | ) |
Initialization of system threads structures.
The function must be called at the beginning of the program, before any thread is created.
void os_thread_terminated | ( | uni_cpu_reg_t | retcode | ) |
Thread termination function.
Although, the thread function should never exit, if it happens execution is redirected to this function. Because such condition is unexpected and signals exceptional state of the system os_bug() is called.
[in] | retcode | Value returned by thread function. It will remain in R0 register. |