SObjectizer-5.7.0 и so5extra-1.4.0
От: so5team https://stiffstream.com
Дата: 23.01.20 12:04
Оценка: 12 (3)
Вышли очередные версии библиотек SObjectizer и so5extra.

SObjectizer -- это один из немногих все еще живых и развивающихся "акторных фреймворков" для C++ (еще есть QP/C++, CAF: C++ Actor Framework и совсем молодой еще проект rotor). Краткий обзор SObjectizer-а можно найти в этой презентации или в этой довольно старой уже статье. Хотелось бы подчеркнуть, что SObjectizer поддерживает не только модель акторов, но еще и такие модели как Publish-Subscribe и Communicating Sequential Processes. А so5extra – это набор дополнительных полезных прибамбасов для SObjectizer-а (например, реализованные на базе Asio диспетчер с пулом нитей и env_infrastructures, дополнительные типы message box-ов, средства для реализации синхронного взаимодействия и т.д.)

Если в двух словах, то:

* SObjectizer-5.7 теперь позволяет использовать `send_case` в функции `select()`. Это делает SObjectizer-овский `select()` гораздо более похожим на `select` из Golang-а. Но это нововведение нарушило совместимость с предыдущей версией 5.6, т.к. теперь старая функция `case_` стала называться `receive_case`;
* в версии 5.7 устранен недочет в механизме доставки обернутых в конверты сообщений (т.е. enveloped messages) в случае использования `transfer_to_state()` и `suppress()` у агентов-получателей;
* код so5extra теперь распространяется под BSD-3-CLAUSE лицензией, что позволяет бесплатно использовать so5extra при разработке закрытого программного обеспечения. Предыдущие версии распространялись под двойной лицензией (GNU Affero GPL v.3 и коммерческой);
* в so5extra-1.4 реализованы mchain-ы фиксированной емкости для случаев, когда эта емкость известна на этапе компиляции.

Если же рассказывать более подробно, то основная фишка SObjectizer-5.7 -- это возможность использования `select()` для отсылки исходящих сообщений (по аналогии с тем, как это происходит в Golang-е). Так что теперь можно делать вот такие вещи:

using namespace so_5;

struct Greetings {
   std::string msg_;
};

// Попытка отослать сообщения в соответствующие каналы,
// но все операции должны уложиться в 250ms.
select(from_all().handle_n(3).total_time(250ms),
   send_case(chAlice,
      message_holder_t<Greetings>::make("Hello, Alice!"),
      []{ std::cout << "message sent to chAlice" << std::endl; }),
   send_case(chBob,
      message_holder_t<Greetings>::make("Hello, Bob!"),
      []{ std::cout << "message sent to chBob" << std::endl; }),
   send_case(chEve,
      message_holder_t<Greeting>::make("Hello, Eve!"),
      []{ std::cout << "message sent to chEve" << std::endl; }));


В одном `select()` можно использовать и `send_case()` и `receive_case()` вместе. Например, вот SObjectizer-овская версия вычисления чисел Фибоначчи из в отдельном рабочем потоке (по мотивам из Golang's tour):

using namespace std;
using namespace std::chrono_literals;
using namespace so_5;

struct quit {};

void fibonacci( mchain_t values_ch, mchain_t quit_ch )
{
   int x = 0, y = 1;
   mchain_select_result_t r;
   do
   {
      r = select(
         from_all().handle_n(1),
         // Отсылка сообщения типа 'int' со значением 'x' внутри.
         // Отсылка выполняется только когда values_ch готов для приема
         // новых исходящих сообщений.
         send_case( values_ch, message_holder_t<int>::make(x),
               [&x, &y] { // This block of code will be called after the send().
                  auto old_x = x;
                  x = y; y = old_x + y;
               } ),
         // Ожидание сообщения типа `quit` из канала quit_ch.
         receive_case( quit_ch, [](quit){} ) );
   }
   // Продолжаем операции пока что-то отсылается и ничего не прочитано.
   while( r.was_sent() && !r.was_handled() );
}

int main()
{
   wrapped_env_t sobj;

   thread fibonacci_thr;
   auto thr_joiner = auto_join( fibonacci_thr );

   // Канал для чисел Фибоначчи будет иметь ограниченный объем.
   auto values_ch = create_mchain( sobj, 1s, 1,
         mchain_props::memory_usage_t::preallocated,
         mchain_props::overflow_reaction_t::abort_app );

   auto quit_ch = create_mchain( sobj );
   auto ch_closer = auto_close_drop_content( values_ch, quit_ch );

   fibonacci_thr = thread{ fibonacci, values_ch, quit_ch };

   // Читаем первые 10 значений из values_ch.
   receive( from( values_ch ).handle_n( 10 ),
         // Отображаем каждое прочитанное значение.
         []( int v ) { cout << v << endl; } );

   send< quit >( quit_ch );
}


Полное описание нововведений версии 5.7.0 можно найти здесь.

Основное изменение в so5extra-1.4 -- это смена лицензии на BSD-3-CLAUSE. Поэтому теперь все множество дополнений к SObjectizer-у из so5extra могут бесплатно использоваться в разработке закрытого коммерческого ПО.

Единственное нововведение в so5extra-1.4 -- это реализация mchain для случая, когда максимальный объем mchain-а известен на этапе компиляции. Подобные mchain-ы зачастую используются в сценариях request-response, где ожидается всего одно ответное сообщение на запрос:

#include <so_5_extra/mchains/fixed_size.hpp>
#include <so_5/all.hpp>
...
using namespace so_5;

// Канал для получения ответного сообщения.
auto reply_ch = extra::mchains::fixed_size::create_mchain<1>(env,
   mchain_props::overflow_reaction_t::drop_newset);
// Отсылаем запрос.
send<SomeRequest>(target, ..., reply_ch, ...);
// Ждем и обрабатываем ответ.
receive(so_5::from(reply_ch).handle_n(1), [](const SomeReply & reply) { ... });


Надеюсь, что SObjectizer/so5extra кому-нибудь окажется полезен. Если есть вопросы, то спрашивайте, постараюсь ответить.

PS. Изначально SObjectizer/so5extra жили на SourceForge, потом перехали на BitBucket, но в связи с тем, что вскоре с BitBucket-а окончательно выпилят все Mercurial-репозитории, SO-5/so5extra теперь уже живут и развиваются только на GitHub-е.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.