Thread: MSDN volatile sample

  1. #1
    Registered User
    Join Date
    May 2006
    Posts
    1,579

    MSDN volatile sample

    Hello everyone,


    In the MSDN volatile sample,

    http://msdn2.microsoft.com/en-us/lib...fd(VS.80).aspx

    I do not understand what is the purpose of the sample. I have tried to remove the keyword volatile, and the result is the same. :-)

    Any ideas?


    thanks in advance,
    George

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,656
    Remove the volatile, then try with an optimised build.

    Remove the volatile, then try with a system under heavy load.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks Salem,


    I have tried to make a optimized build (/O2), but still the same result. :-)

    Well, I think the sample is dependent on system environment. Could you help to describe in your understanding in multi-threading or heavy workload environment, what is the sample tries to demonstrate?

    Quote Originally Posted by Salem View Post
    Remove the volatile, then try with an optimised build.

    Remove the volatile, then try with a system under heavy load.

    regards,
    George

  4. #4
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    The thing is that the processor registers are much faster than memory, so the compiler sometimes tries to optimize by storing a value or variable in a register instead of reading/writing to memory. Of course, this has its own set of problems as well, since other threads, for example, can't modify that variable then, because each thread has its own set of processor registers.
    The keyword volatile tells the compiler that an external process, the OS or another thread might modify this variable and thus, the compiler cannot optimize it by storing it in a register.

    This may or may not be required. Usually, the compiler is smart enough to realize to or not to optimize away variables into registers when used in different threads, so the results may vary. But if you find that a thread tries to modify a variable and its value isn't changed in another thread, then you might need to add volatile to the variable.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  5. #5
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    Quote Originally Posted by Elysia View Post
    Usually, the compiler is smart enough to realize to or not to optimize away variables into registers when used in different threads, so the results may vary.
    Don't think so. C/C++ compilers don't know anything about threads. That's why there is the volatile keyword it tells the compiler that something unexpected might happen.
    Kurt

  6. #6
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Maybe and maybe not. But the compiler can assume all sorts of things. Tricky creatures.
    I haven't had the need to use the volatile keyword yet (and I have messed with plenty of threads), so that tells me that the compiler is intelligent beast and the volatile keyword is not always (if rarely?) needed.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  7. #7
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    You know that a c/c++ compiler doesn't know anything about the final executable that will created, don't you. It's the linker that does that job. The compiler sees only rather small parts ( modules ) so how would it ever know how that modules will finally be used ?
    There is no way for the compiler to know when it is allowed to optimize away a variable into a register. This info has to be given to the compiler, hence the volatile keyword.
    Quote Originally Posted by Elysia View Post
    I have messed with plenty of threads
    You were lucky.
    Kurt

  8. #8
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Hi Elysia,


    Even though in my specific environment, I can not find any differences. What do you expect the different results MSDN sample will be compared with adding/removing volatile keyword?

    Quote Originally Posted by Elysia View Post
    The thing is that the processor registers are much faster than memory, so the compiler sometimes tries to optimize by storing a value or variable in a register instead of reading/writing to memory. Of course, this has its own set of problems as well, since other threads, for example, can't modify that variable then, because each thread has its own set of processor registers.
    The keyword volatile tells the compiler that an external process, the OS or another thread might modify this variable and thus, the compiler cannot optimize it by storing it in a register.

    This may or may not be required. Usually, the compiler is smart enough to realize to or not to optimize away variables into registers when used in different threads, so the results may vary. But if you find that a thread tries to modify a variable and its value isn't changed in another thread, then you might need to add volatile to the variable.

    regards,
    George

  9. #9
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Hi Kurt,


    What different behaviors do you expect if we remove the volatile keyword in MSDN sample?

    If there is actually a different behavior, I suspect this is a bug -- optimization changes the multi-thread application's behavior.

    Quote Originally Posted by ZuK View Post
    Don't think so. C/C++ compilers don't know anything about threads. That's why there is the volatile keyword it tells the compiler that something unexpected might happen.
    Kurt

    regards,
    George

  10. #10
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by George2 View Post
    Hi Elysia,

    Even though in my specific environment, I can not find any differences. What do you expect the different results MSDN sample will be compared with adding/removing volatile keyword?

    regards,
    George
    I expect it will still work. The volatile variable is global and is used in several functions, so there is a good chance the compiler won't cache the value in a register at all. This may or may not be the case as there's no guarantee the compiler will do this. It may or it may not run. It may cause racing conditions, too.

    There's a good chance there might be racing condition for code such as this. If you wonder what a racing condition is, it's when two or more threads are fighting over the same data. Thread 1 might attempt to write data to some data that thread 2 uses currently. This does not always happen due to how threads are scheduled, thus it's called a racing condition. It can happen, but it doesn't always.

    It's difficult to say. Perhaps if we took a look at the assembly code, we could say more.

    And btw, you can edit your posts and do a multi-quote instead of posting a reply for each one you're quoting.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  11. #11
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks Elysia,


    This is the code which makes me confused in MSDN sample.

    Code:
        while (Sentinel)
        {
           Sleep(0);
        }
    I think when thread1 enters into the while loop, the value of Sentinel is true and it should invokes Sleep(0). Sleep(0) will wait until there is a signal. But in the program no other parties send thread1 a signal, and I think thread1 will wait forever to cause deadlock. :-)

    The only reason why there is no deadlock is when thread1 checks variable Sentinel, it happens to be false changed by thread2.

    Anything wrong with my previous analysis? Please feel free to correct me.


    Quote Originally Posted by Elysia View Post
    And btw, you can edit your posts and do a multi-quote instead of posting a reply for each one you're quoting.
    Good idea. I will try my best to follow in the future.


    regards,
    George

  12. #12
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Actually, Sleep(0) tells the OS that the thread yields its current execution time slice. So if a thread executes 10 ms, pauses for 5 ms, then executes for another 10 ms, the behavior will be 1 ms execution time, remaining 9 ms is yielded, so it waits 9 + 5 ms, then the thread will execute again and the process repeats itself until Sentinel is false.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  13. #13
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    This is another case where MS is confusing the hell out of everybody.....

    First, from a standards perspective, the volatile keyword is only useful when used with sig_atomic_t inside a signal handler. The standard does provide other "semantics" to volatile objects but those can't be applied to multi-threading. Basically, what the standard provides for volatile objects is a defined ordering of reads and writes with respect to other volatile objects. It's been a great source of debate as to what this really means to compiler implementors and what guarantees this gives the programmer. Because of this, the general consensus that I've gathered over the years is that any use of volatile other than "volatile sig_atomic_t" is implementation defined.

    A note on re-ordering.....

    There are two ways in which reads and writes to memory can become "re-ordered". The first way is that the compiler itself is free to re-order reads and writes. For example:
    Code:
    int v1, v2;
    ...
    v1 = 1;
    ...
    v2 = 2;
    In this example, the compiler is free to perform "v2 = 2" first. Now consider:
    Code:
    volatile int v1, v2;
    ...
    v1 = 1;
    ...
    v2 = 2;
    Now the compiler must perform "v1 = 1" first. It's also commonly accepted that a compiler implementation should translate each read or write of a volatile object into an actual read or write on the address bus. In other words, volatile objects shouldn't be cached in a CPU register.

    The other way in which reads and writes can be re-ordered arises from the hardware itself. On some architectures the processor can re-order the sequence of read and write instructions before executing those instructions. Also, on some architectures the cache-coherency algorithms used between cores can result in reads and writes appearing as if they were performed in a different order. Both of these issues are solved using "memory barriers". In general, a full (or fence) mbar means that all cores (and associated cache-lines) are "in-sync" with each other. There are also "acquire" and "release" mbars which are less restrictive than a full mbar.

    The great debate: to volatile, or not to volatile in MT.....

    So what does all this mean with respect to multi-threaded programming? Unfortunately, this has also been a great source of debate - which makes finding a definitive answer on the NET and news groups difficult. Under POSIX, things are more clear since POSIX defines memory coherency guarantees in a multi-threaded environment. Under POSIX, the only use of volatile is with sig_atomic_t in signal handlers or for memory mapped I/O (where each read or write must translate to a read or write on the address bus). If you're doing non-POSIX, multi-threaded programming, then you're in "implementation defined" territory when it comes to memory coherency guarantees.

    Defined Implementation of the Microsoft compiler....
    Starting with version 14.0.0 of the MS compiler, the volatile keyword was given additional semantics for use in a multi-threaded environment. From that version forward, reads from volatile objects generate an implied "acquire mbar" and writes to volatile objects generate an implied "release mbar". Note that this is only on architectures that support acquire and release mbars. MS-bashers out there like to suggest that MS made this move so that incorrectly written multithreaded device drivers and applications, which only used the volatile keyword as a means of shared variable synchronization, would simply start working correctly after re-compiling with version 14 or higher. In any case, here's a direct quote from a MS document on the use of volatile:
    Quote Originally Posted by Microsoft Document - Multiprocessor Considerations for Kernel-Mode Drivers
    If you look at the sample drivers shipped with the Windows DDK, you will see that volatile appears infrequently. In general, volatile is of limited use in driver code for the following reasons:
    • Using volatile prevents optimization only of the volatile variables themselves. It does not prevent optimizations of nonvolatile variables relative to volatile variables. For example, a write to a nonvolatile variable that precedes a read from a volatile variable in the source code might be moved to execute after the read.
    • Using volatile does not prevent the reordering of instructions by the processor hardware.
    • Using volatile correctly is not enough on a multiprocessor system to guarantee that all CPUs see memory accesses in the same order.
    Windows synchronization mechanisms are more useful in preventing all these potential problems.
    Conclusion? In my opinion, volatile has no use in multi-threaded programming. MSVC is the only compiler I know of that actually defines volatile semantics in a multi-threaded environment - and even then MS themselves admit that "volatile is of limited use".

    If you look closely at the MSDN page on volatile, there's a big chunk of the remarks section between "[Microsoft Specific]" and "[End Microsoft Specific]". What it doesn't tell you is that the example code demonstrates and relies only on the "Microsoft Specific" implementation of volatile. That example code is not even correct when using a MSVC version prior to 14! The best thing to do is to use proper synchronization primitives provided by the Windows SDK so that your code is correct regardless of the compiler being used......MSVC new and old, mingw, Borland C++, etc..., etc...

    gg

  14. #14
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Some people are making the observation that a global variable has to have a specific address in memory, otherwise how could different modules share the same variable? This is correct, but not the entire picture.

    Declaring a variable "volatile" means that the value in RAM must be up to date at all times. For instance, consider:

    Code:
    int x = 0;
    
    int frob_x()
    {
        x = (15 - x) * x + 1;
        x += 1;
        x /= 5;
    }
    While it is true that x has an address, being a global object, that does not mean that the value at this address is up to date. During the execution of frob_x() the compiler is free to cache the value of x inside a register until the function returns or calls another function. The value in memory does not necessarily go through the transformations seen in the code. That might happen only inside a register. Once frob_x() returns the value will be written back.

    This might seem okay, but consider two threaded functions using a condition variable. One thread wakes up the other. The woken thread tests the value of the condition -- if false, it goes back to sleep again.

    What if the condition is the value of a global variable? Both threads are running inside isolated functions. The compiler may have caused updates to the global condition to be done inside a register, since it is allowed to do so. The compiler has no idea about the existence of separate threads. So, when one thread sets "condition = 1;" this may just cause some register to change value. The other thread will never know of the change in value.

    The variable MUST be declared "volatile" to cause the compiler to store its value up to date at all times.

    Codeplug, you made an interesting post. But this does not change the fact that the in-memory value of a volatile variable is guaranteed to be equal to the semantic value of that variable at each sequence point, I believe that IS guaranteed by the standard. You are correct that volatile alone is not always enough to guarantee safety in multithread contexts, but it is definitely required.
    Last edited by brewbuck; 12-28-2007 at 04:22 PM.

  15. #15
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> You are correct that volatile alone is not always enough to guarantee safety in multithread contexts, but it is definitely required.
    volatile is not required in multi-threaded programming. You can use it if you choose, but then you are at the mercy of the compiler implementation.

    >> The variable MUST be declared "volatile" to cause the compiler to store its value up to date at all times.
    This is a correct statement, but not sufficient for MT contexts on all architectures (as you mention).

    In other words, any multi-threaded code that uses the volatile keyword can be re-written correctly without using volatile. The resulting code will then be much more "compiler agnostic" while still being correct. If you are relying on your compilers documented volatile semantics, then that code should be wrapped in #ifdef MY_COMPILER_IN_USE.

    The use of the volatile keyword in C++ brings up even more issues. Here's a good news group thread that summarizes the debate fairly well.
    http://groups.google.com/group/comp....7c34cd66113e7/

    gg

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. MSDN template sample
    By George2 in forum C++ Programming
    Replies: 12
    Last Post: 03-11-2008, 03:47 AM
  2. MSDN const_cast sample
    By George2 in forum C++ Programming
    Replies: 7
    Last Post: 12-17-2007, 08:32 AM
  3. MSDN OLE DB Sample Provider entry point
    By George2 in forum C++ Programming
    Replies: 0
    Last Post: 07-21-2007, 07:30 AM
  4. Replies: 16
    Last Post: 10-29-2006, 05:04 AM
  5. MSDN Searching Tips
    By jverkoey in forum A Brief History of Cprogramming.com
    Replies: 4
    Last Post: 10-19-2004, 04:51 AM