Есть ли условие гонки в реализации mutex в golang, когда m.state читается без атомарной функции - PullRequest
0 голосов
/ 29 апреля 2019

В golang, если две goroutines читают и записывают переменную без мьютекса и атомарного значения, это может привести к состоянию гонки данных.

Использование команды go run --race xxx.go определит точку гонки.

ПокаРеализация Mutex в src / sync / mutex.go использует следующий код

       func (m *Mutex) Lock() {
   // Fast path: grab unlocked mutex.
   if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
       if race.Enabled {
           race.Acquire(unsafe.Pointer(m))
       }
       return
   }

   var waitStartTime int64
   starving := false
   awoke := false
   iter := 0
   old := m.state     // This line confuse me !!!
       ......

Код old := m.state смущает меня, потому что m.state читается и пишется различными программами.

Следующая функция Test очевидна имеет проблему состояния гонки.Но если я помещу его в mutex.go, никакое состояние гонки не обнаружит.

# mutex.go
func Test(){
    a := int32(1)
        go func(){
                atomic.CompareAndSwapInt32(&a, 1, 4)
        }()
        _ = a
}

Если поместить его в другой пакет, например src / os / exec.go, проблема с расой условий обнаружит.

package main

import(
    "sync"
    "os"
)
func main(){
    sync.Test()        // race condition will not detect
    os.Test()          // race condition will detect
}

Ответы [ 2 ]

2 голосов
/ 29 апреля 2019

Прежде всего источник golang всегда меняется, поэтому давайте убедимся, что мы смотрим на одно и то же. Принять релиз 1.12 на

https://github.com/golang/go/blob/release-branch.go1.12/src/sync/mutex.go

как вы сказали, начинается функция блокировки

func (m *Mutex) Lock() {
    // fast path where it will set the high order bit and return if not locked
    if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
        return
    }
    //reads value to decide on the lower order bits
    for {
        //if statements involving CompareAndSwaps on the lower order bits
    }
}

Что делает этот CompareAndSwap? он выглядит атомарно в этом int32 и, если он равен 0, он меняет его на mutexLocked (который равен 1, как указано выше как const) и возвращает true, что он поменял его местами. Тогда это быстро возвращается. Это его быстрый путь. Программа заполучила блокировку, и теперь она может запустить защищенный путь.

Если оно уже равно 1 (mutexLocked), оно не меняет его и возвращает false (оно не менялось).

Затем он читает состояние и входит в цикл, который выполняет атомарное сравнение, и меняет местами, чтобы определить, как он должен себя вести.

Каковы возможные состояния? комбинации заблокированных, проснувшихся и голодных, как вы видите из константного блока.

Теперь, в зависимости от того, сколько времени программа ожидала в списке ожидания, она получит приоритет, когда снова проверить, свободен ли мьютекс.

Но также обратите внимание, что только Unlock () может установить бит mutexLocked обратно в 0. в цикле Lock () CAS единственными установленными битами являются голодные и пробужденные. Да, у вас может быть несколько читателей, но только один писатель в любое время, и этот писатель является тем, кто держит мьютекс и выполняет его защищенный путь до вызова Unlock (). Проверьте эту статью для более подробной информации.

0 голосов
/ 30 апреля 2019

Путем дизассемблирования двоичного выходного файла функция Test в другом пакете генерирует другой код.

Причина в том, что компилятор запрещает генерировать инструмент обнаружения гонки в пакете синхронизации.

Код:


var norace_inst_pkgs = []string{"sync", "sync/atomic"}  // https://github.com/golang/go/blob/release-branch.go1.12/src/cmd/compile/internal/gc/racewalk.go
``

...