пятница, 2 сентября 2016 г.

[prog.c++] Нововведения в SObjectizer-5.5.18: явный запрет на вложенные смены состояния агента

После того, как в 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-функции). Так что показанный выше код сейчас будет крашить программу.

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