Reputation: 19370
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
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