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

แชร์
ฝัง
  • เผยแพร่เมื่อ 20 พ.ค. 2024
  • 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
  • วิทยาศาสตร์และเทคโนโลยี

ความคิดเห็น • 76

  • @yogimarkmac
    @yogimarkmac ปีที่แล้ว +5

    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!

  • @ksawery6568
    @ksawery6568 3 ปีที่แล้ว +9

    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.

  • @Gopalisaac
    @Gopalisaac 3 ปีที่แล้ว +5

    Shawn, thank you so much. You make one of the most concise and informative vids.

  • @ducdoanxuan9229
    @ducdoanxuan9229 2 ปีที่แล้ว +4

    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.

  • @mahamasamataman3087
    @mahamasamataman3087 2 ปีที่แล้ว +3

    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.

  • @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

  • @manimusicka2
    @manimusicka2 10 หลายเดือนก่อน

    Great great tutorial! Thank you so much

  • @MuhammadDaudkhanTV100
    @MuhammadDaudkhanTV100 3 ปีที่แล้ว +2

    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.

  • @jamesytterstene1721
    @jamesytterstene1721 3 ปีที่แล้ว +10

    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 ปีที่แล้ว +5

      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

  • @crs1325
    @crs1325 3 ปีที่แล้ว +2

    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 ปีที่แล้ว +1

      @@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("

  • @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?

  • @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

  • @yasir45982
    @yasir45982 ปีที่แล้ว

    Wow, Mutex is crazy thing

  • @_PathOfExile
    @_PathOfExile ปีที่แล้ว

    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?

  • @micheldufour2993
    @micheldufour2993 8 หลายเดือนก่อน

    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?

  • @tadm123
    @tadm123 2 หลายเดือนก่อน

    Anyone else having issues running the solution that is in the solution sheet? I'm getting when typing an integer "assert failed: 0x4008883a :832 ()"

  • @piconano
    @piconano 3 ปีที่แล้ว +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 ปีที่แล้ว +1

      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 ปีที่แล้ว +1

      @@ShawnHymel Excellent. Thanks for the quick reply and these videos.

  • @SabinJohn
    @SabinJohn 2 ปีที่แล้ว

    What is the difference between a mutex and a binary semaphore?

  • @pruthvijagadeesh5158
    @pruthvijagadeesh5158 3 ปีที่แล้ว +1

    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

  • @claytube1958
    @claytube1958 3 หลายเดือนก่อน

    I had problems running this code using my feather board. I got it to "work" by increasing the stack size to 4096.

  • @tadm123
    @tadm123 2 หลายเดือนก่อน

    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

  • @fingerprint8479
    @fingerprint8479 3 ปีที่แล้ว +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 ปีที่แล้ว +2

      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 ปีที่แล้ว +1

      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 ปีที่แล้ว +1

      @@ShawnHymel OK, I will try to implement it and report back. Thanks again.

    • @GrahamStw
      @GrahamStw 3 ปีที่แล้ว +1

      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 ปีที่แล้ว +1

      @@GrahamStw Good idea...I didn't think of passing just the filenames with a queue.

  • @GrahamStw
    @GrahamStw 3 ปีที่แล้ว +1

    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 ปีที่แล้ว +1

      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 ปีที่แล้ว +1

    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 ปีที่แล้ว +1

      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 ปีที่แล้ว +1

      @@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!

  • @ytubeleo
    @ytubeleo 3 ปีที่แล้ว +1

    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 ปีที่แล้ว +1

      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 ปีที่แล้ว +1

      @@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).

  • @celery7810
    @celery7810 ปีที่แล้ว

    On esp-idf the solution crashes because the main task tries to take the mutex even though it already has it

  • @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

  • @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 ปีที่แล้ว +1

      @@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.

  • @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 ปีที่แล้ว +1

      ​@@BinGanzLieb exactly, that's why the solution on the Digikey website didn't work.

  • @marcg.272
    @marcg.272 3 ปีที่แล้ว +1

    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 ปีที่แล้ว +1

      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 ปีที่แล้ว +1

      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 ปีที่แล้ว +1

      @@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.

  • @ARUNKUMAR-ql1di
    @ARUNKUMAR-ql1di 3 ปีที่แล้ว

    how to use freertos in iot gate way please help me

  • @eddybash1342
    @eddybash1342 2 ปีที่แล้ว +1

    Warning :
    In case a task crashes then you get a happy deadlock. ;)

    • @son5233
      @son5233 2 ปีที่แล้ว

      Welcome to scoped lock :)

  • @bernard2735
    @bernard2735 3 ปีที่แล้ว +1

    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 ปีที่แล้ว +1

      @@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 ปีที่แล้ว

    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 😂