FB pixel

How to use Mutexes in Azure RTOS ThreadX | Renesas RA - 23

Published


Hello again! In this tutorial we will keep considering the services which provide the interconnection between threads in Azure RTOS ThreadX. In the previous tutorial, we discovered semaphores, and this time we will talk about mutexes. The word “mutex” is a composition of words “mutual exclusion” which perfectly describes the function of this service. Actually, semaphores and mutexes are very similar at first glance and thus they are often mixed up. Let’s see what is different and common between them.

Comparison of Mutexes and Semaphores

Like semaphores, mutexes are objects that can be incremented and decremented by the put and get operations. But unlike semaphores mutexes are assigned with a so-called “ownership counter” which is incremented with the get operation, and is decremented with the put operation.

Initially, when a mutex has been just created, its ownership counter is 0, which means that it is not owned by any thread. Now, if any thread implements the get operation to this mutex, it becomes the owner of the mutex. And then, if any other threads try to implement the get operation to the same mutex, they will be suspended until the thread who owns it, releases it using the put operation. Then the mutex will be owned by the thread who requested it first, and other threads will remain suspended, and so on.

The thread which owns the mutex, can implement the get operation many times (actually 232-1 times), but then to release the mutex it needs to implement an equal number of put operations.

As you see, the main difference between the mutexes and semaphores is that a semaphore is incremented if any thread implements the put operation to it, and the mutex “ownership counter” is decremented only if the thread which owns it implements the put operation.

Like with semaphores, you can get into the same trouble using mutexes, which are priority inversion and the deadly embrace (deadlock). These troubles are resolved with the same methods as with semaphores. But there is also a specific feature of mutexes to prevent priority inversion. It’s called “priority inheritance”. When it’s enabled, the priority of the thread which owns the mutex is automatically increased to the priority of the thread which also wants to get this mutex. When the mutex is released by the first thread, its priority returns to the initial value. You don’t need to do anything about this except for enabling the “priority inheritance” option, as this is the internal feature of the mutex.

Let’s see the difference between the mutexes and semaphores using some examples. But first we need to assemble some test schematics.

Schematics Diagram of the Test Setup

Schematics diagram of the test setup
Figure 1 - Schematics diagram of the test setup

As you can see, the schematics diagram is quite simple. It consists of four additional push buttons S1 - S4, and three LEDs LED1-LE3 with the current-limiting resistors R1-R3 of 1 kOhm. All parts are connected to the J2 connector of the EK-RA2A1 board. As you can see, we skip some of the pins on the connector as we go down. This is because some pins are already configured to be used with some peripherals, and to reduce the configuration, it’s better to just skip them.

The example will be the following. In the project there are two threads: Thread1 and Thread2, and one mutex/semaphore. Pressing button S1 causes the get operation on Thread1. Pressing button S2 causes the put operation at Thread1. Pressing button S3 causes the get operation at Thread2. Pressing the button S4 causes the put operation at Thread2. When Thread1 owns the mutex/semaphore, then LED1 is turned on. When Thread2 owns the mutex/semaphore, then LED2 is turned on. When the mutex/semaphore is released, no LED is turned on. LED3 is not used in the current example, it will be used later.

Program to test the Mutexes

Let’s now open e2 studio and create a new project based on the Azure RTOS ThreadX and the Minimal template (like we did in tutorial 17), and call it “threadx_mutex1”. In the FSP Configurator window switch to the “Pins” tab, as we first need to set up all the pins to which external parts are connected.

All the pins to which the push buttons are connected (P301, P302, P410, P015) should be configured as follows (Figure 2).

Configuration of the P301 pin
Figure 2 - Configuration of the P301 pin

We should set the “Mode” field as “Input mode” and “Pull up” field as “input pull-up”. Also we need to set the “Symbolic Name” field for the pins according to Figure 1: P301 - “S1”, P302 - “S2”, P410 - “S3”, P015 - “S4”. In the code we will use these names to address the buttons.

The pins to which LEDs are connected (P107, P106, P002) should be configured as follows (Figure 3).

Configuration of the P107 pin
Figure 3 - Configuration of the P107 pin

Here we should change the “Mode” to “Output mode (Initial Low)” and give the symbolic names to pins: P107 - “LED1”, P106 - “LED2”, P002 - “LED3”. As for the “Drive capacity” field, we can leave it “Low” as with the 1kOhm series resistor, the output current will be quite low.

Now, as the pin configuration is completed, let’s switch to the “Stacks” tab and create the new thread. Then configure it as follows (Figure 3).

Configuration of Thread 1
Figure 4 - Configuration of Thread 1

In the common ThreadX settings (all lists except for “Thread”) we need only to enable the Event Trace in order to use the TraceX tool, and decrease the TraceX buffer size from 65536 to 16384.

In the thread settings we will just change its Symbol to “thread1” and Name to “Thread 1”.

Now let’s create one more thread and in its settings change its Symbol to “thread2” and Name to “Thread 2” (Figure 4).

Configuration of Thread 2
Figure 5 - Configuration of Thread 2

Now we need to add the mutex to the project. To do this we need to click the “New Object >” button and select the “Mutex” in the drop-down list (Figure 5).

Creation of the new mutex
Figure 6 - Creation of the new mutex

Now we need to configure the mutex as follows (Figure 6).

Configuration of Mutex 1
Figure 7 - Configuration of Mutex 1

As you can see, the mutex has very few parameters: “Name” and “Symbol” which have the same meaning for all FSP stacks, and “Priority Inheritance” which I talked about before. If it is enabled, then the priority of the thread which owns this mutex will be automatically increased to the priority of the thread which also requests it. As in the current example no priority inversion is pending, we can leave this field as “Disabled”.

Now, the whole “Stacks” tab should look as follows (Figure 7).

Final stacks configuration of the project
Figure 8 - Final stacks configuration of the project

We need to press the “Generate Project Content” button to create all necessary files. In the “src” folder of the project there are now three files: “hal_entry.c”, “thread1_entry.c” and “thread2_entry.c”. We will write the code in the last two ones. Let’s start with the “thread1_entry.c” file.

#include "thread1.h"

/* Thread 1 entry function */

void thread1_entry(void)

{

CHAR *name; //Pointer to destination for the pointer to the mutex's name

ULONG count; //Pointer to destination for the ownership count of the mutex

TX_THREAD *owner; //Pointer to destination for the owning thread's pointer

TX_THREAD *first_suspended;//Pointer to destination for the pointer to the thread that is first on the suspension list of this mutex

ULONG suspended_count; //Pointer to destination for the number of threads currently suspended on this mutex

TX_MUTEX *next_mutex; //Pointer to destination for the pointer of the next created mutex

/* TODO: add your own code here */

while (1)

{

if (R_BSP_PinRead(S1) == BSP_IO_LEVEL_LOW) //If S1 button is pressed

{

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S1) == BSP_IO_LEVEL_LOW) //If S1 button is still pressed

{

while (R_BSP_PinRead(S1) == BSP_IO_LEVEL_LOW); //Wait while it is pressed

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S1) == BSP_IO_LEVEL_HIGH) //If S1 button is released

{

tx_mutex_get(&mutex1, TX_WAIT_FOREVER); //Get the mutex

R_BSP_PinAccessEnable(); //Enable access to the IO registers

R_BSP_PinWrite(LED1, BSP_IO_LEVEL_HIGH); //Turn on LED1

R_BSP_PinWrite(LED2, BSP_IO_LEVEL_LOW); //Turn off LED2

R_BSP_PinAccessDisable(); //Disable access to the IO registers

}

}

}

if (R_BSP_PinRead(S2) == BSP_IO_LEVEL_LOW) //If S2 button is pressed

{

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S2) == BSP_IO_LEVEL_LOW) //If S2 button is still pressed

{

while (R_BSP_PinRead(S2) == BSP_IO_LEVEL_LOW); //Wait while it is pressed

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S2) == BSP_IO_LEVEL_HIGH) //If S2 button is released

{

tx_mutex_put(&mutex1); //Put the mutex

tx_mutex_info_get(&mutex1, &name, &count, &owner, &first_suspended, &suspended_count, &next_mutex); //Get the information about the mutex

if (count == 0) //If the ownership counter is 0

{

R_BSP_PinAccessEnable(); //Enable access to the IO registers

R_BSP_PinWrite(LED1, BSP_IO_LEVEL_LOW); //Turn off LED1

R_BSP_PinAccessDisable(); //Disable access to the IO registers

}

}

}

}

}

}

Even though the program is relatively long, it’s still very simple.

In lines 6-11 we declare variables which will be used in the tx_mutex_get_info function in line 43. Actually, of all these variables, we need only one - count, but we will talk about it later.

In lines 15-31 we check to see if button S1 is pressed. It’s the usual routine, so I will not stop on it. Just pay attention that we use the S1 macro when we check the pin to which button S1 is connected (lines 15, 18 etc.), as we defined it in Figure 1.

In lines 24-28 there is what we actually do when the button has been pressed. First, we try to get Mutex 1 by calling the tx_mutex_get function (line 24). This function is very similar to the tx_semaphore_get which we considered in the previous tutorial. Its first parameter is the pointer to the mutex which we want to get, and the second parameter is the waiting option. As you can see, here we wait forever till the mutex becomes available. Once we have got Mutex 1 we enable access to the IO registers (line 25), turn on LED1 to indicate that Thread 1 is now owner of Mutex 1 (line 26), and turn off LED2 to indicate that Thread 2 no longer owns this mutes (line 27). Finally we disable access to the IO registers (line 28).

In lines 33-52 we check if the S2 button is pressed. The payload of this part is located at lines 42-29. First, we put Mutex 1 with the tx_mutex_put function (line 42). This function has only one parameter - the pointer to the mutex to be put. As I mentioned before, when we implement this function, the ownership counter of the mutex decrements and can in some moment reach 0. In this case the mutex belongs to no thread. So here we need to know the value of the ownership counter to turn off LED1 when Thread 1 releases Mutex 1. To do this, we invoke the function tx_mutex_info_get (line 43), which provides a lot of information about the mutex (see the declared variables in lines 6-11). Among all this information we need only the value of the ownership counter which is located in the count variable. So in line 44 we check if the count is 0, and if yes, we turn off LED1 (lines 46-48).

Why don’t we check the ownership counter after the get operation? That’s simple, after the mutex has been “got” by the thread, any further get operations will change nothing from the ownership perspective - the mutex will still be owned by this thread.

Let’s now consider the code of the “thread2_entry.c”. It is very similar to the previous code.

#include "thread2.h"

/* Thread 2 entry function */

void thread2_entry(void)

{

CHAR *name; //Pointer to destination for the pointer to the mutex's name

ULONG count; //Pointer to destination for the ownership count of the mutex

TX_THREAD *owner; //Pointer to destination for the owning thread's pointer

TX_THREAD *first_suspended;//Pointer to destination for the pointer to the thread that is first on the suspension list of this mutex

ULONG suspended_count; //Pointer to destination for the number of threads currently suspended on this mutex

TX_MUTEX *next_mutex; //Pointer to destination for the pointer of the next created mutex

/* TODO: add your own code here */

while (1)

{

if (R_BSP_PinRead(S3) == BSP_IO_LEVEL_LOW) //If S3 button is pressed

{

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S3) == BSP_IO_LEVEL_LOW) //If S3 button is still pressed

{

while (R_BSP_PinRead(S3) == BSP_IO_LEVEL_LOW); //Wait while it is pressed

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S3) == BSP_IO_LEVEL_HIGH) //If S3 button is released

{

tx_mutex_get(&mutex1, TX_WAIT_FOREVER); //Get the mutex

R_BSP_PinAccessEnable(); //Enable access to the IO registers

R_BSP_PinWrite(LED2, BSP_IO_LEVEL_HIGH); //Turn on LED2

R_BSP_PinWrite(LED1, BSP_IO_LEVEL_LOW); //Turn off LED1

R_BSP_PinAccessDisable(); //Disable access to the IO registers

}

}

}

if (R_BSP_PinRead(S4) == BSP_IO_LEVEL_LOW) //If S4 button is pressed

{

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S4) == BSP_IO_LEVEL_LOW) //If S4 button is still pressed

{

while (R_BSP_PinRead(S4) == BSP_IO_LEVEL_LOW); //Wait while it is pressed

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S4) == BSP_IO_LEVEL_HIGH) //If S4 button is released

{

tx_mutex_put(&mutex1); //Put the mutex

tx_mutex_info_get(&mutex1, &name, &count, &owner, &first_suspended, &suspended_count, &next_mutex); //Get the information about the mutex

if (count == 0) //If the ownership counter is 0

{

R_BSP_PinAccessEnable(); //Enable access to the IO registers

R_BSP_PinWrite(LED2, BSP_IO_LEVEL_LOW); //Turn off LED2

R_BSP_PinAccessDisable(); //Disable access to the IO registers

}

}

}

}

}

}

As you can see, this thread is almost the same, the only differences are that we check buttons S3 and S4 instead of S1 and S2, respectively. Also here LED1 and LED2 are swapped in comparison to Thread 1. Everything else is identical.

Now we can build the project, connect the board to the PC and start debugging. When the project is running you can try several cases. I will illustrate the actions with the images from the TraceX. This time I will not explain how to export the TraceX buffer to file, you can read how to do this in tutorials 17 and 18. Just don’t forget to check the current address of the g_tx_trace_buffer variable.

1. Press the S1 button, you will see that LED1 turns on, which means that Thread 1 now owns Mutex 1 (Figure 8).

First pressing of the S1 button
Figure 9 - First pressing of the S1 button

As you can see, initially Mutex 1 is not owned by any thread, and its ownership counter is 0.

Press S1 several times, you will see that nothing visible happens. Each press of the button increments the ownership counter of the mutex by 1, and Mutex 1 still belongs to Thread 1 (Figure 9).

Mutex 1 state after 10 presses of the S1 button
Figure 10 - Mutex 1 state after 10 presses of the S1 button

Now press S2, you will see that nothing has changed. This is because the ownership counter has been decremented just once, and it’s still not 0 (Figure 10).

Mutex 1 state after pressing the S2 button once
Figure 11 - Mutex 1 state after pressing the S2 button once

In Figure 10 you can see that the Own counter is 10, so it’s not decremented but incremented. This is just because here we see the previous state of the mutex before the current put operation. The next block (IG) is getting the mutex information (line 43 of the code).

Press S2 several times until LED1 turns off. When this happens the ownership counter reaches 0, and Mutex 1 is released, so now no thread owns it (Figure 11).

Mutex 1 state right before releasing it by Thread 1
Figure 12 - Mutex 1 state right before releasing it by Thread 1

Try the same with buttons S3 and S4 and make sure it works in the same way with Thread 2.

2. Now let’s see how the mutex moves from one thread to another. Press the S1 button and see that LED1 turns on. Then press S3 to implement the get operation with Thread 2. As you can see, nothing happens, because mutex already is owned by Thread 1, so Thread 2 becomes suspended (Figure 12).

Suspension of Thread 2 after attempting to get Mutex 1
Figure 13 - Suspension of Thread 2 after attempting to get Mutex 1

Now, press S2, and see that LED1 turned off and LED2 turned on. This happened because pressing S2 decremented the ownership counter of the mutex to 0, and thus Thread 1 no longer owns it. And immediately it’s got by Thread 2 which has been suspended previously waiting for the mutex (Figure 13).

Resuming Thread 2 after releasing Mutex 1 by Thread 1
Figure 14 - Resuming Thread 2 after releasing Mutex 1 by Thread 1

Now press S1. This will suspend Thread 1 which now waits for the mutex. And finally if you press S4 to release the mutex from Thread 2, it will be got by Thread 1, and the LEDs will toggle again.

3. Try the previous case but press S1 initially several times then press S3, and then S2. You will see that the LEDs didn’t toggle. This is because the ownership counter of the mutex didn’t reach 0. Press S2 several times until LEDs toggle, at this moment the mutex has been moved to Thread 2.

4. Press the S1 button and make sure that LED1 is on now. Then press the S4 button to try to implement the put operation to the mutex. You will see that nothing happens. And even if you press S4 several times, there will not be any effect, and the ownership counter will remain 1 (Figure 14).

Trying to put the mutex by the thread which doesn’t own it
Figure 15 - Trying to put the mutex by the thread which doesn’t own it

This is because only the thread that owns the mutex can change its ownership counter.

You can invent other cases to test and see how two threads can deal with one mutex.

Program to test the Semaphores

Now, let’s create the same project but replace the mutex with the semaphore and see if there is any difference. To do this let’s create a new project “threadx_sempahore_4” from the current one using the Import & Rename option the same as we did in the previous tutorials (Figure 15).

Creation of the new project based on the existing one
Figure 16 - Creation of the new project based on the existing one

In the new project let’s open the “configuration.xml” file and switch to the “Stacks” tab. There we need to select the “mutex1” and press the “Remove” button (Figure 16).

Removing of Mutex 1
Figure 17 - Removing of Mutex 1

Then we need to press the “New Object >” button and select the semaphore. The properties of it should be the following (Figure 17).

Properties of the semaphore
Figure 18 - Properties of the semaphore

Let’s give the name of the semaphore as “Semaphore 1” and the symbol as “semaphore1”. Also, let’s change the initial count from 0 to 1. This means that now the semaphore will be available at once, and there is no need to wait while some thread implements the put operation. This corresponds to the initial state of the mutex from the previous example.

There are all changes in the project configuration, so let’s press the “Generate Project Content” button and switch to the “thread1_entry.c” file.

#include "thread1.h"

/* Thread 1 entry function */

void thread1_entry(void)

{

/* TODO: add your own code here */

while (1)

{

if (R_BSP_PinRead(S1) == BSP_IO_LEVEL_LOW) //If S1 button is pressed

{

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S1) == BSP_IO_LEVEL_LOW) //If S1 button is still pressed

{

while (R_BSP_PinRead(S1) == BSP_IO_LEVEL_LOW); //Wait while it is pressed

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S1) == BSP_IO_LEVEL_HIGH) //If S1 button is released

{

tx_semaphore_get(&semaphore1, TX_WAIT_FOREVER); //Get the semaphore

R_BSP_PinAccessEnable(); //Enable access to the IO registers

R_BSP_PinWrite(LED1, BSP_IO_LEVEL_HIGH); //Turn on LED1

R_BSP_PinWrite(LED2, BSP_IO_LEVEL_LOW); //Turn off LED2

R_BSP_PinAccessDisable(); //Disable access to the IO registers

}

}

}

if (R_BSP_PinRead(S2) == BSP_IO_LEVEL_LOW) //If S2 button is pressed

{

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S2) == BSP_IO_LEVEL_LOW) //If S2 button is still pressed

{

while (R_BSP_PinRead(S2) == BSP_IO_LEVEL_LOW); //Wait while it is pressed

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S2) == BSP_IO_LEVEL_HIGH) //If S2 button is released

{

tx_semaphore_put(&semaphore1); //Put the semaphore

R_BSP_PinAccessEnable(); //Enable access to the IO registers

R_BSP_PinWrite(LED1, BSP_IO_LEVEL_LOW); //Turn off LED1

R_BSP_PinWrite(LED2, BSP_IO_LEVEL_LOW); //Turn off LED2

R_BSP_PinAccessDisable(); //Disable access to the IO registers

}

}

}

}

}

As you can see, the program is not exactly the same. I’ve marked the changed lines with the green color.

Because a semaphore can’t be owned by any thread, now the meaning of the LEDs will be a bit different. They will indicate the last thread which successfully got the semaphore (line 18). The put operation (line 36) will turn off both LEDs regardless of the semaphore’s counter value (lines 38, 39).

Also, we need to take into account that the semaphore’s counter and the mutex’s ownership counter act in opposite ways. The put operation increases the first and decreases the second, while the get operation decreases the first and increases the second.

The content of the “thread2_entry.c” file is very similar.

#include "thread2.h"

/* Thread 2 entry function */

void thread2_entry(void)

{

/* TODO: add your own code here */

while (1)

{

if (R_BSP_PinRead(S3) == BSP_IO_LEVEL_LOW) //If S3 button is pressed

{

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S3) == BSP_IO_LEVEL_LOW) //If S3 button is still pressed

{

while (R_BSP_PinRead(S3) == BSP_IO_LEVEL_LOW); //Wait while it is pressed

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S3) == BSP_IO_LEVEL_HIGH) //If S3 button is released

{

tx_semaphore_get(&semaphore1, TX_WAIT_FOREVER); //Get the semaphore

R_BSP_PinAccessEnable(); //Enable access to the IO registers

R_BSP_PinWrite(LED2, BSP_IO_LEVEL_HIGH); //Turn on LED2

R_BSP_PinWrite(LED1, BSP_IO_LEVEL_LOW); //Turn off LED1

R_BSP_PinAccessDisable(); //Disable access to the IO registers

}

}

}

if (R_BSP_PinRead(S4) == BSP_IO_LEVEL_LOW) //If S4 button is pressed

{

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S4) == BSP_IO_LEVEL_LOW) //If S4 button is still pressed

{

while (R_BSP_PinRead(S4) == BSP_IO_LEVEL_LOW); //Wait while it is pressed

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S4) == BSP_IO_LEVEL_HIGH) //If S4 button is released

{

tx_semaphore_put(&semaphore1); //Put the semaphore

R_BSP_PinAccessEnable(); //Enable access to the IO registers

R_BSP_PinWrite(LED1, BSP_IO_LEVEL_LOW); //Turn off LED1

R_BSP_PinWrite(LED2, BSP_IO_LEVEL_LOW); //Turn off LED2

R_BSP_PinAccessDisable(); //Disable access to the IO registers

}

}

}

}

}

Here we just replace the S1 button with S3, and S2 button with S4, and swap LED1 and LED2.

Now let’s build and run the project and try to perform the same cases as with the mutex.

1. Press the S1 button, you will see that LED1 turns on, which means that Thread 1 successfully got Semaphore 1 because its initial value was 1 (Figure 18).

First pressing of the S1 button
Figure 19 - First pressing of the S1 button

As you can see, here the semaphore’s value in TraceX also has the previous value.

Press S1 several times, you will see that nothing visible happens. But there is a big difference with the mutex. After the second press of the S1 button, the thread suspends while attempting to get the semaphore, whose counter is 0 (Figure 19).

Suspension of Thread 1 after several presses of the S1 button
Figure 20 - Suspension of Thread 1 after several presses of the S1 button

Now press S2, you will see that nothing has changed because Thread 1 is suspended and can’t process the presses of S1 and S2. To unblock Thread 1 we can press the S4 button. This is another big difference with the mutex, as a semaphore can be put and get by any thread. After the first press of S4, Thread 1 will be resumed and the lines 19-22 will be implemented, turning on LED1 (Figure 20). And the second pressing of S4 (or S2 now) both LEDs will turn off.

Resuming Thread 1 by pressing the S4 button
Figure 21 - Resuming Thread 1 by pressing the S4 button

Try the same with buttons S3, S4, and S2 and make sure it works in the same way with Thread 2.

2. Now let’s see how the semaphore suspends the threads one by one. Press the S1 button and see that LED1 turns on. Then press S3 to implement the get operation with Thread 2. As you can see, nothing happens, because the semaphore’s counter value is already 0, so Thread 2 is suspended (Figure 21).

Suspension of Thread 2 after attempting to get Semaphore 1
Figure 22 - Suspension of Thread 2 after attempting to get Semaphore 1

Now, press S2, and see that LED1 turned off and LED2 turned on. This happened because pressing S2 increments the counter of the semaphore, and thus Thread 2 can now be resumed (Figure 22).

Resuming Thread 2 after putting Semaphore 1 by Thread 1
Figure 23 - Resuming Thread 2 after putting Semaphore 1 by Thread 1

Now press S1. This will suspend Thread 1 which now waits for the semaphore to be put. And finally if you press S4 to put the semaphore, it will resume Thread 1, and the LEDs will toggle again.

3. Try the previous case but press S1 initially several times, this will turn on LED1 then press S3. You will see that the LEDs didn’t toggle. This is because after the second press of S1 Thread 1 becomes suspended, and after pressing S3 Thread 2 also becomes suspended. And they will remain in this state for infinite time because there is no thread to put the semaphore and have them resume (Figure 23).

Suspension of both threads while attempting to get the semaphore
Figure 24 - Suspension of both threads while attempting to get the semaphore

This situation is very similar to the deadly embrace but here, only one semaphore blocks all threads.

4. Press the S2 or S4 button several times. Then press S1 and S3 one by one, you will see that LEDs toggle, and threads are not blocked (well, eventually they will be blocked when the total number of presses of S1 and S3 will exceed the number of presses of S2 and S4). This happens because pressing on S2 and S4 increments the semaphore’s counter almost infinitely (actually till 232-1) (Figure 24).

Semaphore 1 counter value after ten presses of the S2 button
Figure 25 - Semaphore 1 counter value after ten presses of the S2 button

And then pressing S1 or S3 decrements the counter. And while it doesn’t reach 0, the get operation is implemented successfully, and no thread is blocked (Figure 25, 26).

Semaphore 1 counter value after ten presses of S1 and S3
Figure 26 - Semaphore 1 counter value after ten presses of S1 and S3
Suspension of Thread 2 after the eleventh pressing on the S3 button
Figure 27 - Suspension of Thread 2 after the eleventh pressing on the S3 button

As you can see in these simple cases, even though the semaphore and mutex seem to be very similar, in fact their behavior differs significantly. Only in case 2, where threads one by one suspend and resume each other, works the same for mutexes and semaphores. Case 4 is totally different, and it demonstrates the specific applications of these services.

And that’s all I want to share about the mutexes in this tutorial. I know I didn’t consider the example of using “priority inheritance” but I promise I will think of a good example of its implementation in some of the next tutorials and show you.

In the next tutorial, we will talk about the interesting service called “event flags” which allows you to combine events which will resume a thread.

As homework, I suggest you experiment with the two programs which we have written today in order to better understand how semaphores and mutexes work, and in which cases which service is preferable.

Make Bread with our CircuitBread Toaster!

Get the latest tools and tutorials, fresh from the toaster.

What are you looking for?