Order of memory writes observed from another goroutine

According to https://golang.org/ref/mem, in the function

func f() {
	a = 1
	b = 2
}

it is not guaranteed that a is set before b.
What about this:

func f() {
	defer func() {
			b = 2
	}()
	a = 1
}

?

What is the context of your question? What kind of access to these variables would depend on the order?

In my project, I assign device protocols to one or more serial ports. Multiple hardware devices with different protocols and/or port settings can be connected to the same serial port. When a software device has finished polling its hardware (or when a timeout occurs), the port handler sets port.UsedBy = 0 (without closing the port!) as a defered command so that the main loop can map the port to another device. As long as the port is not yet free for use, port.UsedBy contains the device ID.
Go build -race says, this is a data race.
The main loop can’t wait for the device to be finished. Instead it checks every 50ms or so whether a required port is free.

If the defered write to port.UsedBy does what I think it should do (and espacially WHEN it should do it), I do not need any additionally mutex or chan to communicate the state.

(p.s.: in deutsch kann ich viel präziser und flüssiger schreiben als in englisch :wink: )

Check out the behavior of this program on the playground. The defer will execute once for each loop only after it exits its containing function, meaning if you have a loop blocking execution and you are trying to open connections without exiting the containing function first it’s not going to behave like that: https://play.golang.org/p/U62KXONy9a

Thank you. In my application, it’s more like this:

https://play.golang.org/p/rDyvd0oMeP

You need to use a mutex to control the access, or a channel.

2 Likes

Yes, I need some sort of sync to satisfy the -race detector.
But is it really required?
My question is: Is it somehow possible that the defered command is executed before the function came to it’s end?

Yes. It is not about satisfying the race detector. The problems pointed out by the race detector are serious and make your program invalid.

The defer is executed after the rest of the statements in the function. But this is not the point. The point is that for reasons that have to do with how the hardware in your machine works, the writes may be visible by other goroutines in any order or not at all unless you use explicit synchronization.

For example, the other goroutine may run on another CPU core that has a local cache of the memory location that holds a but not of the memory location that holds b. If that goroutine looks at a and b it may see a new value for b but an old value for a even though the writes happened in the other order.

Using a mutex, an atomic, or a channel makes sure that things appear to happen in the correct order.

OK, thank you.
But I may add: all other writes by the goroutine are synced by a mutex. The Used.By = 0 is only to tell the calling function that the goroutine has returned. Even if it starts a new goroutine too early, it will block at the first try to write something until the old goroutine has unlocked the structures.

I even can omit the read of UsedBy and start all goroutines at the same time, because they block each other, but then I have no control over the order in which they are executed.

See https://software.intel.com/en-us/blogs/2013/01/06/benign-data-races-what-could-possibly-go-wrong.

Very revealing blog article, thank you!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.