Ошибка обрабатывается путём перезапуска модуля, в котором она произошла.
Речь идёт об ошибках в самих программах, а не в способе их использования, то есть ситуациях когда программа оказалась в непредусмотренном состоянии. Обработка ошибки это возвращение системы в предусмотренное состояние.
Не нужно путать ошибку программы с ошибкой её использования. Не нужно путать обработку ошибок с их устранением. Ошибка программы это по сути, это ошибка программистов, которые что-то не предусмотрели, что-то не учли и так далее. Например, если пользователь пытается сохранить файл, но на диске не хватает свободного места, то это вовсе не ошибка программы. Да это может повлечь за собой ошибку программы, если она будет рассчитывать на наличие этого файла, потому что его там нет. Но сам факт, что операционная система вернула ошибку записи вместо самой записи, ещё не свидетельствует об ошибке в программе или в операционной системе. Вот если сервер сообщил об ошибке, а клиент никак это не учёл, то будь уверен – это выльется в непредусмотренное состояние клиента, то есть в его ошибку. Причина ошибки – нехватка места на диске, то есть маленький жесткий диск. Устранение ошибки это установка жесткого диска большего объёма, или удаление с него каких то файлов. Но устранение ошибок обычно не в компетенции программистов, да и зачастую они не могут они этого сделать. А вот написать код который работает в случае возникновения той или иной ошибки, они могут и должны.
Как я уже говорил, обработка ошибки это перезапуск модуля, который оказался в непредусмотренном состоянии. Для этого понадобятся следующие вещи:
1. Локализация - узнать в каком модуле произошла ошибка.
2. Распространение - узнать на какие модули распространилась ошибка.
3. Разделение – нужно знать где находятся границы модулей.
4. Устранение - нужно чтобы был механизм перезапуска отдельных модулей.
В простейшем случае – у нас в системе только один поток команд, древовидная структура, а границы между элементами понятны и однозначны. В такой ситуации локализировать ошибку легко. Она распространяется на модуль, в котором она возникла, и на все его дочерние модули. На родительский модуль распространяются только те ошибки вложенных модулей, которые родитель не в состоянии обработать. Область распространения ошибки определяется внутренней структурой системы.
Для указания области распространения ошибки, достаточно одного числа, отображающий модуль, на который она распространилась. Это и есть код ошибки. Более детальная информация, нужна только для отладочных целей, для уточнения причин возникновения ошибки, но не для обработки.
Установка кода ошибки происходит аналогично работе выхода с открытым коллектором. То есть много элементов могут устанавливать код ошибки, и только один элемент может его очищать. Код ошибки очищается обработчиком, который устраняет её последствия. Для устранения последствий, не важна причина, поэтому детали вовсе не обязательны. Но нужно знать насколько обширно распространение ошибки.
Обработка ошибки выполняется путём перезапуска всех модулей, на которые она распространилась. Если по каким то причинам ошибку локализовать не удалось, можно смело полагать что она распространилась на все модули системы, стало быть перезапустить нужно всё.
То есть когда ошибка возникла в некотором модуле, кто-то из его родителей должен перезапустить весь этот модуль, вместе со всеми вложенными в него модулями. Если его невозможно перезапустить отдельно от родителя, значит ошибка распространяется и на родительский модуль. Впрочем логика определения степени распространения ошибки, может быть и другой.
В нашем простом случае, каждый модуль может устанавливать код ошибки, соответствующий номеру своего модуля. Родительский модуль может обработать эту ошибку и очистить её значение. Или же, если он не может этого сделать, и ошибка исключает его нормальную работу, он должен заменить код ошибки вложенного модуля, на свой собственный. Затем его родительский модуль получит этот код ошибки и перезапустит дочерний модуль, если сможет. А если не сможет, ошибка будет распространяться дальше, пока один из родителей её не обработает, или пока она не распространится на все приложение, и система его закроет.
Но это был простой случай. Но в случае если внутренняя структура является произвольным графом, локализация ошибки вещь не тривиальная, иногда невозможная. Теперь ошибка может распространяться не только вверх по дереву, т.е. на родителя, а сразу на два равноправных элемента. И для указания ошибочного элемента одного числа уже не достаточно. Конечно можно посмотреть на такую ситуацию как на две ошибки по одной на каждый модуль, но сути дела это не меняет, одного числа всё равно не достаточно.
В случае если у нас в системе более одного потока инструкций, то и ошибки в каждом из потоков происходят независимо и потенциально одновременно. И тут тоже недостаточно одного числа, нужно как минимум по одному числу на каждый поток. А если при этом ещё и структура не древовидная, то по несколько чисел на каждый поток.
В целом же многопоточные программирование, это довольно странная вещь. В общем случае, там не выполняется фундаментальный принцип работы – последовательность выполнения инструкций, и там всё необычно не только обработка ошибок а вообще всё. Но это уже совсем другая история.
А ещё границы между элементами могут быть неоднозначны, или границы данных не совпадают с границами кода, а логика их вообще непонятна, и к тому же связи не всегда известны и очевидны. Но это тоже совсем другая история.
********
2010-03-16
Отказ сервера или клиента
В случае взаимодействия по принципу клиент-сервер, все понятно. Если упал клиент - его нужно остановить, а сервер при этом продолжает работать. Если же упал сервер, то его нужно остановить, несвязанные с ним клиенты продолжают работать. А вот с его непосредственными клиентами нужно делать одно из двух, либо всех их остановить, либо уведомить об отказе сервера, предоставив им выбор – останавливаться или нет.