FAQ
Here is some example code that demonstrates my question:

var condition bool
var wg sync.WaitGroup
for _, item := range items {
wg.Add(1)
go func(item) {
if meetsCondition(item) {
condition = true
}
wg.Done()
}(item)
}
wg.Wait()
// is it safe to check condition here?

Does wg.Wait() act as a memory barrier so that it's safe to check
condition? If not, what's the best way to represent this idiom?
(atomic.Load/Store?) 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.

Search Discussions

  • Ian Lance Taylor at May 7, 2014 at 1:13 am

    On Tue, May 6, 2014 at 6:55 AM, wrote:
    Here is some example code that demonstrates my question:

    var condition bool
    var wg sync.WaitGroup
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    condition = true
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    // is it safe to check condition here?

    Does wg.Wait() act as a memory barrier so that it's safe to check condition?
    If not, what's the best way to represent this idiom? (atomic.Load/Store?)
    It is safe to check condition after wg.Wait returns.

    On the other hand you have several goroutines racing to set condition,
    so you should be using atomic.Store anyhow.

    Or just send a value on a channel, that always works and doesn't lead
    to any of these issues. Untested:

         var wg sync.WaitGroup
         c := make(chan bool, 1)
         for _, item := range items {
      wg.Add(1)
      go func(item) {
          if meetsCondition(item) {
       select {
       case c <- true:
       default:
       }
          }
          wg.Done()
      }(item)
         }
         wg.Wait()
         close(c)
         condition, _ := <-c

    Ian

    --
    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.
  • Kai Skye at May 7, 2014 at 1:34 am
    Well, I figure that the race condition is benign here, since even if a
    goroutine sees an old value, all I care about is that by the time wg.Wait()
    is finished, condition is either set or unset. But please correct me if I'm
    wrong about that.

    I like the idea of using channels but I couldn't figure out how to get it
    playing nicely with WaitGroup. In your example, you do:

    wg.Wait()
    close(c)
    condition, _ := <-c

    But I am under the impression that reading from a closed channel always
    returns a zero value, so condition would always be false in this case. Is
    that not true here? Otherwise, if we don't close the channel and just try
    to do "condition := <-c", then it would hang if "c <- true" is never called
    by any goroutine. Although, perhaps I could use a select statement after
    wg,Wait(), as in:

    wg.Wait()
    select {
         case <-c:
             // do stuff with condition == true
         default:
             // do other stuff
    }

    Does that seem reasonable?
    On Tuesday, May 6, 2014 9:12:28 PM UTC-4, Ian Lance Taylor wrote:
    On Tue, May 6, 2014 at 6:55 AM, <kai...@gmail.com <javascript:>> wrote:
    Here is some example code that demonstrates my question:

    var condition bool
    var wg sync.WaitGroup
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    condition = true
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    // is it safe to check condition here?

    Does wg.Wait() act as a memory barrier so that it's safe to check
    condition?
    If not, what's the best way to represent this idiom?
    (atomic.Load/Store?)

    It is safe to check condition after wg.Wait returns.

    On the other hand you have several goroutines racing to set condition,
    so you should be using atomic.Store anyhow.

    Or just send a value on a channel, that always works and doesn't lead
    to any of these issues. Untested:

    var wg sync.WaitGroup
    c := make(chan bool, 1)
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    select {
    case c <- true:
    default:
    }
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    close(c)
    condition, _ := <-c

    Ian
    --
    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.
  • Thomas Bushnell, BSG at May 7, 2014 at 1:43 am
    If two goroutines, write the same variable concurrently without any
    protection, you cannot be guaranteed at the end that the value is one or
    the other value.

    On Tue, May 6, 2014 at 6:34 PM, Kai Skye wrote:

    Well, I figure that the race condition is benign here, since even if a
    goroutine sees an old value, all I care about is that by the time wg.Wait()
    is finished, condition is either set or unset. But please correct me if I'm
    wrong about that.

    I like the idea of using channels but I couldn't figure out how to get it
    playing nicely with WaitGroup. In your example, you do:

    wg.Wait()
    close(c)
    condition, _ := <-c

    But I am under the impression that reading from a closed channel always
    returns a zero value, so condition would always be false in this case. Is
    that not true here? Otherwise, if we don't close the channel and just try
    to do "condition := <-c", then it would hang if "c <- true" is never called
    by any goroutine. Although, perhaps I could use a select statement after
    wg,Wait(), as in:

    wg.Wait()
    select {
    case <-c:
    // do stuff with condition == true
    default:
    // do other stuff
    }

    Does that seem reasonable?
    On Tuesday, May 6, 2014 9:12:28 PM UTC-4, Ian Lance Taylor wrote:
    On Tue, May 6, 2014 at 6:55 AM, wrote:
    Here is some example code that demonstrates my question:

    var condition bool
    var wg sync.WaitGroup
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    condition = true
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    // is it safe to check condition here?

    Does wg.Wait() act as a memory barrier so that it's safe to check
    condition?
    If not, what's the best way to represent this idiom?
    (atomic.Load/Store?)

    It is safe to check condition after wg.Wait returns.

    On the other hand you have several goroutines racing to set condition,
    so you should be using atomic.Store anyhow.

    Or just send a value on a channel, that always works and doesn't lead
    to any of these issues. Untested:

    var wg sync.WaitGroup
    c := make(chan bool, 1)
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    select {
    case c <- true:
    default:
    }
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    close(c)
    condition, _ := <-c

    Ian
    --
    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.
  • Kai Skye at May 7, 2014 at 2:11 am

    If two goroutines, write the same variable concurrently without any
    protection, you cannot be guaranteed at the end that the value is one or
    the other value.

    That's certainly true if multiple goroutines are attempting to write
    different values to the same location. But I think that in this case the
    race is benign because it's many goroutines all competing to write the same
    value. So if I have the value "false" stored initially, and 100 different
    goroutines compete to write the value "true", I don't really care which one
    wins as long as the value is "true" by the end. As long as the read happens
    after a memory barrier, I can't think of a situation where that could fail.
    But again, please correct me if I'm wrong.
    On Tuesday, May 6, 2014 9:43:11 PM UTC-4, Thomas Bushnell, BSG wrote:

    If two goroutines, write the same variable concurrently without any
    protection, you cannot be guaranteed at the end that the value is one or
    the other value.


    On Tue, May 6, 2014 at 6:34 PM, Kai Skye <kai...@gmail.com <javascript:>>wrote:
    Well, I figure that the race condition is benign here, since even if a
    goroutine sees an old value, all I care about is that by the time wg.Wait()
    is finished, condition is either set or unset. But please correct me if I'm
    wrong about that.

    I like the idea of using channels but I couldn't figure out how to get it
    playing nicely with WaitGroup. In your example, you do:

    wg.Wait()
    close(c)
    condition, _ := <-c

    But I am under the impression that reading from a closed channel always
    returns a zero value, so condition would always be false in this case. Is
    that not true here? Otherwise, if we don't close the channel and just try
    to do "condition := <-c", then it would hang if "c <- true" is never called
    by any goroutine. Although, perhaps I could use a select statement after
    wg,Wait(), as in:

    wg.Wait()
    select {
    case <-c:
    // do stuff with condition == true
    default:
    // do other stuff
    }

    Does that seem reasonable?
    On Tuesday, May 6, 2014 9:12:28 PM UTC-4, Ian Lance Taylor wrote:
    On Tue, May 6, 2014 at 6:55 AM, wrote:
    Here is some example code that demonstrates my question:

    var condition bool
    var wg sync.WaitGroup
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    condition = true
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    // is it safe to check condition here?

    Does wg.Wait() act as a memory barrier so that it's safe to check
    condition?
    If not, what's the best way to represent this idiom?
    (atomic.Load/Store?)

    It is safe to check condition after wg.Wait returns.

    On the other hand you have several goroutines racing to set condition,
    so you should be using atomic.Store anyhow.

    Or just send a value on a channel, that always works and doesn't lead
    to any of these issues. Untested:

    var wg sync.WaitGroup
    c := make(chan bool, 1)
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    select {
    case c <- true:
    default:
    }
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    close(c)
    condition, _ := <-c

    Ian
    --
    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.
  • Ian Lance Taylor at May 7, 2014 at 5:08 am

    On Tue, May 6, 2014 at 6:34 PM, Kai Skye wrote:
    Well, I figure that the race condition is benign here, since even if a
    goroutine sees an old value, all I care about is that by the time wg.Wait()
    is finished, condition is either set or unset. But please correct me if I'm
    wrong about that.
    In general all races should be avoided. I agree that this one is
    likely benign.
    I like the idea of using channels but I couldn't figure out how to get it
    playing nicely with WaitGroup. In your example, you do:

    wg.Wait()
    close(c)
    condition, _ := <-c

    But I am under the impression that reading from a closed channel always
    returns a zero value, so condition would always be false in this case. Is
    that not true here?
    A close of a channel is in effect sent on the channel. The close will
    only be seen after all other values have been read. So in this case
    if any goroutine wrote true to the channel, that is the value that
    will wind up in the variable "condition". If no goroutine wrote to
    the channel, then it will be closed, and "condition" will be set to
    the zero value == false.

    In fact I now see that the _ is pointless and it would fine to write
    simply
         condition := <-c

    Otherwise, if we don't close the channel and just try to
    do "condition := <-c", then it would hang if "c <- true" is never called by
    any goroutine. Although, perhaps I could use a select statement after
    wg,Wait(), as in:

    wg.Wait()
    select {
    case <-c:
    // do stuff with condition == true
    default:
    // do other stuff
    }

    Does that seem reasonable?
    Yes, that will also work, it's just more verbose.

    Ian

    On Tuesday, May 6, 2014 9:12:28 PM UTC-4, Ian Lance Taylor wrote:
    On Tue, May 6, 2014 at 6:55 AM, wrote:
    Here is some example code that demonstrates my question:

    var condition bool
    var wg sync.WaitGroup
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    condition = true
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    // is it safe to check condition here?

    Does wg.Wait() act as a memory barrier so that it's safe to check
    condition?
    If not, what's the best way to represent this idiom?
    (atomic.Load/Store?)
    It is safe to check condition after wg.Wait returns.

    On the other hand you have several goroutines racing to set condition,
    so you should be using atomic.Store anyhow.

    Or just send a value on a channel, that always works and doesn't lead
    to any of these issues. Untested:

    var wg sync.WaitGroup
    c := make(chan bool, 1)
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    select {
    case c <- true:
    default:
    }
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    close(c)
    condition, _ := <-c
    On Tue, May 6, 2014 at 6:34 PM, Kai Skye wrote:
    Well, I figure that the race condition is benign here, since even if a
    goroutine sees an old value, all I care about is that by the time wg.Wait()
    is finished, condition is either set or unset. But please correct me if I'm
    wrong about that.

    I like the idea of using channels but I couldn't figure out how to get it
    playing nicely with WaitGroup. In your example, you do:

    wg.Wait()
    close(c)
    condition, _ := <-c

    But I am under the impression that reading from a closed channel always
    returns a zero value, so condition would always be false in this case. Is
    that not true here? Otherwise, if we don't close the channel and just try to
    do "condition := <-c", then it would hang if "c <- true" is never called by
    any goroutine. Although, perhaps I could use a select statement after
    wg,Wait(), as in:

    wg.Wait()
    select {
    case <-c:
    // do stuff with condition == true
    default:
    // do other stuff
    }

    Does that seem reasonable?
    On Tuesday, May 6, 2014 9:12:28 PM UTC-4, Ian Lance Taylor wrote:
    On Tue, May 6, 2014 at 6:55 AM, wrote:
    Here is some example code that demonstrates my question:

    var condition bool
    var wg sync.WaitGroup
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    condition = true
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    // is it safe to check condition here?

    Does wg.Wait() act as a memory barrier so that it's safe to check
    condition?
    If not, what's the best way to represent this idiom?
    (atomic.Load/Store?)
    It is safe to check condition after wg.Wait returns.

    On the other hand you have several goroutines racing to set condition,
    so you should be using atomic.Store anyhow.

    Or just send a value on a channel, that always works and doesn't lead
    to any of these issues. Untested:

    var wg sync.WaitGroup
    c := make(chan bool, 1)
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    select {
    case c <- true:
    default:
    }
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    close(c)
    condition, _ := <-c

    Ian
    --
    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.
  • David Anderson at May 7, 2014 at 5:33 am
    An interesting perspective on "benign" data races:
    https://software.intel.com/en-us/blogs/2013/01/06/benign-data-races-what-could-possibly-go-wrong.
    It makes a good case for not standing for races that are "probably
    okay",
    because hidden assumptions can mess with you in very subtle ways down the
    road.

    - Dave

    On Tue, May 6, 2014 at 10:08 PM, Ian Lance Taylor wrote:
    On Tue, May 6, 2014 at 6:34 PM, Kai Skye wrote:

    Well, I figure that the race condition is benign here, since even if a
    goroutine sees an old value, all I care about is that by the time wg.Wait()
    is finished, condition is either set or unset. But please correct me if I'm
    wrong about that.
    In general all races should be avoided. I agree that this one is
    likely benign.
    I like the idea of using channels but I couldn't figure out how to get it
    playing nicely with WaitGroup. In your example, you do:

    wg.Wait()
    close(c)
    condition, _ := <-c

    But I am under the impression that reading from a closed channel always
    returns a zero value, so condition would always be false in this case. Is
    that not true here?
    A close of a channel is in effect sent on the channel. The close will
    only be seen after all other values have been read. So in this case
    if any goroutine wrote true to the channel, that is the value that
    will wind up in the variable "condition". If no goroutine wrote to
    the channel, then it will be closed, and "condition" will be set to
    the zero value == false.

    In fact I now see that the _ is pointless and it would fine to write
    simply
    condition := <-c

    Otherwise, if we don't close the channel and just try to
    do "condition := <-c", then it would hang if "c <- true" is never called by
    any goroutine. Although, perhaps I could use a select statement after
    wg,Wait(), as in:

    wg.Wait()
    select {
    case <-c:
    // do stuff with condition == true
    default:
    // do other stuff
    }

    Does that seem reasonable?
    Yes, that will also work, it's just more verbose.

    Ian

    On Tuesday, May 6, 2014 9:12:28 PM UTC-4, Ian Lance Taylor wrote:
    On Tue, May 6, 2014 at 6:55 AM, wrote:
    Here is some example code that demonstrates my question:

    var condition bool
    var wg sync.WaitGroup
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    condition = true
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    // is it safe to check condition here?

    Does wg.Wait() act as a memory barrier so that it's safe to check
    condition?
    If not, what's the best way to represent this idiom?
    (atomic.Load/Store?)
    It is safe to check condition after wg.Wait returns.

    On the other hand you have several goroutines racing to set condition,
    so you should be using atomic.Store anyhow.

    Or just send a value on a channel, that always works and doesn't lead
    to any of these issues. Untested:

    var wg sync.WaitGroup
    c := make(chan bool, 1)
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    select {
    case c <- true:
    default:
    }
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    close(c)
    condition, _ := <-c
    On Tue, May 6, 2014 at 6:34 PM, Kai Skye wrote:
    Well, I figure that the race condition is benign here, since even if a
    goroutine sees an old value, all I care about is that by the time wg.Wait()
    is finished, condition is either set or unset. But please correct me if I'm
    wrong about that.

    I like the idea of using channels but I couldn't figure out how to get it
    playing nicely with WaitGroup. In your example, you do:

    wg.Wait()
    close(c)
    condition, _ := <-c

    But I am under the impression that reading from a closed channel always
    returns a zero value, so condition would always be false in this case. Is
    that not true here? Otherwise, if we don't close the channel and just try to
    do "condition := <-c", then it would hang if "c <- true" is never called by
    any goroutine. Although, perhaps I could use a select statement after
    wg,Wait(), as in:

    wg.Wait()
    select {
    case <-c:
    // do stuff with condition == true
    default:
    // do other stuff
    }

    Does that seem reasonable?
    On Tuesday, May 6, 2014 9:12:28 PM UTC-4, Ian Lance Taylor wrote:
    On Tue, May 6, 2014 at 6:55 AM, wrote:
    Here is some example code that demonstrates my question:

    var condition bool
    var wg sync.WaitGroup
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    condition = true
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    // is it safe to check condition here?

    Does wg.Wait() act as a memory barrier so that it's safe to check
    condition?
    If not, what's the best way to represent this idiom?
    (atomic.Load/Store?)
    It is safe to check condition after wg.Wait returns.

    On the other hand you have several goroutines racing to set condition,
    so you should be using atomic.Store anyhow.

    Or just send a value on a channel, that always works and doesn't lead
    to any of these issues. Untested:

    var wg sync.WaitGroup
    c := make(chan bool, 1)
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    select {
    case c <- true:
    default:
    }
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    close(c)
    condition, _ := <-c

    Ian
    --
    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.
  • Dmitry Vyukov at May 7, 2014 at 5:46 am
    wg.Add happens-before wg.Wait

    You need to protect accesses to condition.
    Either change it to uint32 and use atomic.StoreUint32 or aggregate
    results with a channel:

    done := make(chan bool)
    for _, item := range items {
       go func(item) {
         done <- meetsCondition(item)
       }(item)
    }
    cond := false
    for _ := range items {
       cond = cond | <-done
    }


    On Tue, May 6, 2014 at 5:55 PM, wrote:
    Here is some example code that demonstrates my question:

    var condition bool
    var wg sync.WaitGroup
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    condition = true
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    // is it safe to check condition here?

    Does wg.Wait() act as a memory barrier so that it's safe to check condition?
    If not, what's the best way to represent this idiom? (atomic.Load/Store?)
    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.
    --
    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.
  • Rui Ueyama at May 7, 2014 at 5:59 am

    On Tue, May 6, 2014 at 10:45 PM, 'Dmitry Vyukov' via golang-nuts wrote:

    wg.Add happens-before wg.Wait
    Is it documented?

    You need to protect accesses to condition.
    Either change it to uint32 and use atomic.StoreUint32 or aggregate
    results with a channel:

    done := make(chan bool)
    for _, item := range items {
    go func(item) {
    done <- meetsCondition(item)
    }(item)
    }
    cond := false
    for _ := range items {
    cond = cond | <-done
    }


    On Tue, May 6, 2014 at 5:55 PM, wrote:
    Here is some example code that demonstrates my question:

    var condition bool
    var wg sync.WaitGroup
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    condition = true
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    // is it safe to check condition here?

    Does wg.Wait() act as a memory barrier so that it's safe to check
    condition?
    If not, what's the best way to represent this idiom? (atomic.Load/Store?)
    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.
    --
    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 May 7, 2014 at 6:17 am
    no
    there is an issue for sync/atomic:
    https://code.google.com/p/go/issues/detail?id=5045
    I've filed a similar one for sync:
    https://code.google.com/p/go/issues/detail?id=7948

    On Wed, May 7, 2014 at 9:58 AM, Rui Ueyama wrote:
    On Tue, May 6, 2014 at 10:45 PM, 'Dmitry Vyukov' via golang-nuts
    wrote:
    wg.Add happens-before wg.Wait

    Is it documented?
    You need to protect accesses to condition.
    Either change it to uint32 and use atomic.StoreUint32 or aggregate
    results with a channel:

    done := make(chan bool)
    for _, item := range items {
    go func(item) {
    done <- meetsCondition(item)
    }(item)
    }
    cond := false
    for _ := range items {
    cond = cond | <-done
    }


    On Tue, May 6, 2014 at 5:55 PM, wrote:
    Here is some example code that demonstrates my question:

    var condition bool
    var wg sync.WaitGroup
    for _, item := range items {
    wg.Add(1)
    go func(item) {
    if meetsCondition(item) {
    condition = true
    }
    wg.Done()
    }(item)
    }
    wg.Wait()
    // is it safe to check condition here?

    Does wg.Wait() act as a memory barrier so that it's safe to check
    condition?
    If not, what's the best way to represent this idiom?
    (atomic.Load/Store?)
    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.
    --
    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.
  • Nico at May 7, 2014 at 9:15 am

    On 07/05/14 07:17, 'Dmitry Vyukov' via golang-nuts wrote:
    no
    there is an issue for sync/atomic:
    https://code.google.com/p/go/issues/detail?id=5045
    Dmitry,

    in the second of the litmus tests you mentioned above:

    2.

    // goroutine 1
    atomic.Store(&X, 1)
    r1 = atomic.Load(&Y)

    // goroutine 2
    atomic.Store(&Y, 1)
    r2 = atomic.Load(&X)

    // afterwards
    if r1 == 0 && r2 == 0 {
        panic("broken")
    }

    does the code below "afterwards" belong to a third goroutine or to
    goroutine 2?

    --
    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 May 7, 2014 at 9:38 am

    On Wed, May 7, 2014 at 1:15 PM, Nico wrote:
    On 07/05/14 07:17, 'Dmitry Vyukov' via golang-nuts wrote:

    no
    there is an issue for sync/atomic:
    https://code.google.com/p/go/issues/detail?id=5045

    Dmitry,

    in the second of the litmus tests you mentioned above:

    2.

    // goroutine 1
    atomic.Store(&X, 1)
    r1 = atomic.Load(&Y)

    // goroutine 2
    atomic.Store(&Y, 1)
    r2 = atomic.Load(&X)

    // afterwards
    if r1 == 0 && r2 == 0 {
    panic("broken")
    }

    does the code below "afterwards" belong to a third goroutine or to goroutine
    2?
    It runs when both goroutines have stored results to r1/r2 in any
    goroutine (it's not important).

    --
    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.
  • Nico at May 7, 2014 at 10:01 am

    On 07/05/14 10:38, Dmitry Vyukov wrote:
    On Wed, May 7, 2014 at 1:15 PM, Nico wrote:
    On 07/05/14 07:17, 'Dmitry Vyukov' via golang-nuts wrote:

    no
    there is an issue for sync/atomic:
    https://code.google.com/p/go/issues/detail?id=5045

    Dmitry,

    in the second of the litmus tests you mentioned above:

    2.

    // goroutine 1
    atomic.Store(&X, 1)
    r1 = atomic.Load(&Y)

    // goroutine 2
    atomic.Store(&Y, 1)
    r2 = atomic.Load(&X)

    // afterwards
    if r1 == 0 && r2 == 0 {
    panic("broken")
    }

    does the code below "afterwards" belong to a third goroutine or to goroutine
    2?
    It runs when both goroutines have stored results to r1/r2 in any
    goroutine (it's not important).
    OK, so both atomic loads happen before the execution of the code below
    "afterwards".

    Why is it important not to break this litmus test?

    ---

    My opinion about issue 5045 is that:

    - sync/atomic should already guarantee the litmus test 1 (I don't see a
    reason not to make the change in the documentation now; I imagine
    everyone using sync/atomic expects this guarantee to hold)

    - the documentation in sync/atomic can be updated again once a decision
    has been made about the litmus test 2.



    --
    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.
  • Nico at May 7, 2014 at 10:39 am

    On 07/05/14 10:59, Dmitry Vyukov wrote:
    Why is it important not to break this litmus test?
    Because otherwise the model allows not sequentially-consistent
    executions. It's extremely difficult to reason about non
    sequentially-consistent executions.
    The current memory model in Go doesn't guarantee a
    sequentially-consistent execution of goroutines. That's what channels
    are used for.

    Why would sequential consistency be guaranteed when using sync/atom and
    not for all running goroutines?

    Am I missing something else?

    --
    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 May 7, 2014 at 10:44 am

    On Wed, May 7, 2014 at 2:39 PM, Nico wrote:
    On 07/05/14 10:59, Dmitry Vyukov wrote:

    Why is it important not to break this litmus test?

    Because otherwise the model allows not sequentially-consistent
    executions. It's extremely difficult to reason about non
    sequentially-consistent executions.
    The current memory model in Go doesn't guarantee a sequentially-consistent
    execution of goroutines. That's what channels are used for.

    Why would sequential consistency be guaranteed when using sync/atom and not
    for all running goroutines?

    Am I missing something else?

    What exactly deviations from sequential consistency do you mean?

    --
    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.
  • Nico at May 7, 2014 at 11:02 am

    On 07/05/14 11:44, Dmitry Vyukov wrote:
    What exactly deviations from sequential consistency do you mean?
    If I understand the current memory model correctly, the example below is
    allowed to output either 12 or 21.

    http://play.golang.org/p/g09XyHzzzW


    package main

    func main() {
      ch := make(chan int, 2)
      go func() {
       ch <- 1
       ch <- 2
      }()
      print(<-ch)
      print(<-ch)
    }

    --
    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.
  • Nico at May 7, 2014 at 11:06 am

    On 07/05/14 12:02, Nico wrote:
    On 07/05/14 11:44, Dmitry Vyukov wrote:
    What exactly deviations from sequential consistency do you mean?
    If I understand the current memory model correctly, the example below is
    allowed to output either 12 or 21.

    http://play.golang.org/p/g09XyHzzzW


    package main

    func main() {
    ch := make(chan int, 2)
    go func() {
    ch <- 1
    ch <- 2
    }()
    print(<-ch)
    print(<-ch)
    }

    This is a bad example.
    Let me come up with something else.

    --
    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.
  • Nico at May 7, 2014 at 11:17 am
    The first thing I had in mind was this example (which is allowed to
    output 00, 02, 10 or 12 :

    http://play.golang.org/p/sxB2hQBmIS


    package main

    func main() {
      var a, b int64
      go func() {
       a = 1
       b = 2
      }()
      print(a)
      print(b)
    }

    Since writing and reading a and b from different goroutines is not safe,
    this example is best rewritten using "sync/atomic":

    http://play.golang.org/p/-pcpO8jggJ


    package main

    import "sync/atomic"

    func main() {
      var a, b int64
      go func() {
       atomic.StoreInt64(&a, 1)
       atomic.StoreInt64(&b, 1)
      }()
      print(a)
      print(b)
    }

    I didn't want to use "sync/atomic" because that is exactly what issue
    5045 is all about, but you can see with the first example what I meant
    by not being sequentially consistent.

    --
    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 May 7, 2014 at 11:55 am

    On Wed, May 7, 2014 at 3:17 PM, Nico wrote:
    The first thing I had in mind was this example (which is allowed to output
    00, 02, 10 or 12 :

    http://play.golang.org/p/sxB2hQBmIS


    package main

    func main() {
    var a, b int64
    go func() {
    a = 1
    b = 2
    }()
    print(a)
    print(b)
    }

    Since writing and reading a and b from different goroutines is not safe,
    this example is best rewritten using "sync/atomic":

    http://play.golang.org/p/-pcpO8jggJ


    package main

    import "sync/atomic"

    func main() {
    var a, b int64
    go func() {
    atomic.StoreInt64(&a, 1)
    atomic.StoreInt64(&b, 1)
    }()
    print(a)
    print(b)
    }

    I didn't want to use "sync/atomic" because that is exactly what issue 5045
    is all about, but you can see with the first example what I meant by not
    being sequentially consistent.

    The first example has data race and behavior of the program is
    undefined. The second one is based on sync/atomic, behavior of which
    we are trying to define. So for now I do not see what does not
    guarantee sequenial consistency.

    --
    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.
  • Nico at May 7, 2014 at 4:37 pm
    I think I've managed to come up with an example of non-sequential
    execution allowed by the current memory model. See here:

    http://play.golang.org/p/J2gwQJcqMl


    package main

    func main() {
      ch1 := make(chan int)
      ch2 := make(chan int)

      go func() {
       // Go is allowed to reorder the two statements below
       ch1 <- 1
       print(<-ch2)
      }()

      // Go is not allowed to reorder the two statements below
      print(<-ch1)
      ch2 <- 2

      // Possible outputs:
      // 1
      // 12
      // [deadlock]

    }

    --
    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 May 8, 2014 at 6:23 am
    There are control dependencies involved.
    For example the following program:

    var c chan int
    c <- 0
    println("1")

    must not print.

    I strongly believe that the intention is that your program cannot deadlock.


    On Wed, May 7, 2014 at 8:36 PM, Nico wrote:
    I think I've managed to come up with an example of non-sequential execution
    allowed by the current memory model. See here:

    http://play.golang.org/p/J2gwQJcqMl


    package main

    func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)

    go func() {
    // Go is allowed to reorder the two statements below
    ch1 <- 1
    print(<-ch2)
    }()

    // Go is not allowed to reorder the two statements below
    print(<-ch1)
    ch2 <- 2

    // Possible outputs:
    // 1
    // 12
    // [deadlock]

    }
    --
    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.
  • Nico at May 8, 2014 at 8:46 am
    I meant to use buffered channels in my last example.

    On 08/05/14 07:22, Dmitry Vyukov wrote:
    There are control dependencies involved.
    What control dependencies would apply to buffered channels?


    http://play.golang.org/p/Q-28faUQ4X

    package main

    func main() {
      ch1 := make(chan int, 1)
      ch2 := make(chan int, 1)

      go func() {
       // Does the current memory model allow Go to reorder the two
    statements below?
       // If so, this program would deadlock
       ch1 <- 1
       <-ch2
      }()

      // The current memory model does not allow Go to reorder the two
    statements below
      <-ch1
      ch2 <- 2
    }

    --
    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 May 9, 2014 at 11:12 am

    On Thu, May 8, 2014 at 9:46 AM, Nico wrote:
    I meant to use buffered channels in my last example.


    On 08/05/14 07:22, Dmitry Vyukov wrote:

    There are control dependencies involved.

    What control dependencies would apply to buffered channels?
    For example, if a chan operation blocks forever, then nothing below it
    can hoist above it. Otherwise the program will execute something that
    it must not execute. I believe something similar applies to the case
    below.
    http://play.golang.org/p/Q-28faUQ4X

    package main

    func main() {
    ch1 := make(chan int, 1)
    ch2 := make(chan int, 1)

    go func() {
    // Does the current memory model allow Go to reorder the two
    statements below?
    // If so, this program would deadlock
    ch1 <- 1
    <-ch2
    }()

    // The current memory model does not allow Go to reorder the two
    statements below
    <-ch1
    ch2 <- 2
    }
    --
    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.
  • Nico at May 9, 2014 at 1:47 pm

    On 09/05/14 12:12, Dmitry Vyukov wrote:
    What control dependencies would apply to buffered channels?
    For example, if a chan operation blocks forever, then nothing below it
    can hoist above it. Otherwise the program will execute something that
    it must not execute.
    Should Go's memory model be updated to guarantee that?
    If so, how?
    Simply by saying that sequential-consistency is always guaranteed for
    channel communications and atomic operations?

    --
    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 May 10, 2014 at 8:19 am

    On Fri, May 9, 2014 at 2:47 PM, Nico wrote:
    On 09/05/14 12:12, Dmitry Vyukov wrote:

    What control dependencies would apply to buffered channels?

    For example, if a chan operation blocks forever, then nothing below it
    can hoist above it. Otherwise the program will execute something that
    it must not execute.

    Should Go's memory model be updated to guarantee that?
    If so, how?
    Simply by saying that sequential-consistency is always guaranteed for
    channel communications and atomic operations?

    This is not about sequential consistency.
    Program order is important. A goroutine executes program in program
    order. Other goroutines can see results of the goroutine actions in a
    different order. So if you do:

    c1, c2 := make(chan int, 1), make(chan int, 1)

    // goorutine 1
    c1 <- 1
    c2 <- 1

    // goroutine 2
    r2 := false
    select {
    case c2 <- 1:
    default: r2 = true
    }
    r1 := false
    select {
    case c1 <- 1:
    default: r1 = true
    }

    r2 == true && r1 == false is a possible outcome.

    But the following program still must not deadlock:

    c1, c2 := make(chan int), make(chan int)

    // goroutine 1
    c1 <- 1
    <-c2

    // goroutine 2
    <-c1
    c2 <- 1

    Because goroutine 1 executes the send first, and it must be eventually
    visible to goroutine 2.

    --
    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
postedMay 7, '14 at 12:28a
activeMay 10, '14 at 8:19a
posts25
users7
websitegolang.org

People

Translate

site design / logo © 2022 Grokbase