Thread: Casting away volatile

  1. #1
    Registered User
    Join Date
    Dec 2009
    Posts
    83

    Casting away volatile

    Hi.

    I have a lock-free singly linked list.

    Where it uses atomic instructions to update its pointers, all the pointers are volatile.

    Volatile prevents compiler optimization.

    There are times when I would like to use the list with only one thread at a time; I enter this state and exit this state, performing the necessary operations such that it is safe to begin single threaded operations and then to resume multi-threaded operations.

    What I want to do when in single threaded mode is cast away the volatile type qualifier so that the compiler can optimize.

    I am however not at all sure if this is permitted.

    To provide a concrete example, let's say I have a list state, and it contains a pointer to a list element. That pointer is volatile, the struct it points to is not volatile, i.e.

    struct list_element * volatile head_pointer;

    I wish to access this as if it were not volatile, as I'm in single threaded mode.

    Is it safe to do this? I am concerned I may be engaging in undefined behaviour.

    struct list_element *temp;

    temp = (struct list_element *) head_pointer;

    Where head_pointer is defined as above, where the pointer itself is volatile, but the struct is not.
    Last edited by Toby Douglass; 12-30-2016 at 11:07 AM.

  2. #2
    Old Took
    Join Date
    Nov 2016
    Location
    Londonistan
    Posts
    121
    Your only option in C is the C-style cast.

    Yes it should be safe to cast away the volatile qualifier on your pointers and C will allow you to do it as C is not particularly type-safe. A second thread can't change your pointers in a single-threaded app so it should be a safe type conversion.

  3. #3
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    From the C99 standard:
    6.7.3:5 If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.
    From Wikipedia: Volatile_(computer_programming)
    Despite being a common keyword, the behavior of volatile differs significantly between programming languages, and is easily misunderstood. In C and C++, it is a type qualifier, like const, and is a property of the type. Furthermore, in C and C++ it does not work in most threading scenarios, and that use is discouraged. In Java and C#, it is a property of a variable and indicates that the object to which the variable is bound may mutate, and is specifically intended for threading.

  4. #4
    Old Took
    Join Date
    Nov 2016
    Location
    Londonistan
    Posts
    121
    Whilst volatile was more for the days of interrupt handlers and for use where memory is shared across process boundaries, some interesting applications can arise in multithreaded code. It's main use is to stop optimisations reordering reads and writes to a memory location. This is C++ rather than C but it shows how the type qualifier can be useful to catch errors at compile time....
    Volatile the multithreaded programmers best friend.
    In C++ volatile is cast away sometimes once grabbing a critical section or a memory barrier. As in that section only one thread at a time can be running. It may well be undefined behaviour but it really ought to be a safe conversion. In this case the programmer knows more than the compiler which errs on the side of safety.

  5. #5
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    Quote Originally Posted by Hobbit View Post
    Whilst volatile was more for the days of interrupt handlers and for use where memory is shared across process boundaries, some interesting applications can arise in multithreaded code. It's main use is to stop optimisations reordering reads and writes to a memory location. This is C++ rather than C but it shows how the type qualifier can be useful to catch errors at compile time....
    Volatile the multithreaded programmers best friend.
    In C++ volatile is cast away sometimes once grabbing a critical section or a memory barrier. As in that section only one thread at a time can be running. It may well be undefined behaviour but it really ought to be a safe conversion. In this case the programmer knows more than the compiler which errs on the side of safety.
    I'm glad you were able to read and use my references. Do you see how that's better than just pulling an "answer" out of your ass? Still, until you prove it you're only assuming that the C++ example can be made to work in C.

  6. #6
    Old Took
    Join Date
    Nov 2016
    Location
    Londonistan
    Posts
    121
    C and C++ compilers treat volatile pointers the same way. Whilst strictly speaking it is undefined behaviour, that's just the standard erring on the side of safety. In this case the OP wants to use his code in a single-threaded environment where no other access to his pointers will be possible so optimisations can be allowed. Undefined or not it is safe to cast away volatile in this situation. It's simply an instruction to the compiler that says you can now cache this pointer in a register if the compiler sees that to be a sensible optimisation. Yes sure casting volatile away in a multithreaded application or where we are dealing with a memory mapped location can be deadly and shouldn't be done that is not the case here. The standard doesn't know what context the programmer wishes to cast away the volatile and so because in many situations it's not a good idea they left the behaviour undefined, but in this situation the programmer knows that there can be no other access to the pointer so the conversion is safe even though it is undefined behaviour. Strictly speaking LockingPtr relies on undefined behaviour yet it has been used in production code for more than 10 years until C++11 gave us a threading library and atomics.
    I didn't use your references by the way. Wikis are hardly authoritative and can be edited by anyone. I have used LockingPtr myself for a decade without issue despite the fact that it is strictly speaking undefined behaviour. I try to read all articles by Alexandrescu, Sutter, and Meyers. They are among the world's foremost experts on the language I use most.
    I was under the impression from the OP's post that he is a professional or decent amateur programmer who had profiled his code and seen the need to allow optimisations in this context. He certainly didn't appear to be a learner who I agree should be steered away from undefined behaviour. Of course the right answer is probably to rewrite the lock-free list so that it doesn't need to use volatile at all, but then it would probably no longer be lock-free.

  7. #7
    Registered User
    Join Date
    Dec 2009
    Posts
    83
    Quote Originally Posted by Hobbit View Post
    Of course the right answer is probably to rewrite the lock-free list so that it doesn't need to use volatile at all, but then it would probably no longer be lock-free.
    I don't think you can - the prototypes for atomic instructions specify volatile.

    Okay, I think I'm clear about this now.

    We can consider the classic case where casting away volatile leads to undefined behaviour; we have a volatile variable, we create a non-volatile pointer to it, and then modify the variable through the pointer. This is wrong. Here's the code;

    int volatile variable;
    int *temp;
    temp = (int *) &variable;
    *temp = 5;

    In this case we are using the wrong type of pointer - it does not match the underlying variable.

    In fact I'm doing exactly the same, as I have to take the address of the volatile pointer, and cast that pointer to pointer to non-volatile, and then access through it.

    I understand it may well work for some compilers, but I'm aiming to be platform independent, so I need to stick to the standard. It's a shame, because there are time when you operate on a lock-free list in a single threaded context, and would like then to obtain the optimization benefits of not using volatile.

    The other way to go about this perhaps is to have all the pointers as non-volatile, and cast them to volatile when they are used in a volatile manner. This will be ugly in the code, though.
    Last edited by Toby Douglass; 12-30-2016 at 03:01 PM.

  8. #8
    Old Took
    Join Date
    Nov 2016
    Location
    Londonistan
    Posts
    121
    Using InterlockedIncrement and family? They take a non-volatile pointer to volatile data.

  9. #9
    Registered User
    Join Date
    Dec 2009
    Posts
    83
    Quote Originally Posted by Hobbit View Post
    Using InterlockedIncrement and family? They take a non-volatile pointer to volatile data.
    Yes. With the list, the pointers themselves are the volatile, as they have the atomic operations running on them, so I don't think I can get rid of them.

  10. #10
    Old Took
    Join Date
    Nov 2016
    Location
    Londonistan
    Posts
    121
    You can almost certainly, volatile is of limited use in C for multithreaded programming, it doesn't do anything for you except turn off optimisations, though there maybe times when that is necessary. In C++ as the article I linked shows you can force the type system to check race conditions at compile time which is very neat. You certainly don't need to send volatile pointers to the Interlocked*** family of functions. As said earlier you should be able to cast the volatile away in definitely single-threaded portions of code but be aware that is undefined behaviour although in practice it should work. For defined behaviour you'll have to rewrite the lock-free list to avoid the use of volatile.

  11. #11
    Registered User
    Join Date
    Dec 2009
    Posts
    83
    Quote Originally Posted by Hobbit View Post
    You can almost certainly, volatile is of limited use in C for multithreaded programming, it doesn't do anything for you except turn off optimisations, though there maybe times when that is necessary. In C++ as the article I linked shows you can force the type system to check race conditions at compile time which is very neat. You certainly don't need to send volatile pointers to the Interlocked*** family of functions.
    Just to make sure we're on the same page - the variables which are being operated upon by the atomic operations are pointers themselves (and as such, being the targets for atomic operations, are volatile). The pointer to pointer (which is what I'm actually passing in to the Interlocked*() functions, as I need to give it the address of the volatile pointer) is non-volatile.

  12. #12
    Old Took
    Join Date
    Nov 2016
    Location
    Londonistan
    Posts
    121
    You probably don't need those pointers to be volatile. What's the lock-free strategy of your list? Compare and swap?

  13. #13
    Registered User
    Join Date
    Dec 2009
    Posts
    83
    Quote Originally Posted by Hobbit View Post
    You probably don't need those pointers to be volatile. What's the lock-free strategy of your list? Compare and swap?
    Yes. Are there other methods?

  14. #14
    Old Took
    Join Date
    Nov 2016
    Location
    Londonistan
    Posts
    121
    I'm not sure, I tend to use lock-free structures that are tried and tested and weren't written by me. I've checked the source for my lock free structures this morning and it seems they all use multibyte compare and swap, and they use a preprocessor macro VOLATILE which is defined as an empty tag with a */ volatile /* and a note that it should be uncommented only on insane compilers. I think you should be able to remove the volatile throughout the lock-free list.

  15. #15
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> Volatile prevents compiler optimization.
    Where in your list code do you believe this is preventing a potential bug? There may be another solution that doesn't involve volatile.

    gg

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Question about volatile
    By homer_3 in forum C++ Programming
    Replies: 26
    Last Post: 12-20-2014, 09:06 AM
  2. To Volatile, or not to Volatile?
    By EVOEx in forum C++ Programming
    Replies: 16
    Last Post: 05-12-2012, 02:07 PM
  3. synchronization and volatile
    By George2 in forum C++ Programming
    Replies: 21
    Last Post: 01-04-2008, 08:31 AM
  4. Volatile Keyword!!
    By maven in forum C Programming
    Replies: 8
    Last Post: 12-06-2005, 12:56 PM
  5. volatile??
    By jacktibet in forum C Programming
    Replies: 2
    Last Post: 05-29-2003, 03:46 PM

Tags for this Thread