вторник, 1 января 2030 г.

О блоге

Более двадцати лет я занимался разработкой ПО, в основном как программист и тим-лид, а в 2012-2014гг как руководитель департамента разработки и внедрения ПО в компании Интервэйл (подробнее на LinkedIn). В настоящее время занимаюсь развитием компании по разработке ПО stiffstream, в которой являюсь одним из соучредителей. Поэтому в моем блоге много заметок о работе, в частности о программировании и компьютерах, а так же об управлении.

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

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

[life.photo] Характерный портрет: вы и ваш мир моими глазами. Безвозмездно :)

Вы художник? Бармен или музыкант? Или, может быть, коллекционер? Плотник или столяр? Кузнец или слесарь? Владеете маленьким магазинчиком или управляете большим производством? Реставрируете старинные часы или просто починяете примус? Всю жизнь занимаетесь своим любимым делом и хотели бы иметь фото на память?

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

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

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

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

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

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

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

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

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

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

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

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

суббота, 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-а.

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