Menu

[ru] Завязывание mbox-а только на одного агента

2014-07-04
2014-07-04
  • Yauheni Akhotnikau

    Mbox-ы в SObjectizer используются для обобщенной реализации модели publish-subscribe. Причем mbox-ы расчитаны на то, что subscriber-ов будет несколько даже в случаях, когда subscriber по определению должен быть только один.

    Например, допустим, что есть агент персонально которому должны отсылать сообщения. Решается это тем, что для агента создается mbox, на сообщения из которого подписывается только этот агент. Ссылки на этот mbox могут быть у разных агентов, но подписчик у mbox-а должен быть только один.

    Здесь есть две проблемы.

    Первая, не очень важная, состоит в том, что имея ссылку на mbox, можно подписаться на него. Намеренно или по ошибке, не суть важно. Это можно сделать. И кто-то еще будет получать сообщения, адресованные конкретному агенту.

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

    В связи с этим предлагаю обсудить идею создания нового типа mbox-ов, которые завязаны всего на одного агента-получателя.

    В коде это может выглядеть, например, вот так:

    class a_some_agent_t : public so_5::rt::agent_t
    {
    public:
        a_some_agent_t( so_5::rt::so_environment_t & env )
            : so_5::rt::agent_t( env )
            , m_mbox( env.create_local_mbox( so_self_ptr() ) )
        {}
    
        const so_5::rt::mbox_ref_t &
        mbox() const { return m_mbox; }
    ...
    private:
        const so_5::rt::mbox_ref_t m_mbox;
    };
    

    Т.е. передача указателя на агент в метод create_local_mbox (как для анонимного, так и для именованного mbox-ов) предписывает environment-у создание mbox-а только для одного агента. Никакие другие агенты не смогут выполнять подписку на этот mbox.

     
    • Yauheni Akhotnikau

      Либо, еще как вариант для случая, когда нужно создать mbox-ы до создания агентов. В том числе и для ad-hoc агентов.

      Вводится moveable-only тип direct_mbox_ticket_t:

      class direct_mbox_ticket_t
      {
      public :
          direct_mbox_ticket_t( direct_mbox_ticket_t && o );
      
          const mbox_ref_t &
          mbox() const;
      
      private :
          direct_mbox_ticket_t( const mbox_ref_t & mbox );
      
          mbox_ref_t m_mbox;
      };
      

      Этот объект возвращается новыми методами класса so_environment_t:

      class so_environment_t
      {
      public :
          virtual direct_mbox_ticket_t
          create_direct_mbox() = 0;
      };
      

      Агент может стать владельцем такого mbox-а посредством метода so_take_direct_mbox():

      class agent_t
      {
      public :
          ...
          void
          so_take_direct_mbox( direct_mbox_ticket_t ticket );
      };
      

      Использоваться это может вот так с обычными агентами:

      auto pinger_mbox_ticket = env.create_direct_mbox();
      auto ponger_mbox_ticket = env.create_direct_mbox();
      
      auto pinger = new a_pinger_t( env,
          pinger_mbox_ticket.mbox(),
          ponger_mbox_ticket.mbox() );
      auto ponger = new a_ponger_t( env,
          ponger_mbox_ticket.mbox(),
          pinger_mbox_ticket.mbox() );
      pinger->so_take_direct_mbox( std::move( pinger_mbox_ticket ) );
      ponger->so_take_direct_mbox( std::move( ponger_mbox_ticket ) );
      
      auto coop = env.create_coop( "ping_pong" );
      coop->add_agent( pinger );
      coop->add_agent( ponger );
      

      Либо, с ad-hoc агентами:

      auto pinger_mbox_ticket = env.create_direct_mbox();
      auto ponger_mbox_ticket = env.create_direct_mbox();
      
      auto pinger_mbox = pinger_mbox_ticket;
      auto ponger_mbox = ponger_mbox_ticket;
      
      auto coop = env.create_coop( "ping_pong" );
      coop->define_agent()
          .take_direct_mbox( std::move( pinger_mbox_ticket ) )
          .on_start( [ponger_mbox]() { ponger_mbox->deliver_signal< msg_ping >(); } )
          .event( pinger_mbox, so_5::signal< msg_pong >, [ponger_mbox]() { ... } );
      
      coop->define_agent()
          .take_direct_mbox( std::move( ponger_mbox_ticket ) )
          .event( ponger_mbox, so_5::signal< msg_pong >,
              [pinger_mbox]() { pinger_mbox->deliver_signal< msg_pong >(); } );
      
       
  • Nicolai Grodzitski

    Идея single subscriber -- хорошая. А по реализации мне кажется проще будет поступить так:
    1. Да, реализуется отдельный интерфейс mbox-а, который может сделать получателем только одного агента.
    2. Фиксировать агента-владельца mbox-а в момент первой подписки, тогда и тикетов не надо, тот кто попытается подписаться вторым получит исключение.

    API:

    so_5::rt::mbox_ref_t
    create_single_subscriber_mbox( so_5::rt::agent_t * owner = nullptr );
    

    Если задать владельца сразу, то можно его зафиксировать и сразу, т.е. сделать 2-е реализации. Первая общая, где фиксируется в момент первой подписки и допускающей вариант:
    1. A подписался
    2. A отписался
    3. B подписался
    ...

    И на ее основе более узкую, коотрая фиксирует подписчика в конструкторе и не дает воплотить описанный выше сценарий.

     

    Last edit: Nicolai Grodzitski 2014-07-04
    • Yauheni Akhotnikau

      Думаю, начать можно с того, чтобы добавить в agent_t метод:

      mbox_ref_t create_personal_mbox();
      

      который будет создавать и возвращать single subscriber mbox, владельцем которого будет тот агент, у которого вызвали этот метод.

      А дальше уже смотреть, как делать тикеты или что-то подобное.

       
  • Yauheni Akhotnikau

    1. Фиксировать агента-владельца mbox-а в момент первой подписки, тогда и тикетов не надо, тот кто попытается подписаться вторым получит исключение.

    Я боюсь, что в многопоточной программе мы не всегда сможем гарантировать, что первый подписавшийся будет именно тем, кто нам нужен.

     

Log in to post a comment.