После того, как в SObjectizer были добавлены обработчики входа/выхода в/из состояния агента появилась возможность совершить ошибку: в on_enter/on_exit разработчик может инициировать переход в другое состояние. Что-то вроде:
class demo : public so_5::agent_t { state_t st_one{ this }; state_t st_two{ this }; ... virtual void so_define_agent() override { st_one.on_enter( [this]{ st_two.activate(); } ); ... } }; |
Такое поведение ошибочно и может приводить к серьезным проблемам:
Во-первых, неправильному порядку вызова on_enter/on_exit. Т.к. состояния могут быть вложенными, то операция смены состояния может приводить к вызову нескольких разных обработчиков on_enter/on_exit. Повторная смена состояния не прерывает эту цепочку, а лишь "встраивает" в нее еще одну цепочку вызовов. Когда эта новая, "встроенная" цепочка отработает, выполнение вернется к старой цепочке для состояний, которые уже нельзя считать актуальными.
Во-вторых, такая смена состояния все равно не приведет к желаемому результату. Т.к. если выполняется операция перехода в состояние A и внутри нее агент переводится в состояние B, то когда обе эти операции будут завершены, агент все равно окажется в состоянии A. Потому, что переход в состояние B не прерывает операцию перехода в состояние A, а как бы "встраивается" в этот переход.
В-третьих, такие вложенные смены состояния могут привести к зацикливанию. Например, on_enter для A делает переход в B, а on_enter для B делает переход в A. В этом случае приложение аварийно завершится из-за исчерпания стека.
Выполнение вложенной смены состояния -- это ошибка разработчика. Нельзя менять состояние агента внутри on_enter/on_exit. Но до версии 5.5.18 SObjectizer никак не контролировал эту ситуацию.
Начиная с версии 5.5.18 SObjectizer при выполнении операции смены состояния агента проверяет, а не идет ли в данный момент еще одна такая операция. Если идет, то порождается исключение. Поэтому сейчас SObjectizer будет явно бить разработчика по рукам, если тот пытается сменить состояние агента в on_enter/on_exit обработчиках.
Примечание. on_enter/on_exit обработчики должны быть noexcept-функциями. Это означает, что если внутри такого обработчика SObjectizer бросает исключение из-за попытки вложенной смены состояния, то, скорее всего, приложение будет аварийно завершено вызовом std::terminate (из-за попытки выброса исключения из noexcept-функции). Так что показанный выше код сейчас будет крашить программу.
Комментариев нет:
Отправить комментарий