суббота, 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 может стать пусть небольшим, но шагом вперед.

[life.photo] Carl Zeiss Jena Biometar 2.8/80mm в деле

Несколько месяцев назад приобрел себе раритетный среднеформатный пленочный PENTACON Six с объективом Carl Zeiss Biometar 2.8/80mm. Толкнули меня на покупку две причины. Во-первых, ну просто очень красивая вещь, сейчас выступающая как декоративный предмет :) Во-вторых, в комплекте шли макрокольца (они же extension tubes), так что была мысль попробовать этот объектив в макросьемке (ведь виньетирование у объективов от среднеформатных камер на 35-мм фотоаппаратах должно быть минимальным).

Какое-то время ушло на приобретение переходника PENTASIX-Nikon. Потом как-то руки не доходили опробовать это все вместе. А потом никак не мог собраться и выложить получившиеся результаты в блог. Но вот, исправляюсь.

Выглядит итоговая конструкция с Nikon D700 + все макрокольца + объектив весьма внушительно:

Итоговый масштаб, если и не 1:1, то очень близко к этому. Правда, глубина резкости минимальна до смешного. Фокусировать можно только на самом близком к объективе расстоянии, ни о какой бесконечности и речи нет.

Получается же как-то так:

Еще несколько фотографий под катом. Не знаю, каково будет мнение опытных фотографов, но лично мне нравится, особенно когда выводишь фотографии на большой Full HD экран.

пятница, 6 декабря 2013 г.

[prog.c++] Пример сохранения ссылок на внешние объекты внутри класса

Эта заметка, как и одна из предыдущих, является иллюстрацией к моим аргументам в большой дискуссии на LOR-е. Очевидно, что у некоторых C++ разработчиков есть предубеждение против сохранения внутри объекта ссылок на внешние объекты. В какой-то степени эти предубеждения оправданы. Но дело, однако, в том, что в таком хитром языке, как C++, нет простых способов обеспечения валидности сохраненной в объекте ссылки/указателя на внешний объект. Даже std::unique_ptr и std::shared_ptr не дают никаких гарантий: объект может быть выделен из пула (возвращается в пул кастомным deleter-ом) и тогда не смотря на unique_ptr/shared_ptr объект может исчезнуть из-за уничтожения пула. Так что бдить приходится всегда. А когда бдишь уже не так страшно хранить в объекте ссылки, а не умные указатели или, уж тем более, голые указатели. Об этом и пойдет речь ниже.

Поскольку это тема специфическая и содержит примеры кода, то она упрятана под кат. Кому интересно, милости прощу.

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

четверг, 5 декабря 2013 г.

[prog.c++] Почему можно вызывать delete для указателя на const-объект?

До вчерашнего дня был убежден, что язык не разрешает вызывать delete для указателя на const-объект. Оказалось, что ошибался. Вот такая программа успешно компилируется и запускается:

class A {};

void bar( const A * p ) { delete p; }

int main() {
   bar( new A() );
}

Кто-нибудь может объяснить, почему это возможно? Upd. Спасибо, объяснение получено, см. ниже.

Лично я очень удивлен по двум причинам:

Во-первых, указатель на const-объект, это read-only view на объект. И когда посредством read-only view объект уничтожается вообще, то это, по меньшей мере, неожиданно.

Во-вторых, через указатель на const-объект компилятор позволяет вызывать только const-методы объекта. Но деструктор не является таким методом с формальной точки зрения.

Соответственно, я не припомню, чтобы видел где-то в "дикой природе" уничтожение объекта через указатель на const. Если кто-то из читателей видел, то подскажите, где на это можно посмотреть, пожалуйста.


А вот и ответ на мой вопрос на stackoverflow.

Это необходимо, чтобы можно было делать так:

void foo()
{
   const A * p = new const A();

   delete p;
}

В принципе, вещь для меня неожиданная. Это если говорить о конкретных типах. Но вот если речь заходит о шаблонах, тогда все становится на свои места.

templateclass T >
void some_common_action()
{
   T * p = new T();
   // do something...
   delete p;
}

Здесь в качестве T для some_common_action может быть как просто A, так и const A. Причем тип аргумента при обращении к some_common_action может выводиться в каком-то другом, возможно совсем нетривиальном шаблоне. Поэтому для шаблонов возможность применения delete к указателю на const выглядит разумной.

Другое дело, что теперь нужно внимательнее относиться к функциям, которые в качестве аргумента принимают const T *. Вдруг ее разработчику взбредет в голову вызвать delete для аргумента. А я ни сном, ни духом :)

среда, 4 декабря 2013 г.

[prog.c++] Выражение модели владения через типы аргументов

На прошлой неделе сразу в двух независимых обсуждениях всплыл вопрос модели владения объектами в C++. Даже не столько сам вопрос владения, сколько методы обозначения этого владения посредством типов аргументов для функций/методов/конструкторов. Одно из них -- это большой флейм на LOR-е, касающийся языка D. Второе обсуждение велось в переписке между разработчиками SObjectizer. По итогам обсуждений у меня возникла мысль, что могло бы быть полезным описание моего взгляда на проблему типов аргументов, который сформировался у меня за 20 лет использования C++, набивания собственных шишек, чтения рекомендаций от других специалистов и пр.

Итак, поскольку в C++ нет сборки мусора, то проблема владения объектами является одной из краеугольных для C++. Большая часть страшилок о C++ -- битые и повисшие ссылки/указатели, повторное удаление объекта, обращение к несуществующему еще объекту и т.д. -- являются ее следствиями и проявлениями ошибок при ее решении. Удивительно, но до сих пор приходится читать на профильных ресурсах или видеть в коде подходы к решению этих проблем, характерные для plain C. Тогда как C++ скоро подберется к своему тридцатилетнему рубежу и в нем за это время накопилось достаточно механизмов для более простого и наглядного решения проблемы владения. Ниже я попробую об этом рассказать.

понедельник, 2 декабря 2013 г.

[life.sport.darts] Дошел до 1/8 финала Чемпионата РБ по дартс

На прошедшем вчера Чемпионате РБ по дартс удалось дойти до 1/8 финала. Вообще на Чемпионате сыграл шесть игр, пять из которых выиграл. Похоже, это тот максимум, на который я сегодня способен. Нужно работать над собой и дальше.

Впечатлений довольно много. Тут и трудная победа над Дмитрием Бехом в матче за первое место в отборочной группе. И сокрушительный проигрыш Игорю Подвойскому в 1/8. Есть еще когорта ТОП-овых белорусских игроков, на соперничество с которыми я еще не готов психологически. Игорь Подвойский из их числа. Хотя я старался.

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