7

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

1
  • tangential to this, but I have a very fast queue implementation at github.com/eapache/queue which you can use - adding mutexes to that is probably still faster than writing your own Commented Jan 30, 2015 at 19:52

3 Answers 3

21

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)
}
Sign up to request clarification or add additional context in comments.

2 Comments

This should be the chosen answer, you don't need to make the mutex a pointer.
@fabrizioM Why do you need to add it before the protected fields?
7

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

1 Comment

Or change the declaration of lock to be lock sync.Mutex., no pointer
2

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++
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.