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. In the GitHub repository for this course, open the base code for this exercise, found in lesson8/fund_less8_exer2 of whichever version directory you are using (v2.x.x or v1.6.0-v1.9.1).

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

This configuration defaults to yes and isn’t strictly necessary to enable manually.

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:

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:

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.

The solution for this exercise can be found in the GitHub repository, lesson7/fund_less8_exer2_solution of whichever version directory you are using (v2.x.x or v1.6.0-v1.9.1).

Register an account
Already have an account? Log in
(All fields are required unless specified optional)

Forgot your password?
Enter your email address, and we will send a link to reset your password.