четверг, 13 октября 2016 г.

[prog.wow] Новость про новый язык P от Microsoft

Собственно, вот сама новость: Microsoft Open-Sources P Language for Safe Async Event-Driven Programming. Небольшая цитата оттуда:

Microsoft describes P as a domain-specific language to model communication between components of an asynchronous system, such as an embedded, networked, or distributed system. A P program is defined in terms of finite state machines than run concurrently. Each of them has an input queue, states, transitions, a machine-local store, and can send asynchronous messages to the others.

Или, в моем корявеньком переводе на русский язык:

Microsoft описывает P как язык, заточенный под моделирование взаимодействия между компонентами в асинхронной системе, вроде встраиваемой, сетевой и распределенной системы. Программа в P определяется в терминах конечных автоматов, которые работают параллельно. Каждый из них имеет входящую очередь, состояния, переходы, локальное хранилище и может отсылать асинхронные сообщения другим [участникам].

Звучит как-то до боли знакомо...

Ба! Да мы же сделали это в SObjectizer-е фиг знает когда и уже много лет пользуемся полученными плюшками ;)

Правда, в P обещают верификацию. Вот чего у нас нет, того нет...

понедельник, 10 октября 2016 г.

[prog.c++] Статистика по использованию try-catch в исходниках SO-5.5.18

На волне темы про использование исключений подсчитал, как часто в исходниках последней стабильной версии SO-5.5.18 встречаются блоки try-catch. Общий объем ядра SO-5 порядка 25KLOC (не считая пустых строк и комментариев). Всего насчитал 25 блоков try (в двух-трех блоках по 2 секции catch). Из общего числа catch-ей:

  • в 10 случаях catch используется для преобразования одного типа исключения в другое. В нескольких из этих случаев перед пробросом нового исключения выполняются действия по очистке/откату операций;
  • в 7 случаях в catch-е иницируется вызов std::abort() (перед этом зачастую идет логирование проблемы);
  • в 4-х случаях исключение "проглатывается". В двух из них предварительно логируется;
  • в 3-х случаях ловятся исключения, выпущенные из обработчиков событий агентов. В этих catch-ах происходит то, что описывалось в свежей статье на Хабре;
  • в одном случае ловится исключение, выпущенное из обработчика события агента, для фиксации этого исключения в std::promise. Этот сценарий используется для синхронного взаимодействия агентов;
  • в одном случае используется логика на исключениях: с помощью исключения разрывается цикл выборки событий из очереди одного из диспетчеров. Оказалось, что такая реализация цикла самая простая;
  • и в одном случае catch используется для эмуляции noexcept для старых VC++ компиляторов.

В общем, получается где-то по одному try-catch на тысячу строк кода.

Отдельно можно сказать про случаи с проглатыванием исключений. В двух из них исключения логируются, но выбрасываются, т.к. исключение вылетает из нотификаторов регистрации и дерегистрации коопераций. Эти нотификаторы вызываются когда основная операция (т.е. либо регистрация, либо дерегистрация) уже завершена, была сделана попытка проинформировать пользователя об этом. Но не получилось. На основную операцию такой сбой повлиять не может, сделать что-то осмысленное с исключением уже нельзя, а прерывать все приложение через std::abort() в данном случае представляется слишком жестоким.

А вот два других случая с игнорированием исключений интереснее. Один из них такой (сорри за качество английского):

   //! Switching storage from map to vector.
   void
   switch_storage_to_vector()
      {
         // All exceptions will be ignored.
         // It is because an exception can be thrown on staged 1-2-3,
         // but not on stage 4.
         try
            {
               // Stage 1. New containers to swap values with old containers.
               // An exception could be thrown in the constructors.
               map_type empty_map;
               vector_type new_storage;

               // Stage 2. Preallocate necessary space in the new vector.
               // An exception can be thrown here.
               new_storage.reserve( m_map.size() );

               // Stage 3. Copy items from old map to the new vector.
               // We do not expect exceptions here at v.5.5.12, but
               // the situation can change in the future versions.

               // Use the fact that items in map is already ordered.
               std::for_each( m_map.begin(), m_map.end(),
                  [&new_storage]( const map_type::value_type & info ) {
                     new_storage.push_back( info.second );
                  } );

               // Stage 4. Swapping.
               // No exceptions expected here.
               m_vector.swap( new_storage );
               m_map.swap( empty_map );
               m_storage = storage_type::vector;
            }
         catch(...)
            {}
      }

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

Какова мораль? Да нет никакой морали. Просто любопытно стало. Думаю, что для библиотек небольшое количество try-catch -- это нормально. В прикладном кода плотность испрользования try-catch наверняка выше.