Introduction to RTOS Part 6 - Mutex | Digi-Key Electronics

  Рет қаралды 93,436

DigiKey

DigiKey

Күн бұрын

A mutex, short for MUTual EXclusion, is a locking mechanism that is used to prevent other threads from interrupting or overwriting a section of shared data (such as a global variable or common buffer).
The solution to the challenge in the video can be found here: www.digikey.com/en/maker/proj...
Code for this video series (including demonstrations, challenges, and solutions) can be found here: github.com/ShawnHymel/introdu...
FreeRTOS implements mutexes and semaphores with queues, and operations to read, increment, and decrement these kernel objects are all atomic (meaning other threads cannot interrupt or modify the variables during those operations).
We use a mutex as a locking mechanism to protect a shared resource or critical section of code. When a thread or task takes a mutex, the value of the mutex is decremented from 1 to 0, and other tasks may not take the mutex while it is 0.
When the task is done working in the critical section, it gives the mutex back, which increments it from 0 to 1. This action of taking and giving a mutex allows only one thread to operate in a critical section of code at a time (it is mutually exclusive: no other threads may execute that section or take the mutex during that time).
In the video, we give an example of a race condition and how a mutex can be used to prevent it from happening. We provide a demonstration of using a mutex in FreeRTOS and then issue a challenge to use a mutex to allow parameters to be passed to tasks (note: this is a hack!).
Product Links:
www.digikey.com/en/products/d...
Related Videos:
Introduction to RTOS Part 1 - What is a Real-Time Operating System (RTOS)? - • Introduction to RTOS P... ​
Introduction to RTOS Part 2 - Getting Started with FreeRTOS - • Introduction to RTOS P... ​
Introduction to RTOS Part 3 - Task Scheduling - • Introduction to RTOS P... ​
Introduction to RTOS Part 4 - Memory Management - • Introduction to RTOS P... ​
Introduction to RTOS Part 5 - Queue - • Introduction to RTOS P... ​
Introduction to RTOS Part 6 - Mutex - • Introduction to RTOS P... ​
Introduction to RTOS Part 7 - • Introduction to RTOS P... ​
Introduction to RTOS Part 8 - • Introduction to RTOS P...
Introduction to RTOS Part 9 - • Introduction to RTOS P...
Introduction to RTOS Part 10 - • Introduction to RTOS P...
Introduction to RTOS Part 11 - • Introduction to RTOS P...
Introduction to RTOS Part 12 - • Introduction to RTOS P...
Related Project Links:
www.digikey.com/en/maker/proj...
Related Articles:
www.digikey.com/en/maker/vide...
Learn more:
Maker.io - www.digikey.com/en/maker
Digi-Key’s Blog - TheCircuit www.digikey.com/en/blog
Connect with Digi-Key on Facebook / digikey.electronics
And follow us on Twitter / digikey

Пікірлер: 78
@yogimarkmac
@yogimarkmac 2 жыл бұрын
I just have to chime in and give another thanks to Shawn for these awesome videos that are so clear and concise! Big thanks to Digi-Key for sponsoring these!
@Gopalisaac
@Gopalisaac 3 жыл бұрын
Shawn, thank you so much. You make one of the most concise and informative vids.
@ducdoanxuan9229
@ducdoanxuan9229 2 жыл бұрын
I wish you could suggest more challenges so that the audience could practice and understand the knowledge much better. Anw, thank you so much for your effort to create such good videos like this one.
@ksawery6568
@ksawery6568 3 жыл бұрын
Your tutorials are amazing, thanks a lot. I've been using mutexes in Linux desktop applications for a while now, but never really understood how they work.
@ppan0915
@ppan0915 2 жыл бұрын
Love it!! Thanks for your effort, it helps me a lot.
@user-vz7zh9dz6p
@user-vz7zh9dz6p 2 жыл бұрын
Best lecture! thank you Digi-key
@mahamasamataman3087
@mahamasamataman3087 2 жыл бұрын
Thank you for the series. In this one, I think you have a great opportunity to demonstrate starvation. If you extend your 'else' to do some work (aka is delayed), then you could get to the point that task A is the only one incrementing the shared resource -- hence task B is starving.
@manimusicka2
@manimusicka2 Жыл бұрын
Great great tutorial! Thank you so much
@MuhammadDaudkhanTV100
@MuhammadDaudkhanTV100 3 жыл бұрын
Fantastic
@shsh8553
@shsh8553 Жыл бұрын
Thanks very much for your wonderful tutorials. Regarding the solution of this challenge, I think it's not essential to set the Mutex Blocking Time to maximum in line 83.
@yasir45982
@yasir45982 Жыл бұрын
Wow, Mutex is crazy thing
@crs1325
@crs1325 3 жыл бұрын
Hi Shawn. Thank you for the nice work. I wanted to make sure "Serial.println(shared_var)" was "actually" being executed by different tasks, so I added Serial.println(pcTaskGetTaskName(xTaskGetCurrentTaskHandle())) to print the task name along the shared_var. It works beautifully for the "race-condition", showing which task is actually printing the 'right' and 'wrong' values. But ... When I did the same for the "demo-mutex", to my surprise only "Increment Task 1" executes, never "Increment Task 2". Maybe the compiler is optimizing something ... even with two functions with the random delay. At the end what worked was to add a small delay "vTaskDelay(1 / portTICK_PERIOD_MS);" right after "xSemaphoreGive(mutex);" Only then you see one task printing after another in sucession. Any thoughts?
@ShawnHymel
@ShawnHymel 3 жыл бұрын
Interesting...were both your task 1 and task 2 created with the same priority level (e.g. priority level 1)? It seems like your Task 1 is executing with a higher priority than Task 2. If they are the same priority, they should execute in a round-robin fashion.
@crs1325
@crs1325 3 жыл бұрын
@@ShawnHymel Hi again, Thanks for the promptly reply. So, no changes to priority or delays. In """esp32-freertos-06-demo-mutex.ino""", JUST changed """Serial.print(shared_var);""" with """Serial.print(shared_var); Serial.print("
@jamesytterstene1721
@jamesytterstene1721 3 жыл бұрын
Nice example, but you print the shared_var after you give the Mutex. The shared variable is updated correct but there is a risk of printing the wrong value. You should print the local_var to safe
@ShawnHymel
@ShawnHymel 3 жыл бұрын
Good catch--it's not completely thread safe, as the other task could interrupt just before the Serial.println(shared_var) line. Either printing local_var or putting the print line inside the critical section should work.
@alexkiravolkov5405
@alexkiravolkov5405 2 жыл бұрын
@@ShawnHymel Printing local_var also isn't safe as the other task may increment and print between xSemaphoreGive() and Serial.println(). The safe way is to move printing inside the critical section
@phizaics
@phizaics 2 жыл бұрын
I attempted your challenge problem but it wasn't working but the code seemed fine.. It looked like the blinkLed task was taking too long to start and just wasn't able to take the mutex in time so I has to use a little bit of delay in setup so that the task can start and take the mutex, copy the variable and return it to signal the setup function to finish.. I was only able to solve the problem this way, is there another way to do this without using delays?
@_PathOfExile
@_PathOfExile 2 жыл бұрын
This works like a charm in a project that has single .ccp file. But how is it going to be implemented, if tasks are scattered inside many cpp files cross the project?
@piconano
@piconano 3 жыл бұрын
Is it possible to put the user program to run on the same core as the WiFi on the ESP32? What happens if a core has nothing to do? Lower power consumption?
@ShawnHymel
@ShawnHymel 3 жыл бұрын
Great question! You should be able to pin any task to core 0 (the core generally used for the WiFi/Bluetooth stack). From my understanding, WiFi functions run as a bunch of tasks on core 0, so you'd probably want to be careful not to interrupt those too much (assuming you're using WiFi). Here's someone who had a similar question: github.com/espressif/arduino-esp32/issues/929 According to this post (esp32.com/viewtopic.php?t=7306), you can power down Core 1, which should save you some power.
@piconano
@piconano 3 жыл бұрын
@@ShawnHymel Excellent. Thanks for the quick reply and these videos.
@tadm123
@tadm123 3 ай бұрын
Hi Shawn, In the solution, I'm completely confused. So in the setup() we took the Mutex a second time with xSemaphoreTake(mutex, portMAX_DELAY). Just for the purpose of waiting the maximum amount of time (so that it is blocked), that's the only reason right? If this is the only reason why not just use a a simple delay function vTaskDelay(1000 / portMAX_DELAY)? Also, after we have run the second xSemaphoreTake(mutex, portMAX_DELAY).. since the mutex was released earlier, then the Mutex will be taken again returning pdTRUE, but I don't see a function afterwards to release it? Any help is appreciated
@rc4625
@rc4625 Жыл бұрын
Because you print the shared var after giving back the mutex, is it not possible that the other task could update the shared var before the print actually happens? Although probably won't happen in this example because of the delay in the critical section Edit: saw the code on GitHub fixes this
@pruthvijagadeesh5158
@pruthvijagadeesh5158 3 жыл бұрын
is it necessary that both the task has to run on the same core for the mutex method?
@ShawnHymel
@ShawnHymel 3 жыл бұрын
You can use semaphores and mutexes to protect resources across multiple cores, assuming all cores have access to shared memory. The ESP32 does have this shared memory, so one mutex should work for tasks running on both cores. I recommend reading this article to learn more about how ESP-IDF differs from FreeRTOS: docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/freertos-smp.html
@SabinJohn
@SabinJohn 2 жыл бұрын
What is the difference between a mutex and a binary semaphore?
@GrahamStw
@GrahamStw 3 жыл бұрын
What happens if a Task exits or is deleted whilst holding a mutex? Does FreeRTOS have any way of auto-releasing a mutex when a Task dies?
@ShawnHymel
@ShawnHymel 3 жыл бұрын
Good question! FreeRTOS apparently does not have a way to auto-release a mutex if a Task holding it is deleted (www.freertos.org/FreeRTOS_Support_Forum_Archive/May_2011/freertos_Delete_task_holding_a_mutex._Is_it_released_4546788.html). As a result, you should probably be careful about when/where you delete tasks.
@user-nt5qi2rm8y
@user-nt5qi2rm8y 3 жыл бұрын
I may have missed this, but how does task created? I am confused about the hack offered in the solution. it seems to me that the xTaskCreatePinnedToCore() function will enter the blinkLED() so it can yield the mutex. is it how it works? thank you.
@ShawnHymel
@ShawnHymel 3 жыл бұрын
That is correct. xTaskCreatePinnedToCore() creates a new task that runs the BlinkLED() function. The task running setup() waits for the mutex to be released by the newly created task running BlinkLED(). Hope that helps!
@user-nt5qi2rm8y
@user-nt5qi2rm8y 3 жыл бұрын
@@ShawnHymel you mentioned in lecture 2 that the scheduler is already running before the setup() function so this hack make sense now. Thanks!
@ShawnHymel
@ShawnHymel 3 жыл бұрын
@@user-nt5qi2rm8y Glad it helped!
@fingerprint8479
@fingerprint8479 3 жыл бұрын
Hi Shawn, great tutorial, thanks. I have an App that runs on a ESP32CAM. Task1, when triggered, takes a picture and stores it to SPIFFS. Task2 keeps checking SPIFFS to see if it finds new pictures, send them to FTP and deletes them. The conflict would be if Task2 finds a picture partially written to SPIFFS by Task1 and tries to send it to FTP while it is still incomplete. So I am using a Global variable fileName that is populated when Task1 takes the picture and saves it to SPIFFS and as soon as the save is done fileName is set to "". So Task2, before grabbing a picture for transfer to FTP it checks if the name of the picture on SPIFFS is equal to the global variable fileName. If it maches it means Task1 is still writing the file and FTP file transfer is postponed. Only Task1 writes to fileName, Task2 only reads although it has a big chance of concurrency as each task runs in its own core. Do I need to implement the strategy shown in this tutorial to my App? Thanks
@ShawnHymel
@ShawnHymel 3 жыл бұрын
If your global variable can be written atomically (i.e. one instruction cycle), then I think your global variable strategy will work fine (as you only have one task writing to it). However, I think this is the perfect opportunity to use a mutex or semaphore to notify other tasks. I believe the simplest solution would be to protect the new file reading and writing process (both functions) with a mutex.
@ShawnHymel
@ShawnHymel 3 жыл бұрын
If you try it, please let me know if it works! I'd love to hear about a mutex getting used in a real application like this!
@fingerprint8479
@fingerprint8479 3 жыл бұрын
@@ShawnHymel OK, I will try to implement it and report back. Thanks again.
@GrahamStw
@GrahamStw 3 жыл бұрын
Why not use a Queue here instead? Task 1 takes the image, writes it to a temp file, then once the file is written, it posts the filename to the Queue. It can then continue to take the next image and write to a new temp file. Meanwhile Task 2 is waiting on the Queue. When a filename arrives on the queue the task uploads that file then deletes the temp file. This split also makes it easier to test your tasks independently.
@ShawnHymel
@ShawnHymel 3 жыл бұрын
@@GrahamStw Good idea...I didn't think of passing just the filenames with a queue.
@ytubeleo
@ytubeleo 3 жыл бұрын
Great video. But why not simply disable interrupts, check the global value, write to it and re-enable interrupts? Could this be what a mutex actually does behind the scenes?
@ShawnHymel
@ShawnHymel 3 жыл бұрын
Good question! You can disable interrupts with taskENTER_CRITICAL() and taskEXIT_CRITICAL() (it's a little different on the ESP32, which I'll show in a future episode). Using a mutex is different--a mutex allows other tasks and interrupts to run but won't let them take the same mutex. This way, you have several options when it comes to protecting shared resources.
@ytubeleo
@ytubeleo 3 жыл бұрын
@@ShawnHymel I guess what I'm getting at is that by entering a critical section to read and write a global variable and then immediately exiting the critical section would solve the problem without using a mutex and I imagine it would be faster than calling a separate function. And although other tasks and interrupts would be blocked for a couple of cycles, that's exactly what we were trying to do. This led me to think that maybe this is exactly what FreeRTOS's functions do internally, or do you know if there is any other way they could achieve this? Thanks. Probably getting a bit too detailed, sorry.
@ShawnHymel
@ShawnHymel 3 жыл бұрын
@@ytubeleo Yes, that is what FreeRTOS is doing when you use taskENTER_CRITICAL()--it disables interrupts (up to a given interrupt priority level). You could avoid a function call if you disabled them manually, but doing it that way would be unique to the processor (i.e. the code would not be as portable as it would be with FreeRTOS).
@fingerprint8479
@fingerprint8479 3 жыл бұрын
Hi Shawn, I have a problem trying to implement Mutex. ESP32, 2 tasks each on its own core. One OLED display, both tasks posting messages to the display. One function handles printing to the display. I want to use Mutex to avoid the scrambling of text that sometimes happen with messages. How can tasks know the display is taken and the task should wait till the display is available? void printToDisplay(String text){ // take mutex xSemaphoreTake(xMutex, portMAX_DELAY); static byte lineCounter = 0; static String displayText[8]; if(lineCounter>7){ for(int i=0;i
@ShawnHymel
@ShawnHymel 3 жыл бұрын
The way you're using the mutex seems correct to me...it forces any other tasks to wait at the top of the function until the task holding the mutex is done. Are you seeing the text scrambling occurring now? What happens if you only have one task call the printToDisplay() function? Does it still work?
@fingerprint8479
@fingerprint8479 3 жыл бұрын
@@ShawnHymel Hi Shawn, at the end I found the problem only happens with tasks running on core0 sending messages to the display. If I move both tasks to core1 it works ok. If I put both tasks to run on Core0 it gets really worse. It seems to be some strange timing problem I assume because Core0 also runs other things. My conclusion is I will have to live with it as the text gets organized when the message is not coming directly from the task running on core 0. Thanks
@ShawnHymel
@ShawnHymel 3 жыл бұрын
@@fingerprint8479 Interesting...if core 0 is running other tasks, then possibly, but I don't see how that interferes with mutex operation. If you disable everything else on core 0, do you still run into the same issue?
@fingerprint8479
@fingerprint8479 3 жыл бұрын
@@ShawnHymel For this test the only two tasks are as below: void task02( void * parameter ){ while(1){ printToDisplay("Task02.............."); vTaskDelay(2500); } } and... void task01( void * parameter ){ while(1){ printToDisplay("Task01.............."); vTaskDelay(1500); } } The only configuration for the problem not to manifest is to put both tasks to run on core1. If any or both are assigned to core0 the problem shows up. The behavior on the display is something like: Task01.............. Task02.............. .Task01............. .Task02............. ..Task01............ And eventually it rights itself but not before it get much worse, it is somewhat cyclic. But, as I said, if both tasks run on core1 no problem.
@apocalye24
@apocalye24 Жыл бұрын
It seems that current version of ESP32 FreeRTOS require that mutex to be taken and given by the same thread or it will triger an assert failed (xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle)
@BinGanzLieb
@BinGanzLieb Жыл бұрын
thats the right way
@claytube1958
@claytube1958 5 ай бұрын
I had problems running this code using my feather board. I got it to "work" by increasing the stack size to 4096.
@mohamedhassanin6054
@mohamedhassanin6054 2 жыл бұрын
The challenge solution didn't work with me since the blinkLed task gives a mutex meanwhile it's not the holder, so It ended up in a deadlock. I used binary semaphore instead.
@BinGanzLieb
@BinGanzLieb Жыл бұрын
the Task who took the mutex has to release it in the end, no one else.
@mohamedhassanin6054
@mohamedhassanin6054 Жыл бұрын
​@@BinGanzLieb exactly, that's why the solution on the Digikey website didn't work.
@marcg.272
@marcg.272 3 жыл бұрын
There is one thing I dont understand. Why can't I just use a global variable instead of the Mutex and set it 0 once the shared ressource is being used?
@ShawnHymel
@ShawnHymel 3 жыл бұрын
You could--you just have to be sure that reading and writing to that global variable happens in a single instruction cycle, or you could end up with a race condition.
@GrahamStw
@GrahamStw 3 жыл бұрын
You need to have an atomic test-and-set operation for that, otherwise Task 1 could read the variable was available, then Task 2 could interrupt and also read it is available and claim it. When Task 1 resumes it will think it is still available.
@ShawnHymel
@ShawnHymel 3 жыл бұрын
@@GrahamStw Ah, right--you are correct. I thought Marc was asking about using a global variable as the resource (rather than as the mutex). The atomic test-and-set operation is necessary for mutex operation.
@celery7810
@celery7810 Жыл бұрын
On esp-idf the solution crashes because the main task tries to take the mutex even though it already has it
@tadm123
@tadm123 3 ай бұрын
Anyone else having issues running the solution that is in the solution sheet? I'm getting when typing an integer "assert failed: 0x4008883a :832 ()"
@ekuzmenko
@ekuzmenko 14 күн бұрын
My solution was to do the following at the end of the setup function, this prevents the function from ending and the variable that's passed from going out of scope: while (xSemaphoreGetMutexHolder(mutex) == NULL) {} Shawn's solution didn't work for me either for some reason, but this did.
@micheldufour2993
@micheldufour2993 10 ай бұрын
In the solution for RTOS 06, how could that work. If the mutex is taken in the setup task, how could it be give in the blinkLED task. For that happened, setup task must give back the mutex. This is the HACK?
@eddybash1342
@eddybash1342 2 жыл бұрын
Warning : In case a task crashes then you get a happy deadlock. ;)
@son5233
@son5233 2 жыл бұрын
Welcome to scoped lock :)
@ARUNKUMAR-ql1di
@ARUNKUMAR-ql1di 3 жыл бұрын
how to use freertos in iot gate way please help me
@bernard2735
@bernard2735 3 жыл бұрын
Moiré
@ShawnHymel
@ShawnHymel 3 жыл бұрын
It is. I wore that shirt for these few episodes, but I won't wear it starting with ep 8 because of that effect. In the future, I'll try adjusting my camera's distance to see if I can get rid of that effect :-/
@bernard2735
@bernard2735 3 жыл бұрын
@@ShawnHymel I actually meant to add ☺️ because it looked kind of cool. Without that it looked like I was dissing you. Sorry 😞
@ShawnHymel
@ShawnHymel 3 жыл бұрын
@@bernard2735 No worries...I think it's a fun effect, but it can be kinda distracting in a video :)
@fandusmercius723
@fandusmercius723 2 жыл бұрын
bro glowing
@michaelwatson7211
@michaelwatson7211 2 жыл бұрын
Usefull, but why make me do a hack in the tutorial? if I go through this course I will have a bunch of hacky reference code doing things in the wrong way
@remy-
@remy- 2 жыл бұрын
When MUtual EXclusions prevent Mutual Assured Destruction 😂
Introduction to RTOS Part 5 - Queue | Digi-Key Electronics
12:01
Дарю Самокат Скейтеру !
00:42
Vlad Samokatchik
Рет қаралды 7 МЛН
Simulating the Evolution of Rock, Paper, Scissors
15:00
Primer
Рет қаралды 526 М.
2 Retro Clocks with an ESP32 (Couldn't be simpler!)
5:37
Brian Lough
Рет қаралды 104 М.
What is a mutex in C? (pthread_mutex)
9:18
CodeVault
Рет қаралды 158 М.
FreeRTOS on the Raspberry Pi Pico (RP2040) Part 5: Semaphores
9:16
Learn Embedded Systems
Рет қаралды 10 М.
Building a Digital Music Player with I2S?! What is I2S! EB#45
10:24
КРУТОЙ ТЕЛЕФОН
0:16
KINO KAIF
Рет қаралды 5 МЛН
تجربة أغرب توصيلة شحن ضد القطع تماما
0:56
صدام العزي
Рет қаралды 51 МЛН