Understanding mutex behviour

Issue

I was thinking mutex in Go would lock the data and won’t allow read/write by any other goroutine unless the fist goroutine releases the lock. It seems like my understanding was wrong. The only way to block read/write from other goroutine is to call lock in other goroutines as well. This would ensure critical section is accessed by one and only one goroutine.

So, I would expect this code to have a deadlock:

package main

import(
    "fmt"
    "sync"
)

type myMap struct {
    m map[string]string
    mutex sync.Mutex
}

func main() {
    done := make(chan bool)
    ch := make(chan bool)
    myM := &myMap{
        m:     make(map[string]string),
    }
    go func() {
        myM.mutex.Lock()
        myM.m["x"] = "i"
        fmt.Println("Locked. Won't release the Lock")
        ch <- true
    }()

    go func() {
        <- ch
        fmt.Println("Trying to write to the myMap")
        myM.m["a"] = "b"
        fmt.Println(myM)
        done <- true
    }()
    <- done
}

Since the fist goroutine locks the struct, I would expect the second goroutine to fail to read/write to the struct but that not happening here.

If I will add mux.Lock() in second goroutine then there will be a deadlock.

I find it a little weird the way mutex works in Go. If I lock then Go shouldn’t allow any other goroutine to read/write to it.

Can someone explain to me the mutex concept in Go?

Solution

There’s no magical force field that surrounds a mutex, protecting any datastructure it happens to be embedded in. If you lock a mutex, it prevents other code from locking it until it’s unlocked. Nothing more, nothing less. It’s well documented in the sync package.

So in your code, where there’s exactly one myM.mutex.Lock(), the effect is the same as if there was no mutex.

A correct use of a mutex that protects data involves locking the mutex before updating or reading the data, and then unlocking it afterwards. Often this code will be wrapped in a function so that defer can be used:

func doSomething(myM *myMap) {
    myM.mutex.Lock()
    defer myM.mutex.Unlock()
    ... read or update myM
}

Answered By – Paul Hankin

Answer Checked By – Terry (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.