On Sat, Apr 14, 2018 at 10:02 AM, Penguin Enormous
<kimkhanh...@gmail.com> wrote:
>
> Could it be this:
>
> Initially wait == notify == 0
>
> Waiter      Signaler
>
> 479 atomic.Xadd(&l.wait, 1) = 1
>
>                 522 atomic.Load(&l.wait) = 0
>                 atomic.Load(&l.notify) = 0
>
>                 523 return (because those above are equal)
>
> 485 notifyListWait(l, t) (blocked forever)
>
> But looking at your answer, I see that you may imply certain race conditions
> are allowed. Could you explain a bit more on that? Aren't race conditions
> supposedly bad?
>
> If atomic load should always returns the last atomic write to a memory
> location and if I make absolutely sure that the last atomic write finish at
> a specific time instant earlier than the atomic load, I can never get data
> written before that last atomic write, is that what you meant?

I didn't mean to imply that data races are OK.  I meant that when
using condition variables, you have to be aware of the race conditions
between Wait and Signal/Broadcast, and you have to mitigate those race
conditions through correct use of the Locker associated with the
condition variable.  These race conditions are not data race
conditions, they are rather logical race conditions, in the sense that
if you do not correctly use the Locker then your goroutines can indeed
block forever in Wait.

You are describing the possible race in terms of the runtime code,
which is the wrong level.  You need to try to describe it in terms of
the sync.Cond methods.  If you do that you see that the atomic
increment of l.wait is done with Locker locked.  Then the Locker is
unlocked, and the code calls notifyListWait.  The loads of l.wait and
l.notify are done by the Signal and Broadcast methods.  While the
Signal and Broadcast methods do not require the Locker to be locked,
if you want to ensure that they wake up all waiting goroutines there
must be some sort of happens-before relationship between the waiting
goroutine and the signaling goroutine.  If there is no happens-before
relationship, then in theory the signal could happen before the wait,
in which case obviously the wait won't see the signal; that's just how
condition variables work.  For your program to work as expected, it
must be the case that the wait happens-before the signal.  And the
obvious way to do that is to acquire the lock before signaling.
Acquire the lock, verify that there is something to signal, and then
signal (releasing the lock before signaling is optional).

This is how condition variables work in any language, it's not specific to Go.

And this is the reason why condition variables are very rarely used in
Go.  It is much easier to reason correctly about channels and mutexes
than it is to reason about condition variables.  Therefore, Go
programs prefer channels and mutexes when at all possible.

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

Reply via email to