суббота, 7 декабря 2013 г.

[prog.c++] Идея по поводу реализации selective receive в SObjectizer

Одной из характерных особенностей обработки сообщений в Erlang является механизм selective receive. Т.е. в выражении receive..end программист может указать сообщения, которые он хочет обрабатывать. Все остальные сообщения, которые не попадают под критерии в receive, остаются в очереди процесса. И становятся доступными при запуске нового выражения receive с другим набором критериев (см. например, фрагмент книги Programming Erlang, раздел 8.6). В SObjectizer же никакого selective receive нет: в каком бы состоянии агент не находился, ему будут доставляться все сообщения, если агент в своем текущем состоянии не обрабатывает какие-то из них, то необработанные сообщения будут для агента потеряны навсегда.

В принципе, я считаю текущий подход SObjectizer-а простым и логичным. Но, если держать курс на добавление в SObjectizer возможностей, которые были бы полезны широкому кругу потенциальных пользователей, то selective receive следовало бы рассмотреть самым внимательным образом. Поэтому-то я время от времени возвращался к поиску механизма, который мог бы позволить эффективно реализовать selective receive в SObjectizer. И вот посетила следующая идея.

Сейчас проблема в том, что когда для агента есть заявка на обработку сообщения, заявка сначала изымается из очереди, затем передается агенту. Агент проверяет, может ли сообщение быть обработано в его текущем состоянии. И, если не может, то заявку агент уже не может вернуть в очередь обратно.

Исправить это можно двумя способами.

Первый способ состоит в том, чтобы отвергнутые в текущем состоянии сообщения сохранялись в локальной очереди состояния агента. А когда состояние меняется (т.е. вызывается метод agent_t::so_change_state), все накопившиеся сообщения одной операцией ставятся в начало текущей очереди агента. Т.о., когда рабочая нить агента обратится к голове очереди за новым сообщением, там будут стоять "старые" сообщения, доступные для повторной передачи агенту.

Второй способ состоит в том, чтобы заявки брались не из головы очереди, а из позиции, на которую указывает некоторый курсор. Первоначально курсор указывает на начало очереди. Заявка, на которую указывает курсор, отдается агенту, а агент возвращает признак, была ли она обработана или же должна остаться и ждать еще. Если заявка обработана, заявка удаляется и курсор остается на том же самом месте. Если заявка не обработана, то курсор сдвигается на одну позицию дальше, к следующей заявке (если таковая есть). При смене состояния агента (т.е. при вызове agent_t::so_change_state) курсор автоматически передвигается на начало очереди и процесс повторяется вновь.

Т.е. появляется реальная возможность сделать selective receive. Какой именно из способов будет выбран -- пока не понятно. Нужно будет посмотреть на различные варианты реализации очередей заявок (поскольку текущая версия уже требует пересмотра и выбор нового способа хранения очереди заявок можно будет делать с учетом selective receive).

Поскольку на основе SObjectizer уже реализована куча агентов, которые не используют selective receive, вероятно, нужно будет явно разделить состояния агента, в которых selective receive не поддерживается (старый класс state_t), и на состояния, в которых selective receive поддерживается. В коде это может выглядеть так:

using namespace so_5::rt;

class my_agent_t : public agent_t
   {
   private :
      // Состояние для обработки всех сообщений.
      state_t st_normal;
      // Состояние для обработки только части сообщений.
      state_with_selective_receive_t st_wait_reply;

   public :
      my_agent_t(
         so_environment_t & env )
         :  agent_t( env )
         ,  st_normal( self_ptr() )
         ,  st_wait_reply( self_ptr() )
         ...
         {}

      // Какое-то событие, которое срабатывает в st_normal.
      void
      evt_do_some_request( const event_data_t< some_signal > & )
         {
            // Инициируем запрос, а ответ ждем в специальном состоянии.
            so_change_state( st_wait_reply );
            m_receiver->deliver_message( new some_request(...) );
            ...
         }

      void
      evt_wait_response( const event_data_t< some_response > & evt )
         {
            // Возвращаемся в нормальное состояние...
            so_change_state( st_normal );
            // ...и обрабатываем ответ
            ...
         }
   ...
   };

Т.о. для версии 5.3 подбирается интересный список фич. Например, описанный только что selective receive. А так же, описанный чуть ранее, механизм синхронного взаимодействия агентов. Плюс, надеюсь, при работе над 5.3 получится уделить самое пристальное внимание вопросам производительности. Однако заняться всем этим получится не раньше января 2014. Поскольку сейчас полным ходом идет работа над версией SO-5.2.3, в которой уже добавлены интересные полезные фичи и этот процесс еще не завершен. Так что релиз сборки 201312-00 может стать пусть небольшим, но шагом вперед.

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