FAQ
Hello,

I mostly concern about visibility in the example below (also available here
<http://play.golang.org/p/eb9WjslFL5>):

var (
A, B int
guard uint32
wg sync.WaitGroup
)

func GoroutineA() {
A = 10
if !atomic.CompareAndSwapUint32(&guard, 0, 1) {
//Here we know that GoroutineB set guard = 1 first,
A = B //but is there any guarantee that we see B == 20 here?
}
wg.Done()
}

func GoroutineB() {
B = 20
if !atomic.CompareAndSwapUint32(&guard, 0, 1) {
//Here we know that GoroutineA set guard = 1 first,
B = A //but is there any guarantee that we see A == 10 here?
}
wg.Done()
}

func main() {
wg.Add(2)
go GoroutineA()
go GoroutineB()
wg.Wait()
if A != B {
panic("shattered dream!!!")
}
}


Please ignore the usefulness of the example, it's just a naive
demonstration for what I asked.

Thank you very much.

--
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+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Search Discussions

  • Henrik Johansson at Sep 8, 2015 at 5:33 am
    My understanding is that there are no such guarantees at all. Even ignoring
    memory effects there is still a race between the execution of the two
    goroutines determined by the scheduler.
    On Tue, Sep 8, 2015, 06:39 enormouspenguin wrote:

    Hello,

    I mostly concern about visibility in the example below (also available
    here <http://play.golang.org/p/eb9WjslFL5>):

    var (
    A, B int
    guard uint32
    wg sync.WaitGroup
    )

    func GoroutineA() {
    A = 10
    if !atomic.CompareAndSwapUint32(&guard, 0, 1) {
    //Here we know that GoroutineB set guard = 1 first,
    A = B //but is there any guarantee that we see B == 20 here?
    }
    wg.Done()
    }

    func GoroutineB() {
    B = 20
    if !atomic.CompareAndSwapUint32(&guard, 0, 1) {
    //Here we know that GoroutineA set guard = 1 first,
    B = A //but is there any guarantee that we see A == 10 here?
    }
    wg.Done()
    }

    func main() {
    wg.Add(2)
    go GoroutineA()
    go GoroutineB()
    wg.Wait()
    if A != B {
    panic("shattered dream!!!")
    }
    }


    Please ignore the usefulness of the example, it's just a naive
    demonstration for what I asked.

    Thank you very much.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Enormouspenguin at Sep 8, 2015 at 7:40 am

    On Tuesday, September 8, 2015 at 12:33:30 PM UTC+7, Henrik Johansson wrote:
    My understanding is that there are no such guarantees at all. Even
    ignoring memory effects there is still a race between the execution of the
    two goroutines determined by the scheduler.
    As I said, I mostly concern about visibility. I am aware of the race and
    know that at the end of main(), A and B could take any value but could each
    take an entirely different value from each other (e.g.: A == 20 && B == 10)?

    A CAS operation compose of 3 little ops that must execute as a single
    transaction: read, compare and write. All atomic functions in Go has
    implicit acq_rel semantic (I get it from here
    <https://github.com/golang/go/issues/5045#issuecomment-68050363>). My
    rationale is that because a CAS always have to execute a read first
    therefore although it failed at the comparison phase (leading to write not
    executed), it should see all side effects prior to the written value that
    it observed through the initial read.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henrik Johansson at Sep 8, 2015 at 5:46 pm
    Hmm, I don't know the nitty gritty but if I remember correctly reading from
    a non atomic variable in concert with atomic variables can lead to
    undefined behaviour.
    I don't think it is safe in general to do this although on some arch it
    might work?

    tis 8 sep. 2015 kl 09:40 skrev enormouspenguin <kimkhanh535@gmail.com>:
    On Tuesday, September 8, 2015 at 12:33:30 PM UTC+7, Henrik Johansson wrote:

    My understanding is that there are no such guarantees at all. Even
    ignoring memory effects there is still a race between the execution of the
    two goroutines determined by the scheduler.
    As I said, I mostly concern about visibility. I am aware of the race and
    know that at the end of main(), A and B could take any value but could each
    take an entirely different value from each other (e.g.: A == 20 && B == 10)?

    A CAS operation compose of 3 little ops that must execute as a single
    transaction: read, compare and write. All atomic functions in Go has
    implicit acq_rel semantic (I get it from here
    <https://github.com/golang/go/issues/5045#issuecomment-68050363>). My
    rationale is that because a CAS always have to execute a read first
    therefore although it failed at the comparison phase (leading to write not
    executed), it should see all side effects prior to the written value that
    it observed through the initial read.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • James Aguilar at Sep 8, 2015 at 8:26 pm

    On Tuesday, September 8, 2015 at 12:40:23 AM UTC-7, enormouspenguin wrote:
    On Tuesday, September 8, 2015 at 12:33:30 PM UTC+7, Henrik Johansson wrote:

    My understanding is that there are no such guarantees at all. Even
    ignoring memory effects there is still a race between the execution of the
    two goroutines determined by the scheduler.
    As I said, I mostly concern about visibility. I am aware of the race and
    know that at the end of main(), A and B could take any value but could each
    take an entirely different value from each other (e.g.: A == 20 && B == 10)?

    A CAS operation compose of 3 little ops that must execute as a single
    transaction: read, compare and write. All atomic functions in Go has
    implicit acq_rel semantic (I get it from here
    <https://github.com/golang/go/issues/5045#issuecomment-68050363>). My
    rationale is that because a CAS always have to execute a read first
    therefore although it failed at the comparison phase (leading to write not
    executed), it should see all side effects prior to the written value that
    it observed through the initial read.
      I read through the memory model <https://golang.org/ref/mem> after seeing
    this, and my impression was that it implies that this is safe. It says this:

    That is, r is guaranteed to observe w if both of the following hold:
    w happens before r.
    Any other write to the shared variable v either happens before w or after
    r.

    According to my understanding, we know that *w* happens before the
    succeeding CAS, because that is how program order is expressed within a
    single goroutine. We also know that the succeeding CAS happens before the
    failing CAS. And finally we know that the failing CAS happens before the *r*.
    As long as there is no other *w'*, I believe *r* should be guaranteed to
    observe the write.

    The memory model is not explicit about happens-before relationships between
    multiple shared variables. However, I'm not really sure how sync.Mutex
    could possibly work if such a relationship didn't exist, since it just uses
    atomics under the covers
    <http://golang.org/src/sync/mutex.go?s=1078:1100#L31>.

    That said, I didn't post anything last night because I wasn't confident,
    and I still am not. I wouldn't rely on behavior like this to work in a
    program. I would use mutex. If I needed the performance, I might wait until
    someone with more knowledge responded on this post, or read the disassembly
    and the manual for my processor until I gained confidence.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Matt Harden at Sep 8, 2015 at 11:23 pm
    Have you tried running this example with the race detector? I think if the
    race detector doesn't see a race here, you should be OK.

    I also think the memory model is explicit enough. Even though you have
    multiple variables, with a write to *any* given variable that "happens
    before" a read from that variable (and no other writes can occur in
    between) the written value will be seen by the read. You can apply this
    rule more than once, and to different variables.

    For A:
    A = 10 H.B. CAS1
    case 1: CAS1 succeeds; CAS2 fails
         No further writes to A occur
         CAS1 H.B. DONE1
    case 2: CAS1 fails; CAS2 succeeds
         B = 20 H.B. CAS2
         No further writes to B occur
         CAS2 H.B. CAS1
         CAS1 H.B. A = B
         A = B H.B. DONE1
    DONE1 H.B. WAIT
    WAIT H.B. A != B

    No race exists on A, because all reads and writes to A are ordered in a
    strict happens-before sequence. The behavior is just as if the reads and
    writes had occurred in sequence within a single thread.

    We can apply the same reasoning to prove there is no race on B, by swapping
    (A, CAS1, DONE1) with (B, CAS2, DONE2) in the above "proof".

    On Tue, Sep 8, 2015 at 3:26 PM James Aguilar wrote:
    On Tuesday, September 8, 2015 at 12:40:23 AM UTC-7, enormouspenguin wrote:

    On Tuesday, September 8, 2015 at 12:33:30 PM UTC+7, Henrik Johansson
    wrote:
    My understanding is that there are no such guarantees at all. Even
    ignoring memory effects there is still a race between the execution of the
    two goroutines determined by the scheduler.
    As I said, I mostly concern about visibility. I am aware of the race and
    know that at the end of main(), A and B could take any value but could each
    take an entirely different value from each other (e.g.: A == 20 && B == 10)?

    A CAS operation compose of 3 little ops that must execute as a single
    transaction: read, compare and write. All atomic functions in Go has
    implicit acq_rel semantic (I get it from here
    <https://github.com/golang/go/issues/5045#issuecomment-68050363>). My
    rationale is that because a CAS always have to execute a read first
    therefore although it failed at the comparison phase (leading to write not
    executed), it should see all side effects prior to the written value that
    it observed through the initial read.
    I read through the memory model <https://golang.org/ref/mem> after
    seeing this, and my impression was that it implies that this is safe. It
    says this:

    That is, r is guaranteed to observe w if both of the following hold:
    w happens before r.
    Any other write to the shared variable v either happens before w or after
    r.

    According to my understanding, we know that *w* happens before the
    succeeding CAS, because that is how program order is expressed within a
    single goroutine. We also know that the succeeding CAS happens before the
    failing CAS. And finally we know that the failing CAS happens before the
    *r*. As long as there is no other *w'*, I believe *r* should be
    guaranteed to observe the write.

    The memory model is not explicit about happens-before relationships
    between multiple shared variables. However, I'm not really sure how
    sync.Mutex could possibly work if such a relationship didn't exist, since
    it just uses atomics under the covers
    <http://golang.org/src/sync/mutex.go?s=1078:1100#L31>.

    That said, I didn't post anything last night because I wasn't confident,
    and I still am not. I wouldn't rely on behavior like this to work in a
    program. I would use mutex. If I needed the performance, I might wait until
    someone with more knowledge responded on this post, or read the disassembly
    and the manual for my processor until I gained confidence.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henrik Johansson at Sep 9, 2015 at 4:30 am
    I still am not sure I agree since there is a write to a third variable, the
    guard. Is it not possible for write to A in one goroutine to be reordered
    visavis the read from A in the other goroutine? And even if this is safe it
    feels very counterintuitive from my experience with Java where reading and
    writing from shared variables outside of the various memory control
    structures is a no-no.

    ons 9 sep. 2015 kl 01:24 skrev Matt Harden <matt.harden@gmail.com>:
    Have you tried running this example with the race detector? I think if the
    race detector doesn't see a race here, you should be OK.

    I also think the memory model is explicit enough. Even though you have
    multiple variables, with a write to *any* given variable that "happens
    before" a read from that variable (and no other writes can occur in
    between) the written value will be seen by the read. You can apply this
    rule more than once, and to different variables.

    For A:
    A = 10 H.B. CAS1
    case 1: CAS1 succeeds; CAS2 fails
    No further writes to A occur
    CAS1 H.B. DONE1
    case 2: CAS1 fails; CAS2 succeeds
    B = 20 H.B. CAS2
    No further writes to B occur
    CAS2 H.B. CAS1
    CAS1 H.B. A = B
    A = B H.B. DONE1
    DONE1 H.B. WAIT
    WAIT H.B. A != B

    No race exists on A, because all reads and writes to A are ordered in a
    strict happens-before sequence. The behavior is just as if the reads and
    writes had occurred in sequence within a single thread.

    We can apply the same reasoning to prove there is no race on B, by
    swapping (A, CAS1, DONE1) with (B, CAS2, DONE2) in the above "proof".

    On Tue, Sep 8, 2015 at 3:26 PM James Aguilar wrote:
    On Tuesday, September 8, 2015 at 12:40:23 AM UTC-7, enormouspenguin wrote:

    On Tuesday, September 8, 2015 at 12:33:30 PM UTC+7, Henrik Johansson
    wrote:
    My understanding is that there are no such guarantees at all. Even
    ignoring memory effects there is still a race between the execution of the
    two goroutines determined by the scheduler.
    As I said, I mostly concern about visibility. I am aware of the race and
    know that at the end of main(), A and B could take any value but could each
    take an entirely different value from each other (e.g.: A == 20 && B == 10)?

    A CAS operation compose of 3 little ops that must execute as a single
    transaction: read, compare and write. All atomic functions in Go has
    implicit acq_rel semantic (I get it from here
    <https://github.com/golang/go/issues/5045#issuecomment-68050363>). My
    rationale is that because a CAS always have to execute a read first
    therefore although it failed at the comparison phase (leading to write not
    executed), it should see all side effects prior to the written value that
    it observed through the initial read.
    I read through the memory model <https://golang.org/ref/mem> after
    seeing this, and my impression was that it implies that this is safe. It
    says this:

    That is, r is guaranteed to observe w if both of the following hold:
    w happens before r.
    Any other write to the shared variable v either happens before w or
    after r.

    According to my understanding, we know that *w* happens before the
    succeeding CAS, because that is how program order is expressed within a
    single goroutine. We also know that the succeeding CAS happens before the
    failing CAS. And finally we know that the failing CAS happens before the
    *r*. As long as there is no other *w'*, I believe *r* should be
    guaranteed to observe the write.

    The memory model is not explicit about happens-before relationships
    between multiple shared variables. However, I'm not really sure how
    sync.Mutex could possibly work if such a relationship didn't exist, since
    it just uses atomics under the covers
    <http://golang.org/src/sync/mutex.go?s=1078:1100#L31>.

    That said, I didn't post anything last night because I wasn't confident,
    and I still am not. I wouldn't rely on behavior like this to work in a
    program. I would use mutex. If I needed the performance, I might wait until
    someone with more knowledge responded on this post, or read the disassembly
    and the manual for my processor until I gained confidence.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • James Aguilar at Sep 9, 2015 at 4:41 am
    Looked into this some more. It's becoming increasingly difficult to believe
    it's not safe (for amd64 -- don't know about the rest but I'd guess the
    same given a plain language reading of the memory model).

    CompareAndSwapUint32
    <https://github.com/golang/go/blob/master/src/sync/atomic/asm_amd64.s#L35>,
    and the other CAS operations, all use LOCK CMPXCHG* ops. According to
    various docs I found (one example
    <http://mechanical-sympathy.blogspot.com/2011/07/memory-barriersfences.html>),
    all LOCK-prefixed instructions entail full memory barriers.

    So, previous stores in one thread followed by a CAS should be visible, at
    least on x64.
    On Tuesday, September 8, 2015 at 9:30:09 PM UTC-7, Henrik Johansson wrote:

    I still am not sure I agree since there is a write to a third variable,
    the guard. Is it not possible for write to A in one goroutine to be
    reordered visavis the read from A in the other goroutine? And even if this
    is safe it feels very counterintuitive from my experience with Java where
    reading and writing from shared variables outside of the various memory
    control structures is a no-no.

    ons 9 sep. 2015 kl 01:24 skrev Matt Harden <matt....@gmail.com
    <javascript:>>:
    Have you tried running this example with the race detector? I think if
    the race detector doesn't see a race here, you should be OK.

    I also think the memory model is explicit enough. Even though you have
    multiple variables, with a write to *any* given variable that "happens
    before" a read from that variable (and no other writes can occur in
    between) the written value will be seen by the read. You can apply this
    rule more than once, and to different variables.

    For A:
    A = 10 H.B. CAS1
    case 1: CAS1 succeeds; CAS2 fails
    No further writes to A occur
    CAS1 H.B. DONE1
    case 2: CAS1 fails; CAS2 succeeds
    B = 20 H.B. CAS2
    No further writes to B occur
    CAS2 H.B. CAS1
    CAS1 H.B. A = B
    A = B H.B. DONE1
    DONE1 H.B. WAIT
    WAIT H.B. A != B

    No race exists on A, because all reads and writes to A are ordered in a
    strict happens-before sequence. The behavior is just as if the reads and
    writes had occurred in sequence within a single thread.

    We can apply the same reasoning to prove there is no race on B, by
    swapping (A, CAS1, DONE1) with (B, CAS2, DONE2) in the above "proof".


    On Tue, Sep 8, 2015 at 3:26 PM James Aguilar <aguila...@gmail.com
    <javascript:>> wrote:
    On Tuesday, September 8, 2015 at 12:40:23 AM UTC-7, enormouspenguin
    wrote:
    On Tuesday, September 8, 2015 at 12:33:30 PM UTC+7, Henrik Johansson
    wrote:
    My understanding is that there are no such guarantees at all. Even
    ignoring memory effects there is still a race between the execution of the
    two goroutines determined by the scheduler.
    As I said, I mostly concern about visibility. I am aware of the race
    and know that at the end of main(), A and B could take any value but could
    each take an entirely different value from each other (e.g.: A == 20 && B
    == 10)?

    A CAS operation compose of 3 little ops that must execute as a single
    transaction: read, compare and write. All atomic functions in Go has
    implicit acq_rel semantic (I get it from here
    <https://github.com/golang/go/issues/5045#issuecomment-68050363>). My
    rationale is that because a CAS always have to execute a read first
    therefore although it failed at the comparison phase (leading to write not
    executed), it should see all side effects prior to the written value that
    it observed through the initial read.
    I read through the memory model <https://golang.org/ref/mem> after
    seeing this, and my impression was that it implies that this is safe. It
    says this:

    That is, r is guaranteed to observe w if both of the following hold:
    w happens before r.
    Any other write to the shared variable v either happens before w or
    after r.

    According to my understanding, we know that *w* happens before the
    succeeding CAS, because that is how program order is expressed within a
    single goroutine. We also know that the succeeding CAS happens before the
    failing CAS. And finally we know that the failing CAS happens before the
    *r*. As long as there is no other *w'*, I believe *r* should be
    guaranteed to observe the write.

    The memory model is not explicit about happens-before relationships
    between multiple shared variables. However, I'm not really sure how
    sync.Mutex could possibly work if such a relationship didn't exist, since
    it just uses atomics under the covers
    <http://golang.org/src/sync/mutex.go?s=1078:1100#L31>.

    That said, I didn't post anything last night because I wasn't confident,
    and I still am not. I wouldn't rely on behavior like this to work in a
    program. I would use mutex. If I needed the performance, I might wait until
    someone with more knowledge responded on this post, or read the disassembly
    and the manual for my processor until I gained confidence.

    --
    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 <javascript:>.
    For more options, visit https://groups.google.com/d/optout.
    --
    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 <javascript:>.
    For more options, visit https://groups.google.com/d/optout.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • James Aguilar at Sep 9, 2015 at 4:50 am
    Urgh. Double negatives in the first sentence. I'm increasingly certain that
    it's safe.
    On Tuesday, September 8, 2015 at 9:41:22 PM UTC-7, James Aguilar wrote:

    Looked into this some more. It's becoming increasingly difficult to
    believe it's not safe (for amd64 -- don't know about the rest but I'd guess
    the same given a plain language reading of the memory model).

    CompareAndSwapUint32
    <https://github.com/golang/go/blob/master/src/sync/atomic/asm_amd64.s#L35>,
    and the other CAS operations, all use LOCK CMPXCHG* ops. According to
    various docs I found (one example
    <http://mechanical-sympathy.blogspot.com/2011/07/memory-barriersfences.html>),
    all LOCK-prefixed instructions entail full memory barriers.

    So, previous stores in one thread followed by a CAS should be visible, at
    least on x64.
    On Tuesday, September 8, 2015 at 9:30:09 PM UTC-7, Henrik Johansson wrote:

    I still am not sure I agree since there is a write to a third variable,
    the guard. Is it not possible for write to A in one goroutine to be
    reordered visavis the read from A in the other goroutine? And even if this
    is safe it feels very counterintuitive from my experience with Java where
    reading and writing from shared variables outside of the various memory
    control structures is a no-no.

    ons 9 sep. 2015 kl 01:24 skrev Matt Harden <matt....@gmail.com>:
    Have you tried running this example with the race detector? I think if
    the race detector doesn't see a race here, you should be OK.

    I also think the memory model is explicit enough. Even though you have
    multiple variables, with a write to *any* given variable that "happens
    before" a read from that variable (and no other writes can occur in
    between) the written value will be seen by the read. You can apply this
    rule more than once, and to different variables.

    For A:
    A = 10 H.B. CAS1
    case 1: CAS1 succeeds; CAS2 fails
    No further writes to A occur
    CAS1 H.B. DONE1
    case 2: CAS1 fails; CAS2 succeeds
    B = 20 H.B. CAS2
    No further writes to B occur
    CAS2 H.B. CAS1
    CAS1 H.B. A = B
    A = B H.B. DONE1
    DONE1 H.B. WAIT
    WAIT H.B. A != B

    No race exists on A, because all reads and writes to A are ordered in a
    strict happens-before sequence. The behavior is just as if the reads and
    writes had occurred in sequence within a single thread.

    We can apply the same reasoning to prove there is no race on B, by
    swapping (A, CAS1, DONE1) with (B, CAS2, DONE2) in the above "proof".


    On Tue, Sep 8, 2015 at 3:26 PM James Aguilar <aguila...@gmail.com>
    wrote:
    On Tuesday, September 8, 2015 at 12:40:23 AM UTC-7, enormouspenguin
    wrote:
    On Tuesday, September 8, 2015 at 12:33:30 PM UTC+7, Henrik Johansson
    wrote:
    My understanding is that there are no such guarantees at all. Even
    ignoring memory effects there is still a race between the execution of the
    two goroutines determined by the scheduler.
    As I said, I mostly concern about visibility. I am aware of the race
    and know that at the end of main(), A and B could take any value but could
    each take an entirely different value from each other (e.g.: A == 20 && B
    == 10)?

    A CAS operation compose of 3 little ops that must execute as a single
    transaction: read, compare and write. All atomic functions in Go has
    implicit acq_rel semantic (I get it from here
    <https://github.com/golang/go/issues/5045#issuecomment-68050363>). My
    rationale is that because a CAS always have to execute a read first
    therefore although it failed at the comparison phase (leading to write not
    executed), it should see all side effects prior to the written value that
    it observed through the initial read.
    I read through the memory model <https://golang.org/ref/mem> after
    seeing this, and my impression was that it implies that this is safe. It
    says this:

    That is, r is guaranteed to observe w if both of the following hold:
    w happens before r.
    Any other write to the shared variable v either happens before w or
    after r.

    According to my understanding, we know that *w* happens before the
    succeeding CAS, because that is how program order is expressed within a
    single goroutine. We also know that the succeeding CAS happens before the
    failing CAS. And finally we know that the failing CAS happens before the
    *r*. As long as there is no other *w'*, I believe *r* should be
    guaranteed to observe the write.

    The memory model is not explicit about happens-before relationships
    between multiple shared variables. However, I'm not really sure how
    sync.Mutex could possibly work if such a relationship didn't exist, since
    it just uses atomics under the covers
    <http://golang.org/src/sync/mutex.go?s=1078:1100#L31>.

    That said, I didn't post anything last night because I wasn't
    confident, and I still am not. I wouldn't rely on behavior like this to
    work in a program. I would use mutex. If I needed the performance, I might
    wait until someone with more knowledge responded on this post, or read the
    disassembly and the manual for my processor until I gained confidence.

    --
    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.
    For more options, visit https://groups.google.com/d/optout.
    --
    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.
    For more options, visit https://groups.google.com/d/optout.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henrik Johansson at Sep 9, 2015 at 5:05 am
    Is it true in the general case or just on x86?

    I don't think I will rely on this unless it is determined to be a
    bottleneck though. Locks and channels are so much easier.
    On Wed, Sep 9, 2015, 06:50 James Aguilar wrote:

    Urgh. Double negatives in the first sentence. I'm increasingly certain
    that it's safe.

    On Tuesday, September 8, 2015 at 9:41:22 PM UTC-7, James Aguilar wrote:

    Looked into this some more. It's becoming increasingly difficult to
    believe it's not safe (for amd64 -- don't know about the rest but I'd guess
    the same given a plain language reading of the memory model).

    CompareAndSwapUint32
    <https://github.com/golang/go/blob/master/src/sync/atomic/asm_amd64.s#L35>,
    and the other CAS operations, all use LOCK CMPXCHG* ops. According to
    various docs I found (one example
    <http://mechanical-sympathy.blogspot.com/2011/07/memory-barriersfences.html>),
    all LOCK-prefixed instructions entail full memory barriers.

    So, previous stores in one thread followed by a CAS should be visible, at
    least on x64.
    On Tuesday, September 8, 2015 at 9:30:09 PM UTC-7, Henrik Johansson wrote:

    I still am not sure I agree since there is a write to a third variable,
    the guard. Is it not possible for write to A in one goroutine to be
    reordered visavis the read from A in the other goroutine? And even if this
    is safe it feels very counterintuitive from my experience with Java where
    reading and writing from shared variables outside of the various memory
    control structures is a no-no.

    ons 9 sep. 2015 kl 01:24 skrev Matt Harden <matt....@gmail.com>:
    Have you tried running this example with the race detector? I think if
    the race detector doesn't see a race here, you should be OK.

    I also think the memory model is explicit enough. Even though you have
    multiple variables, with a write to *any* given variable that "happens
    before" a read from that variable (and no other writes can occur in
    between) the written value will be seen by the read. You can apply this
    rule more than once, and to different variables.

    For A:
    A = 10 H.B. CAS1
    case 1: CAS1 succeeds; CAS2 fails
    No further writes to A occur
    CAS1 H.B. DONE1
    case 2: CAS1 fails; CAS2 succeeds
    B = 20 H.B. CAS2
    No further writes to B occur
    CAS2 H.B. CAS1
    CAS1 H.B. A = B
    A = B H.B. DONE1
    DONE1 H.B. WAIT
    WAIT H.B. A != B

    No race exists on A, because all reads and writes to A are ordered in a
    strict happens-before sequence. The behavior is just as if the reads and
    writes had occurred in sequence within a single thread.

    We can apply the same reasoning to prove there is no race on B, by
    swapping (A, CAS1, DONE1) with (B, CAS2, DONE2) in the above "proof".


    On Tue, Sep 8, 2015 at 3:26 PM James Aguilar <aguila...@gmail.com>
    wrote:
    On Tuesday, September 8, 2015 at 12:40:23 AM UTC-7, enormouspenguin
    wrote:
    On Tuesday, September 8, 2015 at 12:33:30 PM UTC+7, Henrik Johansson
    wrote:
    My understanding is that there are no such guarantees at all. Even
    ignoring memory effects there is still a race between the execution of the
    two goroutines determined by the scheduler.
    As I said, I mostly concern about visibility. I am aware of the race
    and know that at the end of main(), A and B could take any value but could
    each take an entirely different value from each other (e.g.: A == 20 && B
    == 10)?

    A CAS operation compose of 3 little ops that must execute as a single
    transaction: read, compare and write. All atomic functions in Go has
    implicit acq_rel semantic (I get it from here
    <https://github.com/golang/go/issues/5045#issuecomment-68050363>).
    My rationale is that because a CAS always have to execute a read first
    therefore although it failed at the comparison phase (leading to write not
    executed), it should see all side effects prior to the written value that
    it observed through the initial read.
    I read through the memory model <https://golang.org/ref/mem> after
    seeing this, and my impression was that it implies that this is safe. It
    says this:

    That is, r is guaranteed to observe w if both of the following hold:
    w happens before r.
    Any other write to the shared variable v either happens before w or
    after r.

    According to my understanding, we know that *w* happens before the
    succeeding CAS, because that is how program order is expressed within a
    single goroutine. We also know that the succeeding CAS happens before the
    failing CAS. And finally we know that the failing CAS happens before the
    *r*. As long as there is no other *w'*, I believe *r* should be
    guaranteed to observe the write.

    The memory model is not explicit about happens-before relationships
    between multiple shared variables. However, I'm not really sure how
    sync.Mutex could possibly work if such a relationship didn't exist, since
    it just uses atomics under the covers
    <http://golang.org/src/sync/mutex.go?s=1078:1100#L31>.

    That said, I didn't post anything last night because I wasn't
    confident, and I still am not. I wouldn't rely on behavior like this to
    work in a program. I would use mutex. If I needed the performance, I might
    wait until someone with more knowledge responded on this post, or read the
    disassembly and the manual for my processor until I gained confidence.

    --
    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.
    For more options, visit https://groups.google.com/d/optout.
    --
    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.
    For more options, visit https://groups.google.com/d/optout.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Enormouspenguin at Sep 9, 2015 at 7:28 am
    I agree with the explanations of Matt and James.

    On Wednesday, September 9, 2015 at 12:05:58 PM UTC+7, Henrik Johansson
    wrote:
    I don't think I will rely on this unless it is determined to be a
    bottleneck though. Locks and channels are so much easier.
    In production code, I definitely would not use CAS as it's too dangerous.
    But the CAS in the example just an interesting and confusing case that I
    encounter while playing around with atomic.

    I think we should not discuss the nitty-gritty of each arch/os here as
    there are too much details and it's too low level. We should focus on the
    (implicit) guarantee of the current compiler ("every atomic operation
    become memory_order_acq_rel", quoted *dvyukov <https://github.com/dvyukov>* from
    here <https://github.com/golang/go/issues/5045#issuecomment-68050363>) to
    explain what could and should happen.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henrik Johansson at Sep 9, 2015 at 8:42 am
    For reference there is an interesting discussion in the comments in
    http://bartoszmilewski.com/2008/12/01/c-atomics-and-memory-ordering/ by +Dmitry
    Vyukov <dvyukov@google.com> as well. Interesting read and as long as the
    atomic loads are on the same object the oredering semantics should be safe.
    Knock, knock.

    ons 9 sep. 2015 kl 09:28 skrev enormouspenguin <kimkhanh535@gmail.com>:
    I agree with the explanations of Matt and James.


    On Wednesday, September 9, 2015 at 12:05:58 PM UTC+7, Henrik Johansson
    wrote:
    I don't think I will rely on this unless it is determined to be a
    bottleneck though. Locks and channels are so much easier.
    In production code, I definitely would not use CAS as it's too dangerous.
    But the CAS in the example just an interesting and confusing case that I
    encounter while playing around with atomic.

    I think we should not discuss the nitty-gritty of each arch/os here as
    there are too much details and it's too low level. We should focus on the
    (implicit) guarantee of the current compiler ("every atomic operation
    become memory_order_acq_rel", quoted *dvyukov
    <https://github.com/dvyukov>* from here
    <https://github.com/golang/go/issues/5045#issuecomment-68050363>) to
    explain what could and should happen.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Dmitry Vyukov at Sep 9, 2015 at 8:48 am
    If we strive for the simple sequentially-consistent memory model, then
    a failed CAS needs to be an acquire operation (happen-after [some]
    previous mutating release operations on the variable) because it does
    reveal state of the object. If something explicitly or implicitly
    reveals state of an object and is not an acquire operation, then it
    breaks sequential consistency (like panicing double close of a
    channel).

    On Wed, Sep 9, 2015 at 10:41 AM, Henrik Johansson wrote:
    For reference there is an interesting discussion in the comments in
    http://bartoszmilewski.com/2008/12/01/c-atomics-and-memory-ordering/ by
    +Dmitry Vyukov as well. Interesting read and as long as the atomic loads are
    on the same object the oredering semantics should be safe. Knock, knock.

    ons 9 sep. 2015 kl 09:28 skrev enormouspenguin <kimkhanh535@gmail.com>:
    I agree with the explanations of Matt and James.


    On Wednesday, September 9, 2015 at 12:05:58 PM UTC+7, Henrik Johansson
    wrote:
    I don't think I will rely on this unless it is determined to be a
    bottleneck though. Locks and channels are so much easier.

    In production code, I definitely would not use CAS as it's too dangerous.
    But the CAS in the example just an interesting and confusing case that I
    encounter while playing around with atomic.

    I think we should not discuss the nitty-gritty of each arch/os here as
    there are too much details and it's too low level. We should focus on the
    (implicit) guarantee of the current compiler ("every atomic operation become
    memory_order_acq_rel", quoted dvyukov from here) to explain what could and
    should happen.


    --
    Dmitry Vyukov, Software Engineer, dvyukov@google.com
    Google Germany GmbH, Dienerstraße 12, 80331, München
    Geschäftsführer: Graham Law, Christine Elizabeth Flores
    Registergericht und -nummer: Hamburg, HRB 86891
    Sitz der Gesellschaft: Hamburg
    Diese E-Mail ist vertraulich. Wenn Sie nicht der richtige Adressat
    sind, leiten Sie diese bitte nicht weiter, informieren Sie den
    Absender und löschen Sie die E-Mail und alle Anhänge. Vielen Dank.
    This e-mail is confidential. If you are not the right addressee please
    do not forward it, please inform the sender, and please erase this
    e-mail including any attachments. Thanks.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henrik Johansson at Sep 10, 2015 at 6:17 am
    Thanks for clarification Dmitry! Does the race detctor catch this? Does it
    also work when the cas operation is performed on a member of a struct?
    cas(&a.b) for example. Does the sideffects work there as well and will the
    race detector catch errors?



    ons 9 sep. 2015 kl 10:48 skrev Dmitry Vyukov <dvyukov@google.com>:
    If we strive for the simple sequentially-consistent memory model, then
    a failed CAS needs to be an acquire operation (happen-after [some]
    previous mutating release operations on the variable) because it does
    reveal state of the object. If something explicitly or implicitly
    reveals state of an object and is not an acquire operation, then it
    breaks sequential consistency (like panicing double close of a
    channel).

    On Wed, Sep 9, 2015 at 10:41 AM, Henrik Johansson wrote:
    For reference there is an interesting discussion in the comments in
    http://bartoszmilewski.com/2008/12/01/c-atomics-and-memory-ordering/ by
    +Dmitry Vyukov as well. Interesting read and as long as the atomic loads are
    on the same object the oredering semantics should be safe. Knock, knock.

    ons 9 sep. 2015 kl 09:28 skrev enormouspenguin <kimkhanh535@gmail.com>:
    I agree with the explanations of Matt and James.


    On Wednesday, September 9, 2015 at 12:05:58 PM UTC+7, Henrik Johansson
    wrote:
    I don't think I will rely on this unless it is determined to be a
    bottleneck though. Locks and channels are so much easier.

    In production code, I definitely would not use CAS as it's too
    dangerous.
    But the CAS in the example just an interesting and confusing case that I
    encounter while playing around with atomic.

    I think we should not discuss the nitty-gritty of each arch/os here as
    there are too much details and it's too low level. We should focus on
    the
    (implicit) guarantee of the current compiler ("every atomic operation
    become
    memory_order_acq_rel", quoted dvyukov from here) to explain what could
    and
    should happen.


    --
    Dmitry Vyukov, Software Engineer, dvyukov@google.com
    Google Germany GmbH, Dienerstraße 12, 80331, München
    Geschäftsführer: Graham Law, Christine Elizabeth Flores
    Registergericht und -nummer: Hamburg, HRB 86891
    Sitz der Gesellschaft: Hamburg
    Diese E-Mail ist vertraulich. Wenn Sie nicht der richtige Adressat
    sind, leiten Sie diese bitte nicht weiter, informieren Sie den
    Absender und löschen Sie die E-Mail und alle Anhänge. Vielen Dank.
    This e-mail is confidential. If you are not the right addressee please
    do not forward it, please inform the sender, and please erase this
    e-mail including any attachments. Thanks.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Dmitry Vyukov at Sep 10, 2015 at 8:09 am
    Yes, it should.
    Do you have any counter examples?
    On Thu, Sep 10, 2015 at 8:17 AM, Henrik Johansson wrote:
    Thanks for clarification Dmitry! Does the race detctor catch this? Does it
    also work when the cas operation is performed on a member of a struct?
    cas(&a.b) for example. Does the sideffects work there as well and will the
    race detector catch errors?



    ons 9 sep. 2015 kl 10:48 skrev Dmitry Vyukov <dvyukov@google.com>:
    If we strive for the simple sequentially-consistent memory model, then
    a failed CAS needs to be an acquire operation (happen-after [some]
    previous mutating release operations on the variable) because it does
    reveal state of the object. If something explicitly or implicitly
    reveals state of an object and is not an acquire operation, then it
    breaks sequential consistency (like panicing double close of a
    channel).


    On Wed, Sep 9, 2015 at 10:41 AM, Henrik Johansson <dahankzter@gmail.com>
    wrote:
    For reference there is an interesting discussion in the comments in
    http://bartoszmilewski.com/2008/12/01/c-atomics-and-memory-ordering/ by
    +Dmitry Vyukov as well. Interesting read and as long as the atomic loads
    are
    on the same object the oredering semantics should be safe. Knock, knock.

    ons 9 sep. 2015 kl 09:28 skrev enormouspenguin <kimkhanh535@gmail.com>:
    I agree with the explanations of Matt and James.


    On Wednesday, September 9, 2015 at 12:05:58 PM UTC+7, Henrik Johansson
    wrote:
    I don't think I will rely on this unless it is determined to be a
    bottleneck though. Locks and channels are so much easier.

    In production code, I definitely would not use CAS as it's too
    dangerous.
    But the CAS in the example just an interesting and confusing case that
    I
    encounter while playing around with atomic.

    I think we should not discuss the nitty-gritty of each arch/os here as
    there are too much details and it's too low level. We should focus on
    the
    (implicit) guarantee of the current compiler ("every atomic operation
    become
    memory_order_acq_rel", quoted dvyukov from here) to explain what could
    and
    should happen.


    --
    Dmitry Vyukov, Software Engineer, dvyukov@google.com
    Google Germany GmbH, Dienerstraße 12, 80331, München
    Geschäftsführer: Graham Law, Christine Elizabeth Flores
    Registergericht und -nummer: Hamburg, HRB 86891
    Sitz der Gesellschaft: Hamburg
    Diese E-Mail ist vertraulich. Wenn Sie nicht der richtige Adressat
    sind, leiten Sie diese bitte nicht weiter, informieren Sie den
    Absender und löschen Sie die E-Mail und alle Anhänge. Vielen Dank.
    This e-mail is confidential. If you are not the right addressee please
    do not forward it, please inform the sender, and please erase this
    e-mail including any attachments. Thanks.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedSep 8, '15 at 4:39a
activeSep 10, '15 at 8:09a
posts15
users5
websitegolang.org

People

Translate

site design / logo © 2021 Grokbase