Why doesn't Go use atomic.LoadInt32() in sync.Mutex.lockSlow()?

171 views
Skip to first unread message

机智的小小帅

unread,
Apr 1, 2022, 11:31:46 PM4/1/22
to golang-nuts

Hi,all. I'm reading the source code of sync package recently.

Here is a code snippet in sync/mutex.go.

// src/sync/mutex.go
type Mutex struct {
    state int32
    sema  uint32
}

func (m *Mutex) lockSlow() {
    var waitStartTime int64
    starving := false
    awoke := false
    iter := 0
    old := m.state // Here!!! not use atomic.LoadInt32()
    for {
        // Don't spin in starvation mode, ownership is handed off to waiters
        // so we won't be able to acquire the mutex anyway.
        if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {
            // Active spinning makes sense.
            // Try to set mutexWoken flag to inform Unlock
            // to not wake other blocked goroutines.
            if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&
                atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
                awoke = true
            }
            // ...
            old = m.state // And here!!! not use atomic.LoadInt32()
            continue
        }
    // ...
  }
  // ...
}


You can see full context in link1 and link2

I have read The Go Memory Model, It says "Reads and writes of values larger than a single machine word behave as multiple machine-word-sized operations in an unspecified order."

Althought it is usually 32 or 64 bits on modern machine,I think if you want a operator is atomic,you should use atomic.LoadXXX() explicitly.

I think a ordinary read operator like old := m.state in concurrent environment is not safe,it may read a unexpected value.

I wonder whether the oridinary read operation is right? Could you help me? Thanks in advance.

I'm very sorry to disturb you...

Ian Lance Taylor

unread,
Apr 1, 2022, 11:41:00 PM4/1/22
to 机智的小小帅, golang-nuts
When reading or writing a value that is the size of a memory word or
smaller and is aligned such that the value does not cross a word
boundary in memory Go assumes that the entire value will always be
read or written as a unit. This is true of all processors that Go
currently supports.

The purpose of an atomic operation is different: it is about when the
value is visible to other cores on the same system.

Ian

eric...@arm.com

unread,
Apr 1, 2022, 11:55:52 PM4/1/22
to golang-nuts
Why is it not safe to read here? The real write operation is done through CAS, assuming the read value is out of date, then the CAS operation will fail.

liweiforeveryoung

unread,
Apr 3, 2022, 4:39:47 AM4/3/22
to golang-nuts

Thanks for your reply,eric.I've always focused on this situation,without atomic operator,If goroutine A set a variable v to 0 and goroutine B set the variable a to 1,anther goroutine C would probably get a unexpected value which is neither 0 nor 1 when its read operation of a is interrupted.So I thought the code is at risk when this happens.

But I'm wrong.As Ian Lance Taylor said,Go will ensure the read operation of mutex.state is a unit in this case,so what i said will never happen.And as you said, the CAS operation will failed even if what i said happened.

liweiforeveryoung

unread,
Apr 3, 2022, 4:40:37 AM4/3/22
to golang-nuts

Thanks for your reply,Ian.I've always had a misunderstanding about atomic operation,I thought atomic operation is only used to protect the CPU instructions from being interrupted.

I have watched some lectures about C++ memory order on youtube.As you mentioned,another function of atomic operation is to synchronize the value between CPU cache and memory.

As eric said, there is no need to read the latest value of mutex.state here.Even if the latest value of mutex.state is read by atomic.LoadInt32(),it may out of date when execute the CAS operation.

I leave the link of lecture here,it may help people who have the same confusion as me.

C++ and Beyond 2012: Herb Sutter - atomic Weapons 1 of 2

Arvid Norberg: The C++ memory model: an intuition

Reply all
Reply to author
Forward
0 new messages