poundifdef
poundifdef

Reputation: 19370

Is it thread safe to create a new Mutex in Go?

I have a struct in Go which contains a mutex, and I want to ensure that that mutex is never nil. To that end, I have implemented a GetMutex() function, which checks if the mutex is nil, and if it is, then assigns it a value.

My question is: is the following code thread safe? If not, what would be an idiomatic way to ensure that mux is always initialized? The only thing I can think of is to have a global mutex in this package which is used within my GetMutex() function, but perhaps there is a different approach.

package main

import (
    "sync"
)

type Counter struct {
    mux *sync.Mutex
    counter int
}

// Is this thread safe?
func (c *Counter) GetMux() *sync.Mutex {
    if c.mux == nil {
        c.mux = &sync.Mutex{}
    }
    return c.mux
}

func (c *Counter) Inc() {
    c.GetMux().Lock()
    c.counter++
    c.GetMux().Unlock()
}

func main() {
    c := &Counter{}
    c.Inc()
}

Upvotes: 0

Views: 504

Answers (1)

icza
icza

Reputation: 418317

No, it's not safe if Counter.GetMux() is called from multiple goroutines concurrently: GetMux() both reads and writes the Counter.mux field.

The general way is to use a "constructor" like function that takes care of the initialization, like this:

func NewCounter() *Counter {
    return &Counter{
        mux: &sync.Mutex{},
    }
}

And of course always create counters with this NewCounter().

Another–limited–way would be to use a non-pointer mutex value:

type Counter struct {
    mux     sync.Mutex
    counter int
}

So when you have a Counter struct value, it–by design–includes a mutex. But if you do this, then Counter should always be used as a pointer, and Counter struct values must not be copied (else the mutex field would also be copied, but as package doc of sync states: "Values containing the types defined in this package should not be copied.").

The obvious advantage of this is that the zero value of Counter is a valid and ready counter (something you should aim for with your custom types), and no constructor function is needed.

Upvotes: 3

Related Questions