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

О блоге

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

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

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

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

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

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

вторник, 28 июля 2026 г.

[prog.thoughts] И еще одна мысль про влияние ИИ на программирование

Навеяно вот этой статьей: Английский вместо кода

Когда я приходил в профессию на программиста возлагались следующие задачи:

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

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

Никаких бизнес-аналитиков и тестировщиков в то время в наших палестинах еще не существовало как класса, появилось все это лет на 5-10 позже (хотя на Западе, как я слышал, к началу 1990-х все это уже было). Ну да не суть.

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

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

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

Только вот для некоторых это уже не будет тем самым настоящим программированием от которого эти некоторые получали наибольший кайф и в котором пытались достичь своего наивысшего мастерства.

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

Как по мне, так это уже и не программирование вовсе.

понедельник, 27 июля 2026 г.

[prog.thoughts] Подумалось тут про ИИ и будущее программирования...

Продвинутые ИИ модели дошли до того, чтобы переписывать большие проекты и генерировать реализации по спецификациям с минимальным участием человека. А люди, плотно использующие ИИ для разработки ПО все больше говорят о том, что работа с ИИ агентами становится похожа на работу тим-лида или даже технического лида: раздаешь задания, контролируешь исполнение, сам практически ничего не пишешь и, может быть, даже не читаешь то, что ИИ генерирует (хотя бы потому, что объемы сгенерированного кода будут превышать возможности человека-ревьювера).

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

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

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

суббота, 25 июля 2026 г.

[prog.c++.multithreading] Сломал себе мозг пытаясь понять что не нравится thread sanitizer-у

Upd. Похоже, что проблема сперва была в том, что SO-5 подключался в проект через vcpkg и когда проект компилировался с TSan, то линковался SO-5, собранный без TSan. А когда я включил исходники SO-5 в сам проект, чтобы все компилировалось с одинаковыми ключами, то ошибся с порядком команд в CMakeLists.txt и при компиляции SO-5 опции для TSan не учитывались. Если же собрать и SO-5, и остальной проект с одинаковыми опциями, то данной проблемы не возникает (пока?).

В текущем проекте thread sanitizer периодически выдает предупреждение о data race на фрагменте, который относится к SObjectizer-у.

Самое плохое то, что:

  • я не понимаю в чем именно thread sanitizer видит проблему. Соответственно, неизвестно, является ли срабатывание TSan-а ложно позитивным или же есть реальная ошибка, которую следует исправить;
  • мне не удается повторить такую же ситуацию в тестах для самого SO-5. Т.е. в рамках проекта TSan диагностику выдает, а в мелких тестах, которые пытаются повторить тот же сценарий -- нет. Ни в какую. Что сильно затрудняет разбирательства и поиск обходных путей.

Что здесь происходит:

Агент на нити T7 отсылает сообщение GiveMeTask агенту-координатору, который работает на нити T3.

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

В это же время на нити T7 завершается процедура отсылки сообщения.

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

Экземпляр сообщения GiveMeTask создается на нити T7. Указатель на этот экземпляр хранится на нити T7 внутри объекта intrusive_ptr_t.

На нити T3 внутри execution_demand_t так же есть свой объект intrusive_ptr_t который хранит указатель на этот же экземпляр GiveMeTask.

Т.е. на двух нитях есть два разных intrusive_ptr_t, которые хранят в себе указатель на один и тот же объект GiveMeTask.

При этом счетчик ссылок на GiveMeTask хранится в самом объекте GiveMeTask. Класс GiveMeTask наследуется от so_5::message_t:

struct GiveMeTask final : public so_5::message_t
{
    const so_5::mbox_t _workerMbox;

    GiveMeTask(so_5::mbox_t workerMbox)
        : _workerMbox{ std::move(workerMbox) }
    {}
};

А so_5::message_t наследуется от so_5::atomic_refcounted_t:

class message_t : public atomic_refcounted_t
    {
        ...
    };

В so_5::atomic_refcounted_t счетчик ссылок хранится в виде std::atomic. Т.е. операции инкремента-декремента количества ссылок происходят атомарно и не нуждаются в дополнительной синхронизации.

Получается, что на нити T7 создается новый экземпляр GiveMeTask, указатель на него сохраняется в локальном объекте intrusive_ptr_t и счетчик ссылок на GiveMeTask выставляется в 1.

На нити T7 вызывается send для GiveMeTask и формируется execution_demand_t для агента-координатора. Внутри execution_demand_t создается свой intrusive_ptr_t и счетчик ссылок для GiveMeTask получает значение 2.

Затем на нити T3 происходит обработка GiveMeTask, после чего начинается разрушение execution_demand_t и его содержимого (в том числе и второго intrusive_ptr_t).

Но чуть раньше на нити T7 происходит разрушение своего intrusive_ptr_t после чего счетчик ссылок в GiveMeTask опускается до 1.

А уже после этого на нити T3 счетчик ссылок на GiveMeTask обнуляется и происходит разрушение объекта GiveMeTask.

Происходят действия именно в этом порядке. Если бы сперва полностью разрушился execution_demand_t на нити T3 и лишь после этого началось уничтожение intrusive_ptr_t на нити T7, то деструктор GiveMeTask вызвался бы на нити T7, а не на нити T3.

Т.е. с моей точки зрения здесь все OK. Но TSan видит data race. А я не понимаю про какой data race идет речь.

Под катом выхлоп от TSan в текстовом виде.

среда, 22 июля 2026 г.

[prog.flame] Узнал давеча о переписывании какого-то Bun с какого-то Zig на какой-то Rust :)

Т.к. я не тормоз, а всего лишь медленный газ, то только вчера узнал об очередном скандале на просторах этого нашего Интернета. Если кто-то, как и я, все еще не в курсе, то:

Некий проект Bun (говорят, что это какой-то очень и очень быстрый инструментарий для JavaScript, включая и виртуальную машину) внутри Antropic-а переписали с Zig на Rust посредством нескольких десятков параллельно работавших в течении 11 дней агентов: https://bun.com/blog/bun-in-rust

На рассказ об этой процедуре среагировал разработчик языка Zig и высказался в том духе, что баба с возу кобыле легче переписали на Rust и славно, а то эти Bun-овцы уже изрядно задолбали дискредитацией всего Zig-а низким качеством своего кода: https://andrewkelley.me/post/my-thoughts-bun-rust-rewrite.html

Как по мне, так и то, и другое чтиво вполне себе интересное и заслуживает того, чтобы быть прочитанным.

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

fn foo(a_ptr: SharedPtr(TCPSocket)) !void {
  const a: *TCPSocket = a_ptr.get();
  defer a_ptr.deref();

  const b = try do_something_with_a(a);
  defer b.deref();

  // ...
}

Поскольку я не знаю Zig, то очень интересно, а это прям идиоматический код и лучше на Zig уже не получится? Или же это откровенный говнокод, низкое качество которого очевидно даже начинающим Zig-ерам?

PS. Так же я не полностью согласен с тезисом, что для C++ надежность кода достигается за счет следования какому-то административно утвержденному code style (как в случае с упомянутым в блог-посте от Bun-овцев Google C++ code style guide). Многие проблемы (вроде контроля времени жизни объектов, находящихся в совместном использовании) должны решаться не соглашениями, а типами (вроде unique_ptr, shared_ptr и т.д.) Но кто я такой, чтобы рассуждать на эту тему? 😉

PPS. А Rust подтверждает свою репутацию языка, на котором ничего нового не пишут, а только переписывают существующее 🤣

пятница, 17 июля 2026 г.

[prog.c++.multithreading] Мой способ обойти ложное срабатывание в TSan с инверсией порядка захвата mutex-ов

Продолжение вчерашней темы с ложно позитивным срабатыванием thread sanitizer, когда TSan ошибочно диагностировал инверсию порядка захвата mutex-ов.

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

Было найдено вот такое решение:

#if defined( __SANITIZE_THREAD__ )

templatetypename M >
class tsan_friendly_lock_guard
{
   M & m_what;

public:
   tsan_friendly_lock_guard( M & what )
      : m_what{ what }
   {
      while( !m_what.try_lock() )
      {
         std::this_thread::yield();
      }
   }

   ~tsan_friendly_lock_guard()
   {
      m_what.unlock();
   }
};

#else

templatetypename M >
class tsan_friendly_lock_guard
   {
      std::lock_guard< M > m_guard;

   public:
      tsan_friendly_lock_guard( M & mutex )
         : m_guard{ mutex }
         {}
   };

#endif

Затем в тех местах кода, где TSan ругался на потенциальную инверсию порядка захвата mutex-ов, std::lock_guard был заменен на tsan_friendly_lock_guard. И оно сработало: https://godbolt.org/z/fb84zsr4M.