Skip to content

Commit 3a9fa4e

Browse files
committed
Added Mutex example
1 parent e321229 commit 3a9fa4e

File tree

1 file changed

+120
-0
lines changed
  • libraries/MultiThreading/examples/Mutex

1 file changed

+120
-0
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
This example demonstrates basic usage of FreeRTOS Mutually Exclusive Locks (Mutex) for securing access to shared resources in multi threading.
3+
Please refer to other examples in this folder to better understand usage of tasks.
4+
It is also advised to read documentation on FreeRTOS web pages:
5+
https://www.freertos.org/a00106.html
6+
7+
This example creates 2 task with same implementation - they write into shared
8+
variable and then read it and check if it is the same as what they have written.
9+
In single thread programming like on Arduino this is of no concern and will be always ok, however when
10+
multi threading is used the tasks execution is switched by the FreeRTOS and the value can be rewritten from other task before reading again.
11+
The tasks print write and read operation - each in their own column for better reading. Task 0 is on the left and Task 1 is on the right.
12+
Watch the writes and read in secure mode when using the mutex (default) as the results are as you would expect them.
13+
Then try to comment the USE_MUTEX and watch again - there will be a lots of mismatches!
14+
15+
Theory:
16+
Mutex is a specialized version of Semaphore (please see the Semaphore example for more info).
17+
In essence the mutex is a variable whose value determines if the mutes is taken (locked) or given (unlocked).
18+
When two or more processes access the same resource (variable, peripheral, etc) it might happen, for example
19+
that when one task starts to read a variable and the operating system (FreeRTOS) will schedule execution of another task
20+
which will write to this variable and when the previous task runs again it will read something different.
21+
22+
Mutexes and binary semaphores are very similar but have some subtle differences:
23+
Mutexes include a priority inheritance mechanism, binary semaphores do not.
24+
This makes binary semaphores the better choice for implementing
25+
synchronisation (between tasks or between tasks and an interrupt), and mutexes the better
26+
choice for implementing simple mutual exclusion.
27+
28+
You can check the danger by commenting the definition of USE_MUTEX which will disable the mutex and present the danger of concurrent access.
29+
*/
30+
31+
#define USE_MUTEX
32+
int shared_variable = 0;
33+
SemaphoreHandle_t shared_var_mutex = NULL;
34+
35+
// Define a task function
36+
void Task( void *pvParameters );
37+
38+
// The setup function runs once when you press reset or power on the board.
39+
void setup() {
40+
// Initialize serial communication at 115200 bits per second:
41+
Serial.begin(115200);
42+
while(!Serial) delay(100);
43+
Serial.printf(" Task 0 | Task 1\n");
44+
45+
#ifdef USE_MUTEX
46+
shared_var_mutex = xSemaphoreCreateMutex(); // Create the mutex
47+
#endif
48+
49+
50+
// Set up two tasks to run the same function independently.
51+
static int task_number0 = 0;
52+
xTaskCreate(
53+
Task
54+
, "Task 0" // A name just for humans
55+
, 2048 // The stack size
56+
, (void*)&task_number0 // Pass reference to a variable describing the task number
57+
//, 5 // High priority
58+
, 1 // priority
59+
, NULL // Task handle is not used here - simply pass NULL
60+
);
61+
62+
static int task_number1 = 1;
63+
xTaskCreate(
64+
Task
65+
, "Task 1"
66+
, 2048 // Stack size
67+
, (void*)&task_number1 // Pass reference to a variable describing the task number
68+
, 1 // Low priority
69+
, NULL // Task handle is not used here - simply pass NULL
70+
);
71+
72+
// Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
73+
}
74+
75+
void loop(){
76+
}
77+
78+
/*--------------------------------------------------*/
79+
/*---------------------- Tasks ---------------------*/
80+
/*--------------------------------------------------*/
81+
82+
void Task(void *pvParameters){ // This is a task.
83+
int task_num = *((int*)pvParameters);
84+
Serial.printf("%s\n", task_num ? " Starting |" : " | Starting");
85+
for (;;){ // A Task shall never return or exit.
86+
#ifdef USE_MUTEX
87+
if(shared_var_mutex != NULL){ // Sanity check if the mutex exists
88+
// Try to take the mutex and wait indefintly if needed
89+
if(xSemaphoreTake(shared_var_mutex, portMAX_DELAY) == pdTRUE){
90+
// Mutex successfully taken
91+
#endif
92+
int new_value = random(1000);
93+
94+
char str0[32]; sprintf(str0, " %d <- %d |", shared_variable, new_value);
95+
char str1[32]; sprintf(str1, " | %d <- %d", shared_variable, new_value);
96+
Serial.printf("%s\n", task_num ? str0 : str1);
97+
98+
shared_variable = new_value;
99+
delay(random(100)); // wait random time of max 100 ms - simulating some computation
100+
101+
sprintf(str0, " R: %d |", shared_variable);
102+
sprintf(str1, " | R: %d", shared_variable);
103+
Serial.printf("%s\n", task_num ? str0 : str1);
104+
//Serial.printf("Task %d after write: reading %d\n", task_num, shared_variable);
105+
106+
if(shared_variable != new_value){
107+
Serial.printf("%s\n", task_num ? " Mismatch! |" : " | Mismatch!");
108+
//Serial.printf("Task %d: detected race condition - the value changed!\n", task_num);
109+
}
110+
111+
#ifdef USE_MUTEX
112+
xSemaphoreGive(shared_var_mutex); // After accessing the shared resource give the mutex and allow other processes to access it
113+
}else{
114+
// We could not obtain the semaphore and can therefore not access the shared resource safely.
115+
} // mutex take
116+
} // sanity check
117+
#endif
118+
delay(10); // Allow other task to be scheduled
119+
} // Infinite loop
120+
}

0 commit comments

Comments
 (0)