Date: Sat, 23 Nov 2024 18:03:51 -0800
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.
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.
--- So what I end up with is say this is the existing type. typedef struct DataBlock volatile * volatile PDATALIST; Then the creation function is PDATALIST CreateDataList( size_t sz ); and usage is PDATALIST pdl = CreateDataList( sizeof( int ) ); or passed as a parameter to PDATALIST AddDataItem( PDATALIST*, POINTER ); or int GetItemCount( PDATALIST ); That's all great... but then with LLVM's deprecation warning what I end up having to do is typedef struct DataBlock volatile * volatile PDATALIST; typedef struct DataBlock volatile * PDATALIST_rval; PDATALIST_rval CreateDataList( size_t sz ); PDATALIST_rval AddDataItem( PDATALIST*, POINTER ); int GetItemCount( PDATALIST_rval, POINTER ); PDATALIST pdl = CreateDataList( sizeof( int ) ); - it becomes unobvious that one should use PDATALIST as the variable type, while the function that creates the list is a PDATALIST_rval. Hooray for features that improve(err diminish) code clarity? Maybe should have started with deprecating volatile in typedefs? How can I code that so it's not mixing a bunch of different types? PDATALIST_rval and PDATALIST, or any of the half dozen other thread-safe container types that have been implemented since the mid 90's? (and preferably without having to touch many thousands of instances of PLIST <...> to make it PLIST volatile <...>) J Yes, I get it .... in the passing of the volatile variable, it ends up being a cached value in a register and loses the original reference... and technically the GetItemCount should take a PDATALIST* instead... but that's a short time of life of that image anyway, and subsequent calls would end up with the new list address anyway; and there's of course times where the value ends up cached for several instructions, but when it goes back over the line to use the value it gets read again. I feel that a case study for implications of this wasn't really done very well - but then - maybe the usage of multi-threading is just that much less than I think it should be that this didn't show up. None of this has anything to do with Atomics and the locking of such a structure - some of them are lock-free.
Received on 2024-11-24 02:04:05