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

Exercise 2

Mutexes

In this exercise, we will create an application with two threads running and accessing the same code section. The logic looks perfect, but when two different threads try to access the code section simultaneously, unexpected things happen. We will see how a mutex can be utilized to synchronize these two threads.

Exercise Steps:

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

2. Enable multithreading in the application. This is done by adding the following in prj.conf:

CONFIG_MULTITHREADING=y

3. Set the priority of thread0 and thread1 to have equal priority.

#define THREAD0_PRIORITY        4 
#define THREAD1_PRIORITY        4

4. Create the functions for the two threads. The functions should run the function shared_code_section() in a while-loop. shared_code_section() should be commented out in thread1 for now.

void thread0(void)
{
	printk("Thread 0 started\n");
	while (1) {
		shared_code_section(); 
	}
}

void thread1(void)
{
	printk("Thread 1 started\n");
	while (1) {
		//shared_code_section(); 
	}
}

5. Define two counter variables, increment_partner and decrement_partner and a constant macro COMBINED_TOTAL to set their combined total to an arbitrary value, let’s say 40.

#define COMBINED_TOTAL   40

int32_t increment_partner = 0; 
int32_t decrement_partner = COMBINED_TOTAL; 

6. In the function shared_code_section(), add the following logic:

increment_partner is incremented
increment_partner is reset to 0 if the maximum of COMBINED_TOTAL-1 is reached
decrement_partner is decremented
decrement_partner is reset to COMBINED_TOTAL if the minimum value of 1 is reached

increment_partner += 1;
increment_partner = increment_partner % COMBINED_TOTAL; 

decrement_partner -= 1;
if (decrement_partner == 0) 
{
	decrement_partner = COMBINED_TOTAL;
}

Note

If this logic is implemented correctly, increment_partner + decrement_partner = COMBINED_TOTAL should be true at all times. If this hard rule is broken at any point in the run time, there is a flaw in the code.

7. Add an if-statement in shared_code_section() that prints the values of the counters only if their combined value is not equal to COMBINED_TOTAL.

if (increment_partner + decrement_partner != COMBINED_TOTAL)
{
	printk("Increment_partner (%d) + Decrement_partner (%d) = %d \n",
                increment_partner, decrement_partner, (increment_partner + decrement_partner));
}

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

*** Booting Zephyr OS build v3.0.99-ncs1-1  ***
Thread 0 started
Thread 1 started

We confirm that our increment and decrement counter logic is working fine when only thread0 is accessing shared_code_section(), because the printk() check isn’t printing anything.

9. Now let thread1 access shared_code_section() by uncommenting the line to make it look like below.

void thread1(void)
{
	printk("Thread 1 started\n");
	while (1) {
		shared_code_section(); 
	}
}

10. Build the application and flash it on your development kit again. You should see the below output:

*** Booting Zephyr OS build v2.6.99-ncs1-1  ***
Thread 0 started
Thread 1 started
Increment_partner (5) + Decrement_partner (38) = 43 
Increment_partner (6) + Decrement_partner (37) = 43 
IncremeIncrement_partner (8) + Decrement_partner (37) = 45 
Increment_partner (9) + Decrement_partner (36) = 45 
Incremnt_partner (7) + Decrement_partner (36) = 43 
Increment_partner (11) + Decrement_partner (34) = 45 
Increment_parent_partner (10) + Decrement_partner (35) = 45 
Increment_partner (13) + Decrement_partner (32) = 45 
Increment_ptner (12) + Decrement_partner (33) = 45 
Increment_partner (15) + Decrement_partner (30) = 45 
Increment_partner artner (14) + Decrement_partner (31) = 45 
Increment_partner (17) + Decrement_partner (28) = 45 
Increment_partn(16) + Decrement_partner (29) = 45 
Increment_partner (19) + Decrement_partner (26) = 45 
Increment_partner (20) er (18) + Decrement_partner (27) = 45 
Increment_partner (21) + Decrement_partner (24) = 45 
Increment_partner (+ Decrement_partner (25) = 45 
Increment_partner (23) + Decrement_partner (22) = 45 
Increment_partner (24) + Dec22) + Decrement_partner (23) = 45 
Increment_partner (25) + Decrement_partner (20) = 45 
Increment_partner (26) rement_partner (21) = 45 
Increment_partner (27) + Decrement_partner (18) = 45 

As soon as thread1 started to access the shared code section, our working logic failed.

This is a classic example for the need to protect the shared code section to ensure that only one thread can access the shared code section at one time. The basic flaw in our code is that we assume that the operations on the two counters (increment_partner, decrement_partner) will be performed at once without interruptions. When this is not the case, because any thread can access and update these shared memory variables anytime, then the checks on the values will fail. This is where we can use a mutex to serialize the access to this shared code by multiple threads. By using a mutex, we can force shared_code_section() to run uninterrupted by other threads.

11. Add a mutex by first defining the mutex like below.

K_MUTEX_DEFINE(test_mutex);

12. Lock the mutex before performing the counter logic, and unlock it right before the if-statement that checks the counter sum using these functions.

k_mutex_lock(&test_mutex, K_FOREVER);
k_mutex_unlock(&test_mutex);

13. Build the application and flash it on your development kit. You should see the same output as in step 9, i.e the values if the counters are not printed.

You can download the solution for Exercise 2 below.