Threading¶
Support for multi-threading.
Priorities¶
As RIOT is using a fixed priority scheduling algorithm, threads are scheduled based on their priority. The priority is fixed for every thread and specified during the thread’s creation by the priority
parameter.
The lower the priority value, the higher the priority of the thread, with 0 being the highest possible priority.
The lowest possible priority is thread.h::THREAD_PRIORITY_IDLE
- 1.
Note
Assigning the same priority to two or more threads is usually not a good idea. A thread in RIOT may run until it yields (thread.h::thread_yield()
) or another thread with higher priority is runnable (thread.h::STATUS_ON_RUNQUEUE
) again. Multiple threads with the same priority will therefore be scheduled cooperatively: when one of them is running, all others with the same priority depend on it to yield (or be interrupted by a thread with higher priority). This may make it difficult to determine when which of them gets scheduled and how much CPU time they will get. In most applications, the number of threads in application is significantly smaller than the number of available priorities, so assigning distinct priorities per thread should not be a problem. Only assign the same priority to multiple threads if you know what you are doing!
Thread Behavior¶
In addition to the priority, flags can be used when creating a thread to alter the thread’s behavior after creation. The following flags are available:
Flags | Description |
---|---|
thread.h::THREAD_CREATE_SLEEPING |
the thread will sleep until woken up manually |
thread.h::THREAD_CREATE_WOUT_YIELD |
the thread might not run immediately after creation |
thread.h::THREAD_CREATE_STACKTEST |
measures the stack’s memory usage |
Thread creation¶
Creating a new thread is internally done in two steps:
- the new thread’s stack is initialized depending on the platform
- the new thread is added to the scheduler and the scheduler is run (if not indicated otherwise)
Note
Creating threads from within an ISR is currently supported, however it is considered to be a bad programming practice and we strongly discourage you from doing so.
Usage¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include "thread.h"
char rcv_thread_stack[THREAD_STACKSIZE_MAIN];
void *rcv_thread(void *arg)
{
(void) arg;
msg_t m;
while (1) {
msg_receive(&m);
printf("Got msg from %" PRIkernel_pid "\n", m.sender_pid);
}
return NULL;
}
int main(void)
{
thread_create(rcv_thread_stack, sizeof(rcv_thread_stack),
THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST,
rcv_thread, NULL, "rcv_thread");
}
|
Reading from the top down, you can see that first, stack memory for our thread rcv_thread
is preallocated, followed by an implementation of the thread’s function. Communication between threads is done using Messaging / IPC. In this case, rcv_thread
will print the process id of each thread that sent a message to rcv_thread
.
After it has been properly defined, rcv_thread
is created with a call to thread.h::thread_create()
in main()
. It is assigned a priority of THREAD_PRIORITY_MAIN - 1
, i.e. a slightly higher priority than the main thread. Since neither the THREAD_CREATE_SLEEPING
nor the THREAD_CREATE_WOUT_YIELD
flag is set, rcv_thread
will be executed immediately.
Note
If the messages to the thread are sent using msg.h::msg_try_send()
or from an ISR, activate your thread’s message queue by calling msg.h::msg_init_queue()
to prevent messages from being dropped when they can’t be handled right away. The same applies if you’d like msg.h::msg_send()
to your thread to be non-blocking. For more details, see the Messaging documentation.
Usage¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include "thread.h"
char rcv_thread_stack[THREAD_STACKSIZE_MAIN];
void *rcv_thread(void *arg)
{
(void) arg;
msg_t m;
while (1) {
msg_receive(&m);
printf("Got msg from %" PRIkernel_pid "\n", m.sender_pid);
}
return NULL;
}
int main(void)
{
thread_create(rcv_thread_stack, sizeof(rcv_thread_stack),
THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST,
rcv_thread, NULL, "rcv_thread");
}
|
Reading from the top down, you can see that first, stack memory for our thread rcv_thread
is preallocated, followed by an implementation of the thread’s function. Communication between threads is done using Messaging / IPC. In this case, rcv_thread
will print the process id of each thread that sent a message to rcv_thread
.
After it has been properly defined, rcv_thread
is created with a call to thread.h::thread_create()
in main()
. It is assigned a priority of THREAD_PRIORITY_MAIN - 1
, i.e. a slightly higher priority than the main thread. Since neither the THREAD_CREATE_SLEEPING
nor the THREAD_CREATE_WOUT_YIELD
flag is set, rcv_thread
will be executed immediately.
Note
If the messages to the thread are sent using msg.h::msg_try_send()
or from an ISR, activate your thread’s message queue by calling msg.h::msg_init_queue()
to prevent messages from being dropped when they can’t be handled right away. The same applies if you’d like msg.h::msg_send()
to your thread to be non-blocking. For more details, see the Messaging documentation.
-
STATUS_NOT_FOUND
¶ Describes an illegal thread status.
1
(-1)
-
STATUS_STOPPED
¶ has terminated
1
0
-
STATUS_SLEEPING
¶ sleeping
1
1
-
STATUS_MUTEX_BLOCKED
¶ waiting for a locked mutex
1
2
-
STATUS_RECEIVE_BLOCKED
¶ waiting for a message
1
3
-
STATUS_SEND_BLOCKED
¶ waiting for message to be delivered
1
4
-
STATUS_REPLY_BLOCKED
¶ waiting for a message response
1
5
-
STATUS_FLAG_BLOCKED_ANY
¶ waiting for any flag from flag_mask
1
6
-
STATUS_FLAG_BLOCKED_ALL
¶ waiting for all flags in flag_mask
1
7
-
STATUS_MBOX_BLOCKED
¶ waiting for get/put on mbox
1
8
-
STATUS_ON_RUNQUEUE
¶ to check if on run queue:
st >= STATUS_ON_RUNQUEUE
1
STATUS_RUNNING
-
STATUS_RUNNING
¶ currently running
1
9
-
STATUS_PENDING
¶ waiting to be scheduled to run
1
10
-
THREAD_CREATE_SLEEPING
¶ Set the new thread to sleeping.
1
(1)
It must be woken up manually.
-
THREAD_AUTO_FREE
¶ Currently not implemented.
1
(2)
-
THREAD_CREATE_WOUT_YIELD
¶ Do not automatically call
thread.h::thread_yield()
after creation: the newly created thread might not run immediately.1
(4)
Purely for optimization. Any other context switch (i.e. an interrupt) can still start the thread at any time!
-
THREAD_CREATE_STACKTEST
¶ Write markers into the thread’s stack to measure stack usage (for debugging and profiling purposes)
1
(8)
-
void *(*
thread_task_func_t
()¶ Prototype for a thread entry function.
-
kernel_types.h::kernel_pid_t
thread_create
(char * stack, int stacksize, char priority, int flags,thread.h::thread_task_func_t
task_func, void * arg, const char * name)¶ Creates a new thread.
For an in-depth discussion of thread priorities, behavior and and flags, see Threading.
Note
Avoid assigning the same priority to two or more threads.
Note
Creating threads from within an ISR is currently supported, however it is considered to be a bad programming practice and we strongly discourage you from doing so.
Parameters
stack: start address of the preallocated stack memory stacksize: the size of the thread’s stack in bytes priority: priority of the new thread, lower mean higher priority flags: optional flags for the creation of the new thread task_func: pointer to the code that is executed in the new thread arg: the argument to the function name: a human readable descriptor for the thread Return values
- PID of newly created task on success
- -EINVAL, if
priority
is greater than or equal tosched.h::SCHED_PRIO_LEVELS
- -EOVERFLOW, if there are too many threads running already
-
sched.h::thread_t
*thread_get
(kernel_types.h::kernel_pid_t
pid)¶ Retreive a thread control block by PID.
This is a bound-checked variant of accessing
sched_threads[pid]
directly. If you know that the PID is valid, then don’t use this function.Parameters
pid: Thread to retreive. Return values
NULL
if the PID is invalid or there is no such thread.
-
int
thread_getstatus
(kernel_types.h::kernel_pid_t
pid)¶ Returns the status of a process.
Parameters
pid: the PID of the thread to get the status from Return values
- status of the thread
STATUS_NOT_FOUND
if pid is unknown
-
void
thread_sleep
(void)¶ Puts the current thread into sleep mode.
Has to be woken up externally.
-
void
thread_yield
(void)¶ Lets current thread yield.
The current thread will resume operation immediately, if there is no other ready thread with the same or a higher priority.
Differently from
thread.h::thread_yield_higher()
the current thread will be put to the end of the thread’s in its priority class.See also
-
void
thread_yield_higher
(void)¶ Lets current thread yield in favor of a higher prioritized thread.
The current thread will resume operation immediately, if there is no other ready thread with a higher priority.
Differently from
thread.h::thread_yield()
the current thread will be scheduled next in its own priority class, i.e. it stays the first thread in its priority class.See also
-
int
thread_wakeup
(kernel_types.h::kernel_pid_t
pid)¶ Wakes up a sleeping thread.
Parameters
pid: the PID of the thread to be woken up Return values
1
on successSTATUS_NOT_FOUND
if pid is unknown or not sleeping
-
kernel_types.h::kernel_pid_t
thread_getpid
(void)¶ Returns the process ID of the currently running thread.
Return values
- obviously you are not a golfer.
-
char *
thread_stack_init
(thread.h::thread_task_func_t
task_func, void * arg, void * stack_start, int stack_size)¶ Gets called upon thread creation to set CPU registers.
Parameters
task_func: First function to call within the thread arg: Argument to supply to task_func stack_start: Start address of the stack stack_size: Stack size Return values
- stack pointer
-
void
thread_add_to_list
(list.h::list_node_t
* list,sched.h::thread_t
* thread)¶ Add thread to list, sorted by priority (internal)
This will add
thread
tolist
sorted by the thread priority. It reuses the thread’s rq_entry field. Used internally by msg and mutex implementations.Note
Only use for threads not on any runqueue and with interrupts disabled.
Parameters
list: ptr to list root node thread: thread to add
-
const char *
thread_getname
(kernel_types.h::kernel_pid_t
pid)¶ Returns the name of a process.
Note
when compiling without DEVELHELP, this always returns NULL!
Parameters
pid: the PID of the thread to get the name from Return values
- the threads name
NULL
if pid is unknown
-
uintptr_t
thread_measure_stack_free
(char * stack)¶ Measures the stack usage of a stack.
Only works if the thread was created with the flag THREAD_CREATE_STACKTEST.
Parameters
stack: the stack you want to measure. try sched_active_thread->stack_start
Return values
- the amount of unused space of the thread’s stack
-
int
thread_isr_stack_usage
(void)¶ Get the number of bytes used on the ISR stack.
-
void *
thread_isr_stack_pointer
(void)¶ Get the current ISR stack pointer.
-
void *
thread_isr_stack_start
(void)¶ Get the start of the ISR stack.
-
void
thread_stack_print
(void)¶ Print the current stack to stdout.
-
void
thread_print_stack
(void)¶ Prints human readable, ps-like thread information for debugging purposes.
-
THREAD_STACKSIZE_DEFAULT
¶ A reasonable default stack size that will suffice most smaller tasks.
Note
This value must be defined by the CPU specific implementation, please take a look at
cpu/$CPU/include/cpu_conf
.h
-
THREAD_STACKSIZE_IDLE
¶ Size of the idle task’s stack in bytes.
Note
This value must be defined by the CPU specific implementation, please take a look at
cpu/$CPU/include/cpu_conf
.h
-
THREAD_EXTRA_STACKSIZE_PRINTF
¶ Size of the task’s printf stack in bytes.
Note
This value must be defined by the CPU specific implementation, please take a look at
cpu/$CPU/include/cpu_conf
.h
-
THREAD_STACKSIZE_MAIN
¶ Size of the main task’s stack in bytes.
1
(THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF)
-
THREAD_STACKSIZE_LARGE
¶ Large stack size.
1
(THREAD_STACKSIZE_MEDIUM * 2)
-
THREAD_STACKSIZE_MEDIUM
¶ Medium stack size.
1
THREAD_STACKSIZE_DEFAULT
-
THREAD_STACKSIZE_SMALL
¶ Small stack size.
1
(THREAD_STACKSIZE_MEDIUM / 2)
-
THREAD_STACKSIZE_TINY
¶ Tiny stack size.
1
(THREAD_STACKSIZE_MEDIUM / 4)
-
THREAD_STACKSIZE_MINIMUM
¶ Minimum stack size.
1
(sizeof(thread_t))
-
THREAD_PRIORITY_MIN
¶ Least priority a thread can have.
1
(SCHED_PRIO_LEVELS-1)
-
THREAD_PRIORITY_IDLE
¶ Priority of the idle thread.
1
(THREAD_PRIORITY_MIN)
-
THREAD_PRIORITY_MAIN
¶ Priority of the main thread.
1
(THREAD_PRIORITY_MIN - (SCHED_PRIO_LEVELS/2))
-
struct
_thread
¶ thread_t
holds thread’s context data.-
char *
sp
¶ thread’s stack pointer
-
uint8_t
status
¶ thread’s status
-
uint8_t
priority
¶ thread’s priority
-
kernel_types.h::kernel_pid_t
pid
¶ thread’s process id
-
thread_flags.h::thread_flags_t
flags
¶ currently set flags
-
clist.h::clist_node_t
rq_entry
¶ run queue entry
-
void *
wait_data
¶ used by msg, mbox and thread flags
-
list.h::list_node_t
msg_waiters
¶ threads waiting for their message to be delivered to this thread (i.e.
all blocked sends)
-
cib_t
msg_queue
¶ index of this [thread’s message queue] (
thread.h::_thread::msg_array
), if any
-
char *
stack_start
¶ thread’s stack start address
-
const char *
name
¶ thread’s name
-
int
stack_size
¶ thread’s stack size
-
char *