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
I did read through https://go.dev/ref/mem and https://github.com/golang/go/discussions/47141 + links, but could not find a definitive answer to my specific use-case.
Would really appreciate any help getting to the bottom of this!
--
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/CAMrvTSKXb5JQMR9PcCXwYhcT4rq8O_5hiTHrOChk6sUeOrbagw%40mail.gmail.com.
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 visibleThis 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 // W2The 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.
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
--
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.
This question is focused exclusively on the writer side.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/4c8feb7a-f2d9-4392-ad1d-f72253ccafd7n%40googlegroups.com.
On Sat, Jan 21, 2023, 11:12 PM Peter Rabbitson (ribasushi) <riba...@gmail.com> wrote:This question is focused exclusively on the writer side.Perhaps I misunderstand, but it doesn't make sense to ask a question about the memory model only about one side or the other. The memory model is about communication between two goroutines. It has very little to say about the behavior of a single goroutine.
#include <asm/system.h>
void wmb(void);
--
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/CAMrvTSL%2B5XO%3Dz6815RN6sNxLJsce%2BJsyRYm3zq85NiA6L9O_cA%40mail.gmail.com.
The atomic store will force a memory barrier - as long as the reader (in the other process) atomically reads the “new value”, all other writes prior will also be visible.
BUT you can still have an inter-process race condition if you are updating the same memory mapped file regions - and you need an OS mutex to protect against this
You can look at projects like https://github.com/OpenHFT/Chronicle-Queue for ideas.Still, large-scale shared memory systems are usually not required. I would use a highly efficient message system like Nats.io and not reinvent the wheel. Messaging systems are also far more flexible.
--
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/CAMrvTS%2BHqsqCOMay3c8D5LuTwcmtuZQJY7gs8Rw5rXBLiYwErg%40mail.gmail.com.
Write data to memory mapped file/shared memory. Keep track of last written byte as new_length;Use atomic.StoreUint64(pointer to header.length, new_length);
readers read ...
This assumes you are always appending ,,, then it is much more complicated ... all readers have consumed the data before the writer reuses it.
On Jan 22, 2023, at 8:12 PM, Peter Rabbitson <riba...@gmail.com> wrote:
The atomic functions force a memory barrier when atomically in conjunction with the atomic read of the same value.You could use CGO to call a C function to do what you desire - but it shouldn’t be necessary.
Not sure what else I can tell you.On Jan 22, 2023, at 8:12 PM, Peter Rabbitson <riba...@gmail.com> wrote:On Mon, Jan 23, 2023 at 12:42 AM robert engels <ren...@ix.netcom.com> wrote:Write data to memory mapped file/shared memory. Keep track of last written byte as new_length;Use atomic.StoreUint64(pointer to header.length, new_length);This does not answer the question I posed, which boils down to:How does one insert the equivalent of smp_wmb() / asm volatile("" ::: "memory") into a go program.For instance is any of these an answer? https://groups.google.com/g/golang-nuts/c/tnr0T_7tyDk/m/9T2BOvCkAQAJreaders read ...Please don't focus on the reader ;)This assumes you are always appending ,,, then it is much more complicated ... all readers have consumed the data before the writer reuses it.Yes, it is much more complicated :) I am making a note to post the result back to this thread in a few weeks when it is readable enough.
--
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/5F73A3C7-32C3-42A1-91A2-E1A0714FAEA5%40ix.netcom.com.
Memory ordering only makes sense in terms of two different execution
threads using shared memory. In order to answer your question
precisely, you need to tell us what the process reading the memory
region is going to do to access the memory. In order to know how to
write the memory, it's necessary to know how the memory is going to be
read.
There is no Go equivalent to a pure write memory barrier.
There is a model of memory behavior in which programs use pure write
memory barriers and pure read memory barriers. However, Go does not
use that model. Go uses a different model, in which writers and
readers are expected to cooperate using atomic loads and stores.
As such, Go does not provide a pure write memory barrier. I promise.
(As I noted earlier, Go does permit calling into C, and therefore
permits you to do anything that C permits you to do. It's worth
noting that because C code can do anything, a call into a C function
is a full compiler (but not hardware) memory barrier.)