Понимание паники Голанга - PullRequest
       4

Понимание паники Голанга

0 голосов
/ 26 апреля 2018

У меня есть некоторый код Голанга, который очень периодически (раз в несколько часов) приводит к панике, и мне нужно некоторое руководство, чтобы понять, как найти причину. Код такой (с номерами строк):

audio-process.go:

    var MyChannel chan<- interface{}
    var newDatagramList = list.New()

    func operateAudioProcessing() {
        var channel = make(chan interface{})
        MyChannel = channel
        newDatagramList.Init()
        ...
415     go func() {
416        for cmd := range channel {
417            switch msg := cmd.(type) {
418                 case *MyThing:
419                 {
420                     newDatagramList.PushBack(msg)
421                 }
422             }
423         }
424     }()
425 }

... и паника возникает при вызове newDatagramList.PushBack() в строке 420. Код, отправляемый на этот канал:

audio-in.go:

    thing := new(MyThing)
    ...
    MyChannel <- thing

... и, для завершения картины, есть отдельное время go func(), которое обрабатывает newDatagramList следующим образом:

go func() {
    var next *list.Element
    for _ = range processTicker.C {
        for newElement := newDatagramList.Front(); newElement != nil; newElement = next {
            next = newElement.Next();
            myProcessingFunction(newElement.Value.(*MyThing))
            newDatagramList.Remove(newElement)
        }
}

Выход паники:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x5702d1]

goroutine 12 [running]:
panic(0x76e520, 0xc82000e100)
/usr/lib/go-1.6/src/runtime/panic.go:481 +0x3e6
container/list.(*List).PushBack(0xc820054e40, 0x6c4f80, 0xc827294e20, 0xc8273e0b01)
/usr/lib/go-1.6/src/container/list/list.go:139 +0x1c1
main.operateAudioProcessing.func2(0xc8200164e0, 0xc82000f310, 0xc820024078, 0x240)
/home/rob/gocode/src/audio-process.go:420 +0x58b
created by main.operateAudioProcessing
/home/rob/gocode/src/audio-process.go:442 +0x5ba

Какая вещь говорит мне, что паника здесь виновата? Не было никаких претензий по поводу выделения и отправки в канал в первую очередь, поэтому я не вижу, как это может быть неправильно. newDatagramList четко инициализирован (и канал уже некоторое время работает и принимает сообщения).

Как мне определить, что заставляет меня идти на ура?

1 Ответ

0 голосов
/ 26 апреля 2018

Стек паники:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x5702d1]

goroutine 12 [running]:
panic(0x76e520, 0xc82000e100)
/usr/lib/go-1.6/src/runtime/panic.go:481 +0x3e6
container/list.(*List).PushBack(0xc820054e40, 0x6c4f80, 0xc827294e20, 0xc8273e0b01)
/usr/lib/go-1.6/src/container/list/list.go:139 +0x1c1
main.operateAudioProcessing.func2(0xc8200164e0, 0xc82000f310, 0xc820024078, 0x240)
/home/rob/gocode/src/audio-process.go:420 +0x58b
created by main.operateAudioProcessing
/home/rob/gocode/src/audio-process.go:442 +0x5ba

Говорит, что "неверный адрес памяти или разыменование нулевого указателя" происходит в верхнем списке кадров

 container/list.(*List).PushBack(0xc820054e40, 0x6c4f80, 0xc827294e20, 0xc8273e0b01)

Вкл.

 /usr/lib/go-1.6/src/container/list/list.go:139 +0x1c1

Что выглядит как от godoc

// PushBack inserts a new element e with value v at the back of list l and returns e.
func (l *List) PushBack(v interface{}) *Element {
    l.lazyInit()
    return l.insertValue(v, l.root.prev)
}

Это круто, так как именно в этом кадре он выглядит с ошибкой l.root.prev

Тип данных, который создается на container/list, полностью несинхронизирован, и существует много чередований подпрограмм go, которые могут привести к промежуточному состоянию, в котором одновременно добавляются указатели на элементы списка.

Например, remove имеет много разных операций:

func (l *List) remove(e *Element) *Element {
    e.prev.next = e.next
    e.next.prev = e.prev
    e.next = nil // avoid memory leaks
    e.prev = nil // avoid memory leaks
    e.list = nil
    l.len--
    return e
}

Если это было выполнено синхронно, проблем не было бы, но потому что на нем работают несколько процедур go.

Предположим, у нас есть элемент E, с указателем на предыдущий и следующий, и две goroutines, действующие на него

PREV - E - NEXT  

GOROUTINE1                                           GOROUTINE2

READ - E.PREV returns element (E2) with `NEXT` -> E

                                                    REMOVE(E) is called
                                                    e.next = nil // avoid memory leaks

E2.NEXT.NEXT access occurs now Nil!!! 
resulting in panic 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...