================================== Поддержка исключений в SObjectizer ================================== :Author: Евгений Охотников :Contact: eao197 at intervale dot ru; eao197 at yahoo dot com :Version: 1.0 :Date: 2007.09.13 .. _SObjectizer: http://sobjectizer.sourceforge.net .. contents:: **Оглавление** Мотивация ========= Одним из основных просчетов, допущенных при проектировании SObjectizer-4, является отсутствие поддержки исключений. К сожалению, я уже не помню точных причин принятия такого решения. Вероятно, сказалась сложность написания устойчивого к исключениям кода в C++. Как бы то ни было, исключения в SObjectizer-4 не поддерживаются и это ведет, как минимум, к двум негативным последствиям. Во-первых, использующий API SObjectizer-а код может содержать ошибки, вызванные отсутствием проверок кодов возврата: :: void a_client_manager_t::evt_connect_attempt( const msg_client_description & cmd ) { // ...код по проверке клиента и занесению клиента // в список подключенных клиентов... // Информирование о подключении нового клиента. so_4::api::send_msg_safely( so_query_name(), "msg_client_accepted", new msg_client_accepted( ... ) ); // Запуск цикла проверки состояния клиента. so_4::api::send_msg_safely( so_query_name(), "msg_check_client_status", new msg_check_client_status( ... ), so_query_name(), check_status_timeout() ); } Проблема в данном фрагменте в том, что вызовы ``send_msg_safely`` могут завершится неудачно по какой-либо причине. Но сам факт отказа SObjectizer от отсылки сообщений не будет учтен, агент ``a_client_manager_t`` продолжит работать в предположении, что все прошло успешно. Во-вторых, SObjectizer требует, чтобы обработчики событий не выпускали исключений наружу. В принципе, это обоснованное требование -- если уж сам агент не знает, что делать с его прикладным исключением (а это именно прикладное исключение, т.к. SObjectizer не порождает исключений), то откуда SObjectizer будет об этом знать? Однако, ситуация усугубляется некоторыми обстоятельствами. Так, в C++, в отличии от Java, нет спецификаций исключений, соответствие которых контролировал бы компилятор. Поэтому глядя на код обработчика события невозможно сказать, какие именно исключения он может порождать. Более того, в процессе сопровождения может произойти так, что функции, которые раньше не генерировали исключений, начали их генерировать. В результате обработчики начинают выпускать наружу исключения, которых не было в начале проекта. Но даже если писать обработчики событий в параноидальном стиле: :: void a_some_agent_t::evt_some_handler() { try { ... } catch( const std::exception & x ) { // Обработка всех исключений, производных от стандартного корня. ... } } то возникает вопрос: а что же можно сделать при поимке в обработчике события непредусмотренного исключения? И здесь оказывается, что SObjectizer не может предложить ничего подходящего. Исходя из того, что непредвиденное исключение является показателем нарушения нормальных условий работы агента, единственный разумный выбор у агента -- завершить свою работу, т.е. дерегистрироваться. Но, поскольку дерегистрация является не быстрым процессом и SObjectizer не может гарантировать моментальной дерегистрации кооперации, то агенту лучше еще перейти в какое-нибудь особое состояние. Т.е. любой агент, который работает с исключениями, должен иметь специальное состояние и специальный ``catch`` в каждом событии для перехода в это состояние и инициирования дерегистрации агента. Что заставляет разработчиков повторять одну и ту же работу снова и снова. Возможность добавления в SObjectizer поддержки исключений ========================================================= Добавление поддержки исключений в SObjectizer можно разбить на два этапа. На первом этапе, который можно реализовать в рамках SObjectizer-4, добавляется обработка выпущенных из обработчиков событий исключений. На втором этапе, который невозможен в рамках SObjectizer-4, происходит переход от использования ``so_4::ret_code_t`` в API SObjectizer к порождению исключений при возникновении ошибок. Далее расматривается предполагаемый способ внедрения поддержки исключений в SObjectizer-4. Реакция на вышедшие за пределы обработчиков событий исключения -------------------------------------------------------------- Если исключение выходит за пределы обработчика события, значит агент столкнулся с ситуацией, в которой он не может продолжать работу. Причем очень вероятно, что инвариант агента нарушен (например, возникновение исключения оставило значения атрибутов агента в промежуточном состоянии). Поэтому вышедшее за пределы обработчика события исключение инициирует процесс дерегистрации кооперации, в состав которой входит агент (всей кооперации, поскольку нельзя дерегистрировать одного отдельного агента). При этом агент внутри SObjectizer-а помечается специальным образом, чтобы в него не попало больше ни одного события (возможно, агент будет переводится в специальное состояние, унаследованное из класса ``so_4::rt::agent_t``). Однако, дерегистрации проблемной кооперации недостаточно. Нужно дать возможность приложению восстановиться после подобного сбоя. Сейчас при дерегистрации кооперации SObjectizer рассылает сообщение ``msg_coop_deregistered``. Это сообщение будет расширено информацией о причине дерегистрации кооперации. Сейчас с обработкой ``msg_coop_deregistered`` связано серьезное неудобство: сообщение рассылается широковещательно, поэтому если кто-то заинтересован в получении уведомления о дерегистрации конкретной кооперации, то он вынужден будет получать все сообщения ``msg_coop_deregistered`` и выбирать из них нужное сравнивая имена коопераций. Для того, чтобы устранить это неудобство в SObjectizer добавится возможность связать с кооперацией имена агентов, которым будет отсылаться копия ``msg_coop_deregistered``, но не широковещательно, а целенаправлено, под именем ``msg_coop_deregistration_notify``. Уведомления о дерегистрации коопераций можно будет использовать по аналогии с уведомлениями о завершении процессов в Erlang. В Erlang два процесса могут установить друг с другом связь. Обычно это связь между родительским (порождающим) и доченим (рабочим) процессом. Если дочерний процесс завершается, то родительский процесс получает уведомлений и может решить, что делать дальше -- перезапустить дочерний процесс еще раз или же завершится самому. Предполагается, что аналогичным образом будут использоваться ``msg_coop_deregistration`` в SObjectizer: родительская кооперация порождает дочернюю кооперацию для выполнения каких-то действий. Если дочерняя кооперация дерегистрируется из-за ошибки (выпущенного наружу исключения), то родительская кооперация решает имеет ли смысл создать дочернюю кооперацию заново или дерегистрироваться самой. Корневые кооперации ``````````````````` Подход на основе родительских-дочерних коопераций предполагает, что в приложении будет существовать несколько корневых коопераций, на сообщения об аварийной дерегистрации которых реагировать будет некому. Такие кооперации отвечают за "раскручивание" приложения и должны быть реализованны с максимальным вниманием и осторожностью. Но, поскольку ошибки могут быть везде, вполне может произойти ситуация с выходом наружу исключения из агента корневой кооперации. Для такой кооперации необходимо определить, что делать SObjectizer в случае поимки необработанного исключения. Если говорить точнее, то SObjectizer должен завершать свою работу, но вопрос в том, что сделать это можно несколькими способами: с помощью ``abort()``, через сообщения ``msg_normal_shutdown`` или ``msg_alarm_shutdown``, через механизм завершения SoSysConf или же каким-то другим способом. Поэтому при регистрации корневой кооперации можно будет указать SObjectizer-у объект-обработчик аварийной дерегистрации. Если такой обработчик не указан, то SObjectizer завершает свою работу через ``msg_alarm_shutdown``. Адаптация SoSysConf к сообщению ``msg_coop_deregistration_notify`` `````````````````````````````````````````````````````````````````` В случае использования подсистемы SoSysConf приложение часто строится на основе утилит ``so_sysconf.process`` и ``so_sysconf.ntservice``. В этих случаях корневыми и родительскими для всех коопераций будут кооперации подсистемы SoSysConf. Именно они будут принимать решения о перерегистрации аварийных коопераций или же об останове всего приложения. Поэтому подсистему SoSysConf можно будет расширить поддержкой дополнительных параметров для коопераций. Например, нужно ли рестартовать кооперацию, сколько раз ее можно рестартовать и т.д. .. vim:ts=2:sw=2:sts=2:expandtab:tw=78:ft=rst: