В текущем проекте 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 в текстовом виде.
WARNING: ThreadSanitizer: data race (pid=105293)
Write of size 8 at 0x720800038028 by thread T3:
#0 operator delete(void*, unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:150 (libtsan.so.2+0xa66c8) (BuildId: 2a13a7710e361d06f7babbea53065ca2be93f738)
#1 Calculation::CoordinatorImpl::GiveMeTask::~GiveMeTask() /home/eao197/sandboxes/<censored>/include/CalculationCoordinatorAgents.h:23 (<censored>+0x1420c2a) (BuildId: d95e75b5663cd24e4ecc0f7a4ab2777babe82359)
#2 so_5::intrusive_ptr_t<so_5::message_t>::dismiss_object() /home/eao197/sandboxes/<censored>/so_5/atomic_refcounted.hpp:322 (<censored>+0x16ac5db) (BuildId: d95e75b5663cd24e4ecc0f7a4ab2777babe82359)
#3 so_5::intrusive_ptr_t<so_5::message_t>::~intrusive_ptr_t() /home/eao197/sandboxes/<censored>/so_5/atomic_refcounted.hpp:178 (<censored>+0x16ac5db)
#4 so_5::execution_demand_t::~execution_demand_t() /home/eao197/sandboxes/<censored>/so_5/execution_demand.hpp:61 (<censored>+0x16ac5db)
#5 void std::destroy_at<so_5::execution_demand_t>(so_5::execution_demand_t*) /usr/include/c++/13/bits/stl_construct.h:88 (<censored>+0x16ac5db)
#6 void std::allocator_traits<std::allocator<so_5::execution_demand_t> >::destroy<so_5::execution_demand_t>(std::allocator<so_5::execution_demand_t>&, so_5::execution_demand_t*) /usr/include/c++/13/bits/alloc_traits.h:560 (<censored>+0x16ac5db)
#7 std::deque<so_5::execution_demand_t, std::allocator<so_5::execution_demand_t> >::pop_front() /usr/include/c++/13/bits/stl_deque.h:1580 (<censored>+0x16ac5db)
#8 so_5::disp::reuse::work_thread::details::no_activity_tracking_impl_t::serve_demands_block(std::thread::id, std::deque<so_5::execution_demand_t, std::allocator<so_5::execution_demand_t> >&) /home/eao197/sandboxes/<censored>/so_5/disp/reuse/work_thread/work_thread.hpp:448 (<censored>+0x16ac5db)
#9 so_5::disp::reuse::work_thread::details::work_thread_template_t<so_5::disp::reuse::work_thread::details::no_activity_tracking_impl_t>::body() /home/eao197/sandboxes/<censored>/so_5/disp/reuse/work_thread/work_thread.hpp:705 (<censored>+0x16ac5db)
Previous atomic write of size 8 at 0x720800038028 by thread T7:
#0 std::__atomic_base<unsigned long>::operator--() /usr/include/c++/13/bits/atomic_base.h:410 (<censored>+0x141ff80) (BuildId: d95e75b5663cd24e4ecc0f7a4ab2777babe82359)
#1 so_5::atomic_refcounted_t::dec_ref_count() /home/eao197/sandboxes/<censored>/so_5/atomic_refcounted.hpp:80 (<censored>+0x141ff80)
#2 so_5::intrusive_ptr_t<so_5::message_t>::dismiss_object() /home/eao197/sandboxes/<censored>/so_5/atomic_refcounted.hpp:317 (<censored>+0x141ff80)
#3 so_5::intrusive_ptr_t<so_5::message_t>::~intrusive_ptr_t() /home/eao197/sandboxes/<censored>/so_5/atomic_refcounted.hpp:178 (<censored>+0x141ff80)
#4 void so_5::low_level_api::deliver_message<Calculation::CoordinatorImpl::GiveMeTask>(so_5::message_delivery_mode_t, so_5::abstract_message_box_t&, std::type_index, std::unique_ptr<Calculation::CoordinatorImpl::GiveMeTask, std::default_delete<Calculation::CoordinatorImpl::GiveMeTask> >) /home/eao197/sandboxes/<censored>/so_5/mbox.hpp:393 (<censored>+0x141ff80)
#5 void so_5::impl::instantiator_and_sender_base<Calculation::CoordinatorImpl::GiveMeTask, false>::send<so_5::intrusive_ptr_t<so_5::abstract_message_box_t> const&>(so_5::intrusive_ptr_t<so_5::abstract_message_box_t> const&, so_5::intrusive_ptr_t<so_5::abstract_message_box_t> const&) /home/eao197/sandboxes/<censored>/so_5/send_functions.hpp:59 (<censored>+0x141ff80)
#6 void so_5::send<Calculation::CoordinatorImpl::GiveMeTask, so_5::intrusive_ptr_t<so_5::abstract_message_box_t> const&, so_5::intrusive_ptr_t<so_5::abstract_message_box_t> const&>(so_5::intrusive_ptr_t<so_5::abstract_message_box_t> const&, so_5::intrusive_ptr_t<so_5::abstract_message_box_t> const&) /home/eao197/sandboxes/<censored>/so_5/send_functions.hpp:231 (<censored>+0x141ff80)
#7 Calculation::CoordinatorImpl::Worker::so_evt_start() /home/eao197/sandboxes/<censored>/src/model/calc/CalculationCoordinatorAgents.cpp:30 (<censored>+0x141ff80)
#8 so_5::agent_t::demand_handler_on_start(std::thread::id, so_5::execution_demand_t&) /home/eao197/sandboxes/<censored>/so_5/agent.cpp:1433 (<censored>+0x1671404) (BuildId: d95e75b5663cd24e4ecc0f7a4ab2777babe82359)
#9 <null> <null> (libstdc++.so.6+0xecdb3) (BuildId: 753c6c8608b61d4e67be8f0c890e03e0aa046b8b)

Комментариев нет:
Отправить комментарий