You are currently not logged in and your progress will not be saved. Register or Log in

Exercise 2

Time slicing

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.

Exercise Steps:

1. Download the base exercise project and extract it in your exercise folder for this course.

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");
	}
}

void thread1(void)
{
	while (1) {
            printk("Hello, I am thread1\n");
	}
}

We now know that the first thread that starts running will block the other one indefinitely.

3. To avoid that, 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

TIMESLICING 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 a higher priority than this threshold are not subject to time slicing.

4. Build the application and flash it on your development kit. Using a serial terminal you should now see the below output:

*** Booting Zephyr OS build v3.0.99-ncs1  ***
Hello, I am thread0
Hello, I am thread0
Hello, I am thread0
Hello, I am thread0
Hello, I am thread0
Hello, I Hello, I am thread1
Hello, I am thread1
Hello, I am thread1
Hello, I am thread1
Hello, I am thread1
Hello, I aam thread0
Hello, I am thread0
Hello, I am thread0
Hello, I am thread0
Hello, I am thread0
Hello, I am thread0m thread1
Hello, I am thread1

The 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.

Timeline for preemptive time slicing

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

6. 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.

You can download the solution for Exercise 2 below.