How to use the volatile keyword in C?

Sdílet
Vložit
  • čas přidán 20. 06. 2024
  • Patreon ➤ / jacobsorber
    Courses ➤ jacobsorber.thinkific.com
    Website ➤ www.jacobsorber.com
    ---
    What is the volatile keyword in C, and how does it work?
    Some of you for a video about "volatile"- a keyword that concerns and mystifies a lot of beginning programming students. I hope this video demystifies it for you, with some simple examples.
    Related Videos:
    Threads ( • How to create and join... )
    Signals ( • Sending and Handling S... )
    Optimizations ( • Make existing code run... )
    ***
    Welcome! I post videos that help you learn to program and become a more confident software developer. I cover beginner-to-advanced systems topics ranging from network programming, threads, processes, operating systems, embedded systems and others. My goal is to help you get under-the-hood and better understand how computers work and how you can use them to become stronger students and more capable professional developers.
    About me: I'm a computer scientist, electrical engineer, researcher, and teacher. I specialize in embedded systems, mobile computing, sensor networks, and the Internet of Things. I teach systems and networking courses at Clemson University, where I also lead the PERSIST research lab.
    More about me and what I do:
    www.jacobsorber.com
    people.cs.clemson.edu/~jsorber/
    persist.cs.clemson.edu/
    To Support the Channel:
    + like, subscribe, spread the word
    + contribute via Patreon --- [ / jacobsorber ]
    + rep the channel with nerdy merch --- [teespring.com/stores/jacob-so...]
    Source code is also available to Patreon supporters. --- [jsorber-youtube-source.heroku...]
    Want me to review your code?
    Email the code to js.reviews.code@gmail.com. Code should be simple and in one of the following languages: C, C++, python, java, ruby. You must be the author of the code and have rights to post it. Please include the following statement in your email: "I attest that this is my code, and I hereby give Jacob Sorber the right to use, review, post, comment on, and modify this code on his videos."
    You can also find more info about code reviews here.
    • I want to review your ...

Komentáře • 108

  • @JacobSorber
    @JacobSorber  Před 4 lety +43

    A few of you have pointed out that volatile does not provide atomicity and shouldn't be used for atomicity. This is absolutely correct. My point in using threads in this example was simply to illustrate that threads can cause the compiler to optimize out your variables.
    For atomicity, please use locks, synchronized variables (if you're using java), or atomic datatypes if your language/compiler/platform supports them.

  • @bastawa
    @bastawa Před 4 lety +104

    Its very crucial to use volatile when reading micro controllers inputs

    • @JacobSorber
      @JacobSorber  Před 4 lety +32

      Yeah, that's really where I end up using volatile the most.

    • @bastawa
      @bastawa Před 4 lety +7

      @@JacobSorber This actually made your video even more valuable, because when reading microcontroller input if you forget to use volatile, you will know there's something wrong with the code regardless of compiler flags / build settings. Great video as always!

    • @cainabel2553
      @cainabel2553 Před 20 dny

      For MMIO?
      Compilers often have compiler specific primitives for that, and they then adapt each other primitives to make them portable.

    • @cainabel2553
      @cainabel2553 Před 20 dny

      @@bastawa If you compile at the lower possible optimization level, or in fully debug-able mode, no, you won't feel the missing volatile.

  • @ABaumstumpf
    @ABaumstumpf Před 3 lety +13

    I think a slightly better explanation in this case is how it is described in the c++ standard:
    Volatile is a marking that says that any access of that variable (read or write) has effects that can not be seen from the code. So when reading from or writing to the variable it has to actually be done as it can change something else. The compiler is allowed to optimize it but just not reorder it in relation to other things with visible effects - visible is anything that changes the observed behaviour like i/o for example.
    And while it often is used for threads it SHOULD NOT be relied upon as there is no guarantee about timings or even data propagation. Having 2 threads signalling each other via a volatile is NOT guaranteed to work. It basically only works for memory-mapped IO.
    Prior to C11 there is no standard way to have defined behaviour for multithreaded access to any data. There are Compiler-extensions but they are of course not portable and do not support all hardware either. And with C11 "atomic" is the thing you want to use.

    • @Gilgamesh557
      @Gilgamesh557 Před 2 měsíci

      The reason why the confusion exists with multithreading is that it has a different meaning in java, where it additionally imposes memory barriers, effectively making reads and writes atomic.
      However, as you wrote, it is clearly mentioned in the c++ guidelines that atomics should be used for synchronization in c++

  • @israeldlr4365
    @israeldlr4365 Před 3 lety +31

    I just found a video of this guy like an hour ago and this is my 6th in a row of very specific concepts that he manages to pull of crystal clear in under 6 minutes. I feel so blessed to find him because there is literally a hundred videos made with laptop microphones and hard to follow audio that just write jibber into the screen and give 20 minute obscure explanations without going nowhere.
    Seriously Reading the K&R book gave me a lot better understanding but even they start to babble like compilers in some paragraphs, the idea is so abstract the concept just gets lost without a concise example.
    This guy understands it so clearly and with so simple examples that just opens your mind and makes you go "was that all?".
    Einstein was indeed right when he said that if you cannot explain something plain simple you may not understand it very well yourself, granted this has not an universal application but oh my is just so evident in cases like this.
    Instant subscribe, I love this guy, this is the enlightenment I was looking to find among the sea of explanations.

    • @trichomaxxx
      @trichomaxxx Před 3 lety +4

      I come here often and always learn something new, his videos are of immense quality. To the point, easy to understand and without any hint of elitism.

  • @tceffect2353
    @tceffect2353 Před 4 lety +13

    The reason why the program worked when sleep was called was because the compiler has to reload all globals from memory to registers after a function is called(unless the compiler can prove that the function can't access or modify the global)(library functions like sleep can't be proved to not modify a global unless they are declared inline because they are in a different translation unit). The reasoning behind this rule is that when the compiler compiles the caller function it doesn't know if the callee modifies the global variable, so it needs to write the global to memory if it was modified before the function is called, and load it back from memory (into a register) once the function returns(this is one of many reasons that globals make code optimization worse).

    • @JacobSorber
      @JacobSorber  Před 4 lety

      Thanks.

    • @mogenshansen7210
      @mogenshansen7210 Před 2 lety

      Og does not work - the program has data race, which is undefined behaviour.
      The worst case of undefined behaviour is when it is not obvious that the program is broken

    • @unperrier5998
      @unperrier5998 Před 2 lety +1

      indeed. Looking at the disassembly, when an external function is called, the compiler includes fetching and comparing the "done" variable.
      If the function is not external (for example we can use a recursive function like factorial, it will not be optimized) then we see the function being called, but the fetch and compare isn't.
      I've tried building a shared library that has a very simple function returning a constant, and when calling that function instead of sleep() the compiler removes optimization.
      Even when "done" is made static, compilers (gcc and clang) don't optimize away the fetch and compare.
      So it's clearly because we make an external function call.

  • @bullsvip
    @bullsvip Před 3 lety +11

    This exact question came up on my Qualcomm internship interview and I got the offer! Thanks for making this video. I would not have been able to answer it otherwise. I'm glad I found this vid by chance before the interview

    • @JacobSorber
      @JacobSorber  Před 3 lety +2

      Congrats! Glad it was helpful!

    • @tceffect2353
      @tceffect2353 Před 2 lety

      I work at Qualcomm on the GPU compiler team. What team in the company to you work on?

  • @EmanuelZimmermann
    @EmanuelZimmermann Před 3 lety +2

    I had to pause the video to leave you a message. I just cannot stress how good the content is. Thank you. Been watching your videos like Netflix while I have supper.

  • @VTdarkangel
    @VTdarkangel Před 4 lety +6

    I always wondered what that was for. I was told to use volatile for data input addresses for my intro embedded class in school, but was never really told why. I figured it had something to do with what you described, but wasn't sure.

  • @paedestrian
    @paedestrian Před 3 lety

    Very well explained along with an easy to understand example. Thank you very much

  • @barsvelioglu2276
    @barsvelioglu2276 Před 3 lety

    Clear explanation.
    Tells the compiler not making any assumptions about the property with the word volatile

  • @shivakaramadi9444
    @shivakaramadi9444 Před 4 lety +2

    Great work! Thank you so much for explaining it with example so easy to understand.

  • @laminekaba3064
    @laminekaba3064 Před 4 lety +3

    Thanks Jacob for your quick response about my request. I used volatile to catch signals on my Shell project.
    After reading many books and tutorials about volatile, this one is straight forward and well explained.
    Also noticed your videos are getting better. Good job.

    • @JacobSorber
      @JacobSorber  Před 4 lety +1

      You're welcome. It was an easy one to throw together quickly (examples were short) and seemed to fit with the embedded videos I've been making (and am planning to make).

  • @ayanarif1
    @ayanarif1 Před 2 lety

    That was really amazing man! Thanks you for this video. Please keep posting more videos

  • @tanmayagarwal8513
    @tanmayagarwal8513 Před 2 lety +2

    Ur video made much more sense than any other video! Thanks a lot! U earned one more subscriber!

  • @taragnor
    @taragnor Před 3 lety

    Thanks. I was always confused when you'd use volatile before this video. That was a great explanation.

  • @Universaeducation
    @Universaeducation Před 2 lety

    Thank you so much. Very well explained.

  • @Steezylegs_juri
    @Steezylegs_juri Před 7 měsíci

    very good explanation thanks

  • @Adem-ur2di
    @Adem-ur2di Před 4 lety +2

    thank you very much Mr.Jacob ,
    Your videos are really very useful and Point shot ! I am working on embedded systems and I recommend you to the people and I hope that You can post more videos! About embedded systems, Rtos , Linux .. file systems Compilers ....etc ☺️👍 Thanks again

  • @spaceinvader8892
    @spaceinvader8892 Před 3 lety

    Cool explanation & cool example.

  • @misanthropicmusings4596
    @misanthropicmusings4596 Před 3 lety +1

    almost a year in the future -- this is a great explanation of volatile. I saw it in some code and the Microsoft documentation was a little confusing. Thanks!

  • @dl4006
    @dl4006 Před 4 lety +13

    Love your channel, you're doing great work. Keep it up!

  • @rafalmichalski4893
    @rafalmichalski4893 Před 4 lety +1

    Great material. Thanks Jacob !

  • @chezhiyanspartan5080
    @chezhiyanspartan5080 Před rokem

    clear as crystal!

  • @momoanddudu
    @momoanddudu Před 4 měsíci

    volatile is intended for memory mapped registers - a mechanism for CPUs to communicate with I/O devices. This means two important things. First, every access to a volatile has to go all the way to the memory (read: it may not be cached), making accesses to volatile slower than non-volatile variables. Second, access to volatiles may not be reordered within a given thread, as a device might react differently depending on the order in which registers are accessed.
    [To simplify things, say the protocol is for the CPU to write to a disk drive's R1 a sector number, to R2 a RAM address, to R3 whether to write from memory to disk or read from disk to memory, and the write to R3 triggers the operation. The programmer would write to R1 & R2 first, and R3 last, and the compiler & CPU must keep the order, or the disk drive might trigger the operation with the wrong values in R1 & R2. Neither has to care about any other variables, unless used to compute the values stored to the volatile variables.]
    C++ atomics *are* meant for synchronization, and therefore behave differently. If a program runs on a CPU which has cache coherency protocols, every access to an atomic has to go to the cache, but not necessarily to the RAM. More importantly, atomics are memory barriers / fences, meaning not only access to atomic variables ordered, access to all other variables is ordered as well. If a thread writes to an atomic variable with memory order release fence, the compiler and CPU have to guarantee every memory access prior to it in the source code must occur before it when the program executes. Reading from an atomic variable with memory order acquire means every memory access after it in the source code must occur after it when the program execute, and more specifically if a variable was read into a register prior to it, it must be reread again after it.

  • @finmat95
    @finmat95 Před rokem +2

    "Volatile variables everywhere? this is the worst code i've never seen"
    "Yea but it runs!"

    • @TheRojo387
      @TheRojo387 Před 4 měsíci

      Reserve "volatile" for variables dependent on user input or IPC.

  • @ajidaniel8818
    @ajidaniel8818 Před 4 lety +2

    Dude ... You are the best

  • @davidgillies620
    @davidgillies620 Před 5 měsíci

    In addition to not providing atomicity, the compiler is free to reorder non-volatile memory accesses around the volatile access. This can introduce some really subtle bugs. In C++ the atomics work as expected (as a memory barrier), and you also have std::memory_order/std::atomic_thread_fence to really nail down your memory access semantics.

  • @tusharghadge5549
    @tusharghadge5549 Před 3 lety

    Thank you

  • @anonanon3066
    @anonanon3066 Před 3 lety

    Awesome! Thanks!

  • @sumitbhosale3401
    @sumitbhosale3401 Před 4 lety +1

    Perfect Video. Nice Explanation. But Waiting For HashTable. Thank You

  • @KangJangkrik
    @KangJangkrik Před 2 lety

    TL;DW use volatile if that global variable will be accessed by other thread, signal handler, or interrupt (in microcontroller like Arduino).
    Thanks again Jacob Sober, you save my day
    EDIT: This video is not too long, it just perfect 👌 I want to make it shorter to help other beginners

  • @leanobajio
    @leanobajio Před 4 lety +4

    Wow! That was very accessible information about C. Great work!

  • @shayitzhakey593
    @shayitzhakey593 Před 3 lety

    Amazing

  • @mohamednabil1709
    @mohamednabil1709 Před 4 lety +2

    @jacob. This is my first CZcams comment. But I just wanted to say you awesome

  • @kathiravankathir3089
    @kathiravankathir3089 Před 4 lety

    Thanks.

  • @almosteasy9590
    @almosteasy9590 Před 4 lety +2

    It will nice if you make video on Bit Masking. :)

  • @omaral-wadi8746
    @omaral-wadi8746 Před 4 lety +1

    شكرا

  • @TheRojo387
    @TheRojo387 Před 4 měsíci

    In short, volatile is useful for handling modification by input, be it by IPC or by the user.

  • @AlejandroAnzolaAvila
    @AlejandroAnzolaAvila Před 2 lety

    I just noticed that a lot of problems that I had while I was doing for a research project that involved pthreads and shared resources could have worked if I had used this keyword, too bad that I found out about it 3 years after

    • @noxagonal
      @noxagonal Před rokem +1

      Just in case you didn't know, you really shouldn't use volatile between threads at all. To synchronize execution use mutexes and to share data between threads on the fly, use atomics. Volatile is only there to make sure memory for the variable doesn't get optimized away and that the reads and writes are fetched from memory, this does not guarantee CPU cache coherency between threads, which may lead one thread to see an incorrect value for a long time until the changes propagate up the CPU cache levels.
      EDIT: Also I forgot... The compiler and the CPU are allowed to reorganize code and instructions to make execution more efficient or faster. This also means that if you use a volatile variable to flag completion, it may be toggled before the threaded function has finished writing all the data into memory, which may lead to another thread reading incorrect results. I think it's obvious why this is a bad idea.
      The only real use for volatile nowadays is with microcontrollers with only one thread, where a value may be changed by an outside condition, eg, physical pin taken high or low.

  • @islandcave8738
    @islandcave8738 Před 3 lety

    In the allegro game library, the mouse position is defined as volatile.

  • @J_Tanzanite
    @J_Tanzanite Před 4 lety +5

    Your upload schedule is volatile, that explains why notifications don't work!
    Jk, love your stuff, keep it up man! cx

  • @murtazahussain6301
    @murtazahussain6301 Před 3 lety

    In embedded systems, I always use volatile with variables which are changed in ISRs.

    • @wiktorwektor123
      @wiktorwektor123 Před 2 lety +1

      This is logical, because you don't know when interrupt can happen and as a result variable can change outside of main loop.

  • @EduardoMedinaEdlinks
    @EduardoMedinaEdlinks Před 3 lety

    Why didn't I find this channel before 😭😭😭?

  • @kvnagendra5354
    @kvnagendra5354 Před 4 lety +4

    Hey, plz tell me the name of the intro music

    • @kuler6892
      @kuler6892 Před 3 lety +1

      it's the song "urban lullaby" and it's from the youtube audio library. It's available for use by anyone on youtube without having to worry about copyright.

  • @24680kong
    @24680kong Před rokem +1

    As a novice programmer, it makes me unreasonably angry that I have to add a special keyword to ensure the compiler actually uses the code I wrote. I appreciate that compilers can help seed up code, but failing to check a function call seems like a pretty big oversight.

  • @ronnysherer
    @ronnysherer Před 4 lety

    I wonder if the compiler optimizes out the variable or caches it to a register.
    The behavior would be the same.

  • @ramcool82
    @ramcool82 Před 2 lety

    You should have shown assembly output of code for better understanding

  • @Handskemager
    @Handskemager Před 2 lety +1

    So don’t use volatile except when necessary to stop compiler optimizing breaking code.
    Is it good style to include a comment about why that particular variable is volatile or?

    • @JacobSorber
      @JacobSorber  Před 2 lety

      It sounds like a good practice to me.

  • @Gnisha
    @Gnisha Před 4 lety +8

    Please no not use volatile for multi-threaded programming, especially not for locks.
    Use the provided atomic structures by the language environment.
    The compiler and the CPU is allowed to reorder instructions and operations, leading to the very real possibility that the unlock is performed before the last access to the protected data structure happened. This could be fixed by compiler or memory barriers but this is not portable in the least. Using volatile for multi-threading & memory barriers should only ever be relevant in academic settings or when writing OS or LE functions.
    Volatile should be primarily used to access hardware register mapped into memory where the the hardware is owned and changes by the device.

    • @JacobSorber
      @JacobSorber  Před 4 lety

      Absolutely agree. I used threads to give an example of when the optimizer gets it wrong. Volatile is not a synchronization primitive.

    • @ashoksahu1926
      @ashoksahu1926 Před 4 lety

      @@JacobSorber Could you please touch upon what the other user is trying to say? Also if you could touch upon cases when we need locks and when we don't (if any). should we use volatile key word for shared variables which are protected by locks [but not for locks]

    • @JacobSorber
      @JacobSorber  Před 4 lety +1

      @@ashoksahu1926 I use volatile most often when I'm writing interrupt service routines for embedded systems. In those cases, my concern is not synchronization or avoiding race conditions, but just making sure that the compiler doesn't optimize away my variables thinking that they are never updated.

    • @bmuralikrishna8054
      @bmuralikrishna8054 Před 4 lety

      The best example of using volatile is when we are using shared memory.

  • @chrissaltmarsh6777
    @chrissaltmarsh6777 Před 2 lety

    If you are close to the metal (where the real fun is. IMHO) you need volatile. Because that byte you're pointing to really is going to change, so tell the compiler.

  • @hetsmiecht1029
    @hetsmiecht1029 Před 3 lety

    I have a question:
    If the two threads run on a different core, can't it happen that the variable stays in (not shared) cache and that the program is never notified of the change to the variable? Does volatile somehow prevent that or should you use atomic instructions that notify all cores of a change to the variable?
    Another question: what about pointers to pointers (e.g. int** varName)? Can you have a non-volatile pointer pointing to a volatile pointer pointing to a non volatile int? How does that look?
    int* volatile * varName;

  • @diwakar8815
    @diwakar8815 Před 4 lety

    hey jacob
    i am preparing for an entrance exam for a institute. The syllabus is 8086 microprocessor. can you recommend me any textbook that is good for learning 8086???

    • @JacobSorber
      @JacobSorber  Před 4 lety

      What specifically are you looking for? An assembly-level guide? Descriptions of the hardware features and instruction set architecture?

    • @diwakar8815
      @diwakar8815 Před 4 lety

      Complete guide

  • @edgarbonet1
    @edgarbonet1 Před 2 lety

    Re “I could [...] just replace this code with ‘while (true) {}’”: That sounds like an unsafe optimization to me: there is stuff from the C runtime that runs before main, what if that stuff touched `done'? If I were the compiler, I would play safe and replace that code with
    if (!done)
    while (true) {}

  • @JaSamZaljubljen
    @JaSamZaljubljen Před 4 lety +1

    1:32 did you mean "except" instead of "accept"

  • @dorquemadagaming3938
    @dorquemadagaming3938 Před 4 lety

    Why the optimization is not happening if you put a sleep() in between? I think not because of some super heuristics, but because you are calling a function there. And that function potentially has access to that global variable and may modify it. But in an empty loop - you never leave that loop or modify the variable yourself, so technically nobody else should... except for another thread or a signal handler.

  • @steinmil
    @steinmil Před 4 lety

    Good info. Bite sized , to the point.
    So first try and compile with all debug and no optimization enabled. As soon as you build the thing and use optimization then you get hangs and/or crashes. Any way to avoid this ? Can you at least get a heads up of what was optimized in the form of a report, from the compiler ? Or do you need to look at the disassembled code ? Or maybe write tests and pray they cover this situation.
    You would need to know the compiler pretty well to say that something is volatile and should be treated without optimization.

    • @JacobSorber
      @JacobSorber  Před 4 lety

      I don't know of a way to get the compiler to report what it optimized away. It would be a fairly useful tool to have, though. I usually just think about how variables are accessed. If a variable is changed in an interrupt service routine, for example, then it should probably be volatile. I'll let you know if I find something better.

  • @tosemusername
    @tosemusername Před 3 lety

    The only way to make this video perfect is to show the assembly generated.

  • @paulzupan3732
    @paulzupan3732 Před rokem

    I feel like the first example is less of a testament to the usefulness of the volatile keyword and more of a warning to just never use global variables

  • @manoj_kumar_sarkar
    @manoj_kumar_sarkar Před 4 lety +1

    comment

  • @glowiever
    @glowiever Před 3 lety

    java programmer is less scared though

  • @tomershechner
    @tomershechner Před 4 lety

    How's it in Africa?

    • @JacobSorber
      @JacobSorber  Před 4 lety

      I'm enjoying it. Great people. Cool things to see. And, it's just fun to mix things up.

    • @tomershechner
      @tomershechner Před 4 lety

      @@JacobSorber Wow. Sounds so cool. I wish I could've visit Africa someday..

  • @mogenshansen7210
    @mogenshansen7210 Před 2 lety +1

    The use of volatile in relation to thread syncronisation is just pure WRONG!
    It is a common misconception, which does not get more correct by being repeated.
    The examble exhibits undefined behaviour regardless of whether you use volatile or not, and that is the reason for the inconsistent behaviour that you demonstrate.
    Please either compile with ThreadSanitizer or read the language spec.
    This video should be removed!
    Volatile prevent the compiler from optimizing reads, so it is for accessing hardware that may change unrelated to the software - like a real time clock hardware circuit

    • @BowBeforeTheAlgorithm
      @BowBeforeTheAlgorithm Před 2 lety +1

      This is exactly right. It's even more confusing because Microsoft visual C++ compiler actually has a flag that transforms usage of the volatile keyword into atomic machine code.... dependent on your project settings, that volatile to atomic switcheroo is on by default. It's helpful but allows and reinforces incorrect usage.
      I've only seen one correct use of volatile in my career and it was when reading directly from hardware memory (an sd card device).