defer and Go memory model

297 views
Skip to first unread message

Ge

unread,
May 12, 2021, 10:37:49 AM5/12/21
to golang-nuts
According to https://golang.org/ref/spec#Defer_statements there is such an expression:
  `A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns`

Does `defer` ensure  happens-before behaviour with non-defer code?
I have read https://golang.org/ref/mem but got nothing except this.
(It seems not clear to me about the meaning of `the order expressed by the program`.)

  `Within a single goroutine, the happens-before order is the order expressed by the program.`

In following case what I know is ③ happens before ①, 
but what about ②? Is it possible that ② is executed after
③ and ①?

```Go

var A, B, C int

func main() {
    defer func()  { A = 4 }     //①
    B = 5     //②
    defer func()   { C = 6 }   //③
}
```

Appendix:
1. The background: 
    I was digging into the implementation of sync.Once and 
    wonder if `defer atomic.StoreUint32(&o.done, 1)` in 
    function doSlow could be replaced with `defer func() {o.done = 1}`.
```
func (o *Once) Do(f func()) {
  if atomic.LoadUint32(&o.done) == 0 { 
    o.doSlow(f)
  }
}

func (o *Once) doSlow(f func()) {
  o.m.Lock()
  defer o.m.Unlock()
  if o.done == 0 {
    defer atomic.StoreUint32(&o.done, 1)
    f()
  }
}
```

2. As I know, `defer` is implemented by inserting corresponding 
    function calling before any exiting point(Eg. in x86 the ret instruction).
    
    However x86 allows out-of-order execution happening across function calls,
    and I'm not sure if it will happen in this case(f() and o.done has no dependency 
    with each other so it is possible?).
    If anyone knows I would appreciate for your clarification.

Thanks for your time.

Ge


Jan Mercl

unread,
May 12, 2021, 10:48:43 AM5/12/21
to Ge, golang-nuts
On Wed, May 12, 2021 at 4:38 PM Ge <everg...@gmail.com> wrote:
>
> According to https://golang.org/ref/spec#Defer_statements there is such an expression:
> `A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns`
>
> Does `defer` ensure happens-before behaviour with non-defer code?

Happens before talks/defines properties/behavior of concurrently
executing goroutines. The deferred function is executed in the same
goroutine as its surrounding function. Any HB relations wrt other
goroutines are the same as if the deferred function was not deferred
but explicitly called before just returning from the surrounding
function.

> However x86 allows out-of-order execution happening across function calls,

OOE and other peculiar CPU tricks should not be observable by the Go
program, modulo some side channel attacks.

Ge

unread,
May 12, 2021, 1:55:28 PM5/12/21
to golang-nuts
Thank you Jan, sorry for my bad english.
Do you mean in following case if anthoer goroutine is observing A and B, 
there is no possbility that A is 0 and B is 5? (assuming no other synchronization here)

```
var A, B int
func try() {
    defer func() { B = 5 }
    A = 3
}

Ge

Axel Wagner

unread,
May 12, 2021, 2:14:49 PM5/12/21
to golang-nuts
If we assume no other synchronization here, you have concurrent modification of variables, which is undefined behavior.

But to answer your original questions: Yes, the behavior of `defer` is included in the clause "the order expressed by the program" - "the order specified by the program" doesn't mean "lexical order". `defer` is no different in that regard than assigning a function literal to a variable and calling it at a later point - the execution still happens at a later point, even if the statements in the function literal appear lexically before the call.

So, yes `defer` implies happens-before relationships: Every return statement in the function happens-before the deferred execution, which happens-before the function call returns.

--
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/2bf8beb9-1927-4311-9c7c-32f880bded79n%40googlegroups.com.

Jan Mercl

unread,
May 12, 2021, 2:15:43 PM5/12/21
to Ge, golang-nuts
On Wed, May 12, 2021 at 7:55 PM Ge <everg...@gmail.com> wrote:

> Do you mean in following case if anthoer goroutine is observing A and B,
> there is no possbility that A is 0 and B is 5? (assuming no other synchronization here)
>
> ```
> var A, B int
> func try() {
> defer func() { B = 5 }
> A = 3
> }

There's no synchronization in this code, so other goroutines reading
A or B w/o said synchronization may observe any values. Stalled,
changing in wrong order, random..., you name it.

But if has nothing to do with defer. The particular code is equal to

var A, B int
func try() {
A = 3
B = 5
}

with the set of problems as the version using defer.

Ge

unread,
May 13, 2021, 10:49:31 PM5/13/21
to golang-nuts
Thanks Jan and Axel, I may need to figure out go memory model and hardware memory model.
Reply all
Reply to author
Forward
0 new messages