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