C++ Logo

std-discussion

Advanced search

Re: Deprecation of volatile - Implications for exsiting code

From: J Decker <d3ck0r_at_[hidden]>
Date: Mon, 25 Nov 2024 11:29:15 -0800
On Sun, Nov 24, 2024 at 10:27 PM Tiago Freire <tmiguelf_at_[hidden]> wrote:

> Well, I have good news for you.
>
> Volatile isn’t deprecated anymore, they went back on that decision.
>
Do you have a reference/link to that? I dug through github paper issues
and found this...(regarding original paper though?)
https://github.com/cplusplus/papers/issues/138#issuecomment-524453355
:"Adopted 2019-07."




>
> The bad news is, this still will not work for what you want to do with it.
>
> This really has been studied to death, “volatile” is really useless as
> multithreaded synchronization object, mostly misused by developers who
> don’t understand what “volatile” actually does and how modern CPUs work,
> and valid use cases are so rare that “volatile” causes a disproportional
> amount of burden that you would be better off with an alternative solution,
> reason why it had been deprecated to begin with.
>
> The problem is that in the rare cases where you need it, you really need
> it, and no alternative solution exists that would properly replace it,
> hence why they went back to not deprecating it anymore. But I suspect when
> someone starts to work on “special types with volatile operations” instead
> of “volatile variables”, those cases will eventually be covered and
> volatile will still go the way of the dodo.
>
>
>
> In a world where you have multiple CPUs with dedicated cache and
> instruction re-ordering, the days where “your code is executed predictively
> instruction after instruction and have the observable side-effects as one
> would naively read it from the source code” are long gone and they are
> likely never coming back.
>
>
>

instructions can only be re-ordered with constraints; can't just randomly
execute instructions for (x+3)*5. Sure execution local cache that's the
same memory as another core at the same time, if there's contention for
writes to a location, then things like __atomic_exchange_n or
InterlockedExchange which have locks that limit the access are more
appropriate... But even with those,
https://en.wikipedia.org/wiki/Cache_coherence is apparently an expectation
of CPUs... one could in theory mmap a FUSE hosted file over a network...
but there is certainly no cache coherence and conflicts can arise - so you
write it differently.

But yes, thanks for the good news. I'll go back to the implementation(they
said their place wasn't the place to discuss comittee deceisions); they
are still under the impression it went/is going forward... but maybe it's
more like a fluke and I just get to include a variable to suppress the
warnings. It really is that the function returns a pointer whose value can
potentially change later; which makes it harder to comply with 'void
*volatile whatever( void )' being invalid.

>
>
> *From:* Std-Discussion <std-discussion-bounces_at_[hidden]> *On
> Behalf Of *J Decker via Std-Discussion
> *Sent:* Sunday, November 24, 2024 8:35 PM
> *To:* std-discussion_at_[hidden]
> *Cc:* J Decker <d3ck0r_at_[hidden]>
> *Subject:* Re: [std-discussion] Deprecation of volatile - Implications
> for exsiting code
>
>
>
>
>
>
>
> On Sun, Nov 24, 2024 at 9:52 AM Nate Eldredge <nate_at_[hidden]>
> wrote:
>
> I'm afraid your ideas about `volatile` with respect to multi-threading are
> about 14 years out of date. You're reading an object in one thread while
> it's being concurrently written in another, and it's not `std::atomic`.
> Since C++11, that's a data race and your program has undefined behavior.
> Under the standard, `volatile` doesn't save you; `std::atomic` is the only
> conformant way to do it. (And `std::atomic` does in fact achieve
> everything you want here; for instance, it inhibits optimizations like
> "read the value once and then never read it again.")
>
>
>
>
>
> I'm reading a pointer, not an object, so when is a register sized
> load/store/move not atomic?
>
>
>
> "It is unclear how to use such weak guarantees in portable code, except
> in a few rare instances, which we list below." from
>
> "In case you wonder why volatile does not work for multi-threading, see
>
> https://wg21.link/N2016 "
>
>
>
> ... 1) I am one of those instances 2) sorry it wasn't clear, it's quite
> clear to me, but then in the 90's I had the advantage of a logic analyzer
> connected to every signal pin of a 386 processor (which was before there
> was in-cpu cache even), and intimately understand word tear if such
> register sized values were not on a register-sized boundary. can't see how
> processors would devolve into a stream of bits or something that volatile
> for 'read always' and 'execute in the order I specified in the code'
> wouldn't cover... the first bit would either bit the previous or the next
> and all subsequently - maybe there's a grey-code possibility, but then
> that's something the hardware is doing to you, and maybe you should lock at
> a higher level and use a different object for the list of data.
>
>
>
> In many cases, the thread-safety of my atomics implementation is often
> unused - on reflection in regards to this issue, the lock is really just a
> write-lock, with lock-free reads; and in many cases isn't strictly needed
> to be volatile, because there's a code lock around it; but then volatile
> still need to be specified in the type because... PLIST is a simil.ar
> type, but stores only pointers to things, or pointer-sized values.
>
>
>
> #define ever ;;
>
> CRITICALSECTION cs; // assume inited...
>
> void f( PLIST **outList ) {
>
>
>
> PLIST list = NULL;
>
> outList[0] = &list;
>
> EnterCriticalSection( &cs );
>
> for( ever ) {
>
> if( !list ) { // there's nothing in this code path that would change
> this value
>
> // so it's a candidate for read-never optimization;
> even without any locks on the object.
>
> LeaveCriticalSection( &cs ); sleep( forever ); EnterCriticalSection(
> &cs ); continue;
>
> }
>
> /* do something with the data in the list */
>
> // assuming you CAN get here, because the read-again of list didn't
> happen.
>
> }
>
>
>
> }
>
>
>
>
>
> void g( void ) {
>
> PLIST *useList;
>
> uinitptr_t data = 3;
>
> PTHREAD thread = ThreadTo( f, &useList );
>
> AddLink( useList, &data );
>
> WakeThread( thread );
>
> // and later doing other work with this list... which in many cases
> ends up being an address of the volatile
>
> // but not ALL cases...
>
> }
>
>
>
> It's possible that your implementation provides some guarantees about
> `volatile` in multithreading beyond what the standard gives, but if so,
> that's between you and them; nothing to do with ISO C++.
>
>
>
> Now, this doesn't directly answer your question about `volatile` on
> parameters and return types, but it does rather invalidate the use case you
> proposed.
>
>
>
> The intended uses for `volatile` in this day and age are things like
> memory-mapped I/O, where your goal is to just make the machine perform a
> single load or store instruction. (Again, you may *think* that's exactly
> what you want for concurrent multithread access, but the standard doesn't
> promise that it will work.) So if you can propose an example of that kind,
> we could get back on track. However, for memory-mapped I/O, you almost
> never want to define an actual object of `volatile`-qualified type. You
> would work through pointers to such types, but the pointers themselves
> would not be `volatile`.
>
>
>
> 'but the standard doesn't promise that it will work' interpretations of
> the standard that mixed a bunch of correlated but maybe not causally
> related things together to compilcate the issue.... maybe better wording
> would make it less subject to interpretation so you don't have to argue
> with each implementation that decides A is tied to B, and decides to break
> (well, they didn't always have volatile in the type either, but as
> optimizers got too eager and broke too much, they just inherited them
> probably about 15 years ago so right when volatile became unusable for all
> the things that do work and did work. But apparently this is around the
> time this is when they start telling me that 'the sky is green, not blue'
> what YOU(me, from you) see and have working, isn't actually working.
>
>
>
>
>
>
>
>
>
> On Nov 23, 2024, at 19:03, J Decker via Std-Discussion <
> std-discussion_at_[hidden]> wrote:
>
>
>
>
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1152r0.html#parret
>
>
>
>
>
> I have some datatype, which is a volatile list. What makes it volatile is
> that other threads can change the values associated with the list without
> any other threads being aware of the change. Primarily, what happens, when
> the objects are not defined as volatile, the optimizers can read the value
> early in a function, and then never read it again. The only meaning of
> volatile *I* knew of was 'read always'. Writes and locks and other
> unrelated things mentioned in the paper just confuse the issue. What I
> need is just a way to tell compiler optimizers 'hey don't be smart, if the
> code needs the value, get the value from the place specified.' Writes
> could be cached - but it's something the programmer would do. It(volatile)
> really has nothing to do atomics.
>
>

Received on 2024-11-25 19:29:31