суббота, 16 января 2016 г.

[prog.sobjectizer] Неожиданный сценарий использования mchain-ов.

Механизм mchain-ов (сокращение от message chain), появившийся в версии 5.5.13, внезапно оказался полезным в качестве счетчика событий.

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

Оказалось, что в ряде ситуаций вместо такого агента-наблюдателя можно использовать mchain. Рабочие агенты отсылают в mchain уведомления, а кто-то просто висит на вызове so_5::receive и просто считает эти уведомления. Причем, за счет такой штуки, как handle_n(), можно вообще ничего не считать -- все это сделает сам so_5::receive.

Вот небольшой свежий пример. Нужно было дождаться момента старта агентов в двух кооперациях, после чего инициировать завершение работы SObjectizer. Агенты в своих методах so_evt_start отсылали в mchain специальное уведомление complete. А главная нить приложения просто заснула на вызове so_5::receive до тех пор, пока оба уведомления не будут прочитаны из mchain-а:

so_5::launch( [&]( so_5::environment_t & env ) {
   // Создаем простой mchain для получения уведомлений от агентов.
   auto notify_ch = env.create_mchain( so_5::make_unlimited_mchain_params() );

   // Создаем две кооперации, которые нужны для тестов.
   // Ранее созданный mchain передается параметром конструктора
   // агентов в этих кооперациях.
   env.introduce_coop( [&]( so_5::coop_t & coop ) {
         coop.make_agent< a_only_top_level_states_t >( std::ref(log1), notify_ch );
      } );
   env.introduce_coop( [&]( so_5::coop_t & coop ) {
         coop.make_agent< a_substates_of_one_state_t >( std::ref(log2), notify_ch );
      } );

   // Остается просто дождаться двух уведомлений типа complete из нашего mchain-а.
   receive( from( notify_ch ).handle_n( 2 ), []( complete ){} );
   // Уведомления получены. Агенты выполнили свои действия, можно безопасно
   // завешать работу SObjectizer Environment.
   env.stop();
} );

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