If you don’t want to worry about creating perfect logic for yielding between equal priority threads, you can enable time slicing.
In this exercise, we will take a close look at time slicing and how it affects the interaction between threads, both of equal and different priorities.
1. In the GitHub repository for this course, open the base code for this exercise, found in l7/l7_e2
of whichever version directory you are using.
2. As you can see in main.c
, the thread’s execution functions neither sleep nor yield.
void thread0(void)
{
while (1) {
printk("Hello, I am thread0\n");
k_busy_wait(1000000);
}
}
void thread1(void)
{
while (1) {
printk("Hello, I am thread1\n");
k_busy_wait(1000000);
}
}
CWe now know that the first thread that starts running will block the other one indefinitely. The reason we added k_busy_wait()
is to prevent the serial terminal from being flooded by logs. This function call causes the current thread to execute a do-nothing loop for a specified time in microseconds. This function is intended for debugging purposes only and it’s not recommended for production code.
3. To avoid thread0 from starving thread1 which is at the same priority level, let’s enable time slicing in our project, by adding the following lines to the prj.conf
file.
CONFIG_TIMESLICING=y
CONFIG_TIMESLICE_SIZE=10
CONFIG_TIMESLICE_PRIORITY=0
KconfigTIMESLICING
enables the time slicing feature.TIMESLICE_SIZE
is the maximum time (in ms) that the current running thread has before it is forcefully preempted by the scheduler to allow the other equal priority threads to run.TIMESLICE_PRIORITY
is the priority threshold for time slicing, meaning threads with higher priority than this threshold are not subject to time slicing. We set this value to 0
. This means priorities (0 to 15) will be affected by time-slicing only when two or more threads exist in one level. It’s always important to remember that time slicing only affect threads with the same priority level.4. Build the application and flash it on your development kit. Using a serial terminal you should now see the below output:
*** Booting nRF Connect SDK 2.6.1-3758bcbfa5cd ***
Hello, I am thread0
Hello, I am thread1
Hello, I am thread0
Hello, I am thread1
Hello, I am thread0
Hello, I am thread1
Hello, I am thread0
Hello, I am thread1
Hello, I am thread0
Hello, I am thread1
Hello, I am thread1
Hello, I am thread0
Hello, I am thread1
Hello, I am thread0
Hello, I am thread1
Hello, I amHello, I am thread0
thread1
TerminalThe output shows that the scheduler will preempt the running thread after the configured amount of time (10 ms in this case) regardless of what it is doing. We see that the scheduler preempts the threads even though they did not print the whole printk()
message. Notice that the new thread will continue to print the message from the same place it was preempted the last time.
The timeline of the activity looks something like below.
The scheduler forcefully preempts thread0
after it runs for 10 ms and lets thread1
run for 10 ms after which thread1
is preempted to give time for thread0
again.
It is important to note that the time slice configuration only affects equal priority threads. So what happens when time slicing is enabled and there are no equal priority threads to run after the time slice time expires?
We can find out by increasing the priority (lowering the priority value) of one of the threads.
5. Change the value of THREAD0_PRIORITY
to 6
like below, making thread0
a higher priority than thread1
.
#define THREAD0_PRIORITY 6
#define THREAD1_PRIORITY 7
C6. Build the application and flash it on your development kit. Using a serial terminal you should now see from the output that thread0
runs uninterrupted, starving thread1
forever.
Even though the output might not show it, the scheduler still preempts thread0
every 10 ms to check if there are any other equal or higher priority threads in the “Runnable” state. Since the only other thread, thread1
, is of lower priority, the scheduler lets thread0
run for another time slice period. Since thread1
will always be of lower priority, thread0
will run forever like this.
The solution for this exercise can be found in the GitHub repository, l7/l7_e2_sol
of whichever version directory you are using.