Golang. Получить тип паники

#go #channel #panic

#Вперед #канал #паника

Вопрос:

Есть код:

 func (c *Connector) SendPacketFuture(p []byte) (future chan []byte) {
    defer func() {
        // TODO Check r to catch only chan panics
        if r := recover(); r != nil {
            future = nil
        }
    }()
    t := newConnectorTask(p)
    c.tasks <- t
    future = t.PacketFromServerChan
    return
}
  

TODO это довольно понятно.
c.tasks это канал, и он может быть закрыт другой подпрограммой. Поскольку нет безопасного способа отправки на канал, который можно закрыть, я ловлю панику здесь. Проблема в том, что могут возникать разные паники, и я хочу отреагировать на ту, которая возникает, путем записи в закрытый канал.

Возможно ли это сделать в Go?

Комментарии:

1. В общем, вы не должны пытаться это обнаружить, и есть другой способ создания программы. Есть причина, по которой это паника, предназначенная для сбоя программы.

2. Он построен вокруг TTL-кэша. Существует редкое состояние гонки, когда мы пытаемся создать future на соединителе, срок действия которого только что истек и был удален из кэша. Я могу добавить SharedMutex для синхронизации вещей, но это только увеличит постоянную стоимость времени выполнения при решении потенциальной проблемы RC. Борьба с паникой кажется мне разумным взломом

3. Но почему канал закрывается? Закрытие — это сигнал, который отправляет отправитель (обычно), отсюда и паника при закрытии канала.

4. Закрывая, я уведомляю отправителей о том, что ссылка, которую они хранят, больше не актуальна. У меня есть этот код в конце основной функции соединителя: close(c.tasks); for t := range c.tasks { close(t.PacketFromServerChan); }

5. Обычно вы выбираете отдельный канал для подачи сигнала о выходе: play.golang.org/p/-2D4P70zm8

Ответ №1:

Сообщение о восстановлении в этом случае представляет собой не сообщенное значение ошибки из среды выполнения со строковым значением "send on closed channel" .

Единственное, что вы можете сделать здесь, это сопоставить строку ошибки:

 if e, ok := r.(error); ok amp;amp; w.Error() == "send on closed channel" {
    fmt.Println("recover from send on closed channel")
}
  

https://play.golang.org/p/LNcfdE9Bg2

На самом деле, вам, вероятно, нужен отдельный канал для подачи сигнала о выходе.

 t := newConnectorTask(p)

select {
case <-c.close:
    // closing the c.close channel will unblock this case
    return
case c.tasks <- t:
}
  

Комментарии:

1. @ViacheslavKroilov: Я должен упомянуть, что, хотя это маловероятно, что это изменится, нет никакой гарантии, что это продолжит работать в будущих версиях Go или что это работает с другими реализациями Go, поскольку значение ошибки не указано.

2. Что, если я смоделирую эту панику, а затем сохраню тип ошибки? Это должно быть то же самое во время выполнения, не так ли?

3. @ViacheslavKroilov: должно быть, но технически все еще нет ничего, что мешало бы среде выполнения добавлять динамическую информацию в эту строку. Если вы собираетесь это сделать, я бы просто определил постоянную строку и исправил ее, если она сломается.

4. @ViacheslavKroilov: тип ошибки не является специфичным для этой паники, в настоящее время она вызывается plainError