Thread: synchronization and volatile

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

    synchronization and volatile

    Hello everyone,


    Through myself study and previous discussion here, I share the conclusion here that,

    if some object is synchronized (mutex, critical section, etc.), there is no need to add volatile keyword.

    Here is the reference,

    (refer to section volatile, Critical Sections, and Race Conditions)

    http://www.ddj.com/cpp/184403766

    if my understanding is wrong or you have any other options, please feel free to add here.


    thanks in advance,
    George

  2. #2
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Now that's an area that's difficult to tread into to or explain since everyone seems to have different opinions.
    A lot of volatile stuff is compiler specific. Same goes for global variables, I guess.
    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.

  3. #3
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    I don't think you can categorically say that "If you use locking, the compiler will automatically not 'cache' variables" - volatile means that the compiler MUST NOT optimize accesses to the variable, because it may change "randomly".

    The compiler is perfectly able to cache a variable across function calls, although it's very unusual that the compiler will not store the value of a global (or class member) variable before calls to other functions, unless the compiler can inline the function and clearly sees that it's not harmful. But I'm pretty sure we could come up with a scenario where a variable can be updated in a way that is unsafe despite there being a lock in place. The compiler itself has no concept of locks as such, so unless you have further extensions involved that tells the compiler that "anything that comes from an external [to this function] memory location must be stored before and re-loaded after this function".

    Consider this:
    Code:
    class Gadget
    {
    public:
        void Wait()
        {
            Lock();
            while (!flag_)
            {
                Unlock();
                Sleep(1000); // sleeps for 1000 milliseconds
                Lock();
            }
            Unlock();
        }
        void Wakeup()
        {
            Lock();
            flag_ = true;
            Unlock();
        }
        ...
    private:
        bool flag_;
    };
    There is nothing saying that the compiler HAS to re-read flag_ in this code - it can still "remember" the value of flag across the Lock()/Unlock() functions, without any problem.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  4. #4
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    We just had a thread about it. It can't have gone farther than the second page, can it?

    Ah, yes. I was your own one.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Wow, you are really scraping the bottom of the barrel here. When you start turning to articles entitled "Volatile: Multithreaded Programmers Best Friend" in order to find evidence that volatile is not needed... I think you've run out of reasonable arguments.

  6. #6
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    And I can make the example in the MSDN case fail by removing the "Sleep(0)" in ThreadFunc1() - and using g++ -O2. It may well be like Elysia says that MS current compilers don't do very much optimization on global variables - I don't know WHY that would be, but perhaps it is to prevent problems with threads?

    volatile has it's uses - definitely so - but correct locks are ALSO necessary in threaded code.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  7. #7
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    locks and volatile are used for different aspects. You cannot say that if you use locks that you can ignore volatile, since that just isnt the case. There is always a special case where I may need to use volatile to explicitly force the runtime to use the value from memory, even if the variable is also protected by a lock, since in fact I may only lock the write portion of the vairable and let any number of threads read the variable without acquiring the lock. This is particularly an issue with DMA, where reading and writing to a variable that is mapped into a devices DMA region may have different effects. I always want to perform a read operation from the device when I access the address, while I may have to use locks to ensure that only one thread writes to the address at a time.

  8. #8
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by matsp View Post
    volatile has it's uses - definitely so - but correct locks are ALSO necessary in threaded code.
    I think the real point is, how can you even implement a lock properly in the first place without volatile?

    "volatile" and "const" are treated in the same syntactic category for a reason. You could write a correct program without const in all the "right" places, just as you could probably do so without volatile in all the "right" places. But I don't think anybody would argue that the former is preferred, even though some seem to argue for the latter.

  9. #9
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by brewbuck View Post
    I think the real point is, how can you even implement a lock properly in the first place without volatile?
    In assembly, for example
    Or you simply use the OS-provided locks.

    "volatile" and "const" are treated in the same syntactic category for a reason. You could write a correct program without const in all the "right" places, just as you could probably do so without volatile in all the "right" places. But I don't think anybody would argue that the former is preferred, even though some seem to argue for the latter.
    The point is, the semantics of const are quite simple, and there is no interaction with multi-threading. The semantics of volatile are complex, and there is interaction with multi-threading - only, it's not really defined.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  10. #10
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> When you start turning to articles entitled "Volatile: Multithreaded Programmers Best Friend" ...
    In regards to that article, one should read more than just the title before assuming that volatile has anything to do MT programming.
    Quote Originally Posted by David Butenhof
    The main intent of this article is to "hack" the C++ typing system. He's not
    presuming any semantics for "volatile" -- he's using it purely as a
    syntactic tag that's universally supported by C++ and not commonly used by
    applications. The idea is that one declares variables that are shared in a
    way that the compiler will complain if you reference them in a context
    where you don't hold a lock. That is, you lock in a way that removes the
    "volatile tag" from the data and allows you to reference it normally. While
    this idea is "sound" (in a bizarre sort of way) it's also a misuse of the
    compiler typing mechanism.

    This sort of thing isn't a bad idea, and could be used as an argument for an
    extensible "attributes" mechanism in C++, as in Lisp or some other
    languages.

    It also, however, suggests that having "volatile" for unlocked references
    helps the program -- and in general, for all the reasons David Schwartz has
    been trying to explain, that's silly, pointless, and dangerously wrong
    unless you're targeting a SPECIFIC implementation that you happen to KNOW
    provides a NONSTANDARD meaning to "volatile" which is relevant to threads.
    From: http://groups.google.com/group/comp....6be8f0b18bd62d
    I agree with post 54 as well, in regards to that article.

    >> There is nothing saying that the compiler HAS to re-read flag_ in this code
    The only way the compiler can NOT re-read flag_ is if it can guarantee that calling Lock() and Unlock() (and Sleep) will have no side effect to flag_. In most cases, these will eventually call into something like EnterCriticalSection() or pthread_mutex_lock() - which the optimizer can not analyze or make any assumptions about side effects.

    I read in a news group posting somewhere that "there are no compilers that hoist variables into a register across opaque function calls", due to the difficulty of guaranteeing there are no side effects. I don't know if that's true, but I do know that if Pthreads synchronization is used then the compiler can not perform this optimization and still be POSIX compliant.

    I would also say that it's very safe to assume this optimization would not be performed when using Enter/Leave CriticalSection - or any of the Win synchronization primitives. Otherwise, a *lot* of code out there would break.

    I found a great paper that talks about the deficiencies and pitfalls of MT programming. It also talks about other compiler optimizations that can cause problems in an MT environment.
    Threads Cannot be Implemented as a Library
    Author: Hans-J. Boehm, HP Labs
    (PDF file)

    Also interesting:
    Programming with Threads: Questions Frequently Asked by C and C++ Programmers
    Authors: Hans-J. Boehm, HP Labs & Paul McKenney, IBM


    gg

  11. #11
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks for your comprehensive reply and sample, Mats!


    I want to know what is your expected issues (if there are any) if we removed volatile keyword?

    Quote Originally Posted by matsp View Post
    I don't think you can categorically say that "If you use locking, the compiler will automatically not 'cache' variables" - volatile means that the compiler MUST NOT optimize accesses to the variable, because it may change "randomly".

    The compiler is perfectly able to cache a variable across function calls, although it's very unusual that the compiler will not store the value of a global (or class member) variable before calls to other functions, unless the compiler can inline the function and clearly sees that it's not harmful. But I'm pretty sure we could come up with a scenario where a variable can be updated in a way that is unsafe despite there being a lock in place. The compiler itself has no concept of locks as such, so unless you have further extensions involved that tells the compiler that "anything that comes from an external [to this function] memory location must be stored before and re-loaded after this function".

    Consider this:
    Code:
    class Gadget
    {
    public:
        void Wait()
        {
            Lock();
            while (!flag_)
            {
                Unlock();
                Sleep(1000); // sleeps for 1000 milliseconds
                Lock();
            }
            Unlock();
        }
        void Wakeup()
        {
            Lock();
            flag_ = true;
            Unlock();
        }
        ...
    private:
        bool flag_;
    };
    There is nothing saying that the compiler HAS to re-read flag_ in this code - it can still "remember" the value of flag across the Lock()/Unlock() functions, without any problem.

    --
    Mats

    regards,
    George

  12. #12
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks for your general comments, brewbuck!


    Any more detailed and specific comments of my original question?

    Quote Originally Posted by brewbuck View Post
    Wow, you are really scraping the bottom of the barrel here. When you start turning to articles entitled "Volatile: Multithreaded Programmers Best Friend" in order to find evidence that volatile is not needed... I think you've run out of reasonable arguments.

    regards,
    George

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


    I am interested to learn what do you mean *fail*?

    Quote Originally Posted by matsp View Post
    And I can make the example in the MSDN case fail by removing the "Sleep(0)" in ThreadFunc1() - and using g++ -O2. It may well be like Elysia says that MS current compilers don't do very much optimization on global variables - I don't know WHY that would be, but perhaps it is to prevent problems with threads?

    volatile has it's uses - definitely so - but correct locks are ALSO necessary in threaded code.

    --
    Mats

    regards,
    George

  14. #14
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks for sharing experience, abachler!


    I am not sure what will happen if you also use lock for read operations. I think it is more common for people to use lock for both read and write operation.

    Quote Originally Posted by abachler View Post
    locks and volatile are used for different aspects. You cannot say that if you use locks that you can ignore volatile, since that just isnt the case. There is always a special case where I may need to use volatile to explicitly force the runtime to use the value from memory, even if the variable is also protected by a lock, since in fact I may only lock the write portion of the vairable and let any number of threads read the variable without acquiring the lock. This is particularly an issue with DMA, where reading and writing to a variable that is mapped into a devices DMA region may have different effects. I always want to perform a read operation from the device when I access the address, while I may have to use locks to ensure that only one thread writes to the address at a time.

    regards,
    George

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


    Great reply with great referred discussion resource. :-)

    The discussion and the points in your below post looks not consistent -- maybe I am wrong to understand all of your points.

    I would like to confirm your points -- could you write in brief a couple of sentences please -- I think all people here is looking forward to it. :-)

    My guess is your point is no need to use volatile in synchronized area, and need to use volatile in non-synchronzied area when we access thread shared data. I am not sure whether my understanding is correct.

    Quote Originally Posted by Codeplug View Post
    >> When you start turning to articles entitled "Volatile: Multithreaded Programmers Best Friend" ...
    In regards to that article, one should read more than just the title before assuming that volatile has anything to do MT programming.
    From: http://groups.google.com/group/comp....6be8f0b18bd62d
    I agree with post 54 as well, in regards to that article.

    >> There is nothing saying that the compiler HAS to re-read flag_ in this code
    The only way the compiler can NOT re-read flag_ is if it can guarantee that calling Lock() and Unlock() (and Sleep) will have no side effect to flag_. In most cases, these will eventually call into something like EnterCriticalSection() or pthread_mutex_lock() - which the optimizer can not analyze or make any assumptions about side effects.

    I read in a news group posting somewhere that "there are no compilers that hoist variables into a register across opaque function calls", due to the difficulty of guaranteeing there are no side effects. I don't know if that's true, but I do know that if Pthreads synchronization is used then the compiler can not perform this optimization and still be POSIX compliant.

    I would also say that it's very safe to assume this optimization would not be performed when using Enter/Leave CriticalSection - or any of the Win synchronization primitives. Otherwise, a *lot* of code out there would break.

    I found a great paper that talks about the deficiencies and pitfalls of MT programming. It also talks about other compiler optimizations that can cause problems in an MT environment.
    Threads Cannot be Implemented as a Library
    Author: Hans-J. Boehm, HP Labs
    (PDF file)

    Also interesting:
    Programming with Threads: Questions Frequently Asked by C and C++ Programmers
    Authors: Hans-J. Boehm, HP Labs & Paul McKenney, IBM


    gg

    regards,
    George

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Synchronization Question
    By CyrexCore2k in forum C Programming
    Replies: 4
    Last Post: 05-01-2008, 02:51 AM
  2. MSDN volatile sample
    By George2 in forum C++ Programming
    Replies: 38
    Last Post: 01-05-2008, 06:59 AM
  3. question with multi-threaded programming
    By Hermitsky in forum C++ Programming
    Replies: 12
    Last Post: 12-02-2004, 06:30 AM