Go: как программа восстанавливается после паники?

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Паники (Panic) в Go запускаются, когда программа не может должным образом обработать ошибку, например, происходит недопустимый доступ к памяти. Они также могут быть инициированы разработчиком, если ошибка является неожиданной и нет другого способа справиться с ней. Понимание процесса восстановления или прекращения работы в такой ситуации может быть весьма полезно для понимания последствий запаниковавшей программы.

Несколько фреймов

Классический пример паники и ее функции восстановления хорошо задокументированы, в том числе в блоге Go в разделе «Defer, Panic, and Recover». Но давайте сосредоточимся на другом примере, когда паника включает несколько фреймов отложенных (deferred) функций. Вот пример:

Эта программа состоит из трех функций, которые вызываются по цепочке. Как только код достигнет паники на последнем уровне, Go создаст первый фрейм отложенных функций и запустит его:

Код, запущенный в этом фрейме, не восстанавливает (recover) панику. Далее Go создает родительский фрейм и вызывает каждую отложенную функцию в этом фрейме:

Напомним, что отложенные функции выполняются в порядке LIFO (last-in-first-out - последний вошел - первый вышел). Для получения дополнительной информации о способах внутреннего управления отложенными функциями я предлагаю вам почитать мою статью «Go: How Does defer Statement Work?».

Поскольку одна из функций устраняет (recover) панику, Go необходим способ отследить ее возобновить выполнение программы. Для этого в каждую горутину встроен специальный атрибут, указывающий на объект, представляющий панику:

При возникновении паники этот объект создается перед запуском отложенных функций. Затем функция восстановления паники фактически просто возвращает информацию об этом объекте вместе с пометкой паники как устраненной:

Как только паника считается устраненной, Go необходимо возобновить текущую работу. Однако, поскольку рантайм находится среди отложенных фреймов, он не знает, где продолжить. По этой причине, когда паника помечена как устраненная, Go сохраняет текущий счетчик программы и указатель стека текущего фрейма, чтобы возобновить работу сразу после функции, в которой возникла паника:

Мы также можем проверить, что представляет собой счетчик программы, с помощью objdump (например, objdump -D my-binary | grep 105acef):

Эта инструкция указывает на вызов функцией runtime.deferreturn как инструкции, вставленной компилятором в конце каждой функции, которая запускает отложенную функцию. В предыдущем примере большинство из них уже были запущены (до восстановления), поэтому перед возвратом к вызывающей стороне выполняться будут только оставшиеся функции.

WaitGroup

Понимание этого процесса раскрывает для нас важность отложенных функций и то, как они могут иметь очень большое значение, например, при работе с группой горутин. Отсрочка вызова объекта WaitGroup в отложенной функции может предотвратить взаимную блокировку. Вот пример:

Эта программа результирует во взаимной блокировке, поскольку wg.Done никогда не вызывалась. Перемещение этого вызова в отложенную функцию гарантирует вызов и позволит программе продолжить выполнение.

Goexit

Также интересно отметить, что функция runtime.Goexit использует точно такой же рабочий процесс. Фактически он создает объект паники со специальным флагом, чтобы отличить его от настоящей паники. Этот флаг позволяет рантайму пропустить восстановление и завершить работу должным образом, а не останавливать выполнение программы.


Материал подготовлен в рамках курса «Golang Developer. Professional».

Всех желающих приглашаем на открытый урок «Кодогенерация в Go». На уроке вы познакомитесь с механизмом кодогенерации в Go, а еще:
- разберете понятие кодогенерации;
- рассмотрите инструмент `go generate`, а также полезные библиотеки, использующие кодогенерацию: impl, stringer, jsonenums, easyjson и пр.;
- найдете решение проблемы обобщенного программирования (generics) с помощью кодогенерации;
После занятия вы точно не будете бояться библиотек, использующих кодогенерацию.
РЕГИСТРАЦИЯ

Источник: https://habr.com/ru/company/otus/blog/571994/


Интересные статьи

Интересные статьи

На Хабре сегодня публиковалась новость о смерти Макафи, которая, вероятно, огорчила многих читателей — ведь он, несмотря на эксцентричность (об этом упоминаем в первый и последний раз —...
Мне было необходимо делать 2 раза в сутки бэкап сайта на «1С-Битрикс: Управление сайтом» (файлов и базы mysql) и хранить историю изменений за 90 дней. Сайт расположен на VDS под уп...
Эта статья для тех, кто собирается открыть интернет-магазин, но еще рассматривает варианты и думает по какому пути пойти, заказать разработку магазина в студии, у фрилансера или выбрать облачный серви...
Сегодня мы бы хотели проговорить основные плюсы и минусы партнерских программ хостинг-провайдеров средних размеров. Актуально это, потому что все больше и больше компаний отказываются от собс...
В последнее время я часто слышу о том, что Java стала устаревшим языком, на котором сложно строить большие поддерживаемые приложения. В целом, я не согласен с этой точкой зрения. На мой взгляд, я...