четверг, 16 июля 2026 г.

[prog.c++.multithreading] Теперь уж точно false positive в thread sanitizer-е

Следом за предыдущей, нашел еще одну неприятную ситуацию с thread sanitizer. Но теперь это на 100% ложно позитивное срабатывание.

Посмотреть можно на godbolt: https://godbolt.org/z/zfGfhq89d

Суть в том, что в одной нити захватывается сперва mutex у child-а, а затем, при все еще захваченном mutex-е child-а, захватывается mutex у parent-а.

А потом, когда все ранее захваченные mutex-ы освобождены, уже на другой нити сперва захватывается mutex у parent-а, а следом, не отпуская mutex parent-а, захватывается mutex у child-а.

Thread sanitizer выдает предупреждение о потенциальном дедлоке из-за инверсии порядка захвата мутексов.

Только вот здесь эта инверсия невозможна в принципе, т.к. сперва гарантированно заканчиваются все операции с child-ом, и лишь затем стартует нить, на которой делаются манипуляции с parent-ом.

И вот как удовлетворить thread sanitizer, чтобы он в данном месте не выдавал свою диагностику... Это пока для меня большой вопрос.

Upd. Похоже, это уже известная проблема. С 2022-го года.

#include <thread>
#include <mutex>

struct info
{
   std::mutex m_lock;

   info * m_parent{ nullptr };
   info * m_child{ nullptr };

   void
   activate( info * parent )
   {
      std::lock_guard< std::mutex > lock{ m_lock };

      m_parent = parent;

      m_parent->add_child( this );
   }

   void
   deactivate()
   {
      std::lock_guard< std::mutex > lock{ m_lock };

      if( m_child )
         m_child->deactivate();
   }

   void
   add_child( info * child )
   {
      std::lock_guard< std::mutex > lock{ m_lock };

      m_child = child;
   }
};

int main()
{
   info parent, child;

   child.activate( &parent );

   std::thread parallel_task{ [&parent] { parent.deactivate(); } };
   parallel_task.join();
}

Комментариев нет: