суббота, 4 января 2020 г.

[prog.c++] В SObjectizer-овском select() появляется send_case()

В черновой ветке SObjectizer-а появилась возможность делать select() не только для чтения входящих сообщений из нескольких каналов, но и для отсылки сообщений в канал. Так что совсем скоро SObjectizer-овский select() станет еще более близок к Go-шному. Естественно, с поправкой на то, что в SObjectizer-е все это сделано средствами библиотеки, а не вшито в язык намертво с оптимально причесанным под это дело синтаксисом.

Под катом традиционный для демонстрации Go-шного select-а пример: вычисление чисел Фибоначчи в отдельном потоке и отсылка их в канал. Отсылка автоматически приостанавливается, если в канале нет места. Так же вычисление прекращается, если из второго канала вычитали сообщение на завершение работы. Или если какой-то из каналов принудительно закрыли.

Примечательна история появления send_case для SObjectizer-овского select-а.

Сам select() в SObjectizer-е появился чуть менее четырех лет назад, в марте 2016-го. После чего я пытался сделать несколько подходов к реализации в select-е не только receive_case, но и send_case. Но каждый раз эти подходы завершались неудачей из-за отсутствия хороших идей по интеграции send_case в уже имеющуюся реализацию select-а.

За пару недель перед Новым Годом начался очередной, можно сказать, дежурный подход, в начале которого было полторы или две недели безрезультатного выкуривания бамбука. Пока, наконец, не пришло некоторое понимание и того, что хочется вложить в send_case, и того, как это поместить в уже имеющуюся схему работы select-а. После чего потребовалось буквально пара дней, чтобы реализовать это все. Тот самый случай, когда идея реально должна была вызреть и, кроме того, должны были сойтись звезды, чтобы появилась возможность качественно "подойти к снаряду".

Плохо то, что пришлось поломать API select-а, поэтому версия SObjectizer-а будет увеличена до 5.7.0. На подготовку новой версии к релизу потребуется какое-то время, так что доступно все это будет где-то через пару недель.

#include <so_5/all.hpp>

#include <chrono>

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),
         send_case( values_ch, message_holder_t<int>::make(x),
               [&x, &y] { 
                  auto old_x = x;
                  x = y; y = old_x + y;
               } ),
         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 };

   receive( from( values_ch ).handle_n( 10 ), []( int v ) { cout << v << endl; } );

   send< quit >( quit_ch );
}

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