RIOT MultiThreading

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

Workshop 4

Multithreading on RIOT

Plan:
• Introduction
• Thread status
• Thread priorities
• Thread behavior
• Thread creation

Introduction
A 'tick' in OS terms is an interval after which the OS will wake up
to process any pending events.
So, if you have a 100Hz tick, then 100 times a second, the OS will
wake up, check to see whether any timers have expired, execute their
actions if there are, then go to sleep again. On a tick-less system,
rather than waking up 100 times a second to see if anything is ready
to process, the OS will look at when the next timer is due to expire
and then sleep until that time.
Note that when the CPU is active, it's obviously not going to go
into any kind of sleep mode, and the kernel will set up an interrupt
for scheduling purposes. It's only tickless (as the comment points
out) when there's nothing running on the CPU or (in very modern
Linux kernels) when only one process is running.
RIOT OS is based on a Tick-less scheduling policy. Highest
priority thread runs until finished or blocked. There are 16 priority
levels, the lower level the higher priority. The IDLE thread has
priority 15 and the main thread has priority 7.
Any interruption ISR can preempt any thread at any time. If all
threads are blocked, the kernel switch to the special IDLE thread and
can goes into lowest possible power mode.
A thread is just a function with signature and have their own
memory stack.

1. Thread status
A thread throughout its lifecycle will go through multiple states. The
following table shows some states:
Status Value Description
STATUS_NOT_FOUND -1 Describes an
illegal thread
status
STATUS_STOPPED 0 has terminated
STATUS_SLEEPING 1 Sleeping
STATUS_MUTEX_BLOCKED 2 waiting for a
locked mutex
STATUS_RECEIVE_BLOCKED 3 waiting for a
message
STATUS_SEND_BLOCKED 4 waiting for
message to be
delivered
STATUS_RUNNING 9 currently
running
STATUS_PENDING 10 waiting to be
scheduled to
run
2. Thread 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_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_yield) or
another thread with higher priority is runnable
(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!

3. 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:
4. Thread Creation
Creating a new thread is internally done in two steps:
1. the new thread's stack is initialized depending on the platform
2. the new thread is added to the scheduler and the scheduler is
run (if not indicated otherwise)
Note:
Creating threads from within an ISR (Interruption routine) is
currently supported, however it is considered to be a bad programming
practice and we strongly discourage you from doing so.

The used function to create a thread is:


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, c
onst char * name
)
The parameters of the function are:

• 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
The returned values are:
• PID of newly created task on success
• -EINVAL, if priority is greater than or equal to
sched.h::SCHED_PRIO_LEVELS
• -EOVERFLOW, if there are too many threads running already

Example 1:

Stack is a global static byte array like:

By default, the thread starts immediately and here, priority is higher


than main thread (THREAD_PRIORITY_MAIN - 1).
The function thread_getpid() returns the current thread pid.
For more usage examples in tests/thread_* test applications.

Example 2:

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_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_try_send() or
from an ISR, activate your thread's message queue by calling
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_send()
to your thread to be non-blocking. For more details, see the Messaging
documentation.

Exercice 1:
Write an application which creates 3 threads with priorities
respectively 4.10 and 12. Each thread must display in the terminal a
specific message.
Follow the next steps:
• Use the iotlab-m3 node.
• Write the necessary C program.
• Compile and run the application.
• Check on the standard output that the messages are correctly
displayed.
Exercice 2 :
Write an application which creates 2 threads T1 and T2 with
priorities respectively 4 and 12. In the application you declare a global
variable named g_var of type integer and initialized to 0. Each thread
must display a message specific to it on the terminal and act on the
global variable g_var as follows:
• T1: must increment g_var by 10 then display its value.
• T2: must decrement g_var by 2 then display its value.
Follow the next steps:
• Use the iotlab-m3 node.
• Write the necessary C program.
• Compile and run the application.
• Check on the standard output that the messages are correctly
displayed and the execution order of the threads.

You might also like