вторник, 7 ноября 2017 г.

[prog.actors] Хотите решить задачу с помощью акторов? Спросите меня как! :)

После того, как мне довелось разным людям в разных местах рассказывать про Модель Акторов вообще и про SObjectizer в частности, сложилось впечатление, что продвижению Модели Акторов в массы препятствует две вещи:

  1. Отсутствие понимания того, что такое Модель Акторов вообще. Это, на самом-то деле, совсем не проблема. Очевидно, что всего на свете знать нельзя, а объяснить основные принципы работы Модели Акторов можно буквально на пальцах (что, кстати говоря, подтверждается практикой).
  2. Отсутствие понимания того, как эту самую Модель Акторов, принципы которой можно объяснить на пальцах, применить для решения практических задач.

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

В связи с этим предлагаю следующее: любой желающий может описать задачку или проблему, которую он бы хотел попробовать решить с помощью акторов. Ну или хотел бы увидеть, как ее можно было бы попробовать решить с помощью акторов. А я бы попробовал изложить свои соображения о том, можно ли это сделать, если можно, то как, какие будут достоинства и недостатки у этого решения и т.д. Условия задачи и предполагаемое решение (либо причины отсутствия такового) будут затем публиковаться в данном блоге.

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

Зачем это нужно мне? Очевидно, что мои цели исключительно корыстные ;) Прежде всего мне нужен материал, на основе которого можно было бы убедительно рассказывать людям о том, где применение Модели Акторов уместно, а где нет. Кстати говоря, неуместность применения Модели Акторов -- это актуальный вопрос. Бывает, что люди слушая про Модель Акторов теряют представление о том, что данная модель применима далеко не всегда. И хорошо бы уметь вовремя различать, где имеет смысл брать акторов, а где этого делать не нужно. Так же мне полезно прикидывать, насколько наш SObjectizer пригоден для решения тех или иных задач. Опыт показывает, что это сильно идет на пользу SObjectizer-у. А т.к. сам SObjectizer распространяется под BSD-лицензией (бездвоздме т.е. даром), то это пойдет на пользу и всем, кто воспользуется SObjectizer-ом.

Зачем это нужно вам? Ну мало ли :) Может вы хотите убедиться, какая все-таки гадость, эта ваша Модель Акторов, а вот вы молодец, когда придумали свое решение без использования акторов ;) Или может вы правда ломаете голову над чем-то и не прочь бы посоветоваться с умным человеком простофилей, который готов тратить свое время бесплатно. Ну или вам просто захотелось пообщаться ;)

В общем, если есть задачка и желание ее обсудить, то милости прошу. Описывайте свои задачки в комментариях к этой заметке (можно в G+), либо по почте eao197 на gmail тчк com, либо со мной можно связаться через FB, LinkedIn или Habrhabr.

PS. Запись специально повисит вверху до сентября. Но, если дело пойдет, можно будет заказать продление ;)

четверг, 21 сентября 2017 г.

[prog.c++] Вопрос к читателям: интересна ли кому-нибудь статья о деталях реализации asio_thread_pool диспетчера?

Мы вчера выкатили so_5_extra-1.0.2 с реализацией нового диспетчера на базе Asio. В этой самой реализации получилось интересное, как мне кажется, использование фишек С++, включая наследование, шаблоны и даже thread_local переменные. Причем "интересное" именно в плане использования возможностей языка. Специфика SObjectizer-а, конечно же, есть, но скорее как фон и условия, для которых пришлось искать решение.

Пока эти детали реализации еще не стерлись из памяти, я мог бы написать статью с рассказом о том, что было сделано, как это было сделано, и почему это было сделано именно так. Упор будет сделан именно на применение фич языка, мол, вот здесь хорошо подошло наследование и виртуальные методы, а вот здесь пришлось обмазаться шаблонами.

Посему у меня к читателям блога пара вопросов:

  1. Будет ли интересен кому-нибудь такой рассказ вообще? Не хочется тратить несколько дней работы на то, что прочитает 10-15 человек.
  2. Если же статья все-таки кому-то интересна, то где бы вам было бы удобнее ее видеть: у меня в блоге или на Хабре? Место публикации важно потому, что, во-первых, от этого зависит стиль и подробность изложения, а так же, во-вторых, удобство комментирования и качество комментариев. В принципе, с точки зрения величины аудитории я бы нацеливался на Хабр. Но если у многих моих читателей к Хабру отношение негативное, то проще будет написать статью для блога.

В общем, прошу не счесть за труд и высказать свое мнение. Можно и пожелания по будущему тексту (например, на что сделать акцент, чтобы статья была интереснее для вас). Комментарии можно оставлять в блоге или в G+. Ну или там, где я еще размещу ссылки на этот пост (типа Facebook-а или LinkedIn-а).

На 2017.09.24 10:00 "за" проголосовало только четыре человека, этого мало для того, чтобы браться за написание статьи.

понедельник, 18 сентября 2017 г.

[prog.c++14] Мечты сбываются: задышала реализация диспетчера, которая может использовать пользовательские классы нитей

В рамках работ над новой версией so_5_extra разрабатывается asio_thread_pool-диспетчер. Это диспетчер, в котором на пуле потоков запускаются методы asio::io_service::run(), и агенты, которые привязаны к такому диспетчеру, отрабатывают свои события на этих же рабочих потоках. По сути, диспетчеризацией событий для таких агентов занимается asio::io_service. Что должно дать возможность агентам просто и прозрачно выполнять и IO-операции, и обработку прикладных сообщений не задумываясь о том контексте, на котором они работают (ноги у этого всего растут вот отсюда).

Но самое интересное для меня другое: это был еще и эксперимент по созданию диспетчера, который может использовать не только std::thread для создания рабочих потоков, но и предоставленный пользователем класс нити, если этот класс частично мимикрирует под std::thread. Зачем такое может потребоваться?

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

Так вот, этот новый asio_thread_pool показал, что подобный фокус вполне себе работает. И выглядит это как-то вот так:

// Definition of traits to be used with asio_thread_pool.
struct my_disp_traits
{
   // Actual type of thread to be used.
   using thread_type = my_pthread_t;
};
...
   // Create dispatcher for ring of agents.
   auto disp = asio_tp::create_private_disp<my_disp_traits>(
         coop.environment(),
         "asio_tp",
         std::move(disp_params) );

Стоить определить в traits-ах для диспетчера имя класса, который следует использовать вместо std::thread, и asio_thread_pool начнет использовать данный класс.

Любопытные последствия могут быть у этого эксперимента. У меня уже давно есть мысль о том, чтобы добавить возможность такой кастомизации ко всем штатным диспетчерам SO-5. И, поскольку эксперимент оказался удачным, такая возможность может воплотиться в реальность в SO-5.5.20, работа над которой должна начаться вот прямо после фиксации so_5_extra-1.0.2. А это означает, что реализации штатных диспетчеров вынужденно станут шаблонными. Что заметно уменьшит количество .cpp-файлов в исходниках SObjectizer-а. Что будет означать еще одним большой шаг в сторону header-only версии SO-5. При этом header-only -- это вовсе не самоцель. Но чем больше будет в SO-5 кастомизаций через шаблоны, тем ближе к header-only окажется реализация.

Под катом простейшая реализация my_pthread_t, набросанная на коленке за 15 минут...

четверг, 14 сентября 2017 г.

[prog.humour] Маленький фрагмент трудовых будней :)

Небольшой кусочек кода, над которым вот прямо сейчас идет работа.

Зачем я его показываю? Наверное, чтобы было понятно, что короткие идентификаторы -- это не про меня ;)

templatetypename Traits = default_traits_t >
inline private_dispatcher_handle_t
create_private_disp(
   //! SObjectizer Environment to work in.
   environment_t & env,
   //! Value for creating names of data sources for
   //! run-time monitoring.
   const std::string & data_sources_name_base,
   //! Parameters for the dispatcher.
   disp_params_t disp_params )
   {
      const auto io_svc_ptr = disp_params.io_service();
      if( !io_svc_ptr )
//FIXME: must be replaced by SO_5_THROW_EXCEPTION!
         throw std::invalid_argument( "io_service is not set in disp_params" );

      if( !disp_params.thread_count() )
         disp_params.thread_count( default_thread_pool_size() );

      using so_5::stats::activity_tracking_stuff::create_appropriate_disp;
      auto disp = create_appropriate_disp<
            // Type of result pointer.
            private_dispatcher_t,
            // Actual type of dispatcher without thread activity tracking.
            impl::real_private_dispatcher_t<
                  Traits,
                  impl::dispatcher_skeleton_without_thread_activity_tracking_t >,
            // Actual type of dispatcher with thread activity tracking.
            impl::real_private_dispatcher_t<
                  Traits,
                  impl::dispatcher_skeleton_with_thread_activity_tracking_t > >(
         // Args for create_appropriate_disp.
         env,
         disp_params,
         // Args for real_private_dispatcher_t constructors.
         env,
         data_sources_name_base,
         disp_params );

      return { disp.release() };
   }

PS. Код еще на альфа-стадии.

вторник, 12 сентября 2017 г.

[prog] А что, кстати говоря, с хайпом вокруг Haskell?

Сегодня довелось наткнуться на довольно старую презентацию Engineering Large Projects in Haskell: A Decade of FP at Galois и подумалось, что еще несколько лет назад хайп вокруг Haskell-я был достаточно заметным. А что сейчас? Поутих? Или это просто мне на глаза больше ничего подобного не попадается?

PS. Понятно, что язык применяется. И что там, где он успешно применялся, он продолжает использоваться. Интересует именно хайп вокруг языка, т.е. количество восторженных отзывов и всяческий статей типа "Я открыл для себя Haskell и мои волосы стали мягкими и шелковистыми..."

суббота, 9 сентября 2017 г.

[prog.c++] Упарываемся шаблонами: используем их даже для битовых операций

Меня тут давеча на LOR-е типа обвинили в том, что боюсь и шаблонов, и возни с битами и байтами. Юмор этой ситуации заключался в том, что как раз незадолго до этого мы у себя в RESTinio по мере подготовке к релизу очередной публичной версии как раз проводили рефакторинг операций над битами и байтами.

Дело в том, что в коде RESTinio со временем появился ряд операций, в которых требовалось извлечь сколько-то битов из какого-то значения. В принципе, это все элементарные вещи вроде (bs >> 18) & 0x3f. Однако, когда таких элементарных вещей нужно записать несколько штук подряд, да еще в разных местах, да еще с преобразованием результата к разным типам, то лично у меня в голове начинает звучать тревожный звоночек: слишком много хардкодинга и копипасты. А поскольку по поводу копипасты и ее опасности у меня есть собственный пунктик, то в итоге операции с битами и байтами мы упрятали во вспомогательную шаблонную функцию. Там, где у нас было что-то подобное:

result.push_back( alphabet_char( static_cast<char>((bs >> 18) & 0x3f) ) );
result.push_back( alphabet_char( static_cast<char>((bs >> 12) & 0x3f) ) );
result.push_back( alphabet_char( static_cast<char>((bs >> 6) & 0x3f) ) );
result.push_back( alphabet_char( static_cast<char>(bs & 0x3f) ) );

появилось что-то вот такое:

template<unsigned int SHIFT>
char
sixbits_char( uint_type_t bs )
{
   return ::restinio::impl::bitops::n_bits_from< char, SHIFT, 6 >(bs);
}
...
result.push_back( alphabet_char( sixbits_char<18>(bs) ) );
result.push_back( alphabet_char( sixbits_char<12>(bs) ) );
result.push_back( alphabet_char( sixbits_char<6>(bs) ) );
result.push_back( alphabet_char( sixbits_char<0>(bs) ) );

Где ключевую роль играет тривиальная шаблонная функция n_bits_from:

template<
   typename T,
   unsigned SHIFT,
   unsigned BITS_TO_EXTRACT = details::bits_count<T>::count,
   typename F = unsigned int >
T
n_bits_from( F value )
{
   return static_cast<T>(value >> SHIFT) & details::mask<T>(BITS_TO_EXTRACT);
}

Полная реализация n_bits_from в ее текущем виде находится под катом. Но сказать хочется вот что: возможно, это уже оверкилл и злоупотребление шаблонами. Но, как мне кажется, использование n_bits_from в местах, где операции по извлечению битов из байтов производятся в большом количестве, помогает избежать:

  • во-первых, замыливания глаза при повторении однотипных операций. Когда приходится записывать подряд штук 5-6 сдвигов с последующими "логическими И", то очень легко где-то ошибиться и записать не то смещение или не ту битовую маску. Такие ошибки, к сожалению, не так просто заметить и они могут жить в коде очень долго, особенно, если код недостаточно покрыт тестами;
  • во-вторых, неявных приведений типов, которые в C++ могут приводить к неожиданным результатам. Например, можно легко попытаться получить из char-а значение unsigned int, забыв про промежуточный каст в unsigned char. И, если в char-е установлен старший бит, то получить нежданчик. Особенно это круто в ситуации, когда сперва 8 бит извлекаются из int-а в char, а затем этот char используется в качестве индекса в массиве (т.е. может произойти расширение из char в size_t, который беззнаковый).

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

Ну а теперь полная реализация (ее текущий вариант, не факт, что хороший и окончательный):

пятница, 8 сентября 2017 г.

[prog.c++] json_dto-0.2

Мы сегодня обновили свою небольшую библиотеку json_dto, которая служит хоть и тонкой, но очень полезной оберткой над такой замечательной штукой, как rapidjson. Мы сделали json_dto где-то года полтора назад, взяв идеи из Boost.Serialization, для уменьшения объема писанины при работе с JSON в C++. Например, json_dto позволяет писать вот так:

class User {
public :
   /* ... */

   template<typename JSON_IO>
   void json_io(JSON_IO & io) {
      io & json_dto::mandatory("id", _id)
         & json_dto::mandatory("name", _name)
         & json_dto::mandatory("birthday", _birthday)
         & json_dto::mandatory("phone", _phone);
   }
};

вместо того, чтобы писать вот такие простыни (взято отсюда как пример несложного кода):

вторник, 5 сентября 2017 г.

[prog.thoughts] Сложные инструменты уже не нужны в современных условиях?

Размышляя время от времени над феноменом языка Go, над тем, что пишут на Хабре или обсуждают на профильных ресурсах (типа LOR-а или RSDN-а), закрадывается мысль, что в современных условиях сложные инструменты мало кому нужны. И я не могу понять, это объективная реальность такова или же это я вошел в пору конфликта "отцов и детей", но уже со стороны "отцов".

Но вот взять тот же C++, который сейчас повсеместно ругают за сложность. Забывая при этом, что сложность эта возникла не просто так, а как адаптация языка к тем прикладным нишам, в которых он активно используется. Сложная система шаблонов в C++ появилась не просто так же. Специализация шаблонов, к примеру, является следствием того, что обобщенные структуры и/или алгоритмы бывает нужно адаптировать к конкретным условиям. Ну это же объективная реальность такая: ты либо борешься со сложностью свой прикладной задачи посредством мощного инструмента, либо же тупо тратишь больше времени и усилий, обходясь намного более примитивными средствами. Скажем, там, где можно сделать один шаблон и применить его для 5 разнотипных наборов данных, можно же обойтись и без шаблона, посредством копипасты.

Мне часто вспоминается первое знакомство с C++ в далеком 1991-ом году. Язык был гораздо сложнее Паскаля и Бейсика. Но зато он позволял сделать свои классы Set и Matrix, которые бы ничем не отличались бы от встроенных в язык типов. А уж когда в C++ завезли шаблоны, то тут вообще такие бескрайние просторы открылись, что просто дух захватывало. Можно было сделать Matrix<T>, где T мог быть, скажем, Complex<U>, да и сам U мог быть не просто float-ом или double, а каким-то собственным FixedSizeFloat...

Конечно, C++ здесь не очень показательный пример, т.к. за его мощность нужно было платить унаследованными от C граблями. Но можно посмотреть и на другие знаковые языки 1980-х и 1990-х годов. Ada, например. Или Eiffel. Да взять ту же Java, которая вышла в свет в 1995-ом как очень примитивный язык. Который был вынужден со временем усложниться и заиметь таки генерики. С C# затем это так же произошло, но гораздо быстрее.

Т.е. в 1980-х и 1990-х, да даже в начале 2000-х, растущая сложность инструментария воспринималась как само собой разумеющееся. Думаю, что это происходило потому, что программистов было мало, сложность программ росла очень быстро, потребность в софте росла еще быстрее. Получалось, что когда программистов мало, а задача сложная, то решать ее методом грубой силы не получится, просто нет ресурсов. Значит решать ее можно было посредством более мощных, а значит и более сложных, инструментов.

Но потом что-то изменилось. Возможно, двумя последними языками из знакомых мне, которые пошли по пути создания сложного, но мощного инструмента, были D и Scala. А вот то, что стало появляться затем, образует уже иную тенденцию. Go -- это вообще какой-то крайний случай. А вот языки, вроде, Ceylon, Kotlin и даже Rust, как мне кажется, идут по пути снижения сложности, но при этом предоставления пользователям достаточной мощности. Хотя "достаточной" -- это относительное понятие. Например, нет в Rust-е нормального ООП и кому-то Rust может казаться достаточно мощным, а кому-то -- нет.

Думается, что все это таки объективно. Подавляющему большинству разработчиков сейчас не нужны сложные инструменты. Ибо изменился "ландшафт" программирования. Сложных задач в процентном отношении становится меньше. Больше становится рутины, в которой основная сложность не в самом программировании, а в организации процесса разработки: от общения с заказчиком и формализации требований до интеграционного тестирования и запуска в эксплуатацию. Программист -- это винтик, который должен быть быстрообучаемым и легкозаменяемым. Чего сложно достигнуть, если программист будет использовать C++ или Scala, а не Go или Kotlin.

пятница, 1 сентября 2017 г.

[prog.c++] RESTinio в конкурсе HighLoad Cup-2017 от Mail.ru

Мы у себя в "СтифСтриме" занимаемся разработкой небольшого C++ного фреймворка RESTinio (последняя публичная версия лежит здесь). Цель в том, чтобы дать C++ разработчикам легковесный, простой в использовании, но мощный и производительный инструмент для разработки RESTful сервисов на C++. А тут Mail.ru объявляет конкурс HighLoad Cup. Естественно, захотелось попробовать RESTinio в чужом бенчмарке, в котором мы ничего не контролируем. В общем, Коля Гродзицкий, который отвечает за разработку RESTinio, и занялся подготовкой конкурсного решения для HighLoad Cup-а.

В качестве технологического стека использовался C++14/17 (в объеме, поддерживавшемся в gcc-6.2), RESTinio, Asio, Node.js http-parser, RapidJSON и json_dto. Отдельного экстрима доставило то, что в этот же момент в RESTinio вливалется поддержка WebSocket-ов. Так что решение приходилось делать на не стабильной версии RESTinio, нестабильность которой увеличивалась за счет выявленных в процесс работы над конкурсным решением недостатков самого RESTinio. В общем, Николаю досталось :)

В итоге Колино решение вошло в финал с 45-го места. И в финале оно оказалось на 44-ом месте (на 2017.09.04).

С учетом того, что в финал, как мне представляется, вошло всего несколько решений, использующих готовые, более-менее полноценные реализации HTTP-серверов, не заточенных под эту конкретную задачу, результат не самый плохой. Как раз рядом с Колиным решением оказалось еще пару решений на Go и fasthttp (а производительность у RESTinio и fasthttp была очень близка, когда мы такие замеры производили в последний раз). Вероятно, четвертый-пятый десяток ТОПа -- это максимум, на который могут претендовать решения, построенные на универсальных инструментах и не оптимизированные на самом низком уровне под конкретные условия.

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

На github-е начали собирать список исходников решений из конкурса. Коля подчистит код RESTinio и опубликует свое решение где-то в середине-конце следующей недели. Кстати говоря, когда заглядываешь в код лидеров конкурса, то на голове начинают шевелиться остатки волос: понятно, что в коде, заточенном под максимальную скорость, будет жесть. Но не настолько же жестянная жесть :)


От себя лично добавлю, что я не понял логики организаторов турнира в финале. Как по мне, так нужно было либо делать один рейтинговый обстрел (но объемом в 4-5 раз больше, чем в отборочном туре, чтобы нивелировать разные факторы вроде фазы Луны), либо же делать N обстрелов с одинаковыми данными и высчитывать среднее, либо делать N обстрелов с разными данными и суммировать время. А так получается N попыток и в зачет идет лучшее. Что выглядит особенно непонятно в ситуациях, когда какое-то из решений на отдельных обстрелах уходит по времени в бесконечность или вообще падает.

Очевидно, что Колиному решению на RESTinio это бы никак не помогло. Но зато не было бы вопросов по поводу отдельных решений из ТОП-10, которые в некоторых обстрелах показывают времена в районе 150K и более секунд. Или вообще падают. Но находятся на верхних строчках.

Но, подозреваю, организаторы сразу оказались в условиях, когда любое принятое ими решение будет кого-нибудь не устраивать. Так что это не более чем старческое брюзжание :)

[life.cinema] Очередной кинообзор (2017/08)

Внезапно наступило 1-е сентября, что означает не только то, что закончилось лето, но и то, что пришло время публиковать очередной список просмотренных за минувший месяц фильмов. Как обычно, в начале идут те картины, которые понравились мне больше всего, ну а далее по убыванию...

Мегрэ расставляет сети (Maigret Sets a Trap, 2016), Мертвец детектива Мегрэ (Maigret's Dead Man, 2016) и Мегрэ: Ночь на перекрёстке (Maigret: Night at the Crossroads, 2017). Вот все хорошо. От картинки так вообще получаешь огромное удовольствие. Но к чему невозможно привыкнуть, так это к худому комиссару Мегрэ. Как въелось с детства, что Мегрэ был грузным и боролся с перееданием, так и не отпускает до сих пор :)

Тайна 7 сестер (Seven Sisters, 2017). Если не придираться к сюжету, то очень даже неплохо. Руми Рапас, сыгравшая сразу семерых персонажей, вызывает уважение.

Дикая история (El bar, 2016). Обажаю такие, по хорошему укуренные, истории. Поэтому мне очень понравилось. Но фильм для ценителей жанра.

Валериан и город тысячи планет (Valerian and the City of a Thousand Planets, 2017). Очень красочный и очень-очень детский фильм. Рассчитан на аудиторию 8-10 лет, как мне показалось. Так что взрослым можно смотреть только за компанию с детьми.

Послание от Кинга (Message from the King, 2016). Неплохо. Не шедевр, но неплохо. Нормальный такой криминальный фильм без претензий на глобальность и масштабность.

Телохранитель киллера (The Hitman's Bodyguard, 2017). Как по мне, так трейлеры фильма оказались круче, чем сам фильм. Экшен-сцены хороши, но все, что между ними, навевает смертельную скуку. Ну и для фильма такого уровня неожиданно было увидеть не очень уж качественно нарисованные на компьютеры взрывы. Так что фильм несколько разочаровал.

Тёмная башня (The Dart Tower, 2017). Оригинальные произведения Стивена Кинга я не читал, так что для меня это все совершенно новая история. Как по мне, так слишком простенько и недорого. Идрис Эльба и Мэттью МакКонахи, конечно, хороши. Но их не хватает, чтобы сказать, что получилось крутое и зрелищное кино.

6 дней (6 Days, 2017). Не плохая, в общем-то история, но как-то слишком уж скучно и схематично рассказана. Как будто смотришь не художественный фильм, а какую-то полудокументальную реконструкцию.

Стена (The Wall, 2017). Противоречивые ощущение. Сначала история захватывает. Потом становится скучно. Потом главный герой начинает подбешивать. Ближе к концу в фильме почти что разочаровываешься. Но финал откровенно доставляет.

Первое убийство (First Kill, 2017). Как-то ни о чем. Можно и не смотреть.

четверг, 24 августа 2017 г.

[business.book] Дочитал книгу Константина Бакшта "Как загубить собственный бизнес. Вредные советы предпринимателям"

Хочу зафиксировать некоторые впечатления от книги Константина Бакшта "Как загубить собственный бизнес. Вредные советы предпринимателям". Правда, книгу я читал долго, по чуть-чуть. Поэтому ниже описаны общие впечатления, без какой-то конкретики вроде "вот этот совет мне понравился, в вот по поводу материала из такой-то главы у меня есть сомнения".

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

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

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

Однако, подобные недостатки не отменяют того факта, что книга явно относится к категории must read.

Точнее так: тем, кого устраивает роль наемного работника, данная книга ни к чему. А вот тем, кто задумывается над открытием своего дела (какие бы побуждения за этим не стояли), прочесть ее нужно обязательно. Желательно перед открытием своего дела. Но можно и после :)

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

Признаюсь, что это была, пожалуй, первая книга, которая заставила посмотреть на выражение "работать на себя" под совсем другим углом. И в этом главная ценность книги "Как загубить собственный бизнес. Вредные советы предпринимателям" лично для меня. Многократно перевешивающая все недостатки. Эта та вещь, которая заставляет сильно и много думать. Не то, чтобы я ощутил какое-то озарение из разряда "Ах, знал бы я это пару лет назад!". И не то, что я захотел что-то переделать или пожалел о том, что было сделано ранее. Но зато появилась отчетливость восприятия каких-то вещей и лучшее понимание факторов, которые влияют на мое поведение и на мои решения. Ради этого книгу стоило читать.

Ну и еще одна полезная штука в книге. Которая особенно будет полезна технарям, решившимся на создание своего маленького свечного заводика. Это подчеркивание важнейшей роли таких людей, как продажники. Технари, как мне думается, очень сильно недооценивают роль продавцов, маркетологов, бухгалтеров и даже HR-ов, без нормальной работы которых успешного бизнеса не будет в принципе. Книга может заставить взглянуть на этот вопрос повнимательнее. А может и кому-то вообще глаза откроет.

В общем, если задумался о том, чтобы "поработать на себя", то прочти "Как загубить собственный бизнес. Вредные советы предпринимателям". Наверняка поможет. В частности, отказаться от этой затеи ;)

суббота, 19 августа 2017 г.

[prog.c++] Небольшое обновление библиотечки cpp_util

Есть у нас маленькая библиотека cpp_util, которая является небольшой коллекцией всяких мелких полезностей (часть из которых с развитием C++ теряет актуальность, но все-таки). Время от времени мы туда какие-то полезные мелочи добавляем.

Давеча была добавлена вспомогательная функция terminate_if_throws. Эта функция получает и вызывает переданную ей лямбда-функцию. Если лямбда бросает исключение, то автоматически вызывается std::terminate (поскольку сама terminate_if_throws помечена как noexcept).

Предназначена terminate_if_throws для того, чтобы в явном виде отмечать в коде куски, которые не должны бросать исключений в принципе. А если бросают, то нам не остается ничего другого, как прервать работу приложения, т.к. восстановиться мы уже не можем, а в каком состоянии остались наши данные -- не знаем.

Ну, например, допустим у нас есть какой-то архисложный класс some_complex_data и у него есть метод do_some_modification, который меняет состояние объекта. Мы хотим, чтобы do_some_modification обеспечивал строгую гарантию безопасности исключений: т.е. либо все изменения происходят успешно, либо нет никаких изменений.

Для этого в реализации do_some_modification будет, грубо говоря, три основных шага:

  1. Проверка всех необходимых условий. Если какое-то условие не выполняется, то может быть брошено исключение.
  2. Преаллоцация необходимых ресурсов для выполнения операции. Тут запросто может выскочить исключение, если каких-то ресурсов нет.
  3. Окончательная фиксация изменений.

Достаточно тяжело написать do_some_modification() так, чтобы выжить при возникновении исключений на третьем шаге. Гораздо проще, а потому и надежнее, сделать так, чтобы при возникновении исключения на третьем шаге тупо вызывать std::abort()/std::terminate(). Как раз для этого и предназначена terminate_if_throws: она явным образом выставляет в коде метку о том, что вот здесь мы никаких исключений не ждем в принципе, а если исключение таки случится, то пережить это мы не сможем:

#include <cpp_util_3/terminate_if_throws.hpp>
...
// We want to provide strong exception guarantee for that method.
void some_complex_data::do_some_modification(const params & p) {
  // Checks all necessary conditions first.
  // Some exceptions can be thrown here.
  check_condition_one(p);
  check_condition_two(p);
  ...
  // Preallocate some resources.
  // Exceptions are expected here. But this is not a problem
  // because there is no any actual state changes yet.
  auto r1 = preallocate_resource_one(p);
  auto r2 = preallocate_resource_two(p);
  ...
  // All preparations are done. We don't expect exceptions
  // in the following block of code. But if some exception is thrown
  // then we don't know how to repair from it.
  cpp_util_3::terminate_if_throws( [&] {
    do_state_change_action_one(...);
    do_state_change_action_two(...);
    ...
  } );
}

Так же в cpp_util был добавлен макрос CPP_UTIL_3_UNIX. Сейчас он определяется в cpp_util_3/detect_compiler.hpp если определен один из символов: unix, __unix или __unix__.

PS. В cpp_util вряд ли есть что-то уникальное. Наверняка в больших библиотеках, вроде Boost-а или Folly есть соответствующие аналоги. Но смысл cpp_util был как раз в том, чтобы в мелкие проекты не тянуть тяжелые зависимости масштаба Boost-а или Folly.

среда, 16 августа 2017 г.

[prog.bugs] Сделал, нашел и исправил любопытный баг в многопоточном коде :)

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

Сценарий приблизительно такой:

  • нить №1 создает объект env;
  • на контексте нити №1 у объекта env вызывается метод start(). Внутри env.start() запускается цикл обработки событий Asio (т.е. вызывается asio::io_service::run()). По сути, env.start() вернет управление только когда завершится работа asio::io_service::run();
  • в одном из событий на контексте нити №1 создается нить №2. Ссылка на объект env передается в нить №2;
  • нить №2 какое-то время выполняет свои действия, после чего вызывает env.stop(). Внутри stop-а дается команда завершить цикл обработки событий Asio. Точнее говоря, внутри env.stop() выполняется ряд действий, одно из последних в котором -- это вызов asio::io_service::stop();
  • сразу после вызова env.stop() нить №2 завершает свою работу;
  • когда на нити №1 завершается env.start(), нить №1 разрушает объект env и дожидается завершения работы нити №2;
  • когда нить №2 завершается, завершается и работа нити №1.

Все это работало на реальном железе под Windows и gcc-5.2/vc-15.3. Но вот под Linux-ом внутри виртуалки начало падать. Не всегда, но довольно-таки регулярно.

Падало где-то между вызовом env.stop() на контексте нити №2 и сразу после возврата из env.start() на нити №1. Т.е. падало стабильно внутри нити №2 при вызове env.stop(), а нить №1 только что возвращалась из env.start().

Сразу стало очевидно, что это баг. Спустя какое-то время стало понятно, что баг происходит из-за того, что в нити №1 происходит возврат из env.start() и уничтожение env. А нить №2 все еще находится внутри env.stop(). Оставалось понять, как же так происходит, что ссылка на env внутри нити №2 перестает быть валидной прямо внутри вызова env.stop(), ведь вызов asio::io_service::stop() выполняется в самом конце и после этого вызова внутри env.stop() уже ничего не делается.

Метод env.stop() выполнял следующие шаги:

  • захватывал замок объекта env;
  • проверял, запустил ли кто-нибудь процедуру shutdown;
  • если процедура shutdown еще не запущена, то:
    • выставлял признак запуска процедуры shutdown;
    • освобождал замок объекта env;
    • выполнял ряд действий по освобождению выделенных ресурсов (эти действия должны были выполняться при освобожденном захвате объекта env);
    • вновь захватывал замок объекта;
    • проверял, все ли ресурсы освобождены (освобождение может выполниться сразу, а может занять какое-то время). Если все ресурсы освобождены, то вызывал asio::io_service::stop(). Если не все ресурсы освобождены, то просто завершал свою работу, т.к. после освобождения последнего ресурса env.stop() вызвал бы кто-то другой;
  • если же процедура shutdown была запущена, то:
    • проверял, все ли ресурсы освобождены (освобождение может выполниться сразу, а может занять какое-то время). Если все ресурсы освобождены, то вызывал asio::io_service::stop()
  • освобождал замок объекта env.

Проблема оказалась вот в чем: когда нить №2 начинает освобождать ресурсы, то все ресурсы могут быть освобождены сразу же. Как только это случается, просыпается нить №1, которая сама дергает stop() на своем контексте. Когда stop() вызывается на нити №1, то обнаруживается, что процедура shutdown запущена, все ресурсы освобождены. Поэтому вызывается asio::io_service::stop(), это приводит к возврату из asio::io_service::run(), а следом и к возврату из env.start(). А значит и к разрушению env.

Но в это время нить №2 все еще внутри env.stop(). Она как раз завершила освобождение всех ресурсов и пытается вновь захватить замок объекта env. Но к этому моменту объекта env уже нет, а значит и нет его замка. Поэтому тут-то и и возникает сегфолт.

В общем-то, ничего особенного. Нить №1 контролирует время жизни объекта env, а нить №2 пользуется этим объектом, не имея возможности как-то повлиять на время его жизни. Поэтому-то когда нить №1 уничтожает объект env, у нити №2 остается повисшая ссылка.

Любопытным этот баг делает то, что я почему-то посчитал, что метод env.stop() будет являться атомарным. Что на самом деле оказалось не так. Внутри env.stop() было "вложенное" освобождение и повторный захват замка объекта env. Как раз это вложенное освобождение и позволило нити №1 вклиниться в работу и совершить свои черные деяния. При этом вероятность того, что нить №1 окажется свободной от каких-то своих действий для того, чтобы сразу же среагировать на освобождение всех ресурсов, да так быстро, что нить №2 не успеет повторно захватить замок объекта, была очень низка. Что и показывали успешно проходившие под Windows тесты. Но вот под Linux-ом в виртуалке эта вероятность материализовалась. Причем достаточно стабильно. Так что тут мне изрядно повезло.

Посему повторюсь: многопоточное программирование на голых нитях и мутексах -- это пот, боль и кровь сложно. Не нужно такими вещами заниматься. Оставьте это занятие опытным мазохистам ;)

вторник, 8 августа 2017 г.

[prog.flame] Очень, очень не хватает массового исхода на Rust! Доколе, блин? ;)

Когда читаешь вот такое: "Писать серверного демона на С++ — это означает выгребать корки по утрам.", то возникают смешанные чувства. С одной стороны, не понимаешь, то ли это ты такой монстр, которому написать C++приложение не падающее с сегфолтами не проблема, а вокруг одни криворукие джамшуты (что маловероятно), то ли ты читаешь мнение человека с руками из жопы (что вряд ли возможно). То ли это человеку в 2005-ом в универе преподавали C++ образца 1995-го, поэтому он нормального C++ в глаза вообще никогда видел, а озвучивает лишь фобии, давно признанные правдой в кругах почитателей функциональщины. Ну ведь если ты упорот таким говном как Erlang-ом, то ведь все остальное должно же быть в принципе еще хуже, иначе как же жить дальше ? ;)

С другой стороны, C++ оброс уже таким количеством городских легенд и на профильных форумах у C++ уже давно такая дрянная репутация, что волей-неволей появляется желание сказать: да когда ж вы все уже свалите, наконец, на свой Rust? Жить же станет лучше, жизнь станет веселее. И, что характерно, работы прибавиться и тем, кто останется на C++, и тем, кто начнет писать на Rust.

Кстати говоря, если ты хорошо знаешь C++, не страдаешь фобиями по поводу шаблонов, исключений, STL-я и прочих возможностей нормального C++, то у тебя демон на C++ будет написан ну совсем не так, как такой же демон, но на C.

Там дальше еще фееричное от любителей Rust-а: Раст сейчас находится примерно на том же уровне развития, как C++ в начале-середине 90х. Тогда тоже куча школоло с горящими глазами хвалила кресты, обещала похоронить C, мудрые аксакалы говорили что не нужен. 10 лет и всё, C++ в мейнстриме.

Феерично. Просто потому, что в начале, не говоря уже про середину 90-х, C++ уже был самым, что ни есть, мейнстримом. И выбор тогда стоял достаточно просто: для прикладных вещей, где не требовалась производительность, использовались языки, вроде Visual Basic или SmallTalk. Для всего остального приходилось брать либо C++, либо Object Pascal, либо Ada. Ибо нужно было сочетать и борьбу со сложностью, и борьбу с потреблением ресурсов. Ну а C использовался где-то на системном и околосистемном уровне, да и то в начале 90-х уже началось проникновение C++ в нишу системного программирования.

И никому, кроме системщиков и железячников, C не был нужен до тех пор, пока мощности компьютеров не выросли настолько, что большие приложения начали разрабатывать на языках вроде Python, Ruby и JavaScript. Плюс пришел Web, великий и всепроникающий. Вот тогда и возродился интерес к чистому C из-за того, что узкие места для Python-а и Ruby нужно было на чем-то "расшивать", а C здесь оказался естественным выбором. Произошло это достаточно давно, лет десять тому как минимум. Посему и выросло поколение, которое всерьез считает, что C++ не нужен, т.к. писать можно на Python/Ruby/JS/Erlang, а там, где их производительности не хватает, достаточно plain old С.

И вот эти самые люди сейчас радостно кричат Rust, Rust, Rust! Как будто Rust способен серьезно расширить ту самую нишу, в которой все еще теплится какая-то жизнь для C++, и которая с каждым годом скукоживается. Ну-ну, ну-ну. Хочется верить, что я очень сильно не прав.

Посему призываю активных и латентных Rust-оманов: покажите же нам, замшелым ретроградам-C++никам, кузькину мать! Даешь Rust в продакшен! Больше программ на Rust-е, хороших и разных! Больше рабочих мест для Rust-разработчиков! Зарплата Rust-омана должна быть на 20% выше, чем в среднем по отрасли! Каждому Rust-оману по бесплатному смузи и полугодовой абонемент в ближайший барбершоп (чтобы не значили эти слова)... ;)

PS. Для желающих воспринять этот текст слишком всерьез: обратите внимание на слово Humour в списке тегов. Ну и на смайлик в заголовке поста.

среда, 2 августа 2017 г.

[prog.flame] Не нужно отождествлять и увязывать друг с другом Actor Model и Reactive Manifesto

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

Еще более усугубляет дело наличие материально заинтересованных "евангелистов", которым кровно необходимо продать свой продукт и/или услуги, построеные вокруг модели акторов. В частности, таковыми являются товарищи из небезызвестной в узких кругах конторы Lightbend. Эти товарищи стоят, в частности, за разработкой фреймворка Akka. Они же приложили руку и к небезызвестному в тех же кругах Reactive Manifesto. И все бы ничего, но продавцы услуг из Lightbend-а очень плотно увязывают Actor Model и Reactive Manifesto в своих маркетинговых материалах.

В частности, недавно они выпустили white-paper под названием "Modernization: The End Of The Heavyweight Era. Why Actor Model Matters for Could Infrastructure". В котором повествование идет приблизительно в таком ключе: разработка больших монолитных приложений на Java EE -- это отстой, прошлый век и в приличных домах Ландона и Парижу вспоминать о таком уже не комильфо. Сейчас, как-никак, XXI-ый век на дворе, поэтому есть Reactive Manifesto, а значит ваши волосы будут мягкими и пушистыми приложения должны быть отзывчивыми, эластичными и устойчивыми. Для чего вам всего лишь нужно использовать не просто модель акторов вообще, а конкретно Akka от Lightbend-а.

Причем описано все это вполне серьезно, красиво и убедительно. Можно только поучиться тому, как следует писать маркетиновые материалы, наполняя их технической шелухой для придания солидности. Однако, есть две проблемы.

вторник, 1 августа 2017 г.

[life.cinema] Очередной кинообзор (2017/07)

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


Дюнкерк (Dunkirk, 2017). Снято, конечно, мастерски. Однако, сама история не цепляет. И к основным персонажам не проникаешься ни симпатией, ни сочувствием. Может разве что за исключением героя Тома Харди.


Большой (2017). Не могу сказать, что сюжет меня зацепил. Но снято прилично, интересно смотреть, как развиваются события на экране.


Четверо против банка (Vier gegen die Bank, 2016). Немецкая криминальная комедия. Немного примитивная, немного укуренная, немного пошлая, но в целом мне понравилось.


Уйти красиво (Going in Style, 2017). Старички подсобрались и выдали пусть и средненькую, но добротную криминальную почти что комедию. Посмотрел с удовольствием.


Взрывная блондинка (Atomic Blonde, 2017). Фильм должен понравиться фанатам Шарлиз Терон. Но т.к. я не фанат, то в паузах между экшен-сценами мне было откровенно скучно. В самих экшен-сценах, как по мне, было заметно, что несмотря на проделанную работу, Шарлиз Терон -- это не Джина Карано и не Синтия Ротрок (если кто помнит). Правда, ситуацию отчасти спасал саунд-трек из хитовых композиций конца 1980-х. Например, на фрагменте из Depeche Mode я был готов простить фильму все, но и Depeche Mode оказалось слишком мало :)


Выстрел в пустоту (Shot Caller, 2017). Сильно подзатянутая криминальна драма. Мне смотреть было интересно, поскольку от трейлера у меня сложились совсем другие ожидания от сюжета. Но в самом фильме все пошло совсем по-другому, поэтому хотелось досмотреть до финала. Тем не менее некоторую неспешность и затянутость нельзя не отметить.


2:22 (2017). Фильм красивый и снятый хорошо. Но не цепляющий.


Орбита 9 (Orbita 9, 2017). Ожидал большего. Посмотреть можно хотя бы потому, что европейцы снимают кино не так, как американцы, тем более фантастику. Но не шедевр, к сожалению.

Охранник (Security, 2016). Фильм откровенно разочаровал. Как туда вписались Антонио Бандерас и Бен Кингсли -- не понятно.

понедельник, 31 июля 2017 г.

[life.sport.darts] Послесловие к World Matchplay 2017

Ну и послесловие к завершившемуся вчера PDC-шному World Matchplay. Который, как мне кажется, войдет в историю. Ибо то, что сотворил там Фил "Power" Тейлор, -- это просто спорт как он есть.

воскресенье, 30 июля 2017 г.

[life.photo] Мысли на тему хорошей (для меня) travel-камеры

Перед поездкой в Питер был вынужден решать архиважный и архисложный для себя вопрос: с какой камерой и оптикой ехать? В наличии была большая, чОрная и тяжелая зеркалка от Nikon-а (куча мегапикселей, широченный динамический диапазон, рабочие ISO вплоть до 6400) и разнообразной оптикой (в основном большой, чОрной и тяжелой). И маленькая беззеркалка Fujifilm x30 с 12-тью мегапикселями, рабочими ISO не выше 400, никаким динамическим диапазоном, кроп-фактором x7 и встроенным зум-объективом 28-112mm в эквиваленте со светосилой 2.2-2.8 (которую так же нужно пересчитывать с учетом кроп-фактора).

Казалось бы, по техническим параметрам чОрная зеркалка должна была бы выиграть. Но, кроме качества изображения нужно было принимать в расчет еще несколько далеко немаловажных для путешествия факторов, как то: размер, вес и удобство использования. Причем путешествия не в дикую природу, где ты имеешь возможность остановиться в уединенном месте и снимать понравившийся тебе пейзаж без каких-либо помех часами. А в один из самых посещаемых в России городов, в некоторых местах которого количество туристов на квадратный метр прям таки зашкаливает.

четверг, 27 июля 2017 г.

[prog.c++] Принимаются заказы на тему следующей статьи для Хабра

Отпуск закончился и пришло время возвращаться к работе. В планах подготовка очередных релизов SObjectizer-5.5 и so_5_extra, а так же подготовка доклада для C++ CoreHard Autumn 2017. При этом есть возможность выделить 1.5-2 недели для написания очередной статьи для Хабра на тему акторов (вообще) и SObjectizer-а (в частности).

Посему принимаются заявки: о чем бы вам хотелось прочитать?

Сразу хочу предупредить, что статьи получаются не самые маленькие и довольно-таки скучные, хотя на их качество вроде пока никто не жаловался (примеры можно посмотреть здесь).

На данный момент есть одна тема, которая может считаться актуальной, т.к. она всплыла в комментариях к одной из предыдущих статей на Хабре. Речь о том, как же делать stage agents для того, чтобы использовать SEDA way. Немного на эту тему говорилось здесь, но напомню суть. Допустим, нам нужно выполнить операцию, которая состоит из шагов 1, 2 и 3. Мы можем создать актора для выполнения этой операции, который сам, последовательно, будет делать шаги 1, 2 и 3. Если нам нужно одновременно обслуживать 100500 таких операций, то и акторов в программе одновременно будет жить 100500. Что имеет свои негативные последствия. Либо мы можем сделать всего трех акторов: первый будет делать шаг 1, второй -- шаг 2, третий -- шаг 3. При этом каждый актор может одновременно выполнять свой шаг для 100500 параллельных операций. Как оказалось, такая краткая формулировка оставляет у читателей белые пятна в понимании, поэтому более подробный рассказ об особенностях реализации SEDA way на акторах в виде статьи может быть интересен кому-нибудь.

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

Поэтому прошу оставлять свои соображения в комментариях, или же можно прислать их мне на почту: eao197 на stiffstream com.

среда, 26 июля 2017 г.

[life] Братская могила №142

Этот пост будет о личном, о части истории моей семьи. Хотя подобные истории совсем не уникальны для бывшего СССР.

Мой дед по отцовской линии погиб в Ленинграде зимой 1941-го года. Когда он ушел на фронт, моему отцу не было и трех лет. Соответственно, мой отец своего отца даже не помнил. А могилу смог посетить только в 1988-ом году.

В похоронке на деда было указано, что он захоронен на Пискаревском кладбище в траншее с таким-то номером. Когда отец в начале 60-х годов служил в Ленинграде, он пытался найти эту траншею, но тогда шла реконструкция кладбища и узнать где же именно находится захоронение не удалось. А в следующий раз отец смог приехать в Ленинград лишь 25 лет спустя, взяв с собой свою семью.

Тогда в архиве кладбища нашли информацию и сказали номер братской могилы. Мы нашли эту могилу. До недавних пор я думал, что помню этот номер.

Прошло еще 29 лет и вот уже я сам привез свою семью в Ленинград. И пришли на Пискаревское кладбище, на могилу к моему деду, которого даже мой отец совсем не помнил, ну а я родился через 32 года после его смерти.

И тут выяснилось, что я помню не тот номер. Приблизительное место помню, а точный номер -- нет.

Обратился в архив. И по мере ответов на вопросы архивариуса поймал себя вот на чем: дед призывался из под Твери, погиб он в Ленинграде, разыскивает его могилу внук из Гомеля. Воевала вся страна -- так нам рассказывали в детстве. Воевала вся страна, осознал я в тот момент, когда отвечал на вопросы архивариуса.

Информацию подняли достаточно быстро. Охотников Иван Андреевич 1909-го года рождения, умер 25-го декабря 1941-го года, захоронен в братской могиле №142.

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

Полмиллиона. И часть из них оборванны навсегда.

И было это совсем, совсем недавно.

пятница, 14 июля 2017 г.

[prog.c++14] so_5_extra-1.0.1 и so-5.5.19.3

Сегодня мы выкатили очередную версию so_5_extra -- 1.0.1, а накануне обновили SObjectizer до версии 5.5.19.3. Доступно все, как обычно, на SourceForge: здесь so_5_extra, здесь SObjectizer.

В so_5_extra-1.0.1 добавлена всего одна фича: это collecting_mbox.

Смысл collecting_mbox в том, что иногда агенту X нужно получить строго N сообщений типа M от других агентов. Ну вот, например, стартует родительский агент-менеджер и создает пять нужных ему дочерних коопераций. Каждая дочерняя кооперация тратит какое-то время на свои начальные действия (например, загружается конфигурация, устанавливается подключение к БД, инициализируется внешнее устройство и т.д.), после чего отсылает родителю сигнал о том, что дочерняя кооперация готова. Соответственно, родительскому агенту нужно получить все пять сигналов о готовности дочерних коопераций, лишь после этого родительский агент сможет продолжать работать.

понедельник, 10 июля 2017 г.

[prog.c++14] Развернуть std::tuple в вызов конструктора базового класса

Давеча упоролся шаблонами настолько, что потребовалось сделать на C++ что-то вот такое (проще сперва показать на примере, а уже потом рассказывать словами):

templatetypename First_base, typename Second_base >
class Some_complex_template
   : public First_base // Boom #1
   , public Second_base // Boom #2
{
public :
   // Constructor.
   templatetypename... First_base_args, typename... Second_base_args >
   Some_complex_template(
      First_base_args &&...first_args, // Args for the first base class.
      Second_base_args &&...second_args ) // Args for the second base class.
      : First_base{ std::forward<First_base_args>(first_args)... }
      , Second_base{ std::forward<Second_base_args>(second_args)... }
      {}
   ...
};

Т.е. нужно было отнаследовать шаблонный класс Some_complex_template от двух других классов, которые задаются параметрами шаблона. А затем в конструктор Some_complex_template нужно было передать два независимых друг от друга набора параметров. Первый набор параметров должен уйти в конструктор первого базового класса, второй набор -- в конструктор второго базового класса.

Насколько я понимаю, C++ не позволяет написать фукнцию/метод с переменным количеством параметров вот так: f(First &&...first, Second &&...second), что логично, т.к. при вызове f(a1, a2, a3, a4, a5) невозможно понять, что из a(i) относится к first, а что к second.

Поэтому выход из ситуации сейчас ищется в использовании std::tuple вот в таком сценарии:

templatetypename First_base, typename Second_base >
class Some_complex_template
   : public First_base
   , public Second_base
{
public :
   // Constructor.
   templatetypename... First_base_args, typename... Second_base_args >
   Some_complex_template(
      std::tuple<First_base_args...> && first_args, // Args for the first base class.
      std::tuple<Second_base_args...> && second_args ) // Args for the second base class.
      : First_base{ /*some magic is required here!*/(first_args)... }
      , Second_base{ /*some magic is required here!*/(second_args)... }
      {}
   ...
};

Но вот тут возникает вопрос, как же распаковать содержимое std::tuple в вызов конструктора базового типа?

В принципе, вся эта магия с распаковкой std::tuple в вызов некоторой функции f хорошо проиллюстрирована на том же cppreferece.com в описании возможной реализации функции std::apply. Но есть нюанс: нам нужно сделать вызов конструктора базового типа, поэтому у нас нет возможности заводить вспомогательные функции, вроде apply_impl, в которые передается дополнительный аргумент std::index_sequence...

Или все-таки есть?

пятница, 7 июля 2017 г.

[prog.c++] Программирование на шаблонах плохо дружит с программированием на бумаге

Думаю, что старые читатели блога знают, что я большой поклонник программирования на бумаге. Этому здесь было посвящено несколько заметок, начиная с одной и самых старых. И я, по прежнему активно мараю бумагу перед тем, как сделать что-нибудь новое и/или важное.

Однако, если серьезно упороться C++ными шаблонами, то пользы от предварительного программирования на бумаге оказывается заметно меньше. Думаю, это потому, что C++ я знаю не очень хорошо, поэтому не могу на бумаге написать сложный код с шаблонами так, чтобы затем не пришлось его сильно переделывать из-за того, что компилятор на тему использования шаблонов думает совсем по-другому :( Из-за этого намного проще писать код прямо в Vim-е, пытаться его компилировать, сразу же исправлять те места, по которым у меня с компилятором (и со стандартном) обнаруживаются принципиальные расхождения. Затем все повторяется снова и снова, пока я не получаю то, что мне нужно, причем так, чтобы это компилировалось и VC++, и Gcc, и clang.

Однако, от того, что код начинает появляться прямо сидя за компьютером, зачастую я не знаю, как именно будет использовать какая-то новая сущность. Поэтому на начальном этапе настоящая беда и комментариями, и с самими наименованиями в программе. А для того, чтобы все это можно было со временем доработать напильником и довести до приличного состояния, код щедро размечается тегами FIXME. И выглядит это приблизительно вот так:

template<
   typename COLLECTING_MSG,
   typename TRAITS = runtime_size_traits_t,
   typename LOCK_TYPE = std::mutex >
//FIXME: should it be final or not?
class mbox_template_t final
   : public ::so_5::abstract_message_box_t
   , protected TRAITS::size_specific_base_type
   , protected ::so_5::details::lock_holder_detector< LOCK_TYPE >::type
   {
      using specific_base_type = typename TRAITS::size_specific_base_type;

      //FIXME: document this!
      using config_type = details::config_type< COLLECTING_MSG, TRAITS, LOCK_TYPE >;

      //FIXME: document this!
      using messages_collected_builder_t = typename
            details::collected_bunch_type_selector< config_type >::builder_type;
   public :
      //FIXME: document this!
      using messages_collected_t = typename 
            details::collected_bunch_type_selector< config_type >::message_type;

   private :
      //FIXME: document this!
      using messages_collected_subscription_type = typename
         std::conditional<
               is_mutable_message< COLLECTING_MSG >::value,
               mutable_msg< messages_collected_t >,
               messages_collected_t >
            ::type;

   public :
      //FIXME: what about message tracing traits?
      templatetypename... ARGS >
      mbox_template_t(
         mbox_id_t mbox_id,
         ARGS &&... args )
         :  specific_base_type( mbox_id, std::forward<ARGS>(args)... )
         {
            details::check_mutability_validity_for_target_mbox<config_type>(
                  this->m_target );
         }

PS. Ну и еще наброшу на тему C++ных шаблонов. Когда ими реально упарываешься, то начинаешь совсем по другому относиться к спорам на тему того, что вместо C++ можно использовать plain old C или Rust. Программирование на шаблонах C++ -- это, блин, совсем другой подход к программированию. Ни Rust, ни, тем более, C, тут и рядом не стояли. Т.е. сделать одно и тоже можно будет на разных языках, но это будут совершенно разные решения, абсолютно не похожие на друг друга.

вторник, 4 июля 2017 г.

[prog.c++] Новая большая статья про SObjectizer на Хабре

Мы сделали разбор штатного примера machine_control из дистрибутива SObjectizer в виде большой статьи на Хабре: Имитируем управление устройствами с помощью акторов. По ходу дела была мысль вместо одной статьи сделать две, а может и три поменьше. Но по опыту выходит, что каждую следующую читают меньше, чем предыдущие, поэтому решено было ограничиться всего одной.

Статья получилась большой, материала в ней много. Но старались сделать доступной и понятной. Если что, то с удовольствием ответим на вопросы в комментариях.

Темы для следующих статей принимаются :)

PS. Интересное обсуждение завязалось в комментариях к одной из предыдущих статей. При использовании агентов в виде конечных автоматов могут возникать некоторые не очевидные моменты, с которыми, однако нужно считаться. Поскольку эти моменты появились не просто так.

суббота, 1 июля 2017 г.

[life.cinema] Очередной кинообзор (2017/06)

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


Таинственный город Z (The Lost City of Z, 2016). Хороший фильм, мне понравился. Может быть в каких-то моментах он был слегка нудноват и не динамичен. Но это итоговое впечатление не испортило. Тем не менее, подозреваю, что сама по себе история про увлеченного путешественника будет интересна не всем.

Меч короля Артура (King Arthur: Legend of the Sword, 2017). Качественно снятое фэнтези. Гай Ричи не подкачал. Перед просмотром у меня было скептическое отношение из-за негативных отзывов о фильме, которые доводилось читать. Но для своего жанра получилось весьма достойно.

Мумия (The Mummy, 2017). Хороший развлекательный аттракцион. Правда, я лично не понял двух вещей. Во-первых, зачем там Том Круз в главной роли. Ни он в фильм ничего не привнес, ни фильм ему ничего не дал. От участия Круза ждешь чего-то большего, но здесь ничего "эдакого" не было. Во-вторых, зачем нужно было сюда вплетать еще и историю Джекила и Хайда. В общем, сходить и развлечься можно, но не более того.

Киллер поневоле (Un petit boulot, 2016). Понравилось. Вот просто понравилось и все.

Эксперимент "Офис" (The Belko Experiment, 2017). Вообще такого рода фильмы мне нравятся. Здесь же явно ощущалось, что бюджет у картины был более чем скромным, однако несколько постаревших актеров, чей звездный час уже давно в прошлом, смогли удержать фильм на вполне себе достойном уровне.

Жмот (Radin!, 2016). Любопытный фильм. Как комедия он меня не сильно зацепил (хотя и придраться не к чему). Но зато показалось, что Дэни Бун очень хочет сменить амплуа комика на амплуа серьезного драматического актера. И что таланта для этого у него вполне может хватить.

Ограбление в Хаттон-Гарден (The Hatton Garden Job, 2017). Сразу скажу, что снято так себе. Но есть в нем какой-то английский стиль, который добавляет фильму какого-то непонятного очарования и заставляет смотреть даже не смотря на неторопливое развитие событий и отсутствие каких-то эффектных сцен.

Его собачье дело (Once Upon a Time in Venice, 2017). Не шедевр, но посмотреть вполне можно, особенно если шутки "пониже пояса" не смущают.

Возьми меня штурмом (Raid dingue, 2016). Типичная французская комедия. Сильно предсказуемая, местами смешная. Мне особенно понравилось то, что Дэни Бун вообще не кривлялся, и то, как Иван Атталь сыграл эксцентричного злодея.

Калифорнийский дорожный патруль (CHIPS, 2017). Обычная американская комедия. Не шедевр, но и не полный шлак. Можно посмотреть, но и ждать чего-то выдающегося не стоит.

Трансформеры: Последний рыцарь (Transformers: The Last Knight, 2017). Все деньги на экране и это видно, к визуальной составляющей вообще никаких претензий. Но вот все остальное находится где-то по ту сторону добра и зла. Такое ощущение, что Майкла Бэйя настолько утомила вся эта история с трансформерами, что он сам для себя решил: "Ну раз вы просите, то я вам сниму такое, что вы сами не будете рады, что упросили меня." Вот он и снял так, что смотреть это могут разве что дети до 10 лет.

Еще я тут пересмотрел фильм "Эверест" от 2015-го года. Как-то в этот раз он зашел гораздо лучше. Может быть потому, что в этот раз я уже не обращал внимания на то, как снято. Зато следил за тем, о чем рассказывает кино. В итоге поменял свое мнение о фильме на гораздо более лучшее. Достойное кино, как по мне.

вторник, 27 июня 2017 г.

[prog.c++] Практически динамически-типизированное программирование

Давеча, занимаясь примером для демонстрации Asio-инфраструктуры для SObjectizer из нового проекта so_5_extra, написал фрагмент C++кода, в котором практически не фигурировали имена конкретных типов. Буквально возникло впечатление, что программирую на каком-то динамически-типизированном языке (правда, с излишне многословным синтаксисом). Кому интересно посмотреть немного C++14-того хардкора милости прошу под кат.

понедельник, 26 июня 2017 г.

[prog.c++] so_5_extra-1.0.0 и so-5.5.19.2

Мы выпустили первую версию своего нового проекта поверх SObjectizer -- so_5_extra версии 1.0.0.

В этой версии в so_5_extra доступны:

  • so_5::extra::env_infrastructures::asio::simple_not_mtsafe -- реализация однопоточной инфраструктуры SObjectizer-а на базе Asio. Т.е. с использованием этой инфраструктуры и Asio, и SObjectizer смогут работать на одной и той же рабочей нити;
  • so_5::extra::mboxes::round_robin -- специальный mbox, который доставляет сообщения поочередно каждому из N агентов, подписанных на это сообщение;
  • so_5::extra::shutdowner -- небольшой инструмент для упрощения операции завершения работы в больших приложениях.

Исходники можно взять либо из репозитория, либо загрузить из соответствующего раздела.

Документацию по проекту можно найти в Wiki. Если из документации чего-то не понятно или что-то в ней не описано, то не сочтите за труд, дайте нам знать. Улучшим, расширим и углубим :)

Проект header-only. Если захочется собрать тесты и примеры самостоятельно, то придется воспользоваться Ruby и Mxx_ru. Зависимости так же подтягиваются через MxxRu::externals. Но в секции Files есть архивы с именами вида so_5_extra-1.0.0-full.tar.xz, в которых уже все зависимости присутствуют. Поэтому можно брать *-full.tar.xz архив, распаковывать, прописывать в INCLUDE путь к so_5_extra-1.0.0/dev и пробовать.

Работоспособность проверялась под Linux-ом (gcc 5.4 и 7.1, clang 3.7 и 4.8) и Windows (gcc 5.2-7.1, VC++ 14.0 и 15.0). На всякий случай выставлять -Werror при работе с so_5_extra не советуем, т.к. и gcc, и clang очень сильно ругаются на потроха Asio.

В планах у нас добавление еще нескольких фич в so_5_extra. Следующие версии будут выходить по мере добавления новых фич. В том числе в планах и simple_mtsafe-инфраструктура для Asio, но приоритет у этой задачи не самый высокий. Если кому-то нужна thread-safe реализация Asio-инфраструктуры для SO-5, то дайте знать. Постараемся повысить приоритет.

Обращаем внимание, что so_5_extra распространяется под двойной лицензией: GNU Affero GPL для OpenSource применения, и коммерческая лицензия для использования в закрытых проектах. Если кому-то интересна коммерческая лицензия, то пишите на info at stiffstream dot com, там цена вопроса порядка $40 за одного разработчика в год.

Попутно мы сделали SObjectizer-5.5.19.2, в который вошло несколько фич, необходимых для реализации so_5_extra. Дистрибутивы SObjectizer лежат там же, где и обычно.