I am trying to understand how mutexes work. From my understanding so far, it is made to carry atomic operations and synchronize access to some data.
I built an example of a queue data structure here: https://github.com/arnauddri/algorithms/blob/master/data-structures%2Fqueue%2Fqueue.go
Here is a bit of the code:
package queue
import "sync"
type Queue struct {
queue []interface{}
len int
lock *sync.Mutex
}
func New() *Queue {
queue := &Queue{}
queue.queue = make([]interface{}, 0)
queue.len = 0
return queue
}
func (q *Queue) Push(el interface{}) {
q.lock.Lock()
defer q.lock.Unlock()
q.queue = append(q.queue, el)
q.len++
}
However when I try to create a queue and push an item to it I get a runtime error:
q := New()
q.Push(1)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
I really don't understand what is happening here.
How should I use the Mutex here?
Many thanks
Looks like the issue is that you're never instantiating the mutex. When you run the New()
function you're creating an empty Queue with a variable that can reference a mutex, but you never actually tell it to do so, which means that at this point queue.lock == nil
. You can fix this by adding in an instantiation line to your New()
function.
queue.lock = new(sync.Mutex)
Here is a playground demo that works: http://play.golang.org/p/Qa6buDaHIj
You get that error because you did not allocated any mutex, you have only a pointer to a mutex. Mutex usually are declared inside the structure and without the pointer. See working example below:
http://play.golang.org/p/8LF3yVOkSW
import "sync"
type Queue struct {
len int
lock sync.Mutex // add it before the fields that are being protected by the mutex
queue []interface{}
}
func New() *Queue {
queue := &Queue{}
queue.queue = make([]interface{}, 0)
queue.len = 0
return queue
}
func (q *Queue) Push(el interface{}) {
q.lock.Lock()
defer q.lock.Unlock()
q.queue = append(q.queue, el)
q.len++
}
func main() {
q := New()
q.Push(1)
}
Zero value for a pointer is nil, q.lock is a nil pointer, dereference nil pointer will cause panic. You can use lock sync.Mutex instead of *lock sync.Mutex, zero value for a Mutex is an unlocked mutex. Struct anonymous nesting can also solve your problem:
package queue
import "sync"
type Queue struct {
queue []interface{}
len int
sync.Mutex
}
func New() *Queue {
queue := &Queue{}
queue.queue = make([]interface{}, 0)
queue.len = 0
return queue
}
func (q *Queue) Push(el interface{}) {
q.Lock()
defer q.Unlock()
q.queue = append(q.queue, el)
q.len++
}