This question is focused exclusively on the writer side.

So are you saying that this will also work (based 
on https://go.dev/play/p/ZXMg_Qq3ygF )
        mmapBufRaw[fSize-1] = 255        // W1
        (*mmapBufAtomic.Load())[0] = 42  // W2

How about this, would that work as a "everything before the atomic. has to 
appear as a CPU instruction 1st ?
        mmapBufRaw[fSize-1] = 255   // W1
        atomic.LoadInt64(randomVal) // any atomic access acts as barrier
        mmapBufRaw[0] = 42          // W2

This is the exact mechanism I am trying to understand - what is the minimum 
that golang needs to guarantee "as written" order synchronization, within a 
specific single goroutine.

On Sunday, January 22, 2023 at 2:31:37 AM UTC+1 k...@google.com wrote:

> On the write side, you write your mult-GB data using normal writes, then 
> atomic.Store for the final flag uint. On the read side, you use an 
> atomic.Load for the flag uint followed by regular loads for the remaining 
> multi-GB of data.
> Reading a particular flag value ensures that the following loads see all 
> the writes from before the writer wrote that particular flag value. This is 
> guaranteed by the memory model, as the atomic read seeing the atomic write 
> introduces the synchronized-before edge you need.
>
> I agree that the Go memory model doesn't directly address multi-process 
> communication like this, but assuming both ends are Go this is guaranteed 
> to work by the Go memory model. YMMV on what operations/barriers/etc. you 
> need in other languages.
>
> On Saturday, January 21, 2023 at 1:46:09 PM UTC-8 bse...@computer.org 
> wrote:
>
>> On Sat, Jan 21, 2023 at 12:11 PM Peter Rabbitson (ribasushi) <
>> riba...@gmail.com> wrote:
>>
>>> On Saturday, January 21, 2023 at 7:48:12 PM UTC+1 bse...@computer.org 
>>> wrote:
>>> On Sat, Jan 21, 2023 at 10:36 AM Peter Rabbitson <riba...@gmail.com> 
>>> wrote:
>>> Greetings, 
>>>
>>> I am trying to understand the exact mechanics of memory write ordering 
>>> from within the same goroutine. I wrote a self-contained runnable example 
>>> with the question inlined here: https://go.dev/play/p/ZXMg_Qq3ygF and 
>>> am copying its header here:
>>>
>>> // Below is a complete example, with the question starting on line 38:
>>> // how do I ensure that a *separate Linux OS process* observing `IPCfile`
>>> // (either via pread() or mmap()) can *NEVER* observe W2 before W1.
>>> // The only permissible states are:
>>> // 1. no changes visible
>>> // 2. only W1 is visible
>>> // 3. both W1 and W2 are visible
>>>
>>> This is based on my interpretation of the go memory model:
>>>
>>> Atomic memory operations are sequentially consistent, so here:
>>>
>>>  (*mmapBufAtomic.Load())[fSize-1] = 255 // W1
>>> (*mmapBufAtomic.Load())[0] = 42        // W2
>>>
>>> The first atomic load happens before the second load. That also implies 
>>> the first write (W1) happens before the second (W2). However, there is no 
>>> guarantee that W2 will be observed by another goroutine.
>>>
>>> This is perfectly acceptable ( see point 2. above ). Also note that 
>>> there is no other goroutine that is looking at this: the observers are 
>>> separate ( possibly not even go-based ) OS processes. I am strictly trying 
>>> to get to a point where the writer process exemplified in the playground 
>>> will issue the CPU write instructions in the order I expect.
>>>  
>>>
>>> I think what is really needed here is an atomic store byte operation. If 
>>> this is the only goroutine writing to this buffer, you can emulate that by 
>>> atomic.LoadUint32, set the highest/lowest byte, then atomic.StoreUint32
>>>
>>> This would not be viable: the W1 write is a single byte for the sake of 
>>> brevity. In practice it will be a multi-GiB write, with a multi-KiB  write 
>>> following it, followed by a single-UInt write. All part of a lock-free 
>>> "ratcheted" transactional implementation, allowing for incomplete writes, 
>>> but no dirty reads - the "root pointer" is the last thing being updated, so 
>>> an observer process sees "old state" or "new state" and nothing inbetween. 
>>> This is why my quest to understand the precise behavior and guarantees of 
>>> the resulting compiled program.
>>>
>>
>>
>> You realize, if W1 is a multi-GB write, another process will 
>> observe partial writes for W1. But, I believe, if another process observes 
>> W2, then it is guaranteed that all of W1 is written.
>>
>> I think the Go memory model does not really apply here, because you are 
>> talking about other processes reading shared memory. What you are really 
>> relying on is that on x86, there will be a memory barrier associated with 
>> atomic loads. I don't know how this works on arm. I am not sure how 
>> portable this solution would be. The memory model is explicit about 
>> observing the effects of an atomic write operation, and sequential 
>> consistency of atomic memory operations. So it sounds like an unprotected 
>> W1 followed by an atomic store of W2 would still work the same way.
>>  
>>
>>> -- 
>>> You received this message because you are subscribed to the Google 
>>> Groups "golang-nuts" group.
>>> To unsubscribe from this group and stop receiving emails from it, send 
>>> an email to golang-nuts...@googlegroups.com.
>>>
>> To view this discussion on the web visit 
>>> https://groups.google.com/d/msgid/golang-nuts/c23d512e-a307-4f4d-bf23-74398c5cf42bn%40googlegroups.com
>>>  
>>> <https://groups.google.com/d/msgid/golang-nuts/c23d512e-a307-4f4d-bf23-74398c5cf42bn%40googlegroups.com?utm_medium=email&utm_source=footer>
>>> .
>>>
>>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/4c8feb7a-f2d9-4392-ad1d-f72253ccafd7n%40googlegroups.com.

Reply via email to