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.