Skip to content

Conversation

@Lestropie
Copy link
Member

Changes in 2be257c made as part of #3011 resulted in there being two separate attempts to construct an Image class from the same input header.

Fixes #3171.


Classic case of many implicit casts from Image to Header due to not explicitly creating a Header at commencement of run(), and using Image for class construction where a Header would have sufficed. 2be257c was supposed to clean some of these up but clearly I didn't look closely enough or verify.

I tried adding a CI test for tckglobal utilising the data in dwi2fod/msmt/, but I get what seems to me to be an unrelated assertion failure at:
https://github.com/MRtrix3/mrtrix3/blob/3.0.7/src/dwi/tractography/GT/particlegrid.h#L94
Will see if I can figure it out, but @dchristiaens might have a better shot.

Changes in 2be257c made as part of #3011 resulted in there being two separate attempts to construct an Image class from the same input header.
Fixes #3171.
@Lestropie
Copy link
Member Author

Lestropie commented Sep 4, 2025

Wasn't able to resolve assertion failure at first sitting.

If my interpretation is correct, and the purpose of the ParticleGrid initialisation is to produce a voxel grid where:

  • The bottom corner of the first grid element aligns perfectly with the bottom corner of the first input image voxel;
  • The grid extends beyond the top corner of the last input image voxel;

, then perhaps:

T_s2g = H.transform() * newspacing;
T_s2g = T_s2g.inverse().translate(shift);

should be:

T_s2g = H.transform() * newspacing;
T_s2g.translate(-shift);
T_s2g = T_s2g.inverse();

?
This doesn't resolve however, so either I'm wrong and/or there's something upstream permitting particles to be pushed outside the grid.
Is the grid additionally in need of a margin? (That didn't resolve assertions for me either)

@Lestropie Lestropie requested review from a team and dchristiaens September 5, 2025 01:10
@dchristiaens
Copy link
Member

I think I managed to reproduce the assertion error, and I implemented a fix. @Lestropie can you test if this works for you?

@Lestropie
Copy link
Member Author

There looks to still be a race condition somewhere.
I can run the first of the added tests many times with -nthreads 0 without issue, but running multi-threaded yields this ~ 50% of the time:

/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/debug/safe_iterator.h:492:
In function:
    bool gnu_debug::operator!=(const _Self &, const _Self &)

Error: attempt to compare a singular iterator to a past-the-end iterator.

Objects involved in the operation:
    iterator "lhs" @ 0x7fffca7fa1d8 {
      type = gnu_cxx::normal_iterator<MR::DWI::Tractography::GT::Particle* const*, std::vector<MR::DWI::Tractography::GT::Particle*, std::allocator<MR::DWI::Tractography::GT::Particle*> > > (constant iterator);
      state = singular;
      references sequence with type 'std::debug::vector<MR::DWI::Tractography::GT::Particle*, std::allocator<MR::DWI::Tractography::GT::Particle*> >' @ 0x5555558556f0
    }
    iterator "rhs" @ 0x7fffca7fa1b0 {
      type = gnu_cxx::normal_iterator<MR::DWI::Tractography::GT::Particle* const*, std::vector<MR::DWI::Tractography::GT::Particle*, std::allocator<MR::DWI::Tractography::GT::Particle*> > > (constant iterator);
      state = past-the-end;
      references sequence with type 'std::debug::vector<MR::DWI::Tractography::GT::Particle*, std::allocator<MR::DWI::Tractography::GT::Particle*> >' @ 0x5555558556f0
    }
Backtrace:
#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=140736590759488) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=140736590759488) at ./nptl/pthread_kill.c:78
#2  __GI___pthread_kill (threadid=140736590759488, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3  0x00007ffff6042476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff60287f3 in __GI_abort () at ./stdlib/abort.c:79
#5  0x00007ffff64a51fb in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x000055555573dd8e in __gnu_debug::operator!= (__lhs=invalid iterator, __rhs=0x7fffd4001dc0)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/debug/safe_iterator.h:492
#7  0x000055555573d196 in MR::DWI::Tractography::GT::InternalEnergyComputer::scanNeighbourhood (this=0x55555591a340, 
    p=0x7fffbc001690, alpha0=-1, currTemp=0.10000000000000001) at src/dwi/tractography/GT/internalenergy.cpp:66
#8  0x000055555573cefa in MR::DWI::Tractography::GT::InternalEnergyComputer::stageConnect (this=0x55555591a340, 
    pe1=..., pe2=...) at src/dwi/tractography/GT/internalenergy.cpp:29
#9  0x0000555555650b8e in MR::DWI::Tractography::GT::EnergySumComputer::stageConnect (this=0x55555591a300, pe1=..., 
    pe2=...) at src/dwi/tractography/GT/energy.h:90
#10 0x00005555557334ac in MR::DWI::Tractography::GT::MHSampler::connect (this=0x7ffff62aa6e0)
    at src/dwi/tractography/GT/mhsampler.cpp:197
#11 0x000055555573283d in MR::DWI::Tractography::GT::MHSampler::next (this=0x7ffff62aa6e0)
    at src/dwi/tractography/GT/mhsampler.cpp:55
#12 0x00005555557326ed in MR::DWI::Tractography::GT::MHSampler::execute (this=0x7ffff62aa6e0)
    at src/dwi/tractography/GT/mhsampler.cpp:32
#13 0x0000555555688869 in std::__invoke_impl<void, void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> (
    __f=@0x55555592b100: (void (MR::DWI::Tractography::GT::MHSampler::*)(MR::DWI::Tractography::GT::MHSampler * const)) 0x5555557326d0 <MR::DWI::Tractography::GT::MHSampler::execute()>, __t=@0x55555592b0f8: 0x7ffff62aa6e0)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:74
#14 0x00005555556887ad in std::__invoke<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> (
    __fn=@0x55555592b100: (void (MR::DWI::Tractography::GT::MHSampler::*)(MR::DWI::Tractography::GT::MHSampler * const)) 0x5555557326d0 <MR::DWI::Tractography::GT::MHSampler::execute()>, __args=@0x55555592b0f8: 0x7ffff62aa6e0)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:96
#15 0x0000555555688782 in std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >::_M_invoke<0ul, 1ul> (this=0x55555592b0f8)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_thread.h:279
#16 0x0000555555688745 in std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >::operator() (this=0x55555592b0f8)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_thread.h:286
#17 0x000055555568866c in std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>::operator() (this=0x7fffca7fa9a8)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/future:1430
#18 0x0000555555688630 in std::__invoke_impl<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>&> (__f=...)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:61
#19 0x00005555556885b8 in std::__invoke_r<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>, std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>&> (__fn=...)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:142
#20 0x00005555556884b0 in std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void> >::_M_invoke(std::_Any_data const&) (__functor=...)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_function.h:290
#21 0x00005555556739e8 in std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>::operator()() const (this=0x7fffca7fa9a8)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_function.h:591
#22 0x0000555555673769 in std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) (this=0x55555592b0c0, __f=0x7fffca7fa9a8, 
    __did_set=0x7fffca7fa906) at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/future:587
#23 0x000055555567398f in std::__invoke_impl<void, void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::__invoke_memfun_deref, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (
    __f=@0x7fffca7fa8f0: (void (std::__future_base::_State_baseV2::*)(std::__future_base::_State_baseV2 * const, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()> *, bool *)) 0x555555673740 <std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*)>, __t=@0x7fffca7fa8e8: 0x55555592b0c0, 
    __args=@0x7fffca7fa8d8: 0x7fffca7fa906, __args=@0x7fffca7fa8d8: 0x7fffca7fa906)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:74
#24 0x00005555556738fd in std::__invoke<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (
    __fn=@0x7fffca7fa8f0: (void (std::__future_base::_State_baseV2::*)(std::__future_base::_State_baseV2 * const, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()> *, bool *)) 0x555555673740 <std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*)>, __args=@0x7fffca7fa8d8: 0x7fffca7fa906, 
    __args=@0x7fffca7fa8d8: 0x7fffca7fa906, __args=@0x7fffca7fa8d8: 0x7fffca7fa906)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:96
#25 0x00005555556738c4 in std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#1}::operator()() const (this=0x7fffca7fa868)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/mutex:852
#26 0x0000555555673894 in std::once_flag::_Prepare_execution::_Prepare_execution<std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#1}>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::{lambda()#1}::operator()() const (this=0x7fffca7fa798)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/mutex:788
#27 0x0000555555673861 in std::once_flag::_Prepare_execution::_Prepare_execution<std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#1}>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::{lambda()#1}::__invoke() () at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/mutex:788
#28 0x00007ffff6099ee8 in __pthread_once_slow (once_control=0x55555592b0d8, 
    init_routine=0x7ffff64dad50 <__once_proxy>) at ./nptl/pthread_once.c:116
#29 0x0000555555622efb in __gthread_once (__once=0x55555592b0d8, __func=0x7ffff64dad50 <__once_proxy>)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/x86_64-linux-gnu/c++/12/bits/gthr-default.h:700
#30 0x00005555556736e9 in std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (__once=..., 
    __f=@0x7fffca7fa8f0: (void (std::__future_base::_State_baseV2::*)(std::__future_base::_State_baseV2 * const, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()> *, bool *)) 0x555555673740 <std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*)>, __args=@0x7fffca7fa8d8: 0x7fffca7fa906, 
    __args=@0x7fffca7fa8d8: 0x7fffca7fa906, __args=@0x7fffca7fa8d8: 0x7fffca7fa906)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/mutex:859
#31 0x0000555555673331 in std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) (this=0x55555592b0c0, __res=..., 
    __ignore_failure=false) at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/future:426
#32 0x0000555555688014 in std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>::_M_run (this=0x55555592b0c0)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/future:1772
#33 0x0000555555688df9 in std::__invoke_impl<void, void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>*> (
    __f=@0x55555592b150: (void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>::*)(std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void> * const)) 0x555555687fb0 <std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>::_M_run()>, 
    __t=@0x55555592b148: 0x55555592b0c0)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:74
#34 0x0000555555688d3d in std::__invoke<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>*> (
    __fn=@0x55555592b150: (void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>::*)(std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void> * const)) 0x555555687fb0 <std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>::_M_run()>, 
    __args=@0x55555592b148: 0x55555592b0c0)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:96
#35 0x0000555555688d12 in std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>*> >::_M_invoke<0ul, 1ul> (this=0x55555592b148)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_thread.h:279
#36 0x0000555555688cd5 in std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>*> >::operator() (this=0x55555592b148)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_thread.h:286
#37 0x0000555555688bb9 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*> >, void>*> > >::_M_run (this=0x55555592b140)
    at /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_thread.h:231
#38 0x00007ffff64dc253 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#39 0x00007ffff6094ac3 in start_thread (arg=<optimised out>) at ./nptl/pthread_create.c:442
#40 0x00007ffff6126850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

@daljit46
Copy link
Member

daljit46 commented Sep 18, 2025

@dchristiaens If it's a data race (and not a logic race condition), it may be helpful to build and test with thread sanitizer: CFLAGS="-fsanitize=thread" ./configure && ./build.

@daljit46
Copy link
Member

daljit46 commented Sep 18, 2025

I ran with TSAN on my Linux machine and it confirmed that indeed there is a data race:

WARNING: ThreadSanitizer: data race (pid=601769)
  Read of size 4 at 0x7250001006d4 by thread T35:
    #0 MR::DWI::Tractography::GT::InternalEnergyComputer::scanNeighbourhood(MR::DWI::Tractography::GT::Particle const*, int, double) <null> (tckglobal+0xfe758) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #1 MR::DWI::Tractography::GT::InternalEnergyComputer::stageConnect(MR::DWI::Tractography::GT::ParticleEnd const&, MR::DWI::Tractography::GT::ParticleEnd&) <null> (tckglobal+0xfdc84) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #2 MR::DWI::Tractography::GT::EnergySumComputer::stageConnect(MR::DWI::Tractography::GT::ParticleEnd const&, MR::DWI::Tractography::GT::ParticleEnd&) <null> (tckglobal+0x1321d2) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #3 MR::DWI::Tractography::GT::MHSampler::connect() <null> (tckglobal+0xf3e4d) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #4 MR::DWI::Tractography::GT::MHSampler::next() <null> (tckglobal+0xf1d72) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #5 MR::DWI::Tractography::GT::MHSampler::execute() <null> (tckglobal+0xf1ac7) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #6 std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::operator()() const <null> (tckglobal+0x13f9a3) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #7 std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>>::_M_invoke(std::_Any_data const&) <null> (tckglobal+0x13f89e) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #8 std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) <null> (tckglobal+0x1385a0) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #9 std::once_flag::_Prepare_execution::_Prepare_execution<void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::'lambda'()>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::'lambda'()::__invoke() <null> (tckglobal+0x1386dd) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #10 pthread_once <null> (tckglobal+0x714fd) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #11 std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) <null> (tckglobal+0x1382ed) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #12 std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::_M_run() <null> (tckglobal+0x13f5fb) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #13 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>*>>>::_M_run() <null> (tckglobal+0x13fb47) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #14 execute_native_thread_routine /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)

  Previous write of size 4 at 0x7250001006d4 by thread T33:
    #0 MR::DWI::Tractography::GT::ParticleGrid::shift(MR::DWI::Tractography::GT::Particle*, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&) <null> (tckglobal+0x195f4d) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #1 MR::DWI::Tractography::GT::MHSampler::optshift() <null> (tckglobal+0xf3a08) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #2 MR::DWI::Tractography::GT::MHSampler::next() <null> (tckglobal+0xf1d46) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #3 MR::DWI::Tractography::GT::MHSampler::execute() <null> (tckglobal+0xf1ac7) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #4 std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::operator()() const <null> (tckglobal+0x13f9a3) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #5 std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>>::_M_invoke(std::_Any_data const&) <null> (tckglobal+0x13f89e) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #6 std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) <null> (tckglobal+0x1385a0) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #7 std::once_flag::_Prepare_execution::_Prepare_execution<void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::'lambda'()>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::'lambda'()::__invoke() <null> (tckglobal+0x1386dd) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #8 pthread_once <null> (tckglobal+0x714fd) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #9 std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) <null> (tckglobal+0x1382ed) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #10 std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::_M_run() <null> (tckglobal+0x13f5fb) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #11 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>*>>>::_M_run() <null> (tckglobal+0x13fb47) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #12 execute_native_thread_routine /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)

  Location is heap block of size 480 at 0x725000100600 allocated by thread T26:
    #0 operator new(unsigned long) <null> (tckglobal+0xecebb) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #1 void std::deque<MR::DWI::Tractography::GT::Particle, std::allocator<MR::DWI::Tractography::GT::Particle>>::_M_push_back_aux<Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&>(Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&) <null> (tckglobal+0x198654) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #2 MR::DWI::Tractography::GT::ParticlePool::create(Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&) <null> (tckglobal+0x197bcf) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #3 MR::DWI::Tractography::GT::ParticleGrid::add(Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&) <null> (tckglobal+0x1952c5) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #4 MR::DWI::Tractography::GT::MHSampler::birth() <null> (tckglobal+0xf23a9) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #5 MR::DWI::Tractography::GT::MHSampler::next() <null> (tckglobal+0xf1cbc) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #6 MR::DWI::Tractography::GT::MHSampler::execute() <null> (tckglobal+0xf1ac7) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #7 std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::operator()() const <null> (tckglobal+0x13f9a3) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #8 std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>>::_M_invoke(std::_Any_data const&) <null> (tckglobal+0x13f89e) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #9 std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) <null> (tckglobal+0x1385a0) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #10 std::once_flag::_Prepare_execution::_Prepare_execution<void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::'lambda'()>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::'lambda'()::__invoke() <null> (tckglobal+0x1386dd) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #11 pthread_once <null> (tckglobal+0x714fd) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #12 std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) <null> (tckglobal+0x1382ed) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #13 std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::_M_run() <null> (tckglobal+0x13f5fb) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #14 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>*>>>::_M_run() <null> (tckglobal+0x13fb47) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #15 execute_native_thread_routine /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)

  Thread T35 (tid=601827, running) created by main thread at:
    #0 pthread_create <null> (tckglobal+0x6d93f) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #1 __gthread_create /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:676:35 (libstdc++.so.6+0xeceb0) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xeceb0)
    #3 std::future<std::__invoke_result<std::decay<void (MR::DWI::Tractography::GT::MHSampler::*)()>::type, std::decay<MR::DWI::Tractography::GT::MHSampler*>::type>::type> std::async<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>(std::launch, void (MR::DWI::Tractography::GT::MHSampler::*&&)(), MR::DWI::Tractography::GT::MHSampler*&&) <null> (tckglobal+0x13e033) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #4 run() <null> (tckglobal+0x10d48a) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #5 main <null> (tckglobal+0x10097a) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)

  Thread T33 (tid=601825, running) created by main thread at:
    #0 pthread_create <null> (tckglobal+0x6d93f) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #1 __gthread_create /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:676:35 (libstdc++.so.6+0xeceb0) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xeceb0)
    #3 std::future<std::__invoke_result<std::decay<void (MR::DWI::Tractography::GT::MHSampler::*)()>::type, std::decay<MR::DWI::Tractography::GT::MHSampler*>::type>::type> std::async<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>(std::launch, void (MR::DWI::Tractography::GT::MHSampler::*&&)(), MR::DWI::Tractography::GT::MHSampler*&&) <null> (tckglobal+0x13e033) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #4 run() <null> (tckglobal+0x10d48a) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #5 main <null> (tckglobal+0x10097a) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)

  Thread T26 (tid=601818, running) created by main thread at:
    #0 pthread_create <null> (tckglobal+0x6d93f) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #1 __gthread_create /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:676:35 (libstdc++.so.6+0xeceb0) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xeceb0)
    #3 std::future<std::__invoke_result<std::decay<void (MR::DWI::Tractography::GT::MHSampler::*)()>::type, std::decay<MR::DWI::Tractography::GT::MHSampler*>::type>::type> std::async<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>(std::launch, void (MR::DWI::Tractography::GT::MHSampler::*&&)(), MR::DWI::Tractography::GT::MHSampler*&&) <null> (tckglobal+0x13e033) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #4 run() <null> (tckglobal+0x10d48a) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)
    #5 main <null> (tckglobal+0x10097a) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2)

SUMMARY: ThreadSanitizer: data race (/home/ds23/Documents/Dev/mrtrix3-master/bin/tckglobal+0xfe758) (BuildId: 1b7ea2c1e22d1667253812c05dda0646e4dcc8a2) in MR::DWI::Tractography::GT::InternalEnergyComputer::scanNeighbourhood(MR::DWI::Tractography::GT::Particle const*, int, double)

EDIT: actually we have multiple data races.

WARNING: ThreadSanitizer: data race (pid=609578)
  Read of size 4 at 0x7f9d78c25730 by thread T29:
    #0 Eigen::Matrix<double, -1, 1, 0, -1, 1>& Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1>>::operator+=<MR::Image<float>>(MR::Helper::ConstRow<MR::Image<float>> const&) <null> (tckglobal+0x16a618) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 MR::DWI::Tractography::GT::ExternalEnergyComputer::add2vox(Eigen::Matrix<int, 3, 1, 0, 3, 1> const&, double) <null> (tckglobal+0x15b62d) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #2 MR::DWI::Tractography::GT::ExternalEnergyComputer::add(Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, double) <null> (tckglobal+0x15a889) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #3 MR::DWI::Tractography::GT::ExternalEnergyComputer::stageShift(MR::DWI::Tractography::GT::Particle const*, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&) <null> (tckglobal+0x171bf7) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 MR::DWI::Tractography::GT::EnergySumComputer::stageShift(MR::DWI::Tractography::GT::Particle const*, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&) <null> (tckglobal+0x14467a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 MR::DWI::Tractography::GT::MHSampler::randshift() <null> (tckglobal+0x19203a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #6 MR::DWI::Tractography::GT::MHSampler::next() <null> (tckglobal+0x190e9a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #7 MR::DWI::Tractography::GT::MHSampler::execute() <null> (tckglobal+0x190c47) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #8 std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::operator()() const <null> (tckglobal+0x14c573) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #9 std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>>::_M_invoke(std::_Any_data const&) <null> (tckglobal+0x14c46e) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #10 std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) <null> (tckglobal+0x10da80) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #11 std::once_flag::_Prepare_execution::_Prepare_execution<void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::'lambda'()>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::'lambda'()::__invoke() <null> (tckglobal+0x10dbbd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #12 pthread_once <null> (tckglobal+0x714fd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #13 std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) <null> (tckglobal+0x10d7cd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #14 std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::_M_run() <null> (tckglobal+0x14c1cb) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #15 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>*>>>::_M_run() <null> (tckglobal+0x14c717) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #16 execute_native_thread_routine /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)

  Previous write of size 4 at 0x7f9d78c25730 by thread T40:
    #0 MR::DWI::Tractography::GT::ExternalEnergyComputer::acceptChanges() <null> (tckglobal+0x1597f8) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 MR::DWI::Tractography::GT::EnergySumComputer::acceptChanges() <null> (tckglobal+0x1448cc) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #2 MR::DWI::Tractography::GT::MHSampler::optshift() <null> (tckglobal+0x192b67) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #3 MR::DWI::Tractography::GT::MHSampler::next() <null> (tckglobal+0x190ec6) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 MR::DWI::Tractography::GT::MHSampler::execute() <null> (tckglobal+0x190c47) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::operator()() const <null> (tckglobal+0x14c573) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #6 std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>>::_M_invoke(std::_Any_data const&) <null> (tckglobal+0x14c46e) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #7 std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) <null> (tckglobal+0x10da80) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #8 std::once_flag::_Prepare_execution::_Prepare_execution<void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::'lambda'()>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::'lambda'()::__invoke() <null> (tckglobal+0x10dbbd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #9 pthread_once <null> (tckglobal+0x714fd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #10 std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) <null> (tckglobal+0x10d7cd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #11 std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::_M_run() <null> (tckglobal+0x14c1cb) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #12 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>*>>>::_M_run() <null> (tckglobal+0x14c717) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #13 execute_native_thread_routine /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)

  Location is heap block of size 445500 at 0x7f9d78c24000 allocated by main thread:
    #0 operator new[](unsigned long) <null> (tckglobal+0xecfab) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 MR::ImageIO::Scratch::load(MR::Header const&, unsigned long) <null> (libmrtrix.so+0x153cb6) (BuildId: 8f78ae4ca8b8345dc11741777e4f90f2262dd645)
    #2 MR::ImageIO::Base::open(MR::Header const&, unsigned long) <null> (libmrtrix.so+0x3854ba) (BuildId: 8f78ae4ca8b8345dc11741777e4f90f2262dd645)
    #3 MR::Image<float>::Buffer::Buffer(MR::Header&, bool) <null> (tckglobal+0x15189e) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 MR::Image<float> MR::Header::get_image<float>(bool) <null> (tckglobal+0x151415) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 MR::DWI::Tractography::GT::ExternalEnergyComputer::ExternalEnergyComputer(MR::DWI::Tractography::GT::Stats&, MR::Header&, MR::DWI::Tractography::GT::Properties const&) <null> (tckglobal+0x154528) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #6 run() <null> (tckglobal+0x121926) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #7 main <null> (tckglobal+0x11581a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)

  Thread T29 (tid=609610, running) created by main thread at:
    #0 pthread_create <null> (tckglobal+0x6d93f) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 __gthread_create /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:676:35 (libstdc++.so.6+0xeceb0) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xeceb0)
    #3 std::future<std::__invoke_result<std::decay<void (MR::DWI::Tractography::GT::MHSampler::*)()>::type, std::decay<MR::DWI::Tractography::GT::MHSampler*>::type>::type> std::async<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>(std::launch, void (MR::DWI::Tractography::GT::MHSampler::*&&)(), MR::DWI::Tractography::GT::MHSampler*&&) <null> (tckglobal+0x14ac03) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 run() <null> (tckglobal+0x12232a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 main <null> (tckglobal+0x11581a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)

  Thread T40 (tid=609621, running) created by main thread at:
    #0 pthread_create <null> (tckglobal+0x6d93f) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 __gthread_create /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:676:35 (libstdc++.so.6+0xeceb0) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xeceb0)
    #3 std::future<std::__invoke_result<std::decay<void (MR::DWI::Tractography::GT::MHSampler::*)()>::type, std::decay<MR::DWI::Tractography::GT::MHSampler*>::type>::type> std::async<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>(std::launch, void (MR::DWI::Tractography::GT::MHSampler::*&&)(), MR::DWI::Tractography::GT::MHSampler*&&) <null> (tckglobal+0x14ac03) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 run() <null> (tckglobal+0x122650) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 main <null> (tckglobal+0x11581a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)

SUMMARY: ThreadSanitizer: data race (/home/ds23/Documents/Dev/mrtrix3-master/bin/tckglobal+0x16a618) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770) in Eigen::Matrix<double, -1, 1, 0, -1, 1>& Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1>>::operator+=<MR::Image<float>>(MR::Helper::ConstRow<MR::Image<float>> const&)

WARNING: ThreadSanitizer: data race (pid=609578)
  Read of size 4 at 0x729400001730 by thread T29:
    #0 MR::DWI::Tractography::GT::ExternalEnergyComputer::eval() <null> (tckglobal+0x15c3e0) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 MR::DWI::Tractography::GT::ExternalEnergyComputer::stageShift(MR::DWI::Tractography::GT::Particle const*, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&) <null> (tckglobal+0x171c15) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #2 MR::DWI::Tractography::GT::EnergySumComputer::stageShift(MR::DWI::Tractography::GT::Particle const*, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&) <null> (tckglobal+0x14467a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #3 MR::DWI::Tractography::GT::MHSampler::randshift() <null> (tckglobal+0x19203a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 MR::DWI::Tractography::GT::MHSampler::next() <null> (tckglobal+0x190e9a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 MR::DWI::Tractography::GT::MHSampler::execute() <null> (tckglobal+0x190c47) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #6 std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::operator()() const <null> (tckglobal+0x14c573) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #7 std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>>::_M_invoke(std::_Any_data const&) <null> (tckglobal+0x14c46e) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #8 std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) <null> (tckglobal+0x10da80) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #9 std::once_flag::_Prepare_execution::_Prepare_execution<void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::'lambda'()>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::'lambda'()::__invoke() <null> (tckglobal+0x10dbbd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #10 pthread_once <null> (tckglobal+0x714fd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #11 std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) <null> (tckglobal+0x10d7cd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #12 std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::_M_run() <null> (tckglobal+0x14c1cb) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #13 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>*>>>::_M_run() <null> (tckglobal+0x14c717) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #14 execute_native_thread_routine /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)

  Previous write of size 4 at 0x729400001730 by thread T40:
    #0 MR::DWI::Tractography::GT::ExternalEnergyComputer::acceptChanges() <null> (tckglobal+0x159ad6) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 MR::DWI::Tractography::GT::EnergySumComputer::acceptChanges() <null> (tckglobal+0x1448cc) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #2 MR::DWI::Tractography::GT::MHSampler::optshift() <null> (tckglobal+0x192b67) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #3 MR::DWI::Tractography::GT::MHSampler::next() <null> (tckglobal+0x190ec6) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 MR::DWI::Tractography::GT::MHSampler::execute() <null> (tckglobal+0x190c47) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::operator()() const <null> (tckglobal+0x14c573) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #6 std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>>::_M_invoke(std::_Any_data const&) <null> (tckglobal+0x14c46e) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #7 std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) <null> (tckglobal+0x10da80) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #8 std::once_flag::_Prepare_execution::_Prepare_execution<void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::'lambda'()>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::'lambda'()::__invoke() <null> (tckglobal+0x10dbbd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #9 pthread_once <null> (tckglobal+0x714fd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #10 std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) <null> (tckglobal+0x10d7cd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #11 std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::_M_run() <null> (tckglobal+0x14c1cb) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #12 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>*>>>::_M_run() <null> (tckglobal+0x14c717) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #13 execute_native_thread_routine /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)

  Location is heap block of size 9900 at 0x729400000000 allocated by main thread:
    #0 operator new[](unsigned long) <null> (tckglobal+0xecfab) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 MR::ImageIO::Scratch::load(MR::Header const&, unsigned long) <null> (libmrtrix.so+0x153cb6) (BuildId: 8f78ae4ca8b8345dc11741777e4f90f2262dd645)
    #2 MR::ImageIO::Base::open(MR::Header const&, unsigned long) <null> (libmrtrix.so+0x3854ba) (BuildId: 8f78ae4ca8b8345dc11741777e4f90f2262dd645)
    #3 MR::Image<float>::Buffer::Buffer(MR::Header&, bool) <null> (tckglobal+0x15189e) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 MR::Image<float> MR::Header::get_image<float>(bool) <null> (tckglobal+0x151415) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 MR::DWI::Tractography::GT::ExternalEnergyComputer::ExternalEnergyComputer(MR::DWI::Tractography::GT::Stats&, MR::Header&, MR::DWI::Tractography::GT::Properties const&) <null> (tckglobal+0x154cc5) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #6 run() <null> (tckglobal+0x121926) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #7 main <null> (tckglobal+0x11581a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)

  Thread T29 (tid=609610, running) created by main thread at:
    #0 pthread_create <null> (tckglobal+0x6d93f) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 __gthread_create /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:676:35 (libstdc++.so.6+0xeceb0) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xeceb0)
    #3 std::future<std::__invoke_result<std::decay<void (MR::DWI::Tractography::GT::MHSampler::*)()>::type, std::decay<MR::DWI::Tractography::GT::MHSampler*>::type>::type> std::async<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>(std::launch, void (MR::DWI::Tractography::GT::MHSampler::*&&)(), MR::DWI::Tractography::GT::MHSampler*&&) <null> (tckglobal+0x14ac03) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 run() <null> (tckglobal+0x12232a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 main <null> (tckglobal+0x11581a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)

  Thread T40 (tid=609621, running) created by main thread at:
    #0 pthread_create <null> (tckglobal+0x6d93f) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 __gthread_create /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:676:35 (libstdc++.so.6+0xeceb0) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xeceb0)
    #3 std::future<std::__invoke_result<std::decay<void (MR::DWI::Tractography::GT::MHSampler::*)()>::type, std::decay<MR::DWI::Tractography::GT::MHSampler*>::type>::type> std::async<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>(std::launch, void (MR::DWI::Tractography::GT::MHSampler::*&&)(), MR::DWI::Tractography::GT::MHSampler*&&) <null> (tckglobal+0x14ac03) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 run() <null> (tckglobal+0x122650) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 main <null> (tckglobal+0x11581a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)

SUMMARY: ThreadSanitizer: data race (/home/ds23/Documents/Dev/mrtrix3-master/bin/tckglobal+0x15c3e0) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770) in MR::DWI::Tractography::GT::ExternalEnergyComputer::eval()

WARNING: ThreadSanitizer: data race (pid=609578)
  Read of size 8 at 0x725000051c30 by thread T23:
    #0 MR::DWI::Tractography::GT::MHSampler::moveRandom(MR::DWI::Tractography::GT::Particle const*, Eigen::Matrix<float, 3, 1, 0, 3, 1>&, Eigen::Matrix<float, 3, 1, 0, 3, 1>&) <null> (tckglobal+0x194853) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 MR::DWI::Tractography::GT::MHSampler::randshift() <null> (tckglobal+0x191e04) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #2 MR::DWI::Tractography::GT::MHSampler::next() <null> (tckglobal+0x190e9a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #3 MR::DWI::Tractography::GT::MHSampler::execute() <null> (tckglobal+0x190c47) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::operator()() const <null> (tckglobal+0x14c573) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>>::_M_invoke(std::_Any_data const&) <null> (tckglobal+0x14c46e) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #6 std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) <null> (tckglobal+0x10da80) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #7 std::once_flag::_Prepare_execution::_Prepare_execution<void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::'lambda'()>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::'lambda'()::__invoke() <null> (tckglobal+0x10dbbd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #8 pthread_once <null> (tckglobal+0x714fd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #9 std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) <null> (tckglobal+0x10d7cd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #10 std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::_M_run() <null> (tckglobal+0x14c1cb) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #11 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>*>>>::_M_run() <null> (tckglobal+0x14c717) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #12 execute_native_thread_routine /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)

  Previous write of size 4 at 0x725000051c30 by thread T34:
    #0 MR::DWI::Tractography::GT::ParticleGrid::shift(MR::DWI::Tractography::GT::Particle*, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&) <null> (tckglobal+0xef32e) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 MR::DWI::Tractography::GT::MHSampler::randshift() <null> (tckglobal+0x19221c) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #2 MR::DWI::Tractography::GT::MHSampler::next() <null> (tckglobal+0x190e9a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #3 MR::DWI::Tractography::GT::MHSampler::execute() <null> (tckglobal+0x190c47) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::operator()() const <null> (tckglobal+0x14c573) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>>::_M_invoke(std::_Any_data const&) <null> (tckglobal+0x14c46e) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #6 std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) <null> (tckglobal+0x10da80) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #7 std::once_flag::_Prepare_execution::_Prepare_execution<void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::'lambda'()>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::'lambda'()::__invoke() <null> (tckglobal+0x10dbbd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #8 pthread_once <null> (tckglobal+0x714fd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #9 std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) <null> (tckglobal+0x10d7cd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #10 std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::_M_run() <null> (tckglobal+0x14c1cb) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #11 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>*>>>::_M_run() <null> (tckglobal+0x14c717) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #12 execute_native_thread_routine /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)

  Location is heap block of size 480 at 0x725000051c00 allocated by thread T24:
    #0 operator new(unsigned long) <null> (tckglobal+0xecebb) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 void std::deque<MR::DWI::Tractography::GT::Particle, std::allocator<MR::DWI::Tractography::GT::Particle>>::_M_push_back_aux<Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&>(Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&) <null> (tckglobal+0xf3bb4) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #2 MR::DWI::Tractography::GT::ParticlePool::create(Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&) <null> (tckglobal+0xf108f) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #3 MR::DWI::Tractography::GT::ParticleGrid::add(Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&) <null> (tckglobal+0xee785) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 MR::DWI::Tractography::GT::MHSampler::birth() <null> (tckglobal+0x191529) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 MR::DWI::Tractography::GT::MHSampler::next() <null> (tckglobal+0x190e3c) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #6 MR::DWI::Tractography::GT::MHSampler::execute() <null> (tckglobal+0x190c47) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #7 std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::operator()() const <null> (tckglobal+0x14c573) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #8 std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>>::_M_invoke(std::_Any_data const&) <null> (tckglobal+0x14c46e) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #9 std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) <null> (tckglobal+0x10da80) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #10 std::once_flag::_Prepare_execution::_Prepare_execution<void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::'lambda'()>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::'lambda'()::__invoke() <null> (tckglobal+0x10dbbd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #11 pthread_once <null> (tckglobal+0x714fd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #12 std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) <null> (tckglobal+0x10d7cd) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #13 std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::_M_run() <null> (tckglobal+0x14c1cb) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #14 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>>, void>*>>>::_M_run() <null> (tckglobal+0x14c717) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #15 execute_native_thread_routine /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)

  Thread T23 (tid=609604, running) created by main thread at:
    #0 pthread_create <null> (tckglobal+0x6d93f) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 __gthread_create /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:676:35 (libstdc++.so.6+0xeceb0) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xeceb0)
    #3 std::future<std::__invoke_result<std::decay<void (MR::DWI::Tractography::GT::MHSampler::*)()>::type, std::decay<MR::DWI::Tractography::GT::MHSampler*>::type>::type> std::async<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>(std::launch, void (MR::DWI::Tractography::GT::MHSampler::*&&)(), MR::DWI::Tractography::GT::MHSampler*&&) <null> (tckglobal+0x14ac03) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 run() <null> (tckglobal+0x12232a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 main <null> (tckglobal+0x11581a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)

  Thread T34 (tid=609615, running) created by main thread at:
    #0 pthread_create <null> (tckglobal+0x6d93f) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 __gthread_create /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:676:35 (libstdc++.so.6+0xeceb0) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xeceb0)
    #3 std::future<std::__invoke_result<std::decay<void (MR::DWI::Tractography::GT::MHSampler::*)()>::type, std::decay<MR::DWI::Tractography::GT::MHSampler*>::type>::type> std::async<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>(std::launch, void (MR::DWI::Tractography::GT::MHSampler::*&&)(), MR::DWI::Tractography::GT::MHSampler*&&) <null> (tckglobal+0x14ac03) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 run() <null> (tckglobal+0x12232a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 main <null> (tckglobal+0x11581a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)

  Thread T24 (tid=609605, running) created by main thread at:
    #0 pthread_create <null> (tckglobal+0x6d93f) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #1 __gthread_create /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:676:35 (libstdc++.so.6+0xeceb0) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xeceb0)
    #3 std::future<std::__invoke_result<std::decay<void (MR::DWI::Tractography::GT::MHSampler::*)()>::type, std::decay<MR::DWI::Tractography::GT::MHSampler*>::type>::type> std::async<void (MR::DWI::Tractography::GT::MHSampler::*)(), MR::DWI::Tractography::GT::MHSampler*>(std::launch, void (MR::DWI::Tractography::GT::MHSampler::*&&)(), MR::DWI::Tractography::GT::MHSampler*&&) <null> (tckglobal+0x14ac03) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #4 run() <null> (tckglobal+0x12232a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)
    #5 main <null> (tckglobal+0x11581a) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770)

SUMMARY: ThreadSanitizer: data race (/home/ds23/Documents/Dev/mrtrix3-master/bin/tckglobal+0x194853) (BuildId: d2890daaa617025bb34f56b296f662dfc37e2770) in MR::DWI::Tractography::GT::MHSampler::moveRandom(MR::DWI::Tractography::GT::Particle const*, Eigen::Matrix<float, 3, 1, 0, 3, 1>&, Eigen::Matrix<float, 3, 1, 0, 3, 1>&)

- unlock() must hold the mutex because it writes lockcentres[idx].second
- ~SpatialLock() should also lock before clearing lockcentres to avoid a race if another thread is concurrently in try_lock()/unlock().
This class holds a reference and relies on its destructor to call `unlock`. The default copy/move will copy idx and the reference leading to double unlock() calls.
ParticlePool::size() was a const accessor that read pool.size() and avail.size() without taking the pool mutex. Other threads can call create()/destroy() which modify pool/avail under the mutex.
@daljit46
Copy link
Member

daljit46 commented Sep 23, 2025

I had a look at the code yesterday, and all the threading issues identified by TSAN are quite difficult to fix without non-trivial changes. The code employs a shared-state concurrency logic without guaranteeing proper synchronisation and it lacks a clear ownership model.

For example, the MHSampler modifies a connection between two particles but doesn't provide exclusive access to both of them for the duration of the operation. If thread A selects particle p1 and finds a neighbour p2 to connect to, it's entirely possible that thread B could also select p2 as a neighbour of some other p3. Then two threads will be in contention to modify p2's connection pointers, leading to a data race. The SpatialLock class is insufficient here because it only protects a neighbourhood of particles.

Another issue is that the core logic involves operations that must be treated as a single, indivisible atomic transaction, but the code executes them as a sequence of separate and unprotected steps. In ParticleGrid, we have various operations like add, shift and remove, but their correctness relies on the fact that the grid of particles is unmodified during those operations. The code doesn't guarantee this, as multiple threads could access the grid while those operations are being executed.

Also, we're leaking raw pointers to internal data stored in a vector (in ParticleGrid::at), which could be invalidated once a reallocation of the vector happens (e.g. due to another thread calling ParticleGrid::shift).

I'm unsure of how to fix these problems, but if we want to make the code more maintainable for the future, we should reconsider the overall design of it.

@Lestropie
Copy link
Member Author

@dchristiaens Would it be acceptable to disable multi-threading within tckglobal in order to proceed with a 3.0.8 tag?

@dchristiaens
Copy link
Member

Sorry for the late reply - this is a very busy month for me..

Thanks @daljit46 for looking into this. It's clear there is an issue here, although I don't find it to be particularly problematic in practice.

Some thoughts on the specific points you listed:

MHSampler modifies a connection between two particles but doesn't provide exclusive access to both of them for the duration of the operation. If thread A selects particle p1 and finds a neighbour p2 to connect to, it's entirely possible that thread B could also select p2 as a neighbour of some other p3. Then two threads will be in contention to modify p2's connection pointers, leading to a data race. The SpatialLock class is insufficient here because it only protects a neighbourhood of particles.

This is precisely what SpatialLock is intended to prevent. As long as the distance threshold is set large enough, no two particles should ever compete to connect to the same (third) particle.

Another issue is that the core logic involves operations that must be treated as a single, indivisible atomic transaction, but the code executes them as a sequence of separate and unprotected steps. In ParticleGrid, we have various operations like add, shift and remove, but their correctness relies on the fact that the grid of particles is unmodified during those operations. The code doesn't guarantee this, as multiple threads could access the grid while those operations are being executed.

This should likewise be prevented by the SpatialLock.

Also, we're leaking raw pointers to internal data stored in a vector (in ParticleGrid::at), which could be invalidated once a reallocation of the vector happens (e.g. due to another thread calling ParticleGrid::shift).

I don't fully understand what you mean. Memory is managed by the ParticlePool class, which uses a std:deque that - I think - should preserve the address space.


@Lestropie I agree that it's good to add a warning. However, I don't think making single-threaded the default is a good idea, as this would have a strong impact on run time and be prohibitive in most practical use cases.

@dchristiaens
Copy link
Member

I have added a bit of code to make sure that the SpatialLock also covers the grid spacing. It's possible that I broke this in my earlier commit a few weeks ago, in which the grid sise was redefined to take the image voxel size into account too.

I can not reproduce the issue on my system, so I'm curious to see what the CI will think of this.

@daljit46
Copy link
Member

daljit46 commented Oct 8, 2025

This is precisely what SpatialLock is intended to prevent. As long as the distance threshold is set large enough, no two particles should ever compete to connect to the same (third) particle.

That's good to know. If this is a theoretical guarantee that, given a minimum radius, two threads will never try to connect to the same neighbour, then I think that we can ignore that point. At least, when I tested a couple of weeks ago, TSAN reported that this was happening in happening in practice (i.e. two threads were in contention in the MHSampler::connect function). However, since this is not a guarantee in code, perhaps it's worth documenting it? I think that'd be useful to know for anyone looking at the code in the future without being familiar with the logic behind it.

I don't fully understand what you mean. Memory is managed by the ParticlePool class, which uses a std:deque that - I think - should preserve the address space.

In this case, I was referring to the ParticleGrid class, which holds a vector<ParticleVectorType> where ParticleVectorType is a vector<Particle*>. In ParticleGrid::at, we are returning a pointer to an element (which is itself a vector) of this vector, which could be reallocated. I'm not referring to the std::deque used in ParticlePool. So while Particle* pointers are stable, the ParticleVectorType* pointers are not.

I can not reproduce the issue on my system, so I'm curious to see what the CI will think of this.

I tested your changes with TSAN on my machine, and there are a lot of reports of data races, unfortunately.

This provides a more lightweight alternative to a full-blown mutex,
useful in cases where there may be a very large number of small objects
that each need their own thread protection. It relies on
std::atomic_flag, and only takes up a single byte. It should only be
used sparingly and in cases where collisions are expected to be rare,
since spinning threads will keep running.
@jdtournier
Copy link
Member

jdtournier commented Oct 9, 2025

Just had some fun with thread sanitizer to try to fix up all the race conditions here. I've managed to get a clean run with the changes I've just pushed 🎉

As part of this exercise, I've also identified a race condition in the threaded loop code. I'll commit that fix shortly as a separate PR.

@dchristiaens: I've no idea whether the changes I've made here break anything, I've only tested that it runs to completion with no errors reported from thread sanitizer. I've also not looked into how much of an impact these changes might have made to performance. It would be wise to verify that everything still works as expected...

This uses the new SpinLock class to protect multi-threaded access to
individual particles, adds a mutex to protect each element of the
particle grid, and uses a std::deque instead of a std::vector to store
the particles within each element of the particle grid to avoid the
potential for pointers becoming invalid when adding / removing
particles.
@daljit46
Copy link
Member

daljit46 commented Oct 9, 2025

I've also not looked into how much of an impact these changes might have made to performance.

On my system, your changes result in the tests taking about 4x longer, but no data races :)

@jdtournier
Copy link
Member

Ouch. That's quite a hit... Is that running with the code compiled for release...?

@daljit46
Copy link
Member

daljit46 commented Oct 9, 2025

Yes, but interestingly I found this article and adapting the code shown at the bottom:

#include <atomic>
#include <mutex>
#include <thread>

namespace MR {

  class SpinLock
  {
    public:
    
      void lock() noexcept {
        for (;;) {
          if (!m_.exchange(true, std::memory_order_acquire)) {
            return;
          }
          while (m_.load(std::memory_order_relaxed)) {
            std::this_thread::yield();
          }
      }
    }
      void unlock() noexcept { m_.store(false, std::memory_order_release); }
    private:
      std::atomic<bool> m_ = {0};
  };

}

If I use this Spinlock, the tests actually run faster!

EDIT: C++11 apparently has std::this_thread::yield() which works fine too (it's apparently an OS level scheduling hint rather than architecture-based).

@daljit46
Copy link
Member

daljit46 commented Oct 9, 2025

UPDATE: never mind, I tested again with a full clean build, and your code is only marginally slower. So ignore the comment above.

@jdtournier
Copy link
Member

Just checking: marginally slower than your alternative spin lock, or than the original non-thread-safe version?

@daljit46
Copy link
Member

daljit46 commented Oct 10, 2025

Just checking: marginally slower than your alternative spin lock, or than the original non-thread-safe version?

I meant against the alternative spin lock, but I think the difference is well within noise intervals, so they perform roughly the same. The current tip is a little slower (on my M2 Pro) than the thread-unsafe version:

time tckglobal  dwi2fod/msmt/dwi.mif dwi2fod/msmt/wm.txt tmp.tck -riso dwi2fod/msmt/gm.txt -riso dwi2fod/msmt/csf.txt -fiso tmp_fiso.mif -eext tmp_eext.mif -niter 2000000 -force

This runs (from within testing/binaries/data) in about 3.9 seconds (spinlock version) vs 3.1 seconds (unsafe version) over an average of 5 runs.

@dchristiaens
Copy link
Member

Thanks Donald - once again you proved yourself to be the most accomplished programmer of us all!

I've done a few full-brain tests. The code is indeed a bit slower than it used to be, but that's a fair sacrifice to make. The output looks indistinguishable from the old multi-threaded version, so the changes are all fine. I find it equally reassuring to know that the race conditions in the old version did not severely affect the output, which I assume is because the MH sampler is robust to such random perturbations of the system state.

@jdtournier jdtournier added this pull request to the merge queue Oct 16, 2025
Merged via the queue into master with commit 1b6294b Oct 16, 2025
5 checks passed
@jdtournier jdtournier deleted the tckglobal_fix branch October 16, 2025 21:18
@jdtournier jdtournier mentioned this pull request Oct 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tckglobal: don't invoke get_image() with invalid Header!

5 participants